From 8baab3c8d7a6f22888bd581cd5c6098fd2e4b5a8 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 6 May 2024 04:44:24 +0200 Subject: Adding upstream version 2:8.1.0875. Signed-off-by: Daniel Baumann --- src/GvimExt/GvimExt.reg | 20 + src/GvimExt/Make_bc5.mak | 43 + src/GvimExt/Make_ming.mak | 81 + src/GvimExt/Makefile | 80 + src/GvimExt/README.txt | 94 + src/GvimExt/gvimext.cpp | 1090 ++ src/GvimExt/gvimext.def | 8 + src/GvimExt/gvimext.h | 181 + src/GvimExt/gvimext.inf | 22 + src/GvimExt/gvimext.rc | 111 + src/GvimExt/gvimext_ming.def | 10 + src/GvimExt/gvimext_ming.rc | 45 + src/GvimExt/resource.h | 15 + src/GvimExt/uninst.bat | 1 + src/INSTALL | 384 + src/INSTALLami.txt | 34 + src/INSTALLmac.txt | 72 + src/INSTALLpc.txt | 1051 ++ src/INSTALLvms.txt | 393 + src/INSTALLx.txt | 165 + src/Make_all.mak | 15 + src/Make_bc5.mak | 1060 ++ src/Make_cyg.mak | 54 + src/Make_cyg_ming.mak | 1165 ++ src/Make_dice.mak | 285 + src/Make_dvc.mak | 105 + src/Make_ivc.mak | 763 + src/Make_manx.mak | 438 + src/Make_ming.mak | 51 + src/Make_mint.mak | 56 + src/Make_morph.mak | 95 + src/Make_mvc.mak | 1701 ++ src/Make_sas.mak | 440 + src/Make_vms.mms | 858 + src/Makefile | 3871 +++++ src/README.txt | 160 + src/VisVim/Commands.cpp | 710 + src/VisVim/Commands.h | 127 + src/VisVim/DSAddIn.cpp | 160 + src/VisVim/DSAddIn.h | 53 + src/VisVim/OleAut.cpp | 781 + src/VisVim/OleAut.h | 73 + src/VisVim/README_VisVim.txt | 326 + src/VisVim/Reg.cpp | 56 + src/VisVim/Register.bat | 1 + src/VisVim/Res/ToolbarL.bmp | Bin 0 -> 2678 bytes src/VisVim/Res/ToolbarM.bmp | Bin 0 -> 758 bytes src/VisVim/Resource.h | 30 + src/VisVim/StdAfx.cpp | 6 + src/VisVim/StdAfx.h | 73 + src/VisVim/UnRegist.bat | 1 + src/VisVim/VisVim.cpp | 152 + src/VisVim/VisVim.def | 11 + src/VisVim/VisVim.dll | Bin 0 -> 49664 bytes src/VisVim/VisVim.h | 33 + src/VisVim/VisVim.mak | 205 + src/VisVim/VisVim.odl | 61 + src/VisVim/VisVim.rc | 202 + src/VisVim/VsReadMe.txt | 91 + src/alloc.h | 33 + src/arabic.c | 715 + src/arabic.h | 258 + src/ascii.h | 186 + src/auto/configure | 16142 +++++++++++++++++++ src/autocmd.c | 2579 +++ src/beval.c | 281 + src/beval.h | 91 + src/bigvim.bat | 5 + src/bigvim64.bat | 7 + src/blob.c | 258 + src/blowfish.c | 683 + src/buffer.c | 5948 +++++++ src/channel.c | 6100 +++++++ src/charset.c | 2093 +++ src/config.h.in | 491 + src/config.mk.dist | 5 + src/config.mk.in | 175 + src/configure | 10 + src/configure.ac | 4486 ++++++ src/create_cmdidxs.vim | 81 + src/crypt.c | 605 + src/crypt_zip.c | 152 + src/dehqx.py | 45 + src/dict.c | 920 ++ src/diff.c | 3221 ++++ src/digraph.c | 2472 +++ src/dimm.idl | 544 + src/dlldata.c | 38 + src/dosinst.c | 2845 ++++ src/dosinst.h | 531 + src/edit.c | 10296 ++++++++++++ src/eval.c | 10837 +++++++++++++ src/evalfunc.c | 14809 +++++++++++++++++ src/ex_cmdidxs.h | 72 + src/ex_cmds.c | 7754 +++++++++ src/ex_cmds.h | 1819 +++ src/ex_cmds2.c | 5729 +++++++ src/ex_docmd.c | 12587 +++++++++++++++ src/ex_eval.c | 2294 +++ src/ex_getln.c | 7484 +++++++++ src/farsi.c | 2179 +++ src/farsi.h | 234 + src/feature.h | 1352 ++ src/fileio.c | 7872 +++++++++ src/fold.c | 3608 +++++ src/getchar.c | 5323 ++++++ src/glbl_ime.cpp | 263 + src/glbl_ime.h | 33 + src/globals.h | 1670 ++ src/gui.c | 5460 +++++++ src/gui.h | 569 + src/gui_at_fs.c | 2734 ++++ src/gui_at_sb.c | 1192 ++ src/gui_at_sb.h | 161 + src/gui_athena.c | 2292 +++ src/gui_beval.c | 1225 ++ src/gui_dwrite.cpp | 1345 ++ src/gui_dwrite.h | 92 + src/gui_gtk.c | 2609 +++ src/gui_gtk_f.c | 896 + src/gui_gtk_f.h | 85 + src/gui_gtk_res.xml | 18 + src/gui_gtk_vms.h | 740 + src/gui_gtk_x11.c | 7162 ++++++++ src/gui_mac.c | 6731 ++++++++ src/gui_motif.c | 4020 +++++ src/gui_photon.c | 2959 ++++ src/gui_w32.c | 8930 ++++++++++ src/gui_w32_rc.h | 8 + src/gui_x11.c | 3396 ++++ src/gui_x11_pm.h | 92 + src/gui_xmdlg.c | 1287 ++ src/gui_xmebw.c | 1450 ++ src/gui_xmebw.h | 72 + src/gui_xmebwp.h | 88 + src/gvim.exe.mnf | 38 + src/gvimtutor | 8 + src/hangulin.c | 1641 ++ src/hardcopy.c | 3499 ++++ src/hashtab.c | 482 + src/if_cscope.c | 2509 +++ src/if_cscope.h | 73 + src/if_lua.c | 2088 +++ src/if_mzsch.c | 3824 +++++ src/if_mzsch.h | 76 + src/if_ole.cpp | 813 + src/if_ole.h | 292 + src/if_ole.idl | 45 + src/if_perl.xs | 1925 +++ src/if_perl_msvc/stdbool.h | 3 + src/if_perlsfio.c | 66 + src/if_py_both.h | 6951 ++++++++ src/if_python.c | 1600 ++ src/if_python3.c | 1696 ++ src/if_ruby.c | 1741 ++ src/if_tcl.c | 2109 +++ src/if_xcmdsrv.c | 1527 ++ src/iid_ole.c | 59 + src/indent.c | 4460 +++++ src/infplist.xml | 74 + src/install-sh | 501 + src/installman.sh | 124 + src/installml.sh | 170 + src/iscygpty.c | 183 + src/iscygpty.h | 41 + src/json.c | 1108 ++ src/json_test.c | 203 + src/keymap.h | 507 + src/kword_test.c | 81 + src/libvterm/.bzrignore | 13 + src/libvterm/.gitignore | 17 + src/libvterm/LICENSE | 23 + src/libvterm/Makefile | 141 + src/libvterm/README | 30 + src/libvterm/bin/unterm.c | 288 + src/libvterm/bin/vterm-ctrl.c | 368 + src/libvterm/bin/vterm-dump.c | 232 + src/libvterm/doc/URLs | 14 + src/libvterm/doc/seqs.txt | 227 + src/libvterm/include/vterm.h | 435 + src/libvterm/include/vterm_keycodes.h | 64 + src/libvterm/src/encoding.c | 234 + src/libvterm/src/encoding/DECdrawing.inc | 136 + src/libvterm/src/encoding/DECdrawing.tbl | 31 + src/libvterm/src/encoding/uk.inc | 136 + src/libvterm/src/encoding/uk.tbl | 1 + src/libvterm/src/keyboard.c | 229 + src/libvterm/src/mouse.c | 98 + src/libvterm/src/parser.c | 346 + src/libvterm/src/pen.c | 516 + src/libvterm/src/rect.h | 56 + src/libvterm/src/state.c | 1925 +++ src/libvterm/src/termscreen.c | 939 ++ src/libvterm/src/unicode.c | 349 + src/libvterm/src/utf8.h | 47 + src/libvterm/src/vterm.c | 425 + src/libvterm/src/vterm_internal.h | 263 + src/libvterm/t/02parser.test | 200 + src/libvterm/t/03encoding_utf8.test | 122 + src/libvterm/t/10state_putglyph.test | 61 + src/libvterm/t/11state_movecursor.test | 224 + src/libvterm/t/12state_scroll.test | 150 + src/libvterm/t/13state_edit.test | 300 + src/libvterm/t/14state_encoding.test | 105 + src/libvterm/t/15state_mode.test | 86 + src/libvterm/t/16state_resize.test | 48 + src/libvterm/t/17state_mouse.test | 172 + src/libvterm/t/18state_termprops.test | 36 + src/libvterm/t/20state_wrapping.test | 69 + src/libvterm/t/21state_tabstops.test | 60 + src/libvterm/t/22state_save.test | 64 + src/libvterm/t/25state_input.test | 143 + src/libvterm/t/26state_query.test | 62 + src/libvterm/t/27state_reset.test | 32 + src/libvterm/t/28state_dbl_wh.test | 61 + src/libvterm/t/29state_fallback.test | 19 + src/libvterm/t/30pen.test | 106 + src/libvterm/t/40screen_ascii.test | 69 + src/libvterm/t/41screen_unicode.test | 47 + src/libvterm/t/42screen_damage.test | 155 + src/libvterm/t/43screen_resize.test | 90 + src/libvterm/t/44screen_pen.test | 55 + src/libvterm/t/45screen_protect.test | 16 + src/libvterm/t/46screen_extent.test | 11 + src/libvterm/t/47screen_dbl_wh.test | 32 + src/libvterm/t/48screen_termprops.test | 17 + src/libvterm/t/90vttest_01-movement-1.test | 87 + src/libvterm/t/90vttest_01-movement-2.test | 40 + src/libvterm/t/90vttest_01-movement-3.test | 21 + src/libvterm/t/90vttest_01-movement-4.test | 36 + src/libvterm/t/90vttest_02-screen-1.test | 18 + src/libvterm/t/90vttest_02-screen-2.test | 29 + src/libvterm/t/90vttest_02-screen-3.test | 16 + src/libvterm/t/90vttest_02-screen-4.test | 17 + src/libvterm/t/92lp1640917.test | 13 + src/libvterm/t/harness.c | 945 ++ src/libvterm/t/run-test.pl | 196 + src/libvterm/tbl2inc_c.pl | 51 + src/libvterm/vterm.pc.in | 9 + src/link.390 | 7 + src/link.sh | 151 + src/list.c | 1010 ++ src/macros.h | 336 + src/main.c | 4296 +++++ src/mark.c | 2215 +++ src/mbyte.c | 6936 ++++++++ src/memfile.c | 1491 ++ src/memfile_test.c | 143 + src/memline.c | 5679 +++++++ src/menu.c | 2778 ++++ src/message.c | 5161 ++++++ src/message_test.c | 107 + src/misc1.c | 7122 ++++++++ src/misc2.c | 6579 ++++++++ src/move.c | 2864 ++++ src/msvc2008.bat | 7 + src/msvc2010.bat | 7 + src/msvc2015.bat | 37 + src/msvcsetup.bat | 12 + src/msys32.bat | 6 + src/msys64.bat | 6 + src/mysign | 1 + src/nbdebug.c | 162 + src/nbdebug.h | 72 + src/netbeans.c | 3488 ++++ src/normal.c | 9560 +++++++++++ src/ops.c | 7467 +++++++++ src/option.c | 13378 +++++++++++++++ src/option.h | 1179 ++ src/os_amiga.c | 1653 ++ src/os_amiga.h | 230 + src/os_beos.c | 200 + src/os_beos.h | 27 + src/os_beos.rsrc | Bin 0 -> 6956 bytes src/os_dos.h | 135 + src/os_mac.h | 293 + src/os_mac.rsr.hqx | 659 + src/os_mac_conv.c | 586 + src/os_mac_rsrc/app.icns | Bin 0 -> 47086 bytes src/os_mac_rsrc/doc-txt.icns | Bin 0 -> 40106 bytes src/os_mac_rsrc/doc.icns | Bin 0 -> 42287 bytes src/os_macosx.m | 218 + src/os_mint.h | 13 + src/os_mswin.c | 3088 ++++ src/os_qnx.c | 157 + src/os_qnx.h | 19 + src/os_unix.c | 8113 ++++++++++ src/os_unix.h | 494 + src/os_unixx.h | 116 + src/os_vms.c | 804 + src/os_vms_conf.h | 205 + src/os_vms_fix.com | 276 + src/os_vms_mms.c | 77 + src/os_w32dll.c | 24 + src/os_w32exe.c | 137 + src/os_win32.c | 7880 +++++++++ src/os_win32.h | 219 + src/osdef.sh | 95 + src/osdef1.h.in | 138 + src/osdef2.h.in | 100 + src/pathdef.sh | 11 + src/po/Make_all.mak | 135 + src/po/Make_cyg.mak | 79 + src/po/Make_ming.mak | 98 + src/po/Make_mvc.mak | 72 + src/po/Makefile | 177 + src/po/README.txt | 146 + src/po/README_mingw.txt | 105 + src/po/README_mvc.txt | 119 + src/po/af.po | 5393 +++++++ src/po/ca.po | 6939 ++++++++ src/po/check.vim | 213 + src/po/cleanup.vim | 25 + src/po/cs.cp1250.po | 4660 ++++++ src/po/cs.po | 4660 ++++++ src/po/da.po | 7098 ++++++++ src/po/de.po | 7381 +++++++++ src/po/en_GB.po | 767 + src/po/eo.po | 7146 ++++++++ src/po/es.po | 8277 ++++++++++ src/po/fi.po | 6993 ++++++++ src/po/fr.po | 7429 +++++++++ src/po/ga.po | 7511 +++++++++ src/po/it.po | 6553 ++++++++ src/po/ja.euc-jp.po | 7068 ++++++++ src/po/ja.po | 7068 ++++++++ src/po/ja.sjis.po | 7068 ++++++++ src/po/ko.UTF-8.po | 7009 ++++++++ src/po/ko.po | 7009 ++++++++ src/po/lv.po | 282 + 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 | 7015 ++++++++ src/po/ru.cp1251.po | 7111 ++++++++ src/po/ru.po | 7111 ++++++++ src/po/sjiscorr.c | 48 + src/po/sk.cp1250.po | 5822 +++++++ src/po/sk.po | 5822 +++++++ src/po/sr.po | 7115 ++++++++ src/po/sv.po | 6148 +++++++ src/po/uk.cp1251.po | 7333 +++++++++ src/po/uk.po | 7333 +++++++++ src/po/vi.po | 5196 ++++++ src/po/zh_CN.UTF-8.po | 6140 +++++++ 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 | 1361 ++ src/proto.h | 339 + src/proto/arabic.pro | 3 + src/proto/autocmd.pro | 39 + src/proto/beval.pro | 6 + src/proto/blob.pro | 16 + src/proto/blowfish.pro | 6 + src/proto/buffer.pro | 75 + src/proto/channel.pro | 76 + src/proto/charset.pro | 64 + src/proto/crypt.pro | 22 + src/proto/crypt_zip.pro | 5 + src/proto/dict.pro | 32 + src/proto/diff.pro | 29 + src/proto/digraph.pro | 11 + src/proto/edit.pro | 47 + src/proto/eval.pro | 144 + src/proto/evalfunc.pro | 15 + src/proto/ex_cmds.pro | 61 + src/proto/ex_cmds2.pro | 112 + src/proto/ex_docmd.pro | 76 + src/proto/ex_eval.pro | 34 + src/proto/ex_getln.pro | 60 + src/proto/farsi.pro | 12 + src/proto/fileio.pro | 36 + src/proto/fold.pro | 42 + src/proto/getchar.pro | 70 + src/proto/gui.pro | 70 + src/proto/gui_athena.pro | 31 + src/proto/gui_beval.pro | 9 + src/proto/gui_gtk.pro | 22 + src/proto/gui_gtk_gresources.pro | 5 + src/proto/gui_gtk_x11.pro | 79 + src/proto/gui_mac.pro | 151 + src/proto/gui_motif.pro | 46 + src/proto/gui_photon.pro | 70 + src/proto/gui_w32.pro | 99 + src/proto/gui_x11.pro | 73 + src/proto/gui_xmdlg.pro | 3 + src/proto/hangulin.pro | 11 + src/proto/hardcopy.pro | 20 + src/proto/hashtab.pro | 14 + src/proto/if_cscope.pro | 12 + src/proto/if_lua.pro | 11 + src/proto/if_mzsch.pro | 17 + src/proto/if_ole.pro | 5 + src/proto/if_perl.pro | 9 + src/proto/if_perlsfio.pro | 3 + src/proto/if_python.pro | 13 + src/proto/if_python3.pro | 13 + src/proto/if_ruby.pro | 10 + src/proto/if_tcl.pro | 10 + src/proto/if_xcmdsrv.pro | 13 + src/proto/indent.pro | 16 + src/proto/json.pro | 7 + src/proto/list.pro | 40 + src/proto/main.pro | 17 + src/proto/mark.pro | 38 + src/proto/mbyte.pro | 101 + src/proto/memfile.pro | 18 + src/proto/memline.pro | 40 + src/proto/menu.pro | 27 + src/proto/message.pro | 83 + src/proto/misc1.pro | 102 + src/proto/misc2.pro | 119 + src/proto/move.pro | 43 + src/proto/netbeans.pro | 28 + src/proto/normal.pro | 26 + src/proto/ops.pro | 68 + src/proto/option.pro | 85 + src/proto/os_amiga.pro | 46 + src/proto/os_beos.pro | 4 + src/proto/os_mac_conv.pro | 12 + src/proto/os_mswin.pro | 54 + src/proto/os_qnx.pro | 8 + src/proto/os_unix.pro | 86 + src/proto/os_vms.pro | 16 + src/proto/os_win32.pro | 76 + src/proto/popupmnu.pro | 17 + src/proto/pty.pro | 5 + src/proto/quickfix.pro | 34 + src/proto/regexp.pro | 20 + src/proto/screen.pro | 64 + src/proto/search.pro | 51 + src/proto/sha256.pro | 9 + src/proto/sign.pro | 27 + src/proto/spell.pro | 38 + src/proto/spellfile.pro | 9 + src/proto/syntax.pro | 65 + src/proto/tag.pro | 14 + src/proto/term.pro | 83 + src/proto/terminal.pro | 62 + src/proto/termlib.pro | 8 + src/proto/textprop.pro | 18 + src/proto/ui.pro | 72 + src/proto/undo.pro | 31 + src/proto/userfunc.pro | 58 + src/proto/version.pro | 11 + src/proto/winclip.pro | 15 + src/proto/window.pro | 98 + src/protodef.h | 18 + src/pty.c | 443 + src/quickfix.c | 7252 +++++++++ src/regexp.c | 8291 ++++++++++ src/regexp.h | 176 + src/regexp_nfa.c | 7362 +++++++++ src/screen.c | 11034 +++++++++++++ src/search.c | 5751 +++++++ src/sha256.c | 427 + src/sign.c | 1941 +++ src/spell.c | 8841 ++++++++++ src/spell.h | 301 + src/spellfile.c | 6651 ++++++++ src/structs.h | 3540 ++++ src/syntax.c | 10346 ++++++++++++ src/tag.c | 4209 +++++ src/tearoff.bmp | Bin 0 -> 118 bytes src/tee/Make_mvc.mak | 19 + src/tee/Makefile | 21 + src/tee/tee.c | 161 + src/term.c | 7152 ++++++++ src/term.h | 210 + src/terminal.c | 6370 ++++++++ src/termlib.c | 618 + src/testdir/Make_all.mak | 418 + src/testdir/Make_amiga.mak | 45 + src/testdir/Make_dos.mak | 138 + src/testdir/Make_ming.mak | 142 + src/testdir/Make_vms.mms | 212 + src/testdir/Makefile | 173 + src/testdir/README.txt | 73 + src/testdir/amiga.vim | 6 + src/testdir/bench_re_freeze.in | 13 + src/testdir/bench_re_freeze.vim | 13 + src/testdir/color_ramp.vim | 41 + src/testdir/dos.vim | 9 + src/testdir/dotest.in | 3 + src/testdir/dumps/Test_conceal_cul_01.dump | 20 + src/testdir/dumps/Test_conceal_cul_02.dump | 20 + src/testdir/dumps/Test_conceal_cul_03.dump | 20 + src/testdir/dumps/Test_conceal_two_windows_01.dump | 20 + src/testdir/dumps/Test_conceal_two_windows_02.dump | 20 + src/testdir/dumps/Test_conceal_two_windows_03.dump | 20 + src/testdir/dumps/Test_conceal_two_windows_04.dump | 20 + src/testdir/dumps/Test_conceal_two_windows_05.dump | 20 + .../dumps/Test_conceal_two_windows_06c.dump | 20 + .../dumps/Test_conceal_two_windows_06i.dump | 20 + .../dumps/Test_conceal_two_windows_06n.dump | 20 + .../dumps/Test_conceal_two_windows_06v.dump | 20 + .../dumps/Test_conceal_two_windows_07c.dump | 20 + .../dumps/Test_conceal_two_windows_07i.dump | 20 + .../dumps/Test_conceal_two_windows_07n.dump | 20 + .../dumps/Test_conceal_two_windows_07v.dump | 20 + .../dumps/Test_conceal_two_windows_08c.dump | 20 + .../dumps/Test_conceal_two_windows_08i.dump | 20 + .../dumps/Test_conceal_two_windows_08n.dump | 20 + .../dumps/Test_conceal_two_windows_08v.dump | 20 + .../dumps/Test_conceal_two_windows_09c.dump | 20 + .../dumps/Test_conceal_two_windows_09i.dump | 20 + .../dumps/Test_conceal_two_windows_09n.dump | 20 + .../dumps/Test_conceal_two_windows_09v.dump | 20 + src/testdir/dumps/Test_conceal_two_windows_10.dump | 20 + src/testdir/dumps/Test_conceal_two_windows_11.dump | 20 + src/testdir/dumps/Test_conceal_two_windows_12.dump | 20 + src/testdir/dumps/Test_conceal_two_windows_13.dump | 20 + src/testdir/dumps/Test_cursorline_yank_01.dump | 8 + src/testdir/dumps/Test_diff_01.dump | 20 + src/testdir/dumps/Test_diff_02.dump | 20 + src/testdir/dumps/Test_diff_03.dump | 20 + src/testdir/dumps/Test_diff_04.dump | 20 + src/testdir/dumps/Test_diff_05.dump | 20 + src/testdir/dumps/Test_diff_06.dump | 20 + src/testdir/dumps/Test_diff_07.dump | 20 + src/testdir/dumps/Test_diff_08.dump | 20 + src/testdir/dumps/Test_diff_09.dump | 20 + src/testdir/dumps/Test_diff_10.dump | 20 + src/testdir/dumps/Test_diff_11.dump | 20 + src/testdir/dumps/Test_diff_12.dump | 20 + src/testdir/dumps/Test_diff_13.dump | 20 + src/testdir/dumps/Test_diff_14.dump | 20 + src/testdir/dumps/Test_diff_15.dump | 20 + src/testdir/dumps/Test_diff_16.dump | 20 + src/testdir/dumps/Test_diff_17.dump | 20 + src/testdir/dumps/Test_diff_18.dump | 20 + src/testdir/dumps/Test_diff_19.dump | 20 + src/testdir/dumps/Test_diff_20.dump | 20 + src/testdir/dumps/Test_diff_of_diff_01.dump | 20 + .../dumps/Test_diff_with_cursorline_01.dump | 20 + .../dumps/Test_diff_with_cursorline_02.dump | 20 + .../dumps/Test_diff_with_cursorline_03.dump | 20 + src/testdir/dumps/Test_folds_with_rnu_01.dump | 20 + src/testdir/dumps/Test_folds_with_rnu_02.dump | 20 + src/testdir/dumps/Test_incsearch_scrolling_01.dump | 9 + src/testdir/dumps/Test_incsearch_search_01.dump | 9 + src/testdir/dumps/Test_incsearch_search_02.dump | 9 + src/testdir/dumps/Test_incsearch_sort_01.dump | 9 + .../dumps/Test_incsearch_substitute_01.dump | 9 + .../dumps/Test_incsearch_substitute_02.dump | 9 + .../dumps/Test_incsearch_substitute_03.dump | 9 + .../dumps/Test_incsearch_substitute_04.dump | 9 + .../dumps/Test_incsearch_substitute_05.dump | 9 + .../dumps/Test_incsearch_substitute_06.dump | 9 + .../dumps/Test_incsearch_substitute_07.dump | 9 + .../dumps/Test_incsearch_substitute_08.dump | 9 + .../dumps/Test_incsearch_substitute_09.dump | 9 + .../dumps/Test_incsearch_substitute_10.dump | 9 + .../dumps/Test_incsearch_substitute_11.dump | 9 + .../dumps/Test_incsearch_substitute_12.dump | 9 + .../dumps/Test_incsearch_substitute_13.dump | 9 + src/testdir/dumps/Test_incsearch_vimgrep_01.dump | 9 + src/testdir/dumps/Test_incsearch_vimgrep_02.dump | 9 + src/testdir/dumps/Test_incsearch_vimgrep_03.dump | 9 + src/testdir/dumps/Test_incsearch_vimgrep_04.dump | 9 + src/testdir/dumps/Test_incsearch_vimgrep_05.dump | 9 + .../dumps/Test_popup_and_previewwindow_01.dump | 20 + src/testdir/dumps/Test_popup_command_01.dump | 20 + src/testdir/dumps/Test_popup_command_02.dump | 20 + src/testdir/dumps/Test_popup_command_03.dump | 20 + src/testdir/dumps/Test_popup_position_01.dump | 8 + src/testdir/dumps/Test_popup_position_02.dump | 8 + src/testdir/dumps/Test_popup_position_03.dump | 8 + src/testdir/dumps/Test_popup_position_04.dump | 10 + src/testdir/dumps/Test_syntax_c_01.dump | 20 + src/testdir/dumps/Test_tenc_euc_jp_01.dump | 10 + src/testdir/dumps/Test_textprop_01.dump | 6 + src/testdir/gen_opt_test.vim | 226 + src/testdir/gui_init.vim | 6 + src/testdir/gui_preinit.vim | 7 + src/testdir/if_ver-1.vim | 26 + src/testdir/if_ver-2.vim | 10 + src/testdir/lsan-suppress.txt | 3 + src/testdir/python2/module.py | 2 + src/testdir/python3/module.py | 2 + src/testdir/python_after/after.py | 2 + src/testdir/python_before/before.py | 1 + src/testdir/python_before/before_1.py | 1 + src/testdir/python_before/before_2.py | 1 + src/testdir/pythonx/failing.py | 1 + src/testdir/pythonx/failing_import.py | 1 + src/testdir/pythonx/module.py | 1 + src/testdir/pythonx/modulex.py | 1 + src/testdir/pythonx/topmodule/__init__.py | 1 + .../pythonx/topmodule/submodule/__init__.py | 1 + .../topmodule/submodule/subsubmodule/__init__.py | 1 + .../submodule/subsubmodule/subsubsubmodule.py | 1 + src/testdir/pyxfile/py2_magic.py | 4 + src/testdir/pyxfile/py2_shebang.py | 4 + src/testdir/pyxfile/py3_magic.py | 4 + src/testdir/pyxfile/py3_shebang.py | 4 + src/testdir/pyxfile/pyx.py | 2 + src/testdir/runtest.vim | 401 + src/testdir/samples/quickfix.txt | 4 + src/testdir/samples/re.freeze.txt | 6 + src/testdir/samples/test000 | Bin 0 -> 8 bytes src/testdir/sautest/autoload/Test104.vim | 1 + src/testdir/sautest/autoload/foo.vim | 7 + src/testdir/sautest/autoload/footest.vim | 5 + src/testdir/sautest/autoload/globone.vim | 1 + src/testdir/sautest/autoload/globtwo.vim | 1 + src/testdir/sautest/autoload/sourced.vim | 3 + src/testdir/screendump.vim | 130 + src/testdir/setup.vim | 33 + src/testdir/setup_gui.vim | 32 + src/testdir/shared.vim | 357 + src/testdir/test1.in | 52 + src/testdir/test1.ok | 1 + src/testdir/test108.in | 88 + src/testdir/test108.ok | 82 + src/testdir/test11.in | 84 + src/testdir/test11.ok | 61 + src/testdir/test14.in | 100 + src/testdir/test14.ok | 26 + src/testdir/test17.in | 135 + src/testdir/test17.ok | 33 + src/testdir/test17a.in | 3 + src/testdir/test29.in | 231 + src/testdir/test29.ok | 97 + src/testdir/test3.in | 2354 +++ src/testdir/test3.ok | 2102 +++ src/testdir/test30.in | 238 + src/testdir/test30.ok | 130 + src/testdir/test37.in | 116 + src/testdir/test37.ok | 33 + src/testdir/test39.in | 118 + src/testdir/test39.ok | Bin 0 -> 662 bytes src/testdir/test42.in | Bin 0 -> 2368 bytes src/testdir/test42.ok | Bin 0 -> 409 bytes src/testdir/test44.in | 81 + src/testdir/test44.ok | 25 + src/testdir/test48.in | 83 + src/testdir/test48.ok | 23 + src/testdir/test49.in | 32 + src/testdir/test49.ok | 84 + src/testdir/test49.vim | 9009 +++++++++++ src/testdir/test52.in | 65 + src/testdir/test52.ok | 18 + src/testdir/test59.in | 626 + src/testdir/test59.ok | 270 + src/testdir/test64.in | 654 + src/testdir/test64.ok | 1107 ++ src/testdir/test69.in | 192 + src/testdir/test69.ok | 166 + src/testdir/test70.in | 63 + src/testdir/test70.ok | 6 + src/testdir/test72.in | 146 + src/testdir/test72.ok | 31 + src/testdir/test77a.com | 8 + src/testdir/test77a.in | 31 + src/testdir/test77a.ok | 1 + src/testdir/test83-tags2 | 2 + src/testdir/test83-tags3 | 102 + src/testdir/test85.ok | 7 + src/testdir/test86.in | 1711 ++ src/testdir/test86.ok | 1445 ++ src/testdir/test87.in | 1725 ++ src/testdir/test87.ok | 1445 ++ src/testdir/test88.in | 99 + src/testdir/test88.ok | 29 + src/testdir/test94.in | 257 + src/testdir/test94.ok | 123 + src/testdir/test95.in | 141 + src/testdir/test95.ok | 140 + src/testdir/test99.in | 69 + src/testdir/test99.ok | 23 + src/testdir/test_alot.vim | 68 + src/testdir/test_alot_latin.vim | 7 + src/testdir/test_alot_utf8.vim | 16 + src/testdir/test_arabic.vim | 613 + src/testdir/test_arglist.vim | 482 + src/testdir/test_assert.vim | 218 + src/testdir/test_assign.vim | 53 + src/testdir/test_autochdir.vim | 27 + src/testdir/test_autocmd.vim | 1418 ++ src/testdir/test_autoload.vim | 17 + src/testdir/test_backspace_opt.vim | 59 + src/testdir/test_backup.vim | 58 + src/testdir/test_behave.vim | 29 + src/testdir/test_blob.vim | 320 + src/testdir/test_blockedit.vim | 33 + src/testdir/test_breakindent.vim | 617 + src/testdir/test_bufline.vim | 141 + src/testdir/test_bufwintabinfo.vim | 141 + src/testdir/test_cd.vim | 67 + src/testdir/test_cdo.vim | 205 + src/testdir/test_changedtick.vim | 57 + src/testdir/test_changelist.vim | 48 + src/testdir/test_channel.py | 269 + src/testdir/test_channel.vim | 2028 +++ src/testdir/test_channel_pipe.py | 63 + src/testdir/test_channel_write.py | 18 + src/testdir/test_charsearch.vim | 62 + src/testdir/test_charsearch_utf8.vim | 19 + src/testdir/test_cindent.vim | 105 + src/testdir/test_clientserver.vim | 104 + src/testdir/test_close_count.vim | 174 + src/testdir/test_cmdline.vim | 612 + src/testdir/test_command_count.vim | 196 + src/testdir/test_comparators.vim | 9 + src/testdir/test_compiler.vim | 54 + src/testdir/test_conceal.vim | 136 + src/testdir/test_crypt.vim | 113 + src/testdir/test_cscope.vim | 302 + src/testdir/test_cursor_func.vim | 68 + src/testdir/test_curswant.vim | 23 + src/testdir/test_delete.vim | 107 + src/testdir/test_diffmode.vim | 915 ++ src/testdir/test_digraph.vim | 477 + src/testdir/test_display.vim | 69 + src/testdir/test_edit.vim | 1438 ++ src/testdir/test_erasebackword.vim | 19 + src/testdir/test_escaped_glob.vim | 33 + src/testdir/test_eval.in | 249 + src/testdir/test_eval.ok | Bin 0 -> 11279 bytes src/testdir/test_eval_func.vim | 10 + src/testdir/test_eval_stuff.vim | 96 + src/testdir/test_ex_equal.vim | 32 + src/testdir/test_ex_undo.vim | 19 + src/testdir/test_ex_z.vim | 85 + src/testdir/test_exec_while_if.vim | 53 + src/testdir/test_execute_func.vim | 80 + src/testdir/test_exists.vim | 321 + src/testdir/test_exists_autocmd.vim | 26 + src/testdir/test_exit.vim | 57 + src/testdir/test_expand.vim | 49 + src/testdir/test_expand_dllpath.vim | 32 + src/testdir/test_expand_func.vim | 66 + src/testdir/test_expr.vim | 514 + src/testdir/test_expr_utf8.vim | 34 + src/testdir/test_farsi.vim | 133 + src/testdir/test_feedkeys.vim | 14 + src/testdir/test_file_perm.vim | 24 + src/testdir/test_file_size.vim | 58 + src/testdir/test_filechanged.vim | 146 + src/testdir/test_fileformat.vim | 33 + src/testdir/test_filetype.vim | 603 + src/testdir/test_filter_cmd.vim | 132 + src/testdir/test_filter_map.vim | 86 + src/testdir/test_find_complete.vim | 163 + src/testdir/test_findfile.vim | 185 + src/testdir/test_fixeol.vim | 48 + src/testdir/test_float_func.vim | 331 + src/testdir/test_fnameescape.vim | 21 + src/testdir/test_fnamemodify.vim | 53 + src/testdir/test_fold.vim | 743 + src/testdir/test_functions.vim | 1248 ++ src/testdir/test_ga.vim | 33 + src/testdir/test_getcwd.vim | 112 + src/testdir/test_getvar.vim | 104 + src/testdir/test_gf.vim | 61 + src/testdir/test_glob2regpat.vim | 30 + src/testdir/test_global.vim | 20 + src/testdir/test_gn.vim | 153 + src/testdir/test_goto.vim | 373 + src/testdir/test_gui.vim | 779 + src/testdir/test_gui_init.vim | 61 + src/testdir/test_hardcopy.vim | 89 + src/testdir/test_help.vim | 51 + src/testdir/test_help_tagjump.vim | 260 + src/testdir/test_hide.vim | 97 + src/testdir/test_highlight.vim | 554 + src/testdir/test_history.vim | 111 + src/testdir/test_hlsearch.vim | 65 + src/testdir/test_iminsert.vim | 27 + src/testdir/test_increment.vim | 781 + src/testdir/test_increment_dbcs.vim | 27 + src/testdir/test_ins_complete.vim | 312 + src/testdir/test_job_fails.vim | 16 + src/testdir/test_join.vim | 35 + src/testdir/test_json.vim | 291 + src/testdir/test_jumplist.vim | 62 + src/testdir/test_jumps.vim | 11 + src/testdir/test_lambda.vim | 292 + src/testdir/test_langmap.vim | 28 + src/testdir/test_largefile.vim | 34 + src/testdir/test_let.vim | 27 + src/testdir/test_lineending.vim | 19 + src/testdir/test_lispwords.vim | 82 + src/testdir/test_listchars.vim | 115 + src/testdir/test_listdict.vim | 653 + src/testdir/test_listlbr.vim | 235 + src/testdir/test_listlbr_utf8.vim | 271 + src/testdir/test_lua.vim | 575 + src/testdir/test_makeencoding.py | 67 + src/testdir/test_makeencoding.vim | 103 + src/testdir/test_man.vim | 60 + src/testdir/test_maparg.vim | 58 + src/testdir/test_mapping.vim | 320 + src/testdir/test_marks.vim | 176 + src/testdir/test_match.vim | 241 + src/testdir/test_matchadd_conceal.vim | 279 + src/testdir/test_matchadd_conceal_utf8.vim | 43 + src/testdir/test_menu.vim | 66 + src/testdir/test_messages.vim | 94 + src/testdir/test_mksession.vim | 498 + src/testdir/test_mksession_utf8.vim | 105 + src/testdir/test_modeline.vim | 94 + src/testdir/test_move.vim | 40 + src/testdir/test_nested_function.vim | 67 + src/testdir/test_netbeans.py | 91 + src/testdir/test_netbeans.vim | 86 + src/testdir/test_normal.vim | 2544 +++ src/testdir/test_number.vim | 254 + src/testdir/test_options.vim | 520 + src/testdir/test_packadd.vim | 357 + src/testdir/test_partial.vim | 386 + src/testdir/test_paste.vim | 112 + src/testdir/test_perl.vim | 286 + src/testdir/test_plus_arg_edit.vim | 34 + src/testdir/test_popup.vim | 899 ++ src/testdir/test_preview.vim | 13 + src/testdir/test_profile.vim | 520 + src/testdir/test_prompt_buffer.vim | 105 + src/testdir/test_put.vim | 103 + src/testdir/test_python2.vim | 65 + src/testdir/test_python3.vim | 65 + src/testdir/test_pyx2.vim | 74 + src/testdir/test_pyx3.vim | 74 + src/testdir/test_quickfix.vim | 3901 +++++ src/testdir/test_quotestar.vim | 154 + src/testdir/test_recover.vim | 62 + src/testdir/test_regex_char_classes.vim | 295 + src/testdir/test_regexp_latin.vim | 86 + src/testdir/test_regexp_utf8.vim | 208 + src/testdir/test_registers.vim | 65 + src/testdir/test_reltime.vim | 26 + src/testdir/test_retab.vim | 77 + src/testdir/test_ruby.vim | 379 + src/testdir/test_scriptnames.vim | 26 + src/testdir/test_scroll_opt.vim | 36 + src/testdir/test_scrollbind.vim | 32 + src/testdir/test_search.vim | 1189 ++ src/testdir/test_searchpos.vim | 28 + src/testdir/test_set.vim | 27 + src/testdir/test_sha256.vim | 22 + src/testdir/test_short_sleep.py | 11 + src/testdir/test_shortpathname.vim | 70 + src/testdir/test_signs.vim | 1340 ++ src/testdir/test_smartindent.vim | 41 + src/testdir/test_sort.vim | 1253 ++ src/testdir/test_source.vim | 38 + src/testdir/test_source_utf8.vim | 60 + src/testdir/test_spell.vim | 887 + src/testdir/test_startup.vim | 539 + src/testdir/test_startup_utf8.vim | 83 + src/testdir/test_stat.vim | 185 + src/testdir/test_statusline.vim | 343 + src/testdir/test_substitute.vim | 502 + src/testdir/test_suspend.vim | 55 + src/testdir/test_swap.vim | 166 + src/testdir/test_syn_attr.vim | 818 + src/testdir/test_syntax.vim | 585 + src/testdir/test_system.vim | 92 + src/testdir/test_tab.vim | 90 + src/testdir/test_tabline.vim | 72 + src/testdir/test_tabpage.vim | 558 + src/testdir/test_tagcase.vim | 73 + src/testdir/test_tagjump.vim | 369 + src/testdir/test_taglist.vim | 86 + src/testdir/test_tcl.vim | 680 + src/testdir/test_termencoding.vim | 37 + src/testdir/test_terminal.vim | 1758 ++ src/testdir/test_terminal_fail.vim | 21 + src/testdir/test_textformat.vim | 491 + src/testdir/test_textobjects.vim | 259 + src/testdir/test_textprop.vim | 552 + src/testdir/test_timers.vim | 313 + src/testdir/test_true_false.vim | 150 + src/testdir/test_undo.vim | 444 + src/testdir/test_unlet.vim | 57 + src/testdir/test_user_func.vim | 96 + src/testdir/test_usercommands.vim | 306 + src/testdir/test_utf8.vim | 62 + src/testdir/test_utf8_comparisons.vim | 91 + src/testdir/test_vartabs.vim | 374 + src/testdir/test_viminfo.vim | 534 + src/testdir/test_vimscript.vim | 1448 ++ src/testdir/test_virtualedit.vim | 75 + src/testdir/test_visual.vim | 399 + src/testdir/test_winbar.vim | 23 + src/testdir/test_winbuf_close.vim | 160 + src/testdir/test_window_cmd.vim | 618 + src/testdir/test_window_id.vim | 123 + src/testdir/test_windows_home.vim | 121 + src/testdir/test_wordcount.vim | 104 + src/testdir/test_writefile.vim | 152 + src/testdir/test_xxd.vim | 183 + src/testdir/unix.vim | 13 + src/testdir/view_util.vim | 51 + src/testdir/vms.vim | 6 + src/textprop.c | 1081 ++ src/toolbar.phi | 1283 ++ src/toolcheck | 36 + src/tools.bmp | Bin 0 -> 4660 bytes src/typemap | 14 + src/ui.c | 3533 ++++ src/undo.c | 3592 +++++ src/uninstal.c | 428 + src/userfunc.c | 3956 +++++ src/version.c | 3205 ++++ src/version.h | 42 + src/vim.def | 4 + src/vim.h | 2609 +++ src/vim.ico | Bin 0 -> 1078 bytes src/vim.rc | 123 + src/vim.tlb | Bin 0 -> 1792 bytes src/vim_alert.ico | Bin 0 -> 1078 bytes src/vim_error.ico | Bin 0 -> 1078 bytes src/vim_icon.xbm | 14 + src/vim_info.ico | Bin 0 -> 1078 bytes src/vim_mask.xbm | 14 + src/vim_quest.ico | Bin 0 -> 1078 bytes src/vimio.h | 19 + src/vimrun.c | 96 + src/vimtutor | 74 + src/which.sh | 12 + src/winclip.c | 799 + src/window.c | 7286 +++++++++ src/xdiff/COPYING | 504 + src/xdiff/README.txt | 16 + src/xdiff/xdiff.h | 147 + src/xdiff/xdiffi.c | 1043 ++ src/xdiff/xdiffi.h | 64 + src/xdiff/xemit.c | 332 + src/xdiff/xemit.h | 36 + src/xdiff/xhistogram.c | 386 + src/xdiff/xinclude.h | 65 + src/xdiff/xmacros.h | 54 + src/xdiff/xpatience.c | 393 + src/xdiff/xprepare.c | 483 + src/xdiff/xprepare.h | 34 + src/xdiff/xtypes.h | 67 + src/xdiff/xutils.c | 425 + src/xdiff/xutils.h | 47 + src/xpm/COPYRIGHT | 31 + src/xpm/README.txt | 30 + src/xpm/include/simx.h | 139 + src/xpm/include/xpm.h | 501 + src/xpm/x64/lib-vc14/libXpm.lib | Bin 0 -> 121904 bytes src/xpm/x64/lib/libXpm.a | Bin 0 -> 73758 bytes src/xpm/x64/lib/libXpm.lib | Bin 0 -> 195582 bytes src/xpm/x86/lib-vc14/libXpm.lib | Bin 0 -> 86422 bytes src/xpm/x86/lib/libXpm.a | Bin 0 -> 66414 bytes src/xpm/x86/lib/libXpm.lib | Bin 0 -> 139938 bytes src/xpm_w32.c | 56 + src/xpm_w32.h | 7 + src/xxd/Make_amiga.mak | 18 + src/xxd/Make_bc5.mak | 18 + src/xxd/Make_ming.mak | 28 + src/xxd/Make_mvc.mak | 19 + src/xxd/Make_vms.mms | 69 + src/xxd/Makefile | 7 + src/xxd/xxd.c | 890 + 964 files changed, 853027 insertions(+) create mode 100644 src/GvimExt/GvimExt.reg create mode 100644 src/GvimExt/Make_bc5.mak create mode 100644 src/GvimExt/Make_ming.mak create mode 100644 src/GvimExt/Makefile create mode 100644 src/GvimExt/README.txt create mode 100644 src/GvimExt/gvimext.cpp create mode 100644 src/GvimExt/gvimext.def create mode 100644 src/GvimExt/gvimext.h create mode 100644 src/GvimExt/gvimext.inf create mode 100644 src/GvimExt/gvimext.rc create mode 100644 src/GvimExt/gvimext_ming.def create mode 100644 src/GvimExt/gvimext_ming.rc create mode 100644 src/GvimExt/resource.h create mode 100644 src/GvimExt/uninst.bat create mode 100644 src/INSTALL create mode 100644 src/INSTALLami.txt create mode 100644 src/INSTALLmac.txt create mode 100644 src/INSTALLpc.txt create mode 100644 src/INSTALLvms.txt create mode 100644 src/INSTALLx.txt create mode 100644 src/Make_all.mak create mode 100644 src/Make_bc5.mak create mode 100644 src/Make_cyg.mak create mode 100644 src/Make_cyg_ming.mak create mode 100644 src/Make_dice.mak create mode 100644 src/Make_dvc.mak create mode 100644 src/Make_ivc.mak create mode 100644 src/Make_manx.mak create mode 100644 src/Make_ming.mak create mode 100644 src/Make_mint.mak create mode 100644 src/Make_morph.mak create mode 100644 src/Make_mvc.mak create mode 100644 src/Make_sas.mak create mode 100644 src/Make_vms.mms create mode 100644 src/Makefile create mode 100644 src/README.txt create mode 100644 src/VisVim/Commands.cpp create mode 100644 src/VisVim/Commands.h create mode 100644 src/VisVim/DSAddIn.cpp create mode 100644 src/VisVim/DSAddIn.h create mode 100644 src/VisVim/OleAut.cpp create mode 100644 src/VisVim/OleAut.h create mode 100644 src/VisVim/README_VisVim.txt create mode 100644 src/VisVim/Reg.cpp create mode 100644 src/VisVim/Register.bat create mode 100644 src/VisVim/Res/ToolbarL.bmp create mode 100644 src/VisVim/Res/ToolbarM.bmp create mode 100644 src/VisVim/Resource.h create mode 100644 src/VisVim/StdAfx.cpp create mode 100644 src/VisVim/StdAfx.h create mode 100644 src/VisVim/UnRegist.bat create mode 100644 src/VisVim/VisVim.cpp create mode 100644 src/VisVim/VisVim.def create mode 100644 src/VisVim/VisVim.dll create mode 100644 src/VisVim/VisVim.h create mode 100644 src/VisVim/VisVim.mak create mode 100644 src/VisVim/VisVim.odl create mode 100644 src/VisVim/VisVim.rc create mode 100644 src/VisVim/VsReadMe.txt create mode 100644 src/alloc.h create mode 100644 src/arabic.c create mode 100644 src/arabic.h create mode 100644 src/ascii.h create mode 100755 src/auto/configure create mode 100644 src/autocmd.c create mode 100644 src/beval.c create mode 100644 src/beval.h create mode 100644 src/bigvim.bat create mode 100644 src/bigvim64.bat create mode 100644 src/blob.c create mode 100644 src/blowfish.c create mode 100644 src/buffer.c create mode 100644 src/channel.c create mode 100644 src/charset.c create mode 100644 src/config.h.in create mode 100644 src/config.mk.dist create mode 100644 src/config.mk.in create mode 100755 src/configure create mode 100644 src/configure.ac create mode 100644 src/create_cmdidxs.vim create mode 100644 src/crypt.c create mode 100644 src/crypt_zip.c create mode 100644 src/dehqx.py create mode 100644 src/dict.c create mode 100644 src/diff.c create mode 100644 src/digraph.c create mode 100644 src/dimm.idl create mode 100644 src/dlldata.c create mode 100644 src/dosinst.c create mode 100644 src/dosinst.h create mode 100644 src/edit.c create mode 100644 src/eval.c create mode 100644 src/evalfunc.c create mode 100644 src/ex_cmdidxs.h create mode 100644 src/ex_cmds.c create mode 100644 src/ex_cmds.h create mode 100644 src/ex_cmds2.c create mode 100644 src/ex_docmd.c create mode 100644 src/ex_eval.c create mode 100644 src/ex_getln.c create mode 100644 src/farsi.c create mode 100644 src/farsi.h create mode 100644 src/feature.h create mode 100644 src/fileio.c create mode 100644 src/fold.c create mode 100644 src/getchar.c create mode 100644 src/glbl_ime.cpp create mode 100644 src/glbl_ime.h create mode 100644 src/globals.h create mode 100644 src/gui.c create mode 100644 src/gui.h create mode 100644 src/gui_at_fs.c create mode 100644 src/gui_at_sb.c create mode 100644 src/gui_at_sb.h create mode 100644 src/gui_athena.c create mode 100644 src/gui_beval.c create mode 100644 src/gui_dwrite.cpp create mode 100644 src/gui_dwrite.h create mode 100644 src/gui_gtk.c create mode 100644 src/gui_gtk_f.c create mode 100644 src/gui_gtk_f.h create mode 100644 src/gui_gtk_res.xml create mode 100644 src/gui_gtk_vms.h create mode 100644 src/gui_gtk_x11.c create mode 100644 src/gui_mac.c create mode 100644 src/gui_motif.c create mode 100644 src/gui_photon.c create mode 100644 src/gui_w32.c create mode 100644 src/gui_w32_rc.h create mode 100644 src/gui_x11.c create mode 100644 src/gui_x11_pm.h create mode 100644 src/gui_xmdlg.c create mode 100644 src/gui_xmebw.c create mode 100644 src/gui_xmebw.h create mode 100644 src/gui_xmebwp.h create mode 100644 src/gvim.exe.mnf create mode 100644 src/gvimtutor create mode 100644 src/hangulin.c create mode 100644 src/hardcopy.c create mode 100644 src/hashtab.c create mode 100644 src/if_cscope.c create mode 100644 src/if_cscope.h create mode 100644 src/if_lua.c create mode 100644 src/if_mzsch.c create mode 100644 src/if_mzsch.h create mode 100644 src/if_ole.cpp create mode 100644 src/if_ole.h create mode 100644 src/if_ole.idl create mode 100644 src/if_perl.xs create mode 100644 src/if_perl_msvc/stdbool.h create mode 100644 src/if_perlsfio.c create mode 100644 src/if_py_both.h create mode 100644 src/if_python.c create mode 100644 src/if_python3.c create mode 100644 src/if_ruby.c create mode 100644 src/if_tcl.c create mode 100644 src/if_xcmdsrv.c create mode 100644 src/iid_ole.c create mode 100644 src/indent.c create mode 100644 src/infplist.xml create mode 100644 src/install-sh create mode 100755 src/installman.sh create mode 100644 src/installml.sh create mode 100644 src/iscygpty.c create mode 100644 src/iscygpty.h create mode 100644 src/json.c create mode 100644 src/json_test.c create mode 100644 src/keymap.h create mode 100644 src/kword_test.c create mode 100644 src/libvterm/.bzrignore create mode 100644 src/libvterm/.gitignore create mode 100644 src/libvterm/LICENSE create mode 100644 src/libvterm/Makefile create mode 100644 src/libvterm/README create mode 100644 src/libvterm/bin/unterm.c create mode 100644 src/libvterm/bin/vterm-ctrl.c create mode 100644 src/libvterm/bin/vterm-dump.c create mode 100644 src/libvterm/doc/URLs create mode 100644 src/libvterm/doc/seqs.txt create mode 100644 src/libvterm/include/vterm.h create mode 100644 src/libvterm/include/vterm_keycodes.h create mode 100644 src/libvterm/src/encoding.c create mode 100644 src/libvterm/src/encoding/DECdrawing.inc create mode 100644 src/libvterm/src/encoding/DECdrawing.tbl create mode 100644 src/libvterm/src/encoding/uk.inc create mode 100644 src/libvterm/src/encoding/uk.tbl create mode 100644 src/libvterm/src/keyboard.c create mode 100644 src/libvterm/src/mouse.c create mode 100644 src/libvterm/src/parser.c create mode 100644 src/libvterm/src/pen.c create mode 100644 src/libvterm/src/rect.h create mode 100644 src/libvterm/src/state.c create mode 100644 src/libvterm/src/termscreen.c create mode 100644 src/libvterm/src/unicode.c create mode 100644 src/libvterm/src/utf8.h create mode 100644 src/libvterm/src/vterm.c create mode 100644 src/libvterm/src/vterm_internal.h create mode 100644 src/libvterm/t/02parser.test create mode 100644 src/libvterm/t/03encoding_utf8.test create mode 100644 src/libvterm/t/10state_putglyph.test create mode 100644 src/libvterm/t/11state_movecursor.test create mode 100644 src/libvterm/t/12state_scroll.test create mode 100644 src/libvterm/t/13state_edit.test create mode 100644 src/libvterm/t/14state_encoding.test create mode 100644 src/libvterm/t/15state_mode.test create mode 100644 src/libvterm/t/16state_resize.test create mode 100644 src/libvterm/t/17state_mouse.test create mode 100644 src/libvterm/t/18state_termprops.test create mode 100644 src/libvterm/t/20state_wrapping.test create mode 100644 src/libvterm/t/21state_tabstops.test create mode 100644 src/libvterm/t/22state_save.test create mode 100644 src/libvterm/t/25state_input.test create mode 100644 src/libvterm/t/26state_query.test create mode 100644 src/libvterm/t/27state_reset.test create mode 100644 src/libvterm/t/28state_dbl_wh.test create mode 100644 src/libvterm/t/29state_fallback.test create mode 100644 src/libvterm/t/30pen.test create mode 100644 src/libvterm/t/40screen_ascii.test create mode 100644 src/libvterm/t/41screen_unicode.test create mode 100644 src/libvterm/t/42screen_damage.test create mode 100644 src/libvterm/t/43screen_resize.test create mode 100644 src/libvterm/t/44screen_pen.test create mode 100644 src/libvterm/t/45screen_protect.test create mode 100644 src/libvterm/t/46screen_extent.test create mode 100644 src/libvterm/t/47screen_dbl_wh.test create mode 100644 src/libvterm/t/48screen_termprops.test create mode 100644 src/libvterm/t/90vttest_01-movement-1.test create mode 100644 src/libvterm/t/90vttest_01-movement-2.test create mode 100644 src/libvterm/t/90vttest_01-movement-3.test create mode 100644 src/libvterm/t/90vttest_01-movement-4.test create mode 100644 src/libvterm/t/90vttest_02-screen-1.test create mode 100644 src/libvterm/t/90vttest_02-screen-2.test create mode 100644 src/libvterm/t/90vttest_02-screen-3.test create mode 100644 src/libvterm/t/90vttest_02-screen-4.test create mode 100644 src/libvterm/t/92lp1640917.test create mode 100644 src/libvterm/t/harness.c create mode 100644 src/libvterm/t/run-test.pl create mode 100644 src/libvterm/tbl2inc_c.pl create mode 100644 src/libvterm/vterm.pc.in create mode 100644 src/link.390 create mode 100755 src/link.sh create mode 100644 src/list.c create mode 100644 src/macros.h create mode 100644 src/main.c create mode 100644 src/mark.c create mode 100644 src/mbyte.c create mode 100644 src/memfile.c create mode 100644 src/memfile_test.c create mode 100644 src/memline.c create mode 100644 src/menu.c create mode 100644 src/message.c create mode 100644 src/message_test.c create mode 100644 src/misc1.c create mode 100644 src/misc2.c create mode 100644 src/move.c create mode 100644 src/msvc2008.bat create mode 100644 src/msvc2010.bat create mode 100644 src/msvc2015.bat create mode 100644 src/msvcsetup.bat create mode 100755 src/msys32.bat create mode 100755 src/msys64.bat create mode 100644 src/mysign create mode 100644 src/nbdebug.c create mode 100644 src/nbdebug.h create mode 100644 src/netbeans.c create mode 100644 src/normal.c create mode 100644 src/ops.c create mode 100644 src/option.c create mode 100644 src/option.h create mode 100644 src/os_amiga.c create mode 100644 src/os_amiga.h create mode 100644 src/os_beos.c create mode 100644 src/os_beos.h create mode 100644 src/os_beos.rsrc create mode 100644 src/os_dos.h create mode 100644 src/os_mac.h create mode 100644 src/os_mac.rsr.hqx create mode 100644 src/os_mac_conv.c create mode 100644 src/os_mac_rsrc/app.icns create mode 100644 src/os_mac_rsrc/doc-txt.icns create mode 100644 src/os_mac_rsrc/doc.icns create mode 100644 src/os_macosx.m create mode 100644 src/os_mint.h create mode 100644 src/os_mswin.c create mode 100644 src/os_qnx.c create mode 100644 src/os_qnx.h create mode 100644 src/os_unix.c create mode 100644 src/os_unix.h create mode 100644 src/os_unixx.h create mode 100644 src/os_vms.c create mode 100644 src/os_vms_conf.h create mode 100644 src/os_vms_fix.com create mode 100644 src/os_vms_mms.c create mode 100644 src/os_w32dll.c create mode 100644 src/os_w32exe.c create mode 100644 src/os_win32.c create mode 100644 src/os_win32.h create mode 100755 src/osdef.sh create mode 100644 src/osdef1.h.in create mode 100644 src/osdef2.h.in create mode 100755 src/pathdef.sh create mode 100644 src/po/Make_all.mak create mode 100644 src/po/Make_cyg.mak create mode 100644 src/po/Make_ming.mak create mode 100644 src/po/Make_mvc.mak create mode 100644 src/po/Makefile create mode 100644 src/po/README.txt create mode 100644 src/po/README_mingw.txt create mode 100644 src/po/README_mvc.txt create mode 100644 src/po/af.po create mode 100644 src/po/ca.po create mode 100644 src/po/check.vim create mode 100644 src/po/cleanup.vim create mode 100644 src/po/cs.cp1250.po create mode 100644 src/po/cs.po create mode 100644 src/po/da.po create mode 100644 src/po/de.po create mode 100644 src/po/en_GB.po create mode 100644 src/po/eo.po create mode 100644 src/po/es.po create mode 100644 src/po/fi.po create mode 100644 src/po/fr.po create mode 100644 src/po/ga.po create mode 100644 src/po/it.po create mode 100644 src/po/ja.euc-jp.po create mode 100644 src/po/ja.po create mode 100644 src/po/ja.sjis.po create mode 100644 src/po/ko.UTF-8.po create mode 100644 src/po/ko.po create mode 100644 src/po/lv.po create mode 100644 src/po/nb.po create mode 100644 src/po/nl.po create mode 100644 src/po/no.po create mode 100644 src/po/pl.UTF-8.po create mode 100644 src/po/pl.cp1250.po create mode 100644 src/po/pl.po create mode 100644 src/po/pt_BR.po create mode 100644 src/po/ru.cp1251.po create mode 100644 src/po/ru.po create mode 100644 src/po/sjiscorr.c create mode 100644 src/po/sk.cp1250.po create mode 100644 src/po/sk.po create mode 100644 src/po/sr.po create mode 100644 src/po/sv.po create mode 100644 src/po/uk.cp1251.po create mode 100644 src/po/uk.po create mode 100644 src/po/vi.po create mode 100644 src/po/zh_CN.UTF-8.po create mode 100644 src/po/zh_CN.cp936.po create mode 100644 src/po/zh_CN.po create mode 100644 src/po/zh_TW.UTF-8.po create mode 100644 src/po/zh_TW.po create mode 100644 src/popupmnu.c create mode 100644 src/proto.h create mode 100644 src/proto/arabic.pro create mode 100644 src/proto/autocmd.pro create mode 100644 src/proto/beval.pro create mode 100644 src/proto/blob.pro create mode 100644 src/proto/blowfish.pro create mode 100644 src/proto/buffer.pro create mode 100644 src/proto/channel.pro create mode 100644 src/proto/charset.pro create mode 100644 src/proto/crypt.pro create mode 100644 src/proto/crypt_zip.pro create mode 100644 src/proto/dict.pro create mode 100644 src/proto/diff.pro create mode 100644 src/proto/digraph.pro create mode 100644 src/proto/edit.pro create mode 100644 src/proto/eval.pro create mode 100644 src/proto/evalfunc.pro create mode 100644 src/proto/ex_cmds.pro create mode 100644 src/proto/ex_cmds2.pro create mode 100644 src/proto/ex_docmd.pro create mode 100644 src/proto/ex_eval.pro create mode 100644 src/proto/ex_getln.pro create mode 100644 src/proto/farsi.pro create mode 100644 src/proto/fileio.pro create mode 100644 src/proto/fold.pro create mode 100644 src/proto/getchar.pro create mode 100644 src/proto/gui.pro create mode 100644 src/proto/gui_athena.pro create mode 100644 src/proto/gui_beval.pro create mode 100644 src/proto/gui_gtk.pro create mode 100644 src/proto/gui_gtk_gresources.pro create mode 100644 src/proto/gui_gtk_x11.pro create mode 100644 src/proto/gui_mac.pro create mode 100644 src/proto/gui_motif.pro create mode 100644 src/proto/gui_photon.pro create mode 100644 src/proto/gui_w32.pro create mode 100644 src/proto/gui_x11.pro create mode 100644 src/proto/gui_xmdlg.pro create mode 100644 src/proto/hangulin.pro create mode 100644 src/proto/hardcopy.pro create mode 100644 src/proto/hashtab.pro create mode 100644 src/proto/if_cscope.pro create mode 100644 src/proto/if_lua.pro create mode 100644 src/proto/if_mzsch.pro create mode 100644 src/proto/if_ole.pro create mode 100644 src/proto/if_perl.pro create mode 100644 src/proto/if_perlsfio.pro create mode 100644 src/proto/if_python.pro create mode 100644 src/proto/if_python3.pro create mode 100644 src/proto/if_ruby.pro create mode 100644 src/proto/if_tcl.pro create mode 100644 src/proto/if_xcmdsrv.pro create mode 100644 src/proto/indent.pro create mode 100644 src/proto/json.pro create mode 100644 src/proto/list.pro create mode 100644 src/proto/main.pro create mode 100644 src/proto/mark.pro create mode 100644 src/proto/mbyte.pro create mode 100644 src/proto/memfile.pro create mode 100644 src/proto/memline.pro create mode 100644 src/proto/menu.pro create mode 100644 src/proto/message.pro create mode 100644 src/proto/misc1.pro create mode 100644 src/proto/misc2.pro create mode 100644 src/proto/move.pro create mode 100644 src/proto/netbeans.pro create mode 100644 src/proto/normal.pro create mode 100644 src/proto/ops.pro create mode 100644 src/proto/option.pro create mode 100644 src/proto/os_amiga.pro create mode 100644 src/proto/os_beos.pro create mode 100644 src/proto/os_mac_conv.pro create mode 100644 src/proto/os_mswin.pro create mode 100644 src/proto/os_qnx.pro create mode 100644 src/proto/os_unix.pro create mode 100644 src/proto/os_vms.pro create mode 100644 src/proto/os_win32.pro create mode 100644 src/proto/popupmnu.pro create mode 100644 src/proto/pty.pro create mode 100644 src/proto/quickfix.pro create mode 100644 src/proto/regexp.pro create mode 100644 src/proto/screen.pro create mode 100644 src/proto/search.pro create mode 100644 src/proto/sha256.pro create mode 100644 src/proto/sign.pro create mode 100644 src/proto/spell.pro create mode 100644 src/proto/spellfile.pro create mode 100644 src/proto/syntax.pro create mode 100644 src/proto/tag.pro create mode 100644 src/proto/term.pro create mode 100644 src/proto/terminal.pro create mode 100644 src/proto/termlib.pro create mode 100644 src/proto/textprop.pro create mode 100644 src/proto/ui.pro create mode 100644 src/proto/undo.pro create mode 100644 src/proto/userfunc.pro create mode 100644 src/proto/version.pro create mode 100644 src/proto/winclip.pro create mode 100644 src/proto/window.pro create mode 100644 src/protodef.h create mode 100644 src/pty.c create mode 100644 src/quickfix.c create mode 100644 src/regexp.c create mode 100644 src/regexp.h create mode 100644 src/regexp_nfa.c create mode 100644 src/screen.c create mode 100644 src/search.c create mode 100644 src/sha256.c create mode 100644 src/sign.c create mode 100644 src/spell.c create mode 100644 src/spell.h create mode 100644 src/spellfile.c create mode 100644 src/structs.h create mode 100644 src/syntax.c create mode 100644 src/tag.c create mode 100644 src/tearoff.bmp create mode 100644 src/tee/Make_mvc.mak create mode 100644 src/tee/Makefile create mode 100644 src/tee/tee.c create mode 100644 src/term.c create mode 100644 src/term.h create mode 100644 src/terminal.c create mode 100644 src/termlib.c create mode 100644 src/testdir/Make_all.mak create mode 100644 src/testdir/Make_amiga.mak create mode 100644 src/testdir/Make_dos.mak create mode 100644 src/testdir/Make_ming.mak create mode 100644 src/testdir/Make_vms.mms create mode 100644 src/testdir/Makefile create mode 100644 src/testdir/README.txt create mode 100644 src/testdir/amiga.vim create mode 100644 src/testdir/bench_re_freeze.in create mode 100644 src/testdir/bench_re_freeze.vim create mode 100644 src/testdir/color_ramp.vim create mode 100644 src/testdir/dos.vim create mode 100644 src/testdir/dotest.in create mode 100644 src/testdir/dumps/Test_conceal_cul_01.dump create mode 100644 src/testdir/dumps/Test_conceal_cul_02.dump create mode 100644 src/testdir/dumps/Test_conceal_cul_03.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_01.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_02.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_03.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_04.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_05.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_06c.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_06i.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_06n.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_06v.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_07c.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_07i.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_07n.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_07v.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_08c.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_08i.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_08n.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_08v.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_09c.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_09i.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_09n.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_09v.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_10.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_11.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_12.dump create mode 100644 src/testdir/dumps/Test_conceal_two_windows_13.dump create mode 100644 src/testdir/dumps/Test_cursorline_yank_01.dump create mode 100644 src/testdir/dumps/Test_diff_01.dump create mode 100644 src/testdir/dumps/Test_diff_02.dump create mode 100644 src/testdir/dumps/Test_diff_03.dump create mode 100644 src/testdir/dumps/Test_diff_04.dump create mode 100644 src/testdir/dumps/Test_diff_05.dump create mode 100644 src/testdir/dumps/Test_diff_06.dump create mode 100644 src/testdir/dumps/Test_diff_07.dump create mode 100644 src/testdir/dumps/Test_diff_08.dump create mode 100644 src/testdir/dumps/Test_diff_09.dump create mode 100644 src/testdir/dumps/Test_diff_10.dump create mode 100644 src/testdir/dumps/Test_diff_11.dump create mode 100644 src/testdir/dumps/Test_diff_12.dump create mode 100644 src/testdir/dumps/Test_diff_13.dump create mode 100644 src/testdir/dumps/Test_diff_14.dump create mode 100644 src/testdir/dumps/Test_diff_15.dump create mode 100644 src/testdir/dumps/Test_diff_16.dump create mode 100644 src/testdir/dumps/Test_diff_17.dump create mode 100644 src/testdir/dumps/Test_diff_18.dump create mode 100644 src/testdir/dumps/Test_diff_19.dump create mode 100644 src/testdir/dumps/Test_diff_20.dump create mode 100644 src/testdir/dumps/Test_diff_of_diff_01.dump create mode 100644 src/testdir/dumps/Test_diff_with_cursorline_01.dump create mode 100644 src/testdir/dumps/Test_diff_with_cursorline_02.dump create mode 100644 src/testdir/dumps/Test_diff_with_cursorline_03.dump create mode 100644 src/testdir/dumps/Test_folds_with_rnu_01.dump create mode 100644 src/testdir/dumps/Test_folds_with_rnu_02.dump create mode 100644 src/testdir/dumps/Test_incsearch_scrolling_01.dump create mode 100644 src/testdir/dumps/Test_incsearch_search_01.dump create mode 100644 src/testdir/dumps/Test_incsearch_search_02.dump create mode 100644 src/testdir/dumps/Test_incsearch_sort_01.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_01.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_02.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_03.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_04.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_05.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_06.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_07.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_08.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_09.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_10.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_11.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_12.dump create mode 100644 src/testdir/dumps/Test_incsearch_substitute_13.dump create mode 100644 src/testdir/dumps/Test_incsearch_vimgrep_01.dump create mode 100644 src/testdir/dumps/Test_incsearch_vimgrep_02.dump create mode 100644 src/testdir/dumps/Test_incsearch_vimgrep_03.dump create mode 100644 src/testdir/dumps/Test_incsearch_vimgrep_04.dump create mode 100644 src/testdir/dumps/Test_incsearch_vimgrep_05.dump create mode 100644 src/testdir/dumps/Test_popup_and_previewwindow_01.dump create mode 100644 src/testdir/dumps/Test_popup_command_01.dump create mode 100644 src/testdir/dumps/Test_popup_command_02.dump create mode 100644 src/testdir/dumps/Test_popup_command_03.dump create mode 100644 src/testdir/dumps/Test_popup_position_01.dump create mode 100644 src/testdir/dumps/Test_popup_position_02.dump create mode 100644 src/testdir/dumps/Test_popup_position_03.dump create mode 100644 src/testdir/dumps/Test_popup_position_04.dump create mode 100644 src/testdir/dumps/Test_syntax_c_01.dump create mode 100644 src/testdir/dumps/Test_tenc_euc_jp_01.dump create mode 100644 src/testdir/dumps/Test_textprop_01.dump create mode 100644 src/testdir/gen_opt_test.vim create mode 100644 src/testdir/gui_init.vim create mode 100644 src/testdir/gui_preinit.vim create mode 100644 src/testdir/if_ver-1.vim create mode 100644 src/testdir/if_ver-2.vim create mode 100644 src/testdir/lsan-suppress.txt create mode 100644 src/testdir/python2/module.py create mode 100644 src/testdir/python3/module.py create mode 100644 src/testdir/python_after/after.py create mode 100644 src/testdir/python_before/before.py create mode 100644 src/testdir/python_before/before_1.py create mode 100644 src/testdir/python_before/before_2.py create mode 100644 src/testdir/pythonx/failing.py create mode 100644 src/testdir/pythonx/failing_import.py create mode 100644 src/testdir/pythonx/module.py create mode 100644 src/testdir/pythonx/modulex.py create mode 100644 src/testdir/pythonx/topmodule/__init__.py create mode 100644 src/testdir/pythonx/topmodule/submodule/__init__.py create mode 100644 src/testdir/pythonx/topmodule/submodule/subsubmodule/__init__.py create mode 100644 src/testdir/pythonx/topmodule/submodule/subsubmodule/subsubsubmodule.py create mode 100644 src/testdir/pyxfile/py2_magic.py create mode 100644 src/testdir/pyxfile/py2_shebang.py create mode 100644 src/testdir/pyxfile/py3_magic.py create mode 100644 src/testdir/pyxfile/py3_shebang.py create mode 100644 src/testdir/pyxfile/pyx.py create mode 100644 src/testdir/runtest.vim create mode 100644 src/testdir/samples/quickfix.txt create mode 100644 src/testdir/samples/re.freeze.txt create mode 100644 src/testdir/samples/test000 create mode 100644 src/testdir/sautest/autoload/Test104.vim create mode 100644 src/testdir/sautest/autoload/foo.vim create mode 100644 src/testdir/sautest/autoload/footest.vim create mode 100644 src/testdir/sautest/autoload/globone.vim create mode 100644 src/testdir/sautest/autoload/globtwo.vim create mode 100644 src/testdir/sautest/autoload/sourced.vim create mode 100644 src/testdir/screendump.vim create mode 100644 src/testdir/setup.vim create mode 100644 src/testdir/setup_gui.vim create mode 100644 src/testdir/shared.vim create mode 100644 src/testdir/test1.in create mode 100644 src/testdir/test1.ok create mode 100644 src/testdir/test108.in create mode 100644 src/testdir/test108.ok create mode 100644 src/testdir/test11.in create mode 100644 src/testdir/test11.ok create mode 100644 src/testdir/test14.in create mode 100644 src/testdir/test14.ok create mode 100644 src/testdir/test17.in create mode 100644 src/testdir/test17.ok create mode 100644 src/testdir/test17a.in create mode 100644 src/testdir/test29.in create mode 100644 src/testdir/test29.ok create mode 100644 src/testdir/test3.in create mode 100644 src/testdir/test3.ok create mode 100644 src/testdir/test30.in create mode 100644 src/testdir/test30.ok create mode 100644 src/testdir/test37.in create mode 100644 src/testdir/test37.ok create mode 100644 src/testdir/test39.in create mode 100644 src/testdir/test39.ok create mode 100644 src/testdir/test42.in create mode 100644 src/testdir/test42.ok create mode 100644 src/testdir/test44.in create mode 100644 src/testdir/test44.ok create mode 100644 src/testdir/test48.in create mode 100644 src/testdir/test48.ok create mode 100644 src/testdir/test49.in create mode 100644 src/testdir/test49.ok create mode 100644 src/testdir/test49.vim create mode 100644 src/testdir/test52.in create mode 100644 src/testdir/test52.ok create mode 100644 src/testdir/test59.in create mode 100644 src/testdir/test59.ok create mode 100644 src/testdir/test64.in create mode 100644 src/testdir/test64.ok create mode 100644 src/testdir/test69.in create mode 100644 src/testdir/test69.ok create mode 100644 src/testdir/test70.in create mode 100644 src/testdir/test70.ok create mode 100644 src/testdir/test72.in create mode 100644 src/testdir/test72.ok create mode 100644 src/testdir/test77a.com create mode 100644 src/testdir/test77a.in create mode 100644 src/testdir/test77a.ok create mode 100644 src/testdir/test83-tags2 create mode 100644 src/testdir/test83-tags3 create mode 100644 src/testdir/test85.ok create mode 100644 src/testdir/test86.in create mode 100644 src/testdir/test86.ok create mode 100644 src/testdir/test87.in create mode 100644 src/testdir/test87.ok create mode 100644 src/testdir/test88.in create mode 100644 src/testdir/test88.ok create mode 100644 src/testdir/test94.in create mode 100644 src/testdir/test94.ok create mode 100644 src/testdir/test95.in create mode 100644 src/testdir/test95.ok create mode 100644 src/testdir/test99.in create mode 100644 src/testdir/test99.ok create mode 100644 src/testdir/test_alot.vim create mode 100644 src/testdir/test_alot_latin.vim create mode 100644 src/testdir/test_alot_utf8.vim create mode 100644 src/testdir/test_arabic.vim create mode 100644 src/testdir/test_arglist.vim create mode 100644 src/testdir/test_assert.vim create mode 100644 src/testdir/test_assign.vim create mode 100644 src/testdir/test_autochdir.vim create mode 100644 src/testdir/test_autocmd.vim create mode 100644 src/testdir/test_autoload.vim create mode 100644 src/testdir/test_backspace_opt.vim create mode 100644 src/testdir/test_backup.vim create mode 100644 src/testdir/test_behave.vim create mode 100644 src/testdir/test_blob.vim create mode 100644 src/testdir/test_blockedit.vim create mode 100644 src/testdir/test_breakindent.vim create mode 100644 src/testdir/test_bufline.vim create mode 100644 src/testdir/test_bufwintabinfo.vim create mode 100644 src/testdir/test_cd.vim create mode 100644 src/testdir/test_cdo.vim create mode 100644 src/testdir/test_changedtick.vim create mode 100644 src/testdir/test_changelist.vim create mode 100644 src/testdir/test_channel.py create mode 100644 src/testdir/test_channel.vim create mode 100644 src/testdir/test_channel_pipe.py create mode 100644 src/testdir/test_channel_write.py create mode 100644 src/testdir/test_charsearch.vim create mode 100644 src/testdir/test_charsearch_utf8.vim create mode 100644 src/testdir/test_cindent.vim create mode 100644 src/testdir/test_clientserver.vim create mode 100644 src/testdir/test_close_count.vim create mode 100644 src/testdir/test_cmdline.vim create mode 100644 src/testdir/test_command_count.vim create mode 100644 src/testdir/test_comparators.vim create mode 100644 src/testdir/test_compiler.vim create mode 100644 src/testdir/test_conceal.vim create mode 100644 src/testdir/test_crypt.vim create mode 100644 src/testdir/test_cscope.vim create mode 100644 src/testdir/test_cursor_func.vim create mode 100644 src/testdir/test_curswant.vim create mode 100644 src/testdir/test_delete.vim create mode 100644 src/testdir/test_diffmode.vim create mode 100644 src/testdir/test_digraph.vim create mode 100644 src/testdir/test_display.vim create mode 100644 src/testdir/test_edit.vim create mode 100644 src/testdir/test_erasebackword.vim create mode 100644 src/testdir/test_escaped_glob.vim create mode 100644 src/testdir/test_eval.in create mode 100644 src/testdir/test_eval.ok create mode 100644 src/testdir/test_eval_func.vim create mode 100644 src/testdir/test_eval_stuff.vim create mode 100644 src/testdir/test_ex_equal.vim create mode 100644 src/testdir/test_ex_undo.vim create mode 100644 src/testdir/test_ex_z.vim create mode 100644 src/testdir/test_exec_while_if.vim create mode 100644 src/testdir/test_execute_func.vim create mode 100644 src/testdir/test_exists.vim create mode 100644 src/testdir/test_exists_autocmd.vim create mode 100644 src/testdir/test_exit.vim create mode 100644 src/testdir/test_expand.vim create mode 100644 src/testdir/test_expand_dllpath.vim create mode 100644 src/testdir/test_expand_func.vim create mode 100644 src/testdir/test_expr.vim create mode 100644 src/testdir/test_expr_utf8.vim create mode 100644 src/testdir/test_farsi.vim create mode 100644 src/testdir/test_feedkeys.vim create mode 100644 src/testdir/test_file_perm.vim create mode 100644 src/testdir/test_file_size.vim create mode 100644 src/testdir/test_filechanged.vim create mode 100644 src/testdir/test_fileformat.vim create mode 100644 src/testdir/test_filetype.vim create mode 100644 src/testdir/test_filter_cmd.vim create mode 100644 src/testdir/test_filter_map.vim create mode 100644 src/testdir/test_find_complete.vim create mode 100644 src/testdir/test_findfile.vim create mode 100644 src/testdir/test_fixeol.vim create mode 100644 src/testdir/test_float_func.vim create mode 100644 src/testdir/test_fnameescape.vim create mode 100644 src/testdir/test_fnamemodify.vim create mode 100644 src/testdir/test_fold.vim create mode 100644 src/testdir/test_functions.vim create mode 100644 src/testdir/test_ga.vim create mode 100644 src/testdir/test_getcwd.vim create mode 100644 src/testdir/test_getvar.vim create mode 100644 src/testdir/test_gf.vim create mode 100644 src/testdir/test_glob2regpat.vim create mode 100644 src/testdir/test_global.vim create mode 100644 src/testdir/test_gn.vim create mode 100644 src/testdir/test_goto.vim create mode 100644 src/testdir/test_gui.vim create mode 100644 src/testdir/test_gui_init.vim create mode 100644 src/testdir/test_hardcopy.vim create mode 100644 src/testdir/test_help.vim create mode 100644 src/testdir/test_help_tagjump.vim create mode 100644 src/testdir/test_hide.vim create mode 100644 src/testdir/test_highlight.vim create mode 100644 src/testdir/test_history.vim create mode 100644 src/testdir/test_hlsearch.vim create mode 100644 src/testdir/test_iminsert.vim create mode 100644 src/testdir/test_increment.vim create mode 100644 src/testdir/test_increment_dbcs.vim create mode 100644 src/testdir/test_ins_complete.vim create mode 100644 src/testdir/test_job_fails.vim create mode 100644 src/testdir/test_join.vim create mode 100644 src/testdir/test_json.vim create mode 100644 src/testdir/test_jumplist.vim create mode 100644 src/testdir/test_jumps.vim create mode 100644 src/testdir/test_lambda.vim create mode 100644 src/testdir/test_langmap.vim create mode 100644 src/testdir/test_largefile.vim create mode 100644 src/testdir/test_let.vim create mode 100644 src/testdir/test_lineending.vim create mode 100644 src/testdir/test_lispwords.vim create mode 100644 src/testdir/test_listchars.vim create mode 100644 src/testdir/test_listdict.vim create mode 100644 src/testdir/test_listlbr.vim create mode 100644 src/testdir/test_listlbr_utf8.vim create mode 100644 src/testdir/test_lua.vim create mode 100644 src/testdir/test_makeencoding.py create mode 100644 src/testdir/test_makeencoding.vim create mode 100644 src/testdir/test_man.vim create mode 100644 src/testdir/test_maparg.vim create mode 100644 src/testdir/test_mapping.vim create mode 100644 src/testdir/test_marks.vim create mode 100644 src/testdir/test_match.vim create mode 100644 src/testdir/test_matchadd_conceal.vim create mode 100644 src/testdir/test_matchadd_conceal_utf8.vim create mode 100644 src/testdir/test_menu.vim create mode 100644 src/testdir/test_messages.vim create mode 100644 src/testdir/test_mksession.vim create mode 100644 src/testdir/test_mksession_utf8.vim create mode 100644 src/testdir/test_modeline.vim create mode 100644 src/testdir/test_move.vim create mode 100644 src/testdir/test_nested_function.vim create mode 100644 src/testdir/test_netbeans.py create mode 100644 src/testdir/test_netbeans.vim create mode 100644 src/testdir/test_normal.vim create mode 100644 src/testdir/test_number.vim create mode 100644 src/testdir/test_options.vim create mode 100644 src/testdir/test_packadd.vim create mode 100644 src/testdir/test_partial.vim create mode 100644 src/testdir/test_paste.vim create mode 100644 src/testdir/test_perl.vim create mode 100644 src/testdir/test_plus_arg_edit.vim create mode 100644 src/testdir/test_popup.vim create mode 100644 src/testdir/test_preview.vim create mode 100644 src/testdir/test_profile.vim create mode 100644 src/testdir/test_prompt_buffer.vim create mode 100644 src/testdir/test_put.vim create mode 100644 src/testdir/test_python2.vim create mode 100644 src/testdir/test_python3.vim create mode 100644 src/testdir/test_pyx2.vim create mode 100644 src/testdir/test_pyx3.vim create mode 100644 src/testdir/test_quickfix.vim create mode 100644 src/testdir/test_quotestar.vim create mode 100644 src/testdir/test_recover.vim create mode 100644 src/testdir/test_regex_char_classes.vim create mode 100644 src/testdir/test_regexp_latin.vim create mode 100644 src/testdir/test_regexp_utf8.vim create mode 100644 src/testdir/test_registers.vim create mode 100644 src/testdir/test_reltime.vim create mode 100644 src/testdir/test_retab.vim create mode 100644 src/testdir/test_ruby.vim create mode 100644 src/testdir/test_scriptnames.vim create mode 100644 src/testdir/test_scroll_opt.vim create mode 100644 src/testdir/test_scrollbind.vim create mode 100644 src/testdir/test_search.vim create mode 100644 src/testdir/test_searchpos.vim create mode 100644 src/testdir/test_set.vim create mode 100644 src/testdir/test_sha256.vim create mode 100644 src/testdir/test_short_sleep.py create mode 100644 src/testdir/test_shortpathname.vim create mode 100644 src/testdir/test_signs.vim create mode 100644 src/testdir/test_smartindent.vim create mode 100644 src/testdir/test_sort.vim create mode 100644 src/testdir/test_source.vim create mode 100644 src/testdir/test_source_utf8.vim create mode 100644 src/testdir/test_spell.vim create mode 100644 src/testdir/test_startup.vim create mode 100644 src/testdir/test_startup_utf8.vim create mode 100644 src/testdir/test_stat.vim create mode 100644 src/testdir/test_statusline.vim create mode 100644 src/testdir/test_substitute.vim create mode 100644 src/testdir/test_suspend.vim create mode 100644 src/testdir/test_swap.vim create mode 100644 src/testdir/test_syn_attr.vim create mode 100644 src/testdir/test_syntax.vim create mode 100644 src/testdir/test_system.vim create mode 100644 src/testdir/test_tab.vim create mode 100644 src/testdir/test_tabline.vim create mode 100644 src/testdir/test_tabpage.vim create mode 100644 src/testdir/test_tagcase.vim create mode 100644 src/testdir/test_tagjump.vim create mode 100644 src/testdir/test_taglist.vim create mode 100644 src/testdir/test_tcl.vim create mode 100644 src/testdir/test_termencoding.vim create mode 100644 src/testdir/test_terminal.vim create mode 100644 src/testdir/test_terminal_fail.vim create mode 100644 src/testdir/test_textformat.vim create mode 100644 src/testdir/test_textobjects.vim create mode 100644 src/testdir/test_textprop.vim create mode 100644 src/testdir/test_timers.vim create mode 100644 src/testdir/test_true_false.vim create mode 100644 src/testdir/test_undo.vim create mode 100644 src/testdir/test_unlet.vim create mode 100644 src/testdir/test_user_func.vim create mode 100644 src/testdir/test_usercommands.vim create mode 100644 src/testdir/test_utf8.vim create mode 100644 src/testdir/test_utf8_comparisons.vim create mode 100644 src/testdir/test_vartabs.vim create mode 100644 src/testdir/test_viminfo.vim create mode 100644 src/testdir/test_vimscript.vim create mode 100644 src/testdir/test_virtualedit.vim create mode 100644 src/testdir/test_visual.vim create mode 100644 src/testdir/test_winbar.vim create mode 100644 src/testdir/test_winbuf_close.vim create mode 100644 src/testdir/test_window_cmd.vim create mode 100644 src/testdir/test_window_id.vim create mode 100644 src/testdir/test_windows_home.vim create mode 100644 src/testdir/test_wordcount.vim create mode 100644 src/testdir/test_writefile.vim create mode 100644 src/testdir/test_xxd.vim create mode 100644 src/testdir/unix.vim create mode 100644 src/testdir/view_util.vim create mode 100644 src/testdir/vms.vim create mode 100644 src/textprop.c create mode 100644 src/toolbar.phi create mode 100755 src/toolcheck create mode 100644 src/tools.bmp create mode 100644 src/typemap create mode 100644 src/ui.c create mode 100644 src/undo.c create mode 100644 src/uninstal.c create mode 100644 src/userfunc.c create mode 100644 src/version.c create mode 100644 src/version.h create mode 100644 src/vim.def create mode 100644 src/vim.h create mode 100644 src/vim.ico create mode 100644 src/vim.rc create mode 100644 src/vim.tlb create mode 100644 src/vim_alert.ico create mode 100644 src/vim_error.ico create mode 100644 src/vim_icon.xbm create mode 100644 src/vim_info.ico create mode 100644 src/vim_mask.xbm create mode 100644 src/vim_quest.ico create mode 100644 src/vimio.h create mode 100644 src/vimrun.c create mode 100755 src/vimtutor create mode 100755 src/which.sh create mode 100644 src/winclip.c create mode 100644 src/window.c create mode 100644 src/xdiff/COPYING create mode 100644 src/xdiff/README.txt create mode 100644 src/xdiff/xdiff.h create mode 100644 src/xdiff/xdiffi.c create mode 100644 src/xdiff/xdiffi.h create mode 100644 src/xdiff/xemit.c create mode 100644 src/xdiff/xemit.h create mode 100644 src/xdiff/xhistogram.c create mode 100644 src/xdiff/xinclude.h create mode 100644 src/xdiff/xmacros.h create mode 100644 src/xdiff/xpatience.c create mode 100644 src/xdiff/xprepare.c create mode 100644 src/xdiff/xprepare.h create mode 100644 src/xdiff/xtypes.h create mode 100644 src/xdiff/xutils.c create mode 100644 src/xdiff/xutils.h create mode 100644 src/xpm/COPYRIGHT create mode 100644 src/xpm/README.txt create mode 100644 src/xpm/include/simx.h create mode 100644 src/xpm/include/xpm.h create mode 100644 src/xpm/x64/lib-vc14/libXpm.lib create mode 100644 src/xpm/x64/lib/libXpm.a create mode 100644 src/xpm/x64/lib/libXpm.lib create mode 100644 src/xpm/x86/lib-vc14/libXpm.lib create mode 100644 src/xpm/x86/lib/libXpm.a create mode 100644 src/xpm/x86/lib/libXpm.lib create mode 100644 src/xpm_w32.c create mode 100644 src/xpm_w32.h create mode 100644 src/xxd/Make_amiga.mak create mode 100644 src/xxd/Make_bc5.mak create mode 100644 src/xxd/Make_ming.mak create mode 100644 src/xxd/Make_mvc.mak create mode 100644 src/xxd/Make_vms.mms create mode 100644 src/xxd/Makefile create mode 100644 src/xxd/xxd.c (limited to 'src') diff --git a/src/GvimExt/GvimExt.reg b/src/GvimExt/GvimExt.reg new file mode 100644 index 0000000..2b8ebf0 --- /dev/null +++ b/src/GvimExt/GvimExt.reg @@ -0,0 +1,20 @@ +REGEDIT4 + +[HKEY_CLASSES_ROOT\CLSID\{51EEE242-AD87-11d3-9C1E-0090278BBD99}] + @="Vim Shell Extension" +[HKEY_CLASSES_ROOT\CLSID\{51EEE242-AD87-11d3-9C1E-0090278BBD99}\InProcServer32] + @="gvimext.dll" + "ThreadingModel"="Apartment" + +[HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\gvim] + @="{51EEE242-AD87-11d3-9C1E-0090278BBD99}" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved] + "{51EEE242-AD87-11d3-9C1E-0090278BBD99}"="Vim Shell Extension" + +[HKEY_LOCAL_MACHINE\Software\Vim\Gvim] + "path"="gvim.exe" + +[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\Vim 8.1] + "DisplayName"="Vim 8.1: Edit with Vim popup menu entry" + "UninstallString"="uninstal.exe" diff --git a/src/GvimExt/Make_bc5.mak b/src/GvimExt/Make_bc5.mak new file mode 100644 index 0000000..363c6d6 --- /dev/null +++ b/src/GvimExt/Make_bc5.mak @@ -0,0 +1,43 @@ +### USEDLL no for statically linked version of run-time, yes for DLL runtime +### BOR path to root of Borland C install (c:\bc5) + +### (requires cc3250.dll be available in %PATH%) +!if ("$(USEDLL)"=="") +USEDLL = no +!endif + +### BOR: root of the BC installation +!if ("$(BOR)"=="") +BOR = c:\bc5 +!endif + +CC = $(BOR)\bin\Bcc32 +BRC = $(BOR)\bin\brc32 +LINK = $(BOR)\BIN\ILink32 +INCLUDE = $(BOR)\include;. +LIB = $(BOR)\lib + +!if ("$(USEDLL)"=="yes") +RT_DEF = -D_RTLDLL +RT_LIB = cw32i.lib +!else +RT_DEF = +RT_LIB = cw32.lib +!endif + + +all : gvimext.dll + +gvimext.obj : gvimext.cpp gvimext.h + $(CC) -tWD -I$(INCLUDE) -c -DFEAT_GETTEXT $(RT_DEF) -w- gvimext.cpp + +gvimext.res : gvimext.rc + $(BRC) -r gvimext.rc + +gvimext.dll : gvimext.obj gvimext.res + $(LINK) -L$(LIB) -aa gvimext.obj, gvimext.dll, , c0d32.obj $(RT_LIB) import32.lib, gvimext.def, gvimext.res + +clean : + -@del gvimext.obj + -@del gvimext.res + -@del gvimext.dll diff --git a/src/GvimExt/Make_ming.mak b/src/GvimExt/Make_ming.mak new file mode 100644 index 0000000..a6ab3ae --- /dev/null +++ b/src/GvimExt/Make_ming.mak @@ -0,0 +1,81 @@ +# Project: gvimext +# Generates gvimext.dll with gcc. +# To be used with MingW and Cygwin. +# +# Originally, the DLL base address was fixed: -Wl,--image-base=0x1C000000 +# Now it is allocated dymanically by the linker by evaluating all DLLs +# already loaded in memory. The binary image contains as well information +# for automatic pseudo-rebasing, if needed by the system. ALV 2004-02-29 + +# If cross-compiling set this to yes, else set it to no +CROSS = no +#CROSS = yes +# For the old MinGW 2.95 (the one you get e.g. with debian woody) +# set the following variable to yes and check if the executables are +# really named that way. +# If you have a newer MinGW or you are using cygwin set it to no and +# check also the executables +MINGWOLD = no + +# Link against the shared versions of libgcc/libstdc++ by default. Set +# STATIC_STDCPLUS to "yes" to link against static versions instead. +STATIC_STDCPLUS=no +#STATIC_STDCPLUS=yes + +# Note: -static-libstdc++ is not available until gcc 4.5.x. +LDFLAGS += -shared +ifeq (yes, $(STATIC_STDCPLUS)) +LDFLAGS += -static-libgcc -static-libstdc++ +endif + +ifeq ($(CROSS),yes) +DEL = rm +ifeq ($(MINGWOLD),yes) +CXXFLAGS := -O2 -fvtable-thunks +else +CXXFLAGS := -O2 +endif +else +CXXFLAGS := -O2 +ifneq (sh.exe, $(SHELL)) +DEL = rm +else +DEL = del +endif +endif +# Set the default $(WINVER) to make it work with WinXP. +ifndef WINVER +WINVER = 0x0501 +endif +CXX := $(CROSS_COMPILE)g++ +WINDRES := $(CROSS_COMPILE)windres +WINDRES_CXX = $(CXX) +WINDRES_FLAGS = --preprocessor="$(WINDRES_CXX) -E -xc" -DRC_INVOKED +LIBS := -luuid -lgdi32 +RES := gvimext.res +DEFFILE = gvimext_ming.def +OBJ := gvimext.o + +DLL := gvimext.dll + +.PHONY: all all-before all-after clean clean-custom + +all: all-before $(DLL) all-after + +$(DLL): $(OBJ) $(RES) $(DEFFILE) + $(CXX) $(LDFLAGS) $(CXXFLAGS) -s -o $@ \ + -Wl,--enable-auto-image-base \ + -Wl,--enable-auto-import \ + -Wl,--whole-archive \ + $^ \ + -Wl,--no-whole-archive \ + $(LIBS) + +gvimext.o: gvimext.cpp + $(CXX) $(CXXFLAGS) -DFEAT_GETTEXT -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) -c $? -o $@ + +$(RES): gvimext_ming.rc + $(WINDRES) $(WINDRES_FLAGS) --input-format=rc --output-format=coff -DMING $? -o $@ + +clean: clean-custom + -$(DEL) $(OBJ) $(RES) $(DLL) diff --git a/src/GvimExt/Makefile b/src/GvimExt/Makefile new file mode 100644 index 0000000..27ff953 --- /dev/null +++ b/src/GvimExt/Makefile @@ -0,0 +1,80 @@ +# Makefile for GvimExt, using MSVC +# Options: +# DEBUG=yes Build debug version (for VC7 and maybe later) +# CPUARG= /arch:IA32/AVX/etc, call from main makefile to set +# automatically from CPUNR +# + +TARGETOS = WINNT + +!ifndef APPVER +APPVER = 5.01 +!endif + +!if "$(DEBUG)" != "yes" +NODEBUG = 1 +!endif + +!ifdef PROCESSOR_ARCHITECTURE +# On Windows NT +! ifndef CPU +CPU = i386 +! if !defined(PLATFORM) && defined(TARGET_CPU) +PLATFORM = $(TARGET_CPU) +! endif +! ifdef PLATFORM +! if ("$(PLATFORM)" == "x64") || ("$(PLATFORM)" == "X64") +CPU = AMD64 +! elseif ("$(PLATFORM)" != "x86") && ("$(PLATFORM)" != "X86") +! error *** ERROR Unknown target platform "$(PLATFORM)". Make aborted. +! endif +! endif +! endif +!else +CPU = i386 +!endif + +!ifdef SDK_INCLUDE_DIR +!include $(SDK_INCLUDE_DIR)\Win32.mak +!elseif "$(USE_WIN32MAK)"=="yes" +!include +!else +cc = cl +link = link +rc = rc +cflags = -nologo -c +lflags = -incremental:no -nologo +rcflags = /r +olelibsdll = ole32.lib uuid.lib oleaut32.lib user32.lib gdi32.lib advapi32.lib +!endif + +# include CPUARG +cflags = $(cflags) $(CPUARG) + +SUBSYSTEM = console +!if "$(SUBSYSTEM_VER)" != "" +SUBSYSTEM = $(SUBSYSTEM),$(SUBSYSTEM_VER) +!endif + +all: gvimext.dll + +gvimext.dll: gvimext.obj \ + gvimext.res + $(link) $(lflags) -dll -def:gvimext.def -base:0x1C000000 -out:$*.dll $** $(olelibsdll) shell32.lib comctl32.lib -subsystem:$(SUBSYSTEM) + if exist $*.dll.manifest mt -nologo -manifest $*.dll.manifest -outputresource:$*.dll;2 + +gvimext.obj: gvimext.h + +.cpp.obj: + $(cc) $(cflags) -DFEAT_GETTEXT $(cvarsmt) $*.cpp + +gvimext.res: gvimext.rc + $(rc) /nologo $(rcflags) $(rcvars) gvimext.rc + +clean: + - if exist gvimext.dll del gvimext.dll + - if exist gvimext.lib del gvimext.lib + - if exist gvimext.exp del gvimext.exp + - if exist gvimext.obj del gvimext.obj + - if exist gvimext.res del gvimext.res + - if exist gvimext.dll.manifest del gvimext.dll.manifest diff --git a/src/GvimExt/README.txt b/src/GvimExt/README.txt new file mode 100644 index 0000000..4a75cfa --- /dev/null +++ b/src/GvimExt/README.txt @@ -0,0 +1,94 @@ +README.txt for the gvimext DLL. + +Written by Tianmiao Hu. Edited by Bram Moolenaar. + + +INSTALLATION + +To install the "Edit with Vim" popup menu entry, it is recommended to use the +"install.exe" program. It will ask you a few questions and install the needed +registry entries. + +In special situations you might want to make changes by hand. Check these +items: +- The gvimext.dll, gvim.exe and uninstal.exe either need to be in the search + path, or you have to set the full path in the registry entries. You could + move the gvimext.dll to the "windows\system" or "windows\system32" + directory, where the other DLL files are. +- You can find the names of the used registry entries in the file + "GvimExt.reg". You can edit this file to add the paths. To install the + registry entries, right-click the gvimext.reg file and choose the "merge" + menu option. +- The registry key [HKEY_LOCAL_MACHINE\Software\Vim\Gvim] is used by the + gvimext.dll. The value "path" specifies the location of "gvim.exe". If + gvim.exe is in the search path, the path can be omitted. The value "lang" + can be used to set the language, for example "de" for German. If "lang" is + omitted, the language set for Windows will be used. + +It is the preferred method to keep gvim.exe with the runtime files, so that +Vim will find them (also the translated menu items are there). + + +UNINSTALLATION + +To uninstall the "Edit with Vim" popup menu entry, it is recommended to use +the "uninstal.exe" program. + +In special situations you might want to uninstall by hand: +- Open the registry by running regedit.exe. +- Delete all the keys listed in GvimExt.reg, except this one: + [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved] + For this key, only delete one value: + "{51EEE242-AD87-11d3-9C1E-0090278BBD99}"="Vim Shell Extension" +- Delete the gvimext.dll, if you want. You might need to reboot the machine + in order to remove this file. A quick way is to log off and re-login. + +Another method is by using the uninst.bat script: + uninst gvimext.inf +This batch file will remove all the registry keys from the system. Then you +can remove the gvimext.dll file. +Note: In order for this batch file to work, you must have two system files: +rundll32.exe and setupapi.dll. I believe you will have rundll32.exe in your +system. I know windows nt 4.0 with the service pack 4 has setupapi.dll. My +windows 95 has setupapi.dll. I find that the internet explorer 4.0 comes with +the setupapi.dll in file Ie4_5.cab. + +If you do encounter problems running this script, then probably you need to +modify the uninst.bat to suit to your system. Basically, you must find out +where are the locations for your rundll32.exe and setupapi.dll files. In +windows nt, both files are under c:\winnt\system32 directory. In my windows 95 +system, I got setupapi.dll at c:\windows\system and rundll32.exe at +c:\windows. So you might want to try something like: + rundll32.exe c:\windows\system\setupapi.dll,InstallHinfSection DefaultUninstall 128 %1 +where %1 can be substituted by gvimext.inf + + +THE SOURCE CODE + +I have provided the source code here in hope that gvim users around world can +further enhance this little dll. I believe the only thing you need to change +is gvimext.cpp file. The important two functions you need to look at are +QueryContextMenu and InvokeCommand. You can modify right-click menus in the +QueryContextMenu function and invoke gvim in the InvokeCommand function. Note +the selected files can be accessed from the DragQueryFile function. I am not +familiar with the invoking options for gvim. I believe there are some +improvements that can be made on that side. + +I use MS Visual C++ 6.0's nmake to make the gvimext.dll. I don't have a +chance to try earlier versions of MSVC. The files that are required for build +are: + gvimext.cpp + gvimext.h + gvimext.def + gvimext.rc + resource.h + Makefile + +To compile the DLL from the command line: + vcvars32 + nmake -f Makefile + +If you did something interesting to this dll, please let me know +@ tianmiao@acm.org. + +Happy vimming!!! diff --git a/src/GvimExt/gvimext.cpp b/src/GvimExt/gvimext.cpp new file mode 100644 index 0000000..b9d9d91 --- /dev/null +++ b/src/GvimExt/gvimext.cpp @@ -0,0 +1,1090 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved gvimext by Tianmiao Hu + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + */ + +/* + * gvimext is a DLL which is used for the "Edit with Vim" context menu + * extension. It implements a MS defined interface with the Shell. + * + * If you have any questions or any suggestions concerning gvimext, please + * contact Tianmiao Hu: tianmiao@acm.org. + */ + +#include "gvimext.h" + +#ifdef __BORLANDC__ +# include +# ifndef _strnicmp +# define _strnicmp(a, b, c) strnicmp((a), (b), (c)) +# endif +#else +static char *searchpath(char *name); +#endif + +// Always get an error while putting the following stuff to the +// gvimext.h file as class protected variables, give up and +// declare them as global stuff +FORMATETC fmte = {CF_HDROP, + (DVTARGETDEVICE FAR *)NULL, + DVASPECT_CONTENT, + -1, + TYMED_HGLOBAL + }; +STGMEDIUM medium; +HRESULT hres = 0; +UINT cbFiles = 0; + +/* The buffers size used to be MAX_PATH (260 bytes), but that's not always + * enough */ +#define BUFSIZE 1100 + +// +// Get the name of the Gvim executable to use, with the path. +// When "runtime" is non-zero, we were called to find the runtime directory. +// Returns the path in name[BUFSIZE]. It's empty when it fails. +// + static void +getGvimName(char *name, int runtime) +{ + HKEY keyhandle; + DWORD hlen; + + // Get the location of gvim from the registry. + name[0] = 0; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0, + KEY_READ, &keyhandle) == ERROR_SUCCESS) + { + hlen = BUFSIZE; + if (RegQueryValueEx(keyhandle, "path", 0, NULL, (BYTE *)name, &hlen) + != ERROR_SUCCESS) + name[0] = 0; + else + name[hlen] = 0; + RegCloseKey(keyhandle); + } + + // Registry didn't work, use the search path. + if (name[0] == 0) + strcpy(name, searchpath((char *)"gvim.exe")); + + if (!runtime) + { + // Only when looking for the executable, not the runtime dir, we can + // search for the batch file or a name without a path. + if (name[0] == 0) + strcpy(name, searchpath((char *)"gvim.bat")); + if (name[0] == 0) + strcpy(name, "gvim"); // finds gvim.bat or gvim.exe + } +} + + static void +getGvimInvocation(char *name, int runtime) +{ + getGvimName(name, runtime); + // avoid that Vim tries to expand wildcards in the file names + strcat(name, " --literal"); +} + + static void +getGvimInvocationW(wchar_t *nameW) +{ + char *name; + + name = (char *)malloc(BUFSIZE); + getGvimInvocation(name, 0); + mbstowcs(nameW, name, BUFSIZE); + free(name); +} + +// +// Get the Vim runtime directory into buf[BUFSIZE]. +// The result is empty when it failed. +// When it works, the path ends in a slash or backslash. +// + static void +getRuntimeDir(char *buf) +{ + int idx; + + getGvimName(buf, 1); + if (buf[0] != 0) + { + // When no path found, use the search path to expand it. + if (strchr(buf, '/') == NULL && strchr(buf, '\\') == NULL) + strcpy(buf, searchpath(buf)); + + // remove "gvim.exe" from the end + for (idx = (int)strlen(buf) - 1; idx >= 0; idx--) + if (buf[idx] == '\\' || buf[idx] == '/') + { + buf[idx + 1] = 0; + break; + } + } +} + +HBITMAP IconToBitmap(HICON hIcon, HBRUSH hBackground, int width, int height) +{ + HDC hDC = GetDC(NULL); + HDC hMemDC = CreateCompatibleDC(hDC); + HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, width, height); + HBITMAP hResultBmp = NULL; + HGDIOBJ hOrgBMP = SelectObject(hMemDC, hMemBmp); + + DrawIconEx(hMemDC, 0, 0, hIcon, width, height, 0, hBackground, DI_NORMAL); + + hResultBmp = hMemBmp; + hMemBmp = NULL; + + SelectObject(hMemDC, hOrgBMP); + DeleteDC(hMemDC); + ReleaseDC(NULL, hDC); + DestroyIcon(hIcon); + return hResultBmp; +} + +// +// GETTEXT: translated messages and menu entries +// +#ifndef FEAT_GETTEXT +# define _(x) x +#else +# define _(x) (*dyn_libintl_gettext)(x) +# define VIMPACKAGE "vim" +# ifndef GETTEXT_DLL +# define GETTEXT_DLL "libintl.dll" +# define GETTEXT_DLL_ALT "libintl-8.dll" +# endif + +// Dummy functions +static char *null_libintl_gettext(const char *); +static char *null_libintl_textdomain(const char *); +static char *null_libintl_bindtextdomain(const char *, const char *); +static int dyn_libintl_init(char *dir); +static void dyn_libintl_end(void); + +static wchar_t *oldenv = NULL; +static HINSTANCE hLibintlDLL = 0; +static char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext; +static char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain; +static char *(*dyn_libintl_bindtextdomain)(const char *, const char *) + = null_libintl_bindtextdomain; + +// +// Attempt to load libintl.dll. If it doesn't work, use dummy functions. +// "dir" is the directory where the libintl.dll might be. +// Return 1 for success, 0 for failure. +// + static int +dyn_libintl_init(char *dir) +{ + int i; + static struct + { + char *name; + FARPROC *ptr; + } libintl_entry[] = + { + {(char *)"gettext", (FARPROC*)&dyn_libintl_gettext}, + {(char *)"textdomain", (FARPROC*)&dyn_libintl_textdomain}, + {(char *)"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain}, + {NULL, NULL} + }; + DWORD len, len2; + LPWSTR buf = NULL; + LPWSTR buf2 = NULL; + + // No need to initialize twice. + if (hLibintlDLL) + return 1; + + // Load gettext library from $VIMRUNTIME\GvimExt{64,32} directory. + // Add the directory to $PATH temporarily. + len = GetEnvironmentVariableW(L"PATH", NULL, 0); + len2 = MAX_PATH + 1 + len; + buf = (LPWSTR)malloc(len * sizeof(WCHAR)); + buf2 = (LPWSTR)malloc(len2 * sizeof(WCHAR)); + if (buf != NULL && buf2 != NULL) + { + GetEnvironmentVariableW(L"PATH", buf, len); +#ifdef _WIN64 + _snwprintf(buf2, len2, L"%S\\GvimExt64;%s", dir, buf); +#else + _snwprintf(buf2, len2, L"%S\\GvimExt32;%s", dir, buf); +#endif + SetEnvironmentVariableW(L"PATH", buf2); + hLibintlDLL = LoadLibrary(GETTEXT_DLL); +#ifdef GETTEXT_DLL_ALT + if (!hLibintlDLL) + hLibintlDLL = LoadLibrary(GETTEXT_DLL_ALT); +#endif + SetEnvironmentVariableW(L"PATH", buf); + } + free(buf); + free(buf2); + if (!hLibintlDLL) + return 0; + + // Get the addresses of the functions we need. + for (i = 0; libintl_entry[i].name != NULL + && libintl_entry[i].ptr != NULL; ++i) + { + if ((*libintl_entry[i].ptr = GetProcAddress(hLibintlDLL, + libintl_entry[i].name)) == NULL) + { + dyn_libintl_end(); + return 0; + } + } + return 1; +} + + static void +dyn_libintl_end(void) +{ + if (hLibintlDLL) + FreeLibrary(hLibintlDLL); + hLibintlDLL = NULL; + dyn_libintl_gettext = null_libintl_gettext; + dyn_libintl_textdomain = null_libintl_textdomain; + dyn_libintl_bindtextdomain = null_libintl_bindtextdomain; +} + + static char * +null_libintl_gettext(const char *msgid) +{ + return (char *)msgid; +} + + static char * +null_libintl_bindtextdomain(const char * /* domainname */, const char * /* dirname */) +{ + return NULL; +} + + static char * +null_libintl_textdomain(const char* /* domainname */) +{ + return NULL; +} + +// +// Setup for translating strings. +// + static void +dyn_gettext_load(void) +{ + char szBuff[BUFSIZE]; + char szLang[BUFSIZE]; + DWORD len; + HKEY keyhandle; + int gotlang = 0; + + strcpy(szLang, "LANG="); + + // First try getting the language from the registry, this can be + // used to overrule the system language. + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0, + KEY_READ, &keyhandle) == ERROR_SUCCESS) + { + len = BUFSIZE; + if (RegQueryValueEx(keyhandle, "lang", 0, NULL, (BYTE*)szBuff, &len) + == ERROR_SUCCESS) + { + szBuff[len] = 0; + strcat(szLang, szBuff); + gotlang = 1; + } + RegCloseKey(keyhandle); + } + + if (!gotlang && getenv("LANG") == NULL) + { + // Get the language from the system. + // Could use LOCALE_SISO639LANGNAME, but it's not in Win95. + // LOCALE_SABBREVLANGNAME gives us three letters, like "enu", we use + // only the first two. + len = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, + (LPTSTR)szBuff, BUFSIZE); + if (len >= 2 && _strnicmp(szBuff, "en", 2) != 0) + { + // There are a few exceptions (probably more) + if (_strnicmp(szBuff, "cht", 3) == 0 + || _strnicmp(szBuff, "zht", 3) == 0) + strcpy(szBuff, "zh_TW"); + else if (_strnicmp(szBuff, "chs", 3) == 0 + || _strnicmp(szBuff, "zhc", 3) == 0) + strcpy(szBuff, "zh_CN"); + else if (_strnicmp(szBuff, "jp", 2) == 0) + strcpy(szBuff, "ja"); + else + szBuff[2] = 0; // truncate to two-letter code + strcat(szLang, szBuff); + gotlang = 1; + } + } + if (gotlang) + putenv(szLang); + + // Try to locate the runtime files. The path is used to find libintl.dll + // and the vim.mo files. + getRuntimeDir(szBuff); + if (szBuff[0] != 0) + { + len = (DWORD)strlen(szBuff); + if (dyn_libintl_init(szBuff)) + { + strcpy(szBuff + len, "lang"); + + (*dyn_libintl_bindtextdomain)(VIMPACKAGE, szBuff); + (*dyn_libintl_textdomain)(VIMPACKAGE); + } + } +} + + static void +dyn_gettext_free(void) +{ + dyn_libintl_end(); +} +#endif // FEAT_GETTEXT + +// +// Global variables +// +UINT g_cRefThisDll = 0; // Reference count of this DLL. +HINSTANCE g_hmodThisDll = NULL; // Handle to this DLL itself. + + +//--------------------------------------------------------------------------- +// DllMain +//--------------------------------------------------------------------------- +extern "C" int APIENTRY +DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReserved */) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + // Extension DLL one-time initialization + g_hmodThisDll = hInstance; + break; + + case DLL_PROCESS_DETACH: + break; + } + + return 1; // ok +} + + static void +inc_cRefThisDLL() +{ +#ifdef FEAT_GETTEXT + if (g_cRefThisDll == 0) { + dyn_gettext_load(); + oldenv = GetEnvironmentStringsW(); + } +#endif + InterlockedIncrement((LPLONG)&g_cRefThisDll); +} + + static void +dec_cRefThisDLL() +{ +#ifdef FEAT_GETTEXT + if (InterlockedDecrement((LPLONG)&g_cRefThisDll) == 0) { + dyn_gettext_free(); + if (oldenv != NULL) { + FreeEnvironmentStringsW(oldenv); + oldenv = NULL; + } + } +#else + InterlockedDecrement((LPLONG)&g_cRefThisDll); +#endif +} + +//--------------------------------------------------------------------------- +// DllCanUnloadNow +//--------------------------------------------------------------------------- + +STDAPI DllCanUnloadNow(void) +{ + return (g_cRefThisDll == 0 ? S_OK : S_FALSE); +} + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut) +{ + *ppvOut = NULL; + + if (IsEqualIID(rclsid, CLSID_ShellExtension)) + { + CShellExtClassFactory *pcf = new CShellExtClassFactory; + + return pcf->QueryInterface(riid, ppvOut); + } + + return CLASS_E_CLASSNOTAVAILABLE; +} + +CShellExtClassFactory::CShellExtClassFactory() +{ + m_cRef = 0L; + + inc_cRefThisDLL(); +} + +CShellExtClassFactory::~CShellExtClassFactory() +{ + dec_cRefThisDLL(); +} + +STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID riid, + LPVOID FAR *ppv) +{ + *ppv = NULL; + + // any interface on this object is the object pointer + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) + { + *ppv = (LPCLASSFACTORY)this; + + AddRef(); + + return NOERROR; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef() +{ + return InterlockedIncrement((LPLONG)&m_cRef); +} + +STDMETHODIMP_(ULONG) CShellExtClassFactory::Release() +{ + if (InterlockedDecrement((LPLONG)&m_cRef)) + return m_cRef; + + delete this; + + return 0L; +} + +STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + *ppvObj = NULL; + + // Shell extensions typically don't support aggregation (inheritance) + + if (pUnkOuter) + return CLASS_E_NOAGGREGATION; + + // Create the main shell extension object. The shell will then call + // QueryInterface with IID_IShellExtInit--this is how shell extensions are + // initialized. + + LPCSHELLEXT pShellExt = new CShellExt(); // create the CShellExt object + + if (NULL == pShellExt) + return E_OUTOFMEMORY; + + return pShellExt->QueryInterface(riid, ppvObj); +} + + +STDMETHODIMP CShellExtClassFactory::LockServer(BOOL /* fLock */) +{ + return NOERROR; +} + +// *********************** CShellExt ************************* +CShellExt::CShellExt() +{ + m_cRef = 0L; + m_pDataObj = NULL; + + inc_cRefThisDLL(); + + LoadMenuIcon(); +} + +CShellExt::~CShellExt() +{ + if (m_pDataObj) + m_pDataObj->Release(); + + dec_cRefThisDLL(); + + if (m_hVimIconBitmap) + DeleteObject(m_hVimIconBitmap); +} + +STDMETHODIMP CShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv) +{ + *ppv = NULL; + + if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) + { + *ppv = (LPSHELLEXTINIT)this; + } + else if (IsEqualIID(riid, IID_IContextMenu)) + { + *ppv = (LPCONTEXTMENU)this; + } + + if (*ppv) + { + AddRef(); + + return NOERROR; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CShellExt::AddRef() +{ + return InterlockedIncrement((LPLONG)&m_cRef); +} + +STDMETHODIMP_(ULONG) CShellExt::Release() +{ + + if (InterlockedDecrement((LPLONG)&m_cRef)) + return m_cRef; + + delete this; + + return 0L; +} + + +// +// FUNCTION: CShellExt::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY) +// +// PURPOSE: Called by the shell when initializing a context menu or property +// sheet extension. +// +// PARAMETERS: +// pIDFolder - Specifies the parent folder +// pDataObj - Specifies the set of items selected in that folder. +// hRegKey - Specifies the type of the focused item in the selection. +// +// RETURN VALUE: +// +// NOERROR in all cases. +// +// COMMENTS: Note that at the time this function is called, we don't know +// (or care) what type of shell extension is being initialized. +// It could be a context menu or a property sheet. +// + +STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST /* pIDFolder */, + LPDATAOBJECT pDataObj, + HKEY /* hRegKey */) +{ + // Initialize can be called more than once + if (m_pDataObj) + m_pDataObj->Release(); + + // duplicate the object pointer and registry handle + + if (pDataObj) + { + m_pDataObj = pDataObj; + pDataObj->AddRef(); + } + + return NOERROR; +} + + +// +// FUNCTION: CShellExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT) +// +// PURPOSE: Called by the shell just before the context menu is displayed. +// This is where you add your specific menu items. +// +// PARAMETERS: +// hMenu - Handle to the context menu +// indexMenu - Index of where to begin inserting menu items +// idCmdFirst - Lowest value for new menu ID's +// idCmtLast - Highest value for new menu ID's +// uFlags - Specifies the context of the menu event +// +// RETURN VALUE: +// +// +// COMMENTS: +// + +STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu, + UINT indexMenu, + UINT idCmdFirst, + UINT /* idCmdLast */, + UINT /* uFlags */) +{ + UINT idCmd = idCmdFirst; + + hres = m_pDataObj->GetData(&fmte, &medium); + if (medium.hGlobal) + cbFiles = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, 0, 0); + + // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); + + // Initialize m_cntOfHWnd to 0 + m_cntOfHWnd = 0; + + HKEY keyhandle; + bool showExisting = true; + bool showIcons = true; + + // Check whether "Edit with existing Vim" entries are disabled. + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0, + KEY_READ, &keyhandle) == ERROR_SUCCESS) + { + if (RegQueryValueEx(keyhandle, "DisableEditWithExisting", 0, NULL, + NULL, NULL) == ERROR_SUCCESS) + showExisting = false; + if (RegQueryValueEx(keyhandle, "DisableContextMenuIcons", 0, NULL, + NULL, NULL) == ERROR_SUCCESS) + showIcons = false; + RegCloseKey(keyhandle); + } + + // Retrieve all the vim instances, unless disabled. + if (showExisting) + EnumWindows(EnumWindowsProc, (LPARAM)this); + + MENUITEMINFO mii = { sizeof(MENUITEMINFO) }; + mii.fMask = MIIM_STRING | MIIM_ID; + if (showIcons) + { + mii.fMask |= MIIM_BITMAP; + mii.hbmpItem = m_hVimIconBitmap; + } + + if (cbFiles > 1) + { + mii.wID = idCmd++; + mii.dwTypeData = _("Edit with &multiple Vims"); + mii.cch = lstrlen(mii.dwTypeData); + InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); + + mii.wID = idCmd++; + mii.dwTypeData = _("Edit with single &Vim"); + mii.cch = lstrlen(mii.dwTypeData); + InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); + + if (cbFiles <= 4) + { + // Can edit up to 4 files in diff mode + mii.wID = idCmd++; + mii.dwTypeData = _("Diff with Vim"); + mii.cch = lstrlen(mii.dwTypeData); + InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); + m_edit_existing_off = 3; + } + else + m_edit_existing_off = 2; + + } + else + { + mii.wID = idCmd++; + mii.dwTypeData = _("Edit with &Vim"); + mii.cch = lstrlen(mii.dwTypeData); + InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); + m_edit_existing_off = 1; + } + + HMENU hSubMenu = NULL; + if (m_cntOfHWnd > 1) + { + hSubMenu = CreatePopupMenu(); + mii.fMask |= MIIM_SUBMENU; + mii.wID = idCmd; + mii.dwTypeData = _("Edit with existing Vim"); + mii.cch = lstrlen(mii.dwTypeData); + mii.hSubMenu = hSubMenu; + InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); + mii.fMask = mii.fMask & ~MIIM_SUBMENU; + mii.hSubMenu = NULL; + } + // Now display all the vim instances + for (int i = 0; i < m_cntOfHWnd; i++) + { + char title[BUFSIZE]; + char temp[BUFSIZE]; + int index; + HMENU hmenu; + + // Obtain window title, continue if can not + if (GetWindowText(m_hWnd[i], title, BUFSIZE - 1) == 0) + continue; + // Truncate the title before the path, keep the file name + char *pos = strchr(title, '('); + if (pos != NULL) + { + if (pos > title && pos[-1] == ' ') + --pos; + *pos = 0; + } + // Now concatenate + if (m_cntOfHWnd > 1) + temp[0] = '\0'; + else + { + strncpy(temp, _("Edit with existing Vim - "), BUFSIZE - 1); + temp[BUFSIZE - 1] = '\0'; + } + strncat(temp, title, BUFSIZE - 1 - strlen(temp)); + temp[BUFSIZE - 1] = '\0'; + + mii.wID = idCmd++; + mii.dwTypeData = temp; + mii.cch = lstrlen(mii.dwTypeData); + if (m_cntOfHWnd > 1) + { + hmenu = hSubMenu; + index = i; + } + else + { + hmenu = hMenu; + index = indexMenu++; + } + InsertMenuItem(hmenu, index, TRUE, &mii); + } + // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); + + // Must return number of menu items we added. + return ResultFromShort(idCmd-idCmdFirst); +} + +// +// FUNCTION: CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO) +// +// PURPOSE: Called by the shell after the user has selected on of the +// menu items that was added in QueryContextMenu(). +// +// PARAMETERS: +// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure +// +// RETURN VALUE: +// +// +// COMMENTS: +// + +STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) +{ + HRESULT hr = E_INVALIDARG; + + // If HIWORD(lpcmi->lpVerb) then we have been called programmatically + // and lpVerb is a command that should be invoked. Otherwise, the shell + // has called us, and LOWORD(lpcmi->lpVerb) is the menu ID the user has + // selected. Actually, it's (menu ID - idCmdFirst) from QueryContextMenu(). + if (!HIWORD(lpcmi->lpVerb)) + { + UINT idCmd = LOWORD(lpcmi->lpVerb); + + if (idCmd >= m_edit_existing_off) + { + // Existing with vim instance + hr = PushToWindow(lpcmi->hwnd, + lpcmi->lpDirectory, + lpcmi->lpVerb, + lpcmi->lpParameters, + lpcmi->nShow, + idCmd - m_edit_existing_off); + } + else + { + switch (idCmd) + { + case 0: + hr = InvokeGvim(lpcmi->hwnd, + lpcmi->lpDirectory, + lpcmi->lpVerb, + lpcmi->lpParameters, + lpcmi->nShow); + break; + case 1: + hr = InvokeSingleGvim(lpcmi->hwnd, + lpcmi->lpDirectory, + lpcmi->lpVerb, + lpcmi->lpParameters, + lpcmi->nShow, + 0); + break; + case 2: + hr = InvokeSingleGvim(lpcmi->hwnd, + lpcmi->lpDirectory, + lpcmi->lpVerb, + lpcmi->lpParameters, + lpcmi->nShow, + 1); + break; + } + } + } + return hr; +} + +STDMETHODIMP CShellExt::PushToWindow(HWND /* hParent */, + LPCSTR /* pszWorkingDir */, + LPCSTR /* pszCmd */, + LPCSTR /* pszParam */, + int /* iShowCmd */, + int idHWnd) +{ + HWND hWnd = m_hWnd[idHWnd]; + + // Show and bring vim instance to foreground + if (IsIconic(hWnd) != 0) + ShowWindow(hWnd, SW_RESTORE); + else + ShowWindow(hWnd, SW_SHOW); + SetForegroundWindow(hWnd); + + // Post the selected files to the vim instance + PostMessage(hWnd, WM_DROPFILES, (WPARAM)medium.hGlobal, 0); + + return NOERROR; +} + +STDMETHODIMP CShellExt::GetCommandString(UINT_PTR /* idCmd */, + UINT uFlags, + UINT FAR * /* reserved */, + LPSTR pszName, + UINT cchMax) +{ + if (uFlags == GCS_HELPTEXT && cchMax > 35) + lstrcpy(pszName, _("Edits the selected file(s) with Vim")); + + return NOERROR; +} + +BOOL CALLBACK CShellExt::EnumWindowsProc(HWND hWnd, LPARAM lParam) +{ + char temp[BUFSIZE]; + + // First do a bunch of check + // No invisible window + if (!IsWindowVisible(hWnd)) return TRUE; + // No child window ??? + // if (GetParent(hWnd)) return TRUE; + // Class name should be Vim, if failed to get class name, return + if (GetClassName(hWnd, temp, sizeof(temp)) == 0) + return TRUE; + // Compare class name to that of vim, if not, return + if (_strnicmp(temp, "vim", sizeof("vim")) != 0) + return TRUE; + // First check if the number of vim instance exceeds MAX_HWND + CShellExt *cs = (CShellExt*) lParam; + if (cs->m_cntOfHWnd >= MAX_HWND) return TRUE; + // Now we get the vim window, put it into some kind of array + cs->m_hWnd[cs->m_cntOfHWnd] = hWnd; + cs->m_cntOfHWnd ++; + + return TRUE; // continue enumeration (otherwise this would be false) +} + +BOOL CShellExt::LoadMenuIcon() +{ + char vimExeFile[BUFSIZE]; + getGvimName(vimExeFile, 1); + if (vimExeFile[0] == '\0') + return FALSE; + HICON hVimIcon; + if (ExtractIconEx(vimExeFile, 0, NULL, &hVimIcon, 1) == 0) + return FALSE; + m_hVimIconBitmap = IconToBitmap(hVimIcon, + GetSysColorBrush(COLOR_MENU), + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON)); + return TRUE; +} + +#ifndef __BORLANDC__ + static char * +searchpath(char *name) +{ + static char widename[2 * BUFSIZE]; + static char location[2 * BUFSIZE + 2]; + + // There appears to be a bug in FindExecutableA() on Windows NT. + // Use FindExecutableW() instead... + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)name, -1, + (LPWSTR)widename, BUFSIZE); + if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", + (LPWSTR)location) > (HINSTANCE)32) + { + WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, + (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); + return widename; + } + return (char *)""; +} +#endif + +STDMETHODIMP CShellExt::InvokeGvim(HWND hParent, + LPCSTR /* pszWorkingDir */, + LPCSTR /* pszCmd */, + LPCSTR /* pszParam */, + int /* iShowCmd */) +{ + wchar_t m_szFileUserClickedOn[BUFSIZE]; + wchar_t cmdStrW[BUFSIZE]; + UINT i; + + for (i = 0; i < cbFiles; i++) + { + DragQueryFileW((HDROP)medium.hGlobal, + i, + m_szFileUserClickedOn, + sizeof(m_szFileUserClickedOn)); + + getGvimInvocationW(cmdStrW); + wcscat(cmdStrW, L" \""); + + if ((wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 2) < BUFSIZE) + { + wcscat(cmdStrW, m_szFileUserClickedOn); + wcscat(cmdStrW, L"\""); + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + // Start the child process. + if (!CreateProcessW(NULL, // No module name (use command line). + cmdStrW, // Command line. + NULL, // Process handle not inheritable. + NULL, // Thread handle not inheritable. + FALSE, // Set handle inheritance to FALSE. + oldenv == NULL ? 0 : CREATE_UNICODE_ENVIRONMENT, + oldenv, // Use unmodified environment block. + NULL, // Use parent's starting directory. + &si, // Pointer to STARTUPINFO structure. + &pi) // Pointer to PROCESS_INFORMATION structure. + ) + { + MessageBox( + hParent, + _("Error creating process: Check if gvim is in your path!"), + _("gvimext.dll error"), + MB_OK); + } + else + { + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + } + } + else + { + MessageBox( + hParent, + _("Path length too long!"), + _("gvimext.dll error"), + MB_OK); + } + } + + return NOERROR; +} + + +STDMETHODIMP CShellExt::InvokeSingleGvim(HWND hParent, + LPCSTR /* pszWorkingDir */, + LPCSTR /* pszCmd */, + LPCSTR /* pszParam */, + int /* iShowCmd */, + int useDiff) +{ + wchar_t m_szFileUserClickedOn[BUFSIZE]; + wchar_t *cmdStrW; + size_t cmdlen; + size_t len; + UINT i; + + cmdlen = BUFSIZE; + cmdStrW = (wchar_t *) malloc(cmdlen * sizeof(wchar_t)); + if (cmdStrW == NULL) + return E_FAIL; + getGvimInvocationW(cmdStrW); + + if (useDiff) + wcscat(cmdStrW, L" -d"); + for (i = 0; i < cbFiles; i++) + { + DragQueryFileW((HDROP)medium.hGlobal, + i, + m_szFileUserClickedOn, + sizeof(m_szFileUserClickedOn)); + + len = wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 4; + if (len > cmdlen) + { + cmdlen = len + BUFSIZE; + wchar_t *cmdStrW_new = (wchar_t *)realloc(cmdStrW, cmdlen * sizeof(wchar_t)); + if (cmdStrW_new == NULL) + { + free(cmdStrW); + return E_FAIL; + } + cmdStrW = cmdStrW_new; + } + wcscat(cmdStrW, L" \""); + wcscat(cmdStrW, m_szFileUserClickedOn); + wcscat(cmdStrW, L"\""); + } + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + // Start the child process. + if (!CreateProcessW(NULL, // No module name (use command line). + cmdStrW, // Command line. + NULL, // Process handle not inheritable. + NULL, // Thread handle not inheritable. + FALSE, // Set handle inheritance to FALSE. + oldenv == NULL ? 0 : CREATE_UNICODE_ENVIRONMENT, + oldenv, // Use unmodified environment block. + NULL, // Use parent's starting directory. + &si, // Pointer to STARTUPINFO structure. + &pi) // Pointer to PROCESS_INFORMATION structure. + ) + { + MessageBox( + hParent, + _("Error creating process: Check if gvim is in your path!"), + _("gvimext.dll error"), + MB_OK); + } + else + { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + free(cmdStrW); + + return NOERROR; +} diff --git a/src/GvimExt/gvimext.def b/src/GvimExt/gvimext.def new file mode 100644 index 0000000..e6d66f4 --- /dev/null +++ b/src/GvimExt/gvimext.def @@ -0,0 +1,8 @@ +;gvimdef.def : Declares the module parameters for the DLL. + +LIBRARY gvimext +; DESCRIPTION 'Vim Shell Extension' + +EXPORTS + DllCanUnloadNow private + DllGetClassObject private diff --git a/src/GvimExt/gvimext.h b/src/GvimExt/gvimext.h new file mode 100644 index 0000000..e17f2ac --- /dev/null +++ b/src/GvimExt/gvimext.h @@ -0,0 +1,181 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved gvimext by Tianmiao Hu + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + */ + +/* + * If you have any questions or any suggestions concerning gvimext, please + * contact Tianmiao Hu: tianmiao@acm.org. + */ + +#if !defined(AFX_STDAFX_H__3389658B_AD83_11D3_9C1E_0090278BBD99__INCLUDED_) +#define AFX_STDAFX_H__3389658B_AD83_11D3_9C1E_0090278BBD99__INCLUDED_ + +#if defined(_MSC_VER) && _MSC_VER > 1000 +#pragma once +#endif + +// Insert your headers here +// #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +//-------------------------------------------------------------- +// common user interface routines +// +// +//-------------------------------------------------------------- + +#ifndef STRICT +# define STRICT +#endif + +#define INC_OLE2 // WIN32, get ole2 from windows.h + +/* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */ +#if defined(_MSC_VER) && _MSC_VER >= 1400 +# define _CRT_SECURE_NO_DEPRECATE +# define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#include +#include +#include +#include + +/* Accommodate old versions of VC that don't have a modern Platform SDK */ +#if (defined(_MSC_VER) && _MSC_VER < 1300) || !defined(MAXULONG_PTR) +# undef UINT_PTR +# define UINT_PTR UINT +#endif + +#define ResultFromShort(i) ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(i))) + +// Initialize GUIDs (should be done only and at-least once per DLL/EXE) +// +#pragma data_seg(".text") +#define INITGUID +#include +#include + +// +// The class ID of this Shell extension class. +// +// class id: {51EEE242-AD87-11d3-9C1E-0090278BBD99} +// +// +// NOTE!!! If you use this shell extension as a starting point, +// you MUST change the GUID below. Simply run UUIDGEN.EXE +// to generate a new GUID. +// + +// {51EEE242-AD87-11d3-9C1E-0090278BBD99} +// static const GUID <> = +// { 0x51eee242, 0xad87, 0x11d3, { 0x9c, 0x1e, 0x0, 0x90, 0x27, 0x8b, 0xbd, 0x99 } }; +// +// + +// {51EEE242-AD87-11d3-9C1E-0090278BBD99} +// IMPLEMENT_OLECREATE(<>, <>, +// 0x51eee242, 0xad87, 0x11d3, 0x9c, 0x1e, 0x0, 0x90, 0x27, 0x8b, 0xbd, 0x99); +// + +// {51EEE242-AD87-11d3-9C1E-0090278BBD99} -- this is the registry format +DEFINE_GUID(CLSID_ShellExtension, 0x51eee242, 0xad87, 0x11d3, 0x9c, 0x1e, 0x0, 0x90, 0x27, 0x8b, 0xbd, 0x99); + +// this class factory object creates context menu handlers for windows 32 shell +class CShellExtClassFactory : public IClassFactory +{ +protected: + ULONG m_cRef; + +public: + CShellExtClassFactory(); + ~CShellExtClassFactory(); + + //IUnknown members + STDMETHODIMP QueryInterface(REFIID, LPVOID FAR *); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + //IClassFactory members + STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR *); + STDMETHODIMP LockServer(BOOL); + +}; +typedef CShellExtClassFactory *LPCSHELLEXTCLASSFACTORY; +#define MAX_HWND 100 + +// this is the actual OLE Shell context menu handler +class CShellExt : public IContextMenu, + IShellExtInit +{ +private: + BOOL LoadMenuIcon(); + +protected: + ULONG m_cRef; + LPDATAOBJECT m_pDataObj; + UINT m_edit_existing_off; + HBITMAP m_hVimIconBitmap; + + // For some reason, this callback must be static + static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam); + + STDMETHODIMP PushToWindow(HWND hParent, + LPCSTR pszWorkingDir, + LPCSTR pszCmd, + LPCSTR pszParam, + int iShowCmd, + int idHWnd); + + STDMETHODIMP InvokeGvim(HWND hParent, + LPCSTR pszWorkingDir, + LPCSTR pszCmd, + LPCSTR pszParam, + int iShowCmd); + + STDMETHODIMP InvokeSingleGvim(HWND hParent, + LPCSTR pszWorkingDir, + LPCSTR pszCmd, + LPCSTR pszParam, + int iShowCmd, + int useDiff); + +public: + int m_cntOfHWnd; + HWND m_hWnd[MAX_HWND]; + CShellExt(); + ~CShellExt(); + + //IUnknown members + STDMETHODIMP QueryInterface(REFIID, LPVOID FAR *); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + //IShell members + STDMETHODIMP QueryContextMenu(HMENU hMenu, + UINT indexMenu, + UINT idCmdFirst, + UINT idCmdLast, + UINT uFlags); + + STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi); + + STDMETHODIMP GetCommandString(UINT_PTR idCmd, + UINT uFlags, + UINT FAR *reserved, + LPSTR pszName, + UINT cchMax); + + //IShellExtInit methods + STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, + LPDATAOBJECT pDataObj, + HKEY hKeyID); +}; + +typedef CShellExt *LPCSHELLEXT; +#pragma data_seg() + +#endif diff --git a/src/GvimExt/gvimext.inf b/src/GvimExt/gvimext.inf new file mode 100644 index 0000000..8b45bb1 --- /dev/null +++ b/src/GvimExt/gvimext.inf @@ -0,0 +1,22 @@ +[Version] +Signature="$Windows NT$"" +Provider="Tianmiao" + +[Manufacture] + +[DefaultInstall] +AddReg=ThisDll.Add.Reg + +[DefaultUninstall] +DelReg=ThisDLL.Add.Reg + +[ThisDll.Add.Reg] +HKCR,CLSID\{51EEE242-AD87-11d3-9C1E-0090278BBD99} +HKCR,CLSID\{51EEE242-AD87-11d3-9C1E-0090278BBD99}\InProcServer32 +HKCR,*\shellex\ContextMenuHandlers\gvim +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved",{51EEE242-AD87-11d3-9C1E-0090278BBD99} +HKLM,Software\Vim\Gvim +HKLM,"Software\Microsoft\Windows\CurrentVersion\Uninstall\Vim 6.0" + +[Strings] +ThisDll="gvimext.dll" diff --git a/src/GvimExt/gvimext.rc b/src/GvimExt/gvimext.rc new file mode 100644 index 0000000..22102db --- /dev/null +++ b/src/GvimExt/gvimext.rc @@ -0,0 +1,111 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#ifndef __BORLANDC__ +# include "winresrc.h" +#endif + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x3L +#else + FILEFLAGS 0x2L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "Developed using COM architecture!\0" + VALUE "CompanyName", "Tianmiao Hu's Developer Studio\0" + VALUE "FileDescription", "A small project for the context menu of gvim!\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "gvim right-click menu extension\0" + VALUE "LegalCopyright", "Copyright © 1999 Tianmiao Hu\0" + VALUE "LegalTrademarks", "Tianmiao Hu's Gvim Context Menu Extension\0" + VALUE "OriginalFilename", "gvimext.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Tianmiao Hu's gvimext context menu extension\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/GvimExt/gvimext_ming.def b/src/GvimExt/gvimext_ming.def new file mode 100644 index 0000000..b96e1d3 --- /dev/null +++ b/src/GvimExt/gvimext_ming.def @@ -0,0 +1,10 @@ +;gvimdef_ming.def : Declares the module parameters for the DLL. +;The mingw environment doesn't know anything about private declarations +;Hence this is the same file as gvimext.def with private removed + +LIBRARY gvimext +; DESCRIPTION 'Vim Shell Extension build with MinGW' + +EXPORTS + DllCanUnloadNow = DllCanUnloadNow@0 + DllGetClassObject = DllGetClassObject@12 diff --git a/src/GvimExt/gvimext_ming.rc b/src/GvimExt/gvimext_ming.rc new file mode 100644 index 0000000..6c69854 --- /dev/null +++ b/src/GvimExt/gvimext_ming.rc @@ -0,0 +1,45 @@ +#include +#define xstr(x) str(x) +#define str(x) #x +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x3L +#else + FILEFLAGS 0x2L +#endif + FILEOS 0x4L + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "040904b0" + { + VALUE "Comments", "Developed using COM architecture!\0" + VALUE "CompanyName", "Tianmiao Hu's Developer Studio\0" + VALUE "FileDescription", "A small project for the context menu of gvim!\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "gvim right-click menu extension\0" + VALUE "LegalCopyright", "Copyright © 1999 Tianmiao Hu\0" + VALUE "LegalTrademarks", "Tianmiao Hu's Gvim Context Menu Extension\0" + VALUE "OriginalFilename", "gvimext.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Tianmiao Hu's gvimext context menu extension\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + +#if defined(__GNUC__) + VALUE "SpecialBuild", "Build With " "MingW " xstr(__GNUC__) "." xstr(__GNUC_MINOR__) "." xstr(__GNUC_PATCHLEVEL__) " on " __DATE__ " " __TIME__ "\0" +#else + VALUE "SpecialBuild", "Unknown compiler\0" + +#endif + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1200 + } +} diff --git a/src/GvimExt/resource.h b/src/GvimExt/resource.h new file mode 100644 index 0000000..8ddc823 --- /dev/null +++ b/src/GvimExt/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by gvimext.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/GvimExt/uninst.bat b/src/GvimExt/uninst.bat new file mode 100644 index 0000000..71d14f6 --- /dev/null +++ b/src/GvimExt/uninst.bat @@ -0,0 +1 @@ +rundll32.exe setupapi,InstallHinfSection DefaultUninstall 128 %1 diff --git a/src/INSTALL b/src/INSTALL new file mode 100644 index 0000000..3da9141 --- /dev/null +++ b/src/INSTALL @@ -0,0 +1,384 @@ +INSTALL - Installation of Vim on different machines. + +This file contains instructions for compiling Vim. If you already have an +executable version of Vim, you don't need this. + +Contents: +1. Generic +2. Unix +3. OS/2 (with EMX 0.9b) +4. Atari MiNT + +See INSTALLami.txt for Amiga +See INSTALLmac.txt for Macintosh +See INSTALLpc.txt for PC (Windows XP/Vista/7/8/10) +See INSTALLvms.txt for VMS +See INSTALLx.txt for cross-compiling on Unix +See ../READMEdir/README_390.txt for z/OS and OS/390 Unix +See ../runtime/doc/os_beos.txt for BeBox + + +1. Generic +========== + +If you compile Vim without specifying anything, you will get the default +behaviour as is documented, which should be fine for most people. + +For features that you can't enable/disable in another way, you can edit the +file "feature.h" to match your preferences. + + +2. Unix +======= + +Summary: +1. make run configure, compile and link +2. make install installation in /usr/local + +This will include the GUI and X11 libraries, if you have them. If you want a +version of Vim that is small and starts up quickly, see the Makefile for how +to disable the GUI and X11. If you don't have GUI libraries and/or X11, these +features will be disabled automatically. + +See the start of Makefile for more detailed instructions about how to compile +Vim. + +If you need extra compiler and/or linker arguments, set $CFLAGS and/or $LIBS +before starting configure. Example: + + env CFLAGS=-I/usr/local/include LIBS=-lm make + +This is only needed for things that configure doesn't offer a specific argument +for or figures out by itself. First try running configure without extra +arguments. + +GNU Autoconf and a few other tools have been used to make Vim work on many +different Unix systems. The advantage of this is that Vim should compile +on most systems without any adjustments. The disadvantage is that when +adjustments are required, it takes some time to understand what is happening. + +If configure finds all library files and then complains when linking that some +of them can't be found, your linker doesn't return an error code for missing +libraries. Vim should be linked fine anyway, mostly you can just ignore these +errors. + +If you run configure by hand (not using the Makefile), remember that any +changes in the Makefile have no influence on configure. This may be what you +want, but maybe not! + +The advantage of running configure separately, is that you can write a script +to build Vim, without changing the Makefile or feature.h. Example (using sh): + + CFLAGS=-DCOMPILER_FLAG ./configure --enable-gui=motif + +One thing to watch out for: If the configure script itself changes, running +"make" will execute it again, but without your arguments. Do "make clean" and +run configure again. + +If you are compiling Vim for several machines, for each machine: + a. make shadow + b. mv shadow machine_name + c. cd machine_name + d. make; make install + +[Don't use a path for machine_name, just a directory name, otherwise the links +that "make shadow" creates won't work.] + + +Unix: COMPILING WITH/WITHOUT GUI + +NOTE: This is incomplete, look in Makefile for more info. + +These configure arguments can be used to select which GUI to use: +--enable-gui=gtk or: gtk2, motif, athena or auto +--disable-gtk-check +--disable-motif-check +--disable-athena-check + +--enable-gui defaults to "auto", so it will automatically look for a GUI (in +the order of GTK, Motif, then Athena). If one is found, then is uses it and +does not proceed to check any of the remaining ones. Otherwise, it moves on +to the next one. + +--enable-{gtk,gtk2,kde,motif,athena}-check all default to "yes", such that if +--enable-gui is "auto" (which it is by default), GTK, Motif, and Athena will +be checked for. If you want to *exclude* a certain check, then you use +--disable-{gtk,gtk2,kde,motif,athena}-check. + +For example, if --enable-gui is set to "auto", but you don't want it look for +Motif, you then also specify --disable-motif-check. This results in only +checking for GTK and Athena. + +Lastly, if you know which one you want to use, then you can just do +--enable-gui={gtk,gtk2,kde,motif,athena}. So if you wanted to only use Motif, +then you'd specify --enable-gui=motif. Once you specify what you want, the +--enable-{gtk,gtk2,kde,motif,athena}-check options are ignored. + +On Linux you usually need GUI "-devel" packages. You may already have GTK +libraries installed, but that doesn't mean you can compile Vim with GTK, you +also need the header files. + +For compiling with the GTK+ GUI, you need a recent version of glib and gtk+. +Configure checks for at least version 1.1.16. An older version is not selected +automatically. If you want to use it anyway, run configure with +"--disable-gtktest". +GTK requires an ANSI C compiler. If you fail to compile Vim with GTK+ (it +is the preferred choice), try selecting another one in the Makefile. +If you are sure you have GTK installed, but for some reason configure says you +do not, you may have left-over header files and/or library files from an older +(and incompatible) version of GTK. if this is the case, please check +auto/config.log for any error messages that may give you a hint as to what's +happening. + +There used to be a KDE version of Vim, using Qt libraries, but since it didn't +work very well and there was no maintainer it was dropped. + + +Unix: COMPILING WITH MULTI-BYTE + +When you want to compile with the multi-byte features enabled, make sure you +compile on a machine where the locale settings actually work, otherwise the +configure tests may fail. You need to compile with "big" features: + + ./configure --with-features=big + +Unix: COMPILING ON LINUX + +On Linux, when using -g to compile (which is default for gcc), the executable +will probably be statically linked. If you don't want this, remove the -g +option from CFLAGS. + +Unix: PUTTING vimrc IN /etc + +Some Linux distributions prefer to put the global vimrc file in /etc, and the +Vim runtime files in /usr. This can be done with: + ./configure --prefix=/usr + make VIMRCLOC=/etc VIMRUNTIMEDIR=/usr/share/vim MAKE="make -e" + +Unix: COMPILING ON NeXT + +Add the "-posix" argument to the compiler by using one of these commands: + setenv CC 'cc -posix' (csh) + export CC='cc -posix' (sh) +And run configure with "--disable-motif-check". + +Unix: LOCAL HEADERS AND LIBRARIES NOT IN /usr/local + +Sometimes it is necessary to search different path than /usr/local for locally +installed headers (/usr/local/include) and libraries (/usr/local/lib). +To search /stranger/include and /stranger/lib for locally installed +headers and libraries, use: + ./configure --with-local-dir=/stranger +And to not search for locally installed headers and libraries at all, use: + ./configure --without-local-dir + + +3. OS/2 +======= + +OS/2 support was removed in patch 7.4.1008 + + +4. Atari MiNT +============= + +[NOTE: this is quite old, it might not work anymore] + +To compile Vim for MiNT you may either copy Make_mint.mak to Makefile or use +the Unix Makefile adapted for the MiNT configuration. + +Now proceed as described in the Unix section. + +Prerequisites: + +You need a curses or termcap library that supports non-alphanumeric +termcap names. If you don't have any, link with termlib.o. + +----------------------------------------------------------------------------- + +The rest of this file is based on the INSTALL file that comes with GNU +autoconf 2.12. Not everything applies to Vim. Read Makefile too! + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.ac' is used to create `configure' by a program +called `autoconf'. You only need `configure.ac' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/src/INSTALLami.txt b/src/INSTALLami.txt new file mode 100644 index 0000000..0acd6fd --- /dev/null +++ b/src/INSTALLami.txt @@ -0,0 +1,34 @@ +INSTALLami.txt - Installation of Vim from source on Amiga + +This file contains instructions for compiling Vim. If you already have an +executable version of Vim, you don't need this. + +The file "feature.h" can be edited to match your preferences. You can skip +this, then you will get the default behavior as is documented, which should +be fine for most people. + + +Summary: +make -f Make_manx.mak Manx C +make -f Make_sas.mak Lattice/SAS C +make -f Make_dice.mak DICE + +The Manx compiler is preferred, it was used to produce the Amiga executable +and has been tested most. You should not get any errors or warnings. + +The SAS compiler can be used. Older versions (6.0 to 6.3) don't work with the +optimizer switched on. This seems to be fixed with 6.5 or 6.56, but this has +not been tested much. Also disable optimizing when the compiler runs out of +memory or crashes the system (yes, that happens; did I say the Manx compiler +is preferred?). + +The DICE makefile has been reported to work by one person only. + +You will have to set the "VIM" environment variable to the location of the +documentation files. + + +MorphOS + +Use the Make_morph.mak Makefile: + make -f Make_morph.mak diff --git a/src/INSTALLmac.txt b/src/INSTALLmac.txt new file mode 100644 index 0000000..e957682 --- /dev/null +++ b/src/INSTALLmac.txt @@ -0,0 +1,72 @@ +INSTALLmac.txt - Installation of Vim on Macintosh + +This file contains instructions for compiling Vim. If you already have an +executable version of Vim, you don't need this. + +First, make sure you've installed Xcode or CommandLineToots. If not, open a +terminal and do + + $ make --version + +A window pops up instructing you to install the developer tools. + + +---------------------------------------------------------------------------- +Summary +---------------------------------------------------------------------------- + +1 MacOS X + 1.1. Carbon interface + 1.2. X (Athena, GTK, Motif) or plain text. + +MacOS Classic is no longer supported. If you really want it use Vim 6.4. + +---------------------------------------------------------------------------- +1 MacOS X +---------------------------------------------------------------------------- + +1.0 Considerations + + Only '/' supported as path separator. + +1.1 Carbon interface (default) + + You can compile vim with the standard Unix routine: + cd .../src + make + make test + sudo make install + + "make" will create a working Vim.app application bundle in the src + directory. You can move this bundle (the Vim.app directory) anywhere + you want. Or use "make install" to move it to /Applications. + + You need at least Xcode 1.5 to compile Vim 7.0. + + Configure will create a universal binary if possible. This requires + installing the universal SDK (currently for 10.4). + + To overrule the architecture do this before running make: + + ./configure --with-mac-arch=intel + or + ./configure --with-mac-arch=ppc + + +1.2 X-Windows or Plain Text + + If you do not want the Carbon interface, you must explicitly tell + configure to use a different GUI. + + cd .../src + ./configure --disable-darwin --enable-gui=gtk2 + make; make install + + NOTE: The following GUI options are supported: + no (for text), motif, athena, nextaw + gtk, gtk2, gnome, gnome2, + + NOTE: You need to first install XFree86 and XDarwin. + Please visit http://www.XDarwin.org + +------------------------------------------------------ diff --git a/src/INSTALLpc.txt b/src/INSTALLpc.txt new file mode 100644 index 0000000..634f4ad --- /dev/null +++ b/src/INSTALLpc.txt @@ -0,0 +1,1051 @@ +INSTALLpc.txt - Installation of Vim on PC + +This file contains instructions for compiling Vim. If you already have an +executable version of Vim, you don't need this. + +You can find the latest here: https://github.com/vim/vim-win32-installer +This page also has links to install support for interfaces such as Perl, +Python, Lua, etc. + +The file "feature.h" can be edited to match your preferences. You can skip +this, then you will get the default behavior as is documented, which should +be fine for most people. + +This document assumes that you are building Vim for Win32 or later (Windows +XP/2003/Vista/7/8/10). There are also instructions for pre-XP systems, but +they might no longer work. + +The recommended way is to build a 32 bit Vim, also on 64 bit systems. You can +build a 64 bit Vim if you like, the executable will be bigger and Vim won't be +any faster, but you can edit files larger than 2 Gbyte. + + +Contents: +1. Microsoft Visual C++ +2. Using MSYS2 with MinGW +3. Using MinGW +4. Cygwin +5. Borland +6. Cross compiling for Win32 from a Linux machine +7. Building with Python support +8. Building with Python3 support +9. Building with Racket or MzScheme support +10. Building with Lua support +11. Building with Perl support +12. Building with Ruby support +13. Building with Tcl support +14. Building with Terminal support +15. Building with DirectX (DirectWrite) support +16. Windows 3.1 +17. MS-DOS + +18. Installing after building from sources + + +The currently recommended way (that means it has been verified to work) is +using the "Visual Studio Community 2015" installation. This includes the SDK +needed to target Windows XP. But not older Windows versions (95, 98), see +|msvc-2008-express| below for that + + +1. Microsoft Visual C++ +======================= + +We do not provide download links, since Microsoft keeps changing them. You +can search for "Visual Studio Community 2015", for example. You will need to +create a Microsoft account (it's free). + +When installing "Visual Studio Community 2015 with Update 3" make sure to +select "custom" and check "Windows XP Support for C++" and all checkboxes +under "Universal Windows App Development Tools" + + +Visual Studio +------------- + +Building with Visual Studio (VS 98, VS .NET, VS .NET 2003, VS 2005, VS 2008, +VS2010, VS2012, VS2013 and VS2015) is straightforward. (These instructions +should also work for VS 4 and VS 5.) + +Using VS C++ 2008 Express is recommended if you need the binary to run on +Windows 95 or 97, see |msvc-2008-express| below. + +To build Vim from the command line with MSVC, use Make_mvc.mak. +Visual Studio installed a batch file called vcvars32.bat, which you must +run to set up paths for nmake and MSVC. + +nmake -f Make_mvc.mak console Win32 SDK or Microsoft Visual C++ +nmake -f Make_mvc.mak GUI=yes GUI Microsoft Visual C++ +nmake -f Make_mvc.mak OLE=yes OLE Microsoft Visual C++ +nmake -f Make_mvc.mak PERL=C:\Perl PYTHON=C:\Python etc. + Perl, Python, etc. + +Make_mvc.mak allows a Vim to be built with various different features and +debug support. Debugging with MS Devstudio is provided by Make_dvc.mak. +For a description of the use of Make_dvc.mak, look in Make_mvc.mak. + +For compiling Gvim with IME support on far-east Windows, add IME=yes +to the parameters you pass to Make_mvc.mak. + +To build Vim from within the Visual Studio IDE, open the Make_ivc.mak project. +(Note: Make_ivc.mak is not as rich as Make_mvc.mak, which allows for +far more configuration.) Make_ivc.mak can also be built with nmake. + +nmake -f Make_ivc.mak CFG="Vim - Win32 Release gvim" + GUI Microsoft Visual C++ 4.x or later +nmake -f Make_ivc.mak CFG="Vim - Win32 Release gvim OLE" + OLE Microsoft Visual C++ 4.x or later + +See the specific files for comments and options. + +These files have been supplied by George V. Reilly, Ben Singer, Ken Scott and +Ron Aaron; they have been tested. + + +Visual C++ 2008 Express Edition *msvc-2008-express* +------------------------------- + +Visual C++ 2008 Express Edition can be downloaded for free from: + http://www.microsoft.com/express/downloads/ +This includes the IDE and the debugger. + +To set the environment execute the msvc2008.bat script. You can then build +Vim with Make_mvc.mak. + +For building 64 bit binaries you also need to install the SDK: +"Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1" +You don't need the examples and documentation. + +If you get an error that Win32.mak can't be found, you have to set the +variable SDK_INCLUDE_DIR. For example, on Windows 10, installation of MSVC +puts include files in the following directory: + set SDK_INCLUDE_DIR=C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include + + +Visual C++ 2010 Express Edition *msvc-2010-express* +------------------------------- + +Visual C++ 2010 Express Edition can be downloaded for free from: + http://www.microsoft.com/express/vc/Default.aspx +This includes the IDE and the debugger. + +To set the environment execute the msvc2010.bat script. You can then build +Vim with Make_mvc.mak. + + +Targeting Windows XP with MSVC 2012 and later *new-msvc-windows-xp* +--------------------------------------------- + +Beginning with Visual C++ 2012, Microsoft changed the behavior of LINK.EXE +so that it targets Windows 6.0 (Vista) by default. In order to override +this, the target Windows version number needs to be passed to LINK like +follows: + LINK ... /subsystem:console,5.01 + +Make_mvc.mak now supports a macro SUBSYSTEM_VER to pass the Windows version. +Use lines like follows to target Windows XP x86 (assuming using Visual C++ +2012 under 64-bit Windows): + set WinSdk71=%ProgramFiles(x86)%\Microsoft SDKs\Windows\v7.1A + set INCLUDE=%WinSdk71%\Include;%INCLUDE% + set LIB=%WinSdk71%\Lib;%LIB% + set CL=/D_USING_V110_SDK71_ + nmake -f Make_mvc.mak ... WINVER=0x0501 SUBSYSTEM_VER=5.01 + +To target Windows XP x64 instead of x86, you need to change the settings of +LIB and SUBSYSTEM_VER: + ... + set LIB=%WinSdk71%\Lib\x64;%LIB% + ... + nmake -f Make_mvc.mak ... WINVER=0x0501 SUBSYSTEM_VER=5.02 + +If you use Visual C++ 2015 (either Express or Community Edition), executing +msvc2015.bat will set them automatically. For x86 builds run this without +options: + msvc2015 +For x64 builds run this with the "x86_amd64" option: + msvc2015 x86_amd64 +This enables x86_x64 cross compiler. This works on any editions including +Express edition. +If you use Community (or Professional) edition, you can enable the x64 native +compiler by using the "x64" option: + msvc2015 x64 + +The following Visual C++ team blog can serve as a reference page: + http://blogs.msdn.com/b/vcblog/archive/2012/10/08/windows-xp-targeting-with-c-in-visual-studio-2012.aspx + + +OLDER VERSIONS + +The minimal supported version is Windows XP. Building with older compilers +might still work, but these instructions might be outdated. + +If you need the executable to run on Windows 98 or ME, use the 2003 one +|msvc-2003-toolkit|. + +Visual C++ Toolkit 2003 *msvc-2003-toolkit* +----------------------- + +You could download the Microsoft Visual C++ Toolkit 2003 from + http://msdn.microsoft.com/visualc/vctoolkit2003/ +Unfortunately this URL is no longer valid. Unofficial downloads appear to be +available from links mentioned on these pages (use at your own risk): + http://www.filewatcher.com/m/VCToolkitSetup.exe.32952488.0.0.html + http://feargame.net/wiki/index.php?title=Building_Source_with_the_VC2003_Toolkit + +This contains the command-line tools (compiler, linker, CRT headers, +and libraries) for Visual Studio .NET 2003, but not the Visual Studio IDE. +To compile and debug Vim with the VC2003 Toolkit, you will also need +|ms-platform-sdk|, |dotnet-1.1-redist|, |dotnet-1.1-sdk|, +and |windbg-download|. + +It's easier to download Visual C++ 2008 Express Edition, |msvc-2008-express|, +which is freely available in perpetuity. + +The free Code::Blocks IDE works with the VC2003 Toolkit, as described at + http://wiki.codeblocks.org/index.php?title=Integrating_Microsoft_Visual_Toolkit_2003_with_Code::Blocks_IDE +(This site also takes you through configuring a number of other +free C compilers for Win32.) + +To compile Vim using the VC2003 Toolkit and Make_mvc.mak, you must first +execute the following commands in a cmd.exe window (the msvcsetup.bat batch +file can be used): + + set PATH=%SystemRoot%\Microsoft.NET\Framework\v1.1.4322;%PATH% + call "%VCToolkitInstallDir%vcvars32.bat" + set MSVCVer=7.1 + call "%ProgramFiles%\Microsoft Platform SDK\SetEnv.Cmd" + set LIB=%ProgramFiles%\Microsoft Visual Studio .NET 2003\Vc7\lib;%LIB% + +Now you can build Vim with Make_mvc.mak. + + +Getting the Windows Platform SDK *ms-platform-sdk* + +You will also need a copy of the Windows Platform SDK. Specifically, you need +the Windows Core SDK subset of the Platform SDK, which contains the Windows +headers and libraries. You need to search for it, Microsoft keeps changing +the URL. + + +Getting the .NET Framework 1.1 Runtime *dotnet-1.1-redist* + +You need the .NET Framework 1.1 Redistributable Package from + http://www.microsoft.com/downloads/details.aspx?familyid=262d25e3-f589-4842-8157-034d1e7cf3a3 +or from Windows Update: + http://windowsupdate.microsoft.com/ +This is needed to install |dotnet-1.1-sdk|. It also contains cvtres.exe, +which is needed to link Vim. + + +Getting the .NET Framework 1.1 SDK *dotnet-1.1-sdk* + +You need the .NET Framework 1.1 SDK from + http://www.microsoft.com/downloads/details.aspx?familyid=9b3a2ca6-3647-4070-9f41-a333c6b9181d +This contains some additional libraries needed to compile Vim, +such as msvcrt.lib. You must install |dotnet-1.1-redist| before +installing the .NET 1.1 SDK. + + +Getting the WinDbg debugger *windbg-download* + +The Debugging Tools for Windows can be downloaded from + http://www.microsoft.com/whdc/devtools/debugging/default.mspx +This includes the WinDbg debugger, which you will want if you ever need +to debug Vim itself. An earlier version of the Debugging Tools +is also available through the Platform SDK, |ms-platform-sdk|. + + +Visual C++ 2005 Express Edition *msvc-2005-express* +------------------------------- + +Visual C++ 2005 Express Edition can be downloaded for free from: + http://msdn.microsoft.com/vstudio/express/visualC/default.aspx +This includes the IDE and the debugger. You will also need +|ms-platform-sdk|. You can build Vim with Make_mvc.mak. + +Instructions for integrating the Platform SDK into VC Express: + http://msdn.microsoft.com/vstudio/express/visualc/usingpsdk/default.aspx + + +2. MSYS2 with MinGW +=================== + +2.1. Setup the basic msys2 environment + +Go to the official page of MSYS2: https://www.msys2.org +Download an installer: + +* msys2-x86_64-YYYYMMDD.exe for 64-bit Windows + (Even if you want to build 32-bit Vim) +* msys2-i686-YYYYMMDD.exe for 32-bit Windows + +Execute the installer and follow the instructions to update basic packages. +At the end keep the checkbox checked to run msys2 now. If needed, you can +open the window from the start menu, MSYS2 64 bit / MSYS2 MSYS. + +Execute: + $ pacman -Syu + +And restart MSYS2 console (select "MSYS2 MSYS" icon from the Start Menu). +Then execute: + $ pacman -Su + +If pacman complains that `catgets` and `libcatgets` conflict with another +package, select `y` to remove them. + + +2.2. Install additional packages for building Vim + +The following package groups are required for building Vim: + +* base-devel +* mingw-w64-i686-toolchain (for building 32-bit Vim) +* mingw-w64-x86_64-toolchain (for building 64-bit Vim) + +(These groups also include some useful packages which are not used by Vim.) +Use the following command to install them: + + $ pacman -S base-devel mingw-w64-i686-toolchain mingw-w64-x86_64-toolchain + +Or you can use the `pacboy` command to avoid long package names: + + $ pacboy -S base-devel: toolchain:m + +The suffix ":" means that it disables the package name translation. +The suffix ":m" means both i686 and x86_64. You can also use the ":i" suffix +to install only i686, and the ":x" suffix to install only x86_64. +(See `pacboy help` for the help.) + +See also the pacman page in ArchWiki for the general usage of pacman: + https://wiki.archlinux.org/index.php/pacman + +MSYS2 has its own git package, and you can also install it via pacman: + + $ pacman -S git + + +2.3. Keep the build environment up-to-date + +After you have installed the build environment, you may want to keep it +up-to-date (E.g. always use the latest GCC). +In that case, you just need to execute the command: + $ pacman -Syu + + +2.4. Build Vim + +Select one of the following icon from the Start Menu: + +* MSYS2 MinGW 32-bit (To build 32-bit versions of Vim) +* MSYS2 MinGW 64-bit (To build 64-bit versions of Vim) + +Go to the source directory of Vim, then execute the make command. E.g.: + + make -f Make_ming.mak + make -f Make_ming.mak GUI=no + make -f Make_ming.mak GUI=no DEBUG=yes + +NOTE: you can't execute vim.exe in the MSYS2 console, open a normal Windows +console for that. You need to set $PATH to be able to build there, e.g.: + + set PATH=c:\msys64\mingw32\bin;c:\msys64\usr\bin;%PATH% + +This command is in msys32.bat. Or for the 64 bit compiler use msys64.bat: + + set PATH=c:\msys64\mingw64\bin;c:\msys64\usr\bin;%PATH% + +If you have msys64 in another location you will need to adjust the paths for +that. + + +3. MinGW +======== + +(written by Ron Aaron: ) + +This is about how to produce a Win32 binary of gvim with MinGW. + +First, you need to get the 'mingw32' compiler, which is free for the download +at: + + http://www.mingw.org/ + +or you can use 'MinGW-w64' compiler. + + http://mingw-w64.sourceforge.net/ + +Or a compiler provided on msys2: + + https://msys2.github.io/ + +Once you have downloaded the compiler binaries, unpack them on your hard disk +somewhere, and put them on your PATH. If you are on Win95/98 you can edit +your AUTOEXEC.BAT file with a line like: + + set PATH=C:\MinGW\bin;%PATH% + +or on NT/2000/XP, go to the Control Panel, (Performance and Maintenance), +System, Advanced, and edit the environment from there. If you use msys2 +compilers, set your installed paths (normally one of the following): + + C:\msys32\mingw32\bin (32-bit msys2, targeting 32-bit builds) + C:\msys64\mingw32\bin (64-bit msys2, targeting 32-bit builds) + C:\msys64\mingw64\bin (64-bit msys2, targeting 64-bit builds) + +Test if gcc is on your path. From a CMD (or COMMAND on '95/98) window: + + C:\> gcc --version + gcc (GCC) 4.8.1 + + C:\> mingw32-make --version + GNU Make 3.82.90 (...etc...) + +Now you are ready to rock 'n' roll. Unpack the vim sources (look on +www.vim.org for exactly which version of the vim files you need). + +Change directory to 'vim\src': + + C:\> cd vim\src + C:\VIM\SRC> + +and you type: + + mingw32-make -f Make_ming.mak gvim.exe + +After churning for a while, you will end up with 'gvim.exe' in the 'vim\src' +directory. + +You should not need to do *any* editing of any files to get vim compiled this +way. If, for some reason, you want the console-mode-only version of vim (this +is NOT recommended on Win32, especially on '95/'98!!!), you can use: + + mingw32-make -f Make_ming.mak GUI=no vim.exe + +If you are dismayed by how big the EXE is, I strongly recommend you get 'UPX' +(also free!) and compress the file (typical compression is 50%). UPX can be +found at + http://www.upx.org/ + +As of 2011, UPX still does not support compressing 64-bit EXE's; if you have +built a 64-bit vim then an alternative to UPX is 'MPRESS'. MPRESS can be found +at: + http://www.matcode.com/mpress.htm + + +ADDITION: NLS support with MinGW + +(by Eduardo F. Amatria ) + +If you want National Language Support, read the file src/po/README_mingw.txt. +You need to uncomment lines in Make_ming.mak to have NLS defined. + + +4. Cygwin +========= + +Use Make_cyg.mak with Cygwin's GCC. See + http://users.skynet.be/antoine.mechelynck/vim/compile.htm + +With Cygnus gcc you should use the Unix Makefile instead (you need to get the +Unix archive then). Then you get a Cygwin application (feels like Vim is +running on Unix), while with Make_cyg.mak you get a Windows application (like +with the other makefiles). + + +5. Borland +=========== + +Use Make_bc5.mak with Borland C++ 5.x. See + http://users.skynet.be/antoine.mechelynck/vim/compile.htm + + +6. Cross compiling for Win32 from a Linux machine +================================================= + +[Update of 1) needs to be verified] + +If you like, you can compile the 'mingw' Win32 version from the comfort of +your Linux (or other unix) box. To do this, you need to follow a few steps: + 1) Install the mingw32 cross-compiler. See + http://www.mingw.org/wiki/LinuxCrossMinGW + http://www.libsdl.org/extras/win32/cross/README.txt + 2) Get and unpack both the Unix sources and the extra archive + 3) in 'Make_cyg_ming.mak', set 'CROSS' to 'yes' instead of 'no'. + Make further changes to 'Make_cyg_ming.mak' and 'Make_ming.mak' as you + wish. If your cross-compiler prefix differs from the predefined value, + set 'CROSS_COMPILE' corresponding. + 4) make -f Make_ming.mak gvim.exe + +Now you have created the Windows binary from your Linux box! Have fun... + + +7. Building with Python support +=============================== + +For building with MSVC 2008 the "Windows Installer" from www.python.org +works fine. + +When building, you need to set the following variables at least: + + PYTHON: Where Python is installed. E.g. C:\Python27 + DYNAMIC_PYTHON: Whether dynamic linking is used. Usually, set to yes. + PYTHON_VER: Python version. E.g. 27 for Python 2.7.X. + +E.g. When using MSVC (as one line): + + nmake -f Make_mvc.mak + PYTHON=C:\Python27 DYNAMIC_PYTHON=yes PYTHON_VER=27 + +When using MinGW and link with the official Python (as one line): + + mingw32-make -f Make_ming.mak + PYTHON=C:/Python27 DYNAMIC_PYTHON=yes PYTHON_VER=27 + +When using msys2 and link with Python2 bundled with msys2 (as one line): + + mingw32-make -f Make_ming.mak PYTHON=c:/msys64/mingw64 + PYTHON_HOME=c:/msys64/mingw64 + PYTHONINC=-Ic:/msys64/mingw64/include/python2.7 + DYNAMIC_PYTHON=yes + PYTHON_VER=27 + DYNAMIC_PYTHON_DLL=libpython2.7.dll + STATIC_STDCPLUS=yes + +(This is for 64-bit builds. For 32-bit builds, replace mingw64 with mingw32.) +(STATIC_STDCPLUS is optional. Set to yes if you don't want to require +libstdc++-6.dll.) + + +(rest written by Ron Aaron: ) + +Building with the mingw32 compiler, and the ActiveState ActivePython: + http://www.ActiveState.com/Products/ActivePython/ + +After installing the ActivePython, you will have to create a 'mingw32' +'libpython20.a' to link with: + cd $PYTHON/libs + pexports python20.dll > python20.def + dlltool -d python20.def -l libpython20.a + +Once that is done, edit the 'Make_ming.mak' so the PYTHON variable points to +the root of the Python installation (C:\Python20, for example). If you are +cross-compiling on Linux with the mingw32 setup, you need to also convert all +the 'Include' files to *unix* line-endings. This bash command will do it +easily: + for fil in *.h ; do vim -e -c 'set ff=unix|w|q' $fil + +Now just do: + make -f Make_ming.mak gvim.exe + +You will end up with a Python-enabled, Win32 version. Enjoy! + + +8. Building with Python3 support +================================ + +For building with MSVC 2008 the "Windows Installer" from www.python.org +works fine. Python 3.6 is recommended. + +When building, you need to set the following variables at least: + + PYTHON3: Where Python3 is installed. E.g. C:\Python36 + DYNAMIC_PYTHON3: Whether dynamic linking is used. Usually, set to yes. + PYTHON3_VER: Python3 version. E.g. 36 for Python 3.6.X. + +E.g. When using MSVC (as one line): + + nmake -f Make_mvc.mak + PYTHON3=C:\Python36 DYNAMIC_PYTHON3=yes PYTHON3_VER=36 + +When using MinGW and link with the official Python3 (as one line): + + mingw32-make -f Make_ming.mak + PYTHON3=C:/Python36 DYNAMIC_PYTHON3=yes PYTHON3_VER=36 + +When using msys2 and link with Python3 bundled with msys2 (as one line): + + mingw32-make -f Make_ming.mak PYTHON3=c:/msys64/mingw64 + PYTHON3_HOME=c:/msys64/mingw64 + PYTHON3INC=-Ic:/msys64/mingw64/include/python3.6m + DYNAMIC_PYTHON3=yes + PYTHON3_VER=36 + DYNAMIC_PYTHON3_DLL=libpython3.6m.dll + STATIC_STDCPLUS=yes + +(This is for 64-bit builds. For 32-bit builds, replace mingw64 with mingw32.) +(STATIC_STDCPLUS is optional. Set to yes if you don't want to require +libstdc++-6.dll.) + + +9. Building with Racket or MzScheme support +======================================== + +1) Building with Racket support (newest) + +MzScheme and PLT Scheme names have been rebranded as Racket. Vim with Racket +support can be built with either MSVC or MinGW (or Cygwin). +Get it from https://download.racket-lang.org/ + +Copy lib/libracket{version}.dll to your Windows system directory. The system +directory depends on your Windows bitness and Vim bitness: + 32-bit Vim on 32-bit Windows: C:\Windows\System32 + 32-bit Vim on 64-bit Windows: C:\Windows\SysWOW64 + 64-bit Vim on 64-bit Windows: C:\Windows\System32 + +For building you need to set the following variables: + + MZSCHEME: Where Racket is installed. + E.g. C:\Program Files (x86)\Racket + DYNAMIC_MZSCHEME: Whether dynamic linking is used. Usually, set to yes. + MZSCHEME_VER: Racket DLL version which is used for the file name. + See below for a list of MZSCHEME_VER. + The DLL can be found under the lib directory. E.g. + C:\Program Files (x86)\Racket\lib\libracket3m_XXXXXX.dll + MZSCHEME_COLLECTS: (Optional) Path of the collects directory used at + runtime. Default: $(MZSCHEME)\collects + User can override this with the PLTCOLLECTS environment + variable. + +List of MZSCHEME_VER (incomplete): + + Racket ver. | MZSCHEME_VER + ========================== + 6.3 | 3m_9z0ds0 + 6.6 | 3m_a0solc + 6.8 | 3m_a1zjsw + 6.10 | 3m_a36fs8 + + +E.g. When using MSVC (as one line): + + nmake -f Make_mvc.mak + MZSCHEME="C:\Program Files (x86)\Racket" DYNAMIC_MZSCHEME=yes + MZSCHEME_VER=3m_9z0ds0 + +Or when using MinGW (as one line): + + mingw32-make -f Make_ming.mak + MZSCHEME='C:/Program\ Files\ (x86)/Racket' DYNAMIC_MZSCHEME=yes + MZSCHEME_VER=3m_9z0ds0 + + Spaces should be escaped with '\'. + + +2) Building with MzScheme support (older) + +(written by Sergey Khorev ) + +Vim with MzScheme (http://www.plt-scheme.org/software/mzscheme) support can +be built with either MSVC, or MinGW, or Cygwin. Supported versions are 205 and +above (including 299 and 30x series). + +The MSVC build is quite straightforward. Simply invoke (in one line) +nmake -fMake_mvc.mak MZSCHEME= + [MZSCHEME_VER=] [DYNAMIC_MZSCHEME=] +where is the last seven characters from MzScheme dll name +(libmzschXXXXXXX.dll). +If DYNAMIC_MZSCHEME=yes, resulting executable will not depend on MzScheme +DLL's, but will load them in runtime on demand. + +Building dynamic MzScheme support on MinGW and Cygwin is similar. Take into +account that should contain slashes rather than backslashes +(e.g. d:/Develop/MzScheme) + +"Static" MzScheme support (Vim executable will depend on MzScheme DLLs +explicitly) on MinGW and Cygwin requires additional step. + +libmzschXXXXXXX.dll and libmzgcXXXXXXX.dll should be copied from +%WINDOWS%\System32 to other location (either build directory, some temporary +dir or even MzScheme home). + +Pass that path as MZSCHEME_DLLS parameter for Make. E.g., +make -f Make_cyg.mak MZSCHEME=d:/Develop/MzScheme MZSCHEME_VER=209_000 + MZSCHEME_DLLS=c:/Temp DYNAMIC_MZSCHEME=no + +After a successful build, these dlls can be freely removed, leaving them in +%WINDOWS%\System32 only. + + + +10. Building with Lua support +============================ + +Vim with Lua support can be built with either MSVC or MinGW (or maybe Cygwin). +You can use binaries from LuaBinaries: http://luabinaries.sourceforge.net/ +This also applies to when you get a Vim executable and don't build yourself, +do the part up to "Build". + +1) Download and install LuaBinaries + +Go to the Download page of LuaBinaries: + http://luabinaries.sourceforge.net/download.html + +Download lua-X.Y.Z_Win32_dllw4_lib.zip for x86 or +lua-X.Y.Z_Win64_dllw4_lib.zip for x64. You can use them both for MSVC and +MinGW. + +Unpack it to a working directory. E.g. C:\projects\lua53. +Lua's header files will be installed under the include directory. + +Copy luaXY.dll to your Windows system directory. The system directory depends +on your Windows bitness and Vim bitness: + 32-bit Vim on 32-bit Windows: C:\Windows\System32 + 32-bit Vim on 64-bit Windows: C:\Windows\SysWOW64 + 64-bit Vim on 64-bit Windows: C:\Windows\System32 + +Or another option is copying luaXY.dll to the directory where gvim.exe +(or vim.exe) is. + + +2) Build + +You need to set LUA, DYNAMIC_LUA and LUA_VER. + + LUA: Where Lua's header files are installed. E.g. C:\projects\lua53. + DYNAMIC_LUA: Whether dynamic linking is used. Set to yes. + LUA_VER: Lua version. E.g. 53 for Lua 5.3.X. + +E.g. When using MSVC (as one line): + + nmake -f Make_mvc.mak + LUA=C:\projects\lua53 DYNAMIC_LUA=yes LUA_VER=53 + +Or when using MinGW (as one line): + + mingw32-make -f Make_ming.mak + LUA=C:/projects/lua53 DYNAMIC_LUA=yes LUA_VER=53 + + +Or when using Cygwin (as one line) (untested): + + make -f Make_cyg.mak + LUA=/cygdrive/c/projects/lua53 DYNAMIC_LUA=yes LUA_VER=53 + + +11. Building with Perl support +============================== + +Vim with Perl support can be built with either MSVC or MinGW (or Cygwin). +You can use binaries from ActiveState (ActivePerl) or Strawberry Perl. + + http://www.activestate.com/activeperl + http://strawberryperl.com/ + +When building, you need to set the following variables: + + PERL: Where perl is installed. E.g. C:\Perl, C:\Strawberry\perl + DYNAMIC_PERL: Whether dynamic linking is used. Usually, set to yes. + PERL_VER: Perl version. E.g. 522 for Perl 5.22.X. + +E.g. When using MSVC (as one line): + + nmake -f Make_mvc.mak + PERL=C:\Perl DYNAMIC_PERL=yes PERL_VER=522 + +Or when using MinGW (as one line): + + mingw32-make -f Make_ming.mak + PERL=C:/Perl DYNAMIC_PERL=yes PERL_VER=522 + + +12. Building with Ruby support +============================== + +Vim with Ruby support can be built with either MSVC or MinGW (or Cygwin). +Ruby doesn't provide the official Windows binaries. The most widely used +Windows binaries might be RubyInstaller. Currently Ruby 2.4 is recommended. + + http://rubyinstaller.org/ + +If you use MinGW you can easily build with RubyInstaller, but if you use MSVC +you need some tricks described below. +(Another binary distribution is ActiveScriptRuby: + http://www.artonx.org/data/asr/) + +When building, you need to set the following variables at least: + + RUBY: Where ruby is installed. E.g. C:\Ruby24 + DYNAMIC_RUBY: Whether dynamic linking is used. Usually, set to yes. + RUBY_VER: Ruby version. E.g. 24 for Ruby 2.4.X. + RUBY_API_VER_LONG: Ruby API version in a long format. + E.g. 2.4.0 for Ruby 2.4.X. + +Ruby version vs. Ruby API version: + + Ruby ver. | Ruby API ver. + ========================= + 1.8.X | 1.8 + 1.9.[1-3] | 1.9.1 + 2.0.0 | 2.0.0 + 2.X.Y | 2.X.0 + +(Ruby 1.9.0 is excluded from the table because it is an unstable version.) + + +A) Using MSVC + +If you want to link with ruby, normally you must use the same compiler as +which was used to build the ruby binary. RubyInstaller is built with MinGW, +so normally you cannot use MSVC for building Vim if you want to link with +RubyInstaller. If you use a different compiler, there are mainly two problems: +config.h and Ruby's DLL name. Here are the steps for working around them: + + 1) Download and Install RubyInstaller. + You can install RubyInstaller with the default options and directory. + E.g.: + C:\Ruby24 (32-bit) or C:\Ruby24-x64 (64-bit) + + Ruby 2.4.X is used in this example. + + 2) Download Ruby 2.4.X's source code and generate config.h: + + cd C:\projects + git clone https://github.com/ruby/ruby.git -b ruby_2_4 + cd ruby + win32\configure.bat + nmake .config.h.time + + Note that ruby_2_4 is the branch name for Ruby 2.4.X's source code. + There is no need to build whole Ruby, just config.h is needed. + If you use 32-bit MSVC 2015, the config.h is generated in the + .ext\include\i386-mswin32_140 directory. + If you use 64-bit MSVC 2015, the config.h is generated in the + .ext\include\x64-mswin64_140 directory. + + 3) Install the generated config.h. + + For 32-bit version: + + xcopy /s .ext\include C:\Ruby24\include\ruby-2.4.0 + + For 64-bit version: + + xcopy /s .ext\include C:\Ruby24-x64\include\ruby-2.4.0 + + Note that 2.4.0 is Ruby API version of Ruby 2.4.X. + You may need to close the console and reopen it to pick up the new $PATH. + + 4) Build Vim. Note that you need to adjust some variables (as one line): + + For 32-bit version: + + nmake -f Make_mvc.mak + RUBY=C:\Ruby24 DYNAMIC_RUBY=yes RUBY_VER=24 RUBY_API_VER_LONG=2.4.0 + RUBY_MSVCRT_NAME=msvcrt + WINVER=0x501 + + For 64-bit version, replace RUBY=C:\Ruby24 with RUBY=C:\Ruby24-x64. + + If you set WINVER explicitly, it must be set to >=0x500, when building + with Ruby 2.1 or later. (Default is 0x501.) + When using this trick, you also need to set RUBY_MSVCRT_NAME to msvcrt + which is used for the Ruby's DLL name. + +B) Using MinGW + +Using MinGW is easier than using MSVC when linking with RubyInstaller. +After you install RubyInstaller, just type this (as one line): + + mingw32-make -f Make_ming.mak + RUBY=C:/Ruby24 DYNAMIC_RUBY=yes RUBY_VER=24 RUBY_API_VER_LONG=2.4.0 + WINVER=0x600 + +For 64-bit version, replace RUBY=C:/Ruby24 with RUBY=C:/Ruby24-x64. +If you set WINVER explicitly, it must be set to >=0x500, when building with +Ruby 2.1 or later. (Default is 0x600.) + + + +13. Building with Tcl support +============================= + +Vim with Tcl support can be built with either MSVC or MinGW (or Cygwin). +You can use binaries from ActiveState (ActiveTcl). + + http://www.activestate.com/activetcl + +Alternatively, you can use the binaries provided by IronTcl from + + https://www.irontcl.com/ + +They might lack behind the latest version a bit, but should provide 64bit +and 32bit versions even if ActiveTcl does not provide them anymore. + +For building with MSVC 2015 use version 8.6.6 or later. +When building, you need to set the following variables: + + TCL: Where tcl is installed. E.g. C:\Tcl86 + DYNAMIC_TCL: Whether dynamic linking is used. Usually, set to yes. + TCL_VER: Tcl version in a short format. E.g. 86 for Tcl 8.6.X. + TCL_VER_LONG: Tcl version in a long format. E.g. 8.6 for Tcl 8.6.X. + +Sometimes the Tcl dll name changes. E.g. ActiveTcl 8.6.4 comes with tcl86.dll, +but ActiveTcl 8.6.6 comes with tcl86t.dll. You can set the dll name by setting +the TCL_DLL variable: + TCL_DLL=tcl86t.dll + +E.g. When using MSVC (as one line): + + nmake -f Make_mvc.mak + TCL=C:\Tcl86 DYNAMIC_TCL=yes TCL_VER=86 TCL_VER_LONG=8.6 + +Or when using MinGW (as one line): + + mingw32-make -f Make_ming.mak + TCL=C:/Tcl86 DYNAMIC_TCL=yes TCL_VER=86 TCL_VER_LONG=8.6 + + +14. Building with Terminal support +================================== + +Vim with Terminal support can be built with either MSVC, MinGW or Cygwin. +This uses the included libvterm and winpty. No extra header files or +libraries are needed for building. Just set TERMINAL to yes. + +E.g. When using MSVC: + + nmake -f Make_mvc.mak TERMINAL=yes + +Or when using MinGW: + + mingw32-make -f Make_ming.mak TERMINAL=yes + + +15. Building with DirectX (DirectWrite) support +=============================================== + +Vim with DirectX (DirectWrite) support can be built with either MSVC or MinGW. +This requires dwrite_2.h and some other header files which come with Windows +SDK 8.1 or later (or MinGW-w64), if you want to enable color emoji support. +This also requires MBYTE=yes which is enabled by default. + +A) Using MSVC + +If you use MSVC 2013 or later, Windows SDK 8.1 or later is used by default. +You just need to specify DIRECTX=yes: + + nmake -f Make_mvc.mak DIRECTX=yes + +If you use MSVC 2012 or earlier, the required header files are not available +by default. However, you can use the header files from newer SDKs with older +compilers. E.g.: + + set "INCLUDE=%INCLUDE%;C:\Program Files (x86)\Windows Kits\8.1\Include\um" + nmake -f Make_mvc.mak DIRECTX=yes + +If you don't need color emoji support, only dwrite.h is required. You can use +older compilers (e.g. VC2010) without Windows SDK 8.1. E.g.: + + nmake -f Make_mvc.mak DIRECTX=yes COLOR_EMOJI=no + +B) Using MinGW-w64 + +Just set DIRECTX to yes: + + mingw32-make -f Make_ming.mak DIRECTX=yes + + +16. Windows 3.1x +================ + +The Windows 3.1x support was removed in patch 7.4.1364. + + +17. MS-DOS +========== + +The MS-DOS support was removed in patch 7.4.1399. Only very old Vim versions +work on MS-DOS because of the limited amount of memory available. + + +18. Installing after building from sources +========================================== + +[provided by Michael Soyka, updated by Ken Takata] + +After you've built the Vim binaries as described above, you're ready to +install Vim on your system. However, if you've obtained the Vim sources +using Git, Mercurial or by downloading them as a unix tar file, you must +first create a "vim81" directory. If you instead downloaded the sources as +zip files, you can skip this setup as the zip archives already have the +correct directory structure. + + A. Create a Vim "runtime" subdirectory named "vim81" + ----------------------------------------------------- + If you obtained your Vim sources as zip files, you can skip this step. + Otherwise, continue reading. + + Go to the directory that contains the Vim "src" and "runtime" + directories and create a new subdirectory named "vim81". + + Copy the "runtime" files into "vim81": + copy runtime\* vim81 + + B. Copy the new binaries into the "vim81" directory + ---------------------------------------------------- + Regardless of how you installed the Vim sources, you need to copy the + new binaries you created above into "vim81": + + copy src\*.exe vim81 + copy src\tee\tee.exe vim81 + copy src\xxd\xxd.exe vim81 + + To install the "Edit with Vim" popup menu, you need both 32-bit and 64-bit + versions of gvimext.dll. They should be copied to "vim81\GvimExt32" and + "vim81\GvimExt64" respectively. + First, build the 32-bit version, then: + + mkdir vim81\GvimExt32 + copy src\GvimExt\gvimext.dll vim81\GvimExt32 + + Next, clean the 32-bit version and build the 64-bit version, then: + + mkdir vim81\GvimExt64 + copy src\GvimExt\gvimext.dll vim81\GvimExt64 + + C. Copy gettext and iconv DLLs into the "vim81" directory + ---------------------------------------------------------- + Get gettext and iconv DLLs from the following site: + https://github.com/mlocati/gettext-iconv-windows/releases + Both 64- and 32-bit versions are needed. + Download the files gettextX.X.X.X-iconvX.XX-shared-{32,64}.zip, extract + DLLs and place them as follows: + + vim81\ + | libintl-8.dll + | libiconv-2.dll + | libgcc_s_sjlj-1.dll (only for 32-bit) + | + + GvimExt32\ + | libintl-8.dll + | libiconv-2.dll + | libgcc_s_sjlj-1.dll + | + ` GvimExt64\ + libintl-8.dll + libiconv-2.dll + + The DLLs in the "vim81" should be the same bitness with the (g)vim.exe. + + D. Move the "vim81" directory into the Vim installation subdirectory + --------------------------------------------------------------------- + Move the "vim81" subdirectory into the subdirectory where you want Vim + to be installed. Typically, this subdirectory will be named "vim". + If you already have a "vim81" subdirectory in "vim", delete it first + by running its uninstal.exe program. + + E. Install Vim + --------------- + "cd" to your Vim installation subdirectory "vim\vim81" and run the + "install.exe" program. It will ask you a number of questions about + how you would like to have your Vim setup. Among these are: + - You can tell it to write a "_vimrc" file with your preferences in the + parent directory. + - It can also install an "Edit with Vim" entry in the Windows Explorer + popup menu. + - You can have it create batch files, so that you can run Vim from the + console or in a shell. You can select one of the directories in your + PATH or add the directory to PATH using the Windows Control Panel. + - Create entries for Vim on the desktop and in the Start menu. + +Happy Vimming! diff --git a/src/INSTALLvms.txt b/src/INSTALLvms.txt new file mode 100644 index 0000000..cbcbc49 --- /dev/null +++ b/src/INSTALLvms.txt @@ -0,0 +1,393 @@ +INSTALLvms.txt - Installation of Vim on OpenVMS + +Maintainer: Zoltan Arpadffy +Last change: 2008 Jan 06 + +This file contains instructions for compiling Vim on Openvms. +If you already have an executable version of Vim, you don't need this. + +If you skip settings described here, then you will get the default Vim +behavior as it is documented, which should be fine for most users. + +The file "feature.h" can be edited to match your preferences, but this files +does not describe possibilities hidden in feature.h acrobatics, however +parameters from MAKE_VMS.MMS actively uses and sets up parameters in relation +with feature.h + +More information and case analysis you can found in os_vms.txt +([runtime.doc]os_vms.txt or :help vms from vim prompt) + +Contents: +1. Download files +2. Configuration +3. Compilation DECC +4. Compilation VAXC +5. CTAGS, XXD +6. Deployment +7. GTK and other features +8. Notes +9. Authors + +---------------------------------------------------------------------------- +1. Download files + +1.1. Visit the Vim ftp site (see ftp://ftp.vim.org/pub/vim/MIRRORS) + and obtain the following three files: + + unix/vim-X.X-src.tar.gz + unix/vim-X.X-rt.tar.gz + extra/vim-X.X-extra.tar.gz + + where X.X is the version number. + +1.2. Expand the three archives. + +1.3. Apply patches if they exist. (Patch files are found in the ftp + site in the "patches" directory.) + +1.4. You will need either the DECSET mms utility or the freely available clone + of it called mmk (VMS has no make utility in the standard distribution). + You can download mmk from http://www.openvms.digital.com/freeware/MMK/ + +1.5. If you want to have Perl, Python or Tcl support in Vim you will need VMS + distributions for them as well. + +1.6 If you want to have GTK executable, you need to have properly installed + GTK libraries. + +NOTE: procedure in chapter 1 describes source code preparation from multi OS +code, however it is available OpenVMS optimized (and tested) source code from: +ftp://ftp.polarhome.com/pub/vim/source/vms/ +(http://www.polarhome.com/vim/files/source/vms/) + +Current OpenVMS source code as .zip or .tar.gz file is possible to download +from CVS mirror ftp://ftp.polarhome.com/pub/cvs/SOURCE/ +(http://www.polarhome.com/cvs/SOURCE/) + +2. Configuration + +2.1. Edit vim-X.X/src/feature.h for your preference. (You can skip + this, then you will get the default behavior as is documented, + which should be fine for most people.) + + For example, if you want to add the MULTI_BYTE feature, turn on + #define MULTI_BYTE + +2.2 Edit vim-X.X/src/Make_vms.mms to customize your Vim. Options are: + + Parameter name : MODEL + Description : Build model selection + Options: : TINY - Almost no features enabled, not even + multiple windows + SMALL - Few features enabled, as basic as possible + NORMAL - A default selection of features enabled + BIG - Many features enabled, as rich as possible. + (OpenVMS default) + HUGE - All possible features enabled. + Uncommented - will default to BIG + Default : MODEL = BIG + + Parameter name : GUI + Description : GUI or terminal mode executable + Options: : YES - GUI executable + Uncommented - char only + Default : GUI = YES + + Parameter name : GTK + Description : Enable GTK in GUI mode. + It enables features as toolbar etc. + Options: : YES - GTK executable + Uncommented - without GTK + Default : Uncommented + + Parameter name : XPM + Description : Enable XPM libraries in GUI/Motif mode. + It enables features as toolbar etc. + Options: : YES - GUI executable + Uncommented - without XPM + Default : Uncommented + + Parameter name : DECC + Description : Compiler selection + Options: : YES - DECC compiler + Uncommented - VAXC compiler + Default : DECC = YES + + Parameter name : CCVER + Description : Compiler version with :ver command + Options: : YES - Compiler version info will be added + Uncommented - will not be added + Default : CCVER = YES + + Parameter name : DEBUG + Description : Building a debug version + Options: : YES - debug version will be built + Uncommented - building normal executable + Default : Uncommented + + Parameter name : VIM_TCL + Description : Add Tcl support + Options: : YES - Build with support + Uncommented - build without support. + Default : Uncommented + + Parameter name : VIM_PERL + Description : Add Perl support + Options: : YES - Build with support + Uncommented - build without support. + Default : Uncommented + + Parameter name : VIM_PYTHON + Description : Add Python support + Options: : YES - Build with support + Uncommented - build without support. + Default : Uncommented + + Parameter name : VIM_XIM + Description : X Input Method. For entering special languages + like chinese and Japanese. Please define just + one: VIM_XIM or VIM_HANGULIN + Options: : YES - Build with support + Uncommented - build without support. + Default : Uncommented + + Parameter name : VIM_HANGULIN + Description : Internal Hangul input method. GUI only. + Please define just one: VIM_XIM or VIM_HANGULIN + Options: : YES - Build with support + Uncommented - build without support. + Default : Uncommented + + Parameter name : VIM_TAG_ANYWHITE + Description : Allow any white space to separate the fields in a + tags file + When not defined, only a TAB is allowed. + Options: : YES - Build with support + Uncommented - build without support. + Default : Uncommented + + You can edit the *_INC and *_LIB qualifiers, but it is really + not recommended for beginners. + +3. Compilation DECC + +3.1. If you have MSS on your system, the command + + mms /descrip=Make_vms.mms + + will start building your own customized version of Vim. + The adequate command for mmk is: + + mmk /descrip=Make_vms.mms + + NOTE: Because of empty /auto/config.h (needed for Unix configure) build + will fail with very strange messages. Therefore before building, it is + recommended to make one clean up, to prepare everything for OpenVMS + development. The command is: + + mms /descrip=Make_vms.mms clean + +4. Compilation VAXC + +4.1. VAXC compiler is not fully ANSI C compatible in pre-processor directives + semantics, therefore you have to use a converter program what will do the + lion part of the job. + + @os_vms_fix.com *.c *.h <.proto>*.pro + + more information can be found in os_vms_fix.com file itself. + + NOTE: even if os_vms_fix.com will fix all pre-processor directives it will + leave singe (long) line directives. You have to fix them manually. + Known problematic files are option.h and option.c + +4.2. After the conversion you can continue building as it has been described + above. + +5. CTAGS, XXD + +5.1. MMS_VIM.EXE is building together with VIM.EXE, but for CTAGS.EXE and + XXD.EXE you should change to subdirectory <.CTAGS> or <.XXD> and build + them separately. + +5.2. In these directories you can found one make file for VMS as well. + Please read the detailed build instructions in the related *.MMS file. + +6. Deployment + +6.1. Copy over all executables to the deployment directory. + +6.2. Vim uses a special directory structure to hold the document and runtime + files: + + vim (or wherever) + |-- doc + |-- syntax + vimrc (system rc files) + gvimrc + +6.3 Define logicals VIM + + define/nolog VIM device:[leading-path-here.vim] + + to get vim.exe to find its document, filetype, and syntax files. + + Now, if you are lucky you should have one own built, customized and + working Vim. + +7. GTK and other features + +7.1 General notes + + To be able to build external GUI or language support you have to enable + related feature in MAKE_VMS.MMS file. Usually it need some extra tuning + around include files, shared libraries etc. + + Please note, that leading "," are valuable for MMS/MMK syntax. + + MAKE_VMS.MMS uses defines as described below: + +7.1.1 feature_DEF = ,"SOME_FEATURE" + + Submits definition to compiler preprocessor to enable code blocks + defined with + #ifdef SOME_FEATURE + {some code here} + #endif + + Example: TCL_DEF = ,"FEAT_TCL" + + +7.1.2 feature_SRC = code1.c code2.c + + Defines source code related with particular feature. + Example: TCL_SRC = if_tcl.c + +7.1.3 feature_OBJ = code1.obj code2.obj + + Lists objects created from source codes listed in feature_SRC + Example: PERL_OBJ = if_perlsfio.obj if_perl.obj + +7.1.4 feature_LIB = ,OS_VMS_TCL.OPT/OPT + + Defines the libraries that have to be used for build. + If it is an OPT file then MAKE_VMS.MMS creates OPT files + in gen_feature procedure. + + Example: + PERL_LIB = ,OS_VMS_PERL.OPT/OPT + +.IFDEF VIM_PERL +perl_env : + -@ write sys$output "creating OS_VMS_PERL.OPT file." + -@ open/write opt_file OS_VMS_PERL.OPT + -@ write opt_file "PERLSHR /share" + -@ close opt_file +.ELSE +perl_env : + -@ ! +.ENDIF + + +7.1.5 feature_INC = ,dka0:[tcl80.generic] + + Defines the directory where the necessary include files are. + Example: TCL_INC = ,dka0:[tcl80.generic] + +7.2 GTK + + To build VIM with GTK you have to install GTK on your OpenVMS. + So far it works just on Alpha and IA64. More information at: + http://www.openvms.compaq.com/openvms/products/ips/gtk.html + + You need also the OpenVMS Porting Library: + http://www.openvms.compaq.com/openvms/products/ips/porting.html + + Source code for GTK and porting library that is used to build + VMS executables at polarhome.com are at + http://www.polarhome.com/vim/files/source/vms/ + + Enable GTK in make_vms.mms file with GTK = YES + Define GTK_ROOT that points to your GTK root directory. + + You will need to edit GTKDIR variable in order to point + to GTK header files and libraries. + + GTK_DIR = ALPHA$DKA0:[GTK128.] + + ".]" at the end is very important. + + Build it as normally. + + Used sharable images are: + gtk_root:[glib]libglib.exe /share,- + gtk_root:[glib.gmodule]libgmodule.exe /share,- + gtk_root:[gtk.gdk]libgdk.exe /share,- + gtk_root:[gtk.gtk]libgtk.exe /share + + During runtime it is suggested to have all these files installed and + copied to SYS$LIBRARY: to be able to use it without problems. + Also VMS_JACKETS.EXE from OpenVMS Porting Library. + + Please note, that GTK uses /name=(as_is,short)/float=ieee/ieee=denorm + compiler directives that is not compatible with "standard" VMS usage, + therefore other external features might fail as PERL, PYTHON and TCL + support. + +7.3 PERL + + You have to install OpenVMS perl package from: + http://www.openvms.compaq.com/openvms/products/ips/apache/csws_perl_relnotes.html or build on your own from sources downloaded from http://www.perl.org + + You need defined PERLSHR logical that points to PERL shareable image + (or you can just copy over to SYS$LIBRARY:) + + Enable Perl feature at make_vms.mms with VIM_PERL = YES + + Edit PERL_INC = to point to perl includes directory where is extern.h + + Build as usually. + +7.4 PYTHON + + You have to install an OpenVMS python package. + Set up the normal Python work environment. + + You have to have defined PYTHON_INCLUDE and PYTHON_OLB logicals. + PYTHON_INCLUDE should point to Python include files where for ex: + python.h is located. + Enable Python feature at make_vms.mms with VIM_PYTHON = YES + + Build as usually. + +7.5 TCL + + You have to install an OpenVMS TCL package. + Set up the normal TCL work environment. + + You have to have defined TCLSHR logical that points to shareable image. + + Enable TCL feature at make_vms.mms with VIM_TCL = YES + + Edit TCL_INC = to point to TCL includes directory where is tcl.h + + Build as usually. + +8. Notes + +8.1. New Compaq C compiler + + If you are using Compaq C compiler V6.2 or newer, Informational messages + of the type QUESTCOMPARE will be displayed. You should ignore those + messages ; they are generated only because some test comparisons are done + with variables which type vary depending on the OS. Under VMS, those are + "unsigned" and the compiler issue a message whenever the comparison is + done with '<=' to 0. However, the code is correct and will behave as + expected. + ( Jerome Lauret Vim 6.0n ) + NOTE: from version 6.0ad Vim code has been reviewed and these warnings + have been corrected. + +9. Authors + + Initial version, 2000 Jul 19, Zoltan Arpadffy diff --git a/src/INSTALLx.txt b/src/INSTALLx.txt new file mode 100644 index 0000000..e03f54d --- /dev/null +++ b/src/INSTALLx.txt @@ -0,0 +1,165 @@ +INSTALLx.txt - cross-compiling Vim on Unix + +Content: + 1. Introduction + 2. Necessary arguments for "configure" + 3. Necessary environment variables for "configure" + 4. Example + + +1. INTRODUCTION +=============== + +This document discusses cross-compiling VIM on Unix-like systems. We assume +you are already familiar with cross-compiling and have a working cross-compile +environment with at least the following components: + + * a cross-compiler + * a libc to link against + * ncurses library to link against + +Discussing how to set up a cross-compile environment would go beyond the scope +of this document. See http://www.kegel.com/crosstool/ for more information and +a script that aids in setting up such an environment. + + +The problem is that "configure" needs to compile and run small test programs +to check for certain features. Running these test programs can't be done when +cross-compiling so we need to pass the results these checks would produce via +environment variables. See the list of variables and the examples at the end of +this document. + + +2. NECESSARY ARGUMENTS FOR "configure" +====================================== + +You need to set the following "configure" command line switches: + +--build=... : + The build system (i.e. the platform name of the system you compile on + right now). + For example, "i586-linux". + +--host=... : + The system on which VIM will be run. Quite often this the name of your + cross-compiler without the "-gcc". + For example, "powerpc-603-linux-gnu". + +--target=... : + Only relevant for compiling compilers. Set this to the same value as + --host. + +--with-tlib=... : + Which terminal library to use. + For example, "ncurses". + + +3. NECESSARY ENVIRONMENT VARIABLES FOR "configure" +================================================== + +Additionally to the variables listed here you might want to set the CPPFLAGS +environment variable to enable optimization for your target system (e.g. +"CPPFLAGS=-march=arm5te"). + +The following variables need to be set: + +ac_cv_sizeof_int: + The size of an "int" C type in bytes. Should be "4" on all 32bit + machines. + +vi_cv_path_python_conf: + If Python support is enabled, set this variable to the path for + Python's library implementation. This is a path like + "/usr/lib/pythonX.Y/config" (the directory contains a file + "config.c"). + +vi_cv_var_python_epfx: + If Python support is enabled, set this variable to the execution + prefix of your Python interpreter (that is, where it thinks it is + running). + This is the output of the following Python script: + import sys; print sys.exec_prefix + +vi_cv_var_python_pfx: + If Python support is enabled, set this variable to the prefix of your + Python interpreter (that is, where it was installed). + This is the output of the following Python script: + import sys; print sys.prefix + +vi_cv_var_python_version: + If Python support is enabled, set this variable to the version of the + Python interpreter that will be used. + This is the output of the following Python script: + import sys; print sys.version[:3] + +vim_cv_bcopy_handles_overlap: + Whether the "memmove" C library call is able to copy overlapping + memory regions. Set to "yes" if it does or "no" if it does not. + You only need to set this if vim_cv_memmove_handles_overlap is set + to "no". + +vim_cv_getcwd_broken: + Whether the "getcwd" C library call is broken. Set to "yes" if you + know that "getcwd" is implemented as 'system("sh -c pwd")', set to + "no" otherwise. + +vim_cv_memcpy_handles_overlap: + Whether the "memcpy" C library call is able to copy overlapping + memory regions. Set to "yes" if it does or "no" if it does not. + You only need to set this if both vim_cv_memmove_handles_overlap + and vim_cv_bcopy_handles_overlap are set to "no". + +vim_cv_memmove_handles_overlap: + Whether the "memmove" C library call is able to copy overlapping + memory regions. Set to "yes" if it does or "no" if it does not. + +vim_cv_stat_ignores_slash: + Whether the "stat" C library call ignores trailing slashes in the path + name. Set to "yes" if it ignores them or "no" if it does not ignore + them. + +vim_cv_tgetent: + Whether the "tgetent" terminal library call returns a zero or non-zero + value when it encounters an unknown terminal. Set to either the string + "zero" or "non-zero", corresponding. + +vim_cv_terminfo: + Whether the environment has terminfo support. Set to "yes" if so, + otherwise set to "no". + +vim_cv_toupper_broken: + Whether the "toupper" C library function works correctly. Set to "yes" + if you know it's broken, otherwise set to "no". + +vim_cv_tty_group: + The default group of pseudo terminals. Either set to the numeric value + of your tty group or to "world" if they are world accessible. + +vim_cv_tty_mode: + The default mode of pseudo terminals if they are not world accessible. + Most probably the value "0620". + + +4. EXAMPLE: +=========== + +Assuming the target system string is "armeb-xscale-linux-gnu" (a Intel XScale +system) with glibc and ncurses, the call to configure would look like this: + +ac_cv_sizeof_int=4 \ +vim_cv_getcwd_broken=no \ +vim_cv_memmove_handles_overlap=yes \ +vim_cv_stat_ignores_slash=yes \ +vim_cv_tgetent=zero \ +vim_cv_terminfo=yes \ +vim_cv_toupper_broken=no \ +vim_cv_tty_group=world \ +./configure \ + --build=i586-linux \ + --host=armeb-xscale-linux-gnu \ + --target=armeb-xscale-linux-gnu \ + --with-tlib=ncurses + + + +Written 2007 by Marc Haisenko for the VIM project. diff --git a/src/Make_all.mak b/src/Make_all.mak new file mode 100644 index 0000000..591350f --- /dev/null +++ b/src/Make_all.mak @@ -0,0 +1,15 @@ +# +# Common Makefile, defines the list of tests to run and other things. +# + +# Argument for running ctags. +TAGS_FILES = \ + *.c \ + *.cpp \ + *.h \ + auto/*.c \ + libvterm/src/*.c \ + libvterm/src/*.h \ + libvterm/include/*.h \ + xdiff/*.c \ + xdiff/*.h diff --git a/src/Make_bc5.mak b/src/Make_bc5.mak new file mode 100644 index 0000000..b2977e7 --- /dev/null +++ b/src/Make_bc5.mak @@ -0,0 +1,1060 @@ +# +# Makefile for Vim. +# Compiler: Borland C++ 5.0 and later 32-bit compiler +# Targets: Win32 (Windows NT and Windows 95) (with/without GUI) +# +# NOTE: THIS IS OLD AND PROBABLY NO LONGER WORKS. +# +# Contributed by Ben Singer. +# Updated 4/1997 by Ron Aaron +# 2016: removed support for 16 bit DOS +# 6/1997 - added support for 16 bit DOS +# Note: this has been tested, and works, for BC5. Your mileage may vary. +# Has been reported NOT to work with BC 4.52. Maybe it can be fixed? +# 10/1997 - ron - fixed bugs w/ BC 5.02 +# 8/1998 - ron - updated with new targets, fixed some stuff +# 3/2000 - Bram: Made it work with BC 5.5 free command line compiler, +# cleaned up variables. +# 6/2001 - Dan - Added support for compiling Python and TCL +# 7/2001 - Dan - Added support for compiling Ruby +# +# It builds on Windows 95 and NT-Intel, producing the same binary in either +# case. To build using Microsoft Visual C++, use Make_mvc.mak. +# +# This should work with the free Borland command line compiler, version 5.5. +# You need at least sp1 (service pack 1). With sp2 it compiles faster. +# Use a command like this: +# \bin\make /f Make_bc5.mak BOR= +# + +# let the make utility do the hard work: +.AUTODEPEND +.CACHEAUTODEPEND + +# VARIABLES: +# name value (default) +# +# BOR path to root of Borland C install (c:\bc5) +# LINK name of the linker ($(BOR)\bin\ilink32) +# GUI no or yes: set to yes if you want the GUI version (yes) +# LUA define to path to Lua dir to get Lua support (not defined) +# LUA_VER define to version of Lua being used (51) +# DYNAMIC_LUA no or yes: set to yes to load the Lua DLL dynamically (no) +# PERL define to path to Perl dir to get Perl support (not defined) +# PERL_VER define to version of Perl being used (56) +# DYNAMIC_PERL no or yes: set to yes to load the Perl DLL dynamically (no) +# PYTHON define to path to Python dir to get PYTHON support (not defined) +# PYTHON_VER define to version of Python being used (22) +# DYNAMIC_PYTHON no or yes: use yes to load the Python DLL dynamically (no) +# PYTHON3 define to path to Python3 dir to get PYTHON3 support (not defined) +# PYTHON3_VER define to version of Python3 being used (31) +# DYNAMIC_PYTHON3 no or yes: use yes to load the Python3 DLL dynamically (no) +# TCL define to path to TCL dir to get TCL support (not defined) +# TCL_VER define to version of TCL being used (83) +# DYNAMIC_TCL no or yes: use yes to load the TCL DLL dynamically (no) +# RUBY define to path to Ruby dir to get Ruby support (not defined) +# NOTE: You may have to remove the defines for uid_t and gid_t +# from the Ruby config.h header file. +# RUBY_VER define to version of Ruby being used (16) +# NOTE: compilation on WinNT/2K/XP requires +# at least version 1.6.5 of Ruby. Earlier versions +# of Ruby will cause a compile error on these systems. +# RUBY_VER_LONG same, but in format with dot. (1.6) +# DYNAMIC_RUBY no or yes: use yes to load the Ruby DLL dynamically (no) +# IME no or yes: set to yes for multi-byte IME support (yes) +# DYNAMIC_IME no or yes: set to yes to load imm32.dll dynamically (yes) +# GETTEXT no or yes: set to yes for multi-language support (yes) +# ICONV no or yes: set to yes for dynamic iconv support (yes) +# OLE no or yes: set to yes to make OLE gvim (no) +# DEBUG no or yes: set to yes if you wish a DEBUGging build (no) +# CODEGUARD no or yes: set to yes if you want to use CODEGUARD (no) +# CPUNR 1 through 6: select -CPU argument to compile with (3) +# 3 for 386, 4 for 486, 5 for pentium, 6 for pentium pro. +# USEDLL no or yes: set to yes to use the Runtime library DLL (no) +# For USEDLL=yes the cc3250.dll is required to run Vim. +# VIMDLL no or yes: create vim32.dll, and stub (g)vim.exe (no) +# ALIGN 1, 2 or 4: Alignment to use (4 for Win32) +# FASTCALL no or yes: set to yes to use register-based function protocol (yes) +# OPTIMIZE SPACE, SPEED, or MAXSPEED: type of optimization (MAXSPEED) +# POSTSCRIPT no or yes: set to yes for PostScript printing +# FEATURES TINY, SMALL, NORMAL, BIG or HUGE (BIG for WIN32) +# WINVER 0x0400 or 0x0500: minimum Win32 version to support (0x0400) +# CSCOPE no or yes: include support for Cscope interface (yes) +# NETBEANS no or yes: include support for Netbeans interface; also +# requires CHANNEL (yes if GUI +# is yes) +# NBDEBUG no or yes: include support for debugging Netbeans interface (no) +# CHANNEL no or yes: include support for inter process communication (yes +# if GUI is yes) +# XPM define to path to XPM dir to get support for loading XPM images. + +### BOR: root of the BC installation +!if ("$(BOR)"=="") +BOR = c:\bc5 +!endif + +### LINK: Name of the linker: ilink32 (this is below) + +### GUI: yes for GUI version, no for console version +!if ("$(GUI)"=="") +GUI = yes +!endif + +### IME: yes for multibyte support, no to disable it. +!if ("$(IME)"=="") +IME = yes +!endif +!if ("$(DYNAMIC_IME)"=="") +DYNAMIC_IME = yes +!endif + +### GETTEXT: yes for multilanguage support, no to disable it. +!if ("$(GETTEXT)"=="") +GETTEXT = yes +!endif + +### ICONV: yes to enable dynamic-iconv support, no to disable it +!if ("$(ICONV)"=="") +ICONV = yes +!endif + +### CSCOPE: yes to enable Cscope support, no to disable it +!if ("$(CSCOPE)"=="") +CSCOPE = yes +!endif + +### NETBEANS: yes to enable NetBeans interface support, no to disable it +!if ("$(NETBEANS)"=="") && ("$(GUI)"=="yes") +NETBEANS = yes +!endif + +### CHANNEL: yes to enable inter process communication, no to disable it +!if ("$(CHANNEL)"=="") && ("$(GUI)"=="yes") +CHANNEL = yes +!endif + +### LUA: uncomment this line if you want lua support in vim +# LUA=c:\lua + +### PERL: uncomment this line if you want perl support in vim +# PERL=c:\perl + +### PYTHON: uncomment this line if you want python support in vim +# PYTHON=c:\python22 + +### PYTHON3: uncomment this line if you want python3 support in vim +# PYTHON3=c:\python31 + +### RUBY: uncomment this line if you want ruby support in vim +# RUBY=c:\ruby + +### TCL: uncomment this line if you want tcl support in vim +# TCL=c:\tcl + +### OLE: no for normal gvim, yes for OLE-capable gvim (only works with GUI) +#OLE = yes + +### DEBUG: Uncomment to make an executable for debugging +# DEBUG = yes +!if ("$(DEBUG)"=="yes") +DEBUG_FLAG = -v +!endif + +### CODEGUARD: Uncomment to use the CODEGUARD stuff (BC 5.0 or later): +# CODEGUARD = yes +!if ("$(CODEGUARD)"=="yes") +CODEGUARD_FLAG = -vG +!endif + +### CPUNR: set your target processor (3 to 6) +!if ("$(CPUNR)" == "i386") || ("$(CPUNR)" == "3") +CPUNR = 3 +!elif ("$(CPUNR)" == "i486") || ("$(CPUNR)" == "4") +CPUNR = 4 +!elif ("$(CPUNR)" == "i586") || ("$(CPUNR)" == "5") +CPUNR = 5 +!elif ("$(CPUNR)" == "i686") || ("$(CPUNR)" == "6") +CPUNR = 6 +!else +CPUNR = 3 +!endif + +### Comment out to use precompiled headers (faster, but uses lots of disk!) +HEADERS = -H -H=vim.csm -Hc + +### USEDLL: no for statically linked version of run-time, yes for DLL runtime +!if ("$(USEDLL)"=="") +USEDLL = no +!endif + +### VIMDLL: yes for a DLL version of VIM (NOT RECOMMENDED), no otherwise +#VIMDLL = yes + +### ALIGN: alignment you desire: (1,2 or 4: s/b 4 for Win32) +!if ("$(ALIGN)"=="") +ALIGN = 4 +!endif + +### FASTCALL: yes to use FASTCALL calling convention (RECOMMENDED!), no otherwise +# Incompatible when calling external functions (like MSVC-compiled DLLs), so +# don't use FASTCALL when linking with external libs. +!if ("$(FASTCALL)"=="") && \ + ("$(LUA)"=="") && \ + ("$(PYTHON)"=="") && \ + ("$(PYTHON3)"=="") && \ + ("$(PERL)"=="") && \ + ("$(TCL)"=="") && \ + ("$(RUBY)"=="") && \ + ("$(ICONV)"!="yes") && \ + ("$(IME)"!="yes") && \ + ("$(XPM)"=="") +FASTCALL = yes +!endif + +### OPTIMIZE: SPEED to optimize for speed, SPACE otherwise (SPEED RECOMMENDED) +!if ("$(OPTIMIZE)"=="") +OPTIMIZE = MAXSPEED +!endif + +### FEATURES: TINY, SMALL, NORMAL, BIG or HUGE (BIG for WIN32) +!if ("$(FEATURES)"=="") +FEATURES = BIG +!endif + +### POSTSCRIPT: uncomment this line if you want PostScript printing +#POSTSCRIPT = yes + +### +# If you have a fixed directory for $VIM or $VIMRUNTIME, other than the normal +# default, use these lines. +#VIMRCLOC = somewhere +#VIMRUNTIMEDIR = somewhere + +### Set the default $(WINVER) to make it work with Bcc 5.5. +!ifndef WINVER +WINVER = 0x0400 +!endif + +# +# Sanity checks for the above options: +# + +OSTYPE = WIN32 + +# +# Optimizations: change as desired (RECOMMENDATION: Don't change!): +# +!if ("$(DEBUG)"=="yes") +OPT = -Od -N +!else +!if ("$(OPTIMIZE)"=="SPACE") +OPT = -O1 -f- -d +!elif ("$(OPTIMIZE)"=="MAXSPEED") +OPT = -O2 -f- -d -Ocavi -O +!else +OPT = -O2 -f- -d -Oc -O +!endif +!if ("$(FASTCALL)"=="yes") +OPT = $(OPT) -pr +!endif +!if ("$(CODEGUARD)"!="yes") +OPT = $(OPT) -vi- +!endif +!endif +# shouldn't have to change: +LIB = $(BOR)\lib +INCLUDE = $(BOR)\include;.;proto +DEFINES = -DFEAT_$(FEATURES) -DWIN32 -DHAVE_PATHDEF \ + -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) + +!ifdef LUA +INTERP_DEFINES = $(INTERP_DEFINES) -DFEAT_LUA +INCLUDE = $(LUA)\include;$(INCLUDE) +! ifndef LUA_VER +LUA_VER = 51 +! endif +! if ("$(DYNAMIC_LUA)" == "yes") +INTERP_DEFINES = $(INTERP_DEFINES) -DDYNAMIC_LUA -DDYNAMIC_LUA_DLL=\"lua$(LUA_VER).dll\" +LUA_LIB_FLAG = /nodefaultlib: +! endif +!endif + +!ifdef PERL +INTERP_DEFINES = $(INTERP_DEFINES) -DFEAT_PERL +INCLUDE = $(PERL)\lib\core;$(INCLUDE) +! ifndef PERL_VER +PERL_VER = 56 +! endif +! if ("$(DYNAMIC_PERL)" == "yes") +! if ($(PERL_VER) > 55) +INTERP_DEFINES = $(INTERP_DEFINES) -DDYNAMIC_PERL -DDYNAMIC_PERL_DLL=\"perl$(PERL_VER).dll\" +PERL_LIB_FLAG = /nodefaultlib: +! else +! message "Cannot dynamically load Perl versions less than 5.6. Loading statically..." +! endif +! endif +!endif + +!ifdef PYTHON +!ifdef PYTHON3 +DYNAMIC_PYTHON=yes +DYNAMIC_PYTHON3=yes +!endif +!endif + +!ifdef PYTHON +INTERP_DEFINES = $(INTERP_DEFINES) -DFEAT_PYTHON +!ifndef PYTHON_VER +PYTHON_VER = 22 +!endif +!if "$(DYNAMIC_PYTHON)" == "yes" +INTERP_DEFINES = $(INTERP_DEFINES) -DDYNAMIC_PYTHON -DDYNAMIC_PYTHON_DLL=\"python$(PYTHON_VER).dll\" +PYTHON_LIB_FLAG = /nodefaultlib: +!endif +!endif + +!ifdef PYTHON3 +INTERP_DEFINES = $(INTERP_DEFINES) -DFEAT_PYTHON3 +!ifndef PYTHON3_VER +PYTHON3_VER = 31 +!endif +!if "$(DYNAMIC_PYTHON3)" == "yes" +INTERP_DEFINES = $(INTERP_DEFINES) -DDYNAMIC_PYTHON3 -DDYNAMIC_PYTHON3_DLL=\"python$(PYTHON3_VER).dll\" +PYTHON3_LIB_FLAG = /nodefaultlib: +!endif +!endif + + +!ifdef RUBY +!ifndef RUBY_VER +RUBY_VER = 16 +!endif +!ifndef RUBY_VER_LONG +RUBY_VER_LONG = 1.6 +!endif + +!if "$(RUBY_VER)" == "16" +!ifndef RUBY_PLATFORM +RUBY_PLATFORM = i586-mswin32 +!endif +!ifndef RUBY_INSTALL_NAME +RUBY_INSTALL_NAME = mswin32-ruby$(RUBY_VER) +!endif +!else +!ifndef RUBY_PLATFORM +RUBY_PLATFORM = i386-mswin32 +!endif +!ifndef RUBY_INSTALL_NAME +RUBY_INSTALL_NAME = msvcrt-ruby$(RUBY_VER) +!endif +!endif + +INTERP_DEFINES = $(INTERP_DEFINES) -DFEAT_RUBY +INCLUDE = $(RUBY)\lib\ruby\$(RUBY_VER_LONG)\$(RUBY_PLATFORM);$(INCLUDE) + +!if "$(DYNAMIC_RUBY)" == "yes" +INTERP_DEFINES = $(INTERP_DEFINES) -DDYNAMIC_RUBY -DDYNAMIC_RUBY_DLL=\"$(RUBY_INSTALL_NAME).dll\" +INTERP_DEFINES = $(INTERP_DEFINES) -DDYNAMIC_RUBY_VER=$(RUBY_VER) +RUBY_LIB_FLAG = /nodefaultlib: +!endif +!endif + +!ifdef TCL +INTERP_DEFINES = $(INTERP_DEFINES) -DFEAT_TCL +INCLUDE = $(TCL)\include;$(INCLUDE) +!ifndef TCL_VER +TCL_VER = 83 +!endif +TCL_LIB = $(TCL)\lib\tcl$(TCL_VER).lib +TCL_LIB_FLAG = +!if "$(DYNAMIC_TCL)" == "yes" +INTERP_DEFINES = $(INTERP_DEFINES) -DDYNAMIC_TCL -DDYNAMIC_TCL_DLL=\"tcl$(TCL_VER).dll\" +TCL_LIB = tclstub$(TCL_VER)-bor.lib +TCL_LIB_FLAG = +!endif +!endif +# +# DO NOT change below: +# +CPUARG = -$(CPUNR) +ALIGNARG = -a$(ALIGN) +# +!if ("$(DEBUG)"=="yes") +DEFINES=$(DEFINES) -DDEBUG -D_DEBUG +!endif +# +!if ("$(OLE)"=="yes") +DEFINES = $(DEFINES) -DFEAT_OLE +!endif +# +!if ("$(IME)"=="yes") +MBDEFINES = $(MBDEFINES) -DFEAT_MBYTE_IME +!if ("$(DYNAMIC_IME)" == "yes") +MBDEFINES = $(MBDEFINES) -DDYNAMIC_IME +!endif +!endif +!if ("$(ICONV)"=="yes") +MBDEFINES = $(MBDEFINES) -DDYNAMIC_ICONV +!endif +!if ("$(GETTEXT)"=="yes") +MBDEFINES = $(MBDEFINES) -DDYNAMIC_GETTEXT +!endif + +!if ("$(CSCOPE)"=="yes") +DEFINES = $(DEFINES) -DFEAT_CSCOPE +!endif + +!if ("$(GUI)"=="yes") +DEFINES = $(DEFINES) -DFEAT_GUI_W32 -DFEAT_CLIPBOARD +!if ("$(DEBUG)"=="yes") +TARGET = gvimd.exe +!else +TARGET = gvim.exe +!endif +!if ("$(VIMDLL)"=="yes") +EXETYPE=-WD +DEFINES = $(DEFINES) -DVIMDLL +!else +EXETYPE=-W +!endif +STARTUPOBJ = c0w32.obj +LINK2 = -aa +RESFILE = vim.res +!else +!undef NETBEANS +!undef CHANNEL +!undef XPM +!undef VIMDLL +!if ("$(DEBUG)"=="yes") +TARGET = vimd.exe +!else +# for now, anyway: VIMDLL is only for the GUI version +TARGET = vim.exe +!endif +EXETYPE=-WC +STARTUPOBJ = c0x32.obj +LINK2 = -ap -OS -o -P +RESFILE = vim.res +!endif + +!if ("$(NETBEANS)"=="yes") +!if ("$(CHANNEL)"!="yes") +# cannot use Netbeans without CHANNEL +NETBEANS = no +!else +DEFINES = $(DEFINES) -DFEAT_NETBEANS_INTG +!if ("$(NBDEBUG)"=="yes") +DEFINES = $(DEFINES) -DNBDEBUG +NBDEBUG_DEP = nbdebug.h nbdebug.c +!endif +!endif +!endif + +!if ("$(CHANNEL)"=="yes") +DEFINES = $(DEFINES) -DFEAT_JOB_CHANNEL +!endif + +!ifdef XPM +!if ("$(GUI)"=="yes") +DEFINES = $(DEFINES) -DFEAT_XPM_W32 +INCLUDE = $(XPM)\include;$(INCLUDE) +!endif +!endif + +!if ("$(USEDLL)"=="yes") +DEFINES = $(DEFINES) -D_RTLDLL +!endif + +!if ("$(DEBUG)"=="yes") +OBJDIR = $(OSTYPE)\objdbg +!else +!if ("$(GUI)"=="yes") +!if ("$(OLE)"=="yes") +OBJDIR = $(OSTYPE)\oleobj +!else +OBJDIR = $(OSTYPE)\gobj +!endif +!else +OBJDIR = $(OSTYPE)\obj +!endif +!endif + +!if ("$(POSTSCRIPT)"=="yes") +DEFINES = $(DEFINES) -DMSWINPS +!endif + +##### BASE COMPILER/TOOLS RULES ##### +MAKE = $(BOR)\bin\make +CFLAGS = -w-aus -w-par -w-pch -w-ngu -w-csu -I$(INCLUDE) +BRC = $(BOR)\BIN\brc32 +!if ("$(LINK)"=="") +LINK = $(BOR)\BIN\ILink32 +!endif +CC = $(BOR)\BIN\Bcc32 +LFLAGS = -OS -Tpe -c -m -L$(LIB) $(DEBUG_FLAG) $(LINK2) +LFLAGSDLL = -Tpd -c -m -L$(LIB) $(DEBUG_FLAG) $(LINK2) +CFLAGS = $(CFLAGS) -d -RT- -k- -Oi $(HEADERS) -f- + +CC1 = -c +CC2 = -o +CCARG = +$(OBJDIR)\bcc.cfg + +# implicit rules: + +# Without the following, the implicit rule in BUILTINS.MAK is picked up +# for a rule for .c.obj rather than the local implicit rule +.SUFFIXES +.SUFFIXES .c .obj +.path.c = . + +{.}.c{$(OBJDIR)}.obj: + $(CC) $(CCARG) $(CC1) -n$(OBJDIR)\ {$< } + +.cpp.obj: + $(CC) $(CCARG) $(CC1) $(CC2)$@ $*.cpp + +vimmain = \ + $(OBJDIR)\os_w32exe.obj +!if ("$(VIMDLL)"=="yes") +vimwinmain = \ + $(OBJDIR)\os_w32dll.obj +!else +vimwinmain = \ + $(OBJDIR)\os_w32exe.obj +!endif + +vimobj = \ + $(OBJDIR)\arabic.obj \ + $(OBJDIR)\autocmd.obj \ + $(OBJDIR)\blowfish.obj \ + $(OBJDIR)\buffer.obj \ + $(OBJDIR)\charset.obj \ + $(OBJDIR)\crypt.obj \ + $(OBJDIR)\crypt_zip.obj \ + $(OBJDIR)\dict.obj \ + $(OBJDIR)\diff.obj \ + $(OBJDIR)\digraph.obj \ + $(OBJDIR)\edit.obj \ + $(OBJDIR)\eval.obj \ + $(OBJDIR)\evalfunc.obj \ + $(OBJDIR)\ex_cmds.obj \ + $(OBJDIR)\ex_cmds2.obj \ + $(OBJDIR)\ex_docmd.obj \ + $(OBJDIR)\ex_eval.obj \ + $(OBJDIR)\ex_getln.obj \ + $(OBJDIR)\farsi.obj \ + $(OBJDIR)\fileio.obj \ + $(OBJDIR)\fold.obj \ + $(OBJDIR)\getchar.obj \ + $(OBJDIR)\hardcopy.obj \ + $(OBJDIR)\hashtab.obj \ + $(OBJDIR)\indent.obj \ + $(OBJDIR)\json.obj \ + $(OBJDIR)\list.obj \ + $(OBJDIR)\main.obj \ + $(OBJDIR)\mark.obj \ + $(OBJDIR)\memfile.obj \ + $(OBJDIR)\memline.obj \ + $(OBJDIR)\menu.obj \ + $(OBJDIR)\message.obj \ + $(OBJDIR)\misc1.obj \ + $(OBJDIR)\misc2.obj \ + $(OBJDIR)\move.obj \ + $(OBJDIR)\mbyte.obj \ + $(OBJDIR)\normal.obj \ + $(OBJDIR)\ops.obj \ + $(OBJDIR)\option.obj \ + $(OBJDIR)\popupmnu.obj \ + $(OBJDIR)\quickfix.obj \ + $(OBJDIR)\regexp.obj \ + $(OBJDIR)\screen.obj \ + $(OBJDIR)\search.obj \ + $(OBJDIR)\sha256.obj \ + $(OBJDIR)\sign.obj \ + $(OBJDIR)\spell.obj \ + $(OBJDIR)\spellfile.obj \ + $(OBJDIR)\syntax.obj \ + $(OBJDIR)\tag.obj \ + $(OBJDIR)\term.obj \ + $(OBJDIR)\ui.obj \ + $(OBJDIR)\undo.obj \ + $(OBJDIR)\userfunc.obj \ + $(OBJDIR)\version.obj \ + $(OBJDIR)\window.obj \ + $(OBJDIR)\pathdef.obj + +!if ("$(OLE)"=="yes") +vimobj = $(vimobj) \ + $(OBJDIR)\if_ole.obj +!endif + +!ifdef LUA +vimobj = $(vimobj) \ + $(OBJDIR)\if_lua.obj +!endif + +!ifdef PERL +vimobj = $(vimobj) \ + $(OBJDIR)\if_perl.obj +!endif + +!ifdef PYTHON +vimobj = $(vimobj) \ + $(OBJDIR)\if_python.obj +!endif + +!ifdef PYTHON3 +vimobj = $(vimobj) \ + $(OBJDIR)\if_python3.obj +!endif + +!ifdef RUBY +vimobj = $(vimobj) \ + $(OBJDIR)\if_ruby.obj +!endif + +!ifdef TCL +vimobj = $(vimobj) \ + $(OBJDIR)\if_tcl.obj +!endif + +!if ("$(CSCOPE)"=="yes") +vimobj = $(vimobj) \ + $(OBJDIR)\if_cscope.obj +!endif + +!if ("$(NETBEANS)"=="yes") +vimobj = $(vimobj) \ + $(OBJDIR)\netbeans.obj +!endif + +!if ("$(CHANNEL)"=="yes") +vimobj = $(vimobj) \ + $(OBJDIR)\channel.obj +!endif + +!ifdef XPM +vimobj = $(vimobj) \ + $(OBJDIR)\xpm_w32.obj +!endif + +!if ("$(VIMDLL)"=="yes") +vimdllobj = $(vimobj) +!if ("$(DEBUG)"=="yes") +DLLTARGET = vim32d.dll +!else +DLLTARGET = vim32.dll +!endif +!else +DLLTARGET = joebob +!endif + +!if ("$(GUI)"=="yes") +vimobj = $(vimobj) \ + $(vimwinmain) \ + $(OBJDIR)\gui.obj \ + $(OBJDIR)\gui_beval.obj \ + $(OBJDIR)\gui_w32.obj +!endif + +vimobj = $(vimobj) \ + $(OBJDIR)\os_win32.obj $(OBJDIR)\os_mswin.obj $(OBJDIR)\winclip.obj +# Blab what we are going to do: +MSG = Compiling $(OSTYPE) $(TARGET) $(OLETARGET), with: +!if ("$(GUI)"=="yes") +MSG = $(MSG) GUI +!endif +!if ("$(OLE)"=="yes") +MSG = $(MSG) OLE +!endif +!if ("$(USEDLL)"=="yes") +MSG = $(MSG) USEDLL +!endif +!if ("$(VIMDLL)"=="yes") +MSG = $(MSG) VIMDLL +!endif +!if ("$(FASTCALL)"=="yes") +MSG = $(MSG) FASTCALL +!endif +!if ("$(IME)"=="yes") +MSG = $(MSG) IME +! if "$(DYNAMIC_IME)" == "yes" +MSG = $(MSG)(dynamic) +! endif +!endif +!if ("$(GETTEXT)"=="yes") +MSG = $(MSG) GETTEXT +!endif +!if ("$(ICONV)"=="yes") +MSG = $(MSG) ICONV +!endif +!if ("$(DEBUG)"=="yes") +MSG = $(MSG) DEBUG +!endif +!if ("$(CODEGUARD)"=="yes") +MSG = $(MSG) CODEGUARD +!endif +!if ("$(CSCOPE)"=="yes") +MSG = $(MSG) CSCOPE +!endif +!if ("$(NETBEANS)"=="yes") +MSG = $(MSG) NETBEANS +!endif +!if ("$(CHANNEL)"=="yes") +MSG = $(MSG) CHANNEL +!endif +!ifdef XPM +MSG = $(MSG) XPM +!endif +!ifdef LUA +MSG = $(MSG) LUA +! if "$(DYNAMIC_LUA)" == "yes" +MSG = $(MSG)(dynamic) +! endif +!endif +!ifdef PERL +MSG = $(MSG) PERL +! if "$(DYNAMIC_PERL)" == "yes" +MSG = $(MSG)(dynamic) +! endif +!endif +!ifdef PYTHON +MSG = $(MSG) PYTHON +! if "$(DYNAMIC_PYTHON)" == "yes" +MSG = $(MSG)(dynamic) +! endif +!endif +!ifdef PYTHON3 +MSG = $(MSG) PYTHON3 +! if "$(DYNAMIC_PYTHON3)" == "yes" +MSG = $(MSG)(dynamic) +! endif +!endif +!ifdef RUBY +MSG = $(MSG) RUBY +! if "$(DYNAMIC_RUBY)" == "yes" +MSG = $(MSG)(dynamic) +! endif +!endif +!ifdef TCL +MSG = $(MSG) TCL +! if "$(DYNAMIC_TCL)" == "yes" +MSG = $(MSG)(dynamic) +! endif +!endif +MSG = $(MSG) cpu=$(CPUARG) +MSG = $(MSG) Align=$(ALIGNARG) + +!message $(MSG) + +!if ("$(VIMDLL)"=="yes") +TARGETS = $(DLLTARGET) +!endif +TARGETS = $(TARGETS) $(TARGET) + +# Targets: +all: vim vimrun.exe install.exe xxd uninstal.exe GvimExt/gvimext.dll + +vim: $(OSTYPE) $(OBJDIR) $(OBJDIR)\bcc.cfg $(TARGETS) + @if exist $(OBJDIR)\version.obj del $(OBJDIR)\version.obj + @if exist auto\pathdef.c del auto\pathdef.c + +$(OSTYPE): + -@md $(OSTYPE) + +$(OBJDIR): + -@md $(OBJDIR) + +xxd: + @cd xxd + $(MAKE) /f Make_bc5.mak BOR="$(BOR)" BCC="$(CC)" + @cd .. + +GvimExt/gvimext.dll: GvimExt/gvimext.cpp GvimExt/gvimext.rc GvimExt/gvimext.h + cd GvimExt + $(MAKE) /f Make_bc5.mak USEDLL=$(USEDLL) BOR=$(BOR) + cd .. + +install.exe: dosinst.c $(OBJDIR)\bcc.cfg + $(CC) $(CCARG) -WC -DWIN32 -einstall dosinst.c + +uninstal.exe: uninstal.c $(OBJDIR)\bcc.cfg + $(CC) $(CCARG) -WC -DWIN32 -O2 -euninstal uninstal.c + +clean: +!if "$(OS)" == "Windows_NT" + # For Windows NT/2000, doesn't work on Windows 95/98... + # $(COMSPEC) needed to ensure rmdir.exe is not run + -@$(COMSPEC) /C rmdir /Q /S $(OBJDIR) +!else + # For Windows 95/98, doesn't work on Windows NT/2000... + -@deltree /y $(OBJDIR) +!endif + -@del *.res + -@del vim32*.dll + -@del vim32*.lib + -@del *vim*.exe + -@del *install*.exe + -@del *.csm + -@del *.map + -@del *.ilc + -@del *.ild + -@del *.ilf + -@del *.ils + -@del *.tds +!ifdef LUA + -@del lua.lib +!endif +!ifdef PERL + -@del perl.lib + -@del if_perl.c + -@del auto\if_perl.c +!endif +!ifdef PYTHON + -@del python.lib +!endif +!ifdef PYTHON3 + -@del python3.lib +!endif +!ifdef RUBY + -@del ruby.lib +!endif +!ifdef TCL + -@del tcl.lib +!endif +!ifdef XPM + -@del xpm.lib +!endif + cd xxd + $(MAKE) /f Make_bc5.mak BOR="$(BOR)" clean + cd .. + cd GvimExt + $(MAKE) /f Make_bc5.mak BOR="$(BOR)" clean + cd .. + +$(DLLTARGET): $(OBJDIR) $(vimdllobj) + $(LINK) @&&| + $(LFLAGSDLL) + + c0d32.obj + + $(vimdllobj) + $<,$* +!if ("$(CODEGUARD)"=="yes") + cg32.lib+ +!endif +# $(OSTYPE)==WIN32 causes os_mswin.c compilation. FEAT_SHORTCUT in it needs OLE + ole2w32.lib + + import32.lib+ +!ifdef LUA + $(LUA_LIB_FLAG)lua.lib+ +!endif +!ifdef PERL + $(PERL_LIB_FLAG)perl.lib+ +!endif +!ifdef PYTHON + $(PYTHON_LIB_FLAG)python.lib+ +!endif +!ifdef PYTHON3 + $(PYTHON3_LIB_FLAG)python3.lib+ +!endif +!ifdef RUBY + $(RUBY_LIB_FLAG)ruby.lib+ +!endif +!ifdef TCL + $(TCL_LIB_FLAG)tcl.lib+ +!endif +!ifdef XPM + xpm.lib+ +!endif +!if ("$(USEDLL)"=="yes") + cw32i.lib +!else + cw32.lib +!endif + vim.def +| + +!if ("$(VIMDLL)"=="yes") +$(TARGET): $(OBJDIR) $(DLLTARGET) $(vimmain) $(OBJDIR)\$(RESFILE) +!else +$(TARGET): $(OBJDIR) $(vimobj) $(OBJDIR)\$(RESFILE) +!endif + $(LINK) @&&| + $(LFLAGS) + + $(STARTUPOBJ) + +!if ("$(VIMDLL)"=="yes") + $(vimmain) +!else + $(vimobj) +!endif + $<,$* +!if ("$(CODEGUARD)"=="yes") + cg32.lib+ +!endif +# $(OSTYPE)==WIN32 causes os_mswin.c compilation. FEAT_SHORTCUT in it needs OLE + ole2w32.lib + + import32.lib+ +!ifdef LUA + $(LUA_LIB_FLAG)lua.lib+ +!endif +!ifdef PERL + $(PERL_LIB_FLAG)perl.lib+ +!endif +!ifdef PYTHON + $(PYTHON_LIB_FLAG)python.lib+ +!endif +!ifdef PYTHON3 + $(PYTHON3_LIB_FLAG)python3.lib+ +!endif +!ifdef RUBY + $(RUBY_LIB_FLAG)ruby.lib+ +!endif +!ifdef TCL + $(TCL_LIB_FLAG)tcl.lib+ +!endif +!ifdef XPM + xpm.lib+ +!endif +!if ("$(USEDLL)"=="yes") + cw32i.lib +!else + cw32.lib +!endif + + $(OBJDIR)\$(RESFILE) +| + +test: + cd testdir + $(MAKE) /NOLOGO -f Make_dos.mak win32 + cd .. + +$(OBJDIR)\ex_docmd.obj: ex_docmd.c ex_cmds.h + +$(OBJDIR)\ex_eval.obj: ex_eval.c ex_cmds.h + +$(OBJDIR)\if_ole.obj: if_ole.cpp + +$(OBJDIR)\if_lua.obj: if_lua.c lua.lib + $(CC) $(CCARG) $(CC1) $(CC2)$@ -pc if_lua.c + +$(OBJDIR)\if_perl.obj: auto/if_perl.c perl.lib + $(CC) $(CCARG) $(CC1) $(CC2)$@ -pc auto/if_perl.c + +auto/if_perl.c: if_perl.xs typemap + $(PERL)\bin\perl.exe $(PERL)\lib\ExtUtils\xsubpp -prototypes -typemap \ + $(PERL)\lib\ExtUtils\typemap if_perl.xs -output $@ + +$(OBJDIR)\if_python.obj: if_python.c if_py_both.h python.lib + $(CC) -I$(PYTHON)\include $(CCARG) $(CC1) $(CC2)$@ -pc if_python.c + +$(OBJDIR)\if_python3.obj: if_python3.c if_py_both.h python3.lib + $(CC) -I$(PYTHON3)\include $(CCARG) $(CC1) $(CC2)$@ -pc if_python3.c + +$(OBJDIR)\if_ruby.obj: if_ruby.c ruby.lib + $(CC) $(CCARG) $(CC1) $(CC2)$@ -pc if_ruby.c + +$(OBJDIR)\if_tcl.obj: if_tcl.c tcl.lib + $(CC) $(CCARG) $(CC1) $(CC2)$@ -pc if_tcl.c + +$(OBJDIR)\xpm_w32.obj: xpm_w32.c xpm.lib + $(CC) $(CCARG) $(CC1) $(CC2)$@ -pc xpm_w32.c + +$(OBJDIR)\netbeans.obj: netbeans.c $(NBDEBUG_DEP) + $(CC) $(CCARG) $(CC1) $(CC2)$@ netbeans.c + +$(OBJDIR)\channel.obj: channel.c + $(CC) $(CCARG) $(CC1) $(CC2)$@ channel.c + +$(OBJDIR)\vim.res: vim.rc version.h tools.bmp tearoff.bmp \ + vim.ico vim_error.ico vim_alert.ico vim_info.ico vim_quest.ico + $(BRC) -fo$(OBJDIR)\vim.res -i $(BOR)\include -w32 -r vim.rc @&&| + $(DEFINES) +| + +$(OBJDIR)\pathdef.obj: auto\pathdef.c + $(CC) $(CCARG) $(CC1) $(CC2)$@ auto\pathdef.c + + +# Need to escape both quotes and backslashes in $INTERP_DEFINES +INTERP_DEFINES_ESC_BKS=$(INTERP_DEFINES:\=\\) +INTERP_DEFINES_ESC=$(INTERP_DEFINES_ESC_BKS:"=\") + +# Note: the silly /*"*/ below are there to trick make into accepting +# the # character as something other than a comment without messing up +# the preprocessor directive. +auto\pathdef.c:: + -@md auto + @echo creating auto/pathdef.c + @copy /y &&| +/* pathdef.c */ +/*"*/#include "vim.h"/*"*/ + +char_u *default_vim_dir = (char_u *)"$(VIMRCLOC:\=\\)"; +char_u *default_vimruntime_dir = (char_u *)"$(VIMRUNTIMEDIR:\=\\)"; +char_u *all_cflags = (char_u *)"$(CC:\=\\) $(CFLAGS:\=\\) $(DEFINES) $(MBDEFINES) $(INTERP_DEFINES_ESC) $(OPT) $(EXETYPE) $(CPUARG) $(ALIGNARG) $(DEBUG_FLAG) $(CODEGUARD_FLAG)"; +char_u *all_lflags = (char_u *)"$(LINK:\=\\) $(LFLAGS:\=\\)"; +char_u *compiled_user = (char_u *)"$(USERNAME)"; +char_u *compiled_sys = (char_u *)"$(USERDOMAIN)"; +| auto\pathdef.c + +lua.lib: $(LUA)\lib\lua$(LUA_VER).lib + coff2omf $(LUA)\lib\lua$(LUA_VER).lib $@ + +perl.lib: $(PERL)\lib\CORE\perl$(PERL_VER).lib + coff2omf $(PERL)\lib\CORE\perl$(PERL_VER).lib $@ + +python.lib: $(PYTHON)\libs\python$(PYTHON_VER).lib + coff2omf $(PYTHON)\libs\python$(PYTHON_VER).lib $@ + +python3.lib: $(PYTHON3)\libs\python$(PYTHON3_VER).lib + coff2omf $(PYTHON3)\libs\python$(PYTHON3_VER).lib $@ + +ruby.lib: $(RUBY)\lib\$(RUBY_INSTALL_NAME).lib + coff2omf $(RUBY)\lib\$(RUBY_INSTALL_NAME).lib $@ + +# For some reason, the coff2omf method doesn't work on libXpm.lib, so +# we have to manually generate an import library straight from the DLL. +xpm.lib: $(XPM)\lib\libXpm.lib + implib -a $@ $(XPM)\bin\libXpm.dll + +tcl.lib: $(TCL_LIB) +!if ("$(DYNAMIC_TCL)" == "yes") + copy $(TCL_LIB) $@ +!else + coff2omf $(TCL_LIB) $@ +!endif + +!if ("$(DYNAMIC_TCL)" == "yes") +tclstub$(TCL_VER)-bor.lib: + -@IF NOT EXIST $@ ECHO You must download tclstub$(TCL_VER)-bor.lib separately and\ + place it in the src directory in order to compile a dynamic TCL-enabled\ + (g)vim with the Borland compiler. You can get the tclstub$(TCL_VER)-bor.lib file\ + at http://mywebpage.netscape.com/sharppeople/vim/tclstub$(TCL_VER)-bor.lib +!endif + +# vimrun.exe: +vimrun.exe: vimrun.c +!if ("$(USEDLL)"=="yes") + $(CC) -WC -O1 -I$(INCLUDE) -L$(LIB) -D_RTLDLL vimrun.c cw32mti.lib +!else + $(CC) -WC -O1 -I$(INCLUDE) -L$(LIB) vimrun.c +!endif + +# The dependency on $(OBJDIR) is to have bcc.cfg generated each time. +$(OBJDIR)\bcc.cfg: Make_bc5.mak $(OBJDIR) + copy /y &&| + $(CFLAGS) + -L$(LIB) + $(DEFINES) + $(MBDEFINES) + $(INTERP_DEFINES) + $(EXETYPE) + $(DEBUG_FLAG) + $(OPT) + $(CODEGUARD_FLAG) + $(CPUARG) + $(ALIGNARG) +| $@ + +# vi:set sts=4 sw=4: + diff --git a/src/Make_cyg.mak b/src/Make_cyg.mak new file mode 100644 index 0000000..8c1b60e --- /dev/null +++ b/src/Make_cyg.mak @@ -0,0 +1,54 @@ +# +# Makefile for VIM on Win32, using MinGW cross compiler on Cygwin +# +# Also read INSTALLpc.txt! +# +# This compiles Vim as a Windows application. If you want Vim to run as a +# Cygwin application use the Makefile (just like on Unix). +# +# The old Make_cyg.mak (maintained by Dan Sharp et al.) was merged into +# Make_cyg_ming.mak. Note: USEDLL option was removed. +# This file contains Cygwin specific settings. Common settings are contained +# in Make_cyg_ming.mak. +# +# Last updated by Ken Takata. +# Last Change: 2014 Oct 21 + + +# uncomment 'PERL' if you want a perl-enabled version +#PERL=/cygdrive/c/perl + +# uncomment 'LUA' if you want a Lua-enabled version +#LUA=/cygdrive/c/lua + +# uncomment 'MZSCHEME' if you want a MzScheme-enabled version +#MZSCHEME=/cygdrive/d/plt + +# uncomment 'PYTHON' if you want a python-enabled version +#PYTHON=/cygdrive/c/python20 + +# uncomment 'PYTHON3' if you want a python3-enabled version +#PYTHON3=/cygdrive/c/python31 + +# uncomment 'TCL' if you want a Tcl-enabled version +#TCL=/cygdrive/c/tcl + +# uncomment 'RUBY' if you want a Ruby-enabled version +#RUBY=/cygdribe/c/ruby + + +# Use MinGW(-w64) cross compiler. +# There are three MinGW packages in Cygwin: +# 32-bit: mingw-gcc-g++ and mingw64-i686-gcc-g++ +# 64-bit: mingw64-x86_64-gcc-g++ +# You may also need to set 'ARCH' in Make_cyg_ming.mak. +CROSS_COMPILE = i686-pc-mingw32- +#CROSS_COMPILE = i686-w64-mingw32- +#CROSS_COMPILE = x86_64-w64-mingw32- + + +# Do not change this. +UNDER_CYGWIN = yes +include Make_cyg_ming.mak + +# vim: set noet sw=8 ts=8 sts=0 wm=0 tw=0: diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak new file mode 100644 index 0000000..1e26add --- /dev/null +++ b/src/Make_cyg_ming.mak @@ -0,0 +1,1165 @@ +# Makefile for VIM on Win32 (Cygwin and MinGW) +# +# This file contains common part for Cygwin and MinGW and it is included +# from Make_cyg.mak and Make_ming.mak. +# +# Info at http://www.mingw.org +# Alternative x86 and 64-builds: http://mingw-w64.sourceforge.net +# Also requires GNU make, which you can download from the same sites. +# Get missing libraries from http://gnuwin32.sf.net. +# +# Tested on Win32 NT 4 and Win95. +# +# To make everything, just 'make -f Make_ming.mak'. +# To make just e.g. gvim.exe, 'make -f Make_ming.mak gvim.exe'. +# After a run, you can 'make -f Make_ming.mak clean' to clean up. +# +# NOTE: Sometimes 'GNU Make' will stop after building vimrun.exe -- I think +# it's just run out of memory or something. Run again, and it will continue +# with 'xxd'. +# +# "make upx" makes *compressed* versions of the 32 bit GUI and console EXEs, +# using the excellent UPX compressor: +# https://upx.github.io/ +# "make mpress" uses the MPRESS compressor for 32- and 64-bit EXEs: +# http://www.matcode.com/mpress.htm +# +# Maintained by Ron Aaron et al. +# Updated 2014 Oct 13. + +#>>>>> choose options: +# FEATURES=[TINY | SMALL | NORMAL | BIG | HUGE] +# Set to TINY to make minimal version (few features). +FEATURES=HUGE + +# set to yes for a debug build +DEBUG=no + +# set to yes to create a mapfile +# MAP=yes + +# set to SIZE for size, SPEED for speed, MAXSPEED for maximum optimization +OPTIMIZE=MAXSPEED + +# set to yes to make gvim, no for vim +GUI=yes + +# set to no if you do not want to use DirectWrite (DirectX) +# MinGW-w64 is needed, and ARCH should be set to i686 or x86-64. +DIRECTX=yes + +# Disable Color emoji support +# (default is yes if DIRECTX=yes, requires WinSDK 8.1 or later.) +#COLOR_EMOJI=no + +# Set to one of i386, i486, i586, i686 as the minimum target processor. +# For amd64/x64 architecture set ARCH=x86-64 . +# If not set, it will be automatically detected. (Normally i686 or x86-64.) +#ARCH=i686 +# Set to yes to cross-compile from unix; no=native Windows (and Cygwin). +CROSS=no + +# Set to path to iconv.h and libiconv.a to enable using 'iconv.dll'. +# Use "yes" when the path does not need to be define. +#ICONV="." +ICONV=yes +GETTEXT=yes + +# Set to yes to include IME support. +IME=yes +DYNAMIC_IME=yes + +# Set to yes to enable writing a postscript file with :hardcopy. +POSTSCRIPT=no + +# Set to yes to enable OLE support. +OLE=no + +# Set the default $(WINVER). Use 0x0501 to make it work with WinXP. +ifndef WINVER +# WINVER = 0x0501 +WINVER = 0x0600 +endif + +# Set to yes to enable Cscope support. +CSCOPE=yes + +# Set to yes to enable Netbeans support (requires CHANNEL). +NETBEANS=$(GUI) + +# Set to yes to enable inter process communication. +ifeq (HUGE, $(FEATURES)) +CHANNEL=yes +else +CHANNEL=$(GUI) +endif + +# Set to yes to enable terminal support. +ifeq (HUGE, $(FEATURES)) +TERMINAL=yes +else +TERMINAL=no +endif + +ifndef CTAGS +# this assumes ctags is Exuberant ctags +CTAGS = ctags -I INIT+ --fields=+S +endif + +# Link against the shared version of libstdc++ by default. Set +# STATIC_STDCPLUS to "yes" to link against static version instead. +ifndef STATIC_STDCPLUS +STATIC_STDCPLUS=no +endif + + +# Link against the shared version of libwinpthread by default. Set +# STATIC_WINPTHREAD to "yes" to link against static version instead. +ifndef STATIC_WINPTHREAD +STATIC_WINPTHREAD=$(STATIC_STDCPLUS) +endif +# If you use TDM-GCC(-64), change HAS_GCC_EH to "no". +# This is used when STATIC_STDCPLUS=yes. +HAS_GCC_EH=yes + +# If the user doesn't want gettext, undefine it. +ifeq (no, $(GETTEXT)) +GETTEXT= +endif +# Added by E.F. Amatria 2001 Feb 23 +# Uncomment the first line and one of the following three if you want Native Language +# Support. You'll need gnu_gettext.win32, a MINGW32 Windows PORT of gettext by +# Franco Bez . It may be found at +# http://home.a-city.de/franco.bez/gettext/gettext_win32_en.html +# Tested with mingw32 with GCC-2.95.2 on Win98 +# Updated 2001 Jun 9 +#GETTEXT=c:/gettext.win32.msvcrt +#STATIC_GETTEXT=USE_STATIC_GETTEXT +#DYNAMIC_GETTEXT=USE_GETTEXT_DLL +#DYNAMIC_GETTEXT=USE_SAFE_GETTEXT_DLL +SAFE_GETTEXT_DLL_OBJ = $(GETTEXT)/src/safe_gettext_dll/safe_gettext_dll.o +# Alternatively, if you uncomment the two following lines, you get a "safe" version +# without linking the safe_gettext_dll.o object file. +#DYNAMIC_GETTEXT=DYNAMIC_GETTEXT +#GETTEXT_DYNAMIC=gnu_gettext.dll +INTLPATH=$(GETTEXT)/lib/mingw32 +INTLLIB=gnu_gettext + +# If you are using gettext-0.10.35 from http://sourceforge.net/projects/gettext +# or gettext-0.10.37 from http://sourceforge.net/projects/mingwrep/ +# uncomment the following, but I can't build a static version with them, ?-(| +#GETTEXT=c:/gettext-0.10.37-20010430 +#STATIC_GETTEXT=USE_STATIC_GETTEXT +#DYNAMIC_GETTEXT=DYNAMIC_GETTEXT +#INTLPATH=$(GETTEXT)/lib +#INTLLIB=intl + + +# Command definitions (depends on cross-compiling and shell) +ifeq ($(CROSS),yes) +# cross-compiler prefix: +ifndef CROSS_COMPILE +CROSS_COMPILE = i586-pc-mingw32msvc- +endif +DEL = rm +MKDIR = mkdir -p +DIRSLASH = / +else +# normal (Windows) compilation: +ifndef CROSS_COMPILE +CROSS_COMPILE = +endif + +# About the "sh.exe" condition, as explained by Ken Takata: +# +# If the makefile is executed with mingw32-make and sh.exe is not found in +# $PATH, then $SHELL is set to "sh.exe" (without any path). In this case, +# unix-like commands might not work and a dos-style path is needed. +# +# If the makefile is executed with mingw32-make and sh.exe IS found in $PATH, +# then $SHELL is set with the actual path of sh.exe (e.g. +# "C:/msys64/usr/bin/sh.exe"). In this case, unix-like commands can be used. +# +# If it is executed by the "make" command from cmd.exe, $SHELL is set to +# "/bin/sh". If the "make" command is in the $PATH, other unix-like commands +# might also work. +# +# If it is executed by the "make" command from a unix-like shell, +# $SHELL is set with the unix-style path (e.g. "/bin/bash"). +# In this case, unix-like commands can be used. +# +ifneq (sh.exe, $(SHELL)) +DEL = rm +MKDIR = mkdir -p +DIRSLASH = / +else +DEL = del +MKDIR = mkdir +DIRSLASH = \\ +endif +endif +CC := $(CROSS_COMPILE)gcc +CXX := $(CROSS_COMPILE)g++ +ifeq ($(UNDER_CYGWIN),yes) +WINDRES := $(CROSS_COMPILE)windres +else +WINDRES := windres +endif +WINDRES_CC = $(CC) + +# Get the default ARCH. +ifndef ARCH +ARCH := $(shell $(CC) -dumpmachine | sed -e 's/-.*//' -e 's/_/-/' -e 's/^mingw32$$/i686/') +endif + + +# Perl interface: +# PERL=[Path to Perl directory] (Set inside Make_cyg.mak or Make_ming.mak) +# DYNAMIC_PERL=yes (to load the Perl DLL dynamically) +# PERL_VER=[Perl version, eg 56, 58, 510] (default is 524) +ifdef PERL +ifndef PERL_VER +PERL_VER=524 +endif +ifndef DYNAMIC_PERL +DYNAMIC_PERL=yes +endif +# on Linux, for cross-compile, it's here: +#PERLLIB=/home/ron/ActivePerl/lib +# on NT, it's here: +PERLEXE=$(PERL)/bin/perl +PERLLIB=$(PERL)/lib +PERLLIBS=$(PERLLIB)/Core +ifeq ($(UNDER_CYGWIN),yes) +PERLTYPEMAP:=$(shell cygpath -m $(PERLLIB)/ExtUtils/typemap) +XSUBPPTRY:=$(shell cygpath -m $(PERLLIB)/ExtUtils/xsubpp) +else +PERLTYPEMAP=$(PERLLIB)/ExtUtils/typemap +XSUBPPTRY=$(PERLLIB)/ExtUtils/xsubpp +endif +XSUBPP_EXISTS=$(shell $(PERLEXE) -e "print 1 unless -e '$(XSUBPPTRY)'") +ifeq "$(XSUBPP_EXISTS)" "" +XSUBPP=$(PERLEXE) $(XSUBPPTRY) +else +XSUBPP=xsubpp +endif +endif + +# Lua interface: +# LUA=[Path to Lua directory] (Set inside Make_cyg.mak or Make_ming.mak) +# LUA_LIBDIR=[Path to Lua library directory] (default: $LUA/lib) +# LUA_INCDIR=[Path to Lua include directory] (default: $LUA/include) +# DYNAMIC_LUA=yes (to load the Lua DLL dynamically) +# LUA_VER=[Lua version, eg 51, 52] (default is 53) +ifdef LUA +ifndef DYNAMIC_LUA +DYNAMIC_LUA=yes +endif + +ifndef LUA_VER +LUA_VER=53 +endif + +ifeq (no,$(DYNAMIC_LUA)) +LUA_LIBDIR = $(LUA)/lib +LUA_LIB = -L$(LUA_LIBDIR) -llua +endif + +endif + +# MzScheme interface: +# MZSCHEME=[Path to MzScheme directory] (Set inside Make_cyg.mak or Make_ming.mak) +# DYNAMIC_MZSCHEME=yes (to load the MzScheme DLL dynamically) +# MZSCHEME_VER=[MzScheme version] (default is 3m_a0solc (6.6)) +# Used for the DLL file name. E.g.: +# C:\Program Files (x86)\Racket\lib\libracket3m_XXXXXX.dll +# MZSCHEME_DEBUG=no +ifdef MZSCHEME +ifndef DYNAMIC_MZSCHEME +DYNAMIC_MZSCHEME=yes +endif + +ifndef MZSCHEME_VER +MZSCHEME_VER=3m_a0solc +endif + +# for version 4.x we need to generate byte-code for Scheme base +ifndef MZSCHEME_GENERATE_BASE +MZSCHEME_GENERATE_BASE=no +endif + +ifneq ($(wildcard $(MZSCHEME)/lib/msvc/libmzsch$(MZSCHEME_VER).lib),) +MZSCHEME_MAIN_LIB=mzsch +else +MZSCHEME_MAIN_LIB=racket +endif + +ifndef MZSCHEME_PRECISE_GC +MZSCHEME_PRECISE_GC=no +ifneq ($(wildcard $(MZSCHEME)\lib\lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).dll),) +ifeq ($(wildcard $(MZSCHEME)\lib\libmzgc$(MZSCHEME_VER).dll),) +MZSCHEME_PRECISE_GC=yes +endif +else +ifneq ($(wildcard $(MZSCHEME)\lib\msvc\lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).lib),) +ifeq ($(wildcard $(MZSCHEME)\lib\msvc\libmzgc$(MZSCHEME_VER).lib),) +MZSCHEME_PRECISE_GC=yes +endif +endif +endif +endif + +ifeq (no,$(DYNAMIC_MZSCHEME)) +ifeq (yes,$(MZSCHEME_PRECISE_GC)) +MZSCHEME_LIB=-l$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER) +else +MZSCHEME_LIB=-l$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER) -lmzgc$(MZSCHEME_VER) +endif +# the modern MinGW can dynamically link to dlls directly. +# point MZSCHEME_DLLS to where you put libmzschXXXXXXX.dll and libgcXXXXXXX.dll +ifndef MZSCHEME_DLLS +MZSCHEME_DLLS=$(MZSCHEME) +endif +MZSCHEME_LIBDIR=-L$(MZSCHEME_DLLS) -L$(MZSCHEME_DLLS)\lib +endif + +endif + +# Python interface: +# PYTHON=[Path to Python directory] (Set inside Make_cyg.mak or Make_ming.mak) +# DYNAMIC_PYTHON=yes (to load the Python DLL dynamically) +# PYTHON_VER=[Python version, eg 22, 23, ..., 27] (default is 27) +ifdef PYTHON +ifndef DYNAMIC_PYTHON +DYNAMIC_PYTHON=yes +endif + +ifndef PYTHON_VER +PYTHON_VER=27 +endif +ifndef DYNAMIC_PYTHON_DLL +DYNAMIC_PYTHON_DLL=python$(PYTHON_VER).dll +endif +ifdef PYTHON_HOME +PYTHON_HOME_DEF=-DPYTHON_HOME=\"$(PYTHON_HOME)\" +endif + +ifeq (no,$(DYNAMIC_PYTHON)) +PYTHONLIB=-L$(PYTHON)/libs -lpython$(PYTHON_VER) +endif +# my include files are in 'win32inc' on Linux, and 'include' in the standard +# NT distro (ActiveState) +ifndef PYTHONINC +ifeq ($(CROSS),no) +PYTHONINC=-I $(PYTHON)/include +else +PYTHONINC=-I $(PYTHON)/win32inc +endif +endif +endif + +# Python3 interface: +# PYTHON3=[Path to Python3 directory] (Set inside Make_cyg.mak or Make_ming.mak) +# DYNAMIC_PYTHON3=yes (to load the Python3 DLL dynamically) +# PYTHON3_VER=[Python3 version, eg 31, 32] (default is 36) +ifdef PYTHON3 +ifndef DYNAMIC_PYTHON3 +DYNAMIC_PYTHON3=yes +endif + +ifndef PYTHON3_VER +PYTHON3_VER=36 +endif +ifndef DYNAMIC_PYTHON3_DLL +DYNAMIC_PYTHON3_DLL=python$(PYTHON3_VER).dll +endif +ifdef PYTHON3_HOME +PYTHON3_HOME_DEF=-DPYTHON3_HOME=L\"$(PYTHON3_HOME)\" +endif + +ifeq (no,$(DYNAMIC_PYTHON3)) +PYTHON3LIB=-L$(PYTHON3)/libs -lpython$(PYTHON3_VER) +endif + +ifndef PYTHON3INC +ifeq ($(CROSS),no) +PYTHON3INC=-I $(PYTHON3)/include +else +PYTHON3INC=-I $(PYTHON3)/win32inc +endif +endif +endif + +# TCL interface: +# TCL=[Path to TCL directory] (Set inside Make_cyg.mak or Make_ming.mak) +# DYNAMIC_TCL=yes (to load the TCL DLL dynamically) +# TCL_VER=[TCL version, eg 83, 84] (default is 86) +# TCL_VER_LONG=[Tcl version, eg 8.3] (default is 8.6) +# You must set TCL_VER_LONG when you set TCL_VER. +# TCL_DLL=[TCL dll name, eg tcl86.dll] (default is tcl86.dll) +ifdef TCL +ifndef DYNAMIC_TCL +DYNAMIC_TCL=yes +endif +ifndef TCL_VER +TCL_VER = 86 +endif +ifndef TCL_VER_LONG +TCL_VER_LONG = 8.6 +endif +ifndef TCL_DLL +TCL_DLL = tcl$(TCL_VER).dll +endif +TCLINC += -I$(TCL)/include +endif + + +# Ruby interface: +# RUBY=[Path to Ruby directory] (Set inside Make_cyg.mak or Make_ming.mak) +# DYNAMIC_RUBY=yes (to load the Ruby DLL dynamically, "no" for static) +# RUBY_VER=[Ruby version, eg 19, 22] (default is 22) +# RUBY_API_VER_LONG=[Ruby API version, eg 1.8, 1.9.1, 2.2.0] +# (default is 2.2.0) +# You must set RUBY_API_VER_LONG when changing RUBY_VER. +# Note: If you use Ruby 1.9.3, set as follows: +# RUBY_VER=19 +# RUBY_API_VER_LONG=1.9.1 (not 1.9.3, because the API version is 1.9.1.) +ifdef RUBY +ifndef DYNAMIC_RUBY +DYNAMIC_RUBY=yes +endif +# Set default value +ifndef RUBY_VER +RUBY_VER = 22 +endif +ifndef RUBY_VER_LONG +RUBY_VER_LONG = 2.2.0 +endif +ifndef RUBY_API_VER_LONG +RUBY_API_VER_LONG = $(RUBY_VER_LONG) +endif +ifndef RUBY_API_VER +RUBY_API_VER = $(subst .,,$(RUBY_API_VER_LONG)) +endif + +ifndef RUBY_PLATFORM +ifeq ($(RUBY_VER), 16) +RUBY_PLATFORM = i586-mswin32 +else +ifneq ($(wildcard $(RUBY)/lib/ruby/$(RUBY_API_VER_LONG)/i386-mingw32),) +RUBY_PLATFORM = i386-mingw32 +else +ifneq ($(wildcard $(RUBY)/lib/ruby/$(RUBY_API_VER_LONG)/x64-mingw32),) +RUBY_PLATFORM = x64-mingw32 +else +RUBY_PLATFORM = i386-mswin32 +endif +endif +endif +endif + +ifndef RUBY_INSTALL_NAME +ifeq ($(RUBY_VER), 16) +RUBY_INSTALL_NAME = mswin32-ruby$(RUBY_API_VER) +else +ifndef RUBY_MSVCRT_NAME +# Base name of msvcrXX.dll which is used by ruby's dll. +RUBY_MSVCRT_NAME = msvcrt +endif +ifeq ($(ARCH),x86-64) +RUBY_INSTALL_NAME = x64-$(RUBY_MSVCRT_NAME)-ruby$(RUBY_API_VER) +else +RUBY_INSTALL_NAME = $(RUBY_MSVCRT_NAME)-ruby$(RUBY_API_VER) +endif +endif +endif + +ifeq (19, $(word 1,$(sort 19 $(RUBY_VER)))) +RUBY_19_OR_LATER = 1 +endif + +ifdef RUBY_19_OR_LATER +RUBYINC = -I $(RUBY)/include/ruby-$(RUBY_API_VER_LONG) -I $(RUBY)/include/ruby-$(RUBY_API_VER_LONG)/$(RUBY_PLATFORM) +else +RUBYINC = -I $(RUBY)/lib/ruby/$(RUBY_API_VER_LONG)/$(RUBY_PLATFORM) +endif +ifeq (no, $(DYNAMIC_RUBY)) +RUBYLIB = -L$(RUBY)/lib -l$(RUBY_INSTALL_NAME) +endif + +endif # RUBY + +# See feature.h for a list of options. +# Any other defines can be included here. +DEF_GUI=-DFEAT_GUI_W32 -DFEAT_CLIPBOARD +DEFINES=-DWIN32 -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) \ + -DHAVE_PATHDEF -DFEAT_$(FEATURES) -DHAVE_STDINT_H +ifeq ($(ARCH),x86-64) +DEFINES+=-DMS_WIN64 +endif + +#>>>>> end of choices +########################################################################### + +CFLAGS = -I. -Iproto $(DEFINES) -pipe -march=$(ARCH) -Wall +CXXFLAGS = -std=gnu++11 +WINDRES_FLAGS = --preprocessor="$(WINDRES_CC) -E -xc" -DRC_INVOKED +EXTRA_LIBS = + +ifdef GETTEXT +DEFINES += -DHAVE_GETTEXT -DHAVE_LOCALE_H +GETTEXTINCLUDE = $(GETTEXT)/include +GETTEXTLIB = $(INTLPATH) +ifeq (yes, $(GETTEXT)) +DEFINES += -DDYNAMIC_GETTEXT +else +ifdef DYNAMIC_GETTEXT +DEFINES += -D$(DYNAMIC_GETTEXT) +ifdef GETTEXT_DYNAMIC +DEFINES += -DGETTEXT_DYNAMIC -DGETTEXT_DLL=\"$(GETTEXT_DYNAMIC)\" +endif +endif +endif +endif + +ifdef PERL +CFLAGS += -I$(PERLLIBS) -DFEAT_PERL -DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS +ifeq (yes, $(DYNAMIC_PERL)) +CFLAGS += -DDYNAMIC_PERL -DDYNAMIC_PERL_DLL=\"perl$(PERL_VER).dll\" +EXTRA_LIBS += -L$(PERLLIBS) -lperl$(PERL_VER) +endif +endif + +ifdef LUA +LUA_INCDIR = $(LUA)/include +CFLAGS += -I$(LUA_INCDIR) -I$(LUA) -DFEAT_LUA +ifeq (yes, $(DYNAMIC_LUA)) +CFLAGS += -DDYNAMIC_LUA -DDYNAMIC_LUA_DLL=\"lua$(LUA_VER).dll\" +endif +endif + +ifdef MZSCHEME +ifndef MZSCHEME_COLLECTS +MZSCHEME_COLLECTS=$(MZSCHEME)/collects +ifeq (yes, $(UNDER_CYGWIN)) +MZSCHEME_COLLECTS:=$(shell cygpath -m $(MZSCHEME_COLLECTS) | sed -e 's/ /\\ /g') +endif +endif +CFLAGS += -I$(MZSCHEME)/include -DFEAT_MZSCHEME -DMZSCHEME_COLLECTS=\"$(MZSCHEME_COLLECTS)\" +ifeq (yes, $(DYNAMIC_MZSCHEME)) +ifeq (yes, $(MZSCHEME_PRECISE_GC)) +# Precise GC does not use separate dll +CFLAGS += -DDYNAMIC_MZSCHEME -DDYNAMIC_MZSCH_DLL=\"lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).dll\" -DDYNAMIC_MZGC_DLL=\"lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).dll\" +else +CFLAGS += -DDYNAMIC_MZSCHEME -DDYNAMIC_MZSCH_DLL=\"lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).dll\" -DDYNAMIC_MZGC_DLL=\"libmzgc$(MZSCHEME_VER).dll\" +endif +endif +ifeq (yes, "$(MZSCHEME_DEBUG)") +CFLAGS += -DMZSCHEME_FORCE_GC +endif +endif + +ifdef RUBY +CFLAGS += -DFEAT_RUBY $(RUBYINC) +ifeq (yes, $(DYNAMIC_RUBY)) +CFLAGS += -DDYNAMIC_RUBY -DDYNAMIC_RUBY_DLL=\"$(RUBY_INSTALL_NAME).dll\" +CFLAGS += -DDYNAMIC_RUBY_VER=$(RUBY_VER) +endif +ifeq (no, $(DYNAMIC_RUBY)) +CFLAGS += -DRUBY_VERSION=$(RUBY_VER) +endif +ifneq ($(findstring w64-mingw32,$(CC)),) +# A workaround for MinGW-w64 +CFLAGS += -DHAVE_STRUCT_TIMESPEC -DHAVE_STRUCT_TIMEZONE +endif +endif + +ifdef PYTHON +CFLAGS += -DFEAT_PYTHON +ifeq (yes, $(DYNAMIC_PYTHON)) +CFLAGS += -DDYNAMIC_PYTHON -DDYNAMIC_PYTHON_DLL=\"$(DYNAMIC_PYTHON_DLL)\" +endif +endif + +ifdef PYTHON3 +CFLAGS += -DFEAT_PYTHON3 +ifeq (yes, $(DYNAMIC_PYTHON3)) +CFLAGS += -DDYNAMIC_PYTHON3 -DDYNAMIC_PYTHON3_DLL=\"$(DYNAMIC_PYTHON3_DLL)\" +endif +endif + +ifdef TCL +CFLAGS += -DFEAT_TCL $(TCLINC) +ifeq (yes, $(DYNAMIC_TCL)) +CFLAGS += -DDYNAMIC_TCL -DDYNAMIC_TCL_DLL=\"$(TCL_DLL)\" -DDYNAMIC_TCL_VER=\"$(TCL_VER_LONG)\" +endif +endif + +ifeq ($(POSTSCRIPT),yes) +DEFINES += -DMSWINPS +endif + +ifeq (yes, $(OLE)) +DEFINES += -DFEAT_OLE +endif + +ifeq ($(CSCOPE),yes) +DEFINES += -DFEAT_CSCOPE +endif + +ifeq ($(NETBEANS),yes) +# Only allow NETBEANS for a GUI build. +ifeq (yes, $(GUI)) +DEFINES += -DFEAT_NETBEANS_INTG + +ifeq ($(NBDEBUG), yes) +DEFINES += -DNBDEBUG +NBDEBUG_INCL = nbdebug.h +NBDEBUG_SRC = nbdebug.c +endif +endif +endif + +ifeq ($(CHANNEL),yes) +DEFINES += -DFEAT_JOB_CHANNEL +endif + +ifeq ($(TERMINAL),yes) +DEFINES += -DFEAT_TERMINAL +TERM_DEPS = \ + libvterm/include/vterm.h \ + libvterm/include/vterm_keycodes.h \ + libvterm/src/rect.h \ + libvterm/src/utf8.h \ + libvterm/src/vterm_internal.h +endif + +# DirectWrite (DirectX) +ifeq ($(DIRECTX),yes) +# Only allow DirectWrite for a GUI build. +ifeq (yes, $(GUI)) +DEFINES += -DFEAT_DIRECTX -DDYNAMIC_DIRECTX +ifneq ($(COLOR_EMOJI),no) +DEFINES += -DFEAT_DIRECTX_COLOR_EMOJI +endif +endif +endif + +# Only allow XPM for a GUI build. +ifeq (yes, $(GUI)) + +ifndef XPM +ifeq ($(ARCH),i386) +XPM = xpm/x86 +endif +ifeq ($(ARCH),i486) +XPM = xpm/x86 +endif +ifeq ($(ARCH),i586) +XPM = xpm/x86 +endif +ifeq ($(ARCH),i686) +XPM = xpm/x86 +endif +ifeq ($(ARCH),x86-64) +XPM = xpm/x64 +endif +endif +ifdef XPM +ifneq ($(XPM),no) +CFLAGS += -DFEAT_XPM_W32 -I $(XPM)/include -I $(XPM)/../include +endif +endif + +endif + +ifeq ($(DEBUG),yes) +CFLAGS += -g -fstack-check +DEBUG_SUFFIX=d +else +ifeq ($(OPTIMIZE), SIZE) +CFLAGS += -Os +else +ifeq ($(OPTIMIZE), MAXSPEED) +CFLAGS += -O3 +CFLAGS += -fomit-frame-pointer -freg-struct-return +else # SPEED +CFLAGS += -O2 +endif +endif +CFLAGS += -s +endif + +LIB = -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomdlg32 -lcomctl32 -lnetapi32 -lversion +GUIOBJ = $(OUTDIR)/gui.o $(OUTDIR)/gui_w32.o $(OUTDIR)/gui_beval.o $(OUTDIR)/os_w32exe.o +CUIOBJ = $(OUTDIR)/iscygpty.o +OBJ = \ + $(OUTDIR)/arabic.o \ + $(OUTDIR)/autocmd.o \ + $(OUTDIR)/beval.o \ + $(OUTDIR)/blob.o \ + $(OUTDIR)/blowfish.o \ + $(OUTDIR)/buffer.o \ + $(OUTDIR)/charset.o \ + $(OUTDIR)/crypt.o \ + $(OUTDIR)/crypt_zip.o \ + $(OUTDIR)/dict.o \ + $(OUTDIR)/diff.o \ + $(OUTDIR)/digraph.o \ + $(OUTDIR)/edit.o \ + $(OUTDIR)/eval.o \ + $(OUTDIR)/evalfunc.o \ + $(OUTDIR)/ex_cmds.o \ + $(OUTDIR)/ex_cmds2.o \ + $(OUTDIR)/ex_docmd.o \ + $(OUTDIR)/ex_eval.o \ + $(OUTDIR)/ex_getln.o \ + $(OUTDIR)/farsi.o \ + $(OUTDIR)/fileio.o \ + $(OUTDIR)/fold.o \ + $(OUTDIR)/getchar.o \ + $(OUTDIR)/hardcopy.o \ + $(OUTDIR)/hashtab.o \ + $(OUTDIR)/indent.o \ + $(OUTDIR)/json.o \ + $(OUTDIR)/list.o \ + $(OUTDIR)/main.o \ + $(OUTDIR)/mark.o \ + $(OUTDIR)/memfile.o \ + $(OUTDIR)/memline.o \ + $(OUTDIR)/menu.o \ + $(OUTDIR)/message.o \ + $(OUTDIR)/misc1.o \ + $(OUTDIR)/misc2.o \ + $(OUTDIR)/move.o \ + $(OUTDIR)/mbyte.o \ + $(OUTDIR)/normal.o \ + $(OUTDIR)/ops.o \ + $(OUTDIR)/option.o \ + $(OUTDIR)/os_win32.o \ + $(OUTDIR)/os_mswin.o \ + $(OUTDIR)/winclip.o \ + $(OUTDIR)/pathdef.o \ + $(OUTDIR)/popupmnu.o \ + $(OUTDIR)/quickfix.o \ + $(OUTDIR)/regexp.o \ + $(OUTDIR)/screen.o \ + $(OUTDIR)/search.o \ + $(OUTDIR)/sha256.o \ + $(OUTDIR)/sign.o \ + $(OUTDIR)/spell.o \ + $(OUTDIR)/spellfile.o \ + $(OUTDIR)/syntax.o \ + $(OUTDIR)/tag.o \ + $(OUTDIR)/term.o \ + $(OUTDIR)/textprop.o \ + $(OUTDIR)/ui.o \ + $(OUTDIR)/undo.o \ + $(OUTDIR)/userfunc.o \ + $(OUTDIR)/version.o \ + $(OUTDIR)/vimrc.o \ + $(OUTDIR)/window.o + +ifdef PERL +OBJ += $(OUTDIR)/if_perl.o +endif +ifdef LUA +OBJ += $(OUTDIR)/if_lua.o +endif +ifdef MZSCHEME +OBJ += $(OUTDIR)/if_mzsch.o +MZSCHEME_INCL = if_mzsch.h +ifeq (yes,$(MZSCHEME_GENERATE_BASE)) +CFLAGS += -DINCLUDE_MZSCHEME_BASE +MZ_EXTRA_DEP += mzscheme_base.c +endif +ifeq (yes,$(MZSCHEME_PRECISE_GC)) +CFLAGS += -DMZ_PRECISE_GC +endif +endif +ifdef PYTHON +OBJ += $(OUTDIR)/if_python.o +endif +ifdef PYTHON3 +OBJ += $(OUTDIR)/if_python3.o +endif +ifdef RUBY +OBJ += $(OUTDIR)/if_ruby.o +endif +ifdef TCL +OBJ += $(OUTDIR)/if_tcl.o +endif +ifeq ($(CSCOPE),yes) +OBJ += $(OUTDIR)/if_cscope.o +endif + +ifeq ($(NETBEANS),yes) +ifneq ($(CHANNEL),yes) +# Cannot use Netbeans without CHANNEL +NETBEANS=no +else +ifneq (yes, $(GUI)) +# Cannot use Netbeans without GUI. +NETBEANS=no +else +OBJ += $(OUTDIR)/netbeans.o +endif +endif +endif + +ifeq ($(CHANNEL),yes) +OBJ += $(OUTDIR)/channel.o +LIB += -lwsock32 +endif + +ifeq ($(DIRECTX),yes) +# Only allow DIRECTX for a GUI build. +ifeq (yes, $(GUI)) +OBJ += $(OUTDIR)/gui_dwrite.o +LIB += -ld2d1 -ldwrite +USE_STDCPLUS = yes +endif +endif +ifneq ($(XPM),no) +# Only allow XPM for a GUI build. +ifeq (yes, $(GUI)) +OBJ += $(OUTDIR)/xpm_w32.o +# You'll need libXpm.a from http://gnuwin32.sf.net +LIB += -L$(XPM)/lib -lXpm +endif +endif + +ifeq ($(TERMINAL),yes) +OBJ += $(OUTDIR)/terminal.o \ + $(OUTDIR)/encoding.o \ + $(OUTDIR)/keyboard.o \ + $(OUTDIR)/mouse.o \ + $(OUTDIR)/parser.o \ + $(OUTDIR)/pen.o \ + $(OUTDIR)/termscreen.o \ + $(OUTDIR)/state.o \ + $(OUTDIR)/unicode.o \ + $(OUTDIR)/vterm.o +endif + +# Include xdiff +OBJ += $(OUTDIR)/xdiffi.o \ + $(OUTDIR)/xemit.o \ + $(OUTDIR)/xprepare.o \ + $(OUTDIR)/xutils.o \ + $(OUTDIR)/xhistogram.o \ + $(OUTDIR)/xpatience.o + +XDIFF_DEPS = \ + xdiff/xdiff.h \ + xdiff/xdiffi.h \ + xdiff/xemit.h \ + xdiff/xinclude.h \ + xdiff/xmacros.h \ + xdiff/xprepare.h \ + xdiff/xtypes.h \ + xdiff/xutils.h + +ifdef MZSCHEME +MZSCHEME_SUFFIX = Z +endif + +ifeq ($(GUI),yes) +TARGET := gvim$(DEBUG_SUFFIX).exe +DEFINES += $(DEF_GUI) +OBJ += $(GUIOBJ) +LFLAGS += -mwindows +OUTDIR = gobj$(DEBUG_SUFFIX)$(MZSCHEME_SUFFIX)$(ARCH) +else +OBJ += $(CUIOBJ) +TARGET := vim$(DEBUG_SUFFIX).exe +OUTDIR = obj$(DEBUG_SUFFIX)$(MZSCHEME_SUFFIX)$(ARCH) +endif + +ifdef GETTEXT +ifneq (yes, $(GETTEXT)) +CFLAGS += -I$(GETTEXTINCLUDE) +ifndef STATIC_GETTEXT +LIB += -L$(GETTEXTLIB) -l$(INTLLIB) +ifeq (USE_SAFE_GETTEXT_DLL, $(DYNAMIC_GETTEXT)) +OBJ+=$(SAFE_GETTEXT_DLL_OBJ) +endif +else +LIB += -L$(GETTEXTLIB) -lintl +endif +endif +endif + +ifdef PERL +ifeq (no, $(DYNAMIC_PERL)) +LIB += -L$(PERLLIBS) -lperl$(PERL_VER) +endif +endif + +ifdef TCL +LIB += -L$(TCL)/lib +ifeq (yes, $(DYNAMIC_TCL)) +LIB += -ltclstub$(TCL_VER) +else +LIB += -ltcl$(TCL_VER) +endif +endif + +ifeq (yes, $(OLE)) +LIB += -loleaut32 +OBJ += $(OUTDIR)/if_ole.o +USE_STDCPLUS = yes +endif + +ifeq (yes, $(IME)) +DEFINES += -DFEAT_MBYTE_IME +ifeq (yes, $(DYNAMIC_IME)) +DEFINES += -DDYNAMIC_IME +else +LIB += -limm32 +endif +endif + +ifdef ICONV +ifneq (yes, $(ICONV)) +LIB += -L$(ICONV) +CFLAGS += -I$(ICONV) +endif +DEFINES+=-DDYNAMIC_ICONV +endif + +ifeq (yes, $(USE_STDCPLUS)) +LINK = $(CXX) +ifeq (yes, $(STATIC_STDCPLUS)) +#LIB += -static-libstdc++ -static-libgcc +LIB += -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic +endif +else +LINK = $(CC) +endif + +ifeq (yes, $(STATIC_WINPTHREAD)) +ifeq (yes, $(HAS_GCC_EH)) +LIB += -lgcc_eh +endif +LIB += -Wl,-Bstatic -lwinpthread -Wl,-Bdynamic +endif + +ifeq (yes, $(MAP)) +LFLAGS += -Wl,-Map=$(TARGET).map +endif + +all: $(TARGET) vimrun.exe xxd/xxd.exe tee/tee.exe install.exe uninstal.exe GvimExt/gvimext.dll + +vimrun.exe: vimrun.c + $(CC) $(CFLAGS) -o vimrun.exe vimrun.c $(LIB) + +install.exe: dosinst.c + $(CC) $(CFLAGS) -o install.exe dosinst.c $(LIB) -lole32 -luuid + +uninstal.exe: uninstal.c + $(CC) $(CFLAGS) -o uninstal.exe uninstal.c $(LIB) + +$(TARGET): $(OUTDIR) $(OBJ) + $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) + +upx: exes + upx gvim.exe + upx vim.exe + +mpress: exes + mpress gvim.exe + mpress vim.exe + +xxd/xxd.exe: xxd/xxd.c + $(MAKE) -C xxd -f Make_ming.mak CC='$(CC)' + +tee/tee.exe: tee/tee.c + $(MAKE) -C tee CC='$(CC)' + +GvimExt/gvimext.dll: GvimExt/gvimext.cpp GvimExt/gvimext.rc GvimExt/gvimext.h + $(MAKE) -C GvimExt -f Make_ming.mak CROSS=$(CROSS) CROSS_COMPILE=$(CROSS_COMPILE) CXX='$(CXX)' STATIC_STDCPLUS=$(STATIC_STDCPLUS) + +tags: notags + $(CTAGS) $(TAGS_FILES) + +notags: + -$(DEL) tags + +clean: + -$(DEL) $(OUTDIR)$(DIRSLASH)*.o + -$(DEL) $(OUTDIR)$(DIRSLASH)*.res + -rmdir $(OUTDIR) + -$(DEL) $(TARGET) vimrun.exe install.exe uninstal.exe + -$(DEL) pathdef.c +ifdef PERL + -$(DEL) if_perl.c + -$(DEL) auto$(DIRSLASH)if_perl.c +endif +ifdef MZSCHEME + -$(DEL) mzscheme_base.c +endif + $(MAKE) -C GvimExt -f Make_ming.mak clean + $(MAKE) -C xxd -f Make_ming.mak clean + $(MAKE) -C tee clean + +########################################################################### +INCL = vim.h alloc.h arabic.h ascii.h ex_cmds.h farsi.h feature.h globals.h \ + keymap.h macros.h option.h os_dos.h os_win32.h proto.h regexp.h \ + spell.h structs.h term.h beval.h $(NBDEBUG_INCL) +GUI_INCL = gui.h +CUI_INCL = iscygpty.h + +$(OUTDIR)/if_python.o: if_python.c if_py_both.h $(INCL) + $(CC) -c $(CFLAGS) $(PYTHONINC) $(PYTHON_HOME_DEF) $< -o $@ + +$(OUTDIR)/if_python3.o: if_python3.c if_py_both.h $(INCL) + $(CC) -c $(CFLAGS) $(PYTHON3INC) $(PYTHON3_HOME_DEF) $< -o $@ + +$(OUTDIR)/%.o : %.c $(INCL) + $(CC) -c $(CFLAGS) $< -o $@ + +$(OUTDIR)/vimrc.o: vim.rc version.h gui_w32_rc.h + $(WINDRES) $(WINDRES_FLAGS) $(DEFINES) \ + --input-format=rc --output-format=coff -i vim.rc -o $@ + +$(OUTDIR): + $(MKDIR) $(OUTDIR) + +$(OUTDIR)/gui_dwrite.o: gui_dwrite.cpp $(INCL) gui_dwrite.h + $(CC) -c $(CFLAGS) $(CXXFLAGS) gui_dwrite.cpp -o $(OUTDIR)/gui_dwrite.o + +$(OUTDIR)/gui.o: gui.c $(INCL) $(GUI_INCL) + $(CC) -c $(CFLAGS) gui.c -o $(OUTDIR)/gui.o + +$(OUTDIR)/beval.o: beval.c $(INCL) $(GUI_INCL) + $(CC) -c $(CFLAGS) beval.c -o $(OUTDIR)/beval.o + +$(OUTDIR)/gui_beval.o: gui_beval.c $(INCL) $(GUI_INCL) + $(CC) -c $(CFLAGS) gui_beval.c -o $(OUTDIR)/gui_beval.o + +$(OUTDIR)/gui_w32.o: gui_w32.c $(INCL) $(GUI_INCL) + $(CC) -c $(CFLAGS) gui_w32.c -o $(OUTDIR)/gui_w32.o + +$(OUTDIR)/if_cscope.o: if_cscope.c $(INCL) if_cscope.h + $(CC) -c $(CFLAGS) if_cscope.c -o $(OUTDIR)/if_cscope.o + +$(OUTDIR)/if_mzsch.o: if_mzsch.c $(INCL) $(MZSCHEME_INCL) $(MZ_EXTRA_DEP) + $(CC) -c $(CFLAGS) if_mzsch.c -o $(OUTDIR)/if_mzsch.o + +mzscheme_base.c: + $(MZSCHEME)/mzc --c-mods mzscheme_base.c ++lib scheme/base + +# Remove -D__IID_DEFINED__ for newer versions of the w32api +$(OUTDIR)/if_ole.o: if_ole.cpp $(INCL) if_ole.h + $(CC) $(CFLAGS) $(CXXFLAGS) -c -o $(OUTDIR)/if_ole.o if_ole.cpp + +auto/if_perl.c: if_perl.xs typemap + $(XSUBPP) -prototypes -typemap \ + $(PERLTYPEMAP) if_perl.xs -output $@ + +$(OUTDIR)/if_perl.o: auto/if_perl.c $(INCL) + $(CC) -c $(CFLAGS) auto/if_perl.c -o $(OUTDIR)/if_perl.o + + +$(OUTDIR)/if_ruby.o: if_ruby.c $(INCL) +ifeq (16, $(RUBY)) + $(CC) $(CFLAGS) -U_WIN32 -c -o $(OUTDIR)/if_ruby.o if_ruby.c +endif + +$(OUTDIR)/iscygpty.o: iscygpty.c $(CUI_INCL) + $(CC) -c $(CFLAGS) iscygpty.c -o $(OUTDIR)/iscygpty.o -U_WIN32_WINNT -D_WIN32_WINNT=0x0600 -DUSE_DYNFILEID -DENABLE_STUB_IMPL + +$(OUTDIR)/main.o: main.c $(INCL) $(CUI_INCL) + $(CC) -c $(CFLAGS) main.c -o $(OUTDIR)/main.o + +$(OUTDIR)/netbeans.o: netbeans.c $(INCL) $(NBDEBUG_INCL) $(NBDEBUG_SRC) + $(CC) -c $(CFLAGS) netbeans.c -o $(OUTDIR)/netbeans.o + +$(OUTDIR)/os_win32.o: os_win32.c $(INCL) $(MZSCHEME_INCL) + $(CC) -c $(CFLAGS) os_win32.c -o $(OUTDIR)/os_win32.o + +$(OUTDIR)/regexp.o: regexp.c regexp_nfa.c $(INCL) + $(CC) -c $(CFLAGS) regexp.c -o $(OUTDIR)/regexp.o + +$(OUTDIR)/terminal.o: terminal.c $(INCL) $(TERM_DEPS) + $(CC) -c $(CFLAGS) terminal.c -o $(OUTDIR)/terminal.o + +$(OUTDIR)/textprop.o: textprop.c $(INCL) + $(CC) -c $(CFLAGS) textprop.c -o $(OUTDIR)/textprop.o + + +CCCTERM = $(CC) -c $(CFLAGS) -Ilibvterm/include -DINLINE="" \ + -DVSNPRINTF=vim_vsnprintf \ + -DIS_COMBINING_FUNCTION=utf_iscomposing_uint \ + -DWCWIDTH_FUNCTION=utf_uint2cells + +$(OUTDIR)/encoding.o: libvterm/src/encoding.c $(TERM_DEPS) + $(CCCTERM) libvterm/src/encoding.c -o $@ + +$(OUTDIR)/keyboard.o: libvterm/src/keyboard.c $(TERM_DEPS) + $(CCCTERM) libvterm/src/keyboard.c -o $@ + +$(OUTDIR)/mouse.o: libvterm/src/mouse.c $(TERM_DEPS) + $(CCCTERM) libvterm/src/mouse.c -o $@ + +$(OUTDIR)/parser.o: libvterm/src/parser.c $(TERM_DEPS) + $(CCCTERM) libvterm/src/parser.c -o $@ + +$(OUTDIR)/pen.o: libvterm/src/pen.c $(TERM_DEPS) + $(CCCTERM) libvterm/src/pen.c -o $@ + +$(OUTDIR)/termscreen.o: libvterm/src/termscreen.c $(TERM_DEPS) + $(CCCTERM) libvterm/src/termscreen.c -o $@ + +$(OUTDIR)/state.o: libvterm/src/state.c $(TERM_DEPS) + $(CCCTERM) libvterm/src/state.c -o $@ + +$(OUTDIR)/unicode.o: libvterm/src/unicode.c $(TERM_DEPS) + $(CCCTERM) libvterm/src/unicode.c -o $@ + +$(OUTDIR)/vterm.o: libvterm/src/vterm.c $(TERM_DEPS) + $(CCCTERM) libvterm/src/vterm.c -o $@ + +$(OUTDIR)/xdiffi.o: xdiff/xdiffi.c $(XDIFF_DEPS) + $(CC) -c $(CFLAGS) xdiff/xdiffi.c -o $(OUTDIR)/xdiffi.o + +$(OUTDIR)/xemit.o: xdiff/xemit.c $(XDIFF_DEPS) + $(CC) -c $(CFLAGS) xdiff/xemit.c -o $(OUTDIR)/xemit.o + +$(OUTDIR)/xprepare.o: xdiff/xprepare.c $(XDIFF_DEPS) + $(CC) -c $(CFLAGS) xdiff/xprepare.c -o $(OUTDIR)/xprepare.o + +$(OUTDIR)/xutils.o: xdiff/xutils.c $(XDIFF_DEPS) + $(CC) -c $(CFLAGS) xdiff/xutils.c -o $(OUTDIR)/xutils.o + +$(OUTDIR)/xhistogram.o: xdiff/xhistogram.c $(XDIFF_DEPS) + $(CC) -c $(CFLAGS) xdiff/xhistogram.c -o $(OUTDIR)/xhistogram.o + +$(OUTDIR)/xpatience.o: xdiff/xpatience.c $(XDIFF_DEPS) + $(CC) -c $(CFLAGS) xdiff/xpatience.c -o $(OUTDIR)/xpatience.o + +pathdef.c: $(INCL) +ifneq (sh.exe, $(SHELL)) + @echo creating pathdef.c + @echo '/* pathdef.c */' > pathdef.c + @echo '#include "vim.h"' >> pathdef.c + @echo 'char_u *default_vim_dir = (char_u *)"$(VIMRCLOC)";' >> pathdef.c + @echo 'char_u *default_vimruntime_dir = (char_u *)"$(VIMRUNTIMEDIR)";' >> pathdef.c + @echo 'char_u *all_cflags = (char_u *)"$(CC) $(CFLAGS)";' >> pathdef.c + @echo 'char_u *all_lflags = (char_u *)"$(LINK) $(CFLAGS) $(LFLAGS) -o $(TARGET) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB)";' >> pathdef.c + @echo 'char_u *compiled_user = (char_u *)"$(USERNAME)";' >> pathdef.c + @echo 'char_u *compiled_sys = (char_u *)"$(USERDOMAIN)";' >> pathdef.c +else + @echo creating pathdef.c + @echo /* pathdef.c */ > pathdef.c + @echo #include "vim.h" >> pathdef.c + @echo char_u *default_vim_dir = (char_u *)"$(VIMRCLOC)"; >> pathdef.c + @echo char_u *default_vimruntime_dir = (char_u *)"$(VIMRUNTIMEDIR)"; >> pathdef.c + @echo char_u *all_cflags = (char_u *)"$(CC) $(CFLAGS)"; >> pathdef.c + @echo char_u *all_lflags = (char_u *)"$(CC) $(CFLAGS) $(LFLAGS) -o $(TARGET) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB)"; >> pathdef.c + @echo char_u *compiled_user = (char_u *)"$(USERNAME)"; >> pathdef.c + @echo char_u *compiled_sys = (char_u *)"$(USERDOMAIN)"; >> pathdef.c +endif + +# vim: set noet sw=8 ts=8 sts=0 wm=0 tw=0: diff --git a/src/Make_dice.mak b/src/Make_dice.mak new file mode 100644 index 0000000..57aed47 --- /dev/null +++ b/src/Make_dice.mak @@ -0,0 +1,285 @@ +# +# Makefile for VIM, using DICE 3 +# + +#>>>>> choose options: +### See feature.h for a list of optionals. +### Any other defines can be included here. +DEFINES = -DHAVE_TGETENT -DUP_BC_PC_EXTERN -DOSPEED_EXTERN + +#>>>>> if HAVE_TGETENT is defined o/termlib.o has to be used +TERMLIB = o/termlib.o +#TERMLIB = + +#>>>>> end of choices +########################################################################### + +CFLAGS = -c -DAMIGA -Iproto $(DEFINES) + +SYMS = vim.syms +PRE = -H${SYMS}=vim.h +LIBS = -la +CC = dcc +LD = dcc + +.c.o: + ${CC} ${PRE} ${CFLAGS} $< -o $@ + +SRC = \ + arabic.c \ + autocmd.c \ + blowfish.c \ + buffer.c \ + charset.c \ + crypt.c \ + crypt_zip.c \ + dict.c \ + diff.c \ + digraph.c \ + edit.c \ + eval.c \ + evalfunc.c \ + ex_cmds.c \ + ex_cmds2.c \ + ex_docmd.c \ + ex_eval.c \ + ex_getln.c \ + farsi.c \ + fileio.c \ + fold.c \ + getchar.c \ + hardcopy.c \ + hashtab.c \ + indent.c \ + json.c \ + list.c \ + main.c \ + mark.c \ + memfile.c \ + memline.c \ + menu.c \ + message.c \ + misc1.c \ + misc2.c \ + move.c \ + mbyte.c \ + normal.c \ + ops.c \ + option.c \ + os_amiga.c \ + popupmnu.c \ + quickfix.c \ + regexp.c \ + screen.c \ + search.c \ + sha256.c \ + sign.c \ + spell.c \ + spellfile.c \ + syntax.c \ + tag.c \ + term.c \ + ui.c \ + undo.c \ + userfunc.c \ + window.c \ + version.c + +OBJ = o/arabic.o \ + o/autocmd.o \ + o/blowfish.o \ + o/buffer.o \ + o/charset.o \ + o/crypt.o \ + o/crypt_zip.o \ + o/dict.o \ + o/diff.o \ + o/digraph.o \ + o/edit.o \ + o/eval.o \ + o/evalfunc.o \ + o/ex_cmds.o \ + o/ex_cmds2.o \ + o/ex_docmd.o \ + o/ex_eval.o \ + o/ex_getln.o \ + o/farsi.o \ + o/fileio.o \ + o/fold.o \ + o/getchar.o \ + o/hardcopy.o \ + o/hashtab.o \ + o/indent.o \ + o/json.o \ + o/list.o \ + o/main.o \ + o/mark.o \ + o/memfile.o \ + o/memline.o \ + o/menu.o \ + o/message.o \ + o/misc1.o \ + o/misc2.o \ + o/move.o \ + o/mbyte.o \ + o/normal.o \ + o/ops.o \ + o/option.o \ + o/os_amiga.o \ + o/popupmnu.o \ + o/quickfix.o \ + o/regexp.o \ + o/screen.o \ + o/search.o \ + o/sha256.o \ + o/sign.o \ + o/spell.o \ + o/spellfile.o \ + o/syntax.o \ + o/tag.o \ + o/term.o \ + o/ui.o \ + o/undo.o \ + o/userfunc.o \ + o/window.o \ + $(TERMLIB) + +Vim: $(OBJ) version.c version.h + ${CC} $(CFLAGS) version.c -o o/version.o + ${LD} -o Vim $(OBJ) o/version.o $(LIBS) + +debug: $(OBJ) version.c version.h + ${CC} $(CFLAGS) version.c -o o/version.o + ${LD} -s -o Vim $(OBJ) o/version.o $(LIBS) + +tags: + csh -c ctags $(SRC) *.h + +clean: + delete o/*.o Vim $(SYMS) + +$(SYMS) : vim.h globals.h keymap.h macros.h ascii.h term.h os_amiga.h structs.h + delete $(SYMS) + +########################################################################### + +o/arabic.o: arabic.c $(SYMS) + +o/autocmd.o: autocmd.c $(SYMS) + +o/blowfish.o: blowfish.c $(SYMS) + +o/buffer.o: buffer.c $(SYMS) + +o/charset.o: charset.c $(SYMS) + +o/crypt.o: crypt.c $(SYMS) + +o/crypt_zip.o: crypt_zip.c $(SYMS) + +o/dict.o: dict.c $(SYMS) + +o/diff.o: diff.c $(SYMS) + +o/digraph.o: digraph.c $(SYMS) + +o/edit.o: edit.c $(SYMS) + +o/eval.o: eval.c $(SYMS) + +o/evalfunc.o: evalfunc.c $(SYMS) + +o/ex_cmds.o: ex_cmds.c $(SYMS) + +o/ex_cmds2.o: ex_cmds2.c $(SYMS) + +o/ex_docmd.o: ex_docmd.c $(SYMS) ex_cmds.h + +o/ex_eval.o: ex_eval.c $(SYMS) ex_cmds.h + +o/ex_getln.o: ex_getln.c $(SYMS) + +o/farsi.o: farsi.c $(SYMS) + +o/fileio.o: fileio.c $(SYMS) + +o/fold.o: fold.c $(SYMS) + +o/getchar.o: getchar.c $(SYMS) + +o/hardcopy.o: hardcopy.c $(SYMS) + +o/hashtab.o: hashtab.c $(SYMS) + +o/indent.o: indent.c $(SYMS) + +o/json.o: json.c $(SYMS) + +o/list.o: list.c $(SYMS) + +o/main.o: main.c $(SYMS) + +o/mark.o: mark.c $(SYMS) + +o/memfile.o: memfile.c $(SYMS) + +o/memline.o: memline.c $(SYMS) + +o/menu.o: menu.c $(SYMS) + +o/message.o: message.c $(SYMS) + +o/misc1.o: misc1.c $(SYMS) + +o/misc2.o: misc2.c $(SYMS) + +o/move.o: move.c $(SYMS) + +o/mbyte.o: mbyte.c $(SYMS) + +o/normal.o: normal.c $(SYMS) + +o/ops.o: ops.c $(SYMS) + +o/option.o: option.c $(SYMS) +# Because of a bug in DC1 2.06.40, initialisation of unions does not +# work correctly. dc1-21 is DC1 2.06.21 which does work. +# rename dc1-21 dc1 + ${CC} ${CFLAGS} option.c -o o/option.o +# rename dc1 dc1-21 + +o/os_amiga.o: os_amiga.c $(SYMS) os_amiga.h + +o/popupmnu.o: popupmnu.c $(SYMS) + +o/quickfix.o: quickfix.c $(SYMS) + +o/regexp.o: regexp.c $(SYMS) regexp.h + +o/screen.o: screen.c $(SYMS) + +o/search.o: search.c $(SYMS) regexp.h + +o/sha256.o: sha256.c $(SYMS) + +o/sign.o: sign.c $(SYMS) + +o/spell.o: spell.c $(SYMS) spell.h + +o/spellfile.o: spellfile.c $(SYMS) spell.h + +o/syntax.o: syntax.c $(SYMS) + +o/tag.o: tag.c $(SYMS) + +o/term.o: term.c $(SYMS) term.h + +o/termlib.o: termlib.c $(SYMS) + +o/ui.o: ui.c $(SYMS) + +o/undo.o: undo.c $(SYMS) + +o/userfunc.o: userfunc.c $(SYMS) + +o/window.o: window.c $(SYMS) diff --git a/src/Make_dvc.mak b/src/Make_dvc.mak new file mode 100644 index 0000000..46377f0 --- /dev/null +++ b/src/Make_dvc.mak @@ -0,0 +1,105 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=Vim - Win32 IDE for Make_mvc.mak +!MESSAGE No configuration specified. Defaulting to Vim - Win32 IDE for\ + Make_mvc.mak. +!ENDIF + +!IF "$(CFG)" != "Vim - Win32 IDE for Make_mvc.mak" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Make_dvc.mak" CFG="Vim - Win32 IDE for Make_mvc.mak" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Vim - Win32 IDE for Make_mvc.mak" (based on\ + "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "Vim - Win32 IDE for Make_mvc.mak" +CPP=cl.exe +RSC=rc.exe +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "" +# PROP Target_Dir "" +OUTDIR=. +INTDIR=. + +ALL : "$(OUTDIR)\vimrun.exe" + +CLEAN : + -@erase ".\vimrun.exe" + -@erase ".\vimrun.obj" + +# ADD CPP /nologo /c +# ADD BASE RSC /l 0x809 +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/Make_dvc.bsc" +BSC32_SBRS= +LINK32=link.exe +# ADD BASE LINK32 /machine:IX86 +# ADD LINK32 /nologo /pdb:none /machine:IX86 /out:"vimrun.exe" +LINK32_FLAGS=/nologo /pdb:none /machine:IX86 /out:"$(OUTDIR)/vimrun.exe" +LINK32_OBJS= \ + "$(INTDIR)/vimrun.obj" + +"$(OUTDIR)\vimrun.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +CPP_PROJ=/nologo /ML /c + +.c.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx.obj: + $(CPP) $(CPP_PROJ) $< + +.c.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx.sbr: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Target + +# Name "Vim - Win32 IDE for Make_mvc.mak" +################################################################################ +# Begin Source File + +SOURCE=.\vimrun.c + +"$(INTDIR)\vimrun.obj" : $(SOURCE) "$(INTDIR)" + + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/src/Make_ivc.mak b/src/Make_ivc.mak new file mode 100644 index 0000000..96d6a47 --- /dev/null +++ b/src/Make_ivc.mak @@ -0,0 +1,763 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00 +# ** DO NOT EDIT ** +# +# Make_ivc.mak Makefile to build vim in both IDE and nmake. +# This file can be imported as a workspace into Visual Studio. It must be in +# DOS fileformat then! +# +# It is worth making the file read-only as the VC4 IDE will try to overwrite +# it with a HUGELY expanded clone of itself. +# +# The following points are worth noting: +# 1) Comments here are ignored by VC[456].0 IDEs +# 2) # ADD LINK32 /pdb:.\Dbg/vimd.pdb is written so rather than +# # ADD LINK32 /pdb:".\Dbg/vimd.pdb" to avoid VC4 -> VC5 conversion failure +# 3) It is good to delete .pdb file before linking to cope with switch among +# VC[456] as IDE clean action does not remove that file and link clashes +# with it. The following works in VC5 but not in VC4 which does not support +# pre-link actions. The nmake action does such deletions. +# Begin Special Build Tool +PreLink_Cmds=@if exist .\oleDbg\gvimd.pdb del .\oleDbg\gvimd.pdb +# End Special Build Tool +# 4) I was unable to make !IFDEF OLE, etc. work in the VC4 IDE. +# I was aiming for 4 configurations with sub-configurations selected by +# environment variables. +# 5) Optimisation is not supported by disabled versions of VC. This results in +# messages for Release builds like: +# Command line warning D4025 : overriding '/O2' with '/Od' +# 6) nmake 1.62 and later support batch compilation. I was unable to use this +# in a manner acceptable to earlier IDEs. +# +# History +# +# When Who What +# 2001-07-06 W.Briscoe Original derived from Make_[go]vc.mak with less noise +# 2001-07-08 W.Briscoe Further noise reduction; consistent .map and .pdb logic +# Added install.exe rule, etc.; Removed unused libraries. +# 2001-08-09 W.Briscoe Restored VC4.0-required trailing space in !MESSAGE afore +# Enhanced if_ole.idl rule to use /out argument. +# Default rules now relative to . to reduce IDE/nmake difs + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=Vim - Win32 Release gvim OLE +!MESSAGE No configuration specified. Defaulting to Vim - Win32 Release gvim OLE. +!ENDIF + +!IF "$(CFG)" != "Vim - Win32 Release gvim OLE"\ + && "$(CFG)" != "Vim - Win32 Debug gvim OLE"\ + && "$(CFG)" != "Vim - Win32 Release gvim"\ + && "$(CFG)" != "Vim - Win32 Debug gvim"\ + && "$(CFG)" != "Vim - Win32 Release vim"\ + && "$(CFG)" != "Vim - Win32 Debug vim" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Make_ivc.mak" CFG="Vim - Win32 Debug vim" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Vim - Win32 Release gvim OLE" (based on "Win32 (x86) Console Application") +!MESSAGE "Vim - Win32 Debug gvim OLE" (based on "Win32 (x86) Console Application") +!MESSAGE "Vim - Win32 Release gvim" (based on "Win32 (x86) Console Application") +!MESSAGE "Vim - Win32 Debug gvim" (based on "Win32 (x86) Console Application") +!MESSAGE "Vim - Win32 Release vim" (based on "Win32 (x86) Console Application") +!MESSAGE "Vim - Win32 Debug vim" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +DEL_TREE = rmdir /s /q +!ELSE +NULL=nul +DEL_TREE = deltree /y +!ENDIF + +# Begin Project +# PROP Target_Last_Scanned "Vim - Win32 Debug vim" +# PROP Use_MFC 0 + +RSC=rc.exe +CPP=cl.exe +LINK32=link.exe + +CPP_PROJ= /nologo /MT /W3 /GX /I ".\proto" /D "WIN32" /c +# ADD CPP /nologo /MT /W3 /GX /I ".\proto" /D "WIN32" /c + +LINK32_FLAGS= oldnames.lib kernel32.lib user32.lib gdi32.lib version.lib comdlg32.lib comctl32.lib advapi32.lib shell32.lib ole32.lib netapi32.lib uuid.lib /nologo /machine:I386 /nodefaultlib +# ADD LINK32 oldnames.lib kernel32.lib user32.lib gdi32.lib version.lib comdlg32.lib comctl32.lib advapi32.lib shell32.lib ole32.lib uuid.lib /nologo /machine:I386 /nodefaultlib +# SUBTRACT LINK32 /incremental:yes + +RSC_PROJ= /l 0x409 /d "FEAT_GUI_W32" +# ADD RSC /l 0x409 /d "FEAT_GUI_W32" + +!IF "$(CFG)" == "Vim - Win32 Release gvim OLE" + +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir .\oleRel +# PROP Intermediate_Dir .\oleRel + +INTDIR=.\oleRel +VIM=gvim +EXTRAS="$(INTDIR)/if_ole.obj" "$(INTDIR)/vim.res" "$(INTDIR)/gui.obj" "$(INTDIR)/gui_w32.obj" "$(INTDIR)/gui_beval.obj" "$(INTDIR)/os_w32exe.obj" + +CPP_PROJ=$(CPP_PROJ) /Zi /O2 /D "NDEBUG" /D "FEAT_GUI_W32" /D "DYNAMIC_GETTEXT" /D "FEAT_OLE" /Fd.\oleRel/ /Fo.\oleRel/ +# ADD CPP /Zi /O2 /D "NDEBUG" /D "FEAT_GUI_W32" /D "DYNAMIC_GETTEXT" /D "FEAT_OLE" /Fd.\oleRel/ /Fo.\oleRel/ + +RSC_PROJ=$(RSC_PROJ) /I ".\oleRel" /d "NDEBUG" /d "FEAT_OLE" /fo.\oleRel\vim.res +# ADD RSC /I ".\oleRel" /d "NDEBUG" /d "FEAT_OLE" /fo.\oleRel\vim.res + +LINK32_FLAGS=$(LINK32_FLAGS) /pdb:.\oleRel/gvim.pdb -debug:full -debugtype:cv,fixup /map:.\oleDbg\gvim.map libc.lib oleaut32.lib /subsystem:windows /out:.\gvim.exe +# ADD LINK32 /pdb:.\oleRel/gvim.pdb -debug:full -debugtype:cv,fixup /map:.\oleDbg\gvim.map libc.lib oleaut32.lib /subsystem:windows /out:.\gvim.exe + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug gvim OLE" + +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir .\oleDbg +# PROP Intermediate_Dir .\oleDbg + +INTDIR=.\oleDbg +VIM=gvimd +EXTRAS="$(INTDIR)/if_ole.obj" "$(INTDIR)/vim.res" "$(INTDIR)/gui.obj" "$(INTDIR)/gui_w32.obj" "$(INTDIR)/gui_beval.obj" "$(INTDIR)/os_w32exe.obj" + +CPP_PROJ=$(CPP_PROJ) /Zi /Od /D "_DEBUG" /D "FEAT_GUI_W32" /D "DYNAMIC_GETTEXT" /D "FEAT_OLE" /Fd.\oleDbg/ /Fo.\oleDbg/ +# ADD CPP /Zi /Od /D "_DEBUG" /D "FEAT_GUI_W32" /D "DYNAMIC_GETTEXT" /D "FEAT_OLE" /Fd.\oleDbg/ /Fo.\oleDbg/ + +RSC_PROJ=$(RSC_PROJ) /I .\oleDbg /d "_DEBUG" /d "FEAT_OLE" /fo.\oleDbg\vim.res +# ADD RSC /I .\oleDbg /d "_DEBUG" /d "FEAT_OLE" /fo.\oleDbg\vim.res + +LINK32_FLAGS=$(LINK32_FLAGS) libcd.lib oleaut32.lib /subsystem:windows /debug /profile /pdb:.\oleDbg/gvimd.pdb -debug:full -debugtype:cv,fixup /map:.\oleDbg\gvimd.map /out:.\gvimd.exe +# ADD LINK32 libcd.lib oleaut32.lib /subsystem:windows /debug /profile /pdb:.\oleDbg/gvimd.pdb -debug:full -debugtype:cv,fixup /map:.\oleDbg\gvimd.map /out:.\gvimd.exe + + +!ELSEIF "$(CFG)" == "Vim - Win32 Release gvim" + +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir .\gRel +# PROP Intermediate_Dir .\gRel + +INTDIR=.\gRel +VIM=gvim +EXTRAS="$(INTDIR)/vim.res" "$(INTDIR)/gui.obj" "$(INTDIR)/gui_w32.obj" "$(INTDIR)/gui_beval.obj" "$(INTDIR)/os_w32exe.obj" + +CPP_PROJ=$(CPP_PROJ) /Zi /O2 /D "NDEBUG" /D "FEAT_GUI_W32" /Fd.\gRel/ /Fo.\gRel/ +# ADD CPP /Zi /O2 /D "NDEBUG" /D "FEAT_GUI_W32" /Fd.\gRel/ /Fo.\gRel/ + +RSC_PROJ=$(RSC_PROJ) /d "NDEBUG" /fo.\gRel\vim.res +# ADD RSC /d "NDEBUG" /fo.\gRel\vim.res + +LINK32_FLAGS=$(LINK32_FLAGS) /pdb:.\gRel/gvim.pdb -debug:full -debugtype:cv,fixup /map:.\oleDbg\gvim.map libc.lib /subsystem:windows /out:.\gvim.exe +# ADD LINK32 /pdb:.\gRel/gvim.pdb -debug:full -debugtype:cv,fixup /map:.\oleDbg\gvim.map libc.lib /subsystem:windows /out:.\gvim.exe + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug gvim" + +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir .\gDbg +# PROP Intermediate_Dir .\gDbg + +INTDIR=.\gDbg +VIM=gvimd +EXTRAS="$(INTDIR)/vim.res" "$(INTDIR)/gui.obj" "$(INTDIR)/gui_w32.obj" "$(INTDIR)/gui_beval.obj" "$(INTDIR)/os_w32exe.obj" + +CPP_PROJ=$(CPP_PROJ) /Zi /Od /D "_DEBUG" /D "FEAT_GUI_W32" /Fd.\gDbg/ /Fo.\gDbg/ +# ADD CPP /Zi /Od /D "_DEBUG" /D "FEAT_GUI_W32" /Fd.\gDbg/ /Fo.\gDbg/ + +RSC_PROJ=$(RSC_PROJ) /d "_DEBUG" /fo.\gDbg\vim.res +# ADD RSC /d "_DEBUG" /fo.\gDbg\vim.res + +LINK32_FLAGS=$(LINK32_FLAGS) libcd.lib /subsystem:windows /debug /profile /pdb:.\gDbg/gvimd.pdb -debug:full -debugtype:cv,fixup /map:.\gDbg\gvimd.map /out:.\gvimd.exe +# ADD LINK32 libcd.lib /subsystem:windows /debug /profile /pdb:.\gDbg/gvimd.pdb -debug:full -debugtype:cv,fixup /map:.\gDbg\gvimd.map /out:.\gvimd.exe + +!ELSEIF "$(CFG)" == "Vim - Win32 Release vim" + +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir .\Rel +# PROP Intermediate_Dir .\Rel + +INTDIR=.\Rel +VIM=vim +EXTRAS= + +CPP_PROJ=$(CPP_PROJ) /Zi /O2 /D "NDEBUG" /Fd.\Rel/ /Fo.\Rel/ +# ADD CPP /Zi /O2 /D "NDEBUG" /Fd.\Rel/ /Fo.\Rel/ + +LINK32_FLAGS=$(LINK32_FLAGS) /pdb:.\Rel/vim.pdb -debug:full -debugtype:cv,fixup /map:.\oleDbg\vim.map libc.lib /subsystem:console /out:.\vim.exe +# ADD LINK32 /pdb:.\Rel/vim.pdb -debug:full -debugtype:cv,fixup /map:.\oleDbg\vim.map libc.lib /subsystem:console /out:.\vim.exe + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug vim" + +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir .\Dbg +# PROP Intermediate_Dir .\Dbg + +INTDIR=.\Dbg +VIM=vimd +EXTRAS= + +CPP_PROJ=$(CPP_PROJ) /Zi /Od /D "_DEBUG" /Fd.\Dbg/ /Fo.\Dbg/ +# ADD CPP /Zi /Od /D "_DEBUG" /Fd.\Dbg/ /Fo.\Dbg/ + +LINK32_FLAGS=$(LINK32_FLAGS) libcd.lib /subsystem:console /debug /profile /pdb:.\Dbg/vimd.pdb -debug:full -debugtype:cv,fixup /map:.\Dbg/vimd.map /out:.\vimd.exe +# ADD LINK32 libcd.lib /subsystem:console /debug /profile /pdb:.\Dbg/vimd.pdb -debug:full -debugtype:cv,fixup /map:.\Dbg/vimd.map /out:.\vimd.exe + +!ENDIF + +ALL : .\$(VIM).exe vimrun.exe install.exe uninstal.exe xxd/xxd.exe GvimExt/gvimext.dll + +LINK32_OBJS= \ + $(EXTRAS) \ + "$(INTDIR)/arabic.obj" \ + "$(INTDIR)/autocmd.obj" \ + "$(INTDIR)/blowfish.obj" \ + "$(INTDIR)/buffer.obj" \ + "$(INTDIR)/charset.obj" \ + "$(INTDIR)/crypt.obj" \ + "$(INTDIR)/crypt_zip.obj" \ + "$(INTDIR)/dict.obj" \ + "$(INTDIR)/diff.obj" \ + "$(INTDIR)/digraph.obj" \ + "$(INTDIR)/edit.obj" \ + "$(INTDIR)/eval.obj" \ + "$(INTDIR)/evalfunc.obj" \ + "$(INTDIR)/ex_cmds.obj" \ + "$(INTDIR)/ex_cmds2.obj" \ + "$(INTDIR)/ex_docmd.obj" \ + "$(INTDIR)/ex_eval.obj" \ + "$(INTDIR)/ex_getln.obj" \ + "$(INTDIR)/farsi.obj" \ + "$(INTDIR)/fileio.obj" \ + "$(INTDIR)/fold.obj" \ + "$(INTDIR)/getchar.obj" \ + "$(INTDIR)/hardcopy.obj" \ + "$(INTDIR)/hashtab.obj" \ + "$(INTDIR)/indent.obj" \ + "$(INTDIR)/json.obj" \ + "$(INTDIR)/list.obj" \ + "$(INTDIR)/main.obj" \ + "$(INTDIR)/mark.obj" \ + "$(INTDIR)/mbyte.obj" \ + "$(INTDIR)/memfile.obj" \ + "$(INTDIR)/memline.obj" \ + "$(INTDIR)/menu.obj" \ + "$(INTDIR)/message.obj" \ + "$(INTDIR)/misc1.obj" \ + "$(INTDIR)/misc2.obj" \ + "$(INTDIR)/move.obj" \ + "$(INTDIR)/normal.obj" \ + "$(INTDIR)/ops.obj" \ + "$(INTDIR)/option.obj" \ + "$(INTDIR)/os_mswin.obj" \ + "$(INTDIR)/winclip.obj" \ + "$(INTDIR)/os_win32.obj" \ + "$(INTDIR)/popupmnu.obj" \ + "$(INTDIR)/quickfix.obj" \ + "$(INTDIR)/regexp.obj" \ + "$(INTDIR)/screen.obj" \ + "$(INTDIR)/search.obj" \ + "$(INTDIR)/sha256.obj" \ + "$(INTDIR)/sign.obj" \ + "$(INTDIR)/spell.obj" \ + "$(INTDIR)/spellfile.obj" \ + "$(INTDIR)/syntax.obj" \ + "$(INTDIR)/tag.obj" \ + "$(INTDIR)/term.obj" \ + "$(INTDIR)/ui.obj" \ + "$(INTDIR)/undo.obj" \ + "$(INTDIR)/userfunc.obj" \ + "$(INTDIR)/version.obj" \ + "$(INTDIR)/window.obj" + +".\$(VIM).exe" : "$(INTDIR)" $(EXTRAS) $(LINK32_OBJS) + @if exist $(INTDIR)\$(VIM).pdb del $(INTDIR)\$(VIM).pdb + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) + +"$(INTDIR)" : + if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)" + +CLEAN : + -@if exist "$(INTDIR)/$(NULL)" $(DEL_TREE) "$(INTDIR)" + -@if exist $(VIM).exe erase $(VIM).exe + -@if exist $(VIM).ilk erase $(VIM).ilk + -@if exist $(VIM).map erase $(VIM).map + -@if exist $(VIM).pdb erase $(VIM).pdb + -@if exist DLLDATA.C erase DLLDATA.C + -@if exist Make_ivc.bak attrib -r Make_ivc.bak + -@if exist Make_ivc.bak erase Make_ivc.bak + -@if exist Make_ivc.dsp erase Make_ivc.dsp + -@if exist Make_ivc.dsw erase Make_ivc.dsw + -@if exist Make_ivc.mdp erase Make_ivc.mdp + -@if exist Make_ivc.ncb erase Make_ivc.ncb + -@if exist Make_ivc.opt erase Make_ivc.opt + -@if exist Make_ivc.plg erase Make_ivc.plg + -@if exist dosinst.obj erase dosinst.obj + -@if exist install.exe erase install.exe + -@if exist uninstal.exe erase uninstal.exe + -@if exist uninstal.obj erase uninstal.obj + -@if exist vimrun.exe erase vimrun.exe + -@if exist vimrun.obj erase vimrun.obj + + +install.exe: dosinst.c + $(CPP) /Fe$@ /nologo /W3 -DNDEBUG -DWIN32 dosinst.c kernel32.lib shell32.lib user32.lib ole32.lib advapi32.lib uuid.lib + +uninstal.exe: uninstal.c + $(CPP) /nologo /W3 -DNDEBUG -DWIN32 uninstal.c shell32.lib advapi32.lib + +vimrun.exe: vimrun.c + $(CPP) /nologo /W3 -DNDEBUG vimrun.c + +xxd/xxd.exe: xxd/xxd.c + cd xxd + $(MAKE) /NOLOGO -f Make_mvc.mak + cd .. + +GvimExt/gvimext.dll: GvimExt/gvimext.cpp GvimExt/gvimext.rc GvimExt/gvimext.h + cd GvimExt + $(MAKE) /NOLOGO -f Makefile + cd .. + +{.}.c{$(INTDIR)/}.obj: + $(CPP) $(CPP_PROJ) $< + +{.}.cpp{$(INTDIR)/}.obj: + $(CPP) $(CPP_PROJ) /I $(INTDIR) $< + +{.}.rc{$(INTDIR)/}.res: + $(RSC) $(RSC_PROJ) $< + +# Begin Target + +# Name "Vim - Win32 Release gvim OLE" +# Name "Vim - Win32 Debug gvim OLE" +# Name "Vim - Win32 Release gvim" +# Name "Vim - Win32 Debug gvim" +# Name "Vim - Win32 Release vim" +# Name "Vim - Win32 Debug vim" + +# Begin Source File + +SOURCE=.\arabic.c +# End Source File +# Begin Source File +# +SOURCE=.\autocmd.c +# End Source File +# Begin Source File + +SOURCE=.\blowfish.c +# End Source File +# Begin Source File + +SOURCE=.\buffer.c +# End Source File +# Begin Source File + +SOURCE=.\charset.c +# End Source File +# Begin Source File + +SOURCE=.\crypt.c +# End Source File +# Begin Source File + +SOURCE=.\crypt_zip.c +# End Source File +# Begin Source File + +SOURCE=.\dict.c +# End Source File +# Begin Source File + +SOURCE=.\diff.c +# End Source File +# Begin Source File + +SOURCE=.\digraph.c +# End Source File +# Begin Source File + +SOURCE=.\edit.c +# End Source File +# Begin Source File + +SOURCE=.\eval.c +# End Source File +# Begin Source File + +SOURCE=.\evalfunc.c +# End Source File +# Begin Source File + +SOURCE=.\ex_cmds.c +# End Source File +# Begin Source File + +SOURCE=.\ex_cmds2.c +# End Source File +# Begin Source File + +SOURCE=.\ex_docmd.c +# End Source File +# Begin Source File + +SOURCE=.\ex_eval.c +# End Source File +# Begin Source File + +SOURCE=.\ex_getln.c +# End Source File +# Begin Source File + +SOURCE=.\farsi.c +# End Source File +# Begin Source File + +SOURCE=.\fileio.c +# End Source File +# Begin Source File + +SOURCE=.\fold.c +# End Source File +# Begin Source File + +SOURCE=.\getchar.c +# End Source File +# Begin Source File + +SOURCE=.\hardcopy.c +# End Source File +# Begin Source File + +SOURCE=.\hashtab.c +# End Source File +# Begin Source File +# +SOURCE=.\indent.c +# End Source File +# Begin Source File + +SOURCE=.\gui.c + +!IF "$(CFG)" == "Vim - Win32 Release vim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug vim" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\gui_w32.c + +!IF "$(CFG)" == "Vim - Win32 Release vim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug vim" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\gui_beval.c + +!IF "$(CFG)" == "Vim - Win32 Release vim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug vim" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\os_w32exe.c + +!IF "$(CFG)" == "Vim - Win32 Release vim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug vim" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\if_ole.cpp + +!IF "$(CFG)" == "Vim - Win32 Release gvim OLE" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build + +"$(INTDIR)\if_ole.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\if_ole.h" + cl.exe /nologo /MT /W3 /GX /I ".\proto" /D "WIN32" /c /Zi /O2 /D "NDEBUG" /D "FEAT_GUI_W32" /D "FEAT_OLE" /Fd.\oleRel/ /Fo.\oleRel/ /I ".\oleRel" .\if_ole.cpp + @rem This is the default rule with /I "$(IntDir)" added + +# End Custom Build + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug gvim OLE" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build + +"$(INTDIR)\if_ole.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\if_ole.h" + cl.exe /nologo /MT /W3 /GX /I ".\proto" /D "WIN32" /c /Zi /Od /D "_DEBUG" /D "FEAT_GUI_W32" /D "FEAT_OLE" /Fd.\oleDbg/ /Fo.\oleDbg/ /I ".\oleDbg" .\if_ole.cpp + @rem This is the default rule with /I "$(IntDir)" added + +# End Custom Build + +!ELSEIF "$(CFG)" == "Vim - Win32 Release gvim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug gvim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Release vim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug vim" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\if_ole.idl + +!IF "$(CFG)" == "Vim - Win32 Release gvim OLE" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build + +"$(INTDIR)\if_ole.h" : $(SOURCE) "$(INTDIR)" + if exist .\if_ole.h del .\if_ole.h + midl /out .\oleRel /iid iid_ole.c /tlb vim.tlb /proxy nul /header if_ole.h .\if_ole.idl + +# End Custom Build + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug gvim OLE" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build + +"$(INTDIR)\if_ole.h" : $(SOURCE) "$(INTDIR)" + if exist .\if_ole.h del .\if_ole.h + midl /out .\oleDbg /iid iid_ole.c /tlb vim.tlb /proxy nul /header if_ole.h .\if_ole.idl + +# End Custom Build + +!ELSEIF "$(CFG)" == "Vim - Win32 Release gvim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug gvim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Release vim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug vim" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\json.c +# End Source File +# Begin Source File + +SOURCE=.\list.c +# End Source File +# Begin Source File + +SOURCE=.\main.c +# End Source File +# Begin Source File + +SOURCE=.\mark.c +# End Source File +# Begin Source File + +SOURCE=.\mbyte.c +# End Source File +# Begin Source File + +SOURCE=.\memfile.c +# End Source File +# Begin Source File + +SOURCE=.\memline.c +# End Source File +# Begin Source File + +SOURCE=.\menu.c +# End Source File +# Begin Source File + +SOURCE=.\message.c +# End Source File +# Begin Source File + +SOURCE=.\misc1.c +# End Source File +# Begin Source File + +SOURCE=.\misc2.c +# End Source File +# Begin Source File + +SOURCE=.\move.c +# End Source File +# Begin Source File + +SOURCE=.\normal.c +# End Source File +# Begin Source File + +SOURCE=.\ops.c +# End Source File +# Begin Source File + +SOURCE=.\option.c +# End Source File +# Begin Source File + +SOURCE=.\os_mswin.c +# End Source File +# Begin Source File + +SOURCE=.\winclip.c +# End Source File +# Begin Source File + +SOURCE=.\os_win32.c +# End Source File +# Begin Source File + +SOURCE=.\popupmnu.c +# End Source File +# Begin Source File + +SOURCE=.\quickfix.c +# End Source File +# Begin Source File + +SOURCE=.\regexp.c +# End Source File +# Begin Source File + +SOURCE=.\screen.c +# End Source File +# Begin Source File + +SOURCE=.\search.c +# End Source File +# Begin Source File + +SOURCE=.\sha256.c +# End Source File +# Begin Source File + +SOURCE=.\sign.c +# End Source File +# Begin Source File + +SOURCE=.\spell.c +# End Source File +# Begin Source File + +SOURCE=.\spellfile.c +# End Source File +# Begin Source File + +SOURCE=.\syntax.c +# End Source File +# Begin Source File + +SOURCE=.\tag.c +# End Source File +# Begin Source File + +SOURCE=.\term.c +# End Source File +# Begin Source File + +SOURCE=.\ui.c +# End Source File +# Begin Source File + +SOURCE=.\undo.c +# End Source File +# Begin Source File + +SOURCE=.\userfunc.c +# End Source File +# Begin Source File + +SOURCE=.\version.c +# End Source File +# Begin Source File + +SOURCE=.\vim.rc + +!IF "$(CFG)" == "Vim - Win32 Release gvim OLE" + +"$(INTDIR)\vim.res" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\if_ole.h" + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug gvim OLE" + +"$(INTDIR)\vim.res" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\if_ole.h" + +!ELSEIF "$(CFG)" == "Vim - Win32 Release gvim" + +"$(INTDIR)\vim.res" : $(SOURCE) "$(INTDIR)" + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug gvim" + +"$(INTDIR)\vim.res" : $(SOURCE) "$(INTDIR)" + +!ELSEIF "$(CFG)" == "Vim - Win32 Release vim" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "Vim - Win32 Debug vim" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\window.c +# End Source File +# End Target +# End Project diff --git a/src/Make_manx.mak b/src/Make_manx.mak new file mode 100644 index 0000000..a44ad65 --- /dev/null +++ b/src/Make_manx.mak @@ -0,0 +1,438 @@ +# +# Makefile for VIM on the Amiga, using Aztec/Manx C 5.0 or later +# +# NOTE: THIS IS OLD AND PROBABLY NO LONGER WORKS. +# +# Note: Not all dependencies are included. This was done to avoid having +# to compile everything when a global variable or function is added. +# Careful when changing a global struct or variable! +# + +#>>>>> choose options: + +### See feature.h for a list of optionals. +### Any other defines can be included here. +DEFINES = + +#>>>>> if HAVE_TGETENT is defined obj/termlib.o has to be used +#TERMLIB = obj/termlib.o +TERMLIB = + +#>>>>> choose between debugging (-bs) or optimizing (-so) +OPTIONS = -so +#OPTIONS = -bs + +#>>>>> end of choices +########################################################################### + +CFLAGS = $(OPTIONS) -wapruq -ps -qf -Iproto $(DEFINES) -DAMIGA + +LIBS = -lc16 +SYMS = vim.syms +CC = cc +LN = ln +LNFLAGS = +q +SHELL = csh +REN = $(SHELL) -c mv -f +DEL = $(SHELL) -c rm -f + +SRC = arabic.c \ + autocmd.c \ + blowfish.c \ + buffer.c \ + charset.c \ + crypt.c \ + crypt_zip.c \ + dict.c \ + diff.c \ + digraph.c \ + edit.c \ + eval.c \ + evalfunc.c \ + ex_cmds.c \ + ex_cmds2.c \ + ex_docmd.c \ + ex_eval.c \ + ex_getln.c \ + farsi.c \ + fileio.c \ + fold.c \ + getchar.c \ + hardcopy.c \ + hashtab.c \ + indent.c \ + json.c \ + list.c \ + main.c \ + mark.c \ + memfile.c \ + memline.c \ + menu.c \ + message.c \ + misc1.c \ + misc2.c \ + move.c \ + mbyte.c \ + normal.c \ + ops.c \ + option.c \ + os_amiga.c \ + popupmnu.c \ + quickfix.c \ + regexp.c \ + screen.c \ + search.c \ + sha256.c \ + sign.c \ + spell.c \ + spellfile.c \ + syntax.c \ + tag.c \ + term.c \ + ui.c \ + undo.c \ + userfunc.c \ + window.c \ + version.c + +INCL = vim.h feature.h keymap.h macros.h ascii.h term.h structs.h os_amiga.h + +OBJ = obj/arabic.o \ + obj/autocmd.o \ + obj/blowfish.o \ + obj/buffer.o \ + obj/charset.o \ + obj/crypt.o \ + obj/crypt_zip.o \ + obj/dict.o \ + obj/diff.o \ + obj/digraph.o \ + obj/edit.o \ + obj/eval.o \ + obj/evalfunc.o \ + obj/ex_cmds.o \ + obj/ex_cmds2.o \ + obj/ex_docmd.o \ + obj/ex_eval.o \ + obj/ex_getln.o \ + obj/farsi.o \ + obj/fileio.o \ + obj/fold.o \ + obj/getchar.o \ + obj/hardcopy.o \ + obj/hashtab.o \ + obj/indent.o \ + obj/json.o \ + obj/list.o \ + obj/main.o \ + obj/mark.o \ + obj/memfile.o \ + obj/memline.o \ + obj/menu.o \ + obj/message.o \ + obj/misc1.o \ + obj/misc2.o \ + obj/move.o \ + obj/mbyte.o \ + obj/normal.o \ + obj/ops.o \ + obj/option.o \ + obj/os_amiga.o \ + obj/popupmnu.o \ + obj/quickfix.o \ + obj/regexp.o \ + obj/screen.o \ + obj/search.o \ + obj/sha256.o \ + obj/sign.o \ + obj/spell.o \ + obj/spellfile.o \ + obj/syntax.o \ + obj/tag.o \ + obj/term.o \ + obj/ui.o \ + obj/undo.o \ + obj/userfunc.o \ + obj/window.o \ + $(TERMLIB) + +PRO = proto/arabic.pro \ + proto/autocmd.pro \ + proto/blowfish.pro \ + proto/buffer.pro \ + proto/charset.pro \ + proto/crypt.pro \ + proto/crypt_zip.pro \ + proto/dict.pro \ + proto/diff.pro \ + proto/digraph.pro \ + proto/edit.pro \ + proto/eval.pro \ + proto/evalfunc.pro \ + proto/ex_cmds.pro \ + proto/ex_cmds2.pro \ + proto/ex_docmd.pro \ + proto/ex_eval.pro \ + proto/ex_getln.pro \ + proto/farsi.pro \ + proto/fileio.pro \ + proto/fold.pro \ + proto/getchar.pro \ + proto/hardcopy.pro \ + proto/hashtab.pro \ + proto/indent.pro \ + proto/json.pro \ + proto/list.pro \ + proto/main.pro \ + proto/mark.pro \ + proto/memfile.pro \ + proto/memline.pro \ + proto/menu.pro \ + proto/message.pro \ + proto/misc1.pro \ + proto/misc2.pro \ + proto/move.pro \ + proto/mbyte.pro \ + proto/normal.pro \ + proto/ops.pro \ + proto/option.pro \ + proto/os_amiga.pro \ + proto/popupmnu.pro \ + proto/quickfix.pro \ + proto/regexp.pro \ + proto/screen.pro \ + proto/search.pro \ + proto/sha256.pro \ + proto/sign.pro \ + proto/spell.pro \ + proto/spellfile.pro \ + proto/syntax.pro \ + proto/tag.pro \ + proto/term.pro \ + proto/termlib.pro \ + proto/ui.pro \ + proto/undo.pro \ + proto/userfunc.pro \ + proto/window.pro + +all: Vim xxd/Xxd + +Vim: obj $(OBJ) version.c version.h + $(CC) $(CFLAGS) version.c -o obj/version.o + $(LN) $(LNFLAGS) -m -o Vim $(OBJ) obj/version.o $(LIBS) + +debug: obj $(OBJ) version.c version.h + $(CC) $(CFLAGS) version.c -o obj/version.o + $(LN) $(LNFLAGS) -m -g -o Vim $(OBJ) obj/version.o $(LIBS) + +xxd/Xxd: xxd/xxd.c + $(SHELL) -c cd xxd; make -f Make_amiga.mak; cd .. + +# Making prototypes with Manx has been removed, because it caused too many +# problems. +#proto: $(SYMS) $(PRO) + +obj: + makedir obj + +tags: $(SRC) $(INCL) + $(SHELL) -c ctags $(SRC) *.h + +# can't use delete here, too many file names +clean: + $(DEL) $(OBJ) obj/version.o \ + obj/termlib.o Vim $(SYMS) xxd/Xxd + +test: + $(SHELL) -c cd testdir; make -f Make_amiga.mak; cd .. + +$(SYMS): $(INCL) $(PRO) + $(CC) $(CFLAGS) -ho$(SYMS) vim.h + +########################################################################### + +# Unfortunately, Manx's make doesn't understand a .c.o rule, so each +# compilation command has to be given explicitly. + +CCSYM = $(CC) $(CFLAGS) -hi$(SYMS) -o +CCNOSYM = $(CC) $(CFLAGS) -o + +$(OBJ): $(SYMS) + +obj/arabic.o: arabic.c + $(CCSYM) $@ arabic.c + +obj/autocmd.o: autocmd.c + $(CCSYM) $@ autocmd.c + +obj/blowfish.o: blowfish.c + $(CCSYM) $@ blowfish.c + +obj/buffer.o: buffer.c + $(CCSYM) $@ buffer.c + +obj/charset.o: charset.c + $(CCSYM) $@ charset.c + +obj/crypt.o: crypt.c + $(CCSYM) $@ crypt.c + +obj/crypt_zip.o: crypt_zip.c + $(CCSYM) $@ crypt_zip.c + +obj/dict.o: dict.c + $(CCSYM) $@ dict.c + +obj/diff.o: diff.c + $(CCSYM) $@ diff.c + +obj/digraph.o: digraph.c + $(CCSYM) $@ digraph.c + +obj/edit.o: edit.c + $(CCSYM) $@ edit.c + +obj/eval.o: eval.c + $(CCSYM) $@ eval.c + +obj/evalfunc.o: evalfunc.c + $(CCSYM) $@ evalfunc.c + +obj/ex_cmds.o: ex_cmds.c + $(CCSYM) $@ ex_cmds.c + +obj/ex_cmds2.o: ex_cmds2.c + $(CCSYM) $@ ex_cmds2.c + +# Don't use $(SYMS) here, because ex_docmd.c defines DO_DECLARE_EXCMD +obj/ex_docmd.o: ex_docmd.c ex_cmds.h + $(CCNOSYM) $@ ex_docmd.c + +obj/ex_eval.o: ex_eval.c ex_cmds.h + $(CCSYM) $@ ex_eval.c + +obj/ex_getln.o: ex_getln.c + $(CCSYM) $@ ex_getln.c + +obj/farsi.o: farsi.c + $(CCSYM) $@ farsi.c + +obj/fileio.o: fileio.c + $(CCSYM) $@ fileio.c + +obj/fold.o: fold.c + $(CCSYM) $@ fold.c + +obj/getchar.o: getchar.c + $(CCSYM) $@ getchar.c + +obj/hardcopy.o: hardcopy.c + $(CCSYM) $@ hardcopy.c + +obj/hashtab.o: hashtab.c + $(CCSYM) $@ hashtab.c + +obj/indent.o: indent.c + $(CCSYM) $@ indent.c + +obj/json.o: json.c + $(CCSYM) $@ json.c + +obj/list.o: list.c + $(CCSYM) $@ list.c + +# Don't use $(SYMS) here, because main.c defines EXTERN +obj/main.o: main.c option.h globals.h + $(CCNOSYM) $@ main.c + +obj/mark.o: mark.c + $(CCSYM) $@ mark.c + +obj/memfile.o: memfile.c + $(CCSYM) $@ memfile.c + +obj/memline.o: memline.c + $(CCSYM) $@ memline.c + +obj/menu.o: menu.c + $(CCSYM) $@ menu.c + +# Don't use $(SYMS) here, because message.c defines MESSAGE_FILE +obj/message.o: message.c + $(CCNOSYM) $@ message.c + +obj/misc1.o: misc1.c + $(CCSYM) $@ misc1.c + +obj/misc2.o: misc2.c + $(CCSYM) $@ misc2.c + +obj/move.o: move.c + $(CCSYM) $@ move.c + +obj/mbyte.o: mbyte.c + $(CCSYM) $@ mbyte.c + +obj/normal.o: normal.c + $(CCSYM) $@ normal.c + +obj/ops.o: ops.c + $(CCSYM) $@ ops.c + +# Don't use $(SYMS) here, because option.h defines variables here +obj/option.o: option.c + $(CCNOSYM) $@ option.c + +obj/os_amiga.o: os_amiga.c + $(CCSYM) $@ os_amiga.c + +obj/popupmnu.o: popupmnu.c + $(CCSYM) $@ popupmnu.c + +obj/quickfix.o: quickfix.c + $(CCSYM) $@ quickfix.c + +obj/regexp.o: regexp.c + $(CCSYM) $@ regexp.c + +obj/screen.o: screen.c + $(CCSYM) $@ screen.c + +obj/search.o: search.c + $(CCSYM) $@ search.c + +obj/sha256.o: sha256.c + $(CCSYM) $@ sha256.c + +obj/sign.o: sign.c + $(CCSYM) $@ sign.c + +obj/spell.o: spell.c + $(CCSYM) $@ spell.c + +obj/spellfile.o: spellfile.c + $(CCSYM) $@ spellfile.c + +obj/syntax.o: syntax.c + $(CCSYM) $@ syntax.c + +obj/tag.o: tag.c + $(CCSYM) $@ tag.c + +obj/term.o: term.c term.h + $(CCSYM) $@ term.c + +obj/termlib.o: termlib.c + $(CCSYM) $@ termlib.c + +obj/ui.o: ui.c + $(CCSYM) $@ ui.c + +obj/undo.o: undo.c + $(CCSYM) $@ undo.c + +obj/userfunc.o: userfunc.c + $(CCSYM) $@ userfunc.c + +obj/window.o: window.c + $(CCSYM) $@ window.c diff --git a/src/Make_ming.mak b/src/Make_ming.mak new file mode 100644 index 0000000..e9e20f6 --- /dev/null +++ b/src/Make_ming.mak @@ -0,0 +1,51 @@ +# +# Makefile for VIM on Win32, using MinGW +# +# Also read INSTALLpc.txt! +# +# The old Make_ming.mak (maintained by Ron Aaron et al.) was merged into +# Make_cyg_ming.mak. +# This file contains MinGW specific settings. Common settings are contained +# in Make_cyg_ming.mak. +# +# Last updated by Ken Takata. +# Last Change: 2014 Oct 21 + + +# uncomment 'PERL' if you want a perl-enabled version +#PERL=c:/perl + +# uncomment 'LUA' if you want a Lua-enabled version +#LUA=c:/lua + +# uncomment 'MZSCHEME' if you want a MzScheme-enabled version +#MZSCHEME=d:/plt + +# uncomment 'PYTHON' if you want a python-enabled version +# Put the path to the python distro here. If cross compiling from Linux, you +# will also need to convert the header files to unix instead of dos format: +# for fil in *.h ; do vim -e -c 'set ff=unix|w|q' $fil +# and also, you will need to make a mingw32 'libpython20.a' to link with: +# cd $PYTHON/libs +# pexports python20.dll > python20.def +# dlltool -d python20.def -l libpython20.a +# on my Linux box, I put the Python stuff here: +#PYTHON=/home/ron/ActivePython-2.0.0-202/src/Core +# on my NT box, it's here: +#PYTHON=c:/python20 + +# uncomment 'PYTHON3' if you want a python3-enabled version +#PYTHON3=c:/python31 + +# uncomment 'TCL' if you want a Tcl-enabled version +#TCL=c:/tcl + +# uncomment 'RUBY' if you want a Ruby-enabled version +#RUBY=c:/ruby + + +# Do not change this. +UNDER_CYGWIN = no +include Make_cyg_ming.mak + +# vim: set noet sw=8 ts=8 sts=0 wm=0 tw=0: diff --git a/src/Make_mint.mak b/src/Make_mint.mak new file mode 100644 index 0000000..97a2e4b --- /dev/null +++ b/src/Make_mint.mak @@ -0,0 +1,56 @@ +# +# Makefile for Vim on MiNT vim:ts=8:sw=8:tw=78 +# +# This is a wrapper around the Unix Makefile. It is configured to accompany +# the MiNT distribution of Vim. +# +# See "Makefile" for instructions how to run "make". +# +# BUT: Always run: "make -f Make_mint.mak config", +# and then: "make -f Make_mint.mak"! +# Otherwise the postprocessing won't get done. +# + +### This Makefile has been successfully tested on these systems. +### Check the (*) column for remarks, listed below. +### Later code changes may cause small problems, otherwise Vim is supposed to +### compile and run without problems. + +#system: configurations: version (*) tested by: +#------------- ------------------------ ------- - ---------- +#MiNT 1.12.5 gcc gcc-2.6.1 3.29 Jens Felderhoff +#MiNT 1.12.6 gcc gcc-2.6.1 -GUI 4.6b Jens Felderhoff +#MiNT 1.12.6 gcc gcc-2.6.1 -GUI 4.6 Jens Felderhoff + +# set this to the pathname prefix of your symbol link editor, i.e. if it is +# /usr/local/bin/sym-ld set: +# +SYMLDPREFIX = /usr/local/bin/sym- +#SYMLDPREFIX = /gnu/bin/sym- + +POSTPROCESS = fixstk 20k $(VIMTARGET) +DBGPOSTPROCESS = fixstk 20k $(DBGTARGET) +DBGLDFLAGS = -B$(SYMLDPREFIX) +DBGTARGET = $(VIMTARGET).sym + + +# Default target is making the executable and then do the post processing +all: $(VIMTARGET) $(TOOLS) + $(POSTPROCESS) + +debug: $(DBGTARGET) + $(DBGPOSTPROCESS) + +#################### include the Unix Makefile ############### + +include Makefile + + +### (M) MiNT with gcc 2.6.1 and gdb 3.5 +CC = gcc -mint +CFLAGS = -g -O -Iproto + +$(DBGTARGET): $(OBJ) version.c version.h + $(CC) -c $(ALL_CFLAGS) version.c + $(CC) $(LDFLAGS) $(DBGLDFLAGS) -o $(DBGTARGET) -g $(OBJ) \ + version.o $(ALL_LIBS) diff --git a/src/Make_morph.mak b/src/Make_morph.mak new file mode 100644 index 0000000..6bcae1a --- /dev/null +++ b/src/Make_morph.mak @@ -0,0 +1,95 @@ +# +# Makefile for VIM, using MorphOS SDK (gcc 2.95.3) +# + +CFLAGS = -c \ + -pipe \ + -O2 \ + -Wall \ + \ + -DNO_ARP \ + -DUSE_TMPNAM \ + \ + -I proto \ + \ + -noixemul + +PRG = Vim +LIBS = -noixemul -s +CC = gcc +LD = gcc +OBJDUMP = objdump +RM = rm + +.c.o: + ${CC} ${CFLAGS} $< -o $@ + +SRC = arabic.c \ + autocmd.c \ + blowfish.c \ + buffer.c \ + charset.c \ + crypt.c \ + crypt_zip.c \ + dict.c \ + diff.c \ + digraph.c \ + edit.c \ + eval.c \ + evalfunc.c \ + ex_cmds.c \ + ex_cmds2.c \ + ex_docmd.c \ + ex_eval.c \ + ex_getln.c \ + farsi.c \ + fileio.c \ + fold.c \ + getchar.c \ + hardcopy.c \ + hashtab.c \ + indent.c \ + json.c \ + list.c \ + main.c \ + mark.c \ + mbyte.c \ + memfile.c \ + memline.c \ + menu.c \ + message.c \ + misc1.c \ + misc2.c \ + move.c \ + normal.c \ + ops.c \ + option.c \ + os_amiga.c \ + popupmnu.c \ + quickfix.c \ + regexp.c \ + screen.c \ + search.c \ + sha256.c \ + sign.c \ + spell.c \ + spellfile.c \ + syntax.c \ + tag.c \ + term.c \ + ui.c \ + undo.c \ + userfunc.c \ + version.c \ + window.c \ + +OBJ = $(SRC:.c=.o) + +$(PRG): $(OBJ) + ${LD} -o $(PRG) $(OBJ) $(LIBS) + +dump: $(PRG) + $(OBJDUMP) --reloc --disassemble-all $(PRG) > $(PRG).s + +clean: + $(RM) -fv $(OBJ) $(PRG) $(PRG).s diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak new file mode 100644 index 0000000..52deb54 --- /dev/null +++ b/src/Make_mvc.mak @@ -0,0 +1,1701 @@ +# Makefile for Vim on Win32 (Windows XP/2003/Vista/7/8/10) and Win64, +# using the Microsoft Visual C++ compilers. Known to work with VC5, VC6 (VS98), +# VC7.0 (VS2002), VC7.1 (VS2003), VC8 (VS2005), VC9 (VS2008), VC10 (VS2010), +# VC11 (VS2012), VC12 (VS2013), VC14 (VS2015) and VC15 (VS2017) +# +# To build using other Windows compilers, see INSTALLpc.txt +# +# This makefile can build the console, GUI, OLE-enable, Perl-enabled and +# Python-enabled versions of Vim for Win32 platforms. +# +# The basic command line to build Vim is: +# +# nmake -f Make_mvc.mak +# +# This will build the console version of Vim with no additional interfaces. +# To add features, define any of the following: +# +# For MSVC 11, if you want to include Win32.mak, you need to specify +# where the file is, e.g.: +# SDK_INCLUDE_DIR="C:\Program Files\Microsoft SDKs\Windows\v7.1\Include" +# +# !!!! After changing features do "nmake clean" first !!!! +# +# Feature Set: FEATURES=[TINY, SMALL, NORMAL, BIG, HUGE] (default is HUGE) +# +# GUI interface: GUI=yes (default is no) +# +# GUI with DirectWrite (DirectX): DIRECTX=yes +# (default is yes if GUI=yes, requires GUI=yes) +# +# Color emoji support: COLOR_EMOJI=yes +# (default is yes if DIRECTX=yes, requires WinSDK 8.1 or later.) +# +# OLE interface: OLE=yes (usually with GUI=yes) +# +# IME support: IME=yes (requires GUI=yes) +# DYNAMIC_IME=[yes or no] (to load the imm32.dll dynamically, default +# is yes) +# Global IME support: GIME=yes (requires GUI=yes) +# +# Terminal support: TERMINAL=yes (default is yes) +# +# Lua interface: +# LUA=[Path to Lua directory] +# DYNAMIC_LUA=yes (to load the Lua DLL dynamically) +# LUA_VER=[Lua version] (default is 53) +# +# MzScheme interface: +# MZSCHEME=[Path to MzScheme directory] +# DYNAMIC_MZSCHEME=yes (to load the MzScheme DLLs dynamically) +# MZSCHEME_VER=[MzScheme version] (default is 3m_a0solc (6.6)) +# Used for the DLL file name. E.g.: +# C:\Program Files (x86)\Racket\lib\libracket3m_XXXXXX.dll +# MZSCHEME_DEBUG=no +# +# Perl interface: +# PERL=[Path to Perl directory] +# DYNAMIC_PERL=yes (to load the Perl DLL dynamically) +# PERL_VER=[Perl version, in the form 55 (5.005), 56 (5.6.x), +# 510 (5.10.x), etc] +# (default is 524) +# +# Python interface: +# PYTHON=[Path to Python directory] +# DYNAMIC_PYTHON=yes (to load the Python DLL dynamically) +# PYTHON_VER=[Python version, eg 22, 23, ..., 27] (default is 27) +# +# Python3 interface: +# PYTHON3=[Path to Python3 directory] +# DYNAMIC_PYTHON3=yes (to load the Python3 DLL dynamically) +# PYTHON3_VER=[Python3 version, eg 30, 31] (default is 36) +# +# Ruby interface: +# RUBY=[Path to Ruby directory] +# DYNAMIC_RUBY=yes (to load the Ruby DLL dynamically) +# RUBY_VER=[Ruby version, eg 19, 22] (default is 22) +# RUBY_API_VER_LONG=[Ruby API version, eg 1.8, 1.9.1, 2.2.0] +# (default is 2.2.0) +# You must set RUBY_API_VER_LONG when change RUBY_VER. +# Note: If you use Ruby 1.9.3, set as follows: +# RUBY_VER=19 +# RUBY_API_VER_LONG=1.9.1 (not 1.9.3, because the API version is 1.9.1.) +# +# Tcl interface: +# TCL=[Path to Tcl directory] +# DYNAMIC_TCL=yes (to load the Tcl DLL dynamically) +# TCL_VER=[Tcl version, e.g. 80, 83] (default is 86) +# TCL_VER_LONG=[Tcl version, eg 8.3] (default is 8.6) +# You must set TCL_VER_LONG when you set TCL_VER. +# TCL_DLL=[Tcl dll name, e.g. tcl86.dll] (default is tcl86.dll) +# +# Cscope support: CSCOPE=yes +# +# Iconv library support (always dynamically loaded): +# ICONV=[yes or no] (default is yes) +# +# Intl library support (always dynamically loaded): +# GETTEXT=[yes or no] (default is yes) +# See http://sourceforge.net/projects/gettext/ +# +# PostScript printing: POSTSCRIPT=yes (default is no) +# +# Netbeans Support: NETBEANS=[yes or no] (default is yes if GUI is yes) +# Requires CHANNEL. +# +# Netbeans Debugging Support: NBDEBUG=[yes or no] (should be no, yes +# doesn't work) +# +# Inter process communication: CHANNEL=[yes or no] (default is yes if GUI +# is yes) +# +# XPM Image Support: XPM=[path to XPM directory] +# Default is "xpm", using the files included in the distribution. +# Use "no" to disable this feature. +# +# Optimization: OPTIMIZE=[SPACE, SPEED, MAXSPEED] (default is MAXSPEED) +# +# Processor Version: CPUNR=[any, i586, i686, sse, sse2, avx, avx2] (default is +# any) +# avx is available on Visual C++ 2010 and after. +# avx2 is available on Visual C++ 2013 Update 2 and after. +# +# Version Support: WINVER=[0x0501, 0x0502, 0x0600, 0x0601, 0x0602, +# 0x0603, 0x0A00] (default is 0x0501) +# Supported versions depends on your target SDK, check SDKDDKVer.h +# See https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt +# +# Debug version: DEBUG=yes +# Mapfile: MAP=[no, yes or lines] (default is yes) +# no: Don't write a mapfile. +# yes: Write a normal mapfile. +# lines: Write a mapfile with line numbers (only for VC6 and later) +# +# Static Code Analysis: ANALYZE=yes (works with VS2012 or later) +# +# You can combine any of these interfaces +# +# Example: To build the non-debug, GUI version with Perl interface: +# nmake -f Make_mvc.mak GUI=yes PERL=C:\Perl +# +# DEBUG with Make_mvc.mak and Make_dvc.mak: +# This makefile gives a fineness of control which is not supported in +# Visual C++ configuration files. Therefore, debugging requires a bit of +# extra work. +# Make_dvc.mak is a Visual C++ project to access that support. It may be +# badly out of date for the Visual C++ you are using... +# To use Make_dvc.mak: +# 1) Build Vim with Make_mvc.mak. +# Use a "DEBUG=yes" argument to build Vim with debug support. +# E.g. the following builds gvimd.exe: +# nmake -f Make_mvc.mak debug=yes gui=yes +# 2) Use MS Devstudio and set it up to allow that file to be debugged: +# i) Pass Make_dvc.mak to the IDE. +# Use the "open workspace" menu entry to load Make_dvc.mak. +# Alternatively, from the command line: +# msdev /nologo Make_dvc.mak +# Note: Make_dvc.mak is in VC4.0 format. Later VC versions see +# this and offer to convert it to their own format. Accept that. +# It creates a file called Make_dvc.dsw which can then be used +# for further operations. E.g. +# msdev /nologo Make_dvc.dsw +# ii) Set the built executable for debugging: +# a) Alt+F7/Debug takes you to the Debug dialog. +# b) Fill "Executable for debug session". e.g. gvimd.exe +# c) Fill "Program arguments". e.g. -R dosinst.c +# d) Complete the dialog +# 3) You can now debug the executable you built with Make_mvc.mak +# +# Note: Make_dvc.mak builds vimrun.exe, because it must build something +# to be a valid makefile.. + +### See feature.h for a list of optionals. +# If you want to build some optional features without modifying the source, +# you can set DEFINES on the command line, e.g., +# nmake -f Make_mvc.mvc "DEFINES=-DEMACS_TAGS" + +# Build on Windows NT/XP + +TARGETOS = WINNT + +!ifndef DIRECTX +DIRECTX = $(GUI) +!endif + +# Select one of eight object code directories, depends on GUI, OLE, DEBUG and +# interfaces. +# If you change something else, do "make clean" first! +!if "$(GUI)" == "yes" +OBJDIR = .\ObjG +!else +OBJDIR = .\ObjC +!endif +!if "$(DIRECTX)" == "yes" +OBJDIR = $(OBJDIR)X +!endif +!if "$(OLE)" == "yes" +OBJDIR = $(OBJDIR)O +!endif +!ifdef LUA +OBJDIR = $(OBJDIR)U +!endif +!ifdef PERL +OBJDIR = $(OBJDIR)L +!endif +!ifdef PYTHON +OBJDIR = $(OBJDIR)Y +!endif +!ifdef PYTHON3 +OBJDIR = $(OBJDIR)H +!endif +!ifdef TCL +OBJDIR = $(OBJDIR)T +!endif +!ifdef RUBY +OBJDIR = $(OBJDIR)R +!endif +!ifdef MZSCHEME +OBJDIR = $(OBJDIR)Z +!endif +!if "$(DEBUG)" == "yes" +OBJDIR = $(OBJDIR)d +!endif + +# If you include Win32.mak, it requires that CPU be set appropriately. +# To cross-compile for Win64, set CPU=AMD64 or CPU=IA64. + +!ifdef PROCESSOR_ARCHITECTURE +# We're on Windows NT or using VC 6+ +! ifdef CPU +ASSEMBLY_ARCHITECTURE=$(CPU) +# Using I386 for $ASSEMBLY_ARCHITECTURE doesn't work for VC7. +! if "$(CPU)" == "I386" +CPU = i386 +! endif +! else # !CPU +CPU = i386 +! if !defined(PLATFORM) && defined(TARGET_CPU) +PLATFORM = $(TARGET_CPU) +! endif +! ifdef PLATFORM +! if ("$(PLATFORM)" == "x64") || ("$(PLATFORM)" == "X64") +CPU = AMD64 +! elseif ("$(PLATFORM)" != "x86") && ("$(PLATFORM)" != "X86") +! error *** ERROR Unknown target platform "$(PLATFORM)". Make aborted. +! endif +! endif # !PLATFORM +! endif +!else # !PROCESSOR_ARCHITECTURE +# We're on Windows 95 +CPU = i386 +!endif # !PROCESSOR_ARCHITECTURE +ASSEMBLY_ARCHITECTURE=$(CPU) +OBJDIR = $(OBJDIR)$(CPU) + +# Build a retail version by default + +!if "$(DEBUG)" != "yes" +NODEBUG = 1 +!else +!undef NODEBUG +MAKEFLAGS_GVIMEXT = DEBUG=yes +!endif + + +# Get all sorts of useful, standard macros from the Platform SDK, +# if SDK_INCLUDE_DIR is set or USE_WIN32MAK is set to "yes". + +!ifdef SDK_INCLUDE_DIR +!include $(SDK_INCLUDE_DIR)\Win32.mak +!elseif "$(USE_WIN32MAK)"=="yes" +!include +!else +link = link +!endif + + +# Check VC version. +!if [echo MSVCVER=_MSC_VER> msvcver.c && $(CC) /EP msvcver.c > msvcver.~ 2> nul] +!message *** ERROR +!message Cannot run Visual C to determine its version. Make sure cl.exe is in your PATH. +!message This can usually be done by running "vcvarsall.bat", located in the bin directory where Visual Studio was installed. +!error Make aborted. +!else +!include msvcver.~ +!if [del msvcver.c msvcver.~] +!endif +!endif + +!if $(MSVCVER) < 1900 +MSVC_MAJOR = ($(MSVCVER) / 100 - 6) +MSVCRT_VER = ($(MSVCVER) / 10 - 60) +# Visual C++ 2017 needs special handling +# it has an _MSC_VER of 1910->14.1, but is actually v15 with runtime v140 +# TODO: what's the maximum value? +!elseif $(MSVCVER) >= 1910 +MSVC_MAJOR = 15 +MSVCRT_VER = 140 +!else +MSVC_MAJOR = ($(MSVCVER) / 100 - 5) +MSVCRT_VER = ($(MSVCVER) / 10 - 50) +!endif + +# Calculate MSVC_FULL for Visual C++ 8 and up. +!if $(MSVC_MAJOR) >= 8 +! if [echo MSVC_FULL=_MSC_FULL_VER> msvcfullver.c && $(CC) /EP msvcfullver.c > msvcfullver.~ 2> nul] +! message *** ERROR +! message Cannot run Visual C to determine its version. Make sure cl.exe is in your PATH. +! message This can usually be done by running "vcvarsall.bat", located in the bin directory where Visual Studio was installed. +! error Make aborted. +! else +! include msvcfullver.~ +! if [del msvcfullver.c msvcfullver.~] +! endif +! endif +!endif + + +# Calculate MSVCRT_VER +!if [(set /a MSVCRT_VER="$(MSVCRT_VER)" > nul) && set MSVCRT_VER > msvcrtver.~] == 0 +!include msvcrtver.~ +!if [del msvcrtver.~] +!endif +!endif + +# Base name of the msvcrXX.dll +!if $(MSVCRT_VER) <= 60 +MSVCRT_NAME = msvcrt +!elseif $(MSVCRT_VER) <= 130 +MSVCRT_NAME = msvcr$(MSVCRT_VER) +!else +MSVCRT_NAME = vcruntime$(MSVCRT_VER) +!endif + +!if $(MSVC_MAJOR) == 6 +CPU = ix86 +!endif + + +# Flag to turn on Win64 compatibility warnings for VC7.x and VC8. +WP64CHECK = /Wp64 + +# Use multiprocess build +USE_MP = yes + +#>>>>> path of the compiler and linker; name of include and lib directories +# PATH = c:\msvc20\bin;$(PATH) +# INCLUDE = c:\msvc20\include +# LIB = c:\msvc20\lib + +!if "$(FEATURES)"=="" +FEATURES = HUGE +!endif + +!ifndef CTAGS +# this assumes ctags is Exuberant ctags +CTAGS = ctags -I INIT+ --fields=+S +!endif + +!ifndef CSCOPE +CSCOPE = yes +!endif + +!if "$(CSCOPE)" == "yes" +# CSCOPE - Include support for Cscope +CSCOPE_INCL = if_cscope.h +CSCOPE_OBJ = $(OBJDIR)/if_cscope.obj +CSCOPE_DEFS = -DFEAT_CSCOPE +!endif + +!ifndef TERMINAL +!if "$(FEATURES)"=="HUGE" +TERMINAL = yes +!else +TERMINAL = no +!endif +!endif + +!if "$(TERMINAL)" == "yes" +TERM_OBJ = \ + $(OBJDIR)/terminal.obj \ + $(OBJDIR)/encoding.obj \ + $(OBJDIR)/keyboard.obj \ + $(OBJDIR)/mouse.obj \ + $(OBJDIR)/parser.obj \ + $(OBJDIR)/pen.obj \ + $(OBJDIR)/termscreen.obj \ + $(OBJDIR)/state.obj \ + $(OBJDIR)/unicode.obj \ + $(OBJDIR)/vterm.obj +TERM_DEFS = -DFEAT_TERMINAL +TERM_DEPS = \ + libvterm/include/vterm.h \ + libvterm/include/vterm_keycodes.h \ + libvterm/src/rect.h \ + libvterm/src/utf8.h \ + libvterm/src/vterm_internal.h +!endif + +!ifndef NETBEANS +NETBEANS = $(GUI) +!endif + +!ifndef CHANNEL +!if "$(FEATURES)"=="HUGE" +CHANNEL = yes +!else +CHANNEL = $(GUI) +!endif +!endif + +# GUI sepcific features. +!if "$(GUI)" == "yes" +# Only allow NETBEANS for a GUI build and CHANNEL. +!if "$(NETBEANS)" == "yes" && "$(CHANNEL)" == "yes" +# NETBEANS - Include support for Netbeans integration +NETBEANS_PRO = proto/netbeans.pro +NETBEANS_OBJ = $(OBJDIR)/netbeans.obj +NETBEANS_DEFS = -DFEAT_NETBEANS_INTG + +!if "$(NBDEBUG)" == "yes" +NBDEBUG_DEFS = -DNBDEBUG +NBDEBUG_INCL = nbdebug.h +NBDEBUG_SRC = nbdebug.c +!endif +NETBEANS_LIB = WSock32.lib +!endif + +# DirectWrite (DirectX) +!if "$(DIRECTX)" == "yes" +DIRECTX_DEFS = -DFEAT_DIRECTX -DDYNAMIC_DIRECTX +!if "$(COLOR_EMOJI)" != "no" +DIRECTX_DEFS = $(DIRECTX_DEFS) -DFEAT_DIRECTX_COLOR_EMOJI +!endif +DIRECTX_INCL = gui_dwrite.h +DIRECTX_OBJ = $(OUTDIR)\gui_dwrite.obj +!endif + +# Only allow XPM for a GUI build. +!ifndef XPM +!ifndef USE_MSVCRT +# Both XPM and USE_MSVCRT are not set, use the included xpm files, depending +# on the architecture. +!if "$(CPU)" == "AMD64" +XPM = xpm\x64 +!elseif "$(CPU)" == "i386" +XPM = xpm\x86 +!else +XPM = no +!endif +!else # USE_MSVCRT +XPM = no +!endif # USE_MSVCRT +!endif # XPM +!if "$(XPM)" != "no" +# XPM - Include support for XPM signs +# See the xpm directory for more information. +XPM_OBJ = $(OBJDIR)/xpm_w32.obj +XPM_DEFS = -DFEAT_XPM_W32 +!if $(MSVC_MAJOR) >= 14 +# VC14 cannot use a library built by VC12 or eariler, because VC14 uses +# Universal CRT. +XPM_LIB = $(XPM)\lib-vc14\libXpm.lib +!else +XPM_LIB = $(XPM)\lib\libXpm.lib +!endif +XPM_INC = -I $(XPM)\include -I $(XPM)\..\include +!endif +!endif + +!if "$(CHANNEL)" == "yes" +CHANNEL_PRO = proto/channel.pro +CHANNEL_OBJ = $(OBJDIR)/channel.obj +CHANNEL_DEFS = -DFEAT_JOB_CHANNEL + +NETBEANS_LIB = WSock32.lib +!endif + +# Set which version of the CRT to use +!if defined(USE_MSVCRT) +# CVARS = $(cvarsdll) +# !elseif defined(MULTITHREADED) +# CVARS = $(cvarsmt) +!else +# CVARS = $(cvars) +# CVARS = $(cvarsmt) +!endif + +# need advapi32.lib for GetUserName() +# need shell32.lib for ExtractIcon() +# need netapi32.lib for NetUserEnum() +# gdi32.lib and comdlg32.lib for printing support +# ole32.lib and uuid.lib are needed for FEAT_SHORTCUT +CON_LIB = oldnames.lib kernel32.lib advapi32.lib shell32.lib gdi32.lib \ + comdlg32.lib ole32.lib netapi32.lib uuid.lib /machine:$(CPU) +!if "$(DELAYLOAD)" == "yes" +CON_LIB = $(CON_LIB) /DELAYLOAD:comdlg32.dll /DELAYLOAD:ole32.dll DelayImp.lib +!endif + +### Set the default $(WINVER) to make it work with VC++7.0 (VS.NET) +!ifndef WINVER +WINVER = 0x0501 +!endif + +# If you have a fixed directory for $VIM or $VIMRUNTIME, other than the normal +# default, use these lines. +#VIMRCLOC = somewhere +#VIMRUNTIMEDIR = somewhere + +CFLAGS = -c /W3 /nologo $(CVARS) -I. -Iproto -DHAVE_PATHDEF -DWIN32 \ + $(CSCOPE_DEFS) $(TERM_DEFS) $(NETBEANS_DEFS) $(CHANNEL_DEFS) \ + $(NBDEBUG_DEFS) $(XPM_DEFS) \ + $(DEFINES) -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) + +#>>>>> end of choices +########################################################################### + +DEL_TREE = rmdir /s /q + +INTDIR=$(OBJDIR) +OUTDIR=$(OBJDIR) + +### Validate CPUNR +!ifndef CPUNR +# default to untargeted code +CPUNR = any +!elseif "$(CPUNR)" == "i386" || "$(CPUNR)" == "i486" +# alias i386 and i486 to i586 +! message *** WARNING CPUNR=$(CPUNR) is not a valid target architecture. +! message Windows XP is the minimum target OS, with a minimum target +! message architecture of i586. +! message Retargeting to i586 +CPUNR = i586 +!elseif "$(CPUNR)" == "pentium4" +# alias pentium4 to sse2 +! message *** WARNING CPUNR=pentium4 is deprecated in favour of sse2. +! message Retargeting to sse2. +CPUNR = sse2 +!elseif "$(CPUNR)" != "any" && "$(CPUNR)" != "i586" && "$(CPUNR)" != "i686" && "$(CPUNR)" != "sse" && "$(CPUNR)" != "sse2" && "$(CPUNR)" != "avx" && "$(CPUNR)" != "avx2" +! error *** ERROR Unknown target architecture "$(CPUNR)". Make aborted. +!endif + +# Convert processor ID to MVC-compatible number +!if $(MSVC_MAJOR) < 8 +! if "$(CPUNR)" == "i586" +CPUARG = /G5 +! elseif "$(CPUNR)" == "i686" +CPUARG = /G6 +! elseif "$(CPUNR)" == "sse" +CPUARG = /G6 /arch:SSE +! elseif "$(CPUNR)" == "sse2" +CPUARG = /G7 /arch:SSE2 +! elseif "$(CPUNR)" == "avx" || "$(CPUNR)" == "avx2" +! message AVX/AVX2 Instruction Sets are not supported by Visual C++ v$(MSVC_MAJOR) +! message Falling back to SSE2 +CPUARG = /G7 /arch:SSE2 +! elseif "$(CPUNR)" == "any" +CPUARG = +! endif +!else +# IA32/SSE/SSE2 are only supported on x86 +! if "$(ASSEMBLY_ARCHITECTURE)" == "i386" && ("$(CPUNR)" == "i586" || "$(CPUNR)" == "i686" || "$(CPUNR)" == "any") +# VC<11 generates fp87 code by default +! if $(MSVC_MAJOR) < 11 +CPUARG = +# VC>=11 needs explicit insturctions to generate fp87 code +! else +CPUARG = /arch:IA32 +! endif +! elseif "$(ASSEMBLY_ARCHITECTURE)" == "i386" && "$(CPUNR)" == "sse" +CPUARG = /arch:SSE +! elseif "$(ASSEMBLY_ARCHITECTURE)" == "i386" && "$(CPUNR)" == "sse2" +CPUARG = /arch:SSE2 +! elseif "$(CPUNR)" == "avx" +# AVX is only supported by VC 10 and up +! if $(MSVC_MAJOR) < 10 +! message AVX Instruction Set is not supported by Visual C++ v$(MSVC_MAJOR) +! if "$(ASSEMBLY_ARCHITECTURE)" == "i386" +! message Falling back to SSE2 +CPUARG = /arch:SSE2 +! else +CPUARG = +! endif +! else +CPUARG = /arch:AVX +! endif +! elseif "$(CPUNR)" == "avx2" +# AVX is only supported by VC 10 and up +! if $(MSVC_MAJOR) < 10 +! message AVX2 Instruction Set is not supported by Visual C++ v$(MSVC_MAJOR) +! if "$(ASSEMBLY_ARCHITECTURE)" == "i386" +! message Falling back to SSE2 +CPUARG = /arch:SSE2 +! else +CPUARG = +! endif +# AVX2 is only supported by VC 12U2 and up +# 180030501 is the full version number for Visual Studio 2013/VC 12 Update 2 +! elseif $(MSVC_FULL) < 180030501 +! message AVX2 Instruction Set is not supported by Visual C++ v$(MSVC_MAJOR)-$(MSVC_FULL) +! message Falling back to AVX +CPUARG = /arch:AVX +! else +CPUARG = /arch:AVX2 +! endif +! endif +!endif + +# Pass CPUARG to GvimExt, to avoid using version-dependent defaults +MAKEFLAGS_GVIMEXT = $(MAKEFLAGS_GVIMEXT) CPUARG="$(CPUARG)" + + +LIBC = +DEBUGINFO = /Zi + +# Don't use /nodefaultlib on MSVC 14 +!if $(MSVC_MAJOR) >= 14 +NODEFAULTLIB = +!else +NODEFAULTLIB = /nodefaultlib +!endif + +# Use multiprocess build on MSVC 10 +!if "$(USE_MP)"=="yes" +!if $(MSVC_MAJOR) >= 10 +CFLAGS = $(CFLAGS) /MP +!endif +!endif + + +!ifdef NODEBUG +VIM = vim +!if "$(OPTIMIZE)" == "SPACE" +OPTFLAG = /O1 +!elseif "$(OPTIMIZE)" == "SPEED" +OPTFLAG = /O2 +!else # MAXSPEED +OPTFLAG = /Ox +!endif + +!if $(MSVC_MAJOR) >= 8 +# Use link time code generation if not worried about size +!if "$(OPTIMIZE)" != "SPACE" +OPTFLAG = $(OPTFLAG) /GL +!endif +!endif + +# (/Wp64 is deprecated in VC9 and generates an obnoxious warning.) +!if ($(MSVC_MAJOR) == 7) || ($(MSVC_MAJOR) == 8) +CFLAGS=$(CFLAGS) $(WP64CHECK) +!endif + +# VC10 or later has stdint.h. +!if $(MSVC_MAJOR) >= 10 +CFLAGS = $(CFLAGS) -DHAVE_STDINT_H +!endif + +# Static code analysis generally available starting with VS2012 (VC11) or +# Windows SDK 7.1 (VC10) +!if ("$(ANALYZE)" == "yes") && ($(MSVC_MAJOR) >= 10) +CFLAGS=$(CFLAGS) /analyze +!endif + +CFLAGS = $(CFLAGS) $(OPTFLAG) -DNDEBUG $(CPUARG) +RCFLAGS = $(rcflags) $(rcvars) -DNDEBUG +! ifdef USE_MSVCRT +CFLAGS = $(CFLAGS) /MD +LIBC = msvcrt.lib +! else +LIBC = libcmt.lib +CFLAGS = $(CFLAGS) /Zl /MT +! endif +!else # DEBUG +VIM = vimd +! if ("$(CPU)" == "i386") || ("$(CPU)" == "ix86") +DEBUGINFO = /ZI +! endif +CFLAGS = $(CFLAGS) -D_DEBUG -DDEBUG /Od +RCFLAGS = $(rcflags) $(rcvars) -D_DEBUG -DDEBUG +# The /fixed:no is needed for Quantify. Assume not 4.? as unsupported in VC4.0. +! if $(MSVC_MAJOR) == 4 +LIBC = +! else +LIBC = /fixed:no +! endif +! ifdef USE_MSVCRT +CFLAGS = $(CFLAGS) /MDd +LIBC = $(LIBC) msvcrtd.lib +! else +LIBC = $(LIBC) libcmtd.lib +CFLAGS = $(CFLAGS) /Zl /MTd +! endif +!endif # DEBUG + +!include Make_all.mak +!include testdir\Make_all.mak + +INCL = vim.h alloc.h arabic.h ascii.h ex_cmds.h farsi.h feature.h globals.h \ + keymap.h macros.h option.h os_dos.h os_win32.h proto.h regexp.h \ + spell.h structs.h term.h beval.h $(NBDEBUG_INCL) + +OBJ = \ + $(OUTDIR)\arabic.obj \ + $(OUTDIR)\autocmd.obj \ + $(OUTDIR)\beval.obj \ + $(OUTDIR)\blob.obj \ + $(OUTDIR)\blowfish.obj \ + $(OUTDIR)\buffer.obj \ + $(OUTDIR)\charset.obj \ + $(OUTDIR)\crypt.obj \ + $(OUTDIR)\crypt_zip.obj \ + $(OUTDIR)\dict.obj \ + $(OUTDIR)\diff.obj \ + $(OUTDIR)\digraph.obj \ + $(OUTDIR)\edit.obj \ + $(OUTDIR)\eval.obj \ + $(OUTDIR)\evalfunc.obj \ + $(OUTDIR)\ex_cmds.obj \ + $(OUTDIR)\ex_cmds2.obj \ + $(OUTDIR)\ex_docmd.obj \ + $(OUTDIR)\ex_eval.obj \ + $(OUTDIR)\ex_getln.obj \ + $(OUTDIR)\farsi.obj \ + $(OUTDIR)\fileio.obj \ + $(OUTDIR)\fold.obj \ + $(OUTDIR)\getchar.obj \ + $(OUTDIR)\hardcopy.obj \ + $(OUTDIR)\hashtab.obj \ + $(OUTDIR)\indent.obj \ + $(OUTDIR)\json.obj \ + $(OUTDIR)\list.obj \ + $(OUTDIR)\main.obj \ + $(OUTDIR)\mark.obj \ + $(OUTDIR)\mbyte.obj \ + $(OUTDIR)\memfile.obj \ + $(OUTDIR)\memline.obj \ + $(OUTDIR)\menu.obj \ + $(OUTDIR)\message.obj \ + $(OUTDIR)\misc1.obj \ + $(OUTDIR)\misc2.obj \ + $(OUTDIR)\move.obj \ + $(OUTDIR)\normal.obj \ + $(OUTDIR)\ops.obj \ + $(OUTDIR)\option.obj \ + $(OUTDIR)\os_mswin.obj \ + $(OUTDIR)\winclip.obj \ + $(OUTDIR)\os_win32.obj \ + $(OUTDIR)\pathdef.obj \ + $(OUTDIR)\popupmnu.obj \ + $(OUTDIR)\quickfix.obj \ + $(OUTDIR)\regexp.obj \ + $(OUTDIR)\screen.obj \ + $(OUTDIR)\search.obj \ + $(OUTDIR)\sha256.obj \ + $(OUTDIR)\sign.obj \ + $(OUTDIR)\spell.obj \ + $(OUTDIR)\spellfile.obj \ + $(OUTDIR)\syntax.obj \ + $(OUTDIR)\tag.obj \ + $(OUTDIR)\term.obj \ + $(OUTDIR)\textprop.obj \ + $(OUTDIR)\ui.obj \ + $(OUTDIR)\undo.obj \ + $(OUTDIR)\userfunc.obj \ + $(OUTDIR)\window.obj \ + $(OUTDIR)\vim.res + +!if "$(OLE)" == "yes" +CFLAGS = $(CFLAGS) -DFEAT_OLE +RCFLAGS = $(RCFLAGS) -DFEAT_OLE +OLE_OBJ = $(OUTDIR)\if_ole.obj +OLE_IDL = if_ole.idl +OLE_LIB = oleaut32.lib +!endif + +!if "$(IME)" == "yes" +CFLAGS = $(CFLAGS) -DFEAT_MBYTE_IME +!ifndef DYNAMIC_IME +DYNAMIC_IME = yes +!endif +!if "$(DYNAMIC_IME)" == "yes" +CFLAGS = $(CFLAGS) -DDYNAMIC_IME +!else +IME_LIB = imm32.lib +!endif +!endif + +!if "$(GIME)" == "yes" +CFLAGS = $(CFLAGS) -DGLOBAL_IME +OBJ = $(OBJ) $(OUTDIR)\dimm_i.obj $(OUTDIR)\glbl_ime.obj +!endif + +!if "$(GUI)" == "yes" +SUBSYSTEM = windows +CFLAGS = $(CFLAGS) -DFEAT_GUI_W32 +RCFLAGS = $(RCFLAGS) -DFEAT_GUI_W32 +VIM = g$(VIM) +GUI_INCL = \ + gui.h +GUI_OBJ = \ + $(OUTDIR)\gui.obj \ + $(OUTDIR)\gui_beval.obj \ + $(OUTDIR)\gui_w32.obj \ + $(OUTDIR)\os_w32exe.obj +GUI_LIB = \ + gdi32.lib version.lib $(IME_LIB) \ + winspool.lib comctl32.lib advapi32.lib shell32.lib netapi32.lib \ + /machine:$(CPU) +!else +SUBSYSTEM = console +CUI_INCL = iscygpty.h +CUI_OBJ = $(OUTDIR)\iscygpty.obj +!endif +SUBSYSTEM_TOOLS = console + +XDIFF_OBJ = $(OBJDIR)/xdiffi.obj \ + $(OBJDIR)/xemit.obj \ + $(OBJDIR)/xprepare.obj \ + $(OBJDIR)/xutils.obj \ + $(OBJDIR)/xhistogram.obj \ + $(OBJDIR)/xpatience.obj + +XDIFF_DEPS = \ + xdiff/xdiff.h \ + xdiff/xdiffi.h \ + xdiff/xemit.h \ + xdiff/xinclude.h \ + xdiff/xmacros.h \ + xdiff/xprepare.h \ + xdiff/xtypes.h \ + xdiff/xutils.h + + +!if "$(SUBSYSTEM_VER)" != "" +SUBSYSTEM = $(SUBSYSTEM),$(SUBSYSTEM_VER) +SUBSYSTEM_TOOLS = $(SUBSYSTEM_TOOLS),$(SUBSYSTEM_VER) +# Pass SUBSYSTEM_VER to GvimExt and other tools +MAKEFLAGS_GVIMEXT = $(MAKEFLAGS_GVIMEXT) SUBSYSTEM_VER=$(SUBSYSTEM_VER) +MAKEFLAGS_TOOLS = $(MAKEFLAGS_TOOLS) SUBSYSTEM_VER=$(SUBSYSTEM_VER) +!endif + +!if "$(GUI)" == "yes" && "$(DIRECTX)" == "yes" +CFLAGS = $(CFLAGS) $(DIRECTX_DEFS) +GUI_INCL = $(GUI_INCL) $(DIRECTX_INCL) +GUI_OBJ = $(GUI_OBJ) $(DIRECTX_OBJ) +!endif + +# iconv.dll library (dynamically loaded) +!ifndef ICONV +ICONV = yes +!endif +!if "$(ICONV)" == "yes" +CFLAGS = $(CFLAGS) -DDYNAMIC_ICONV +!endif + +# libintl.dll library +!ifndef GETTEXT +GETTEXT = yes +!endif +!if "$(GETTEXT)" == "yes" +CFLAGS = $(CFLAGS) -DDYNAMIC_GETTEXT +!endif + +# TCL interface +!ifdef TCL +!ifndef TCL_VER +TCL_VER = 86 +TCL_VER_LONG = 8.6 +!endif +!message Tcl requested (version $(TCL_VER)) - root dir is "$(TCL)" +!if "$(DYNAMIC_TCL)" == "yes" +!message Tcl DLL will be loaded dynamically +!ifndef TCL_DLL +TCL_DLL = tcl$(TCL_VER).dll +!endif +CFLAGS = $(CFLAGS) -DFEAT_TCL -DDYNAMIC_TCL -DDYNAMIC_TCL_DLL=\"$(TCL_DLL)\" \ + -DDYNAMIC_TCL_VER=\"$(TCL_VER_LONG)\" +TCL_OBJ = $(OUTDIR)\if_tcl.obj +TCL_INC = /I "$(TCL)\Include" /I "$(TCL)" +TCL_LIB = "$(TCL)\lib\tclstub$(TCL_VER).lib" +!else +CFLAGS = $(CFLAGS) -DFEAT_TCL +TCL_OBJ = $(OUTDIR)\if_tcl.obj +TCL_INC = /I "$(TCL)\Include" /I "$(TCL)" +TCL_LIB = $(TCL)\lib\tcl$(TCL_VER)vc.lib +!endif +!endif + +# Lua interface +!ifdef LUA +!ifndef LUA_VER +LUA_VER = 53 +!endif +!message Lua requested (version $(LUA_VER)) - root dir is "$(LUA)" +!if "$(DYNAMIC_LUA)" == "yes" +!message Lua DLL will be loaded dynamically +!endif +CFLAGS = $(CFLAGS) -DFEAT_LUA +LUA_OBJ = $(OUTDIR)\if_lua.obj +LUA_INC = /I "$(LUA)\include" /I "$(LUA)" +!if "$(DYNAMIC_LUA)" == "yes" +CFLAGS = $(CFLAGS) -DDYNAMIC_LUA \ + -DDYNAMIC_LUA_DLL=\"lua$(LUA_VER).dll\" +LUA_LIB = /nodefaultlib:lua$(LUA_VER).lib +!else +LUA_LIB = "$(LUA)\lib\lua$(LUA_VER).lib" +!endif +!endif + +!ifdef PYTHON +!ifdef PYTHON3 +DYNAMIC_PYTHON=yes +DYNAMIC_PYTHON3=yes +!endif +!endif + +# PYTHON interface +!ifdef PYTHON +!ifndef PYTHON_VER +PYTHON_VER = 27 +!endif +!message Python requested (version $(PYTHON_VER)) - root dir is "$(PYTHON)" +!if "$(DYNAMIC_PYTHON)" == "yes" +!message Python DLL will be loaded dynamically +!endif +CFLAGS = $(CFLAGS) -DFEAT_PYTHON +PYTHON_OBJ = $(OUTDIR)\if_python.obj +PYTHON_INC = /I "$(PYTHON)\Include" /I "$(PYTHON)\PC" +!if "$(DYNAMIC_PYTHON)" == "yes" +CFLAGS = $(CFLAGS) -DDYNAMIC_PYTHON \ + -DDYNAMIC_PYTHON_DLL=\"python$(PYTHON_VER).dll\" +PYTHON_LIB = /nodefaultlib:python$(PYTHON_VER).lib +!else +PYTHON_LIB = $(PYTHON)\libs\python$(PYTHON_VER).lib +!endif +!endif + +# PYTHON3 interface +!ifdef PYTHON3 +!ifndef PYTHON3_VER +PYTHON3_VER = 36 +!endif +!message Python3 requested (version $(PYTHON3_VER)) - root dir is "$(PYTHON3)" +!if "$(DYNAMIC_PYTHON3)" == "yes" +!message Python3 DLL will be loaded dynamically +!endif +CFLAGS = $(CFLAGS) -DFEAT_PYTHON3 +PYTHON3_OBJ = $(OUTDIR)\if_python3.obj +PYTHON3_INC = /I "$(PYTHON3)\Include" /I "$(PYTHON3)\PC" +!if "$(DYNAMIC_PYTHON3)" == "yes" +CFLAGS = $(CFLAGS) -DDYNAMIC_PYTHON3 \ + -DDYNAMIC_PYTHON3_DLL=\"python$(PYTHON3_VER).dll\" +PYTHON3_LIB = /nodefaultlib:python$(PYTHON3_VER).lib +!else +PYTHON3_LIB = $(PYTHON3)\libs\python$(PYTHON3_VER).lib +!endif +!endif + +# MzScheme interface +!ifdef MZSCHEME +!message MzScheme requested - root dir is "$(MZSCHEME)" +!ifndef MZSCHEME_VER +MZSCHEME_VER = 3m_a0solc +!endif +!ifndef MZSCHEME_COLLECTS +MZSCHEME_COLLECTS=$(MZSCHEME)\collects +!endif +CFLAGS = $(CFLAGS) -DFEAT_MZSCHEME -I "$(MZSCHEME)\include" +!if EXIST("$(MZSCHEME)\lib\msvc\libmzsch$(MZSCHEME_VER).lib") +MZSCHEME_MAIN_LIB=mzsch +!else +MZSCHEME_MAIN_LIB=racket +!endif +!if (EXIST("$(MZSCHEME)\lib\lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).dll") \ + && !EXIST("$(MZSCHEME)\lib\libmzgc$(MZSCHEME_VER).dll")) \ + || (EXIST("$(MZSCHEME)\lib\msvc\lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).lib") \ + && !EXIST("$(MZSCHEME)\lib\msvc\libmzgc$(MZSCHEME_VER).lib")) +!message Building with Precise GC +MZSCHEME_PRECISE_GC = yes +CFLAGS = $(CFLAGS) -DMZ_PRECISE_GC +!endif +!if "$(DYNAMIC_MZSCHEME)" == "yes" +!message MzScheme DLLs will be loaded dynamically +CFLAGS = $(CFLAGS) -DDYNAMIC_MZSCHEME +!if "$(MZSCHEME_PRECISE_GC)" == "yes" +# Precise GC does not use separate dll +CFLAGS = $(CFLAGS) \ + -DDYNAMIC_MZSCH_DLL=\"lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).dll\" \ + -DDYNAMIC_MZGC_DLL=\"lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).dll\" +!else +CFLAGS = $(CFLAGS) \ + -DDYNAMIC_MZSCH_DLL=\"lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).dll\" \ + -DDYNAMIC_MZGC_DLL=\"libmzgc$(MZSCHEME_VER).dll\" +!endif +!else +!if "$(MZSCHEME_DEBUG)" == "yes" +CFLAGS = $(CFLAGS) -DMZSCHEME_FORCE_GC +!endif +!if "$(MZSCHEME_PRECISE_GC)" == "yes" +# Precise GC does not use separate dll +!if EXIST("$(MZSCHEME)\lib\lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).def") +# create .lib from .def +MZSCHEME_LIB = lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).lib +MZSCHEME_EXTRA_DEP = lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).lib +!else +MZSCHEME_LIB = "$(MZSCHEME)\lib\msvc\lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).lib" +!endif +!else +MZSCHEME_LIB = "$(MZSCHEME)\lib\msvc\libmzgc$(MZSCHEME_VER).lib" \ + "$(MZSCHEME)\lib\msvc\lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).lib" +!endif +!endif +MZSCHEME_OBJ = $(OUTDIR)\if_mzsch.obj +# increase stack size +MZSCHEME_LIB = $(MZSCHEME_LIB) /STACK:8388608 +MZSCHEME_INCL = if_mzsch.h +!endif + +# Perl interface +!ifdef PERL +!ifndef PERL_VER +PERL_VER = 524 +!endif +!message Perl requested (version $(PERL_VER)) - root dir is "$(PERL)" +!if "$(DYNAMIC_PERL)" == "yes" +!if $(PERL_VER) >= 56 +!message Perl DLL will be loaded dynamically +!else +!message Dynamic loading is not supported for Perl versions earlier than 5.6.0 +!message Reverting to static loading... +!undef DYNAMIC_PERL +!endif +!endif + +# Is Perl installed in architecture-specific directories? +!if exist($(PERL)\Bin\MSWin32-x86) +PERL_ARCH = \MSWin32-x86 +!endif + +PERL_INCDIR = $(PERL)\Lib$(PERL_ARCH)\Core + +# Version-dependent stuff +!if $(PERL_VER) == 55 +PERL_LIB = $(PERL_INCDIR)\perl.lib +!else +PERL_DLL = perl$(PERL_VER).dll +!if exist($(PERL_INCDIR)\perl$(PERL_VER).lib) +PERL_LIB = $(PERL_INCDIR)\perl$(PERL_VER).lib +!else +# For ActivePerl 5.18 and later +PERL_LIB = $(PERL_INCDIR)\libperl$(PERL_VER).a +!endif +!endif + +CFLAGS = $(CFLAGS) -DFEAT_PERL -DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS + +# Do we want to load Perl dynamically? +!if "$(DYNAMIC_PERL)" == "yes" +CFLAGS = $(CFLAGS) -DDYNAMIC_PERL -DDYNAMIC_PERL_DLL=\"$(PERL_DLL)\" +!undef PERL_LIB +!endif + +PERL_EXE = $(PERL)\Bin$(PERL_ARCH)\perl +PERL_INC = /I $(PERL_INCDIR) +!if $(MSVC_MAJOR) <= 11 +# ActivePerl 5.20+ requires stdbool.h but VC2012 or earlier doesn't have it. +# Use a stub stdbool.h. +PERL_INC = $(PERL_INC) /I if_perl_msvc +!endif +PERL_OBJ = $(OUTDIR)\if_perl.obj $(OUTDIR)\if_perlsfio.obj +XSUBPP = $(PERL)\lib\ExtUtils\xsubpp +!if exist($(XSUBPP)) +XSUBPP = $(PERL_EXE) $(XSUBPP) +!else +XSUBPP = xsubpp +!endif +XSUBPP_TYPEMAP = $(PERL)\lib\ExtUtils\typemap + +!endif + +# +# Support Ruby interface +# +!ifdef RUBY +# Set default value +!ifndef RUBY_VER +RUBY_VER = 22 +!endif +!ifndef RUBY_VER_LONG +RUBY_VER_LONG = 2.2.0 +!endif +!ifndef RUBY_API_VER_LONG +RUBY_API_VER_LONG = $(RUBY_VER_LONG) +!endif +!ifndef RUBY_API_VER +RUBY_API_VER = $(RUBY_API_VER_LONG:.=) +!endif + +!if $(RUBY_VER) >= 18 + +!ifndef RUBY_PLATFORM +!if "$(CPU)" == "i386" +RUBY_PLATFORM = i386-mswin32 +!else # CPU +RUBY_PLATFORM = x64-mswin64 +!endif # CPU +!if $(MSVCRT_VER) >= 70 && $(RUBY_VER) > 19 +RUBY_PLATFORM = $(RUBY_PLATFORM)_$(MSVCRT_VER) +!endif # MSVCRT_VER +!endif # RUBY_PLATFORM + +!ifndef RUBY_INSTALL_NAME +!ifndef RUBY_MSVCRT_NAME +# Base name of msvcrXX.dll which is used by ruby's dll. +RUBY_MSVCRT_NAME = $(MSVCRT_NAME) +!endif # RUBY_MSVCRT_NAME +!if "$(CPU)" == "i386" +RUBY_INSTALL_NAME = $(RUBY_MSVCRT_NAME)-ruby$(RUBY_API_VER) +!else # CPU +RUBY_INSTALL_NAME = x64-$(RUBY_MSVCRT_NAME)-ruby$(RUBY_API_VER) +!endif # CPU +!endif # RUBY_INSTALL_NAME + +!else # $(RUBY_VER) >= 18 + +!ifndef RUBY_PLATFORM +RUBY_PLATFORM = i586-mswin32 +!endif +!ifndef RUBY_INSTALL_NAME +RUBY_INSTALL_NAME = mswin32-ruby$(RUBY_API_VER) +!endif + +!endif # $(RUBY_VER) >= 18 + +!message Ruby requested (version $(RUBY_VER)) - root dir is "$(RUBY)" +CFLAGS = $(CFLAGS) -DFEAT_RUBY +RUBY_OBJ = $(OUTDIR)\if_ruby.obj +!if $(RUBY_VER) >= 19 +RUBY_INC = /I "$(RUBY)\include\ruby-$(RUBY_API_VER_LONG)" /I "$(RUBY)\include\ruby-$(RUBY_API_VER_LONG)\$(RUBY_PLATFORM)" +!else +RUBY_INC = /I "$(RUBY)\lib\ruby\$(RUBY_API_VER_LONG)\$(RUBY_PLATFORM)" +!endif +RUBY_LIB = $(RUBY)\lib\$(RUBY_INSTALL_NAME).lib +# Do we want to load Ruby dynamically? +!if "$(DYNAMIC_RUBY)" == "yes" +!message Ruby DLL will be loaded dynamically +CFLAGS = $(CFLAGS) -DDYNAMIC_RUBY -DDYNAMIC_RUBY_VER=$(RUBY_VER) \ + -DDYNAMIC_RUBY_DLL=\"$(RUBY_INSTALL_NAME).dll\" +!undef RUBY_LIB +!endif +!endif # RUBY + +# +# Support PostScript printing +# +!if "$(POSTSCRIPT)" == "yes" +CFLAGS = $(CFLAGS) -DMSWINPS +!endif # POSTSCRIPT + +# +# FEATURES: TINY, SMALL, NORMAL, BIG or HUGE +# +CFLAGS = $(CFLAGS) -DFEAT_$(FEATURES) + +# +# Always generate the .pdb file, so that we get debug symbols that can be used +# on a crash (doesn't add overhead to the executable). +# Generate edit-and-continue debug info when no optimization - allows to +# debug more conveniently (able to look at variables which are in registers) +# +CFLAGS = $(CFLAGS) /Fd$(OUTDIR)/ $(DEBUGINFO) +LINK_PDB = /PDB:$(VIM).pdb -debug + +# +# End extra feature include +# +!message + +# CFLAGS with /Fo$(OUTDIR)/ +CFLAGS_OUTDIR=$(CFLAGS) /Fo$(OUTDIR)/ + +# Add /opt:ref to remove unreferenced functions and data even when /DEBUG is +# added. +conflags = /nologo /subsystem:$(SUBSYSTEM) /opt:ref + +PATHDEF_SRC = $(OUTDIR)\pathdef.c + +!IF "$(MAP)" == "yes" +# "/map" is for debugging +conflags = $(conflags) /map +!ELSEIF "$(MAP)" == "lines" +# "/mapinfo:lines" is for debugging, only works for VC6 and later +conflags = $(conflags) /map /mapinfo:lines +!ENDIF + +LINKARGS1 = $(linkdebug) $(conflags) +LINKARGS2 = $(CON_LIB) $(GUI_LIB) $(NODEFAULTLIB) $(LIBC) $(OLE_LIB) user32.lib \ + $(LUA_LIB) $(MZSCHEME_LIB) $(PERL_LIB) $(PYTHON_LIB) $(PYTHON3_LIB) $(RUBY_LIB) \ + $(TCL_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(LINK_PDB) + +# Report link time code generation progress if used. +!ifdef NODEBUG +!if $(MSVC_MAJOR) >= 8 +!if "$(OPTIMIZE)" != "SPACE" +LINKARGS1 = $(LINKARGS1) /LTCG:STATUS +!endif +!endif +!endif + +!if $(MSVC_MAJOR) >= 11 && "$(CPU)" == "AMD64" && "$(GUI)" == "yes" +# This option is required for VC2012 or later so that 64-bit gvim can +# accept D&D from 32-bit applications. NOTE: This disables 64-bit ASLR, +# therefore the security level becomes as same as VC2010. +LINKARGS1 = $(LINKARGS1) /HIGHENTROPYVA:NO +!endif + +all: $(VIM).exe \ + vimrun.exe \ + install.exe \ + uninstal.exe \ + xxd/xxd.exe \ + tee/tee.exe \ + GvimExt/gvimext.dll + +$(VIM).exe: $(OUTDIR) $(OBJ) $(XDIFF_OBJ) $(GUI_OBJ) $(CUI_OBJ) $(OLE_OBJ) $(OLE_IDL) $(MZSCHEME_OBJ) \ + $(LUA_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) $(TCL_OBJ) \ + $(CSCOPE_OBJ) $(TERM_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) $(XPM_OBJ) \ + version.c version.h + $(CC) $(CFLAGS_OUTDIR) version.c + $(link) $(LINKARGS1) -out:$(VIM).exe $(OBJ) $(XDIFF_OBJ) $(GUI_OBJ) $(CUI_OBJ) $(OLE_OBJ) \ + $(LUA_OBJ) $(MZSCHEME_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) \ + $(TCL_OBJ) $(CSCOPE_OBJ) $(TERM_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) \ + $(XPM_OBJ) $(OUTDIR)\version.obj $(LINKARGS2) + if exist $(VIM).exe.manifest mt.exe -nologo -manifest $(VIM).exe.manifest -updateresource:$(VIM).exe;1 + +$(VIM): $(VIM).exe + +$(OUTDIR): + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +install.exe: dosinst.c + $(CC) /nologo -DNDEBUG -DWIN32 dosinst.c kernel32.lib shell32.lib \ + user32.lib ole32.lib advapi32.lib uuid.lib \ + -link -subsystem:$(SUBSYSTEM_TOOLS) + - if exist install.exe del install.exe + ren dosinst.exe install.exe + +uninstal.exe: uninstal.c + $(CC) /nologo -DNDEBUG -DWIN32 uninstal.c shell32.lib advapi32.lib \ + -link -subsystem:$(SUBSYSTEM_TOOLS) + +vimrun.exe: vimrun.c + $(CC) /nologo -DNDEBUG vimrun.c -link -subsystem:$(SUBSYSTEM_TOOLS) + +xxd/xxd.exe: xxd/xxd.c + cd xxd + $(MAKE) /NOLOGO -f Make_mvc.mak $(MAKEFLAGS_TOOLS) + cd .. + +tee/tee.exe: tee/tee.c + cd tee + $(MAKE) /NOLOGO -f Make_mvc.mak $(MAKEFLAGS_TOOLS) + cd .. + +GvimExt/gvimext.dll: GvimExt/gvimext.cpp GvimExt/gvimext.rc GvimExt/gvimext.h + cd GvimExt + $(MAKE) /NOLOGO -f Makefile $(MAKEFLAGS_GVIMEXT) + cd .. + + +tags: notags + $(CTAGS) $(TAGS_FILES) + +notags: + - if exist tags del tags + +clean: + - if exist $(OUTDIR)/nul $(DEL_TREE) $(OUTDIR) + - if exist *.obj del *.obj + - if exist $(VIM).exe del $(VIM).exe + - if exist $(VIM).ilk del $(VIM).ilk + - if exist $(VIM).pdb del $(VIM).pdb + - if exist $(VIM).map del $(VIM).map + - if exist $(VIM).ncb del $(VIM).ncb + - if exist vimrun.exe del vimrun.exe + - if exist install.exe del install.exe + - if exist uninstal.exe del uninstal.exe + - if exist if_perl.c del if_perl.c + - if exist auto\if_perl.c del auto\if_perl.c + - if exist dimm.h del dimm.h + - if exist dimm_i.c del dimm_i.c + - if exist dimm.tlb del dimm.tlb + - if exist dosinst.exe del dosinst.exe + cd xxd + $(MAKE) /NOLOGO -f Make_mvc.mak clean + cd .. + cd tee + $(MAKE) /NOLOGO -f Make_mvc.mak clean + cd .. + cd GvimExt + $(MAKE) /NOLOGO -f Makefile clean + cd .. + - if exist testdir\*.out del testdir\*.out + +test: + cd testdir + $(MAKE) /NOLOGO -f Make_dos.mak win32 + cd .. + +testgvim: + cd testdir + $(MAKE) /NOLOGO -f Make_dos.mak VIMPROG=..\gvim win32 + cd .. + +testclean: + cd testdir + $(MAKE) /NOLOGO -f Make_dos.mak clean + cd .. + +$(NEW_TESTS): + cd testdir + - if exist $@.res del $@.res + $(MAKE) /NOLOGO -f Make_dos.mak nolog + $(MAKE) /NOLOGO -f Make_dos.mak $@.res + $(MAKE) /NOLOGO -f Make_dos.mak report + type messages + cd .. + +########################################################################### + +# Create a default rule for transforming .c files to .obj files in $(OUTDIR) +# Batch compilation is supported by nmake 1.62 (part of VS 5.0) and later) +!IF "$(_NMAKE_VER)" == "" +.c{$(OUTDIR)/}.obj: +!ELSE +.c{$(OUTDIR)/}.obj:: +!ENDIF + $(CC) $(CFLAGS_OUTDIR) $< + +# Create a default rule for transforming .cpp files to .obj files in $(OUTDIR) +# Batch compilation is supported by nmake 1.62 (part of VS 5.0) and later) +!IF "$(_NMAKE_VER)" == "" +.cpp{$(OUTDIR)/}.obj: +!ELSE +.cpp{$(OUTDIR)/}.obj:: +!ENDIF + $(CC) $(CFLAGS_OUTDIR) $< + +$(OUTDIR)/arabic.obj: $(OUTDIR) arabic.c $(INCL) + +$(OUTDIR)/autocmd.obj: $(OUTDIR) autocmd.c $(INCL) + +$(OUTDIR)/beval.obj: $(OUTDIR) beval.c $(INCL) + +$(OUTDIR)/blob.obj: $(OUTDIR) blob.c $(INCL) + +$(OUTDIR)/blowfish.obj: $(OUTDIR) blowfish.c $(INCL) + +$(OUTDIR)/buffer.obj: $(OUTDIR) buffer.c $(INCL) + +$(OUTDIR)/charset.obj: $(OUTDIR) charset.c $(INCL) + +$(OUTDIR)/crypt.obj: $(OUTDIR) crypt.c $(INCL) + +$(OUTDIR)/crypt_zip.obj: $(OUTDIR) crypt_zip.c $(INCL) + +$(OUTDIR)/dict.obj: $(OUTDIR) dict.c $(INCL) + +$(OUTDIR)/diff.obj: $(OUTDIR) diff.c $(INCL) + +$(OUTDIR)/xdiffi.obj: $(OUTDIR) xdiff/xdiffi.c $(XDIFF_DEPS) + $(CC) $(CFLAGS_OUTDIR) xdiff/xdiffi.c + +$(OUTDIR)/xemit.obj: $(OUTDIR) xdiff/xemit.c $(XDIFF_DEPS) + $(CC) $(CFLAGS_OUTDIR) xdiff/xemit.c + +$(OUTDIR)/xprepare.obj: $(OUTDIR) xdiff/xprepare.c $(XDIFF_DEPS) + $(CC) $(CFLAGS_OUTDIR) xdiff/xprepare.c + +$(OUTDIR)/xutils.obj: $(OUTDIR) xdiff/xutils.c $(XDIFF_DEPS) + $(CC) $(CFLAGS_OUTDIR) xdiff/xutils.c + +$(OUTDIR)/xhistogram.obj: $(OUTDIR) xdiff/xhistogram.c $(XDIFF_DEPS) + $(CC) $(CFLAGS_OUTDIR) xdiff/xhistogram.c + +$(OUTDIR)/xpatience.obj: $(OUTDIR) xdiff/xpatience.c $(XDIFF_DEPS) + $(CC) $(CFLAGS_OUTDIR) xdiff/xpatience.c + +$(OUTDIR)/digraph.obj: $(OUTDIR) digraph.c $(INCL) + +$(OUTDIR)/edit.obj: $(OUTDIR) edit.c $(INCL) + +$(OUTDIR)/eval.obj: $(OUTDIR) eval.c $(INCL) + +$(OUTDIR)/evalfunc.obj: $(OUTDIR) evalfunc.c $(INCL) + +$(OUTDIR)/ex_cmds.obj: $(OUTDIR) ex_cmds.c $(INCL) + +$(OUTDIR)/ex_cmds2.obj: $(OUTDIR) ex_cmds2.c $(INCL) + +$(OUTDIR)/ex_docmd.obj: $(OUTDIR) ex_docmd.c $(INCL) + +$(OUTDIR)/ex_eval.obj: $(OUTDIR) ex_eval.c $(INCL) + +$(OUTDIR)/ex_getln.obj: $(OUTDIR) ex_getln.c $(INCL) + +$(OUTDIR)/farsi.obj: $(OUTDIR) farsi.c $(INCL) + +$(OUTDIR)/fileio.obj: $(OUTDIR) fileio.c $(INCL) + +$(OUTDIR)/fold.obj: $(OUTDIR) fold.c $(INCL) + +$(OUTDIR)/getchar.obj: $(OUTDIR) getchar.c $(INCL) + +$(OUTDIR)/hardcopy.obj: $(OUTDIR) hardcopy.c $(INCL) + +$(OUTDIR)/hashtab.obj: $(OUTDIR) hashtab.c $(INCL) + +$(OUTDIR)/indent.obj: $(OUTDIR) indent.c $(INCL) + +$(OUTDIR)/gui.obj: $(OUTDIR) gui.c $(INCL) $(GUI_INCL) + +$(OUTDIR)/gui_beval.obj: $(OUTDIR) gui_beval.c $(INCL) $(GUI_INCL) + +$(OUTDIR)/gui_w32.obj: $(OUTDIR) gui_w32.c $(INCL) $(GUI_INCL) + +$(OUTDIR)/gui_dwrite.obj: $(OUTDIR) gui_dwrite.cpp $(INCL) $(GUI_INCL) + +$(OUTDIR)/if_cscope.obj: $(OUTDIR) if_cscope.c $(INCL) if_cscope.h + +$(OUTDIR)/if_lua.obj: $(OUTDIR) if_lua.c $(INCL) + $(CC) $(CFLAGS_OUTDIR) $(LUA_INC) if_lua.c + +auto/if_perl.c : if_perl.xs typemap + $(XSUBPP) -prototypes -typemap $(XSUBPP_TYPEMAP) \ + -typemap typemap if_perl.xs -output $@ + +$(OUTDIR)/if_perl.obj: $(OUTDIR) auto/if_perl.c $(INCL) + $(CC) $(CFLAGS_OUTDIR) $(PERL_INC) auto/if_perl.c + +$(OUTDIR)/if_perlsfio.obj: $(OUTDIR) if_perlsfio.c $(INCL) + $(CC) $(CFLAGS_OUTDIR) $(PERL_INC) if_perlsfio.c + +$(OUTDIR)/if_mzsch.obj: $(OUTDIR) if_mzsch.c $(MZSCHEME_INCL) $(INCL) $(MZSCHEME_EXTRA_DEP) + $(CC) $(CFLAGS_OUTDIR) if_mzsch.c \ + -DMZSCHEME_COLLECTS="\"$(MZSCHEME_COLLECTS:\=\\)\"" + +lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).lib: + lib /DEF:"$(MZSCHEME)\lib\lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).def" + +$(OUTDIR)/if_python.obj: $(OUTDIR) if_python.c if_py_both.h $(INCL) + $(CC) $(CFLAGS_OUTDIR) $(PYTHON_INC) if_python.c + +$(OUTDIR)/if_python3.obj: $(OUTDIR) if_python3.c if_py_both.h $(INCL) + $(CC) $(CFLAGS_OUTDIR) $(PYTHON3_INC) if_python3.c + +$(OUTDIR)/if_ole.obj: $(OUTDIR) if_ole.cpp $(INCL) if_ole.h + +$(OUTDIR)/if_ruby.obj: $(OUTDIR) if_ruby.c $(INCL) + $(CC) $(CFLAGS_OUTDIR) $(RUBY_INC) if_ruby.c + +$(OUTDIR)/if_tcl.obj: $(OUTDIR) if_tcl.c $(INCL) + $(CC) $(CFLAGS_OUTDIR) $(TCL_INC) if_tcl.c + +$(OUTDIR)/iscygpty.obj: $(OUTDIR) iscygpty.c $(CUI_INCL) + $(CC) $(CFLAGS_OUTDIR) iscygpty.c -D_WIN32_WINNT=0x0600 -DUSE_DYNFILEID -DENABLE_STUB_IMPL + +$(OUTDIR)/json.obj: $(OUTDIR) json.c $(INCL) + +$(OUTDIR)/list.obj: $(OUTDIR) list.c $(INCL) + +$(OUTDIR)/main.obj: $(OUTDIR) main.c $(INCL) $(CUI_INCL) + +$(OUTDIR)/mark.obj: $(OUTDIR) mark.c $(INCL) + +$(OUTDIR)/memfile.obj: $(OUTDIR) memfile.c $(INCL) + +$(OUTDIR)/memline.obj: $(OUTDIR) memline.c $(INCL) + +$(OUTDIR)/menu.obj: $(OUTDIR) menu.c $(INCL) + +$(OUTDIR)/message.obj: $(OUTDIR) message.c $(INCL) + +$(OUTDIR)/misc1.obj: $(OUTDIR) misc1.c $(INCL) + +$(OUTDIR)/misc2.obj: $(OUTDIR) misc2.c $(INCL) + +$(OUTDIR)/move.obj: $(OUTDIR) move.c $(INCL) + +$(OUTDIR)/mbyte.obj: $(OUTDIR) mbyte.c $(INCL) + +$(OUTDIR)/netbeans.obj: $(OUTDIR) netbeans.c $(NBDEBUG_SRC) $(INCL) + +$(OUTDIR)/channel.obj: $(OUTDIR) channel.c $(INCL) + +$(OUTDIR)/normal.obj: $(OUTDIR) normal.c $(INCL) + +$(OUTDIR)/option.obj: $(OUTDIR) option.c $(INCL) + +$(OUTDIR)/ops.obj: $(OUTDIR) ops.c $(INCL) + +$(OUTDIR)/os_mswin.obj: $(OUTDIR) os_mswin.c $(INCL) + +$(OUTDIR)/terminal.obj: $(OUTDIR) terminal.c $(INCL) $(TERM_DEPS) + +$(OUTDIR)/winclip.obj: $(OUTDIR) winclip.c $(INCL) + +$(OUTDIR)/os_win32.obj: $(OUTDIR) os_win32.c $(INCL) $(MZSCHEME_INCL) + +$(OUTDIR)/os_w32exe.obj: $(OUTDIR) os_w32exe.c $(INCL) + +$(OUTDIR)/pathdef.obj: $(OUTDIR) $(PATHDEF_SRC) $(INCL) + $(CC) $(CFLAGS_OUTDIR) $(PATHDEF_SRC) + +$(OUTDIR)/popupmnu.obj: $(OUTDIR) popupmnu.c $(INCL) + +$(OUTDIR)/quickfix.obj: $(OUTDIR) quickfix.c $(INCL) + +$(OUTDIR)/regexp.obj: $(OUTDIR) regexp.c regexp_nfa.c $(INCL) + +$(OUTDIR)/screen.obj: $(OUTDIR) screen.c $(INCL) + +$(OUTDIR)/search.obj: $(OUTDIR) search.c $(INCL) + +$(OUTDIR)/sha256.obj: $(OUTDIR) sha256.c $(INCL) + +$(OUTDIR)/sign.obj: $(OUTDIR) sign.c $(INCL) + +$(OUTDIR)/spell.obj: $(OUTDIR) spell.c $(INCL) + +$(OUTDIR)/spellfile.obj: $(OUTDIR) spellfile.c $(INCL) + +$(OUTDIR)/syntax.obj: $(OUTDIR) syntax.c $(INCL) + +$(OUTDIR)/tag.obj: $(OUTDIR) tag.c $(INCL) + +$(OUTDIR)/term.obj: $(OUTDIR) term.c $(INCL) + +$(OUTDIR)/textprop.obj: $(OUTDIR) textprop.c $(INCL) + +$(OUTDIR)/ui.obj: $(OUTDIR) ui.c $(INCL) + +$(OUTDIR)/undo.obj: $(OUTDIR) undo.c $(INCL) + +$(OUTDIR)/userfunc.obj: $(OUTDIR) userfunc.c $(INCL) + +$(OUTDIR)/window.obj: $(OUTDIR) window.c $(INCL) + +$(OUTDIR)/xpm_w32.obj: $(OUTDIR) xpm_w32.c + $(CC) $(CFLAGS_OUTDIR) $(XPM_INC) xpm_w32.c + +$(OUTDIR)/vim.res: $(OUTDIR) vim.rc gvim.exe.mnf version.h tools.bmp \ + tearoff.bmp vim.ico vim_error.ico \ + vim_alert.ico vim_info.ico vim_quest.ico + $(RC) /nologo /l 0x409 /Fo$(OUTDIR)/vim.res $(RCFLAGS) vim.rc + +iid_ole.c if_ole.h vim.tlb: if_ole.idl + midl /nologo /error none /proxy nul /iid iid_ole.c /tlb vim.tlb \ + /header if_ole.h if_ole.idl + +dimm.h dimm_i.c: dimm.idl + midl /nologo /error none /proxy nul dimm.idl + +$(OUTDIR)/dimm_i.obj: $(OUTDIR) dimm_i.c $(INCL) + +$(OUTDIR)/glbl_ime.obj: $(OUTDIR) glbl_ime.cpp dimm.h $(INCL) + + +CCCTERM = $(CC) $(CFLAGS) -Ilibvterm/include -DINLINE="" \ + -DVSNPRINTF=vim_vsnprintf \ + -DIS_COMBINING_FUNCTION=utf_iscomposing_uint \ + -DWCWIDTH_FUNCTION=utf_uint2cells \ + -D_CRT_SECURE_NO_WARNINGS + +$(OUTDIR)/encoding.obj: $(OUTDIR) libvterm/src/encoding.c $(TERM_DEPS) + $(CCCTERM) -Fo$@ libvterm/src/encoding.c + +$(OUTDIR)/keyboard.obj: $(OUTDIR) libvterm/src/keyboard.c $(TERM_DEPS) + $(CCCTERM) -Fo$@ libvterm/src/keyboard.c + +$(OUTDIR)/mouse.obj: $(OUTDIR) libvterm/src/mouse.c $(TERM_DEPS) + $(CCCTERM) -Fo$@ libvterm/src/mouse.c + +$(OUTDIR)/parser.obj: $(OUTDIR) libvterm/src/parser.c $(TERM_DEPS) + $(CCCTERM) -Fo$@ libvterm/src/parser.c + +$(OUTDIR)/pen.obj: $(OUTDIR) libvterm/src/pen.c $(TERM_DEPS) + $(CCCTERM) -Fo$@ libvterm/src/pen.c + +$(OUTDIR)/termscreen.obj: $(OUTDIR) libvterm/src/termscreen.c $(TERM_DEPS) + $(CCCTERM) -Fo$@ libvterm/src/termscreen.c + +$(OUTDIR)/state.obj: $(OUTDIR) libvterm/src/state.c $(TERM_DEPS) + $(CCCTERM) -Fo$@ libvterm/src/state.c + +$(OUTDIR)/unicode.obj: $(OUTDIR) libvterm/src/unicode.c $(TERM_DEPS) + $(CCCTERM) -Fo$@ libvterm/src/unicode.c + +$(OUTDIR)/vterm.obj: $(OUTDIR) libvterm/src/vterm.c $(TERM_DEPS) + $(CCCTERM) -Fo$@ libvterm/src/vterm.c + + +# $CFLAGS may contain backslashes and double quotes, escape them both. +E0_CFLAGS = $(CFLAGS:\=\\) +E_CFLAGS = $(E0_CFLAGS:"=\") +# ") stop the string +# $LINKARGS2 may contain backslashes and double quotes, escape them both. +E0_LINKARGS2 = $(LINKARGS2:\=\\) +E_LINKARGS2 = $(E0_LINKARGS2:"=\") +# ") stop the string + +$(PATHDEF_SRC): auto + @echo creating $(PATHDEF_SRC) + @echo /* pathdef.c */ > $(PATHDEF_SRC) + @echo #include "vim.h" >> $(PATHDEF_SRC) + @echo char_u *default_vim_dir = (char_u *)"$(VIMRCLOC:\=\\)"; >> $(PATHDEF_SRC) + @echo char_u *default_vimruntime_dir = (char_u *)"$(VIMRUNTIMEDIR:\=\\)"; >> $(PATHDEF_SRC) + @echo char_u *all_cflags = (char_u *)"$(CC:\=\\) $(E_CFLAGS)"; >> $(PATHDEF_SRC) + @echo char_u *all_lflags = (char_u *)"$(link:\=\\) $(LINKARGS1:\=\\) $(E_LINKARGS2)"; >> $(PATHDEF_SRC) + @echo char_u *compiled_user = (char_u *)"$(USERNAME)"; >> $(PATHDEF_SRC) + @echo char_u *compiled_sys = (char_u *)"$(USERDOMAIN)"; >> $(PATHDEF_SRC) + +auto: + if not exist auto/nul mkdir auto + +# End Custom Build +proto.h: \ + proto/arabic.pro \ + proto/autocmd.pro \ + proto/blob.pro \ + proto/blowfish.pro \ + proto/buffer.pro \ + proto/charset.pro \ + proto/crypt.pro \ + proto/crypt_zip.pro \ + proto/dict.pro \ + proto/diff.pro \ + proto/digraph.pro \ + proto/edit.pro \ + proto/eval.pro \ + proto/evalfunc.pro \ + proto/ex_cmds.pro \ + proto/ex_cmds2.pro \ + proto/ex_docmd.pro \ + proto/ex_eval.pro \ + proto/ex_getln.pro \ + proto/farsi.pro \ + proto/fileio.pro \ + proto/getchar.pro \ + proto/hardcopy.pro \ + proto/hashtab.pro \ + proto/indent.pro \ + proto/json.pro \ + proto/list.pro \ + proto/main.pro \ + proto/mark.pro \ + proto/memfile.pro \ + proto/memline.pro \ + proto/menu.pro \ + proto/message.pro \ + proto/misc1.pro \ + proto/misc2.pro \ + proto/move.pro \ + proto/mbyte.pro \ + proto/normal.pro \ + proto/ops.pro \ + proto/option.pro \ + proto/os_mswin.pro \ + proto/winclip.pro \ + proto/os_win32.pro \ + proto/popupmnu.pro \ + proto/quickfix.pro \ + proto/regexp.pro \ + proto/screen.pro \ + proto/search.pro \ + proto/sha256.pro \ + proto/sign.pro \ + proto/spell.pro \ + proto/spellfile.pro \ + proto/syntax.pro \ + proto/tag.pro \ + proto/term.pro \ + proto/textprop.pro \ + proto/ui.pro \ + proto/undo.pro \ + proto/userfunc.pro \ + proto/window.pro \ + $(NETBEANS_PRO) \ + $(CHANNEL_PRO) + +.SUFFIXES: .cod .i + +# Generate foo.cod (mixed source and assembly listing) from foo.c via "nmake +# foo.cod" +.c.cod: + $(CC) $(CFLAGS) /FAcs $< + +# Generate foo.i (preprocessor listing) from foo.c via "nmake foo.i" +.c.i: + $(CC) $(CFLAGS) /P /C $< + +# vim: set noet sw=8 ts=8 sts=0 wm=0 tw=0: diff --git a/src/Make_sas.mak b/src/Make_sas.mak new file mode 100644 index 0000000..e7faf56 --- /dev/null +++ b/src/Make_sas.mak @@ -0,0 +1,440 @@ +# vim: set ft=make : +# Makefile for VIM on the Amiga, using SAS/Lattice C 6.0 to 6.58 +# +# Do NOT use the peephole optimizer with a version before 6.56! +# It messes up all kinds of things: +# For 6.0 and 6.1, expand_env() will not work correctly. +# For 6.2 and 6.3 the call to free_line in u_freeentry is wrong. +# The "read.me" file for version 6.56 includes a remark about a fix for the +# peephole optimizer. Everything before 6.56 will probably fail. +# +# You should use Manx Aztec C whenever possible, because it has been tested. +# +# The prototypes from Manx and SAS are incompatible. If the prototypes +# were generated by Manx, first do "touch *.c; make proto" before "make". +# The prototypes generated on Unix work for both. +# +# Note: Not all dependencies are included. This was done to avoid having +# to compile everything when a global variable or function is added. + +#>>>>> choose options: + +### See feature.h for a list of optionals. +### Any other defines can be included here. + +# NO_ARP Don't include ARP functions +# SASC=658 Sas/C version number +# NEWSASC fixes a bug in the syntax highlighting? +DEFINES = DEF=NO_ARP DEF=NEWSASC DEF="SASC=658" + +#>>>>> if HAVE_TGETENT is defined termlib.o has to be used +#TERMLIB = termlib.o +TERMLIB = + +#>>>>> choose NODEBUG for normal compiling, the other for debugging and +# profiling +# don't switch on debugging when generating proto files, it crashes the +# compiler. +DBG = NODEBUG +#DBG = DBG=SF + +#>>>>> choose NOOPTPEEP for 6.0 to 6.3, NOOPT for debugging +# with version 6.56 and later you can probably use OPT +OPTIMIZE = OPT +#OPTIMIZE = NOOPTPEEP +#OPTIMIZE = NOOPT + +# for 6.58 you can use the line below, but be warned it takes a loooonnnggg time +#OPTIMIZE=OPT OPTIMIZERSCHEDULER OPTIMIZERTIME NoOPTIMIZERALIAS \ + OptimizerComplexity=10 OptimizerDepth=10 OptimizerRecurDepth=10 \ + OptimizerInLocal OPTPEEP + +#generate code for your processor - 68060 will work for 040's as well. +CPU=68000 +#CPU=68020 +#CPU=68030 +#CPU=68040 +#CPU=68060 + +#Error reporting - rexx or console +ERROR = ERRORCONSOLE ERRORSOURCE ERRORHIGHLIGHT +#ERROR = ERRORREXX ERRORCONSOLE ERRORSOURCE ERRORHIGHLIGHT + +#memory types, if you have fast use it :->, +# ANY = will work on all machines +# FAST = this is the best option, for speed +#MEMORYTYPE=FAST +MEMORYTYPE=ANY + +#MEMSIZE - this is for compile time only for speed of compilation +#MEMSIZE=HUGE +MEMSIZE=LARGE +#MEMSIZE=SMALL + +#>>>>> end of choices +########################################################################### + +CC = sc +GST = vim.gst +COPTS = SINT SCODE SDATA +SHELL = csh +DEL = $(SHELL) -c rm -f + +# ignore error messages for uninitialized variables, they are mostly not correct +CFLAGS = NOLINK $(DBG) CPU=$(CPU) NOSTACKCHECK DEF=AMIGA CODE=FAR idir=proto ignore=317 +CFLAGS2 = $(OPTIMIZE) $(ERROR) GSTIMMEDIATE GST=$(GST) +CFLAGS3 = $(COPTS) STRINGMERGE MEMSIZE=$(MEMSIZE) +CFLAGS4 = $(DEFINES) DATAMEMORY=$(MEMORYTYPE) + +PROPT = DEF=PROTO GPROTO GPPARM MAXIMUMERRORS=999 GENPROTOSTATICS GENPROTOPARAMETERS + +SRC = \ + arabic.c \ + autocmd.c \ + blowfish.c \ + buffer.c \ + charset.c \ + crypt.c \ + crypt_zip.c \ + dict.c \ + diff.c \ + digraph.c \ + edit.c \ + eval.c \ + evalfunc.c \ + ex_cmds.c \ + ex_cmds2.c \ + ex_docmd.c \ + ex_eval.c \ + ex_getln.c \ + farsi.c \ + fileio.c \ + fold.c \ + getchar.c \ + hardcopy.c \ + hashtab.c \ + indent.c \ + json.c \ + list.c \ + main.c \ + mark.c \ + memfile.c \ + memline.c \ + menu.c \ + message.c \ + misc1.c \ + misc2.c \ + move.c \ + mbyte.c \ + normal.c \ + ops.c \ + option.c \ + os_amiga.c \ + popupmnu.c \ + quickfix.c \ + regexp.c \ + screen.c \ + search.c \ + sha256.c \ + sign.c \ + spell.c \ + spellfile.c \ + syntax.c \ + tag.c \ + term.c \ + ui.c \ + undo.c \ + userfunc.c \ + window.c \ + version.c + +OBJ = \ + arabic.o \ + autocmd.o \ + blowfish.o \ + buffer.o \ + charset.o \ + crypt.o \ + crypt_zip.o \ + dict.o \ + diff.o \ + digraph.o \ + edit.o \ + eval.o \ + evalfunc.o \ + ex_cmds.o \ + ex_cmds2.o \ + ex_docmd.o \ + ex_eval.o \ + ex_getln.o \ + farsi.o \ + fileio.o \ + fold.o \ + getchar.o \ + hardcopy.o \ + hashtab.o \ + indent.o \ + json.o \ + list.o \ + main.o \ + mark.o \ + memfile.o \ + memline.o \ + menu.o \ + message.o \ + misc1.o \ + misc2.o \ + move.o \ + mbyte.o \ + normal.o \ + ops.o \ + option.o \ + os_amiga.o \ + popupmnu.o \ + quickfix.o \ + regexp.o \ + screen.o \ + search.o \ + sha256.o \ + sign.o \ + spell.o \ + spellfile.o \ + syntax.o \ + tag.o \ + term.o \ + ui.o \ + undo.o \ + userfunc.o \ + window.o \ + $(TERMLIB) + +PRO = \ + proto/arabic.pro \ + proto/autocmd.pro \ + proto/blowfish.pro \ + proto/buffer.pro \ + proto/charset.pro \ + proto/crypt.pro \ + proto/crypt_zip.pro \ + proto/dict.pro \ + proto/diff.pro \ + proto/digraph.pro \ + proto/edit.pro \ + proto/eval.pro \ + proto/evalfunc.pro \ + proto/ex_cmds.pro \ + proto/ex_cmds2.pro \ + proto/ex_docmd.pro \ + proto/ex_eval.pro \ + proto/ex_getln.pro \ + proto/farsi.pro \ + proto/fileio.pro \ + proto/fold.pro \ + proto/getchar.pro \ + proto/hardcopy.pro \ + proto/hashtab.pro \ + proto/indent.pro \ + proto/json.pro \ + proto/list.pro \ + proto/main.pro \ + proto/mark.pro \ + proto/memfile.pro \ + proto/memline.pro \ + proto/menu.pro \ + proto/message.pro \ + proto/misc1.pro \ + proto/misc2.pro \ + proto/move.pro \ + proto/mbyte.pro \ + proto/normal.pro \ + proto/ops.pro \ + proto/option.pro \ + proto/os_amiga.pro \ + proto/popupmnu.pro \ + proto/quickfix.pro \ + proto/regexp.pro \ + proto/screen.pro \ + proto/search.pro \ + proto/sha256.pro \ + proto/sign.pro \ + proto/spell.pro \ + proto/spellfile.pro \ + proto/syntax.pro \ + proto/tag.pro \ + proto/term.pro \ + proto/termlib.pro \ + proto/ui.pro \ + proto/undo.pro \ + proto/userfunc.pro \ + proto/window.pro + +all: proto Vim + +Vim: scoptions $(OBJ) version.c version.h + $(CC) $(CFLAGS) version.c + $(CC) LINK $(COPTS) $(OBJ) version.o $(DBG) PNAME=Vim + +debug: scoptions $(OBJ) version.c version.h + $(CC) $(CFLAGS) version.c + $(CC) LINK $(COPTS) $(OBJ) version.o $(DBG) PNAME=Vim + +proto: $(GST) $(PRO) + +tags: + spat ctags $(SRC) *.h +# csh -c ctags $(SRC) *.h + +# can't use delete here, too many file names +clean: + $(DEL) *.o Vim $(GST) + +# generate GlobalSymbolTable, which speeds up the compile time. +# +# A preprocessing stage is used to work around a bug in the GST generator, in +# that it does not handle nested makefiles properly in this stage. +# Ignore error message for not producing any code (105). +$(GST): scoptions vim.h keymap.h macros.h ascii.h term.h structs.h + $(CC) $(CFLAGS) PREPROCESSORONLY vim.h objectname pre.h + $(CC) MGST=$(GST) pre.h ignore=105 + $(DEL) pre.h + +# generate an options file, because SAS/C smake can't handle the amiga command +# line can handle the lengths that this makefile will impose on the shell. +# (Manx's make can do this). +scoptions: Make_sas.mak + @echo "Generating - $@ ..." + @echo $(CFLAGS) > scoptions + @echo $(CFLAGS1) >> scoptions + @echo $(CFLAGS2) >> scoptions + @echo $(CFLAGS3) >> scoptions + @echo $(CFLAGS4) >> scoptions + @echo $(COPTS) >>scoptions + @echo done + +########################################################################### + +$(OBJ): $(GST) vim.h +$(PRO): $(GST) vim.h + +.c.o: + $(CC) $(CFLAGS) $*.c + +.c.pro: + $(CC) $(CFLAGS) GPFILE=proto/$*.pro $(PROPT) $*.c + +# dependencies +arabic.o: arabic.c +proto/arabic.pro: arabic.c +autocmd.o: autocmd.c +proto/autocmd.pro: autocmd.c +blowfish.o: blowfish.c +proto/blowfish.pro: blowfish.c +buffer.o: buffer.c +proto/buffer.pro: buffer.c +charset.o: charset.c +proto/charset.pro: charset.c +crypt.o: crypt.c +proto/crypt.pro: crypt.c +crypt_zip.o: crypt_zip.c +proto/crypt_zip.pro: crypt_zip.c +dict.o: dict.c +proto/dict.pro: dict.c +diff.o: diff.c +proto/diff.pro: diff.c +digraph.o: digraph.c +proto/digraph.pro: digraph.c +edit.o: edit.c +proto/edit.pro: edit.c +eval.o: eval.c +proto/eval.pro: eval.c +evalfunc.o: evalfunc.c +proto/evalfunc.pro: evalfunc.c +ex_cmds.o: ex_cmds.c +proto/ex_cmds.pro: ex_cmds.c +ex_cmds2.o: ex_cmds2.c +proto/ex_cmds2.pro: ex_cmds2.c +ex_docmd.o: ex_docmd.c ex_cmds.h +proto/ex_docmd.pro: ex_docmd.c ex_cmds.h +ex_eval.o: ex_eval.c ex_cmds.h +proto/ex_eval.pro: ex_eval.c ex_cmds.h +ex_getln.o: ex_getln.c +proto/ex_getln.pro: ex_getln.c +farsi.o: farsi.c +proto/farsi.pro: farsi.c +fileio.o: fileio.c +proto/fileio.pro: fileio.c +fold.o: fold.c +proto/fold.pro: fold.c +getchar.o: getchar.c +proto/getchar.pro: getchar.c +hardcopy.o: hardcopy.c +proto/hardcopy.pro: hardcopy.c +hashtab.o: hashtab.c +proto/hashtab.pro: hashtab.c +indent.o: indent.c +proto/indent.pro: indent.c +json.o: json.c +proto/json.pro: json.c +list.o: list.c +proto/list.pro: list.c +main.o: main.c +proto/main.pro: main.c +mark.o: mark.c +proto/mark.pro: mark.c +memfile.o: memfile.c +proto/memfile.pro: memfile.c +memline.o: memline.c +proto/memline.pro: memline.c +menu.o: menu.c +proto/menu.pro: menu.c +message.o: message.c +proto/message.pro: message.c +misc1.o: misc1.c +proto/misc1.pro: misc1.c +misc2.o: misc2.c +proto/misc2.pro: misc2.c +move.o: move.c +proto/move.pro: move.c +mbyte.o: mbyte.c +proto/mbyte.pro: mbyte.c +normal.o: normal.c +proto/normal.pro: normal.c +ops.o: ops.c +proto/ops.pro: ops.c +option.o: option.c +proto/option.pro: option.c +os_amiga.o: os_amiga.c +proto/os_amiga.pro: os_amiga.c +popupmnu.o: popupmnu.c +proto/popupmnu.pro: popupmnu.c +quickfix.o: quickfix.c +proto/quickfix.pro: quickfix.c +regexp.o: regexp.c +proto/regexp.pro: regexp.c +screen.o: screen.c +proto/screen.pro: screen.c +search.o: search.c +proto/search.pro: search.c +sha256.o: sha256.c +proto/sha256.pro: sha256.c +sign.o: sign.c +proto/sign.pro: sign.c +spell.o: spell.c +proto/spell.pro: spell.c +spellfile.o: spellfile.c +proto/spellfile.pro: spellfile.c +syntax.o: syntax.c +proto/syntax.pro: syntax.c +tag.o: tag.c +proto/tag.pro: tag.c +term.o: term.c +proto/term.pro: term.c +termlib.o: termlib.c +proto/termlib.pro: termlib.c +ui.o: ui.c +proto/ui.pro: ui.c +undo.o: undo.c +proto/undo.pro: undo.c +userfunc.o: userfunc.c +proto/userfunc.pro: userfunc.c +window.o: window.c diff --git a/src/Make_vms.mms b/src/Make_vms.mms new file mode 100644 index 0000000..1a21d15 --- /dev/null +++ b/src/Make_vms.mms @@ -0,0 +1,858 @@ +# +# Makefile for Vim on OpenVMS +# +# Maintainer: Zoltan Arpadffy +# Last change: 2019 Jan 18 +# +# This has script been tested on VMS 6.2 to 8.2 on DEC Alpha, VAX and IA64 +# with MMS and MMK +# +# The following could be built: +# vim.exe: standard (terminal, GUI/Motif, GUI/GTK) +# dvim.exe: debug +# +# Edit the lines in the Configuration section below for fine tuning. +# +# To build: mms/descrip=Make_vms.mms /ignore=warning +# To clean up: mms/descrip=Make_vms.mms clean +# +# Hints and detailed description could be found in INSTALLVMS.TXT file. +# +###################################################################### +# Configuration section. +###################################################################### + +# Compiler selection. +# Comment out if you use the VAXC compiler +DECC = YES + +# Build model selection +# TINY - Almost no features enabled, not even multiple windows +# SMALL - Few features enabled, as basic as possible +# NORMAL - A default selection of features enabled +# BIG - Many features enabled, as rich as possible. (default) +# HUGE - All possible features enabled. +# Please select one of these alternatives above. +MODEL = HUGE + +# GUI or terminal mode executable. +# Comment out if you want just the character terminal mode only. +# GUI with Motif +GUI = YES + +# GUI with GTK +# If you have GTK installed you might want to enable this option. +# NOTE: you will need to properly define GTK_DIR below +# NOTE: since Vim 7.3 GTK 2+ is used that is not ported to VMS, +# therefore this option should not be used +# GTK = YES + +# GUI/Motif with XPM +# If you have XPM installed you might want to build Motif version with toolbar +# XPM = YES + +# Comment out if you want the compiler version with :ver command. +# NOTE: This part can make some complications if you're using some +# predefined symbols/flags for your compiler. If does, just leave behind +# the comment variable CCVER. +CCVER = YES + +# Uncomment if want a debug version. Resulting executable is DVIM.EXE +# Development purpose only! Normally, it should not be defined. !!! +# DEBUG = YES + +# Languages support for Perl, Python, TCL etc. +# If you don't need it really, leave them behind the comment. +# You will need related libraries, include files etc. +# VIM_TCL = YES +# VIM_PERL = YES +# VIM_PYTHON = YES +# VIM_RUBY = YES + +# X Input Method. For entering special languages like chinese and +# Japanese. Please define just one: VIM_XIM or VIM_HANGULIN +# If you don't need it really, leave it behind the comment. +# VIM_XIM = YES + +# Internal Hangul input method. GUI only. +# If you don't need it really, leave it behind the comment. +# VIM_HANGULIN = YES + +# Allow any white space to separate the fields in a tags file +# When not defined, only a TAB is allowed. +# VIM_TAG_ANYWHITE = YES + +# Allow FEATURE_MZSCHEME +# VIM_MZSCHEME = YES + +# Use ICONV +# VIM_ICONV = YES + +###################################################################### +# Directory, library and include files configuration section. +# Normally you need not to change anything below. ! +# These may need to be defined if things are not in standard locations +# +# You can find some explanation in INSTALLVMS.TXT +###################################################################### + +# Compiler setup + +.IFDEF MMSVAX +.IFDEF DECC # VAX with DECC +CC_DEF = cc # /decc # some versions require /decc switch but when it is not required /ver might fail +PREFIX = /prefix=all +OPTIMIZE= /noopt # do not optimize on VAX. The compiler has hard time with crypto functions +.ELSE # VAX with VAXC +CC_DEF = cc +PREFIX = +OPTIMIZE= /noopt +CCVER = +.ENDIF +.ELSE # AXP and IA64 with DECC +CC_DEF = cc +PREFIX = /prefix=all +OPTIMIZE= /opt +.ENDIF + + +LD_DEF = link +C_INC = [.proto] + +.IFDEF DEBUG +DEBUG_DEF = ,"DEBUG" +TARGET = dvim.exe +CFLAGS = /debug/noopt$(PREFIX) +LDFLAGS = /debug +.ELSE +TARGET = vim.exe +CFLAGS = $(OPTIMIZE)$(PREFIX) +LDFLAGS = +.ENDIF + +# Predefined VIM directories +# Please, use $VIM and $VIMRUNTIME logicals instead +VIMLOC = "" +VIMRUN = "" + +CONFIG_H = os_vms_conf.h + +# GTK or XPM but not both +.IFDEF GTK +.IFDEF GUI +.ELSE +GUI = YES +.ENDIF +.IFDEF XPM +XPM = "" +.ENDIF +.ENDIF + +.IFDEF XPM +.IFDEF GUI +.ELSE +GUI = YES +.ENDIF +.IFDEF GTK +GTK = "" +.ENDIF +.ENDIF + +.IFDEF GUI +# X/Motif/GTK executable (also works in terminal mode ) + +.IFDEF GTK +# NOTE: you need to set up your GTK_DIR (GTK root directory), because it is +# unique on every system - logicals are not accepted +# please note: directory should end with . in order to /trans=conc work +# This value for GTK_DIR is an example. +GTK_DIR = DKA0:[WORK.GTK1210.] +DEFS = "HAVE_CONFIG_H","FEAT_GUI_GTK" +LIBS = ,OS_VMS_GTK.OPT/OPT +GUI_FLAG = /name=(as_is,short)/float=ieee/ieee=denorm +GUI_SRC = gui.c gui_gtk.c gui_gtk_f.c gui_gtk_x11.c gui_beval.c pty.c +GUI_OBJ = gui.obj gui_gtk.obj gui_gtk_f.obj gui_gtk_x11.obj gui_beval.obj pty.obj +GUI_INC = ,"/gtk_root/gtk","/gtk_root/glib" +# GUI_INC_VER is used just for :ver information +# this string should escape from C and DCL in the same time +GUI_INC_VER= ,\""/gtk_root/gtk\"",\""/gtk_root/glib\"" +.ELSE +MOTIF = YES +.IFDEF XPM +DEFS = "HAVE_CONFIG_H","FEAT_GUI_MOTIF","HAVE_XPM" +XPM_INC = ,[.xpm.include] +.ELSE +DEFS = "HAVE_CONFIG_H","FEAT_GUI_MOTIF" +XPM_INC = +.ENDIF +LIBS = ,OS_VMS_MOTIF.OPT/OPT +GUI_FLAG = +GUI_SRC = gui.c gui_motif.c gui_x11.c gui_beval.c gui_xmdlg.c gui_xmebw.c +GUI_OBJ = gui.obj gui_motif.obj gui_x11.obj gui_beval.obj gui_xmdlg.obj gui_xmebw.obj +GUI_INC = +.ENDIF + +# You need to define these variables if you do not have DECW files +# at standard location +GUI_INC_DIR = ,decw$include: +# GUI_LIB_DIR = ,sys$library: + +.ELSE +# Character terminal only executable +DEFS = "HAVE_CONFIG_H" +LIBS = +.ENDIF + +.IFDEF VIM_PERL +# Perl related setup. +PERL = perl +PERL_DEF = ,"FEAT_PERL" +PERL_SRC = if_perlsfio.c if_perl.xs +PERL_OBJ = if_perlsfio.obj if_perl.obj +PERL_LIB = ,OS_VMS_PERL.OPT/OPT +PERL_INC = ,dka0:[perlbuild.perl.lib.vms_axp.5_6_1.core] +.ENDIF + +.IFDEF VIM_PYTHON +# Python related setup. +PYTHON_DEF = ,"FEAT_PYTHON" +PYTHON_SRC = if_python.c +PYTHON_OBJ = if_python.obj +PYTHON_LIB = ,OS_VMS_PYTHON.OPT/OPT +PYTHON_INC = ,PYTHON_INCLUDE +.ENDIF + +.IFDEF VIM_TCL +# TCL related setup. +TCL_DEF = ,"FEAT_TCL" +TCL_SRC = if_tcl.c +TCL_OBJ = if_tcl.obj +TCL_LIB = ,OS_VMS_TCL.OPT/OPT +TCL_INC = ,dka0:[tcl80.generic] +.ENDIF + +.IFDEF VIM_RUBY +# RUBY related setup. +RUBY_DEF = ,"FEAT_RUBY" +RUBY_SRC = if_ruby.c +RUBY_OBJ = if_ruby.obj +RUBY_LIB = ,OS_VMS_RUBY.OPT/OPT +RUBY_INC = +.ENDIF + +.IFDEF VIM_XIM +# XIM related setup. +.IFDEF GUI +XIM_DEF = ,"FEAT_XIM" +.ENDIF +.ENDIF + +.IFDEF VIM_HANGULIN +# HANGULIN related setup. +.IFDEF GUI +HANGULIN_DEF = ,"FEAT_HANGULIN" +HANGULIN_SRC = hangulin.c +HANGULIN_OBJ = hangulin.obj +.ENDIF +.ENDIF + +.IFDEF VIM_TAG_ANYWHITE +# TAG_ANYWHITE related setup. +TAG_DEF = ,"FEAT_TAG_ANYWHITE" +.ENDIF + +.IFDEF VIM_MZSCHEME +# MZSCHEME related setup +MZSCH_DEF = ,"FEAT_MZSCHEME" +MZSCH_SRC = if_mzsch.c +MZSCH_OBJ = if_mzsch.obj +.ENDIF + +.IFDEF VIM_ICONV +# ICONV related setup +ICONV_DEF = ,"USE_ICONV" +.ENDIF + +# XDIFF related setup. +XDIFF_SRC = xdiffi.c,xemit.c,xprepare.c,xutils.c,xhistogram.c,xpatience.c +XDIFF_OBJ = xdiffi.obj,xemit.obj,xprepare.obj,xutils.obj,xhistogram.obj,xpatience.obj +XDIFF_INC = ,[.xdiff] + +###################################################################### +# End of configuration section. +# Please, do not change anything below without programming experience. +###################################################################### + +MODEL_DEF = "FEAT_$(MODEL)", + +# These go into pathdef.c +VIMUSER = "''F$EDIT(F$GETJPI(" ","USERNAME"),"TRIM")'" +VIMHOST = "''F$TRNLNM("SYS$NODE")'''F$TRNLNM("UCX$INET_HOST")'.''F$TRNLNM("UCX$INET_DOMAIN")'" + +.SUFFIXES : .obj .c + +ALL_CFLAGS = /def=($(MODEL_DEF)$(DEFS)$(DEBUG_DEF)$(PERL_DEF)$(PYTHON_DEF) - + $(TCL_DEF)$(RUBY_DEF)$(XIM_DEF)$(HANGULIN_DEF)$(TAG_DEF)$(MZSCH_DEF) - + $(ICONV_DEF)) - + $(CFLAGS)$(GUI_FLAG) - + /include=($(C_INC)$(GUI_INC_DIR)$(GUI_INC)$(PERL_INC)$(PYTHON_INC) - + $(TCL_INC)$(XDIFF_INC)$(XPM_INC)) + +# CFLAGS displayed in :ver information +# It is specially formated for correct display of unix like includes +# as $(GUI_INC) - replaced with $(GUI_INC_VER) +# Otherwise should not be any other difference. +ALL_CFLAGS_VER = /def=($(MODEL_DEF)$(DEFS)$(DEBUG_DEF)$(PERL_DEF)$(PYTHON_DEF) - + $(TCL_DEF)$(RUBY_DEF)$(XIM_DEF)$(HANGULIN_DEF)$(TAG_DEF)$(MZSCH_DEF) - + $(ICONV_DEF)) - + $(CFLAGS)$(GUI_FLAG) - + /include=($(C_INC)$(GUI_INC_DIR)$(GUI_INC_VER)$(PERL_INC)$(PYTHON_INC) - + $(TCL_INC)$(XDIFF_INC)$(XPM_INC)) + +ALL_LIBS = $(LIBS) $(GUI_LIB_DIR) $(GUI_LIB) \ + $(PERL_LIB) $(PYTHON_LIB) $(TCL_LIB) $(RUBY_LIB) + +SRC = arabic.c autocmd.c beval.c blob.c blowfish.c buffer.c charset.c crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c \ + evalfunc.c ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c if_cscope.c if_xcmdsrv.c farsi.c fileio.c fold.c \ + getchar.c hardcopy.c hashtab.c indent.c json.c list.c main.c mark.c menu.c mbyte.c memfile.c memline.c message.c misc1.c \ + misc2.c move.c normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c sha256.c sign.c \ + spell.c spellfile.c syntax.c tag.c term.c termlib.c textprop.c ui.c undo.c userfunc.c version.c screen.c \ + window.c os_unix.c os_vms.c pathdef.c \ + $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \ + $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC) + +OBJ = arabic.obj autocmd.obj beval.obj blob.obj blowfish.obj buffer.obj charset.obj crypt.obj crypt_zip.obj dict.obj diff.obj digraph.obj \ + edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj ex_docmd.obj ex_eval.obj ex_getln.obj if_cscope.obj \ + if_xcmdsrv.obj farsi.obj fileio.obj fold.obj getchar.obj hardcopy.obj hashtab.obj indent.obj json.obj list.obj main.obj mark.obj \ + menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \ + move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj quickfix.obj \ + regexp.obj search.obj sha256.obj sign.obj spell.obj spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \ + ui.obj undo.obj userfunc.obj screen.obj version.obj window.obj os_unix.obj \ + os_vms.obj pathdef.obj if_mzsch.obj\ + $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ + $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) + +# Default target is making the executable +all : [.auto]config.h mmk_compat motif_env gtk_env perl_env python_env tcl_env ruby_env $(TARGET) + ! $@ + +[.auto]config.h : $(CONFIG_H) + copy/nolog $(CONFIG_H) [.auto]config.h + +mmk_compat : + -@ open/write pd pathdef.c + -@ write pd "/* Empty file to satisfy MMK depend. */" + -@ write pd "/* It will be overwritten later on... */" + -@ close pd +clean : + -@ if "''F$SEARCH("*.exe")'" .NES. "" then delete/noconfirm/nolog *.exe;* + -@ if "''F$SEARCH("*.obj")'" .NES. "" then delete/noconfirm/nolog *.obj;* + -@ if "''F$SEARCH("[.auto]config.h")'" .NES. "" then delete/noconfirm/nolog [.auto]config.h;* + -@ if "''F$SEARCH("pathdef.c")'" .NES. "" then delete/noconfirm/nolog pathdef.c;* + -@ if "''F$SEARCH("if_perl.c")'" .NES. "" then delete/noconfirm/nolog if_perl.c;* + -@ if "''F$SEARCH("*.opt")'" .NES. "" then delete/noconfirm/nolog *.opt;* + +# Link the target +$(TARGET) : $(OBJ) + $(LD_DEF) $(LDFLAGS) /exe=$(TARGET) $+ $(ALL_LIBS) + +.c.obj : + $(CC_DEF) $(ALL_CFLAGS) $< + +pathdef.c : check_ccver $(CONFIG_H) + -@ write sys$output "creating PATHDEF.C file." + -@ open/write pd pathdef.c + -@ write pd "/* pathdef.c -- DO NOT EDIT! */" + -@ write pd "/* This file is automatically created by MAKE_VMS.MMS" + -@ write pd " * Change the file MAKE_VMS.MMS Only. */" + -@ write pd "typedef unsigned char char_u;" + -@ write pd "char_u *default_vim_dir = (char_u *)"$(VIMLOC)";" + -@ write pd "char_u *default_vimruntime_dir = (char_u *)"$(VIMRUN)";" + -@ write pd "char_u *all_cflags = (char_u *)""$(CC_DEF)$(ALL_CFLAGS_VER)"";" + -@ write pd "char_u *all_lflags = (char_u *)""$(LD_DEF)$(LDFLAGS) /exe=$(TARGET) *.OBJ $(ALL_LIBS)"";" + -@ write pd "char_u *compiler_version = (char_u *) ""''CC_VER'"";" + -@ write pd "char_u *compiled_user = (char_u *) "$(VIMUSER)";" + -@ write pd "char_u *compiled_sys = (char_u *) "$(VIMHOST)";" + -@ write pd "char_u *compiled_arch = (char_u *) ""$(MMSARCH_NAME)"";" + -@ close pd + +if_perl.c : if_perl.xs + -@ $(PERL) PERL_ROOT:[LIB.ExtUtils]xsubpp -prototypes -typemap - + PERL_ROOT:[LIB.ExtUtils]typemap if_perl.xs >> $@ + +make_vms.mms : + -@ write sys$output "The name of the makefile MUST be !!!" + +.IFDEF CCVER +# This part can make some complications if you're using some predefined +# symbols/flags for your compiler. If does, just comment out CCVER variable +check_ccver : + -@ define sys$output cc_ver.tmp + -@ $(CC_DEF)/version + -@ deassign sys$output + -@ open/read file cc_ver.tmp + -@ read file CC_VER + -@ close file + -@ delete/noconfirm/nolog cc_ver.tmp.* +.ELSE +check_ccver : + -@ ! +.ENDIF + +.IFDEF MOTIF +motif_env : +.IFDEF XPM + -@ write sys$output "using DECW/Motif/XPM environment." +.ELSE + -@ write sys$output "using DECW/Motif environment." +.ENDIF + -@ write sys$output "creating OS_VMS_MOTIF.OPT file." + -@ open/write opt_file OS_VMS_MOTIF.OPT + -@ write opt_file "sys$share:decw$xmlibshr12.exe/share,-" + -@ write opt_file "sys$share:decw$xtlibshrr5.exe/share,-" + -@ write opt_file "sys$share:decw$xlibshr.exe/share" + -@ close opt_file +.ELSE +motif_env : + -@ ! +.ENDIF + + +.IFDEF GTK +gtk_env : + -@ write sys$output "using GTK environment:" + -@ define/nolog gtk_root /trans=conc $(GTK_DIR) + -@ show logical gtk_root + -@ write sys$output " include path: "$(GUI_INC)"" + -@ write sys$output "creating OS_VMS_GTK.OPT file." + -@ open/write opt_file OS_VMS_GTK.OPT + -@ write opt_file "gtk_root:[glib]libglib.exe /share,-" + -@ write opt_file "gtk_root:[glib.gmodule]libgmodule.exe /share,-" + -@ write opt_file "gtk_root:[gtk.gdk]libgdk.exe /share,-" + -@ write opt_file "gtk_root:[gtk.gtk]libgtk.exe /share,-" + -@ write opt_file "sys$share:decw$xmlibshr12.exe/share,-" + -@ write opt_file "sys$share:decw$xtlibshrr5.exe/share,-" + -@ write opt_file "sys$share:decw$xlibshr.exe/share" + -@ close opt_file +.ELSE +gtk_env : + -@ ! +.ENDIF + +.IFDEF VIM_PERL +perl_env : + -@ write sys$output "using PERL environment:" + -@ show logical PERLSHR + -@ write sys$output " include path: ""$(PERL_INC)""" + -@ show symbol perl + -@ open/write pd if_perl.c + -@ write pd "/* Empty file to satisfy MMK depend. */" + -@ write pd "/* It will be overwritten later on... */" + -@ close pd + -@ write sys$output "creating OS_VMS_PERL.OPT file." + -@ open/write opt_file OS_VMS_PERL.OPT + -@ write opt_file "PERLSHR /share" + -@ close opt_file +.ELSE +perl_env : + -@ ! +.ENDIF + +.IFDEF VIM_PYTHON +python_env : + -@ write sys$output "using PYTHON environment:" + -@ show logical PYTHON_INCLUDE + -@ show logical PYTHON_OLB + -@ write sys$output "creating OS_VMS_PYTHON.OPT file." + -@ open/write opt_file OS_VMS_PYTHON.OPT + -@ write opt_file "PYTHON_OLB:PYTHON.OLB /share" + -@ close opt_file +.ELSE +python_env : + -@ ! +.ENDIF + +.IFDEF VIM_TCL +tcl_env : + -@ write sys$output "using TCL environment:" + -@ show logical TCLSHR + -@ write sys$output " include path: ""$(TCL_INC)""" + -@ write sys$output "creating OS_VMS_TCL.OPT file." + -@ open/write opt_file OS_VMS_TCL.OPT + -@ write opt_file "TCLSHR /share" + -@ close opt_file +.ELSE +tcl_env : + -@ ! +.ENDIF + +.IFDEF VIM_RUBY +ruby_env : + -@ write sys$output "using RUBY environment:" + -@ write sys$output " include path: ""$(RUBY_INC)""" + -@ write sys$output "creating OS_VMS_RUBY.OPT file." + -@ open/write opt_file OS_VMS_RUBY.OPT + -@ write opt_file "RUBYSHR /share" + -@ close opt_file +.ELSE +ruby_env : + -@ ! +.ENDIF + +arabic.obj : arabic.c vim.h +autocmd.obj : autocmd.c vim.h [.auto]config.h feature.h os_unix.h +blowfish.obj : blowfish.c vim.h [.auto]config.h feature.h os_unix.h +blob.obj : blob.c vim.h [.auto]config.h feature.h os_unix.h +buffer.obj : buffer.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h version.h +charset.obj : charset.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +crypt.obj : crypt.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ + globals.h farsi.h arabic.h +crypt_zip.obj : crypt_zip.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h \ + regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +dict.obj : dict.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ + globals.h farsi.h arabic.h +diff.obj : diff.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +digraph.obj : digraph.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +edit.obj : edit.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +eval.obj : eval.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h version.h +evalfunc.obj : evalfunc.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h \ + regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +ex_cmds.obj : ex_cmds.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h version.h +ex_cmds2.obj : ex_cmds2.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h version.h +ex_docmd.obj : ex_docmd.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +ex_eval.obj : ex_eval.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +ex_getln.obj : ex_getln.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +farsi.obj : farsi.c vim.h +fileio.obj : fileio.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +fold.obj : fold.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +getchar.obj : getchar.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +hardcopy.obj : hardcopy.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +hashtab.obj : hashtab.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +if_cscope.obj : if_cscope.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h if_cscope.h +if_xcmdsrv.obj : if_xcmdsrv.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h version.h +if_mzsch.obj : if_mzsch.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h \ + regexp.h gui.h beval.h [.proto]gui_beval.pro ex_cmds.h proto.h \ + globals.h farsi.h arabic.h if_mzsch.h +indent.obj : indent.c vim.h [.auto]config.h feature.h os_unix.h +json.obj : json.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h version.h +list.obj : list.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ + globals.h farsi.h arabic.h +main.obj : main.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h farsi.c arabic.c +mark.obj : mark.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +memfile.obj : memfile.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +memline.obj : memline.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +menu.obj : menu.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +message.obj : message.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +misc1.obj : misc1.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h version.h +misc2.obj : misc2.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +move.obj : move.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +mbyte.obj : mbyte.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +normal.obj : normal.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +ops.obj : ops.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +option.obj : option.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +os_unix.obj : os_unix.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h os_unixx.h +os_vms.obj : os_vms.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h os_unixx.h +pathdef.obj : pathdef.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +popupmnu.obj : popupmnu.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +quickfix.obj : quickfix.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +regexp.obj : regexp.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +screen.obj : screen.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +search.obj : search.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +sha256.obj : sha256.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ + globals.h farsi.h arabic.h +sign.obj : sign.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ + globals.h farsi.h arabic.h +spell.obj : spell.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +spellfile.obj : spellfile.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h \ + regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +syntax.obj : syntax.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +tag.obj : tag.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +term.obj : term.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +termlib.obj : termlib.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +textprop.obj : textprop.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +ui.obj : ui.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +undo.obj : undo.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +userfunc.obj : userfunc.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h \ + regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +version.obj : version.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h version.h +window.obj : window.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +gui.obj : gui.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +gui_gtk.obj : gui_gtk.c gui_gtk_f.h vim.h [.auto]config.h feature.h \ + os_unix.h ascii.h keymap.h term.h macros.h structs.h \ + regexp.h gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h \ + proto.h globals.h farsi.h arabic.h [-.pixmaps]stock_icons.h +gui_gtk_f.obj : gui_gtk_f.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h gui_gtk_f.h +gui_motif.obj : gui_motif.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h [-.pixmaps]alert.xpm [-.pixmaps]error.xpm \ + [-.pixmaps]generic.xpm [-.pixmaps]info.xpm [-.pixmaps]quest.xpm +gui_athena.obj : gui_athena.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h gui_at_sb.h +gui_gtk_x11.obj : gui_gtk_x11.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h gui_gtk_f.h [-.runtime]vim32x32.xpm \ + [-.runtime]vim16x16.xpm [-.runtime]vim48x48.xpm +gui_x11.obj : gui_x11.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h [-.runtime]vim32x32.xpm \ + [-.runtime]vim16x16.xpm [-.runtime]vim48x48.xpm [-.pixmaps]tb_new.xpm \ + [-.pixmaps]tb_open.xpm [-.pixmaps]tb_close.xpm [-.pixmaps]tb_save.xpm \ + [-.pixmaps]tb_print.xpm [-.pixmaps]tb_cut.xpm [-.pixmaps]tb_copy.xpm \ + [-.pixmaps]tb_paste.xpm [-.pixmaps]tb_find.xpm \ + [-.pixmaps]tb_find_next.xpm [-.pixmaps]tb_find_prev.xpm \ + [-.pixmaps]tb_find_help.xpm [-.pixmaps]tb_exit.xpm \ + [-.pixmaps]tb_undo.xpm [-.pixmaps]tb_redo.xpm [-.pixmaps]tb_help.xpm \ + [-.pixmaps]tb_macro.xpm [-.pixmaps]tb_make.xpm \ + [-.pixmaps]tb_save_all.xpm [-.pixmaps]tb_jump.xpm \ + [-.pixmaps]tb_ctags.xpm [-.pixmaps]tb_load_session.xpm \ + [-.pixmaps]tb_save_session.xpm [-.pixmaps]tb_new_session.xpm \ + [-.pixmaps]tb_blank.xpm [-.pixmaps]tb_maximize.xpm \ + [-.pixmaps]tb_split.xpm [-.pixmaps]tb_minimize.xpm \ + [-.pixmaps]tb_shell.xpm [-.pixmaps]tb_replace.xpm \ + [-.pixmaps]tb_vsplit.xpm [-.pixmaps]tb_maxwidth.xpm \ + [-.pixmaps]tb_minwidth.xpm +gui_at_sb.obj : gui_at_sb.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h gui_at_sb.h +gui_at_fs.obj : gui_at_fs.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h gui_at_sb.h +pty.obj : pty.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +hangulin.obj : hangulin.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +if_perl.obj : [.auto]if_perl.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +if_perlsfio.obj : if_perlsfio.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +if_python.obj : if_python.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +if_tcl.obj : if_tcl.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +if_ruby.obj : if_ruby.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h version.h +beval.obj : beval.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +gui_beval.obj : gui_beval.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h +workshop.obj : workshop.c [.auto]config.h integration.h vim.h feature.h \ + os_unix.h ascii.h keymap.h term.h macros.h structs.h \ + regexp.h gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h \ + proto.h globals.h farsi.h arabic.h version.h workshop.h +wsdebug.obj : wsdebug.c +integration.obj : integration.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h integration.h +netbeans.obj : netbeans.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h version.h +gui_xmdlg.obj : gui_xmdlg.c [.auto]config.h vim.h feature.h os_unix.h +gui_xmebw.obj : gui_xmebw.c [.auto]config.h vim.h feature.h os_unix.h +xdiffi.obj : [.xdiff]xdiffi.c [.xdiff]xinclude.h [.auto]config.h vim.h feature.h os_unix.h +xemit.obj : [.xdiff]xemit.c [.xdiff]xinclude.h [.auto]config.h vim.h feature.h os_unix.h +xprepare.obj : [.xdiff]xprepare.c [.xdiff]xinclude.h [.auto]config.h vim.h feature.h os_unix.h +xutils.obj : [.xdiff]xutils.c [.xdiff]xinclude.h [.auto]config.h vim.h feature.h os_unix.h +xhistogram.obj : [.xdiff]xhistogram.c [.xdiff]xinclude.h [.auto]config.h vim.h feature.h os_unix.h +xpatience.obj : [.xdiff]xpatience.c [.xdiff]xinclude.h [.auto]config.h vim.h feature.h os_unix.h diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..8b83e95 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,3871 @@ +# Makefile for Vim on Unix and Unix-like systems vim:ts=8:sw=8:tw=78 +# +# This Makefile is loosely based on the GNU Makefile conventions found in +# standards.info. +# +# Compiling Vim, summary: +# +# 3. make +# 5. make install +# +# Compiling Vim, details: +# +# Edit this file for adjusting to your system. You should not need to edit any +# other file for machine specific things! +# The name of this file MUST be Makefile (note the uppercase 'M'). +# +# 1. Edit this Makefile {{{1 +# The defaults for Vim should work on most machines, but you may want to +# uncomment some lines or make other changes below to tune it to your +# system, compiler or preferences. Uncommenting means that the '#' in +# the first column of a line is removed. +# - If you want a version of Vim that is small and starts up quickly, +# you might want to disable the GUI, X11, Perl, Python and Tcl. +# - Uncomment the line with --disable-gui if you have Motif, GTK and/or +# Athena but don't want to make gvim (the GUI version of Vim with nice +# menus and scrollbars, but makes Vim bigger and startup slower). +# - Uncomment --disable-darwin if on Mac OS X but you want to compile a +# Unix version. +# - Uncomment the line "CONF_OPT_X = --without-x" if you have X11 but +# want to disable using X11 libraries. This speeds up starting Vim, +# but the window title will not be set and the X11 selection can not +# be used. +# - Uncomment the line "CONF_OPT_XSMP = --disable-xsmp" if you have the +# X11 Session Management Protocol (XSMP) library (libSM) but do not +# want to use it. +# This can speedup Vim startup but Vim loses the ability to catch the +# user logging out from session-managers like GNOME and work +# could be lost. +# - Uncomment one or more of these lines to include an interface; +# each makes Vim quite a bit bigger: +# --enable-luainterp for Lua interpreter +# --enable-mzschemeinterp for MzScheme interpreter +# --enable-perlinterp for Perl interpreter +# --enable-python3interp for Python3 interpreter +# --enable-pythoninterp for Python interpreter +# --enable-rubyinterp for Ruby interpreter +# --enable-tclinterp for Tcl interpreter +# --enable-cscope for Cscope interface +# - Uncomment one of the lines with --with-features= to enable a set of +# features (but not the interfaces just mentioned). +# - Uncomment the line with --disable-acl to disable ACL support even +# though your system supports it. +# - Uncomment the line with --disable-gpm to disable gpm support +# even though you have gpm libraries and includes. +# - Uncomment the line with --disable-sysmouse to disable sysmouse +# support even though you have /dev/sysmouse and includes. +# - Uncomment one of the lines with CFLAGS and/or CC if you have +# something very special or want to tune the optimizer. +# - Search for the name of your system to see if it needs anything +# special. +# - A few versions of make use '.include "file"' instead of 'include +# file'. Adjust the include line below if yours does. +# +# 2. Edit feature.h {{{1 +# Only if you do not agree with the default compile features, e.g.: +# - you want Vim to be as vi compatible as it can be +# - you want to use Emacs tags files +# - you want right-to-left editing (Hebrew) +# - you want 'langmap' support (Greek) +# - you want to remove features to make Vim smaller +# +# 3. "make" {{{1 +# Will first run ./configure with the options in this file. Then it will +# start make again on this Makefile to do the compiling. You can also do +# this in two steps with: +# make config +# make +# The configuration phase creates/overwrites auto/config.h and +# auto/config.mk. +# The configure script is created with "make autoconf". It can detect +# different features of your system and act accordingly. However, it is +# not correct for all systems. Check this: +# - If you have X windows, but configure could not find it or reported +# another include/library directory then you wanted to use, you have +# to set CONF_OPT_X below. You might also check the installation of +# xmkmf. +# - If you have --enable-gui=motif and have Motif on your system, but +# configure reports "checking for location of gui... ", you +# have to set GUI_INC_LOC and GUI_LIB_LOC below. +# If you changed something, do this to run configure again: +# make reconfig +# +# - If you do not trust the automatic configuration code, then inspect +# auto/config.h and auto/config.mk, before starting the actual build +# phase. If possible edit this Makefile, rather than auto/config.mk -- +# especially look at the definition of VIMLOC below. Note that the +# configure phase overwrites auto/config.mk and auto/config.h again. +# - If you get error messages, find out what is wrong and try to correct +# it in this Makefile. You may need to do "make reconfig" when you +# change anything that configure uses (e.g. switching from an old C +# compiler to an ANSI C compiler). Only when auto/configure does +# something wrong you may need to change one of the other files. If +# you find a clean way to fix the problem, consider sending a note to +# the author of autoconf (bug-gnu-utils@prep.ai.mit.edu) or Vim +# (Bram@vim.org). Don't bother to do that when you made a hack +# solution for a non-standard system. +# +# 4. "make test" {{{1 +# This is optional. This will run Vim scripts on a number of test +# files, and compare the produced output with the expected output. +# If all is well, you will get the "ALL DONE" message in the end. If a +# test fails you get "TEST FAILURE". See below (search for "/^test"). +# +# 5. "make install" {{{1 +# If the new Vim seems to be working OK you can install it and the +# documentation in the appropriate location. The default is +# "/usr/local". Change "prefix" below to change the location. +# "auto/pathdef.c" will be compiled again after changing this to make +# the executable know where the help files are located. +# Note that any existing executable is removed or overwritten. If you +# want to keep it you will have to make a backup copy first. +# The runtime files are in a different directory for each version. You +# might want to delete an older version. +# If you don't want to install everything, there are other targets: +# make installvim only installs Vim, not the tools +# make installvimbin only installs the Vim executable +# make installruntime installs most of the runtime files +# make installrtbase only installs the Vim help and +# runtime files +# make installlinks only installs the Vim binary links +# make installmanlinks only installs the Vim manpage links +# make installmacros only installs the Vim macros +# make installpack only installs the packages +# make installtutorbin only installs the Vim tutor program +# make installtutor only installs the Vim tutor files +# make installspell only installs the spell files +# make installtools only installs xxd +# If you install Vim, not to install for real but to prepare a package +# or RPM, set DESTDIR to the root of the tree. +# +# 6. Use Vim until a new version comes out. {{{1 +# +# 7. "make uninstall_runtime" {{{1 +# Will remove the runtime files for the current version. This is safe +# to use while another version is being used, only version-specific +# files will be deleted. +# To remove the runtime files of another version: +# make uninstall_runtime VIMRTDIR=/vim54 +# If you want to delete all installed files, use: +# make uninstall +# Note that this will delete files that have the same name for any +# version, thus you might need to do a "make install" soon after this. +# Be careful not to remove a version of Vim that is still being used! +# To find out which files and directories will be deleted, use: +# make -n uninstall +# }}} +# +### This Makefile has been successfully tested on many systems. {{{ +### Only the ones that require special options are mentioned here. +### Check the (*) column for remarks, listed below. +### Later code changes may cause small problems, otherwise Vim is supposed to +### compile and run without problems. + +#system: configurations: version (*) tested by: +#------------- ------------------------ ------- - ---------- +#AIX 3.2.5 cc (not gcc) - 4.5 (M) Will Fiveash +#AIX 4 cc +X11 -GUI 3.27 (4) Axel Kielhorn +#AIX 4.1.4 cc +X11 +GUI 4.5 (5) Nico Bakker +#AIX 4.2.1 cc 5.2k (C) Will Fiveash +#AIX 4.3.3.12 xic 3.6.6 5.6 (5) David R. Favor +#A/UX 3.1.1 gcc +X11 4.0 (6) Jim Jagielski +#BeOS PR mwcc DR3 5.0n (T) Olaf Seibert +#BSDI 2.1 (x86) shlicc2 gcc-2.6.3 -X11 X11R6 4.5 (1) Jos Backus +#BSD/OS 3.0 (x86) gcc gcc-2.7.2.1 -X11 X11R6 4.6c (1) Jos Backus +#CX/UX 6.2 cc +X11 +GUI_Mofif 5.4 (V) Kipp E. Howard +#DG/UX 5.4* gcc 2.5.8 GUI 5.0e (H) Jonas Schlein +#DG/UX 5.4R4.20 gcc 2.7.2 GUI 5.0s (H) Rocky Olive +#HP-UX (most) c89 cc 5.1 (2) Bram Moolenaar +#HP-UX_9.04 cc +X11 +Motif 5.0 (2) Carton Lao +#Irix 6.3 (O2) cc ? 4.5 (L) Edouard Poor +#Irix 6.4 cc ? 5.0m (S) Rick Sayre +#Irix 6.5 cc ? 6.0 (S) David Harrison +#Irix 64 bit 4.5 (K) Jon Wright +#Linux 2.0 gcc-2.7.2 Infomagic Motif 4.3 (3) Ronald Rietman +#Linux 2.0.31 gcc +X11 +GUI Athena 5.0w (U) Darren Hiebert +#LynxOS 3.0.1 2.9-gnupro-98r2 +X11 +GUI Athena 5.7.1(O) Lorenz Hahn +#LynxOS 3.1.0 2.9-gnupro-98r2 +X11 +GUI Athena 5.7.1(O) Lorenz Hahn +#NEC UP4800 UNIX_SV 4.2MP cc +X11R6 Motif,Athena4.6b (Q) Lennart Schultz +#NetBSD 1.0A gcc-2.4.5 -X11 -GUI 3.21 (X) Juergen Weigert +#QNX 4.2 wcc386-10.6 -X11 4.2 (D) G.F. Desrochers +#QNX 4.23 Watcom -X11 4.2 (F) John Oleynick +#SCO Unix v3.2.5 cc +X11 Motif 3.27 (C) M. Kuperblum +#SCO Open Server 5 gcc 2.7.2.3 +X11 +GUI Motif 5.3 (A) Glauber Ribeiro +#SINIX-N 5.43 RM400 R4000 cc +X11 +GUI 5.0l (I) Martin Furter +#SINIX-Z 5.42 i386 gcc 2.7.2.3 +X11 +GUI Motif 5.1 (I) Joachim Fehn +#SINIX-Y 5.43 RM600 R4000 gcc 2.7.2.3 +X11 +GUI Motif 5.1 (I) Joachim Fehn +#Reliant/SINIX 5.44 cc +X11 +GUI 5.5a (I) B. Pruemmer +#SNI Targon31 TOS 4.1.11 gcc-2.4.5 +X11 -GUI 4.6c (B) Paul Slootman +#Solaris 2.4 (Sparc) cc +X11 +GUI 3.29 (9) Glauber +#Solaris 2.4/2.5 clcc +X11 -GUI openwin 3.20 (7) Robert Colon +#Solaris 2.5 (sun4m) cc (SC4.0) +X11R6 +GUI (CDE) 4.6b (E) Andrew Large +#Solaris 2.5 cc +X11 +GUI Athena 4.2 (9) Sonia Heimann +#Solaris 2.5 gcc 2.5.6 +X11 Motif 5.0m (R) Ant. Colombo +#Solaris 2.6 gcc 2.8.1 ncurses 5.3 (G) Larry W. Virden +#Solaris with -lthread 5.5 (W) K. Nagano +#Solaris gcc (b) Riccardo +#SunOS 4.1.x +X11 -GUI 5.1b (J) Bram Moolenaar +#SunOS 4.1.3_U1 (sun4c) gcc +X11 +GUI Athena 5.0w (J) Darren Hiebert +#SUPER-UX 6.2 (NEC SX-4) cc +X11R6 Motif,Athena4.6b (P) Lennart Schultz +#Tandem/NSK (c) Matthew Woehlke +#Unisys 6035 cc +X11 Motif 5.3 (8) Glauber Ribeiro +#ESIX V4.2 cc +X11 6.0 (a) Reinhard Wobst +#Mac OS X 10.[23] gcc Carbon 6.2 (x) Bram Moolenaar +# }}} + +# (*) Remarks: {{{ +# +# (1) Uncomment line below for shlicc2 +# (2) HPUX with compile problems or wrong digraphs, uncomment line below +# (3) Infomagic Motif needs GUI_LIB_LOC and GUI_INC_LOC set, see below. +# And add "-lXpm" to MOTIF_LIBS2. +# (4) For cc the optimizer must be disabled (use CFLAGS= after running +# configure) (symptom: ":set termcap" output looks weird). +# (5) Compiler may need extra argument, see below. +# (6) See below for a few lines to uncomment +# (7) See below for lines which enable the use of clcc +# (8) Needs some EXTRA_LIBS, search for Unisys below +# (9) Needs an extra compiler flag to compile gui_at_sb.c, see below. +# (A) May need EXTRA_LIBS, see below +# (B) Can't compile GUI because there is no waitpid()... Disable GUI below. +# (C) Force the use of curses instead of termcap, see below. +# (D) Uncomment lines below for QNX +# (E) You might want to use termlib instead of termcap, see below. +# (F) See below for instructions. +# (G) Using ncurses version 4.2 has reported to cause a crash. Use the +# Sun curses library instead. +# (H) See line for EXTRA_LIBS below. +# (I) SINIX-N 5.42 and 5.43 need some EXTRA_LIBS. Also for Reliant-Unix. +# (J) If you get undefined symbols, see below for a solution. +# (K) See lines to uncomment below for machines with 64 bit pointers. +# (L) For Silicon Graphics O2 workstations remove "-lnsl" from auto/config.mk +# (M) gcc version cygnus-2.0.1 does NOT work (symptom: "dl" deletes two +# characters instead of one). +# (N) SCO with decmouse. +# (O) LynxOS needs EXTRA_LIBS, see below. +# (P) For SuperUX 6.2 on NEC SX-4 see a few lines below to uncomment. +# (Q) For UNIXSVR 4.2MP on NEC UP4800 see below for lines to uncomment. +# (R) For Solaris 2.5 (or 2.5.1) with gcc > 2.5.6, uncomment line below. +# (S) For Irix 6.x with MipsPro compiler, use -OPT:Olimit. See line below. +# (T) See ../doc/os_beos.txt. +# (U) Must uncomment CONF_OPT_PYTHON option below to disable Python +# detection, since the configure script runs into an error when it +# detects Python (probably because of the bash shell). +# (V) See lines to uncomment below. +# (X) Need to use the .include "auto/config.mk" line below +# (Y) See line with c89 below +# (Z) See lines with cc or c89 below +# (a) See line with EXTRA_LIBS below. +# (b) When using gcc with the Solaris linker, make sure you don't use GNU +# strip, otherwise the binary may not run: "Cannot find ELF". +# (c) Add -lfloss to EXTRA_LIBS, see below. +# (x) When you get warnings for precompiled header files, run +# "sudo fixPrecomps". Also see CONF_OPT_DARWIN below. +# }}} + + +#DO NOT CHANGE the next line, we need it for configure to find the compiler +#instead of using the default from the "make" program. +#Use a line further down to change the value for CC. +CC= + +# Change and use these defines if configure cannot find your Motif stuff. +# Unfortunately there is no "standard" location for Motif. {{{ +# These defines can contain a single directory (recommended) or a list of +# directories (for when you are working with several systems). The LAST +# directory that exists is used. +# When changed, run "make reconfig" next! +#GUI_INC_LOC = -I/usr/include/Motif2.0 -I/usr/include/Motif1.2 +#GUI_LIB_LOC = -L/usr/lib/Motif2.0 -L/usr/lib/Motif1.2 +### Use these two lines for Infomagic Motif (3) +#GUI_INC_LOC = -I/usr/X11R6/include +#GUI_LIB_LOC = -L/usr/X11R6/lib +# }}} + +# Defaults used when auto/config.mk does not exist. +srcdir = . +VIMNAME = vim +EXNAME = ex +VIEWNAME = view + +######################## auto/config.mk ######################## {{{1 +# At this position auto/config.mk is included. When starting from the +# toplevel Makefile it is almost empty. After running auto/configure it +# contains settings that have been discovered for your system. Settings below +# this include override settings in auto/config.mk! + +# Note: If make fails because auto/config.mk does not exist (it is not +# included in the repository), do: +# cp config.mk.dist auto/config.mk + +# (X) How to include auto/config.mk depends on the version of "make" you have, +# if the current choice doesn't work, try the other one. + +include auto/config.mk +#.include "auto/config.mk" +CClink = $(CC) + +#}}} + +# Include the configuration choices first, so we can override everything +# below. As shipped, this file contains a target that causes to run +# configure. Once configure was run, this file contains a list of +# make variables with predefined values instead. Thus any second invocation +# of make, will build Vim. + +# CONFIGURE - configure arguments {{{1 +# You can give a lot of options to configure. +# Change this to your desire and do 'make config' afterwards + +# examples you can uncomment: +#CONF_ARGS1 = --exec-prefix=/usr +#CONF_ARGS2 = --with-vim-name=vim8 --with-ex-name=ex8 --with-view-name=view8 +#CONF_ARGS3 = --with-global-runtime=/etc/vim,/usr/share/vim +#CONF_ARGS4 = --with-local-dir=/usr/share +#CONF_ARGS5 = --without-local-dir + +# Use this one if you distribute a modified version of Vim. +#CONF_ARGS6 = --with-modified-by="John Doe" + +# GUI - For creating Vim with GUI (gvim) (B) +# Uncomment this line when you don't want to get the GUI version, although you +# have GTK, Motif and/or Athena. Also use --without-x if you don't want X11 +# at all. +#CONF_OPT_GUI = --disable-gui + +# Uncomment one of these lines if you have that GUI but don't want to use it. +# The automatic check will use another one that can be found. +# Gnome is disabled by default, because it may cause trouble. +# +# When both GTK+ 2 and GTK+ 3 are possible then GTK+ 2 will be selected. +# To use GTK+ 3 instead use --enable-gui=gtk3 (see below). +#CONF_OPT_GUI = --disable-gtk2-check +#CONF_OPT_GUI = --enable-gnome-check +#CONF_OPT_GUI = --disable-gtk3-check +#CONF_OPT_GUI = --disable-motif-check +#CONF_OPT_GUI = --disable-athena-check +#CONF_OPT_GUI = --disable-nextaw-check + +# Uncomment one of these lines to select a specific GUI to use. +# When using "yes" or nothing, configure will use the first one found: GTK+, +# Motif or Athena. +# +# GTK versions that are known not to work 100% are rejected. +# Use "--disable-gtktest" to accept them anyway. +# For GTK 1 use Vim 7.2. +# +# GNOME means GTK with Gnome support. If using GTK and --enable-gnome-check +# is used then GNOME will automatically be used if it is found. If you have +# GNOME, but do not want to use it (e.g., want a GTK-only version), then use +# --enable-gui=gtk or leave out --enable-gnome-check. +# +# GNOME makes sense only for GTK+ 2. Avoid use of --enable-gnome-check with +# GTK+ 3 build, as the functionality of GNOME is already incooperated into +# GTK+ 3. +# +# If the selected GUI isn't found, the GUI is disabled automatically +#CONF_OPT_GUI = --enable-gui=gtk2 +#CONF_OPT_GUI = --enable-gui=gtk2 --disable-gtktest +#CONF_OPT_GUI = --enable-gui=gnome2 +#CONF_OPT_GUI = --enable-gui=gnome2 --disable-gtktest +#CONF_OPT_GUI = --enable-gui=gtk3 +#CONF_OPT_GUI = --enable-gui=gtk3 --disable-gtktest +#CONF_OPT_GUI = --enable-gui=motif +#CONF_OPT_GUI = --enable-gui=motif --with-motif-lib="-static -lXm -shared" +#CONF_OPT_GUI = --enable-gui=athena +#CONF_OPT_GUI = --enable-gui=nextaw + +# Carbon GUI for Mac OS X +#CONF_OPT_GUI = --enable-gui=carbon + +# Uncomment this line to run an individual test with gvim. +#GUI_TESTARG = GUI_FLAG=-g + +# DARWIN - detecting Mac OS X +# Uncomment this line when you want to compile a Unix version of Vim on +# Darwin. None of the Mac specific options or files will be used. +#CONF_OPT_DARWIN = --disable-darwin + +# Select the architecture supported. Default is to build for the current +# platform. Use "both" for a universal binary. That probably doesn't work +# when including Perl, Python, etc. +#CONF_OPT_DARWIN = --with-mac-arch=i386 +#CONF_OPT_DARWIN = --with-mac-arch=ppc +#CONF_OPT_DARWIN = --with-mac-arch=both + +# Uncomment the next line to fail if one of the requested language interfaces +# cannot be configured. Without this Vim will be build anyway, without +# the failing interfaces. +#CONF_OPT_FAIL = --enable-fail-if-missing + +# LUA +# Uncomment one of these when you want to include the Lua interface. +# First one is for static linking, second one for dynamic loading. +# Use --with-luajit if you want to use LuaJIT instead of Lua. +# Set PATH environment variable to find lua or luajit executable. +# This requires at least "normal" features, "tiny" and "small" don't work. +#CONF_OPT_LUA = --enable-luainterp +#CONF_OPT_LUA = --enable-luainterp=dynamic +#CONF_OPT_LUA = --enable-luainterp --with-luajit +#CONF_OPT_LUA = --enable-luainterp=dynamic --with-luajit +# Lua installation dir (when not set uses $LUA_PREFIX or defaults to /usr) +#CONF_OPT_LUA_PREFIX = --with-lua-prefix=/usr/local + +# MZSCHEME +# Uncomment this when you want to include the MzScheme interface. +# NOTE: does not work well together with valgrind. +#CONF_OPT_MZSCHEME = --enable-mzschemeinterp +# PLT/mrscheme/drscheme Home dir; the PLTHOME environment variable also works +#CONF_OPT_PLTHOME = --with-plthome=/usr/local/plt +#CONF_OPT_PLTHOME = --with-plthome=/usr/local/drscheme +#CONF_OPT_PLTHOME = --with-plthome=/home/me/mz + +# PERL +# Uncomment one of these when you want to include the Perl interface. +# First one is for static linking, second one for dynamic loading. +# The Perl option sometimes causes problems, because it adds extra flags +# +# to the command line. If you see strange flags during compilation, check in +# auto/config.mk where they come from. If it's PERL_CFLAGS, try commenting +# the next line. +# When you get an error for a missing "perl.exp" file, try creating an empty +# one: "touch perl.exp". +# This requires at least "normal" features, "tiny" and "small" don't work. +#CONF_OPT_PERL = --enable-perlinterp +#CONF_OPT_PERL = --enable-perlinterp=dynamic + +# PYTHON +# Uncomment lines here when you want to include the Python interface. +# This requires at least "normal" features, "tiny" and "small" don't work. +# NOTE: This may cause threading to be enabled, which has side effects (such +# as using different libraries and debugging becomes more difficult). +# For Python3 support make a symbolic link in /usr/local/bin: +# ln -s python3 python3.1 +# If both python2.x and python3.x are enabled then the linking will be via +# dlopen(), dlsym(), dlclose(), i.e. pythonX.Y.so must be available +# However, this may still cause problems, such as "import termios" failing. +# Build two separate versions of Vim in that case. +#CONF_OPT_PYTHON = --enable-pythoninterp +#CONF_OPT_PYTHON = --enable-pythoninterp --with-python-command=python2.7 +#CONF_OPT_PYTHON = --enable-pythoninterp=dynamic +#CONF_OPT_PYTHON3 = --enable-python3interp +#CONF_OPT_PYTHON3 = --enable-python3interp --with-python3-command=python3.6 +#CONF_OPT_PYTHON3 = --enable-python3interp=dynamic + +# RUBY +# Uncomment this when you want to include the Ruby interface. +# First one for static linking, second one for loading when used. +# Note: you need the development package (e.g., ruby1.9.1-dev on Ubuntu). +# This requires at least "normal" features, "tiny" and "small" don't work. +#CONF_OPT_RUBY = --enable-rubyinterp +#CONF_OPT_RUBY = --enable-rubyinterp=dynamic +#CONF_OPT_RUBY = --enable-rubyinterp --with-ruby-command=ruby1.9.1 + +# TCL +# Uncomment this when you want to include the Tcl interface. +# First one is for static linking, second one for dynamic loading. +#CONF_OPT_TCL = --enable-tclinterp +#CONF_OPT_TCL = --enable-tclinterp=dynamic +#CONF_OPT_TCL = --enable-tclinterp --with-tclsh=tclsh8.4 + +# CSCOPE +# Uncomment this when you want to include the Cscope interface. +#CONF_OPT_CSCOPE = --enable-cscope + +# NETBEANS - NetBeans interface. Only works with Motif, GTK, and gnome. +# Motif version must have XPM libraries (see |netbeans-xpm|). +# Uncomment this when you do not want the netbeans interface. +#CONF_OPT_NETBEANS = --disable-netbeans + +# CHANNEL - inter process communication. Same conditions as NetBeans. +# Uncomment this when you do not want inter process communication. +#CONF_OPT_CHANNEL = --disable-channel + +# TERMINAL - Terminal emulator support, :terminal command. Requires the +# channel feature. The default is enable for when using "huge" features. +# Uncomment the first line when you want terminal emulator support for +# not-huge builds. Uncomment the second line when you don't want terminal +# emulator support in the huge build. +#CONF_OPT_TERMINAL = --enable-terminal +#CONF_OPT_TERMINAL = --disable-terminal + +# MULTIBYTE - To edit multi-byte characters. +# This is now always enabled. + +# When building with at least "big" features, right-left, Arabic and Farsi +# features are enabled. Use this to disable them. +#CONF_OPT_MULTIBYTE = --disable-rightleft --disable-farsi --disable-arabic + +# NLS - National Language Support +# Uncomment this when you do not want to support translated messages, even +# though configure can find support for it. +#CONF_OPT_NLS = --disable-nls + +# XIM - X Input Method. Special character input support for X11 (Chinese, +# Japanese, special symbols, etc). Also needed for dead-key support. +# When omitted it's automatically enabled for the X-windows GUI. +# HANGUL - Input Hangul (Korean) language using internal routines. +# Uncomment one of these when you want to input a multibyte language. +#CONF_OPT_INPUT = --enable-xim +#CONF_OPT_INPUT = --disable-xim +#CONF_OPT_INPUT = --enable-hangulinput + +# FONTSET - X fontset support for output of languages with many characters. +# Uncomment this when you want to output a multibyte language. +#CONF_OPT_OUTPUT = --enable-fontset + +# ACL - Uncomment this when you do not want to include ACL support, even +# though your system does support it. E.g., when it's buggy. +#CONF_OPT_ACL = --disable-acl + +# gpm - For mouse support on Linux console via gpm +# Uncomment this when you do not want to include gpm support, even +# though you have gpm libraries and includes. +#CONF_OPT_GPM = --disable-gpm + +# sysmouse - For mouse support on FreeBSD and DragonFly console via sysmouse +# Uncomment this when you do not want do include sysmouse support, even +# though you have /dev/sysmouse and includes. +#CONF_OPT_SYSMOUSE = --disable-sysmouse + +# FEATURES - For creating Vim with more or less features +# Uncomment one of these lines when you want to include few to many features. +# The default is "huge" for most systems. +#CONF_OPT_FEAT = --with-features=tiny +#CONF_OPT_FEAT = --with-features=small +#CONF_OPT_FEAT = --with-features=normal +#CONF_OPT_FEAT = --with-features=big +#CONF_OPT_FEAT = --with-features=huge + +# COMPILED BY - For including a specific e-mail address for ":version". +#CONF_OPT_COMPBY = "--with-compiledby=John Doe " + +# X WINDOWS DISABLE - For creating a plain Vim without any X11 related fancies +# (otherwise Vim configure will try to include xterm titlebar access) +# Also disable the GUI above, otherwise it will be included anyway. +# When both GUI and X11 have been disabled this may save about 15% of the +# code and make Vim startup quicker. +#CONF_OPT_X = --without-x + +# X WINDOWS DIRECTORY - specify X directories +# If configure can't find you X stuff, or if you have multiple X11 derivatives +# installed, you may wish to specify which one to use. +# Select nothing to let configure choose. +# This here selects openwin (as found on sun). +#XROOT = /usr/openwin +#CONF_OPT_X = --x-include=$(XROOT)/include --x-libraries=$(XROOT)/lib + +# X11 Session Management Protocol support +# Vim will try to use XSMP to catch the user logging out if there are unsaved +# files. Uncomment this line to disable that (it prevents vim trying to open +# communications with the session manager). +#CONF_OPT_XSMP = --disable-xsmp + +# You may wish to include xsmp but use exclude xsmp-interact if the logout +# XSMP functionality does not work well with your session-manager (at time of +# writing, this would be early GNOME-1 gnome-session: it 'freezes' other +# applications after Vim has cancelled a logout (until Vim quits). This +# *might* be the Vim code, but is more likely a bug in early GNOME-1. +# This disables the dialog that asks you if you want to save files or not. +#CONF_OPT_XSMP = --disable-xsmp-interact + +# If you want to always automatically add a servername, also in the terminal. +#CONF_OPT_AUTOSERVE = --enable-autoservername + +# COMPILER - Name of the compiler {{{1 +# The default from configure will mostly be fine, no need to change this, just +# an example. If a compiler is defined here, configure will use it rather than +# probing for one. It is dangerous to change this after configure was run. +# Make will use your choice then -- but beware: Many things may change with +# another compiler. It is wise to run 'make reconfig' to start all over +# again. +#CC = cc +#CC = gcc +#CC = clang + +# COMPILER FLAGS - change as you please. Either before running {{{1 +# configure or afterwards. For examples see below. +# When using -g with some older versions of Linux you might get a +# statically linked executable. +# When not defined, configure will try to use -O2 -g for gcc and -O for cc. +#CFLAGS = -g +#CFLAGS = -O + +# Optimization limits - depends on the compiler. Automatic check in configure +# doesn't work very well, because many compilers only give a warning for +# unrecognized arguments. +#CFLAGS = -O -OPT:Olimit=2600 +#CFLAGS = -O -Olimit 2000 +#CFLAGS = -O -FOlimit,2000 + +# Often used for GCC: mixed optimizing, lot of optimizing, debugging +#CFLAGS = -g -O2 -fno-strength-reduce -Wall -Wshadow -Wmissing-prototypes +#CFLAGS = -g -O2 -fno-strength-reduce -Wall -Wmissing-prototypes +#CFLAGS = -g -Wall -Wmissing-prototypes +#CFLAGS = -O6 -fno-strength-reduce -Wall -Wshadow -Wmissing-prototypes +#CFLAGS = -g -DDEBUG -Wall -Wshadow -Wmissing-prototypes +#CFLAGS = -g -O2 '-DSTARTUPTIME="vimstartup"' -fno-strength-reduce -Wall -Wmissing-prototypes + +# Use this with GCC to check for mistakes, unused arguments, etc. +# Note: If you use -Wextra and get warnings in GTK code about function +# parameters, you can add -Wno-cast-function-type +#CFLAGS = -g -Wall -Wextra -Wshadow -Wmissing-prototypes -Wunreachable-code -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 +# Add -Wpedantic to find // comments and other C99 constructs. +# Better disable Perl and Python to avoid a lot of warnings. +#CFLAGS = -g -Wall -Wextra -Wshadow -Wmissing-prototypes -Wpedantic -Wunreachable-code -Wunused-result -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 +#CFLAGS = -g -O2 -Wall -Wextra -Wshadow -Wmissing-prototypes -Wpedantic -Wunreachable-code -Wunused-result -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 +#PYTHON_CFLAGS_EXTRA = -Wno-missing-field-initializers +#MZSCHEME_CFLAGS_EXTRA = -Wno-unreachable-code -Wno-unused-parameter + +# EFENCE - Electric-Fence malloc debugging: catches memory accesses beyond +# allocated memory (and makes every malloc()/free() very slow). +# Electric Fence is free (search ftp sites). +# You may want to set the EF_PROTECT_BELOW environment variable to check the +# other side of allocated memory. +# On FreeBSD you might need to enlarge the number of mmaps allowed. Do this +# as root: sysctl -w vm.max_proc_mmap=30000 +#EXTRA_LIBS = /usr/local/lib/libefence.a + +# Autoconf binary. +AUTOCONF = autoconf + +# PURIFY - remove the # to use the "purify" program (hoi Nia++!) +#PURIFY = purify + +# VALGRIND - remove the # to use valgrind for memory leaks and access errors. +# Used for the unittest targets. +# VALGRIND = valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind.$@ + +# NBDEBUG - debugging the netbeans interface. +#EXTRA_DEFS = -DNBDEBUG + +# }}} + +# LINT - for running lint +# For standard Unix lint +LINT = lint +LINT_OPTIONS = -beprxzF +# For splint +# It doesn't work well, crashes on include files and non-ascii characters. +#LINT = splint +#LINT_OPTIONS = +unixlib -weak -macrovarprefixexclude -showfunc -linelen 9999 + +# PROFILING - Uncomment the next two lines to do profiling with gcc and gprof. +# Might not work with GUI or Perl. +# After running Vim see the profile result with: gprof vim gmon.out | vim - +# Need to recompile everything after changing this: "make clean" "make". +#PROFILE_CFLAGS = -pg -g -DWE_ARE_PROFILING +#PROFILE_LIBS = -pg + +# GCC 5 and later need the -no-pie argument. +#PROFILE_LIBS = -pg -no-pie + +# For unknown reasons adding "-lc" fixes a linking problem with some versions +# of GCC. That's probably a bug in the "-pg" implementation. +#PROFILE_LIBS = -pg -lc + + +# TEST COVERAGE - Uncomment the two lines below the explanation to get code +# coverage information. (provided by Yegappan Lakshmanan) +# 1. make clean, run configure and build Vim as usual. +# 2. Generate the baseline code coverage information: +# $ lcov -c -i -b . -d objects -o objects/coverage_base.info +# 3. Run "make test" to run the unit tests. The code coverage information will +# be generated in the src/objects directory. +# 4. Generate the code coverage information from the tests: +# $ lcov -c -b . -d objects/ -o objects/coverage_test.info +# 5. Combine the baseline and test code coverage data: +# $ lcov -a objects/coverage_base.info -a objects/coverage_test.info -o objects/coverage_total.info +# 6. Process the test coverage data and generate a report in html: +# $ genhtml objects/coverage_total.info -o objects +# 7. Open the objects/index.html file in a web browser to view the coverage +# information. +# +# PROFILE_CFLAGS=-g -O0 -fprofile-arcs -ftest-coverage +# LDFLAGS=--coverage + + +# Uncomment one of the next two lines to compile Vim with the +# address sanitizer or with the undefined sanitizer. Works with gcc and +# clang. May make Vim twice as slow. Errors reported on stderr. +# More at: https://code.google.com/p/address-sanitizer/ +#SANITIZER_CFLAGS = -g -O0 -fsanitize=address -fno-omit-frame-pointer +#SANITIZER_CFLAGS = -g -O0 -fsanitize=undefined -fno-omit-frame-pointer +SANITIZER_LIBS = $(SANITIZER_CFLAGS) + +# MEMORY LEAK DETECTION +# Requires installing the ccmalloc library. +# Configuration is in the .ccmalloc or ~/.ccmalloc file. +# Doesn't work very well, since memory linked to from global variables +# (in libraries) is also marked as leaked memory. +#LEAK_CFLAGS = -DEXITFREE +#LEAK_LIBS = -lccmalloc + +# Uncomment this line to have Vim call abort() when an internal error is +# detected. Useful when using a tool to find errors. +#ABORT_CLFAGS = -DABORT_ON_INTERNAL_ERROR + +##################################################### +### Specific systems, check if yours is listed! ### {{{ +##################################################### + +### Uncomment things here only if the values chosen by configure are wrong. +### It's better to adjust configure.ac and "make autoconf", if you can! +### Then send the required changes to configure.ac to the bugs list. + +### (1) BSD/OS 2.0.1, 2.1 or 3.0 using shared libraries +### +#CC = shlicc2 +#CFLAGS = -O2 -g -m486 -Wall -Wshadow -Wmissing-prototypes -fno-builtin + +### (2) HP-UX with a non-ANSI cc, use the c89 ANSI compiler +### The first probably works on all systems +### The second should work a bit better on newer systems +### The third should work a bit better on HPUX 11.11 +### Information provided by: Richard Allen +#CC = c89 -D_HPUX_SOURCE +#CC = c89 -O +Onolimit +ESlit -D_HPUX_SOURCE +#CC = c89 -O +Onolimit +ESlit +e -D_HPUX_SOURCE + +### (2) For HP-UX: Enable the use of a different set of digraphs. Use this +### when the default (ISO) digraphs look completely wrong. +### After changing this do "touch digraph.c; make". +#EXTRA_DEFS = -DHPUX_DIGRAPHS + +### (2) For HP-UX: 9.04 cpp default macro definition table of 128000 bytes +### is too small to compile many routines. It produces too much defining +### and no space errors. +### Uncomment the following to specify a larger macro definition table. +#CFLAGS = -Wp,-H256000 + +### (2) For HP-UX 10.20 using the HP cc, with X11R6 and Motif 1.2, with +### libraries in /usr/lib instead of /lib (avoiding transition links). +### Information provided by: David Green +#XROOT = /usr +#CONF_OPT_X = --x-include=$(XROOT)/include/X11R6 --x-libraries=$(XROOT)/lib/X11R6 +#GUI_INC_LOC = -I/usr/include/Motif1.2 +#GUI_LIB_LOC = -L/usr/lib/Motif1.2_R6 + +### (5) AIX 4.1.4 with cc +#CFLAGS = -O -qmaxmem=8192 + +### AIX with c89 (Walter Briscoe) +#CC = c89 +#CPPFLAGS = -D_ALL_SOURCE + +### AIX 4.3.3.12 with xic 3.6.6 (David R. Favor) +# needed to avoid a problem where strings.h gets included +#CFLAGS = -qsrcmsg -O2 -qmaxmem=8192 -D__STR31__ + +### (W) Solaris with multi-threaded libraries (-lthread): +### If suspending doesn't work properly, try using this line: +#EXTRA_DEFS = -D_REENTRANT + +### (7) Solaris 2.4/2.5 with Centerline compiler +#CC = clcc +#X_LIBS_DIR = -L/usr/openwin/lib -R/usr/openwin/lib +#CFLAGS = -O + +### (9) Solaris 2.x with cc (SunPro), using Athena. +### Only required for compiling gui_at_sb.c. +### Symptom: "identifier redeclared: vim_XawScrollbarSetThumb" +### Use one of the lines (either Full ANSI or no ANSI at all) +#CFLAGS = $(CFLAGS) -Xc +#CFLAGS = $(CFLAGS) -Xs + +### Solaris 2.3 with X11 and specific cc +#CC=/opt/SUNWspro/bin/cc -O -Xa -v -R/usr/openwin/lib + +### Solaris with /usr/ucb/cc (it is rejected by autoconf as "cc") +#CC = /usr/ucb/cc +#EXTRA_LIBS = -R/usr/ucblib + +### Solaris with Forte Developer and NetBeans. +# The Xpm library is available from http://koala.ilog.fr/ftp/pub/xpm. +#CC = cc +#XPM_DIR = /usr/local/xpm/xpm-3.4k-solaris +#XPM_LIB = -L$(XPM_DIR)/lib -R$(XPM_DIR)/lib -lXpm +#XPM_IPATH = -I$(XPM_DIR)/include +#EXTRA_LIBS = $(XPM_LIB) +#EXTRA_IPATHS = $(XPM_IPATH) +#EXTRA_DEFS = -xCC -DHAVE_X11_XPM_H + +### (R) for Solaris 2.5 (or 2.5.1) with gcc > 2.5.6 you might need this: +#LDFLAGS = -lw -ldl -lXmu +#GUI_LIB_LOC = -L/usr/local/lib + +### (8) Unisys 6035 (Glauber Ribeiro) +#EXTRA_LIBS = -lnsl -lsocket -lgen + +### When builtin functions cause problems with gcc (for Sun 4.1.x) +#CFLAGS = -O2 -Wall -traditional -Wno-implicit + +### Apollo DOMAIN (with SYSTYPE = bsd4.3) (TESTED for version 3.0) +#EXTRA_DEFS = -DDOMAIN +#CFLAGS= -O -A systype,bsd4.3 + +### Coherent 4.2.10 on Intel 386 platform +#EXTRA_DEFS = -Dvoid=int +#EXTRA_LIBS = -lterm -lsocket + +### SCO 3.2, with different library name for terminfo +#EXTRA_LIBS = -ltinfo + +### UTS2 for Amdahl UTS 2.1.x +#EXTRA_DEFS = -DUTS2 +#EXTRA_LIBS = -lsocket + +### UTS4 for Amdahl UTS 4.x +#EXTRA_DEFS = -DUTS4 -Xa + +### USL for Unix Systems Laboratories (SYSV 4.2) +#EXTRA_DEFS = -DUSL + +### (6) A/UX 3.1.1 with gcc (Jim Jagielski) +#CC= gcc -D_POSIX_SOURCE +#CFLAGS= -O2 +#EXTRA_LIBS = -lposix -lbsd -ltermcap -lX11 + +### (A) Some versions of SCO Open Server 5 (Jan Christiaan van Winkel) +### Also use the CONF_TERM_LIB below! +#EXTRA_LIBS = -lgen + +### (D) QNX (by G.F. Desrochers) +#CFLAGS = -g -O -mf -4 + +### (F) QNX (by John Oleynick) +# 1. If you don't have an X server: Comment out CONF_OPT_GUI and uncomment +# CONF_OPT_X = --without-x. +# 2. make config +# 3. edit auto/config.mk and remove -ldir and -ltermcap from LIBS. It doesn't +# have -ldir (does config find it somewhere?) and -ltermcap has at +# least one problem so I use termlib.o instead. The problem with +# termcap is that it segfaults if you call it with the name of +# a non-existent terminal type. +# 4. edit auto/config.h and add #define USE_TMPNAM +# 5. add termlib.o to OBJ +# 6. make + +### (H) for Data general DG/UX 5.4.2 and 5.4R3.10 (Jonas J. Schlein) +#EXTRA_LIBS = -lgen + +### (I) SINIX-N 5.42 or 5.43 RM400 R4000 (also SINIX-Y and SINIX-Z) +#EXTRA_LIBS = -lgen -lnsl +### For SINIX-Y this is needed for the right prototype of gettimeofday() +#EXTRA_DEFS = -D_XPG_IV + +### (I) Reliant-Unix (aka SINIX) 5.44 with standard cc +# Use both "-F O3" lines for optimization or the "-g" line for debugging +#EXTRA_LIBS = -lgen -lsocket -lnsl -lSM -lICE +#CFLAGS = -F O3 -DSINIXN +#LDFLAGS = -F O3 +#CFLAGS = -g -DSINIXN + +### (P) SCO 3.2.42, with different termcap names for some useful keys DJB +#EXTRA_DEFS = -DSCOKEYS -DNETTERM_MOUSE -DDEC_MOUSE -DXTERM_MOUSE -DHAVE_GETTIMEOFDAY +#EXTRA_LIBS = -lsocket -ltermcap -lmalloc -lc_s + +### (P) SuperUX 6.2 on NEC SX-4 (Lennart Schultz) +#GUI_INC_LOC = -I/usr/include +#GUI_LIB_LOC = -L/usr/lib +#EXTRA_LIBS = -lgen + +### (Q) UNIXSVR 4.2MP on NEC UP4800 (Lennart Schultz) +#GUI_INC_LOC = -I/usr/necccs/include +#GUI_LIB_LOC = -L/usr/necccs/lib/X11R6 +#XROOT = /usr/necccs +#CONF_OPT_X = --x-include=$(XROOT)/include --x-libraries=$(XROOT)/lib/X11R6 +#EXTRA_LIBS = -lsocket -lgen + +### Irix 4.0 & 5.2 (Silicon Graphics Machines, __sgi will be defined) +# Not needed for Irix 5.3, Ives Aerts reported +#EXTRA_LIBS = -lmalloc -lc_s +# Irix 4.0, when regexp and regcmp cannot be found when linking: +#EXTRA_LIBS = -lmalloc -lc_s -lPW + +### (S) Irix 6.x (MipsPro compiler): Uses different Olimit flag: +# Note: This newer option style is used with the MipsPro compilers ONLY if +# you are compiling an "n32" or "64" ABI binary (use either a -n32 +# flag or a -64 flag for CFLAGS). If you explicitly use a -o32 flag, +# then the CFLAGS option format will be the typical style (i.e. +# -Olimit 3000). +#CFLAGS = -OPT:Olimit=3000 -O + +### (S) Irix 6.5 with MipsPro C compiler. Try this as a test to see new +# compiler features! Beware, the optimization is EXTREMELY thorough +# and takes quite a long time. +# Note: See the note above. Here, the -mips3 option automatically +# enables either the "n32" or "64" ABI, depending on what machine you +# are compiling on (n32 is explicitly enabled here, just to make sure). +#CFLAGS = -OPT:Olimit=3500 -O -n32 -mips3 -IPA:aggr_cprop=ON -INLINE:dfe=ON:list=ON:must=screen_char,out_char,ui_write,out_flush +#LDFLAGS= -OPT:Olimit=3500 -O -n32 -mips3 -IPA:aggr_cprop=ON -INLINE:dfe=ON:list=ON:must=screen_char,out_char,ui_write,out_flush + +### (K) for SGI Irix machines with 64 bit pointers ("uname -s" says IRIX64) +### Suggested by Jon Wright . +### Tested on R8000 IRIX6.1 Power Indigo2. +### Check /etc/compiler.defaults for your compiler settings. +# either (for 64 bit pointers) uncomment the following line +#GUI_LIB_LOC = -L/usr/lib64 +# then +# 1) make config +# 2) edit auto/config.mk and delete the -lelf entry in the LIBS line +# 3) make +# +# or (for 32bit pointers) uncomment the following line +#EXTRA_DEFS = -n32 +#GUI_LIB_LOC = -L/usr/lib32 +# then +# 1) make config +# 2) edit auto/config.mk, add -n32 to LDFLAGS +# 3) make +# +#Alternatively: use -o32 instead of -n32. +### + +### (C) On SCO Unix v3.2.5 (and probably other versions) the termcap library, +### which is found by configure, doesn't work correctly. Symptom is the +### error message "Termcap entry too long". Uncomment the next line. +### On AIX 4.2.1 (and other versions probably), libtermcap is reported +### not to display properly. +### after changing this, you need to do "make reconfig". +#CONF_TERM_LIB = --with-tlib=curses + +### (E) If you want to use termlib library instead of the automatically found +### one. After changing this, you need to do "make reconfig". +#CONF_TERM_LIB = --with-tlib=termlib + +### (a) ESIX V4.2 (Reinhard Wobst) +#EXTRA_LIBS = -lnsl -lsocket -lgen -lXIM -lXmu -lXext + +### (c) Tandem/NSK (Matthew Woehlke) +#EXTRA_LIBS = -lfloss + +### If you want to use ncurses library instead of the automatically found one +### after changing this, you need to do "make reconfig". +#CONF_TERM_LIB = --with-tlib=ncurses + +### For GCC on MS-Windows, the ".exe" suffix will be added. +#EXEEXT = .exe +#LNKEXT = .exe + +### (O) For LynxOS 2.5.0, tested on PC. +#EXTRA_LIBS = -lXext -lSM -lICE -lbsd +### For LynxOS 3.0.1, tested on PPC +#EXTRA_LIBS= -lXext -lSM -lICE -lnetinet -lXmu -liberty -lX11 +### For LynxOS 3.1.0, tested on PC +#EXTRA_LIBS= -lXext -lSM -lICE -lnetinet -lXmu + + +### (V) For CX/UX 6.2 (on Harris/Concurrent NightHawk 4800, 5800). Remove +### -Qtarget if only in a 5800 environment. (Kipp E. Howard) +#CFLAGS = -O -Qtarget=m88110compat +#EXTRA_LIBS = -lgen + +# The value of QUOTESED comes from auto/config.mk. +# Uncomment the next line to use the default value. +# QUOTESED = sed -e 's/[\\"]/\\&/g' -e 's/\\"/"/' -e 's/\\";$$/";/' + +##################### end of system specific lines ################### }}} + +### Names of the programs and targets {{{1 +VIMTARGET = $(VIMNAME)$(EXEEXT) +EXTARGET = $(EXNAME)$(LNKEXT) +VIEWTARGET = $(VIEWNAME)$(LNKEXT) +GVIMNAME = g$(VIMNAME) +GVIMTARGET = $(GVIMNAME)$(LNKEXT) +GVIEWNAME = g$(VIEWNAME) +GVIEWTARGET = $(GVIEWNAME)$(LNKEXT) +RVIMNAME = r$(VIMNAME) +RVIMTARGET = $(RVIMNAME)$(LNKEXT) +RVIEWNAME = r$(VIEWNAME) +RVIEWTARGET = $(RVIEWNAME)$(LNKEXT) +RGVIMNAME = r$(GVIMNAME) +RGVIMTARGET = $(RGVIMNAME)$(LNKEXT) +RGVIEWNAME = r$(GVIEWNAME) +RGVIEWTARGET = $(RGVIEWNAME)$(LNKEXT) +VIMDIFFNAME = $(VIMNAME)diff +GVIMDIFFNAME = g$(VIMDIFFNAME) +VIMDIFFTARGET = $(VIMDIFFNAME)$(LNKEXT) +GVIMDIFFTARGET = $(GVIMDIFFNAME)$(LNKEXT) +EVIMNAME = e$(VIMNAME) +EVIMTARGET = $(EVIMNAME)$(LNKEXT) +EVIEWNAME = e$(VIEWNAME) +EVIEWTARGET = $(EVIEWNAME)$(LNKEXT) + +### Names of the tools that are also made {{{1 +TOOLS = xxd/xxd$(EXEEXT) + +### Installation directories. The defaults come from configure. {{{1 +# +### prefix the top directory for the data (default "/usr/local") +# +# Uncomment the next line to install Vim in your home directory. +#prefix = $(HOME) + +### exec_prefix is the top directory for the executable (default $(prefix)) +# +# Uncomment the next line to install the Vim executable in "/usr/machine/bin" +#exec_prefix = /usr/machine + +### BINDIR dir for the executable (default "$(exec_prefix)/bin") +### MANDIR dir for the manual pages (default "$(prefix)/man") +### DATADIR dir for the other files (default "$(prefix)/lib" or +# "$(prefix)/share") +# They may be different when using different architectures for the +# executable and a common directory for the other files. +# +# Uncomment the next line to install Vim in "/usr/bin" +#BINDIR = /usr/bin +# Uncomment the next line to install Vim manuals in "/usr/share/man/man1" +#MANDIR = /usr/share/man +# Uncomment the next line to install Vim help files in "/usr/share/vim" +#DATADIR = /usr/share + +### DESTDIR root of the installation tree. This is prepended to the other +# directories. This directory must exist. +#DESTDIR = ~/pkg/vim + +### Directory of the man pages +MAN1DIR = /man1 + +### Vim version (adjusted by a script) +VIMMAJOR = 8 +VIMMINOR = 1 + +### Location of Vim files (should not need to be changed, and {{{1 +### some things might not work when they are changed!) +VIMDIR = /vim +VIMRTDIR = /vim$(VIMMAJOR)$(VIMMINOR) +HELPSUBDIR = /doc +COLSUBDIR = /colors +SYNSUBDIR = /syntax +INDSUBDIR = /indent +AUTOSUBDIR = /autoload +PLUGSUBDIR = /plugin +FTPLUGSUBDIR = /ftplugin +LANGSUBDIR = /lang +COMPSUBDIR = /compiler +KMAPSUBDIR = /keymap +MACROSUBDIR = /macros +PACKSUBDIR = /pack +TOOLSSUBDIR = /tools +TUTORSUBDIR = /tutor +SPELLSUBDIR = /spell +PRINTSUBDIR = /print +PODIR = po + +### VIMLOC common root of the Vim files (all versions) +### VIMRTLOC common root of the runtime Vim files (this version) +### VIMRCLOC compiled-in location for global [g]vimrc files (all versions) +### VIMRUNTIMEDIR compiled-in location for runtime files (optional) +### HELPSUBLOC location for help files +### COLSUBLOC location for colorscheme files +### SYNSUBLOC location for syntax files +### INDSUBLOC location for indent files +### AUTOSUBLOC location for standard autoload files +### PLUGSUBLOC location for standard plugin files +### FTPLUGSUBLOC location for ftplugin files +### LANGSUBLOC location for language files +### COMPSUBLOC location for compiler files +### KMAPSUBLOC location for keymap files +### MACROSUBLOC location for macro files +### PACKSUBLOC location for packages +### TOOLSSUBLOC location for tools files +### TUTORSUBLOC location for tutor files +### SPELLSUBLOC location for spell files +### PRINTSUBLOC location for PostScript files (prolog, latin1, ..) +### SCRIPTLOC location for script files (menu.vim, bugreport.vim, ..) +### You can override these if you want to install them somewhere else. +### Edit feature.h for compile-time settings. +VIMLOC = $(DATADIR)$(VIMDIR) +VIMRTLOC = $(DATADIR)$(VIMDIR)$(VIMRTDIR) +VIMRCLOC = $(VIMLOC) +HELPSUBLOC = $(VIMRTLOC)$(HELPSUBDIR) +COLSUBLOC = $(VIMRTLOC)$(COLSUBDIR) +SYNSUBLOC = $(VIMRTLOC)$(SYNSUBDIR) +INDSUBLOC = $(VIMRTLOC)$(INDSUBDIR) +AUTOSUBLOC = $(VIMRTLOC)$(AUTOSUBDIR) +PLUGSUBLOC = $(VIMRTLOC)$(PLUGSUBDIR) +FTPLUGSUBLOC = $(VIMRTLOC)$(FTPLUGSUBDIR) +LANGSUBLOC = $(VIMRTLOC)$(LANGSUBDIR) +COMPSUBLOC = $(VIMRTLOC)$(COMPSUBDIR) +KMAPSUBLOC = $(VIMRTLOC)$(KMAPSUBDIR) +MACROSUBLOC = $(VIMRTLOC)$(MACROSUBDIR) +PACKSUBLOC = $(VIMRTLOC)$(PACKSUBDIR) +TOOLSSUBLOC = $(VIMRTLOC)$(TOOLSSUBDIR) +TUTORSUBLOC = $(VIMRTLOC)$(TUTORSUBDIR) +SPELLSUBLOC = $(VIMRTLOC)$(SPELLSUBDIR) +PRINTSUBLOC = $(VIMRTLOC)$(PRINTSUBDIR) +SCRIPTLOC = $(VIMRTLOC) + +### Only set VIMRUNTIMEDIR when VIMRTLOC is set to a different location and +### the runtime directory is not below it. +#VIMRUNTIMEDIR = $(VIMRTLOC) + +### Name of the defaults/evim/mswin file target. +VIM_DEFAULTS_FILE = $(DESTDIR)$(SCRIPTLOC)/defaults.vim +EVIM_FILE = $(DESTDIR)$(SCRIPTLOC)/evim.vim +MSWIN_FILE = $(DESTDIR)$(SCRIPTLOC)/mswin.vim + +### Name of the menu file target. +SYS_MENU_FILE = $(DESTDIR)$(SCRIPTLOC)/menu.vim +SYS_SYNMENU_FILE = $(DESTDIR)$(SCRIPTLOC)/synmenu.vim +SYS_DELMENU_FILE = $(DESTDIR)$(SCRIPTLOC)/delmenu.vim + +### Name of the bugreport file target. +SYS_BUGR_FILE = $(DESTDIR)$(SCRIPTLOC)/bugreport.vim + +### Name of the rgb.txt file target. +SYS_RGB_FILE = $(DESTDIR)$(SCRIPTLOC)/rgb.txt + +### Name of the file type detection file target. +SYS_FILETYPE_FILE = $(DESTDIR)$(SCRIPTLOC)/filetype.vim + +### Name of the file type detection file target. +SYS_FTOFF_FILE = $(DESTDIR)$(SCRIPTLOC)/ftoff.vim + +### Name of the file type detection script file target. +SYS_SCRIPTS_FILE = $(DESTDIR)$(SCRIPTLOC)/scripts.vim + +### Name of the ftplugin-on file target. +SYS_FTPLUGIN_FILE = $(DESTDIR)$(SCRIPTLOC)/ftplugin.vim + +### Name of the ftplugin-off file target. +SYS_FTPLUGOF_FILE = $(DESTDIR)$(SCRIPTLOC)/ftplugof.vim + +### Name of the indent-on file target. +SYS_INDENT_FILE = $(DESTDIR)$(SCRIPTLOC)/indent.vim + +### Name of the indent-off file target. +SYS_INDOFF_FILE = $(DESTDIR)$(SCRIPTLOC)/indoff.vim + +### Name of the option window script file target. +SYS_OPTWIN_FILE = $(DESTDIR)$(SCRIPTLOC)/optwin.vim + +# Program to install the program in the target directory. Could also be "mv". +INSTALL_PROG = cp + +# Program to install the data in the target directory. Cannot be "mv"! +INSTALL_DATA = cp +INSTALL_DATA_R = cp -r + +### Program to run on installed binary. Use the second one to disable strip. +#STRIP = strip +#STRIP = /bin/true + +### Permissions for binaries {{{1 +BINMOD = 755 + +### Permissions for man page +MANMOD = 644 + +### Permissions for help files +HELPMOD = 644 + +### Permissions for Perl and shell scripts +SCRIPTMOD = 755 + +### Permission for Vim script files (menu.vim, bugreport.vim, ..) +VIMSCRIPTMOD = 644 + +### Permissions for all directories that are created +DIRMOD = 755 + +### Permissions for all other files that are created +FILEMOD = 644 + +# Where to copy the man and help files from +HELPSOURCE = ../runtime/doc + +# Where to copy the script files from (menu, bugreport) +SCRIPTSOURCE = ../runtime + +# Where to copy the colorscheme files from +COLSOURCE = ../runtime/colors + +# Where to copy the syntax files from +SYNSOURCE = ../runtime/syntax + +# Where to copy the indent files from +INDSOURCE = ../runtime/indent + +# Where to copy the standard plugin files from +AUTOSOURCE = ../runtime/autoload + +# Where to copy the standard plugin files from +PLUGSOURCE = ../runtime/plugin + +# Where to copy the ftplugin files from +FTPLUGSOURCE = ../runtime/ftplugin + +# Where to copy the macro files from +MACROSOURCE = ../runtime/macros + +# Where to copy the package files from +PACKSOURCE = ../runtime/pack + +# Where to copy the tools files from +TOOLSSOURCE = ../runtime/tools + +# Where to copy the tutor files from +TUTORSOURCE = ../runtime/tutor + +# Where to copy the spell files from +SPELLSOURCE = ../runtime/spell + +# Where to look for language specific files +LANGSOURCE = ../runtime/lang + +# Where to look for compiler files +COMPSOURCE = ../runtime/compiler + +# Where to look for keymap files +KMAPSOURCE = ../runtime/keymap + +# Where to look for print resource files +PRINTSOURCE = ../runtime/print + +# If you are using Linux, you might want to use this to make vim the +# default vi editor, it will create a link from vi to Vim when doing +# "make install". An existing file will be overwritten! +# When not using it, some make programs can't handle an undefined $(LINKIT). +#LINKIT = ln -f -s $(DEST_BIN)/$(VIMTARGET) $(DESTDIR)/usr/bin/vi +LINKIT = @echo >/dev/null + +### +### GRAPHICAL USER INTERFACE (GUI). {{{1 +### 'configure --enable-gui' can enable one of these for you if you did set +### a corresponding CONF_OPT_GUI above and have X11. +### Override configures choice by uncommenting all the following lines. +### As they are, the GUI is disabled. Replace "NONE" with "ATHENA" or "MOTIF" +### for enabling the Athena or Motif GUI. +#GUI_SRC = $(NONE_SRC) +#GUI_OBJ = $(NONE_OBJ) +#GUI_DEFS = $(NONE_DEFS) +#GUI_IPATH = $(NONE_IPATH) +#GUI_LIBS_DIR = $(NONE_LIBS_DIR) +#GUI_LIBS1 = $(NONE_LIBS1) +#GUI_LIBS2 = $(NONE_LIBS2) +#GUI_INSTALL = $(NONE_INSTALL) +#GUI_TARGETS = $(NONE_TARGETS) +#GUI_MAN_TARGETS= $(NONE_MAN_TARGETS) +#GUI_TESTTARGET = $(NONE_TESTTARGET) +#GUI_BUNDLE = $(NONE_BUNDLE) + +# Without a GUI install the normal way. +NONE_INSTALL = install_normal + +### GTK GUI +GTK_SRC = gui.c gui_gtk.c gui_gtk_x11.c gui_gtk_f.c \ + gui_beval.c $(GRESOURCE_SRC) +GTK_OBJ = objects/gui.o objects/gui_gtk.o objects/gui_gtk_x11.o \ + objects/gui_gtk_f.o \ + objects/gui_beval.o $(GRESOURCE_OBJ) +GTK_DEFS = -DFEAT_GUI_GTK $(NARROW_PROTO) +GTK_IPATH = $(GUI_INC_LOC) +GTK_LIBS_DIR = $(GUI_LIB_LOC) +GTK_LIBS1 = +GTK_LIBS2 = $(GTK_LIBNAME) +GTK_INSTALL = install_normal install_gui_extra +GTK_TARGETS = installglinks +GTK_MAN_TARGETS = yes +GTK_TESTTARGET = gui +GTK_BUNDLE = + +### Motif GUI +MOTIF_SRC = gui.c gui_motif.c gui_x11.c gui_beval.c \ + gui_xmdlg.c gui_xmebw.c +MOTIF_OBJ = objects/gui.o objects/gui_motif.o objects/gui_x11.o \ + objects/gui_beval.o \ + objects/gui_xmdlg.o objects/gui_xmebw.o +MOTIF_DEFS = -DFEAT_GUI_MOTIF $(NARROW_PROTO) +MOTIF_IPATH = $(GUI_INC_LOC) +MOTIF_LIBS_DIR = $(GUI_LIB_LOC) +MOTIF_LIBS1 = +MOTIF_LIBS2 = $(MOTIF_LIBNAME) -lXt +MOTIF_INSTALL = install_normal install_gui_extra +MOTIF_TARGETS = installglinks +MOTIF_MAN_TARGETS = yes +MOTIF_TESTTARGET = gui +MOTIF_BUNDLE = + +### Athena GUI +### Use Xaw3d to make the menus look a little bit nicer +#XAW_LIB = -lXaw3d +XAW_LIB = -lXaw + +### When using Xaw3d, uncomment/comment the following lines to also get the +### scrollbars from Xaw3d. +#ATHENA_SRC = gui.c gui_athena.c gui_x11.c gui_beval.c gui_at_fs.c +#ATHENA_OBJ = objects/gui.o objects/gui_athena.o objects/gui_x11.o \ +# objects/gui_beval.o objects/gui_at_fs.o +#ATHENA_DEFS = -DFEAT_GUI_ATHENA $(NARROW_PROTO) \ +# -Dvim_scrollbarWidgetClass=scrollbarWidgetClass \ +# -Dvim_XawScrollbarSetThumb=XawScrollbarSetThumb +ATHENA_SRC = gui.c gui_athena.c gui_x11.c gui_beval.c \ + gui_at_sb.c gui_at_fs.c +ATHENA_OBJ = objects/gui.o objects/gui_athena.o objects/gui_x11.o \ + objects/gui_beval.o \ + objects/gui_at_sb.o objects/gui_at_fs.o +ATHENA_DEFS = -DFEAT_GUI_ATHENA $(NARROW_PROTO) + +ATHENA_IPATH = $(GUI_INC_LOC) +ATHENA_LIBS_DIR = $(GUI_LIB_LOC) +ATHENA_LIBS1 = $(XAW_LIB) +ATHENA_LIBS2 = -lXt +ATHENA_INSTALL = install_normal install_gui_extra +ATHENA_TARGETS = installglinks +ATHENA_MAN_TARGETS = yes +ATHENA_TESTTARGET = gui +ATHENA_BUNDLE = + +### neXtaw GUI +NEXTAW_LIB = -lneXtaw + +NEXTAW_SRC = gui.c gui_athena.c gui_x11.c gui_beval.c gui_at_fs.c +NEXTAW_OBJ = objects/gui.o objects/gui_athena.o objects/gui_x11.o \ + objects/gui_beval.o objects/gui_at_fs.o +NEXTAW_DEFS = -DFEAT_GUI_ATHENA -DFEAT_GUI_NEXTAW $(NARROW_PROTO) + +NEXTAW_IPATH = $(GUI_INC_LOC) +NEXTAW_LIBS_DIR = $(GUI_LIB_LOC) +NEXTAW_LIBS1 = $(NEXTAW_LIB) +NEXTAW_LIBS2 = -lXt +NEXTAW_INSTALL = install_normal install_gui_extra +NEXTAW_TARGETS = installglinks +NEXTAW_MAN_TARGETS = yes +NEXTAW_TESTTARGET = gui +NEXTAW_BUNDLE = + +### (J) Sun OpenWindows 3.2 (SunOS 4.1.x) or earlier that produce these ld +# errors: ld: Undefined symbol +# _get_wmShellWidgetClass +# _get_applicationShellWidgetClass +# then you need to get patches 100512-02 and 100573-03 from Sun. In the +# meantime, uncomment the following GUI_X_LIBS definition as a workaround: +#GUI_X_LIBS = -Bstatic -lXmu -Bdynamic -lXext +# If you also get cos, sin etc. as undefined symbols, try uncommenting this +# too: +#EXTRA_LIBS = /usr/openwin/lib/libXmu.sa -lm + +# PHOTON GUI +PHOTONGUI_SRC = gui.c gui_photon.c +PHOTONGUI_OBJ = objects/gui.o objects/gui_photon.o +PHOTONGUI_DEFS = -DFEAT_GUI_PHOTON +PHOTONGUI_IPATH = +PHOTONGUI_LIBS_DIR = +PHOTONGUI_LIBS1 = -lph -lphexlib +PHOTONGUI_LIBS2 = +PHOTONGUI_INSTALL = install_normal install_gui_extra +PHOTONGUI_TARGETS = installglinks +PHOTONGUI_MAN_TARGETS = yes +PHOTONGUI_TESTTARGET = gui +PHOTONGUI_BUNDLE = + +# CARBON GUI +CARBONGUI_SRC = gui.c gui_mac.c +CARBONGUI_OBJ = objects/gui.o objects/gui_mac.o +CARBONGUI_DEFS = -DFEAT_GUI_MAC -fno-common -fpascal-strings \ + -Wall -Wno-unknown-pragmas \ + -mdynamic-no-pic -pipe +CARBONGUI_IPATH = -I. -Iproto +CARBONGUI_LIBS_DIR = +CARBONGUI_LIBS1 = -framework Carbon +CARBONGUI_LIBS2 = +CARBONGUI_INSTALL = install_macosx +CARBONGUI_TARGETS = +CARBONGUI_MAN_TARGETS = +CARBONGUI_TESTTARGET = gui +CARBONGUI_BUNDLE = gui_bundle +APPDIR = $(VIMNAME).app +CARBONGUI_TESTARG = VIMPROG=../$(APPDIR)/Contents/MacOS/$(VIMTARGET) + +# All GUI files +ALL_GUI_SRC = gui.c gui_gtk.c gui_gtk_f.c gui_motif.c gui_xmdlg.c gui_xmebw.c gui_athena.c gui_gtk_x11.c gui_x11.c gui_at_sb.c gui_at_fs.c +ALL_GUI_PRO = gui.pro gui_gtk.pro gui_motif.pro gui_xmdlg.pro gui_athena.pro gui_gtk_x11.pro gui_x11.pro gui_w32.pro gui_photon.pro + +# }}} + +TERM_DEPS = \ + libvterm/include/vterm.h \ + libvterm/include/vterm_keycodes.h \ + libvterm/src/rect.h \ + libvterm/src/utf8.h \ + libvterm/src/vterm_internal.h + +TERM_SRC = libvterm/src/*.c + +XDIFF_SRC = \ + xdiff/xdiffi.c \ + xdiff/xemit.c \ + xdiff/xprepare.c \ + xdiff/xutils.c \ + xdiff/xhistogram.c \ + xdiff/xpatience.c \ + +XDIFF_OBJS = \ + objects/xdiffi.o \ + objects/xemit.o \ + objects/xprepare.o \ + objects/xutils.o \ + objects/xhistogram.o \ + objects/xpatience.o \ + +XDIFF_INCL = \ + xdiff/xdiff.h \ + xdiff/xdiffi.h \ + xdiff/xemit.h \ + xdiff/xinclude.h \ + xdiff/xmacros.h \ + xdiff/xprepare.h \ + xdiff/xtypes.h \ + xdiff/xutils.h \ + +### Command to create dependencies based on #include "..." +### prototype headers are ignored due to -DPROTO, system +### headers #include <...> are ignored if we use the -MM option, as +### e.g. provided by gcc-cpp. +### Include FEAT_GUI to get dependency on gui.h +### Need to change "-I /" to "-isystem /" for GCC 3.x. +CPP_DEPEND = $(CC) -I$(srcdir) -M$(CPP_MM) \ + `echo "$(DEPEND_CFLAGS)" $(DEPEND_CFLAGS_FILTER)` + +# flags for cproto +# This is for cproto 3 patchlevel 8 or below +# __inline, __attribute__ and __extension__ are not recognized by cproto +# G_IMPLEMENT_INLINES is to avoid functions defined in glib/gutils.h. +#NO_ATTR = -D__inline= -D__inline__= -DG_IMPLEMENT_INLINES \ +# -D"__attribute__\\(x\\)=" -D"__asm__\\(x\\)=" \ +# -D__extension__= -D__restrict="" \ +# -D__gnuc_va_list=char -D__builtin_va_list=char + +# +# This is for cproto 3 patchlevel 9 or above (currently 4.6, 4.7g) +# __inline and __attribute__ are now recognized by cproto +# -D"foo()=" is not supported by all compilers so do not use it +NO_ATTR= +# +# Use this for cproto 3 patchlevel 6 or below (use "cproto -V" to check): +# PROTO_FLAGS = -f4 -d -E"$(CPP)" $(NO_ATTR) +# +# Use this for cproto 3 patchlevel 7 or above (use "cproto -V" to check): +PROTO_FLAGS = -d -E"$(CPP)" $(NO_ATTR) + + +################################################ +## no changes required below this line ## +################################################ + +SHELL = /bin/sh + +# We would normally use "mkdir -p" but it doesn't work properly everywhere. +# Using AC_PROG_MKDIR_P in configure.ac has a problem with the "auto" +# directory. Always use the install-sh script, it's slower but reliable. +MKDIR_P = $(SHELL) install-sh -c -d + +.SUFFIXES: +.SUFFIXES: .c .o .pro + +VTERM_CFLAGS = -Ilibvterm/include + +PRE_DEFS = -Iproto $(DEFS) $(GUI_DEFS) $(GUI_IPATH) $(CPPFLAGS) $(EXTRA_IPATHS) +POST_DEFS = $(X_CFLAGS) $(MZSCHEME_CFLAGS) $(EXTRA_DEFS) + +ALL_CFLAGS = $(PRE_DEFS) $(CFLAGS) $(PROFILE_CFLAGS) $(SANITIZER_CFLAGS) $(LEAK_CFLAGS) $(ABORT_CLFAGS) $(POST_DEFS) + +# Exclude $CFLAGS for osdef.sh, for Mac 10.4 some flags don't work together +# with "-E". +OSDEF_CFLAGS = $(PRE_DEFS) $(POST_DEFS) + +LINT_CFLAGS = -DLINT -I. $(PRE_DEFS) $(POST_DEFS) \ + $(RUBY_CFLAGS) $(LUA_CFLAGS) $(PERL_CFLAGS) $(PYTHON_CFLAGS) \ + $(PYTHON3_CFLAGS) $(TCL_CFLAGS) $(VTERM_CFLAGS) \ + -Dinline= -D__extension__= -Dalloca=alloca + +LINT_EXTRA = -DHANGUL_INPUT -D"__attribute__(x)=" + +DEPEND_CFLAGS = -DPROTO -DDEPEND -DFEAT_GUI $(LINT_CFLAGS) + +# Note: MZSCHEME_LIBS must come before LIBS, because LIBS adds -lm which is +# needed by racket. +ALL_LIB_DIRS = $(GUI_LIBS_DIR) $(X_LIBS_DIR) +ALL_LIBS = \ + $(GUI_LIBS1) \ + $(GUI_X_LIBS) \ + $(GUI_LIBS2) \ + $(X_PRE_LIBS) \ + $(X_LIBS) \ + $(X_EXTRA_LIBS) \ + $(MZSCHEME_LIBS) \ + $(LIBS) \ + $(EXTRA_LIBS) \ + $(LUA_LIBS) \ + $(PERL_LIBS) \ + $(PYTHON_LIBS) \ + $(PYTHON3_LIBS) \ + $(TCL_LIBS) \ + $(RUBY_LIBS) \ + $(PROFILE_LIBS) \ + $(SANITIZER_LIBS) \ + $(LEAK_LIBS) + +# abbreviations +DEST_BIN = $(DESTDIR)$(BINDIR) +DEST_VIM = $(DESTDIR)$(VIMLOC) +DEST_RT = $(DESTDIR)$(VIMRTLOC) +DEST_HELP = $(DESTDIR)$(HELPSUBLOC) +DEST_COL = $(DESTDIR)$(COLSUBLOC) +DEST_SYN = $(DESTDIR)$(SYNSUBLOC) +DEST_IND = $(DESTDIR)$(INDSUBLOC) +DEST_AUTO = $(DESTDIR)$(AUTOSUBLOC) +DEST_PLUG = $(DESTDIR)$(PLUGSUBLOC) +DEST_FTP = $(DESTDIR)$(FTPLUGSUBLOC) +DEST_LANG = $(DESTDIR)$(LANGSUBLOC) +DEST_COMP = $(DESTDIR)$(COMPSUBLOC) +DEST_KMAP = $(DESTDIR)$(KMAPSUBLOC) +DEST_MACRO = $(DESTDIR)$(MACROSUBLOC) +DEST_PACK = $(DESTDIR)$(PACKSUBLOC) +DEST_TOOLS = $(DESTDIR)$(TOOLSSUBLOC) +DEST_TUTOR = $(DESTDIR)$(TUTORSUBLOC) +DEST_SPELL = $(DESTDIR)$(SPELLSUBLOC) +DEST_SCRIPT = $(DESTDIR)$(SCRIPTLOC) +DEST_PRINT = $(DESTDIR)$(PRINTSUBLOC) +DEST_MAN_TOP = $(DESTDIR)$(MANDIR) + +# We assume that the ".../man/xx/man1/" directory is for latin1 manual pages. +# Some systems use UTF-8, but these should find the ".../man/xx.UTF-8/man1/" +# directory first. +# FreeBSD uses ".../man/xx.ISO8859-1/man1" for latin1, use that one too. +DEST_MAN = $(DEST_MAN_TOP)$(MAN1DIR) +DEST_MAN_DA = $(DEST_MAN_TOP)/da$(MAN1DIR) +DEST_MAN_DA_I = $(DEST_MAN_TOP)/da.ISO8859-1$(MAN1DIR) +DEST_MAN_DA_U = $(DEST_MAN_TOP)/da.UTF-8$(MAN1DIR) +DEST_MAN_DE = $(DEST_MAN_TOP)/de$(MAN1DIR) +DEST_MAN_DE_I = $(DEST_MAN_TOP)/de.ISO8859-1$(MAN1DIR) +DEST_MAN_DE_U = $(DEST_MAN_TOP)/de.UTF-8$(MAN1DIR) +DEST_MAN_FR = $(DEST_MAN_TOP)/fr$(MAN1DIR) +DEST_MAN_FR_I = $(DEST_MAN_TOP)/fr.ISO8859-1$(MAN1DIR) +DEST_MAN_FR_U = $(DEST_MAN_TOP)/fr.UTF-8$(MAN1DIR) +DEST_MAN_IT = $(DEST_MAN_TOP)/it$(MAN1DIR) +DEST_MAN_IT_I = $(DEST_MAN_TOP)/it.ISO8859-1$(MAN1DIR) +DEST_MAN_IT_U = $(DEST_MAN_TOP)/it.UTF-8$(MAN1DIR) +DEST_MAN_JA_U = $(DEST_MAN_TOP)/ja$(MAN1DIR) +DEST_MAN_PL = $(DEST_MAN_TOP)/pl$(MAN1DIR) +DEST_MAN_PL_I = $(DEST_MAN_TOP)/pl.ISO8859-2$(MAN1DIR) +DEST_MAN_PL_U = $(DEST_MAN_TOP)/pl.UTF-8$(MAN1DIR) +DEST_MAN_RU = $(DEST_MAN_TOP)/ru.KOI8-R$(MAN1DIR) +DEST_MAN_RU_U = $(DEST_MAN_TOP)/ru.UTF-8$(MAN1DIR) + +# stuff common to all systems +include Make_all.mak + +# get the list of tests +include testdir/Make_all.mak + +# BASIC_SRC: files that are always used +# GUI_SRC: extra GUI files for current configuration +# ALL_GUI_SRC: all GUI files for Unix +# +# SRC: files used for current configuration +# ALL_SRC: source files used for make depend and make lint + +BASIC_SRC = \ + arabic.c \ + autocmd.c \ + beval.c \ + blob.c \ + blowfish.c \ + buffer.c \ + charset.c \ + crypt.c \ + crypt_zip.c \ + dict.c \ + diff.c \ + digraph.c \ + edit.c \ + eval.c \ + evalfunc.c \ + ex_cmds.c \ + ex_cmds2.c \ + ex_docmd.c \ + ex_eval.c \ + ex_getln.c \ + farsi.c \ + fileio.c \ + fold.c \ + getchar.c \ + hardcopy.c \ + hashtab.c \ + if_cscope.c \ + if_xcmdsrv.c \ + indent.c \ + json.c \ + list.c \ + main.c \ + mark.c \ + memfile.c \ + memline.c \ + menu.c \ + message.c \ + misc1.c \ + misc2.c \ + move.c \ + mbyte.c \ + normal.c \ + ops.c \ + option.c \ + os_unix.c \ + auto/pathdef.c \ + popupmnu.c \ + pty.c \ + quickfix.c \ + regexp.c \ + screen.c \ + search.c \ + sha256.c \ + sign.c \ + spell.c \ + spellfile.c \ + syntax.c \ + tag.c \ + term.c \ + terminal.c \ + textprop.c \ + ui.c \ + undo.c \ + userfunc.c \ + version.c \ + window.c \ + $(OS_EXTRA_SRC) + +SRC = $(BASIC_SRC) \ + $(GUI_SRC) \ + $(TERM_SRC) \ + $(XDIFF_SRC) \ + $(HANGULIN_SRC) \ + $(LUA_SRC) \ + $(MZSCHEME_SRC) \ + $(PERL_SRC) \ + $(PYTHON_SRC) $(PYTHON3_SRC) \ + $(TCL_SRC) \ + $(RUBY_SRC) + +EXTRA_SRC = hangulin.c if_lua.c if_mzsch.c auto/if_perl.c if_perlsfio.c \ + if_python.c if_python3.c if_tcl.c if_ruby.c \ + gui_beval.c netbeans.c channel.c \ + $(GRESOURCE_SRC) + +# Unittest files +JSON_TEST_SRC = json_test.c +JSON_TEST_TARGET = json_test$(EXEEXT) +KWORD_TEST_SRC = kword_test.c +KWORD_TEST_TARGET = kword_test$(EXEEXT) +MEMFILE_TEST_SRC = memfile_test.c +MEMFILE_TEST_TARGET = memfile_test$(EXEEXT) +MESSAGE_TEST_SRC = message_test.c +MESSAGE_TEST_TARGET = message_test$(EXEEXT) + +UNITTEST_SRC = $(JSON_TEST_SRC) $(KWORD_TEST_SRC) $(MEMFILE_TEST_SRC) $(MESSAGE_TEST_SRC) +UNITTEST_TARGETS = $(JSON_TEST_TARGET) $(KWORD_TEST_TARGET) $(MEMFILE_TEST_TARGET) $(MESSAGE_TEST_TARGET) +RUN_UNITTESTS = run_json_test run_kword_test run_memfile_test run_message_test + +# All sources, also the ones that are not configured +ALL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(UNITTEST_SRC) \ + $(EXTRA_SRC) $(TERM_SRC) $(XDIFF_SRC) + +# Which files to check with lint. Select one of these three lines. ALL_SRC +# checks more, but may not work well for checking a GUI that wasn't configured. +# The perl sources also don't work well with lint. +LINT_SRC = $(BASIC_SRC) $(GUI_SRC) $(HANGULIN_SRC) \ + $(PYTHON_SRC) $(PYTHON3_SRC) $(TCL_SRC) \ + $(NETBEANS_SRC) $(CHANNEL_SRC) $(TERM_SRC) +#LINT_SRC = $(SRC) +#LINT_SRC = $(ALL_SRC) +#LINT_SRC = $(BASIC_SRC) + +OBJ_COMMON = \ + objects/arabic.o \ + objects/autocmd.o \ + objects/beval.o \ + objects/buffer.o \ + objects/blob.o \ + objects/blowfish.o \ + objects/crypt.o \ + objects/crypt_zip.o \ + objects/dict.o \ + objects/diff.o \ + objects/digraph.o \ + objects/edit.o \ + objects/eval.o \ + objects/evalfunc.o \ + objects/ex_cmds.o \ + objects/ex_cmds2.o \ + objects/ex_docmd.o \ + objects/ex_eval.o \ + objects/ex_getln.o \ + objects/farsi.o \ + objects/fileio.o \ + objects/fold.o \ + objects/getchar.o \ + objects/hardcopy.o \ + objects/hashtab.o \ + $(HANGULIN_OBJ) \ + objects/if_cscope.o \ + objects/if_xcmdsrv.o \ + objects/indent.o \ + objects/list.o \ + objects/mark.o \ + objects/memline.o \ + objects/menu.o \ + objects/misc1.o \ + objects/misc2.o \ + objects/move.o \ + objects/mbyte.o \ + objects/normal.o \ + objects/ops.o \ + objects/option.o \ + objects/os_unix.o \ + objects/pathdef.o \ + objects/popupmnu.o \ + objects/pty.o \ + objects/quickfix.o \ + objects/regexp.o \ + objects/screen.o \ + objects/search.o \ + objects/sha256.o \ + objects/sign.o \ + objects/spell.o \ + objects/spellfile.o \ + objects/syntax.o \ + objects/tag.o \ + objects/term.o \ + objects/terminal.o \ + objects/textprop.o \ + objects/ui.o \ + objects/undo.o \ + objects/userfunc.o \ + objects/version.o \ + objects/window.o \ + $(GUI_OBJ) \ + $(TERM_OBJ) \ + $(LUA_OBJ) \ + $(MZSCHEME_OBJ) \ + $(PERL_OBJ) \ + $(PYTHON_OBJ) \ + $(PYTHON3_OBJ) \ + $(TCL_OBJ) \ + $(RUBY_OBJ) \ + $(OS_EXTRA_OBJ) \ + $(NETBEANS_OBJ) \ + $(CHANNEL_OBJ) \ + $(XDIFF_OBJS) + +# The files included by tests are not in OBJ_COMMON. +OBJ_MAIN = \ + objects/charset.o \ + objects/json.o \ + objects/main.o \ + objects/memfile.o \ + objects/message.o + +OBJ = $(OBJ_COMMON) $(OBJ_MAIN) + +OBJ_JSON_TEST = \ + objects/charset.o \ + objects/memfile.o \ + objects/message.o \ + objects/json_test.o + +JSON_TEST_OBJ = $(OBJ_COMMON) $(OBJ_JSON_TEST) + +OBJ_KWORD_TEST = \ + objects/json.o \ + objects/memfile.o \ + objects/message.o \ + objects/kword_test.o + +KWORD_TEST_OBJ = $(OBJ_COMMON) $(OBJ_KWORD_TEST) + +OBJ_MEMFILE_TEST = \ + objects/charset.o \ + objects/json.o \ + objects/message.o \ + objects/memfile_test.o + +MEMFILE_TEST_OBJ = $(OBJ_COMMON) $(OBJ_MEMFILE_TEST) + +OBJ_MESSAGE_TEST = \ + objects/charset.o \ + objects/json.o \ + objects/memfile.o \ + objects/message_test.o + +MESSAGE_TEST_OBJ = $(OBJ_COMMON) $(OBJ_MESSAGE_TEST) + +ALL_OBJ = $(OBJ_COMMON) \ + $(OBJ_MAIN) \ + $(OBJ_JSON_TEST) \ + $(OBJ_KWORD_TEST) \ + $(OBJ_MEMFILE_TEST) \ + $(OBJ_MESSAGE_TEST) + + +PRO_AUTO = \ + arabic.pro \ + autocmd.pro \ + blowfish.pro \ + buffer.pro \ + charset.pro \ + crypt.pro \ + crypt_zip.pro \ + dict.pro \ + diff.pro \ + digraph.pro \ + edit.pro \ + eval.pro \ + evalfunc.pro \ + ex_cmds.pro \ + ex_cmds2.pro \ + ex_docmd.pro \ + ex_eval.pro \ + ex_getln.pro \ + farsi.pro \ + fileio.pro \ + fold.pro \ + getchar.pro \ + hardcopy.pro \ + hashtab.pro \ + hangulin.pro \ + if_cscope.pro \ + if_lua.pro \ + if_mzsch.pro \ + if_python.pro \ + if_python3.pro \ + if_ruby.pro \ + if_xcmdsrv.pro \ + indent.pro \ + json.pro \ + list.pro \ + main.pro \ + mark.pro \ + mbyte.pro \ + memfile.pro \ + memline.pro \ + menu.pro \ + message.pro \ + misc1.pro \ + misc2.pro \ + move.pro \ + normal.pro \ + ops.pro \ + option.pro \ + os_mac_conv.pro \ + os_unix.pro \ + popupmnu.pro \ + pty.pro \ + quickfix.pro \ + regexp.pro \ + screen.pro \ + search.pro \ + sha256.pro \ + sign.pro \ + spell.pro \ + spellfile.pro \ + syntax.pro \ + tag.pro \ + term.pro \ + terminal.pro \ + termlib.pro \ + textprop.pro \ + ui.pro \ + undo.pro \ + userfunc.pro \ + version.pro \ + window.pro \ + beval.pro \ + gui_beval.pro \ + netbeans.pro \ + channel.pro \ + $(ALL_GUI_PRO) \ + $(TCL_PRO) + +# Resources used for the Mac are in one directory. +RSRC_DIR = os_mac_rsrc + +PRO_MANUAL = os_amiga.pro os_win32.pro \ + os_mswin.pro winclip.pro os_beos.pro os_vms.pro $(PERL_PRO) + +# Default target is making the executable and tools +all: $(VIMTARGET) $(TOOLS) languages $(GUI_BUNDLE) + +tools: $(TOOLS) + +# Run configure with all the setting from above. +# +# Note: auto/config.h doesn't depend on configure, because running configure +# doesn't always update auto/config.h. The timestamp isn't changed if the +# file contents didn't change (to avoid recompiling everything). Including a +# dependency on auto/config.h would cause running configure each time when +# auto/config.h isn't updated. The dependency on auto/config.mk should make +# sure configure is run when it's needed. +# +# Remove the config.cache every time, once in a while it causes problems that +# are very hard to figure out. +# +config auto/config.mk: auto/configure config.mk.in config.h.in + -rm -f auto/config.cache + if test "X$(MAKECMDGOALS)" != "Xclean" \ + -a "X$(MAKECMDGOALS)" != "Xdistclean" \ + -a "X$(MAKECMDGOALS)" != "Xautoconf" \ + -a "X$(MAKECMDGOALS)" != "Xreconfig"; then \ + GUI_INC_LOC="$(GUI_INC_LOC)" GUI_LIB_LOC="$(GUI_LIB_LOC)" \ + CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ + LDFLAGS="$(LDFLAGS)" $(CONF_SHELL) srcdir="$(srcdir)" \ + ./configure $(CONF_OPT_GUI) $(CONF_OPT_X) $(CONF_OPT_XSMP) \ + $(CONF_OPT_AUTOSERVE) $(CONF_OPT_DARWIN) $(CONF_OPT_FAIL) \ + $(CONF_OPT_PERL) $(CONF_OPT_PYTHON) $(CONF_OPT_PYTHON3) \ + $(CONF_OPT_TCL) $(CONF_OPT_RUBY) $(CONF_OPT_NLS) \ + $(CONF_OPT_CSCOPE) $(CONF_OPT_MULTIBYTE) $(CONF_OPT_INPUT) \ + $(CONF_OPT_OUTPUT) $(CONF_OPT_GPM) \ + $(CONF_OPT_FEAT) $(CONF_TERM_LIB) \ + $(CONF_OPT_COMPBY) $(CONF_OPT_ACL) $(CONF_OPT_NETBEANS) \ + $(CONF_OPT_CHANNEL) $(CONF_OPT_TERMINAL) \ + $(CONF_ARGS) $(CONF_ARGS1) $(CONF_ARGS2) $(CONF_ARGS3) \ + $(CONF_ARGS4) $(CONF_ARGS5) $(CONF_ARGS6) \ + $(CONF_OPT_MZSCHEME) $(CONF_OPT_PLTHOME) \ + $(CONF_OPT_LUA) $(CONF_OPT_LUA_PREFIX) \ + $(CONF_OPT_SYSMOUSE); \ + fi + +# Use "make reconfig" to rerun configure without cached values. +# When config.h changes, most things will be recompiled automatically. +# Invoke $(MAKE) to run config with the empty auto/config.mk. +# Invoke $(MAKE) to build all with the filled auto/config.mk. +reconfig: scratch clean + $(MAKE) -f Makefile config + $(MAKE) -f Makefile all + +# Run autoconf to produce auto/configure. +# Note: +# - DO NOT RUN autoconf MANUALLY! It will overwrite ./configure instead of +# producing auto/configure. +# - autoconf is not run automatically, because a patch usually changes both +# configure.ac and auto/configure but can't update the timestamps. People +# who do not have (the correct version of) autoconf would run into trouble. +# +# Two tricks are required to make autoconf put its output in the "auto" dir: +# - Temporarily move the ./configure script to ./configure.save. Don't +# overwrite it, it's probably the result of an aborted autoconf. +# - Use sed to change ./config.log to auto/config.log in the configure script. +# Autoconf 2.5x (2.59 at least) produces a few more files that we need to take +# care of: +# - configure.lineno: has the line numbers replaced with $LINENO. That +# improves patches a LOT, thus use it instead (until someone says it doesn't +# work on some system). +# - autom4te.cache directory is created and not cleaned up. Delete it. +# - Uses ">config.log" instead of "./config.log". +autoconf: + if test ! -f configure.save; then mv configure configure.save; fi + $(AUTOCONF) + sed -e 's+>config.log+>auto/config.log+' -e 's+\./config.log+auto/config.log+' configure > auto/configure + chmod 755 auto/configure + mv -f configure.save configure + -rm -rf autom4te.cache + -rm -f auto/config.status auto/config.cache + +# Run vim script to generate the Ex command lookup table. +# This only needs to be run when a command name has been added or changed. +# If this fails because you don't have Vim yet, first build and install Vim +# without changes. +cmdidxs: ex_cmds.h + vim -u NONE -i NONE -X -S create_cmdidxs.vim + + +# The normal command to compile a .c file to its .o file. +# Without or with ALL_CFLAGS. +CCC_NF = $(CC) -c -I$(srcdir) +CCC = $(CCC_NF) $(ALL_CFLAGS) + + +# Link the target for normal use or debugging. +# A shell script is used to try linking without unnecessary libraries. +$(VIMTARGET): auto/config.mk objects $(OBJ) version.c version.h + $(CCC) version.c -o objects/version.o + @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ + -o $(VIMTARGET) $(OBJ) $(ALL_LIBS)" \ + MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + sh $(srcdir)/link.sh + +xxd/xxd$(EXEEXT): xxd/xxd.c + cd xxd; CC="$(CC)" CFLAGS="$(CPPFLAGS) $(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + $(MAKE) -f Makefile + +# Build the language specific files if they were unpacked. +# Generate the converted .mo files separately, it's no problem if this fails. +languages: + @if test -n "$(MAKEMO)" -a -f $(PODIR)/Makefile; then \ + cd $(PODIR); \ + CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix); \ + fi + -@if test -n "$(MAKEMO)" -a -f $(PODIR)/Makefile; then \ + cd $(PODIR); \ + CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix) converted; \ + fi + +# Update the *.po files for changes in the sources. Only run manually. +update-po: + cd $(PODIR); CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix) update-po + +# Generate function prototypes. This is not needed to compile vim, but if +# you want to use it, cproto is out there on the net somewhere -- Webb +# +# When generating os_amiga.pro and os_win32.pro there will be a +# few include files that can not be found, that's OK. + +proto: $(PRO_AUTO) $(PRO_MANUAL) + +# Filter out arguments that cproto doesn't support. +# Don't pass "-pthread", "-fwrapv" and similar arguments to cproto, it sees +# them as a list of individual flags. +# The -E"gcc -E" argument must be separate to avoid problems with shell +# quoting. +# Strip -O2, it may cause cproto to write stderr to the file "2". +CPROTO = cproto $(PROTO_FLAGS) -DPROTO \ + `echo '$(LINT_CFLAGS)' | sed -e 's/ -[a-z-]\+//g' -e 's/ -O[^ ]\+//g'` + +### Would be nice if this would work for "normal" make. +### Currently it only works for (Free)BSD make. +#$(PRO_AUTO): $$(*F).c +# $(CPROTO) -DFEAT_GUI $(*F).c > $@ + +# Always define FEAT_GUI. This may generate a few warnings if it's also +# defined in auto/config.h, you can ignore that. +.c.pro: + $(CPROTO) -DFEAT_GUI $< > proto/$@ + echo "/* vim: set ft=c : */" >> proto/$@ + +os_amiga.pro: os_amiga.c + $(CPROTO) -DAMIGA -UHAVE_CONFIG_H -DBPTR=char* $< > proto/$@ + echo "/* vim: set ft=c : */" >> proto/$@ + +os_win32.pro: os_win32.c + $(CPROTO) -DWIN32 -UHAVE_CONFIG_H $< > proto/$@ + echo "/* vim: set ft=c : */" >> proto/$@ + +os_mswin.pro: os_mswin.c + $(CPROTO) -DWIN32 -UHAVE_CONFIG_H $< > proto/$@ + echo "/* vim: set ft=c : */" >> proto/$@ + +winclip.pro: winclip.c + $(CPROTO) -DWIN32 -UHAVE_CONFIG_H $< > proto/$@ + echo "/* vim: set ft=c : */" >> proto/$@ + +os_beos.pro: os_beos.c + $(CPROTO) -D__BEOS__ -UHAVE_CONFIG_H $< > proto/$@ + echo "/* vim: set ft=c : */" >> proto/$@ + +os_vms.pro: os_vms.c +# must use os_vms_conf.h for auto/config.h + mv auto/config.h auto/config.h.save + cp os_vms_conf.h auto/config.h + $(CPROTO) -DVMS -UFEAT_GUI_ATHENA -UFEAT_GUI_NEXTAW -UFEAT_GUI_MOTIF -UFEAT_GUI_GTK $< > proto/$@ + echo "/* vim: set ft=c : */" >> proto/$@ + rm auto/config.h + mv auto/config.h.save auto/config.h + +# if_perl.pro is special: Use the generated if_perl.c for input and remove +# prototypes for local functions. +if_perl.pro: auto/if_perl.c + $(CPROTO) -DFEAT_GUI auto/if_perl.c | sed "/_VI/d" > proto/$@ + +gui_gtk_gresources.pro: auto/gui_gtk_gresources.c + $(CPROTO) -DFEAT_GUI $< > proto/$@ + echo "/* vim: set ft=c : */" >> proto/$@ + +notags: + -rm -f tags + +# Note: tags is made for the currently configured version, can't include both +# Motif and Athena GUI +# You can ignore error messages for missing files. +tags TAGS: notags + $(TAGPRG) $(TAGS_FILES) + +# Make a highlight file for types. Requires Exuberant ctags and awk +types: types.vim +types.vim: $(TAGS_FILES) + ctags --c-kinds=gstu -o- $(TAGS_FILES) |\ + awk 'BEGIN{printf("syntax keyword Type\t")}\ + {printf("%s ", $$1)}END{print ""}' > $@ + echo "syn keyword Constant OK FAIL TRUE FALSE MAYBE" >> $@ + +# TESTING +# +# Execute the test scripts and the unittests. +test check: scripttests unittests test_libvterm + +# Execute the test scripts. Run these after compiling Vim, before installing. +# This doesn't depend on $(VIMTARGET), because that won't work when configure +# wasn't run yet. Restart make to build it instead. +# +# This will produce a lot of garbage on your screen, including a few error +# messages. Don't worry about that. +# If there is a real error, there will be a difference between "testXX.out" and +# a "testXX.ok" file. +# If everything is alright, the final message will be "ALL DONE". If not you +# get "TEST FAILURE". +# +scripttests: + $(MAKE) -f Makefile $(VIMTARGET) + if test -n "$(MAKEMO)" -a -f $(PODIR)/Makefile; then \ + cd $(PODIR); $(MAKE) -f Makefile check VIM=../$(VIMTARGET); \ + fi + -if test $(VIMTARGET) != vim -a ! -r vim; then \ + ln -s $(VIMTARGET) vim; \ + fi + cd testdir; $(MAKE) -f Makefile $(GUI_TESTTARGET) VIMPROG=../$(VIMTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) + +# Run the tests with the GUI. Assumes vim/gvim was already built +testgui: + cd testdir; $(MAKE) -f Makefile $(GUI_TESTTARGET) VIMPROG=../$(VIMTARGET) GUI_FLAG=-g $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) + +benchmark: + cd testdir; $(MAKE) -f Makefile benchmark VIMPROG=../$(VIMTARGET) SCRIPTSOURCE=../$(SCRIPTSOURCE) + +unittesttargets: + $(MAKE) -f Makefile $(UNITTEST_TARGETS) + +# Swap these lines to run individual tests with gvim instead of vim. +VIMTESTTARGET = $(VIMTARGET) +# VIMTESTTARGET = $(GVIMTARGET) + +# Execute the unittests one by one. +unittest unittests: $(RUN_UNITTESTS) + +run_json_test: $(JSON_TEST_TARGET) + $(VALGRIND) ./$(JSON_TEST_TARGET) || exit 1; echo $* passed; + +run_kword_test: $(KWORD_TEST_TARGET) + $(VALGRIND) ./$(KWORD_TEST_TARGET) || exit 1; echo $* passed; + +run_memfile_test: $(MEMFILE_TEST_TARGET) + $(VALGRIND) ./$(MEMFILE_TEST_TARGET) || exit 1; echo $* passed; + +run_message_test: $(MESSAGE_TEST_TARGET) + $(VALGRIND) ./$(MESSAGE_TEST_TARGET) || exit 1; echo $* passed; + +# Run the libvterm tests. +# This currently doesn't work on Mac, only run on Linux for now. +test_libvterm: + @if test `uname` = "Linux"; then \ + cd libvterm; $(MAKE) -f Makefile test \ + CC="$(CC)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"; \ + fi + +# Run individual OLD style test. +# These do not depend on the executable, compile it when needed. +test1 \ + test_eval \ + test3 test11 test14 test17 \ + test29 test30 test37 test39 \ + test42 test44 test48 test49 \ + test52 test59 \ + test64 test69 \ + test70 test72 \ + test86 test87 test88 \ + test94 test95 test99 test108: + cd testdir; rm -f $@.out; $(MAKE) -f Makefile $@.out VIMPROG=../$(VIMTESTTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) + +# Run individual NEW style test. +# These do not depend on the executable, compile it when needed. +$(NEW_TESTS): + cd testdir; $(MAKE) $@ VIMPROG=../$(VIMTESTTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) + +newtests: + cd testdir; rm -f $@.res test.log messages; $(MAKE) -f Makefile newtestssilent VIMPROG=../$(VIMTESTTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) + @if test -f testdir/test.log; then \ + cat testdir/test.log; \ + fi + cat testdir/messages + +testclean: + cd testdir; $(MAKE) -f Makefile clean + if test -d $(PODIR); then \ + cd $(PODIR); $(MAKE) checkclean; \ + fi + +# Unittests +# It's build just like Vim to satisfy all dependencies. +$(JSON_TEST_TARGET): auto/config.mk objects $(JSON_TEST_OBJ) + $(CCC) version.c -o objects/version.o + @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ + -o $(JSON_TEST_TARGET) $(JSON_TEST_OBJ) $(ALL_LIBS)" \ + MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + sh $(srcdir)/link.sh + +$(KWORD_TEST_TARGET): auto/config.mk objects $(KWORD_TEST_OBJ) + $(CCC) version.c -o objects/version.o + @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ + -o $(KWORD_TEST_TARGET) $(KWORD_TEST_OBJ) $(ALL_LIBS)" \ + MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + sh $(srcdir)/link.sh + +$(MEMFILE_TEST_TARGET): auto/config.mk objects $(MEMFILE_TEST_OBJ) + $(CCC) version.c -o objects/version.o + @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ + -o $(MEMFILE_TEST_TARGET) $(MEMFILE_TEST_OBJ) $(ALL_LIBS)" \ + MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + sh $(srcdir)/link.sh + +$(MESSAGE_TEST_TARGET): auto/config.mk objects $(MESSAGE_TEST_OBJ) + $(CCC) version.c -o objects/version.o + @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ + -o $(MESSAGE_TEST_TARGET) $(MESSAGE_TEST_OBJ) $(ALL_LIBS)" \ + MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + sh $(srcdir)/link.sh + +# install targets + +install: $(GUI_INSTALL) + +install_normal: installvim installtools $(INSTALL_LANGS) install-icons + +install_gui_extra: installgtutorbin + +installvim: installvimbin installtutorbin \ + installruntime installlinks installmanlinks + +# +# Avoid overwriting an existing executable, somebody might be running it and +# overwriting it could cause it to crash. Deleting it is OK, it won't be +# really deleted until all running processes for it have exited. It is +# renamed first, in case the deleting doesn't work. +# +# If you want to keep an older version, rename it before running "make +# install". +# +installvimbin: $(VIMTARGET) $(DESTDIR)$(exec_prefix) $(DEST_BIN) + -if test -f $(DEST_BIN)/$(VIMTARGET); then \ + mv -f $(DEST_BIN)/$(VIMTARGET) $(DEST_BIN)/$(VIMNAME).rm; \ + rm -f $(DEST_BIN)/$(VIMNAME).rm; \ + fi + $(INSTALL_PROG) $(VIMTARGET) $(DEST_BIN) + $(STRIP) $(DEST_BIN)/$(VIMTARGET) + chmod $(BINMOD) $(DEST_BIN)/$(VIMTARGET) +# may create a link to the new executable from /usr/bin/vi + -$(LINKIT) + +# Long list of arguments for the shell script that installs the manual pages +# for one language. +INSTALLMANARGS = $(VIMLOC) $(SCRIPTLOC) $(VIMRCLOC) $(HELPSOURCE) $(MANMOD) \ + $(VIMNAME) $(VIMDIFFNAME) $(EVIMNAME) + +# Install most of the runtime files +installruntime: installrtbase installmacros installpack installtutor installspell + +# install the help files; first adjust the contents for the final location +installrtbase: $(HELPSOURCE)/vim.1 $(DEST_VIM) $(DEST_RT) \ + $(DEST_HELP) $(DEST_PRINT) $(DEST_COL) $(DEST_SYN) $(DEST_IND) \ + $(DEST_FTP) $(DEST_AUTO) $(DEST_AUTO)/dist $(DEST_AUTO)/xml \ + $(DEST_PLUG) $(DEST_TUTOR) $(DEST_SPELL) $(DEST_COMP) + -$(SHELL) ./installman.sh install $(DEST_MAN) "" $(INSTALLMANARGS) +# Generate the help tags with ":helptags" to handle all languages. +# Move the distributed tags file aside and restore it, to avoid it being +# different from the repository. + cd $(HELPSOURCE); if test -z "$(CROSS_COMPILING)" -a -f tags; then \ + mv -f tags tags.dist; fi + @echo generating help tags + -@cd $(HELPSOURCE); if test -z "$(CROSS_COMPILING)"; then \ + $(MAKE) VIMEXE=$(DEST_BIN)/$(VIMTARGET) vimtags; fi + cd $(HELPSOURCE); \ + files=`ls *.txt tags`; \ + files="$$files `ls *.??x tags-?? 2>/dev/null || true`"; \ + $(INSTALL_DATA) $$files $(DEST_HELP); \ + cd $(DEST_HELP); \ + chmod $(HELPMOD) $$files + $(INSTALL_DATA) $(HELPSOURCE)/*.pl $(DEST_HELP) + chmod $(SCRIPTMOD) $(DEST_HELP)/*.pl + cd $(HELPSOURCE); if test -f tags.dist; then mv -f tags.dist tags; fi +# install the menu files + $(INSTALL_DATA) $(SCRIPTSOURCE)/menu.vim $(SYS_MENU_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_MENU_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/synmenu.vim $(SYS_SYNMENU_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_SYNMENU_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/delmenu.vim $(SYS_DELMENU_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_DELMENU_FILE) +# install the defaults/evim/mswin file + $(INSTALL_DATA) $(SCRIPTSOURCE)/defaults.vim $(VIM_DEFAULTS_FILE) + chmod $(VIMSCRIPTMOD) $(VIM_DEFAULTS_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/evim.vim $(EVIM_FILE) + chmod $(VIMSCRIPTMOD) $(EVIM_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/mswin.vim $(MSWIN_FILE) + chmod $(VIMSCRIPTMOD) $(MSWIN_FILE) +# install the rgb.txt file + $(INSTALL_DATA) $(SCRIPTSOURCE)/rgb.txt $(SYS_RGB_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_RGB_FILE) +# install the bugreport file + $(INSTALL_DATA) $(SCRIPTSOURCE)/bugreport.vim $(SYS_BUGR_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_BUGR_FILE) +# install the example vimrc files + $(INSTALL_DATA) $(SCRIPTSOURCE)/vimrc_example.vim $(DEST_SCRIPT) + chmod $(VIMSCRIPTMOD) $(DEST_SCRIPT)/vimrc_example.vim + $(INSTALL_DATA) $(SCRIPTSOURCE)/gvimrc_example.vim $(DEST_SCRIPT) + chmod $(VIMSCRIPTMOD) $(DEST_SCRIPT)/gvimrc_example.vim +# install the file type detection files + $(INSTALL_DATA) $(SCRIPTSOURCE)/filetype.vim $(SYS_FILETYPE_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_FILETYPE_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/ftoff.vim $(SYS_FTOFF_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_FTOFF_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/scripts.vim $(SYS_SCRIPTS_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_SCRIPTS_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/ftplugin.vim $(SYS_FTPLUGIN_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_FTPLUGIN_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/ftplugof.vim $(SYS_FTPLUGOF_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_FTPLUGOF_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/indent.vim $(SYS_INDENT_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_INDENT_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/indoff.vim $(SYS_INDOFF_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_INDOFF_FILE) + $(INSTALL_DATA) $(SCRIPTSOURCE)/optwin.vim $(SYS_OPTWIN_FILE) + chmod $(VIMSCRIPTMOD) $(SYS_OPTWIN_FILE) +# install the print resource files + cd $(PRINTSOURCE); $(INSTALL_DATA) *.ps $(DEST_PRINT) + cd $(DEST_PRINT); chmod $(FILEMOD) *.ps +# install the colorscheme files + cd $(COLSOURCE); $(INSTALL_DATA_R) *.vim tools README.txt $(DEST_COL) + cd $(DEST_COL); chmod $(DIRMOD) tools + cd $(DEST_COL); chmod $(HELPMOD) *.vim README.txt tools/*.vim +# install the syntax files + cd $(SYNSOURCE); $(INSTALL_DATA) *.vim README.txt $(DEST_SYN) + cd $(DEST_SYN); chmod $(HELPMOD) *.vim README.txt +# install the indent files + cd $(INDSOURCE); $(INSTALL_DATA) *.vim README.txt $(DEST_IND) + cd $(DEST_IND); chmod $(HELPMOD) *.vim README.txt +# install the standard autoload files + cd $(AUTOSOURCE); $(INSTALL_DATA) *.vim README.txt $(DEST_AUTO) + cd $(DEST_AUTO); chmod $(HELPMOD) *.vim README.txt + cd $(AUTOSOURCE)/dist; $(INSTALL_DATA) *.vim $(DEST_AUTO)/dist + cd $(DEST_AUTO)/dist; chmod $(HELPMOD) *.vim + cd $(AUTOSOURCE)/xml; $(INSTALL_DATA) *.vim $(DEST_AUTO)/xml + cd $(DEST_AUTO)/xml; chmod $(HELPMOD) *.vim +# install the standard plugin files + cd $(PLUGSOURCE); $(INSTALL_DATA) *.vim README.txt $(DEST_PLUG) + cd $(DEST_PLUG); chmod $(HELPMOD) *.vim README.txt +# install the ftplugin files + cd $(FTPLUGSOURCE); $(INSTALL_DATA) *.vim README.txt logtalk.dict $(DEST_FTP) + cd $(DEST_FTP); chmod $(HELPMOD) *.vim README.txt +# install the compiler files + cd $(COMPSOURCE); $(INSTALL_DATA) *.vim README.txt $(DEST_COMP) + cd $(DEST_COMP); chmod $(HELPMOD) *.vim README.txt + +installmacros: $(DEST_VIM) $(DEST_RT) $(DEST_MACRO) + $(INSTALL_DATA_R) $(MACROSOURCE)/* $(DEST_MACRO) + chmod $(DIRMOD) `find $(DEST_MACRO) -type d -print` + chmod $(FILEMOD) `find $(DEST_MACRO) -type f -print` + chmod $(SCRIPTMOD) $(DEST_MACRO)/less.sh +# When using CVS some CVS directories might have been copied. +# Also delete AAPDIR and *.info files. + cvs=`find $(DEST_MACRO) \( -name CVS -o -name AAPDIR -o -name "*.info" \) -print`; \ + if test -n "$$cvs"; then \ + rm -rf $$cvs; \ + fi + +installpack: $(DEST_VIM) $(DEST_RT) $(DEST_PACK) + $(INSTALL_DATA_R) $(PACKSOURCE)/* $(DEST_PACK) + chmod $(DIRMOD) `find $(DEST_PACK) -type d -print` + chmod $(FILEMOD) `find $(DEST_PACK) -type f -print` + +# install the tutor files +installtutorbin: $(DEST_VIM) + $(INSTALL_DATA) vimtutor $(DEST_BIN)/$(VIMNAME)tutor + chmod $(SCRIPTMOD) $(DEST_BIN)/$(VIMNAME)tutor + +installgtutorbin: $(DEST_VIM) + $(INSTALL_DATA) gvimtutor $(DEST_BIN)/$(GVIMNAME)tutor + chmod $(SCRIPTMOD) $(DEST_BIN)/$(GVIMNAME)tutor + +installtutor: $(DEST_RT) $(DEST_TUTOR) + -$(INSTALL_DATA) $(TUTORSOURCE)/README* $(TUTORSOURCE)/tutor* $(DEST_TUTOR) + -rm -f $(DEST_TUTOR)/*.info + chmod $(HELPMOD) $(DEST_TUTOR)/* + +# Install the spell files, if they exist. This assumes at least the English +# spell file is there. +installspell: $(DEST_VIM) $(DEST_RT) $(DEST_SPELL) + if test -f $(SPELLSOURCE)/en.latin1.spl; then \ + $(INSTALL_DATA) $(SPELLSOURCE)/*.spl $(SPELLSOURCE)/*.sug $(SPELLSOURCE)/*.vim $(DEST_SPELL); \ + chmod $(HELPMOD) $(DEST_SPELL)/*.spl $(DEST_SPELL)/*.sug $(DEST_SPELL)/*.vim; \ + fi + +# install helper program xxd +installtools: $(TOOLS) $(DESTDIR)$(exec_prefix) $(DEST_BIN) \ + $(TOOLSSOURCE) $(DEST_VIM) $(DEST_RT) $(DEST_TOOLS) \ + $(INSTALL_TOOL_LANGS) + if test -f $(DEST_BIN)/xxd$(EXEEXT); then \ + mv -f $(DEST_BIN)/xxd$(EXEEXT) $(DEST_BIN)/xxd.rm; \ + rm -f $(DEST_BIN)/xxd.rm; \ + fi + $(INSTALL_PROG) xxd/xxd$(EXEEXT) $(DEST_BIN) + $(STRIP) $(DEST_BIN)/xxd$(EXEEXT) + chmod $(BINMOD) $(DEST_BIN)/xxd$(EXEEXT) + -$(SHELL) ./installman.sh xxd $(DEST_MAN) "" $(INSTALLMANARGS) + +# install the runtime tools + $(INSTALL_DATA_R) $(TOOLSSOURCE)/* $(DEST_TOOLS) +# When using CVS some CVS directories might have been copied. + cvs=`find $(DEST_TOOLS) \( -name CVS -o -name AAPDIR \) -print`; \ + if test -n "$$cvs"; then \ + rm -rf $$cvs; \ + fi + -chmod $(FILEMOD) $(DEST_TOOLS)/* +# replace the path in some tools + perlpath=`./which.sh perl` && sed -e "s+/usr/bin/perl+$$perlpath+" $(TOOLSSOURCE)/efm_perl.pl >$(DEST_TOOLS)/efm_perl.pl + awkpath=`./which.sh nawk` && sed -e "s+/usr/bin/nawk+$$awkpath+" $(TOOLSSOURCE)/mve.awk >$(DEST_TOOLS)/mve.awk; if test -z "$$awkpath"; then \ + awkpath=`./which.sh gawk` && sed -e "s+/usr/bin/nawk+$$awkpath+" $(TOOLSSOURCE)/mve.awk >$(DEST_TOOLS)/mve.awk; if test -z "$$awkpath"; then \ + awkpath=`./which.sh awk` && sed -e "s+/usr/bin/nawk+$$awkpath+" $(TOOLSSOURCE)/mve.awk >$(DEST_TOOLS)/mve.awk; fi; fi + -chmod $(SCRIPTMOD) `grep -l "^#!" $(DEST_TOOLS)/*` + +# install the language specific files for tools, if they were unpacked +install-tool-languages: + -$(SHELL) ./installman.sh xxd $(DEST_MAN_DA) "-da" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_DA_I) "-da" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_DA_U) "-da.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_DE) "-de" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_DE_I) "-de" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_DE_U) "-de.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_FR) "-fr" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_FR_I) "-fr" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_FR_U) "-fr.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_IT) "-it" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_IT_I) "-it" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_IT_U) "-it.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_JA_U) "-ja.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_PL) "-pl" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_PL_I) "-pl" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_PL_U) "-pl.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_RU) "-ru" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh xxd $(DEST_MAN_RU_U) "-ru.UTF-8" $(INSTALLMANARGS) + +# install the language specific files, if they were unpacked +install-languages: languages $(DEST_LANG) $(DEST_KMAP) + -$(SHELL) ./installman.sh install $(DEST_MAN_DA) "-da" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_DA_I) "-da" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_DA_U) "-da.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_DE) "-de" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_DE_I) "-de" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_DE_U) "-de.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_FR) "-fr" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_FR_I) "-fr" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_FR_U) "-fr.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_IT) "-it" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_IT_I) "-it" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_IT_U) "-it.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_JA_U) "-ja.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_PL) "-pl" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_PL_I) "-pl" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_PL_U) "-pl.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_RU) "-ru" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh install $(DEST_MAN_RU_U) "-ru.UTF-8" $(INSTALLMANARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DA) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DA_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DA_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DE) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DE_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DE_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_FR) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_FR_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_FR_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_IT) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_IT_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_IT_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_JA_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_PL) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_PL_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_PL_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_RU) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_RU_U) $(INSTALLMLARGS) + if test -n "$(MAKEMO)" -a -f $(PODIR)/Makefile; then \ + cd $(PODIR); $(MAKE) prefix=$(DESTDIR)$(prefix) LOCALEDIR=$(DEST_LANG) \ + INSTALL_DATA=$(INSTALL_DATA) FILEMOD=$(FILEMOD) install; \ + fi + if test -d $(LANGSOURCE); then \ + $(INSTALL_DATA) $(LANGSOURCE)/README.txt $(LANGSOURCE)/*.vim $(DEST_LANG); \ + chmod $(FILEMOD) $(DEST_LANG)/README.txt $(DEST_LANG)/*.vim; \ + fi + if test -d $(KMAPSOURCE); then \ + $(INSTALL_DATA) $(KMAPSOURCE)/README.txt $(KMAPSOURCE)/*.vim $(DEST_KMAP); \ + chmod $(FILEMOD) $(DEST_KMAP)/README.txt $(DEST_KMAP)/*.vim; \ + fi + +# Install the icons for KDE, if the directory exists and the icon doesn't. +# Always when $(DESTDIR) is not empty. +ICON48PATH = $(DESTDIR)$(DATADIR)/icons/hicolor/48x48/apps +ICON32PATH = $(DESTDIR)$(DATADIR)/icons/locolor/32x32/apps +ICON16PATH = $(DESTDIR)$(DATADIR)/icons/locolor/16x16/apps +ICONTHEMEPATH = $(DATADIR)/icons/hicolor +DESKTOPPATH = $(DESTDIR)$(DATADIR)/applications +KDEPATH = $(HOME)/.kde/share/icons +install-icons: + if test -n "$(DESTDIR)"; then \ + $(MKDIR_P) $(ICON48PATH) $(ICON32PATH) \ + $(ICON16PATH) $(DESKTOPPATH); \ + fi + + if test -d $(ICON48PATH) -a -w $(ICON48PATH) \ + -a ! -f $(ICON48PATH)/gvim.png; then \ + $(INSTALL_DATA) $(SCRIPTSOURCE)/vim48x48.png $(ICON48PATH)/gvim.png; \ + if test -z "$(DESTDIR)" -a -x "$(GTK_UPDATE_ICON_CACHE)" \ + -a -w $(ICONTHEMEPATH) \ + -a -f $(ICONTHEMEPATH)/index.theme; then \ + $(GTK_UPDATE_ICON_CACHE) -q $(ICONTHEMEPATH); \ + fi \ + fi + if test -d $(ICON32PATH) -a -w $(ICON32PATH) \ + -a ! -f $(ICON32PATH)/gvim.png; then \ + $(INSTALL_DATA) $(SCRIPTSOURCE)/vim32x32.png $(ICON32PATH)/gvim.png; \ + fi + if test -d $(ICON16PATH) -a -w $(ICON16PATH) \ + -a ! -f $(ICON16PATH)/gvim.png; then \ + $(INSTALL_DATA) $(SCRIPTSOURCE)/vim16x16.png $(ICON16PATH)/gvim.png; \ + fi + if test -d $(DESKTOPPATH) -a -w $(DESKTOPPATH); then \ + $(INSTALL_DATA) $(SCRIPTSOURCE)/vim.desktop \ + $(SCRIPTSOURCE)/gvim.desktop \ + $(DESKTOPPATH); \ + if test -z "$(DESTDIR)" -a -x "$(UPDATE_DESKTOP_DATABASE)"; then \ + $(UPDATE_DESKTOP_DATABASE) -q $(DESKTOPPATH); \ + fi \ + fi + +$(HELPSOURCE)/vim.1 $(MACROSOURCE) $(TOOLSSOURCE): + @echo Runtime files not found. + @echo You need to unpack the runtime archive before running "make install". + test -f error + +$(DESTDIR)$(exec_prefix) $(DEST_BIN) \ + $(DEST_VIM) $(DEST_RT) $(DEST_HELP) \ + $(DEST_PRINT) $(DEST_COL) $(DEST_SYN) $(DEST_IND) $(DEST_FTP) \ + $(DEST_LANG) $(DEST_KMAP) $(DEST_COMP) $(DEST_MACRO) \ + $(DEST_PACK) $(DEST_TOOLS) $(DEST_TUTOR) $(DEST_SPELL) \ + $(DEST_AUTO) $(DEST_AUTO)/dist $(DEST_AUTO)/xml $(DEST_PLUG): + $(MKDIR_P) $@ + -chmod $(DIRMOD) $@ + +# create links from various names to vim. This is only done when the links +# (or executables with the same name) don't exist yet. +installlinks: $(GUI_TARGETS) \ + $(DEST_BIN)/$(EXTARGET) \ + $(DEST_BIN)/$(VIEWTARGET) \ + $(DEST_BIN)/$(RVIMTARGET) \ + $(DEST_BIN)/$(RVIEWTARGET) \ + $(INSTALLVIMDIFF) + +installglinks: $(DEST_BIN)/$(GVIMTARGET) \ + $(DEST_BIN)/$(GVIEWTARGET) \ + $(DEST_BIN)/$(RGVIMTARGET) \ + $(DEST_BIN)/$(RGVIEWTARGET) \ + $(DEST_BIN)/$(EVIMTARGET) \ + $(DEST_BIN)/$(EVIEWTARGET) \ + $(INSTALLGVIMDIFF) + +installvimdiff: $(DEST_BIN)/$(VIMDIFFTARGET) +installgvimdiff: $(DEST_BIN)/$(GVIMDIFFTARGET) + +$(DEST_BIN)/$(EXTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(EXTARGET) + +$(DEST_BIN)/$(VIEWTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(VIEWTARGET) + +$(DEST_BIN)/$(GVIMTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(GVIMTARGET) + +$(DEST_BIN)/$(GVIEWTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(GVIEWTARGET) + +$(DEST_BIN)/$(RVIMTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(RVIMTARGET) + +$(DEST_BIN)/$(RVIEWTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(RVIEWTARGET) + +$(DEST_BIN)/$(RGVIMTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(RGVIMTARGET) + +$(DEST_BIN)/$(RGVIEWTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(RGVIEWTARGET) + +$(DEST_BIN)/$(VIMDIFFTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(VIMDIFFTARGET) + +$(DEST_BIN)/$(GVIMDIFFTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(GVIMDIFFTARGET) + +$(DEST_BIN)/$(EVIMTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(EVIMTARGET) + +$(DEST_BIN)/$(EVIEWTARGET): + cd $(DEST_BIN); ln -s $(VIMTARGET) $(EVIEWTARGET) + +# Create links for the manual pages with various names to vim. This is only +# done when the links (or manpages with the same name) don't exist yet. + +INSTALLMLARGS = $(VIMNAME) $(VIMDIFFNAME) $(EVIMNAME) \ + $(EXNAME) $(VIEWNAME) $(RVIMNAME) $(RVIEWNAME) \ + $(GVIMNAME) $(GVIEWNAME) $(RGVIMNAME) $(RGVIEWNAME) \ + $(GVIMDIFFNAME) $(EVIEWNAME) + +installmanlinks: + -$(SHELL) ./installml.sh install "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN) $(INSTALLMLARGS) + +uninstall: uninstall_runtime + -rm -f $(DEST_BIN)/$(VIMTARGET) + -rm -f $(DEST_BIN)/vimtutor + -rm -f $(DEST_BIN)/gvimtutor + -rm -f $(DEST_BIN)/$(EXTARGET) $(DEST_BIN)/$(VIEWTARGET) + -rm -f $(DEST_BIN)/$(GVIMTARGET) $(DEST_BIN)/$(GVIEWTARGET) + -rm -f $(DEST_BIN)/$(RVIMTARGET) $(DEST_BIN)/$(RVIEWTARGET) + -rm -f $(DEST_BIN)/$(RGVIMTARGET) $(DEST_BIN)/$(RGVIEWTARGET) + -rm -f $(DEST_BIN)/$(VIMDIFFTARGET) $(DEST_BIN)/$(GVIMDIFFTARGET) + -rm -f $(DEST_BIN)/$(EVIMTARGET) $(DEST_BIN)/$(EVIEWTARGET) + -rm -f $(DEST_BIN)/xxd$(EXEEXT) + +# Note: the "rmdir" will fail if any files were added after "make install" +uninstall_runtime: + -$(SHELL) ./installman.sh uninstall $(DEST_MAN) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_DA) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_DA_I) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_DA_U) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_DE) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_DE_I) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_DE_U) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_FR) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_FR_I) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_FR_U) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_IT) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_IT_I) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_IT_U) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_JA_U) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_PL) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_PL_I) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_PL_U) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_RU) "" $(INSTALLMANARGS) + -$(SHELL) ./installman.sh uninstall $(DEST_MAN_RU_U) "" $(INSTALLMANARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DA) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DA_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DA_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DE) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DE_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_DE_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_FR) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_FR_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_FR_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_IT) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_IT_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_IT_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_JA_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_PL) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_PL_I) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_PL_U) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_RU) $(INSTALLMLARGS) + -$(SHELL) ./installml.sh uninstall "$(GUI_MAN_TARGETS)" \ + $(DEST_MAN_RU_U) $(INSTALLMLARGS) + -rm -f $(DEST_MAN)/xxd.1 + -rm -f $(DEST_MAN_DA)/xxd.1 $(DEST_MAN_DA_I)/xxd.1 $(DEST_MAN_DA_U)/xxd.1 + -rm -f $(DEST_MAN_DE)/xxd.1 $(DEST_MAN_DE_I)/xxd.1 $(DEST_MAN_DE_U)/xxd.1 + -rm -f $(DEST_MAN_FR)/xxd.1 $(DEST_MAN_FR_I)/xxd.1 $(DEST_MAN_FR_U)/xxd.1 + -rm -f $(DEST_MAN_IT)/xxd.1 $(DEST_MAN_IT_I)/xxd.1 $(DEST_MAN_IT_U)/xxd.1 + -rm -f $(DEST_MAN_JA_U)/xxd.1 + -rm -f $(DEST_MAN_PL)/xxd.1 $(DEST_MAN_PL_I)/xxd.1 $(DEST_MAN_PL_U)/xxd.1 + -rm -f $(DEST_MAN_RU)/xxd.1 $(DEST_MAN_RU_U)/xxd.1 + -rm -f $(DEST_HELP)/*.txt $(DEST_HELP)/tags $(DEST_HELP)/*.pl + -rm -f $(DEST_HELP)/*.??x $(DEST_HELP)/tags-?? + -rm -f $(SYS_RGB_FILE) + -rm -f $(SYS_MENU_FILE) $(SYS_SYNMENU_FILE) $(SYS_DELMENU_FILE) + -rm -f $(SYS_BUGR_FILE) $(VIM_DEFAULTS_FILE) $(EVIM_FILE) $(MSWIN_FILE) + -rm -f $(DEST_SCRIPT)/gvimrc_example.vim $(DEST_SCRIPT)/vimrc_example.vim + -rm -f $(SYS_FILETYPE_FILE) $(SYS_FTOFF_FILE) $(SYS_SCRIPTS_FILE) + -rm -f $(SYS_INDOFF_FILE) $(SYS_INDENT_FILE) + -rm -f $(SYS_FTPLUGOF_FILE) $(SYS_FTPLUGIN_FILE) + -rm -f $(SYS_OPTWIN_FILE) + -rm -f $(DEST_COL)/*.vim $(DEST_COL)/README.txt + -rm -rf $(DEST_COL)/tools + -rm -f $(DEST_SYN)/*.vim $(DEST_SYN)/README.txt + -rm -f $(DEST_IND)/*.vim $(DEST_IND)/README.txt + -rm -rf $(DEST_MACRO) + -rm -rf $(DEST_PACK) + -rm -rf $(DEST_TUTOR) + -rm -rf $(DEST_SPELL) + -rm -rf $(DEST_TOOLS) + -rm -rf $(DEST_LANG) + -rm -rf $(DEST_KMAP) + -rm -rf $(DEST_COMP) + -rm -f $(DEST_PRINT)/*.ps + -rmdir $(DEST_HELP) $(DEST_PRINT) $(DEST_COL) $(DEST_SYN) $(DEST_IND) + -rm -rf $(DEST_FTP)/*.vim $(DEST_FTP)/README.txt $(DEST_FTP)/logtalk.dict + -rm -f $(DEST_AUTO)/*.vim $(DEST_AUTO)/README.txt + -rm -f $(DEST_AUTO)/dist/*.vim $(DEST_AUTO)/xml/*.vim + -rm -f $(DEST_PLUG)/*.vim $(DEST_PLUG)/README.txt + -rmdir $(DEST_FTP) $(DEST_AUTO)/dist $(DEST_AUTO)/xml $(DEST_AUTO) + -rmdir $(DEST_PLUG) $(DEST_RT) +# This will fail when other Vim versions are installed, no worries. + -rmdir $(DEST_VIM) + +# Clean up all the files that have been produced, except configure's. +# We support common typing mistakes for Juergen! :-) +clean celan: testclean + -rm -f *.o core $(VIMTARGET).core $(VIMTARGET) vim xxd/*.o + -rm -rf objects + -rm -f $(TOOLS) auto/osdef.h auto/pathdef.c auto/if_perl.c auto/gui_gtk_gresources.c auto/gui_gtk_gresources.h + -rm -f conftest* *~ auto/link.sed + -rm -f testdir/opt_test.vim + -rm -f $(UNITTEST_TARGETS) + -rm -f runtime pixmaps + -rm -rf $(APPDIR) + -rm -rf mzscheme_base.c + if test -d $(PODIR); then \ + cd $(PODIR); $(MAKE) prefix=$(DESTDIR)$(prefix) clean; \ + fi + +# Make a shadow directory for compilation on another system or with different +# features. +SHADOWDIR = shadow + +shadow: runtime pixmaps + $(MKDIR_P) $(SHADOWDIR) + cd $(SHADOWDIR); ln -s ../*.[chm] ../*.in ../*.sh ../*.xs ../*.xbm ../gui_gtk_res.xml ../toolcheck ../proto ../libvterm ../vimtutor ../gvimtutor ../install-sh ../Make_all.mak . + mkdir $(SHADOWDIR)/auto + cd $(SHADOWDIR)/auto; ln -s ../../auto/configure . + $(MKDIR_P) $(SHADOWDIR)/po + cd $(SHADOWDIR)/po; ln -s ../../po/*.po ../../po/*.mak ../../po/*.vim ../../po/Makefile . + cd $(SHADOWDIR); rm -f auto/link.sed + cp Makefile configure $(SHADOWDIR) + rm -f $(SHADOWDIR)/auto/config.mk $(SHADOWDIR)/config.mk.dist + cp config.mk.dist $(SHADOWDIR)/auto/config.mk + cp config.mk.dist $(SHADOWDIR) + $(MKDIR_P) $(SHADOWDIR)/xxd + cd $(SHADOWDIR)/xxd; ln -s ../../xxd/*.[ch] ../../xxd/Make* . + $(MKDIR_P) $(SHADOWDIR)/xdiff + cd $(SHADOWDIR)/xdiff; ln -s ../../xdiff/*.[ch] . + if test -d $(RSRC_DIR); then \ + cd $(SHADOWDIR); \ + ln -s ../infplist.xml .; \ + ln -s ../$(RSRC_DIR) ../os_mac.rsr.hqx ../dehqx.py .; \ + fi + $(MKDIR_P) $(SHADOWDIR)/testdir + cd $(SHADOWDIR)/testdir; ln -s ../../testdir/Makefile \ + ../../testdir/Make_all.mak \ + ../../testdir/README.txt \ + ../../testdir/*.in \ + ../../testdir/*.vim \ + ../../testdir/*.py \ + ../../testdir/python* \ + ../../testdir/pyxfile \ + ../../testdir/sautest \ + ../../testdir/samples \ + ../../testdir/dumps \ + ../../testdir/test83-tags? \ + ../../testdir/*.ok . + +# Link needed for doing "make install" in a shadow directory. +runtime: + -ln -s ../runtime . + +# Link needed for doing "make" using GTK in a shadow directory. +pixmaps: + -ln -s ../pixmaps . + +# Update the synmenu.vim file with the latest Syntax menu. +# This is only needed when runtime/makemenu.vim was changed. +menu: ./vim ../runtime/makemenu.vim + ./vim -u ../runtime/makemenu.vim + +# Start configure from scratch +scrub scratch: + -rm -f auto/config.status auto/config.cache config.log auto/config.log + -rm -f auto/config.h auto/link.log auto/link.sed auto/config.mk + touch auto/config.h + cp config.mk.dist auto/config.mk + +distclean: clean scratch + -rm -f tags + +dist: distclean + @echo + @echo Making the distribution has to be done in the top directory + +mdepend: + -@rm -f Makefile~ + cp Makefile Makefile~ + sed -e '/\#\#\# Dependencies/q' < Makefile > tmp_make + @for i in $(ALL_SRC) ; do \ + echo "$$i" ; \ + echo `echo "$$i" | sed -e 's/[^ ]*\.c$$/objects\/\1.o/'`": $$i" `\ + $(CPP) $$i |\ + grep '^# .*"\./.*\.h"' |\ + sort -t'"' -u +1 -2 |\ + sed -e 's/.*"\.\/\(.*\)".*/\1/'\ + ` >> tmp_make ; \ + done + mv tmp_make Makefile + +depend: + -@rm -f Makefile~ + cp Makefile Makefile~ + sed -e '/\#\#\# Dependencies/q' < Makefile > tmp_make + -for i in $(ALL_SRC); do echo $$i; \ + $(CPP_DEPEND) $$i | \ + sed -e 's+^\([^ ]*\.o\)+objects/\1+' -e 's+xdiff/\.\./++g' >> tmp_make; done + mv tmp_make Makefile + +# Run lint. Clean up the *.ln files that are sometimes left behind. +lint: + $(LINT) $(LINT_OPTIONS) $(LINT_CFLAGS) $(LINT_EXTRA) $(LINT_SRC) + -rm -f *.ln + +# Check dosinst.c with lint. +lintinstall: + $(LINT) $(LINT_OPTIONS) -DWIN32 -DUNIX_LINT dosinst.c + -rm -f dosinst.ln + +########################################################################### + +.c.o: + $(CCC) $< + +auto/if_perl.c: if_perl.xs + $(PERL) -e 'unless ( $$] >= 5.005 ) { for (qw(na defgv errgv)) { print "#define PL_$$_ $$_\n" }}' > $@ + $(PERL) $(PERL_XSUBPP) -prototypes -typemap \ + $(PERLLIB)/ExtUtils/typemap if_perl.xs >> $@ + +auto/osdef.h: auto/config.h osdef.sh osdef1.h.in osdef2.h.in + CC="$(CC) $(OSDEF_CFLAGS)" srcdir=$(srcdir) sh $(srcdir)/osdef.sh + +auto/pathdef.c: Makefile auto/config.mk + -@echo creating $@ + -@echo '/* pathdef.c */' > $@ + -@echo '/* This file is automatically created by Makefile' >> $@ + -@echo ' * DO NOT EDIT! Change Makefile only. */' >> $@ + -@echo '#include "vim.h"' >> $@ + -@echo 'char_u *default_vim_dir = (char_u *)"$(VIMRCLOC)";' | $(QUOTESED) >> $@ + -@echo 'char_u *default_vimruntime_dir = (char_u *)"$(VIMRUNTIMEDIR)";' | $(QUOTESED) >> $@ + -@echo 'char_u *all_cflags = (char_u *)"$(CC) -c -I$(srcdir) $(ALL_CFLAGS)";' | $(QUOTESED) >> $@ + -@echo 'char_u *all_lflags = (char_u *)"$(CC) $(ALL_LIB_DIRS) $(LDFLAGS) -o $(VIMTARGET) $(ALL_LIBS) ";' | $(QUOTESED) >> $@ + -@echo 'char_u *compiled_user = (char_u *)"' | tr -d $(NL) >> $@ + -@if test -n "$(COMPILEDBY)"; then \ + echo "$(COMPILEDBY)" | tr -d $(NL) >> $@; \ + else ((logname) 2>/dev/null || whoami) | tr -d $(NL) >> $@; fi + -@echo '";' >> $@ + -@echo 'char_u *compiled_sys = (char_u *)"' | tr -d $(NL) >> $@ + -@if test -z "$(COMPILEDBY)"; then hostname | tr -d $(NL) >> $@; fi + -@echo '";' >> $@ + -@sh $(srcdir)/pathdef.sh + +GUI_GTK_RES_INPUTS = \ + ../pixmaps/stock_vim_build_tags.png \ + ../pixmaps/stock_vim_find_help.png \ + ../pixmaps/stock_vim_save_all.png \ + ../pixmaps/stock_vim_session_load.png \ + ../pixmaps/stock_vim_session_new.png \ + ../pixmaps/stock_vim_session_save.png \ + ../pixmaps/stock_vim_shell.png \ + ../pixmaps/stock_vim_window_maximize.png \ + ../pixmaps/stock_vim_window_maximize_width.png \ + ../pixmaps/stock_vim_window_minimize.png \ + ../pixmaps/stock_vim_window_minimize_width.png \ + ../pixmaps/stock_vim_window_split.png \ + ../pixmaps/stock_vim_window_split_vertical.png + +auto/gui_gtk_gresources.c: gui_gtk_res.xml $(GUI_GTK_RES_INPUTS) + $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=../pixmaps --generate --c-name=gui_gtk --manual-register gui_gtk_res.xml +auto/gui_gtk_gresources.h: gui_gtk_res.xml $(GUI_GTK_RES_INPUTS) + if test -z "$(GLIB_COMPILE_RESOURCES)"; then touch $@; else \ + $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=../pixmaps --generate --c-name=gui_gtk --manual-register gui_gtk_res.xml; \ + fi + +# All the object files are put in the "objects" directory. Since not all make +# commands understand putting object files in another directory, it must be +# specified for each file separately. + +objects: objects/.dirstamp + +objects/.dirstamp: + $(MKDIR_P) objects + touch objects/.dirstamp + +# All object files depend on the objects directory, so that parallel make +# works. Can't depend on the directory itself, its timestamp changes all the +# time. +$(ALL_OBJ): objects/.dirstamp + +objects/arabic.o: arabic.c + $(CCC) -o $@ arabic.c + +objects/autocmd.o: autocmd.c + $(CCC) -o $@ autocmd.c + +objects/blob.o: blob.c + $(CCC) -o $@ blob.c + +objects/blowfish.o: blowfish.c + $(CCC) -o $@ blowfish.c + +objects/buffer.o: buffer.c + $(CCC) -o $@ buffer.c + +objects/charset.o: charset.c + $(CCC) -o $@ charset.c + +objects/crypt.o: crypt.c + $(CCC) -o $@ crypt.c + +objects/crypt_zip.o: crypt_zip.c + $(CCC) -o $@ crypt_zip.c + +objects/dict.o: dict.c + $(CCC) -o $@ dict.c + +objects/diff.o: diff.c $(XDIFF_INCL) + $(CCC) -o $@ diff.c + +objects/digraph.o: digraph.c + $(CCC) -o $@ digraph.c + +objects/edit.o: edit.c + $(CCC) -o $@ edit.c + +objects/eval.o: eval.c + $(CCC) -o $@ eval.c + +objects/evalfunc.o: evalfunc.c + $(CCC) -o $@ evalfunc.c + +objects/ex_cmds.o: ex_cmds.c + $(CCC) -o $@ ex_cmds.c + +objects/ex_cmds2.o: ex_cmds2.c + $(CCC) -o $@ ex_cmds2.c + +objects/ex_docmd.o: ex_docmd.c + $(CCC) -o $@ ex_docmd.c + +objects/ex_eval.o: ex_eval.c + $(CCC) -o $@ ex_eval.c + +objects/ex_getln.o: ex_getln.c + $(CCC) -o $@ ex_getln.c + +objects/farsi.o: farsi.c + $(CCC) -o $@ farsi.c + +objects/fileio.o: fileio.c + $(CCC) -o $@ fileio.c + +objects/fold.o: fold.c + $(CCC) -o $@ fold.c + +objects/getchar.o: getchar.c + $(CCC) -o $@ getchar.c + +objects/hardcopy.o: hardcopy.c + $(CCC) -o $@ hardcopy.c + +objects/hashtab.o: hashtab.c + $(CCC) -o $@ hashtab.c + +objects/gui.o: gui.c + $(CCC) -o $@ gui.c + +objects/gui_at_fs.o: gui_at_fs.c + $(CCC) -o $@ gui_at_fs.c + +objects/gui_at_sb.o: gui_at_sb.c + $(CCC) -o $@ gui_at_sb.c + +objects/gui_athena.o: gui_athena.c + $(CCC) -o $@ gui_athena.c + +objects/beval.o: beval.c + $(CCC) -o $@ beval.c + +objects/gui_beval.o: gui_beval.c + $(CCC) -o $@ gui_beval.c + +objects/gui_gtk.o: gui_gtk.c + $(CCC) -o $@ gui_gtk.c + +objects/gui_gtk_f.o: gui_gtk_f.c + $(CCC) -o $@ gui_gtk_f.c + +objects/gui_gtk_gresources.o: auto/gui_gtk_gresources.c + $(CCC_NF) $(PERL_CFLAGS) $(ALL_CFLAGS) -o $@ auto/gui_gtk_gresources.c + +objects/gui_gtk_x11.o: gui_gtk_x11.c + $(CCC) -o $@ gui_gtk_x11.c + +objects/gui_motif.o: gui_motif.c + $(CCC) -o $@ gui_motif.c + +objects/gui_xmdlg.o: gui_xmdlg.c + $(CCC) -o $@ gui_xmdlg.c + +objects/gui_xmebw.o: gui_xmebw.c + $(CCC) -o $@ gui_xmebw.c + +objects/gui_x11.o: gui_x11.c + $(CCC) -o $@ gui_x11.c + +objects/gui_photon.o: gui_photon.c + $(CCC) -o $@ gui_photon.c + +objects/gui_mac.o: gui_mac.c + $(CCC) -o $@ gui_mac.c + +objects/hangulin.o: hangulin.c + $(CCC) -o $@ hangulin.c + +objects/if_cscope.o: if_cscope.c + $(CCC) -o $@ if_cscope.c + +objects/if_xcmdsrv.o: if_xcmdsrv.c + $(CCC) -o $@ if_xcmdsrv.c + +objects/if_lua.o: if_lua.c + $(CCC_NF) $(LUA_CFLAGS) $(ALL_CFLAGS) -o $@ if_lua.c + +objects/if_mzsch.o: if_mzsch.c $(MZSCHEME_EXTRA) + $(CCC) -o $@ $(MZSCHEME_CFLAGS_EXTRA) if_mzsch.c + +mzscheme_base.c: + $(MZSCHEME_MZC) --c-mods mzscheme_base.c ++lib scheme/base + +objects/if_perl.o: auto/if_perl.c + $(CCC_NF) $(PERL_CFLAGS) $(ALL_CFLAGS) -o $@ auto/if_perl.c + +objects/if_perlsfio.o: if_perlsfio.c + $(CCC_NF) $(PERL_CFLAGS) $(ALL_CFLAGS) -o $@ if_perlsfio.c + +objects/if_python.o: if_python.c if_py_both.h + $(CCC_NF) $(PYTHON_CFLAGS) $(PYTHON_CFLAGS_EXTRA) $(ALL_CFLAGS) -o $@ if_python.c + +objects/if_python3.o: if_python3.c if_py_both.h + $(CCC_NF) $(PYTHON3_CFLAGS) $(PYTHON3_CFLAGS_EXTRA) $(ALL_CFLAGS) -o $@ if_python3.c + +objects/if_ruby.o: if_ruby.c + $(CCC_NF) $(RUBY_CFLAGS) $(ALL_CFLAGS) -o $@ if_ruby.c + +objects/if_tcl.o: if_tcl.c + $(CCC_NF) $(TCL_CFLAGS) $(ALL_CFLAGS) -o $@ if_tcl.c + +objects/indent.o: indent.c + $(CCC) -o $@ indent.c + +objects/json.o: json.c + $(CCC) -o $@ json.c + +objects/json_test.o: json_test.c + $(CCC) -o $@ json_test.c + +objects/kword_test.o: kword_test.c + $(CCC) -o $@ kword_test.c + +objects/list.o: list.c + $(CCC) -o $@ list.c + +objects/main.o: main.c + $(CCC) -o $@ main.c + +objects/mark.o: mark.c + $(CCC) -o $@ mark.c + +objects/memfile.o: memfile.c + $(CCC) -o $@ memfile.c + +objects/memfile_test.o: memfile_test.c + $(CCC) -o $@ memfile_test.c + +objects/memline.o: memline.c + $(CCC) -o $@ memline.c + +objects/menu.o: menu.c + $(CCC) -o $@ menu.c + +objects/message.o: message.c + $(CCC) -o $@ message.c + +objects/message_test.o: message_test.c + $(CCC) -o $@ message_test.c + +objects/misc1.o: misc1.c + $(CCC) -o $@ misc1.c + +objects/misc2.o: misc2.c + $(CCC) -o $@ misc2.c + +objects/move.o: move.c + $(CCC) -o $@ move.c + +objects/mbyte.o: mbyte.c + $(CCC) -o $@ mbyte.c + +objects/normal.o: normal.c + $(CCC) -o $@ normal.c + +objects/ops.o: ops.c + $(CCC) -o $@ ops.c + +objects/option.o: option.c + $(CCC_NF) $(LUA_CFLAGS) $(PERL_CFLAGS) $(PYTHON_CFLAGS) $(PYTHON3_CFLAGS) $(RUBY_CFLAGS) $(TCL_CFLAGS) $(ALL_CFLAGS) -o $@ option.c + +objects/os_beos.o: os_beos.c + $(CCC) -o $@ os_beos.c + +objects/os_qnx.o: os_qnx.c + $(CCC) -o $@ os_qnx.c + +objects/os_macosx.o: os_macosx.m + $(CCC) -o $@ os_macosx.m + +objects/os_mac_conv.o: os_mac_conv.c + $(CCC) -o $@ os_mac_conv.c + +objects/os_unix.o: os_unix.c + $(CCC) -o $@ os_unix.c + +objects/os_mswin.o: os_mswin.c + $(CCC) -o $@ os_mswin.c + +objects/winclip.o: winclip.c + $(CCC) -o $@ winclip.c + +objects/pathdef.o: auto/pathdef.c + $(CCC) -o $@ auto/pathdef.c + +objects/popupmnu.o: popupmnu.c + $(CCC) -o $@ popupmnu.c + +objects/pty.o: pty.c + $(CCC) -o $@ pty.c + +objects/quickfix.o: quickfix.c + $(CCC) -o $@ quickfix.c + +objects/regexp.o: regexp.c regexp_nfa.c + $(CCC) -o $@ regexp.c + +objects/screen.o: screen.c + $(CCC) -o $@ screen.c + +objects/search.o: search.c + $(CCC) -o $@ search.c + +objects/sha256.o: sha256.c + $(CCC) -o $@ sha256.c + +objects/sign.o: sign.c + $(CCC) -o $@ sign.c + +objects/spell.o: spell.c + $(CCC) -o $@ spell.c + +objects/spellfile.o: spellfile.c + $(CCC) -o $@ spellfile.c + +objects/syntax.o: syntax.c + $(CCC) -o $@ syntax.c + +objects/tag.o: tag.c + $(CCC) -o $@ tag.c + +objects/term.o: term.c + $(CCC) -o $@ term.c + +objects/terminal.o: terminal.c $(TERM_DEPS) + $(CCC) -o $@ terminal.c + +objects/textprop.o: textprop.c + $(CCC) -o $@ textprop.c + +objects/ui.o: ui.c + $(CCC) -o $@ ui.c + +objects/undo.o: undo.c + $(CCC) -o $@ undo.c + +objects/userfunc.o: userfunc.c + $(CCC) -o $@ userfunc.c + +objects/window.o: window.c + $(CCC) -o $@ window.c + +objects/netbeans.o: netbeans.c + $(CCC) -o $@ netbeans.c + +objects/channel.o: channel.c + $(CCC) -o $@ channel.c + +Makefile: + @echo The name of the makefile MUST be "Makefile" (with capital M)!!!! + +CCCTERM = $(CCC_NF) $(VTERM_CFLAGS) $(ALL_CFLAGS) -DINLINE="" \ + -DVSNPRINTF=vim_vsnprintf \ + -DIS_COMBINING_FUNCTION=utf_iscomposing_uint \ + -DWCWIDTH_FUNCTION=utf_uint2cells + +objects/encoding.o: libvterm/src/encoding.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/encoding.c + +objects/keyboard.o: libvterm/src/keyboard.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/keyboard.c + +objects/mouse.o: libvterm/src/mouse.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/mouse.c + +objects/parser.o: libvterm/src/parser.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/parser.c + +objects/pen.o: libvterm/src/pen.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/pen.c + +objects/termscreen.o: libvterm/src/termscreen.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/termscreen.c + +objects/state.o: libvterm/src/state.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/state.c + +objects/unicode.o: libvterm/src/unicode.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/unicode.c + +objects/vterm.o: libvterm/src/vterm.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/vterm.c + +CCCDIFF = $(CCC_NF) $(ALL_CFLAGS) + +objects/xdiffi.o: xdiff/xdiffi.c $(XDIFF_INCL) + $(CCCDIFF) -o $@ xdiff/xdiffi.c + +objects/xprepare.o: xdiff/xprepare.c $(XDIFF_INCL) + $(CCCDIFF) -o $@ xdiff/xprepare.c + +objects/xutils.o: xdiff/xutils.c $(XDIFF_INCL) + $(CCCDIFF) -o $@ xdiff/xutils.c + +objects/xemit.o: xdiff/xemit.c $(XDIFF_INCL) + $(CCCDIFF) -o $@ xdiff/xemit.c + +objects/xhistogram.o: xdiff/xhistogram.c $(XDIFF_INCL) + $(CCCDIFF) -o $@ xdiff/xhistogram.c + +objects/xpatience.o: xdiff/xpatience.c $(XDIFF_INCL) + $(CCCDIFF) -o $@ xdiff/xpatience.c + + +############################################################################### +### MacOS X installation +### +### This installs a runnable Vim.app in $(prefix) + +REZ = /Developer/Tools/Rez +RESDIR = $(APPDIR)/Contents/Resources +VERSION = $(VIMMAJOR).$(VIMMINOR) + +### Common flags +M4FLAGSX = $(M4FLAGS) -DAPP_EXE=$(VIMNAME) -DAPP_NAME=$(VIMNAME) \ + -DAPP_VER=$(VERSION) + +install_macosx: gui_bundle +# Remove the link to the runtime dir, don't want to copy all of that. + -rm $(RESDIR)/vim/runtime + $(INSTALL_DATA_R) $(APPDIR) $(DESTDIR)$(prefix) +# Generate the help tags file now, it won't work with "make installruntime". + -@srcdir=`pwd`; cd $(HELPSOURCE); $(MAKE) VIMEXE=$$srcdir/$(VIMTARGET) vimtags +# Install the runtime files. Recursive! + $(MKDIR_P) $(DESTDIR)$(prefix)/$(RESDIR)/vim/runtime + srcdir=`pwd`; $(MAKE) -f Makefile installruntime \ + VIMEXE=$$srcdir/$(VIMTARGET) \ + prefix=$(DESTDIR)$(prefix)/$(RESDIR)$(VIMDIR) \ + exec_prefix=$(DESTDIR)$(prefix)/$(APPDIR)/Contents \ + BINDIR=$(DESTDIR)$(prefix)/$(APPDIR)/Contents/MacOS \ + VIMLOC=$(DESTDIR)$(prefix)/$(RESDIR)$(VIMDIR) \ + VIMRTLOC=$(DESTDIR)$(prefix)/$(RESDIR)$(VIMDIR)/runtime +# Put the link back. + ln -s `pwd`/../runtime $(RESDIR)/vim +# Copy rgb.txt, Mac doesn't always have X11 + $(INSTALL_DATA) $(SCRIPTSOURCE)/rgb.txt $(DESTDIR)$(prefix)/$(RESDIR)/vim/runtime +# TODO: Create the vimtutor and/or gvimtutor application. + +gui_bundle: $(RESDIR) bundle-dir bundle-executable bundle-info bundle-resource \ + bundle-language + +$(RESDIR): + $(MKDIR_P) $@ + +bundle-dir: $(APPDIR)/Contents $(VIMTARGET) +# Make a link to the runtime directory, so that we can try out the executable +# without installing it. + $(MKDIR_P) $(RESDIR)/vim + -ln -s `pwd`/../runtime $(RESDIR)/vim + +bundle-executable: $(VIMTARGET) + $(MKDIR_P) $(APPDIR)/Contents/MacOS + cp $(VIMTARGET) $(APPDIR)/Contents/MacOS/$(VIMTARGET) + +bundle-info: bundle-dir + @echo "Creating PkgInfo" + @echo -n "APPLVIM!" > $(APPDIR)/Contents/PkgInfo + @echo "Creating Info.plist" + m4 $(M4FLAGSX) infplist.xml > $(APPDIR)/Contents/Info.plist + +bundle-resource: bundle-dir bundle-rsrc + cp -f $(RSRC_DIR)/*.icns $(RESDIR) + +### Classic resources +# Resource fork (in the form of a .rsrc file) for Classic Vim (Mac OS 9) +# This file is also required for OS X Vim. +bundle-rsrc: os_mac.rsr.hqx + @echo "Creating resource fork" + python dehqx.py $< + rm -f gui_mac.rsrc + mv gui_mac.rsrc.rsrcfork $(RESDIR)/$(VIMNAME).rsrc + +# po/Make_osx.pl says something about generating a Mac message file +# for Ukrainian. Would somebody using Mac OS X in Ukrainian +# *really* be upset that Carbon Vim was not localised in +# Ukrainian? +# +#bundle-language: bundle-dir po/Make_osx.pl +# cd po && perl Make_osx.pl --outdir ../$(RESDIR) $(MULTILANG) +bundle-language: bundle-dir + +$(APPDIR)/Contents: + $(MKDIR_P) $(APPDIR)/Contents/MacOS + $(MKDIR_P) $(RESDIR)/English.lproj + + +############################################################################### +### (automatically generated by 'make depend') +### Dependencies: +objects/arabic.o: arabic.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/autocmd.o: autocmd.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/beval.o: beval.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/blob.o: blob.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/blowfish.o: blowfish.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/buffer.o: buffer.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/charset.o: charset.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/crypt.o: crypt.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/crypt_zip.o: crypt_zip.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/dict.o: dict.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/diff.o: diff.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h xdiff/xdiff.h vim.h +objects/digraph.o: digraph.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/edit.o: edit.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/eval.o: eval.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/evalfunc.o: evalfunc.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/ex_cmds.o: ex_cmds.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/ex_cmds2.o: ex_cmds2.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/ex_docmd.o: ex_docmd.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h ex_cmdidxs.h +objects/ex_eval.o: ex_eval.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/ex_getln.o: ex_getln.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/farsi.o: farsi.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/fileio.o: fileio.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/fold.o: fold.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/getchar.o: getchar.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/hardcopy.o: hardcopy.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/hashtab.o: hashtab.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/if_cscope.o: if_cscope.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h if_cscope.h +objects/if_xcmdsrv.o: if_xcmdsrv.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/indent.o: indent.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/json.o: json.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/list.o: list.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/main.o: main.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/mark.o: mark.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/memfile.o: memfile.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/memline.o: memline.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/menu.o: menu.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/message.o: message.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/misc1.o: misc1.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/misc2.o: misc2.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/move.o: move.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/mbyte.o: mbyte.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/normal.o: normal.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/ops.o: ops.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/option.o: option.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/os_unix.o: os_unix.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h os_unixx.h +objects/pathdef.o: auto/pathdef.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/popupmnu.o: popupmnu.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/pty.o: pty.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/quickfix.o: quickfix.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/regexp.o: regexp.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h regexp_nfa.c +objects/screen.o: screen.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/search.o: search.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/sha256.o: sha256.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/sign.o: sign.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/spell.o: spell.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/spellfile.o: spellfile.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/syntax.o: syntax.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/tag.o: tag.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/term.o: term.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h libvterm/include/vterm.h \ + libvterm/include/vterm_keycodes.h +objects/terminal.o: terminal.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h libvterm/include/vterm.h \ + libvterm/include/vterm_keycodes.h +objects/textprop.o: textprop.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/ui.o: ui.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/undo.o: undo.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/userfunc.o: userfunc.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/version.o: version.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/window.o: window.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/gui.o: gui.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/gui_gtk.o: gui_gtk.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h gui_gtk_f.h +objects/gui_gtk_f.o: gui_gtk_f.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h gui_gtk_f.h +objects/gui_motif.o: gui_motif.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h gui_xmebw.h ../pixmaps/alert.xpm \ + ../pixmaps/error.xpm ../pixmaps/generic.xpm ../pixmaps/info.xpm \ + ../pixmaps/quest.xpm gui_x11_pm.h ../pixmaps/tb_new.xpm \ + ../pixmaps/tb_open.xpm ../pixmaps/tb_close.xpm ../pixmaps/tb_save.xpm \ + ../pixmaps/tb_print.xpm ../pixmaps/tb_cut.xpm ../pixmaps/tb_copy.xpm \ + ../pixmaps/tb_paste.xpm ../pixmaps/tb_find.xpm \ + ../pixmaps/tb_find_next.xpm ../pixmaps/tb_find_prev.xpm \ + ../pixmaps/tb_find_help.xpm ../pixmaps/tb_exit.xpm \ + ../pixmaps/tb_undo.xpm ../pixmaps/tb_redo.xpm ../pixmaps/tb_help.xpm \ + ../pixmaps/tb_macro.xpm ../pixmaps/tb_make.xpm \ + ../pixmaps/tb_save_all.xpm ../pixmaps/tb_jump.xpm \ + ../pixmaps/tb_ctags.xpm ../pixmaps/tb_load_session.xpm \ + ../pixmaps/tb_save_session.xpm ../pixmaps/tb_new_session.xpm \ + ../pixmaps/tb_blank.xpm ../pixmaps/tb_maximize.xpm \ + ../pixmaps/tb_split.xpm ../pixmaps/tb_minimize.xpm \ + ../pixmaps/tb_shell.xpm ../pixmaps/tb_replace.xpm \ + ../pixmaps/tb_vsplit.xpm ../pixmaps/tb_maxwidth.xpm \ + ../pixmaps/tb_minwidth.xpm +objects/gui_xmdlg.o: gui_xmdlg.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/gui_xmebw.o: gui_xmebw.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h gui_xmebwp.h gui_xmebw.h +objects/gui_athena.o: gui_athena.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h gui_at_sb.h gui_x11_pm.h \ + ../pixmaps/tb_new.xpm ../pixmaps/tb_open.xpm ../pixmaps/tb_close.xpm \ + ../pixmaps/tb_save.xpm ../pixmaps/tb_print.xpm ../pixmaps/tb_cut.xpm \ + ../pixmaps/tb_copy.xpm ../pixmaps/tb_paste.xpm ../pixmaps/tb_find.xpm \ + ../pixmaps/tb_find_next.xpm ../pixmaps/tb_find_prev.xpm \ + ../pixmaps/tb_find_help.xpm ../pixmaps/tb_exit.xpm \ + ../pixmaps/tb_undo.xpm ../pixmaps/tb_redo.xpm ../pixmaps/tb_help.xpm \ + ../pixmaps/tb_macro.xpm ../pixmaps/tb_make.xpm \ + ../pixmaps/tb_save_all.xpm ../pixmaps/tb_jump.xpm \ + ../pixmaps/tb_ctags.xpm ../pixmaps/tb_load_session.xpm \ + ../pixmaps/tb_save_session.xpm ../pixmaps/tb_new_session.xpm \ + ../pixmaps/tb_blank.xpm ../pixmaps/tb_maximize.xpm \ + ../pixmaps/tb_split.xpm ../pixmaps/tb_minimize.xpm \ + ../pixmaps/tb_shell.xpm ../pixmaps/tb_replace.xpm \ + ../pixmaps/tb_vsplit.xpm ../pixmaps/tb_maxwidth.xpm \ + ../pixmaps/tb_minwidth.xpm +objects/gui_gtk_x11.o: gui_gtk_x11.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h auto/gui_gtk_gresources.h gui_gtk_f.h \ + ../runtime/vim32x32.xpm ../runtime/vim16x16.xpm ../runtime/vim48x48.xpm +objects/gui_x11.o: gui_x11.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h ../runtime/vim32x32.xpm \ + ../runtime/vim16x16.xpm ../runtime/vim48x48.xpm +objects/gui_at_sb.o: gui_at_sb.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h gui_at_sb.h +objects/gui_at_fs.o: gui_at_fs.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h gui_at_sb.h +objects/json_test.o: json_test.c main.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h json.c +objects/kword_test.o: kword_test.c main.c vim.h protodef.h auto/config.h \ + feature.h os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h \ + option.h beval.h proto/gui_beval.pro structs.h regexp.h gui.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h farsi.h arabic.h charset.c +objects/memfile_test.o: memfile_test.c main.c vim.h protodef.h auto/config.h \ + feature.h os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h \ + option.h beval.h proto/gui_beval.pro structs.h regexp.h gui.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h farsi.h arabic.h memfile.c +objects/message_test.o: message_test.c main.c vim.h protodef.h auto/config.h \ + feature.h os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h \ + option.h beval.h proto/gui_beval.pro structs.h regexp.h gui.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h farsi.h arabic.h message.c +objects/hangulin.o: hangulin.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/if_lua.o: if_lua.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/if_mzsch.o: if_mzsch.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h if_mzsch.h +objects/if_perl.o: auto/if_perl.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/if_perlsfio.o: if_perlsfio.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/if_python.o: if_python.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h if_py_both.h +objects/if_python3.o: if_python3.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h if_py_both.h +objects/if_tcl.o: if_tcl.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/if_ruby.o: if_ruby.c protodef.h auto/config.h vim.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/gui_beval.o: gui_beval.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/netbeans.o: netbeans.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h version.h +objects/channel.o: channel.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h +objects/gui_gtk_gresources.o: auto/gui_gtk_gresources.c +objects/encoding.o: libvterm/src/encoding.c libvterm/src/vterm_internal.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ + libvterm/src/encoding/DECdrawing.inc libvterm/src/encoding/uk.inc +objects/keyboard.o: libvterm/src/keyboard.c libvterm/src/vterm_internal.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ + libvterm/src/utf8.h +objects/mouse.o: libvterm/src/mouse.c libvterm/src/vterm_internal.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ + libvterm/src/utf8.h +objects/parser.o: libvterm/src/parser.c libvterm/src/vterm_internal.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h +objects/pen.o: libvterm/src/pen.c libvterm/src/vterm_internal.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h +objects/state.o: libvterm/src/state.c libvterm/src/vterm_internal.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h +objects/termscreen.o: libvterm/src/termscreen.c libvterm/src/vterm_internal.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ + libvterm/src/rect.h libvterm/src/utf8.h +objects/unicode.o: libvterm/src/unicode.c libvterm/src/vterm_internal.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h +objects/vterm.o: libvterm/src/vterm.c libvterm/src/vterm_internal.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ + libvterm/src/utf8.h +objects/xdiffi.o: xdiff/xdiffi.c xdiff/xinclude.h auto/config.h \ + xdiff/xmacros.h xdiff/xdiff.h vim.h protodef.h \ + auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h \ + macros.h option.h beval.h proto/gui_beval.pro \ + structs.h regexp.h gui.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h \ + farsi.h arabic.h xdiff/xtypes.h xdiff/xutils.h \ + xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h +objects/xemit.o: xdiff/xemit.c xdiff/xinclude.h auto/config.h \ + xdiff/xmacros.h xdiff/xdiff.h vim.h protodef.h \ + auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h \ + macros.h option.h beval.h proto/gui_beval.pro \ + structs.h regexp.h gui.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h \ + farsi.h arabic.h xdiff/xtypes.h xdiff/xutils.h \ + xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h +objects/xprepare.o: xdiff/xprepare.c xdiff/xinclude.h auto/config.h \ + xdiff/xmacros.h xdiff/xdiff.h vim.h protodef.h \ + auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h \ + macros.h option.h beval.h proto/gui_beval.pro \ + structs.h regexp.h gui.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h \ + farsi.h arabic.h xdiff/xtypes.h xdiff/xutils.h \ + xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h +objects/xutils.o: xdiff/xutils.c xdiff/xinclude.h auto/config.h \ + xdiff/xmacros.h xdiff/xdiff.h vim.h protodef.h \ + auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h \ + macros.h option.h beval.h proto/gui_beval.pro \ + structs.h regexp.h gui.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h \ + farsi.h arabic.h xdiff/xtypes.h xdiff/xutils.h \ + xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h +objects/xhistogram.o: xdiff/xhistogram.c xdiff/xinclude.h auto/config.h \ + xdiff/xmacros.h xdiff/xdiff.h vim.h protodef.h \ + auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h \ + macros.h option.h beval.h proto/gui_beval.pro \ + structs.h regexp.h gui.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h \ + farsi.h arabic.h xdiff/xtypes.h xdiff/xutils.h \ + xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h +objects/xpatience.o: xdiff/xpatience.c xdiff/xinclude.h auto/config.h \ + xdiff/xmacros.h xdiff/xdiff.h vim.h protodef.h \ + auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h \ + macros.h option.h beval.h proto/gui_beval.pro \ + structs.h regexp.h gui.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h \ + farsi.h arabic.h xdiff/xtypes.h xdiff/xutils.h \ + xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h diff --git a/src/README.txt b/src/README.txt new file mode 100644 index 0000000..e1dc36f --- /dev/null +++ b/src/README.txt @@ -0,0 +1,160 @@ +README for the Vim source code + +Here are a few hints for finding your way around the source code. This +doesn't make it less complex than it is, but it gets you started. + +You might also want to read ":help development". + + +JUMPING AROUND + +First of all, use ":make tags" to generate a tags file, so that you can jump +around in the source code. + +To jump to a function or variable definition, move the cursor on the name and +use the CTRL-] command. Use CTRL-T or CTRL-O to jump back. + +To jump to a file, move the cursor on its name and use the "gf" command. + +Most code can be found in a file with an obvious name (incomplete list): + autocmd.c autocommands + buffer.c manipulating buffers (loaded files) + diff.c diff mode (vimdiff) + eval.c expression evaluation + fileio.c reading and writing files + fold.c folding + getchar.c getting characters and key mapping + mark.c marks + mbyte.c multi-byte character handling + memfile.c storing lines for buffers in a swapfile + memline.c storing lines for buffers in memory + menu.c menus + message.c (error) messages + ops.c handling operators ("d", "y", "p") + option.c options + quickfix.c quickfix commands (":make", ":cn") + regexp.c pattern matching + screen.c updating the windows + search.c pattern searching + sign.c signs + spell.c spell checking + syntax.c syntax and other highlighting + tag.c tags + term.c terminal handling, termcap codes + undo.c undo and redo + window.c handling split windows + + +DEBUGGING + +If you have a reasonable recent version of gdb, you can use the :Termdebug +command to debug Vim. See ":help :Termdebug". + +When something is time critical or stepping through code is a hassle, use the +channel logging to create a time-stamped log file. Add lines to the code like +this: + ch_log(NULL, "Value is now %02x", value); +After compiling and starting Vim, do: + :call ch_logfile('debuglog', 'w') +And edit "debuglog" to see what happens. The channel functions already have +ch_log() calls, thus you always see that in the log. + + +IMPORTANT VARIABLES + +The current mode is stored in "State". The values it can have are NORMAL, +INSERT, CMDLINE, and a few others. + +The current window is "curwin". The current buffer is "curbuf". These point +to structures with the cursor position in the window, option values, the file +name, etc. These are defined in structs.h. + +All the global variables are declared in globals.h. + + +THE MAIN LOOP + +This is conveniently called main_loop(). It updates a few things and then +calls normal_cmd() to process a command. This returns when the command is +finished. + +The basic idea is that Vim waits for the user to type a character and +processes it until another character is needed. Thus there are several places +where Vim waits for a character to be typed. The vgetc() function is used for +this. It also handles mapping. + +Updating the screen is mostly postponed until a command or a sequence of +commands has finished. The work is done by update_screen(), which calls +win_update() for every window, which calls win_line() for every line. +See the start of screen.c for more explanations. + + +COMMAND-LINE MODE + +When typing a ":", normal_cmd() will call getcmdline() to obtain a line with +an Ex command. getcmdline() contains a loop that will handle each typed +character. It returns when hitting or or some other character that +ends the command line mode. + + +EX COMMANDS + +Ex commands are handled by the function do_cmdline(). It does the generic +parsing of the ":" command line and calls do_one_cmd() for each separate +command. It also takes care of while loops. + +do_one_cmd() parses the range and generic arguments and puts them in the +exarg_t and passes it to the function that handles the command. + +The ":" commands are listed in ex_cmds.h. The third entry of each item is the +name of the function that handles the command. The last entry are the flags +that are used for the command. + + +NORMAL MODE COMMANDS + +The Normal mode commands are handled by the normal_cmd() function. It also +handles the optional count and an extra character for some commands. These +are passed in a cmdarg_t to the function that handles the command. + +There is a table nv_cmds in normal.c which lists the first character of every +command. The second entry of each item is the name of the function that +handles the command. + + +INSERT MODE COMMANDS + +When doing an "i" or "a" command, normal_cmd() will call the edit() function. +It contains a loop that waits for the next character and handles it. It +returns when leaving Insert mode. + + +OPTIONS + +There is a list with all option names in option.c, called options[]. + + +THE GUI + +Most of the GUI code is implemented like it was a clever terminal. Typing a +character, moving a scrollbar, clicking the mouse, etc. are all translated +into events which are written in the input buffer. These are read by the +main code, just like reading from a terminal. The code for this is scattered +through gui.c. For example: gui_send_mouse_event() for a mouse click and +gui_menu_cb() for a menu action. Key hits are handled by the system-specific +GUI code, which calls add_to_input_buf() to send the key code. + +Updating the GUI window is done by writing codes in the output buffer, just +like writing to a terminal. When the buffer gets full or is flushed, +gui_write() will parse the codes and draw the appropriate items. Finally the +system-specific GUI code will be called to do the work. + + +DEBUGGING THE GUI + +Remember to prevent that gvim forks and the debugger thinks Vim has exited, +add the "-f" argument. In gdb: "run -f -g". + +When stepping through display updating code, the focus event is triggered +when going from the debugger to Vim and back. To avoid this, recompile with +some code in gui_focus_change() disabled. diff --git a/src/VisVim/Commands.cpp b/src/VisVim/Commands.cpp new file mode 100644 index 0000000..569fbb8 --- /dev/null +++ b/src/VisVim/Commands.cpp @@ -0,0 +1,710 @@ +#include "stdafx.h" +#include // For _bstr_t +#include "VisVim.h" +#include "Commands.h" +#include "OleAut.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; + +#endif + + +// Change directory before opening file? +#define CD_SOURCE 0 // Cd to source path +#define CD_SOURCE_PARENT 1 // Cd to parent directory of source path +#define CD_NONE 2 // No cd + + +static BOOL g_bEnableVim = TRUE; // Vim enabled +static BOOL g_bDevStudioEditor = FALSE; // Open file in Dev Studio editor simultaneously +static BOOL g_bNewTabs = FALSE; +static int g_ChangeDir = CD_NONE; // CD after file open? + +static void VimSetEnableState(BOOL bEnableState); +static BOOL VimOpenFile(BSTR& FileName, long LineNr); +static DISPID VimGetDispatchId(COleAutomationControl& VimOle, char* Method); +static void VimErrDiag(COleAutomationControl& VimOle); +static void VimChangeDir(COleAutomationControl& VimOle, DISPID DispatchId, BSTR& FileName); +static void DebugMsg(char* Msg, char* Arg = NULL); + + +///////////////////////////////////////////////////////////////////////////// +// CCommands + +CCommands::CCommands() +{ + // m_pApplication == NULL; M$ Code generation bug!!! + m_pApplication = NULL; + m_pApplicationEventsObj = NULL; + m_pDebuggerEventsObj = NULL; +} + +CCommands::~CCommands() +{ + ASSERT(m_pApplication != NULL); + if (m_pApplication) + { + m_pApplication->Release(); + m_pApplication = NULL; + } +} + +void CCommands::SetApplicationObject(IApplication * pApplication) +{ + // This function assumes pApplication has already been AddRef'd + // for us, which CDSAddIn did in it's QueryInterface call + // just before it called us. + m_pApplication = pApplication; + if (! m_pApplication) + return; + + // Create Application event handlers + XApplicationEventsObj::CreateInstance(&m_pApplicationEventsObj); + if (! m_pApplicationEventsObj) + { + ReportInternalError("XApplicationEventsObj::CreateInstance"); + return; + } + m_pApplicationEventsObj->AddRef(); + m_pApplicationEventsObj->Connect(m_pApplication); + m_pApplicationEventsObj->m_pCommands = this; + +#ifdef NEVER + // Create Debugger event handler + CComPtr < IDispatch > pDebugger; + if (SUCCEEDED(m_pApplication->get_Debugger(&pDebugger)) + && pDebugger != NULL) + { + XDebuggerEventsObj::CreateInstance(&m_pDebuggerEventsObj); + m_pDebuggerEventsObj->AddRef(); + m_pDebuggerEventsObj->Connect(pDebugger); + m_pDebuggerEventsObj->m_pCommands = this; + } +#endif + + // Get settings from registry HKEY_CURRENT_USER\Software\Vim\VisVim + HKEY hAppKey = GetAppKey("Vim"); + if (hAppKey) + { + HKEY hSectionKey = GetSectionKey(hAppKey, "VisVim"); + if (hSectionKey) + { + g_bEnableVim = GetRegistryInt(hSectionKey, "EnableVim", + g_bEnableVim); + g_bDevStudioEditor = GetRegistryInt(hSectionKey, + "DevStudioEditor", g_bDevStudioEditor); + g_bNewTabs = GetRegistryInt(hSectionKey, "NewTabs", + g_bNewTabs); + g_ChangeDir = GetRegistryInt(hSectionKey, "ChangeDir", + g_ChangeDir); + RegCloseKey(hSectionKey); + } + RegCloseKey(hAppKey); + } +} + +void CCommands::UnadviseFromEvents() +{ + ASSERT(m_pApplicationEventsObj != NULL); + if (m_pApplicationEventsObj) + { + m_pApplicationEventsObj->Disconnect(m_pApplication); + m_pApplicationEventsObj->Release(); + m_pApplicationEventsObj = NULL; + } + +#ifdef NEVER + if (m_pDebuggerEventsObj) + { + // Since we were able to connect to the Debugger events, we + // should be able to access the Debugger object again to + // unadvise from its events (thus the VERIFY_OK below--see + // stdafx.h). + CComPtr < IDispatch > pDebugger; + VERIFY_OK(m_pApplication->get_Debugger(&pDebugger)); + ASSERT(pDebugger != NULL); + m_pDebuggerEventsObj->Disconnect(pDebugger); + m_pDebuggerEventsObj->Release(); + m_pDebuggerEventsObj = NULL; + } +#endif +} + + +///////////////////////////////////////////////////////////////////////////// +// Event handlers + +// Application events + +HRESULT CCommands::XApplicationEvents::BeforeBuildStart() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::BuildFinish(long nNumErrors, long nNumWarnings) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::BeforeApplicationShutDown() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +// The open document event handle is the place where the real interface work +// is done. +// Vim gets called from here. +// +HRESULT CCommands::XApplicationEvents::DocumentOpen(IDispatch * theDocument) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (! g_bEnableVim) + // Vim not enabled or empty command line entered + return S_OK; + + // First get the current file name and line number + + // Get the document object + CComQIPtr < ITextDocument, &IID_ITextDocument > pDoc(theDocument); + if (! pDoc) + return S_OK; + + BSTR FileName; + long LineNr = -1; + + // Get the document name + if (FAILED(pDoc->get_FullName(&FileName))) + return S_OK; + + LPDISPATCH pDispSel; + + // Get a selection object dispatch pointer + if (SUCCEEDED(pDoc->get_Selection(&pDispSel))) + { + // Get the selection object + CComQIPtr < ITextSelection, &IID_ITextSelection > pSel(pDispSel); + + if (pSel) + // Get the selection line number + pSel->get_CurrentLine(&LineNr); + + pDispSel->Release(); + } + + // Open the file in Vim and position to the current line + if (VimOpenFile(FileName, LineNr)) + { + if (! g_bDevStudioEditor) + { + // Close the document in developer studio + CComVariant vSaveChanges = dsSaveChangesPrompt; + DsSaveStatus Saved; + + pDoc->Close(vSaveChanges, &Saved); + } + } + + // We're done here + SysFreeString(FileName); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::BeforeDocumentClose(IDispatch * theDocument) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::DocumentSave(IDispatch * theDocument) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::NewDocument(IDispatch * theDocument) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (! g_bEnableVim) + // Vim not enabled or empty command line entered + return S_OK; + + // First get the current file name and line number + + CComQIPtr < ITextDocument, &IID_ITextDocument > pDoc(theDocument); + if (! pDoc) + return S_OK; + + BSTR FileName; + HRESULT hr; + + hr = pDoc->get_FullName(&FileName); + if (FAILED(hr)) + return S_OK; + + // Open the file in Vim and position to the current line + if (VimOpenFile(FileName, 0)) + { + if (! g_bDevStudioEditor) + { + // Close the document in developer studio + CComVariant vSaveChanges = dsSaveChangesPrompt; + DsSaveStatus Saved; + + pDoc->Close(vSaveChanges, &Saved); + } + } + + SysFreeString(FileName); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::WindowActivate(IDispatch * theWindow) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::WindowDeactivate(IDispatch * theWindow) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::WorkspaceOpen() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::WorkspaceClose() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +HRESULT CCommands::XApplicationEvents::NewWorkspace() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + +// Debugger event + +HRESULT CCommands::XDebuggerEvents::BreakpointHit(IDispatch * pBreakpoint) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return S_OK; +} + + +///////////////////////////////////////////////////////////////////////////// +// VisVim dialog + +class CMainDialog : public CDialog +{ + public: + CMainDialog(CWnd * pParent = NULL); // Standard constructor + + //{{AFX_DATA(CMainDialog) + enum { IDD = IDD_ADDINMAIN }; + int m_ChangeDir; + BOOL m_bDevStudioEditor; + BOOL m_bNewTabs; + //}}AFX_DATA + + //{{AFX_VIRTUAL(CMainDialog) + protected: + virtual void DoDataExchange(CDataExchange * pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + protected: + //{{AFX_MSG(CMainDialog) + afx_msg void OnEnable(); + afx_msg void OnDisable(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CMainDialog::CMainDialog(CWnd * pParent /* =NULL */ ) + : CDialog(CMainDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(CMainDialog) + m_ChangeDir = -1; + m_bDevStudioEditor = FALSE; + m_bNewTabs = FALSE; + //}}AFX_DATA_INIT +} + +void CMainDialog::DoDataExchange(CDataExchange * pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CMainDialog) + DDX_Radio(pDX, IDC_CD_SOURCE_PATH, m_ChangeDir); + DDX_Check(pDX, IDC_DEVSTUDIO_EDITOR, m_bDevStudioEditor); + DDX_Check(pDX, IDC_NEW_TABS, m_bNewTabs); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CMainDialog, CDialog) + //{{AFX_MSG_MAP(CMainDialog) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CCommands methods + +STDMETHODIMP CCommands::VisVimDialog() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + // Use m_pApplication to access the Developer Studio Application + // object, + // and VERIFY_OK to see error strings in DEBUG builds of your add-in + // (see stdafx.h) + + VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE)); + + CMainDialog Dlg; + + Dlg.m_bDevStudioEditor = g_bDevStudioEditor; + Dlg.m_bNewTabs = g_bNewTabs; + Dlg.m_ChangeDir = g_ChangeDir; + if (Dlg.DoModal() == IDOK) + { + g_bDevStudioEditor = Dlg.m_bDevStudioEditor; + g_bNewTabs = Dlg.m_bNewTabs; + g_ChangeDir = Dlg.m_ChangeDir; + + // Save settings to registry HKEY_CURRENT_USER\Software\Vim\VisVim + HKEY hAppKey = GetAppKey("Vim"); + if (hAppKey) + { + HKEY hSectionKey = GetSectionKey(hAppKey, "VisVim"); + if (hSectionKey) + { + WriteRegistryInt(hSectionKey, "DevStudioEditor", + g_bDevStudioEditor); + WriteRegistryInt(hSectionKey, "NewTabs", + g_bNewTabs); + WriteRegistryInt(hSectionKey, "ChangeDir", g_ChangeDir); + RegCloseKey(hSectionKey); + } + RegCloseKey(hAppKey); + } + } + + VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE)); + return S_OK; +} + +STDMETHODIMP CCommands::VisVimEnable() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + VimSetEnableState(true); + return S_OK; +} + +STDMETHODIMP CCommands::VisVimDisable() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + VimSetEnableState(false); + return S_OK; +} + +STDMETHODIMP CCommands::VisVimToggle() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + VimSetEnableState(! g_bEnableVim); + return S_OK; +} + +STDMETHODIMP CCommands::VisVimLoad() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + // Use m_pApplication to access the Developer Studio Application object, + // and VERIFY_OK to see error strings in DEBUG builds of your add-in + // (see stdafx.h) + + CComBSTR bStr; + // Define dispatch pointers for document and selection objects + CComPtr < IDispatch > pDispDoc, pDispSel; + + // Get a document object dispatch pointer + VERIFY_OK(m_pApplication->get_ActiveDocument(&pDispDoc)); + if (! pDispDoc) + return S_OK; + + BSTR FileName; + long LineNr = -1; + + // Get the document object + CComQIPtr < ITextDocument, &IID_ITextDocument > pDoc(pDispDoc); + + if (! pDoc) + return S_OK; + + // Get the document name + if (FAILED(pDoc->get_FullName(&FileName))) + return S_OK; + + // Get a selection object dispatch pointer + if (SUCCEEDED(pDoc->get_Selection(&pDispSel))) + { + // Get the selection object + CComQIPtr < ITextSelection, &IID_ITextSelection > pSel(pDispSel); + + if (pSel) + // Get the selection line number + pSel->get_CurrentLine(&LineNr); + } + + // Open the file in Vim + VimOpenFile(FileName, LineNr); + + SysFreeString(FileName); + return S_OK; +} + + +// +// Here we do the actual processing and communication with Vim +// + +// Set the enable state and save to registry +// +static void VimSetEnableState(BOOL bEnableState) +{ + g_bEnableVim = bEnableState; + HKEY hAppKey = GetAppKey("Vim"); + if (hAppKey) + { + HKEY hSectionKey = GetSectionKey(hAppKey, "VisVim"); + if (hSectionKey) + WriteRegistryInt(hSectionKey, "EnableVim", g_bEnableVim); + RegCloseKey(hAppKey); + } +} + +// Open the file 'FileName' in Vim and goto line 'LineNr' +// 'FileName' is expected to contain an absolute DOS path including the drive +// letter. +// 'LineNr' must contain a valid line number or 0, e. g. for a new file +// +static BOOL VimOpenFile(BSTR& FileName, long LineNr) +{ + + // OLE automation object for com. with Vim + // When the object goes out of scope, it's destructor destroys the OLE + // connection; + // This is important to avoid blocking the object + // (in this memory corruption would be likely when terminating Vim + // while still running DevStudio). + // So keep this object local! + COleAutomationControl VimOle; + + // :cd D:/Src2/VisVim/ + // + // Get a dispatch id for the SendKeys method of Vim; + // enables connection to Vim if necessary + DISPID DispatchId; + DispatchId = VimGetDispatchId(VimOle, "SendKeys"); + if (! DispatchId) + // OLE error, can't obtain dispatch id + goto OleError; + + OLECHAR Buf[MAX_OLE_STR]; + char FileNameTmp[MAX_OLE_STR]; + char VimCmd[MAX_OLE_STR]; + char *s, *p; + + // Prepend CTRL-\ CTRL-N to exit insert mode + VimCmd[0] = 0x1c; + VimCmd[1] = 0x0e; + VimCmd[2] = 0; + +#ifdef SINGLE_WINDOW + // Update the current file in Vim if it has been modified. + // Disabled, because it could write the file when you don't want to. + sprintf(VimCmd + 2, ":up\n"); +#endif + if (! VimOle.Method(DispatchId, "s", TO_OLE_STR_BUF(VimCmd, Buf))) + goto OleError; + + // Change Vim working directory to where the file is if desired + if (g_ChangeDir != CD_NONE) + VimChangeDir(VimOle, DispatchId, FileName); + + // Make Vim open the file. + // In the filename convert all \ to /, put a \ before a space. + if (g_bNewTabs) + { + sprintf(VimCmd, ":tab drop "); + s = VimCmd + 10; + } + else + { + sprintf(VimCmd, ":drop "); + s = VimCmd + 6; + } + sprintf(FileNameTmp, "%S", (char *)FileName); + for (p = FileNameTmp; *p != '\0' && s < VimCmd + MAX_OLE_STR - 4; ++p) + if (*p == '\\') + *s++ = '/'; + else + { + if (*p == ' ') + *s++ = '\\'; + *s++ = *p; + } + *s++ = '\n'; + *s = '\0'; + + if (! VimOle.Method(DispatchId, "s", TO_OLE_STR_BUF(VimCmd, Buf))) + goto OleError; + + if (LineNr > 0) + { + // Goto line + sprintf(VimCmd, ":%ld\n", LineNr); + if (! VimOle.Method(DispatchId, "s", TO_OLE_STR_BUF(VimCmd, Buf))) + goto OleError; + } + + // Make Vim come to the foreground + if (! VimOle.Method("SetForeground")) + VimOle.ErrDiag(); + + // We're done + return true; + + OleError: + // There was an OLE error + // Check if it's the "unknown class string" error + VimErrDiag(VimOle); + return false; +} + +// Return the dispatch id for the Vim method 'Method' +// Create the Vim OLE object if necessary +// Returns a valid dispatch id or null on error +// +static DISPID VimGetDispatchId(COleAutomationControl& VimOle, char* Method) +{ + // Initialize Vim OLE connection if not already done + if (! VimOle.IsCreated()) + { + if (! VimOle.CreateObject("Vim.Application")) + return NULL; + } + + // Get the dispatch id for the SendKeys method. + // By doing this, we are checking if Vim is still there... + DISPID DispatchId = VimOle.GetDispatchId("SendKeys"); + if (! DispatchId) + { + // We can't get a dispatch id. + // This means that probably Vim has been terminated. + // Don't issue an error message here, instead + // destroy the OLE object and try to connect once more + // + // In fact, this should never happen, because the OLE aut. object + // should not be kept long enough to allow the user to terminate Vim + // to avoid memory corruption (why the heck is there no system garbage + // collection for those damned OLE memory chunks???). + VimOle.DeleteObject(); + if (! VimOle.CreateObject("Vim.Application")) + // If this create fails, it's time for an error msg + return NULL; + + if (! (DispatchId = VimOle.GetDispatchId("SendKeys"))) + // There is something wrong... + return NULL; + } + + return DispatchId; +} + +// Output an error message for an OLE error +// Check on the classstring error, which probably means Vim wasn't registered. +// +static void VimErrDiag(COleAutomationControl& VimOle) +{ + SCODE sc = GetScode(VimOle.GetResult()); + if (sc == CO_E_CLASSSTRING) + { + char Buf[256]; + sprintf(Buf, "There is no registered OLE automation server named " + "\"Vim.Application\".\n" + "Use the OLE-enabled version of Vim with VisVim and " + "make sure to register Vim by running \"vim -register\"."); + MessageBox(NULL, Buf, "OLE Error", MB_OK); + } + else + VimOle.ErrDiag(); +} + +// Change directory to the directory the file 'FileName' is in or it's parent +// directory according to the setting of the global 'g_ChangeDir': +// 'FileName' is expected to contain an absolute DOS path including the drive +// letter. +// CD_NONE +// CD_SOURCE_PATH +// CD_SOURCE_PARENT +// +static void VimChangeDir(COleAutomationControl& VimOle, DISPID DispatchId, BSTR& FileName) +{ + // Do a :cd first + + // Get the path name of the file ("dir/") + CString StrFileName = FileName; + char Drive[_MAX_DRIVE]; + char Dir[_MAX_DIR]; + char DirUnix[_MAX_DIR * 2]; + char *s, *t; + + _splitpath(StrFileName, Drive, Dir, NULL, NULL); + + // Convert to Unix path name format, escape spaces. + t = DirUnix; + for (s = Dir; *s; ++s) + if (*s == '\\') + *t++ = '/'; + else + { + if (*s == ' ') + *t++ = '\\'; + *t++ = *s; + } + *t = '\0'; + + + // Construct the cd command; append /.. if cd to parent + // directory and not in root directory + OLECHAR Buf[MAX_OLE_STR]; + char VimCmd[MAX_OLE_STR]; + + sprintf(VimCmd, ":cd %s%s%s\n", Drive, DirUnix, + g_ChangeDir == CD_SOURCE_PARENT && DirUnix[1] ? ".." : ""); + VimOle.Method(DispatchId, "s", TO_OLE_STR_BUF(VimCmd, Buf)); +} + +#ifdef _DEBUG +// Print out a debug message +// +static void DebugMsg(char* Msg, char* Arg) +{ + char Buf[400]; + sprintf(Buf, Msg, Arg); + AfxMessageBox(Buf); +} +#endif diff --git a/src/VisVim/Commands.h b/src/VisVim/Commands.h new file mode 100644 index 0000000..e47c81a --- /dev/null +++ b/src/VisVim/Commands.h @@ -0,0 +1,127 @@ +// Commands.h : header file +// + +#if !defined(AFX_COMMANDS_H__AC726717_2977_11D1_B2F3_006008040780__INCLUDED_) +#define AFX_COMMANDS_H__AC726717_2977_11D1_B2F3_006008040780__INCLUDED_ + +#include "vsvtypes.h" + +class CCommands : + public CComDualImpl < ICommands, + &IID_ICommands, + &LIBID_VisVim >, + public CComObjectRoot, + public CComCoClass < CCommands, + &CLSID_Commands > +{ + protected: + IApplication * m_pApplication; + + public: + CCommands (); + ~CCommands (); + void SetApplicationObject (IApplication * m_pApplication); + IApplication *GetApplicationObject () + { + return m_pApplication; + } + void UnadviseFromEvents (); + + BEGIN_COM_MAP (CCommands) + COM_INTERFACE_ENTRY (IDispatch) + COM_INTERFACE_ENTRY (ICommands) + END_COM_MAP () + DECLARE_NOT_AGGREGATABLE (CCommands) + + protected: + // This class template is used as the base class for the Application + // event handler object and the Debugger event handler object, + // which are declared below. + template < class IEvents, + const IID * piidEvents, + const GUID * plibid, + class XEvents, + const CLSID * pClsidEvents > + class XEventHandler : + public CComDualImpl < IEvents, + piidEvents, + plibid >, + public CComObjectRoot, + public CComCoClass < XEvents, + pClsidEvents > + { + public: + BEGIN_COM_MAP (XEvents) + COM_INTERFACE_ENTRY (IDispatch) + COM_INTERFACE_ENTRY_IID (*piidEvents, IEvents) + END_COM_MAP () + DECLARE_NOT_AGGREGATABLE (XEvents) + void Connect (IUnknown * pUnk) + { + VERIFY (SUCCEEDED (AtlAdvise (pUnk, this, *piidEvents, + &m_dwAdvise))); + } + void Disconnect (IUnknown * pUnk) + { + AtlUnadvise (pUnk, *piidEvents, m_dwAdvise); + } + + CCommands *m_pCommands; + + protected: + DWORD m_dwAdvise; + }; + + // This object handles events fired by the Application object + class XApplicationEvents : public XEventHandler < IApplicationEvents, + &IID_IApplicationEvents, + &LIBID_VisVim, + XApplicationEvents, + &CLSID_ApplicationEvents > + { + public: + // IApplicationEvents methods + STDMETHOD (BeforeBuildStart) (THIS); + STDMETHOD (BuildFinish) (THIS_ long nNumErrors, long nNumWarnings); + STDMETHOD (BeforeApplicationShutDown) (THIS); + STDMETHOD (DocumentOpen) (THIS_ IDispatch * theDocument); + STDMETHOD (BeforeDocumentClose) (THIS_ IDispatch * theDocument); + STDMETHOD (DocumentSave) (THIS_ IDispatch * theDocument); + STDMETHOD (NewDocument) (THIS_ IDispatch * theDocument); + STDMETHOD (WindowActivate) (THIS_ IDispatch * theWindow); + STDMETHOD (WindowDeactivate) (THIS_ IDispatch * theWindow); + STDMETHOD (WorkspaceOpen) (THIS); + STDMETHOD (WorkspaceClose) (THIS); + STDMETHOD (NewWorkspace) (THIS); + }; + typedef CComObject < XApplicationEvents > XApplicationEventsObj; + XApplicationEventsObj *m_pApplicationEventsObj; + + // This object handles events fired by the Application object + class XDebuggerEvents : public XEventHandler < IDebuggerEvents, + &IID_IDebuggerEvents, + &LIBID_VisVim, + XDebuggerEvents, + &CLSID_DebuggerEvents > + { + public: + // IDebuggerEvents method + STDMETHOD (BreakpointHit) (THIS_ IDispatch * pBreakpoint); + }; + typedef CComObject < XDebuggerEvents > XDebuggerEventsObj; + XDebuggerEventsObj *m_pDebuggerEventsObj; + + public: + // ICommands methods + STDMETHOD (VisVimDialog) (THIS); + STDMETHOD (VisVimEnable) (THIS); + STDMETHOD (VisVimDisable) (THIS); + STDMETHOD (VisVimToggle) (THIS); + STDMETHOD (VisVimLoad) (THIS); +}; + +typedef CComObject < CCommands > CCommandsObj; + +//{{AFX_INSERT_LOCATION}} + +#endif // !defined(AFX_COMMANDS_H__AC726717_2977_11D1_B2F3_006008040780__INCLUDED) diff --git a/src/VisVim/DSAddIn.cpp b/src/VisVim/DSAddIn.cpp new file mode 100644 index 0000000..b16361b --- /dev/null +++ b/src/VisVim/DSAddIn.cpp @@ -0,0 +1,160 @@ +#include "stdafx.h" +#include "VisVim.h" +#include "DSAddIn.h" +#include "Commands.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; + +#endif + +// This is called when the user first loads the add-in, and on start-up +// of each subsequent Developer Studio session +STDMETHODIMP CDSAddIn::OnConnection (IApplication * pApp, VARIANT_BOOL bFirstTime, + long dwCookie, VARIANT_BOOL * OnConnection) +{ + AFX_MANAGE_STATE (AfxGetStaticModuleState ()); + *OnConnection = VARIANT_FALSE; + + // Store info passed to us + IApplication *pApplication = NULL; + HRESULT hr; + + hr = pApp->QueryInterface (IID_IApplication, (void **) &pApplication); + if (FAILED (hr)) + { + ReportLastError (hr); + return E_UNEXPECTED; + } + if (pApplication == NULL) + { + ReportInternalError ("IApplication::QueryInterface"); + return E_UNEXPECTED; + } + + m_dwCookie = dwCookie; + + // Create command dispatch, send info back to DevStudio + CCommandsObj::CreateInstance (&m_pCommands); + if (! m_pCommands) + { + ReportInternalError ("CCommandsObj::CreateInstance"); + return E_UNEXPECTED; + } + m_pCommands->AddRef (); + + // The QueryInterface above AddRef'd the Application object. It will + // be Release'd in CCommand's destructor. + m_pCommands->SetApplicationObject (pApplication); + + hr = pApplication->SetAddInInfo ((long) AfxGetInstanceHandle (), + (LPDISPATCH) m_pCommands, IDR_TOOLBAR_MEDIUM, IDR_TOOLBAR_LARGE, + m_dwCookie); + if (FAILED (hr)) + { + ReportLastError (hr); + return E_UNEXPECTED; + } + + // Inform DevStudio of the commands we implement + if (! AddCommand (pApplication, "VisVimDialog", "VisVimDialogCmd", + IDS_CMD_DIALOG, 0, bFirstTime)) + return E_UNEXPECTED; + if (! AddCommand (pApplication, "VisVimEnable", "VisVimEnableCmd", + IDS_CMD_ENABLE, 1, bFirstTime)) + return E_UNEXPECTED; + if (! AddCommand (pApplication, "VisVimDisable", "VisVimDisableCmd", + IDS_CMD_DISABLE, 2, bFirstTime)) + return E_UNEXPECTED; + if (! AddCommand (pApplication, "VisVimToggle", "VisVimToggleCmd", + IDS_CMD_TOGGLE, 3, bFirstTime)) + return E_UNEXPECTED; + if (! AddCommand (pApplication, "VisVimLoad", "VisVimLoadCmd", + IDS_CMD_LOAD, 4, bFirstTime)) + return E_UNEXPECTED; + + *OnConnection = VARIANT_TRUE; + return S_OK; +} + +// This is called on shut-down, and also when the user unloads the add-in +STDMETHODIMP CDSAddIn::OnDisconnection (VARIANT_BOOL bLastTime) +{ + AFX_MANAGE_STATE (AfxGetStaticModuleState ()); + + m_pCommands->UnadviseFromEvents (); + m_pCommands->Release (); + m_pCommands = NULL; + + return S_OK; +} + +// Add a command to DevStudio +// Creates a toolbar button for the command also. +// 'MethodName' is the name of the method specified in the .odl file +// 'StrResId' the resource id of the descriptive string +// 'GlyphIndex' the image index into the command buttons bitmap +// Return true on success +// +bool CDSAddIn::AddCommand (IApplication* pApp, char* MethodName, char* CmdName, + UINT StrResId, UINT GlyphIndex, VARIANT_BOOL bFirstTime) +{ + CString CmdString; + CString CmdText; + + CmdText.LoadString (StrResId); + CmdString = CmdName; + CmdString += CmdText; + + CComBSTR bszCmdString (CmdString); + CComBSTR bszMethod (MethodName); + CComBSTR bszCmdName (CmdName); + + // (see stdafx.h for the definition of VERIFY_OK) + + VARIANT_BOOL bRet; + VERIFY_OK (pApp->AddCommand (bszCmdString, bszMethod, GlyphIndex, + m_dwCookie, &bRet)); + if (bRet == VARIANT_FALSE) + { + // AddCommand failed because a command with this name already exists. + ReportInternalError ("IApplication::AddCommand"); + return FALSE; + } + + // Add toolbar buttons only if this is the first time the add-in + // is being loaded. Toolbar buttons are automatically remembered + // by Developer Studio from session to session, so we should only + // add the toolbar buttons once. + if (bFirstTime == VARIANT_TRUE) + VERIFY_OK (pApp->AddCommandBarButton (dsGlyph, bszCmdName, m_dwCookie)); + + return TRUE; +} + +void ReportLastError (HRESULT Err) +{ + char *Buf = NULL; + char Msg[512]; + + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, Err, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + Buf, 400, NULL); + sprintf (Msg, "Unexpected error (Error code: %lx)\n%s", Err, Buf); + + ::MessageBox (NULL, Msg, "VisVim", MB_OK | MB_ICONSTOP); + if (Buf) + LocalFree (Buf); +} + +void ReportInternalError (char* Fct) +{ + char Msg[512]; + + sprintf (Msg, "Unexpected error\n%s failed", Fct); + ::MessageBox (NULL, Msg, "VisVim", MB_OK | MB_ICONSTOP); +} + diff --git a/src/VisVim/DSAddIn.h b/src/VisVim/DSAddIn.h new file mode 100644 index 0000000..7282872 --- /dev/null +++ b/src/VisVim/DSAddIn.h @@ -0,0 +1,53 @@ +// DSAddIn.h : header file +// + +#if !defined(AFX_DSADDIN_H__AC726715_2977_11D1_B2F3_006008040780__INCLUDED_) +#define AFX_DSADDIN_H__AC726715_2977_11D1_B2F3_006008040780__INCLUDED_ + +#include "commands.h" + +// {4F9E01C0-406B-11d2-8006-00001C405077} +DEFINE_GUID (CLSID_DSAddIn, + 0x4f9e01c0, 0x406b, 0x11d2, 0x80, 0x6, 0x0, 0x0, 0x1c, 0x40, 0x50, 0x77); + +///////////////////////////////////////////////////////////////////////////// +// CDSAddIn + +class CDSAddIn : + public IDSAddIn, + public CComObjectRoot, + public CComCoClass < CDSAddIn, + &CLSID_DSAddIn > +{ + public: + DECLARE_REGISTRY (CDSAddIn, "VisVim.DSAddIn.1", + "VisVim Developer Studio Add-in", IDS_VISVIM_LONGNAME, + THREADFLAGS_BOTH) + + CDSAddIn () + { + } + + BEGIN_COM_MAP (CDSAddIn) + COM_INTERFACE_ENTRY (IDSAddIn) + END_COM_MAP () + DECLARE_NOT_AGGREGATABLE (CDSAddIn) + + // IDSAddIns + public: + STDMETHOD (OnConnection) (THIS_ IApplication * pApp, VARIANT_BOOL bFirstTime, + long dwCookie, VARIANT_BOOL * OnConnection); + STDMETHOD (OnDisconnection) (THIS_ VARIANT_BOOL bLastTime); + + protected: + bool AddCommand (IApplication* pApp, char* MethodName, char* CmdName, + UINT StrResId, UINT GlyphIndex, VARIANT_BOOL bFirstTime); + + protected: + CCommandsObj * m_pCommands; + DWORD m_dwCookie; +}; + +//{{AFX_INSERT_LOCATION}} + +#endif // !defined(AFX_DSADDIN_H__AC726715_2977_11D1_B2F3_006008040780__INCLUDED) diff --git a/src/VisVim/OleAut.cpp b/src/VisVim/OleAut.cpp new file mode 100644 index 0000000..6902df9 --- /dev/null +++ b/src/VisVim/OleAut.cpp @@ -0,0 +1,781 @@ +// +// Class for creating OLE automation controllers. +// +// CreateObject() creates an automation object +// Invoke() will call a property or method of the automation object. +// GetProperty() returns a property +// SetProperty() changes a property +// Method() invokes a method +// +// For example, the following VB code will control Microsoft Word: +// +// Private Sub Form_Load() +// Dim wb As Object +// Set wb = CreateObject("Word.Basic") +// wb.AppShow +// wb.FileNewDefault +// wb.Insert "This is a test" +// wb.FileSaveAs "c:\sample.doc)" +// End Sub +// +// A C++ automation controller that does the same can be written as follows: +// the helper functions: +// +// Void FormLoad () +// { +// COleAutomationControl Aut; +// Aut.CreateObject("Word.Basic"); +// Aut.Method ("AppShow"); +// Aut.Method ("FileNewDefault"); +// Aut.Method ("Insert", "s", (LPOLESTR) OLESTR ("This is a test")); +// Aut.Method ("FileSaveAs", "s", OLESTR ("c:\\sample.doc")); +// } +// +// + +#include "stdafx.h" +#include +#include "oleaut.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + + +static bool CountArgsInFormat (LPCTSTR Format, UINT* nArgs); +static LPCTSTR GetNextVarType (LPCTSTR Format, VARTYPE* pVarType); + + +COleAutomationControl::COleAutomationControl () +{ + m_pDispatch = NULL; + m_hResult = NOERROR; + m_nErrArg = 0; + VariantInit (&m_VariantResult); +} + +COleAutomationControl::~COleAutomationControl () +{ + DeleteObject (); +} + +void COleAutomationControl::DeleteObject () +{ + if (m_pDispatch) + { + m_pDispatch->Release (); + m_pDispatch = NULL; + } +} + +// Creates an instance of the Automation object and +// obtains it's IDispatch interface. +// +// Parameters: +// ProgId ProgID of Automation object +// +bool COleAutomationControl::CreateObject (char* ProgId) +{ + CLSID ClsId; // CLSID of automation object + LPUNKNOWN pUnknown = NULL; // IUnknown of automation object + + // Retrieve CLSID from the progID that the user specified + LPOLESTR OleProgId = TO_OLE_STR (ProgId); + m_hResult = CLSIDFromProgID (OleProgId, &ClsId); + if (FAILED (m_hResult)) + goto error; + + // Create an instance of the automation object and ask for the + // IDispatch interface + m_hResult = CoCreateInstance (ClsId, NULL, CLSCTX_SERVER, + IID_IUnknown, (void**) &pUnknown); + if (FAILED (m_hResult)) + goto error; + + m_hResult = pUnknown->QueryInterface (IID_IDispatch, (void**) &m_pDispatch); + if (FAILED (m_hResult)) + goto error; + + pUnknown->Release (); + return true; + +error: + if (pUnknown) + pUnknown->Release (); + if (m_pDispatch) + m_pDispatch->Release (); + return false; +} + +// Return the dispatch id of a named service +// This id can be used in subsequent calls to GetProperty (), SetProperty () and +// Method (). This is the preferred method when performance is important. +// +DISPID COleAutomationControl::GetDispatchId (char* Name) +{ + DISPID DispatchId; + + ASSERT (m_pDispatch); + + // Get DISPID of property/method + LPOLESTR OleName = TO_OLE_STR (Name); + m_hResult = m_pDispatch->GetIDsOfNames (IID_NULL, &OleName, 1, + LOCALE_USER_DEFAULT, &DispatchId); + if (FAILED (m_hResult)) + return NULL; + return DispatchId; +} + +// The following functions use these parameters: +// +// Parameters: +// +// Name Name of property or method. +// +// Format Format string that describes the variable list of parameters that +// follows. The format string can contain the following characters. +// & = mark the following format character as VT_BYREF +// B = VT_BOOL +// i = VT_I2 +// I = VT_I4 +// r = VT_R2 +// R = VT_R4 +// c = VT_CY +// s = VT_BSTR (string pointer can be passed, +// BSTR will be allocated by this function). +// e = VT_ERROR +// d = VT_DATE +// v = VT_VARIANT. Use this to pass data types that are not described +// in the format string. (For example SafeArrays). +// D = VT_DISPATCH +// U = VT_UNKNOWN +// +// ... Arguments of the property or method. +// Arguments are described by Format. +// + +bool COleAutomationControl::GetProperty (char* Name) +{ + return Invoke (DISPATCH_PROPERTYGET, Name, NULL, NULL); +} + +bool COleAutomationControl::GetProperty (DISPID DispatchId) +{ + return Invoke (DISPATCH_PROPERTYGET, DispatchId, NULL, NULL); +} + +bool COleAutomationControl::PutProperty (char* Name, LPCTSTR Format, ...) +{ + va_list ArgList; + + va_start (ArgList, Format); + bool bRet = Invoke (DISPATCH_PROPERTYPUT, Name, Format, ArgList); + va_end (ArgList); + return bRet; +} + +bool COleAutomationControl::PutProperty (DISPID DispatchId, LPCTSTR Format, ...) +{ + va_list ArgList; + + va_start (ArgList, Format); + bool bRet = Invoke (DISPATCH_PROPERTYPUT, DispatchId, Format, ArgList); + va_end (ArgList); + return bRet; +} + +bool COleAutomationControl::Method (char* Name, LPCTSTR Format, ...) +{ + va_list ArgList; + + va_start (ArgList, Format); + bool bRet = Invoke (DISPATCH_METHOD, Name, Format, ArgList); + va_end (ArgList); + return bRet; +} + +bool COleAutomationControl::Method (DISPID DispatchId, LPCTSTR Format, ...) +{ + va_list ArgList; + + va_start (ArgList, Format); + bool bRet = Invoke (DISPATCH_METHOD, DispatchId, Format, ArgList); + va_end (ArgList); + return bRet; +} + +bool COleAutomationControl::Invoke (WORD Flags, char* Name, + LPCTSTR Format, va_list ArgList) +{ + DISPID DispatchId = GetDispatchId (Name); + if (! DispatchId) + return false; + return Invoke (Flags, DispatchId, Format, ArgList); +} + +bool COleAutomationControl::Invoke (WORD Flags, DISPID DispatchId, + LPCTSTR Format, va_list ArgList) +{ + UINT ArgCount = 0; + VARIANTARG* ArgVector = NULL; + + ASSERT (m_pDispatch); + + DISPPARAMS DispatchParams; + memset (&DispatchParams, 0, sizeof (DispatchParams)); + + // Determine number of arguments + if (Format) + CountArgsInFormat (Format, &ArgCount); + + // Property puts have a named argument that represents the value that + // the property is being assigned. + DISPID DispIdNamed = DISPID_PROPERTYPUT; + if (Flags & DISPATCH_PROPERTYPUT) + { + if (ArgCount == 0) + { + m_hResult = ResultFromScode (E_INVALIDARG); + return false; + } + DispatchParams.cNamedArgs = 1; + DispatchParams.rgdispidNamedArgs = &DispIdNamed; + } + + if (ArgCount) + { + // Allocate memory for all VARIANTARG parameters + ArgVector = (VARIANTARG*) CoTaskMemAlloc ( + ArgCount * sizeof (VARIANTARG)); + if (! ArgVector) + { + m_hResult = ResultFromScode (E_OUTOFMEMORY); + return false; + } + memset (ArgVector, 0, sizeof (VARIANTARG) * ArgCount); + + // Get ready to walk vararg list + LPCTSTR s = Format; + + VARIANTARG *p = ArgVector + ArgCount - 1; // Params go in opposite order + + for (;;) + { + VariantInit (p); + if (! (s = GetNextVarType (s, &p->vt))) + break; + + if (p < ArgVector) + { + m_hResult = ResultFromScode (E_INVALIDARG); + goto Cleanup; + } + switch (p->vt) + { + case VT_I2: + V_I2 (p) = va_arg (ArgList, short); + break; + case VT_I4: + V_I4 (p) = va_arg (ArgList, long); + break; + case VT_R4: + V_R4 (p) = va_arg (ArgList, float); + break; + case VT_DATE: + case VT_R8: + V_R8 (p) = va_arg (ArgList, double); + break; + case VT_CY: + V_CY (p) = va_arg (ArgList, CY); + break; + case VT_BSTR: + V_BSTR (p) = SysAllocString (va_arg (ArgList, + OLECHAR*)); + if (! p->bstrVal) + { + m_hResult = ResultFromScode (E_OUTOFMEMORY); + p->vt = VT_EMPTY; + goto Cleanup; + } + break; + case VT_DISPATCH: + V_DISPATCH (p) = va_arg (ArgList, LPDISPATCH); + break; + case VT_ERROR: + V_ERROR (p) = va_arg (ArgList, SCODE); + break; + case VT_BOOL: + V_BOOL (p) = va_arg (ArgList, BOOL) ? -1 : 0; + break; + case VT_VARIANT: + *p = va_arg (ArgList, VARIANTARG); + break; + case VT_UNKNOWN: + V_UNKNOWN (p) = va_arg (ArgList, LPUNKNOWN); + break; + + case VT_I2 | VT_BYREF: + V_I2REF (p) = va_arg (ArgList, short*); + break; + case VT_I4 | VT_BYREF: + V_I4REF (p) = va_arg (ArgList, long*); + break; + case VT_R4 | VT_BYREF: + V_R4REF (p) = va_arg (ArgList, float*); + break; + case VT_R8 | VT_BYREF: + V_R8REF (p) = va_arg (ArgList, double*); + break; + case VT_DATE | VT_BYREF: + V_DATEREF (p) = va_arg (ArgList, DATE*); + break; + case VT_CY | VT_BYREF: + V_CYREF (p) = va_arg (ArgList, CY*); + break; + case VT_BSTR | VT_BYREF: + V_BSTRREF (p) = va_arg (ArgList, BSTR*); + break; + case VT_DISPATCH | VT_BYREF: + V_DISPATCHREF (p) = va_arg (ArgList, LPDISPATCH*); + break; + case VT_ERROR | VT_BYREF: + V_ERRORREF (p) = va_arg (ArgList, SCODE*); + break; + case VT_BOOL | VT_BYREF: + { + BOOL* pBool = va_arg (ArgList, BOOL*); + + *pBool = 0; + V_BOOLREF (p) = (VARIANT_BOOL*) pBool; + } + break; + case VT_VARIANT | VT_BYREF: + V_VARIANTREF (p) = va_arg (ArgList, VARIANTARG*); + break; + case VT_UNKNOWN | VT_BYREF: + V_UNKNOWNREF (p) = va_arg (ArgList, LPUNKNOWN*); + break; + + default: + { + m_hResult = ResultFromScode (E_INVALIDARG); + goto Cleanup; + } + break; + } + + --p; // Get ready to fill next argument + } + } + + DispatchParams.cArgs = ArgCount; + DispatchParams.rgvarg = ArgVector; + + // Initialize return variant, in case caller forgot. Caller can pass + // NULL if return value is not expected. + VariantInit (&m_VariantResult); + + // Make the call + m_hResult = m_pDispatch->Invoke (DispatchId, IID_NULL, LOCALE_USER_DEFAULT, + Flags, &DispatchParams, &m_VariantResult, + &m_ExceptionInfo, &m_nErrArg); + + Cleanup: + // Cleanup any arguments that need cleanup + if (ArgCount) + { + VARIANTARG* p = ArgVector; + + while (ArgCount--) + { + switch (p->vt) + { + case VT_BSTR: + VariantClear (p); + break; + } + ++p; + } + CoTaskMemFree (ArgVector); + } + + return FAILED (m_hResult) ? false : true; +} + +#define CASE_SCODE(sc) \ + case sc: \ + lstrcpy((char*)ErrName, (char*)#sc); \ + break; + +void COleAutomationControl::ErrDiag () +{ + char ErrName[200]; + + SCODE sc = GetScode (m_hResult); + switch (sc) + { + // SCODE's defined in SCODE.H + CASE_SCODE (S_OK) + CASE_SCODE (S_FALSE) + CASE_SCODE (E_UNEXPECTED) + CASE_SCODE (E_OUTOFMEMORY) + CASE_SCODE (E_INVALIDARG) + CASE_SCODE (E_NOINTERFACE) + CASE_SCODE (E_POINTER) + CASE_SCODE (E_HANDLE) + CASE_SCODE (E_ABORT) + CASE_SCODE (E_FAIL) + CASE_SCODE (E_ACCESSDENIED) + + // SCODE's defined in OLE2.H + CASE_SCODE (OLE_E_OLEVERB) + CASE_SCODE (OLE_E_ADVF) + CASE_SCODE (OLE_E_ENUM_NOMORE) + CASE_SCODE (OLE_E_ADVISENOTSUPPORTED) + CASE_SCODE (OLE_E_NOCONNECTION) + CASE_SCODE (OLE_E_NOTRUNNING) + CASE_SCODE (OLE_E_NOCACHE) + CASE_SCODE (OLE_E_BLANK) + CASE_SCODE (OLE_E_CLASSDIFF) + CASE_SCODE (OLE_E_CANT_GETMONIKER) + CASE_SCODE (OLE_E_CANT_BINDTOSOURCE) + CASE_SCODE (OLE_E_STATIC) + CASE_SCODE (OLE_E_PROMPTSAVECANCELLED) + CASE_SCODE (OLE_E_INVALIDRECT) + CASE_SCODE (OLE_E_WRONGCOMPOBJ) + CASE_SCODE (OLE_E_INVALIDHWND) + CASE_SCODE (OLE_E_NOT_INPLACEACTIVE) + CASE_SCODE (OLE_E_CANTCONVERT) + CASE_SCODE (OLE_E_NOSTORAGE) + + CASE_SCODE (DV_E_FORMATETC) + CASE_SCODE (DV_E_DVTARGETDEVICE) + CASE_SCODE (DV_E_STGMEDIUM) + CASE_SCODE (DV_E_STATDATA) + CASE_SCODE (DV_E_LINDEX) + CASE_SCODE (DV_E_TYMED) + CASE_SCODE (DV_E_CLIPFORMAT) + CASE_SCODE (DV_E_DVASPECT) + CASE_SCODE (DV_E_DVTARGETDEVICE_SIZE) + CASE_SCODE (DV_E_NOIVIEWOBJECT) + + CASE_SCODE (OLE_S_USEREG) + CASE_SCODE (OLE_S_STATIC) + CASE_SCODE (OLE_S_MAC_CLIPFORMAT) + + CASE_SCODE (CONVERT10_E_OLESTREAM_GET) + CASE_SCODE (CONVERT10_E_OLESTREAM_PUT) + CASE_SCODE (CONVERT10_E_OLESTREAM_FMT) + CASE_SCODE (CONVERT10_E_OLESTREAM_BITMAP_TO_DIB) + CASE_SCODE (CONVERT10_E_STG_FMT) + CASE_SCODE (CONVERT10_E_STG_NO_STD_STREAM) + CASE_SCODE (CONVERT10_E_STG_DIB_TO_BITMAP) + CASE_SCODE (CONVERT10_S_NO_PRESENTATION) + + CASE_SCODE (CLIPBRD_E_CANT_OPEN) + CASE_SCODE (CLIPBRD_E_CANT_EMPTY) + CASE_SCODE (CLIPBRD_E_CANT_SET) + CASE_SCODE (CLIPBRD_E_BAD_DATA) + CASE_SCODE (CLIPBRD_E_CANT_CLOSE) + + CASE_SCODE (DRAGDROP_E_NOTREGISTERED) + CASE_SCODE (DRAGDROP_E_ALREADYREGISTERED) + CASE_SCODE (DRAGDROP_E_INVALIDHWND) + CASE_SCODE (DRAGDROP_S_DROP) + CASE_SCODE (DRAGDROP_S_CANCEL) + CASE_SCODE (DRAGDROP_S_USEDEFAULTCURSORS) + + CASE_SCODE (OLEOBJ_E_NOVERBS) + CASE_SCODE (OLEOBJ_E_INVALIDVERB) + CASE_SCODE (OLEOBJ_S_INVALIDVERB) + CASE_SCODE (OLEOBJ_S_CANNOT_DOVERB_NOW) + CASE_SCODE (OLEOBJ_S_INVALIDHWND) + CASE_SCODE (INPLACE_E_NOTUNDOABLE) + CASE_SCODE (INPLACE_E_NOTOOLSPACE) + CASE_SCODE (INPLACE_S_TRUNCATED) + + // SCODE's defined in COMPOBJ.H + CASE_SCODE (CO_E_NOTINITIALIZED) + CASE_SCODE (CO_E_ALREADYINITIALIZED) + CASE_SCODE (CO_E_CANTDETERMINECLASS) + CASE_SCODE (CO_E_CLASSSTRING) + CASE_SCODE (CO_E_IIDSTRING) + CASE_SCODE (CO_E_APPNOTFOUND) + CASE_SCODE (CO_E_APPSINGLEUSE) + CASE_SCODE (CO_E_ERRORINAPP) + CASE_SCODE (CO_E_DLLNOTFOUND) + CASE_SCODE (CO_E_ERRORINDLL) + CASE_SCODE (CO_E_WRONGOSFORAPP) + CASE_SCODE (CO_E_OBJNOTREG) + CASE_SCODE (CO_E_OBJISREG) + CASE_SCODE (CO_E_OBJNOTCONNECTED) + CASE_SCODE (CO_E_APPDIDNTREG) + CASE_SCODE (CLASS_E_NOAGGREGATION) + CASE_SCODE (CLASS_E_CLASSNOTAVAILABLE) + CASE_SCODE (REGDB_E_READREGDB) + CASE_SCODE (REGDB_E_WRITEREGDB) + CASE_SCODE (REGDB_E_KEYMISSING) + CASE_SCODE (REGDB_E_INVALIDVALUE) + CASE_SCODE (REGDB_E_CLASSNOTREG) + CASE_SCODE (REGDB_E_IIDNOTREG) + CASE_SCODE (RPC_E_CALL_REJECTED) + CASE_SCODE (RPC_E_CALL_CANCELED) + CASE_SCODE (RPC_E_CANTPOST_INSENDCALL) + CASE_SCODE (RPC_E_CANTCALLOUT_INASYNCCALL) + CASE_SCODE (RPC_E_CANTCALLOUT_INEXTERNALCALL) + CASE_SCODE (RPC_E_CONNECTION_TERMINATED) + CASE_SCODE (RPC_E_SERVER_DIED) + CASE_SCODE (RPC_E_CLIENT_DIED) + CASE_SCODE (RPC_E_INVALID_DATAPACKET) + CASE_SCODE (RPC_E_CANTTRANSMIT_CALL) + CASE_SCODE (RPC_E_CLIENT_CANTMARSHAL_DATA) + CASE_SCODE (RPC_E_CLIENT_CANTUNMARSHAL_DATA) + CASE_SCODE (RPC_E_SERVER_CANTMARSHAL_DATA) + CASE_SCODE (RPC_E_SERVER_CANTUNMARSHAL_DATA) + CASE_SCODE (RPC_E_INVALID_DATA) + CASE_SCODE (RPC_E_INVALID_PARAMETER) + CASE_SCODE (RPC_E_CANTCALLOUT_AGAIN) + CASE_SCODE (RPC_E_UNEXPECTED) + + // SCODE's defined in DVOBJ.H + CASE_SCODE (DATA_S_SAMEFORMATETC) + CASE_SCODE (VIEW_E_DRAW) + CASE_SCODE (VIEW_S_ALREADY_FROZEN) + CASE_SCODE (CACHE_E_NOCACHE_UPDATED) + CASE_SCODE (CACHE_S_FORMATETC_NOTSUPPORTED) + CASE_SCODE (CACHE_S_SAMECACHE) + CASE_SCODE (CACHE_S_SOMECACHES_NOTUPDATED) + + // SCODE's defined in STORAGE.H + CASE_SCODE (STG_E_INVALIDFUNCTION) + CASE_SCODE (STG_E_FILENOTFOUND) + CASE_SCODE (STG_E_PATHNOTFOUND) + CASE_SCODE (STG_E_TOOMANYOPENFILES) + CASE_SCODE (STG_E_ACCESSDENIED) + CASE_SCODE (STG_E_INVALIDHANDLE) + CASE_SCODE (STG_E_INSUFFICIENTMEMORY) + CASE_SCODE (STG_E_INVALIDPOINTER) + CASE_SCODE (STG_E_NOMOREFILES) + CASE_SCODE (STG_E_DISKISWRITEPROTECTED) + CASE_SCODE (STG_E_SEEKERROR) + CASE_SCODE (STG_E_WRITEFAULT) + CASE_SCODE (STG_E_READFAULT) + CASE_SCODE (STG_E_SHAREVIOLATION) + CASE_SCODE (STG_E_LOCKVIOLATION) + CASE_SCODE (STG_E_FILEALREADYEXISTS) + CASE_SCODE (STG_E_INVALIDPARAMETER) + CASE_SCODE (STG_E_MEDIUMFULL) + CASE_SCODE (STG_E_ABNORMALAPIEXIT) + CASE_SCODE (STG_E_INVALIDHEADER) + CASE_SCODE (STG_E_INVALIDNAME) + CASE_SCODE (STG_E_UNKNOWN) + CASE_SCODE (STG_E_UNIMPLEMENTEDFUNCTION) + CASE_SCODE (STG_E_INVALIDFLAG) + CASE_SCODE (STG_E_INUSE) + CASE_SCODE (STG_E_NOTCURRENT) + CASE_SCODE (STG_E_REVERTED) + CASE_SCODE (STG_E_CANTSAVE) + CASE_SCODE (STG_E_OLDFORMAT) + CASE_SCODE (STG_E_OLDDLL) + CASE_SCODE (STG_E_SHAREREQUIRED) + CASE_SCODE (STG_E_NOTFILEBASEDSTORAGE) + CASE_SCODE (STG_E_EXTANTMARSHALLINGS) + CASE_SCODE (STG_S_CONVERTED) + + // SCODE's defined in STORAGE.H + CASE_SCODE (MK_E_CONNECTMANUALLY) + CASE_SCODE (MK_E_EXCEEDEDDEADLINE) + CASE_SCODE (MK_E_NEEDGENERIC) + CASE_SCODE (MK_E_UNAVAILABLE) + CASE_SCODE (MK_E_SYNTAX) + CASE_SCODE (MK_E_NOOBJECT) + CASE_SCODE (MK_E_INVALIDEXTENSION) + CASE_SCODE (MK_E_INTERMEDIATEINTERFACENOTSUPPORTED) + CASE_SCODE (MK_E_NOTBINDABLE) + CASE_SCODE (MK_E_NOTBOUND) + CASE_SCODE (MK_E_CANTOPENFILE) + CASE_SCODE (MK_E_MUSTBOTHERUSER) + CASE_SCODE (MK_E_NOINVERSE) + CASE_SCODE (MK_E_NOSTORAGE) + CASE_SCODE (MK_E_NOPREFIX) + CASE_SCODE (MK_S_REDUCED_TO_SELF) + CASE_SCODE (MK_S_ME) + CASE_SCODE (MK_S_HIM) + CASE_SCODE (MK_S_US) + CASE_SCODE (MK_S_MONIKERALREADYREGISTERED) + + // SCODE's defined in DISPATCH.H + CASE_SCODE (DISP_E_UNKNOWNINTERFACE) + CASE_SCODE (DISP_E_MEMBERNOTFOUND) + CASE_SCODE (DISP_E_PARAMNOTFOUND) + CASE_SCODE (DISP_E_TYPEMISMATCH) + CASE_SCODE (DISP_E_UNKNOWNNAME) + CASE_SCODE (DISP_E_NONAMEDARGS) + CASE_SCODE (DISP_E_BADVARTYPE) + CASE_SCODE (DISP_E_EXCEPTION) + CASE_SCODE (DISP_E_OVERFLOW) + CASE_SCODE (DISP_E_BADINDEX) + CASE_SCODE (DISP_E_UNKNOWNLCID) + CASE_SCODE (DISP_E_ARRAYISLOCKED) + CASE_SCODE (DISP_E_BADPARAMCOUNT) + CASE_SCODE (DISP_E_PARAMNOTOPTIONAL) + CASE_SCODE (DISP_E_BADCALLEE) + CASE_SCODE (DISP_E_NOTACOLLECTION) + + CASE_SCODE (TYPE_E_BUFFERTOOSMALL) + CASE_SCODE (TYPE_E_INVDATAREAD) + CASE_SCODE (TYPE_E_UNSUPFORMAT) + CASE_SCODE (TYPE_E_REGISTRYACCESS) + CASE_SCODE (TYPE_E_LIBNOTREGISTERED) + CASE_SCODE (TYPE_E_UNDEFINEDTYPE) + CASE_SCODE (TYPE_E_QUALIFIEDNAMEDISALLOWED) + CASE_SCODE (TYPE_E_INVALIDSTATE) + CASE_SCODE (TYPE_E_WRONGTYPEKIND) + CASE_SCODE (TYPE_E_ELEMENTNOTFOUND) + CASE_SCODE (TYPE_E_AMBIGUOUSNAME) + CASE_SCODE (TYPE_E_NAMECONFLICT) + CASE_SCODE (TYPE_E_UNKNOWNLCID) + CASE_SCODE (TYPE_E_DLLFUNCTIONNOTFOUND) + CASE_SCODE (TYPE_E_BADMODULEKIND) + CASE_SCODE (TYPE_E_SIZETOOBIG) + CASE_SCODE (TYPE_E_DUPLICATEID) + CASE_SCODE (TYPE_E_TYPEMISMATCH) + CASE_SCODE (TYPE_E_OUTOFBOUNDS) + CASE_SCODE (TYPE_E_IOERROR) + CASE_SCODE (TYPE_E_CANTCREATETMPFILE) + CASE_SCODE (TYPE_E_CANTLOADLIBRARY) + CASE_SCODE (TYPE_E_INCONSISTENTPROPFUNCS) + CASE_SCODE (TYPE_E_CIRCULARTYPE) + + default: + lstrcpy (ErrName, "UNKNOWN SCODE"); + } + + char Buf[256]; + sprintf (Buf, "An OLE error occurred:\r\nCode = %s\r\nResult = %lx.", + (char*) ErrName, m_hResult); + MessageBox (NULL, Buf, "OLE Error", MB_OK); +} + + +static bool CountArgsInFormat (LPCTSTR Format, UINT* pArgCount) +{ + *pArgCount = 0; + + if (! Format) + return true; + + while (*Format) + { + if (*Format == '&') + Format++; + + switch (*Format) + { + case 'b': + case 'i': + case 'I': + case 'r': + case 'R': + case 'c': + case 's': + case 'e': + case 'd': + case 'v': + case 'D': + case 'U': + ++ (*pArgCount); + Format++; + break; + case '\0': + default: + return false; + } + } + return true; +} + +static LPCTSTR GetNextVarType (LPCTSTR Format, VARTYPE* pVarType) +{ + *pVarType = 0; + if (*Format == '&') + { + *pVarType = VT_BYREF; + Format++; + if (!*Format) + return NULL; + } + switch (*Format) + { + case 'b': + *pVarType |= VT_BOOL; + break; + case 'i': + *pVarType |= VT_I2; + break; + case 'I': + *pVarType |= VT_I4; + break; + case 'r': + *pVarType |= VT_R4; + break; + case 'R': + *pVarType |= VT_R8; + break; + case 'c': + *pVarType |= VT_CY; + break; + case 's': + *pVarType |= VT_BSTR; + break; + case 'e': + *pVarType |= VT_ERROR; + break; + case 'd': + *pVarType |= VT_DATE; + break; + case 'v': + *pVarType |= VT_VARIANT; + break; + case 'U': + *pVarType |= VT_UNKNOWN; + break; + case 'D': + *pVarType |= VT_DISPATCH; + break; + case '\0': + return NULL; // End of Format string + default: + return NULL; + } + return ++Format; +} + +#ifndef UNICODE +char* ConvertToAnsi (OLECHAR* sUnicode) +{ + static char BufAscii[MAX_OLE_STR]; + return ConvertToAnsiBuf (sUnicode, BufAscii); +} + +char* ConvertToAnsiBuf (OLECHAR* sUnicode, char* BufAscii) +{ + WideCharToMultiByte (CP_ACP, 0, sUnicode, -1, BufAscii, MAX_OLE_STR, NULL, NULL); + return BufAscii; +} + +OLECHAR* ConvertToUnicode (char* sAscii) +{ + static OLECHAR BufUnicode[MAX_OLE_STR]; + return ConvertToUnicodeBuf (sAscii, BufUnicode); +} + +OLECHAR* ConvertToUnicodeBuf (char* sAscii, OLECHAR* BufUnicode) +{ + MultiByteToWideChar (CP_ACP, 0, sAscii, -1, BufUnicode, MAX_OLE_STR); + return BufUnicode; +} +#endif + diff --git a/src/VisVim/OleAut.h b/src/VisVim/OleAut.h new file mode 100644 index 0000000..37de807 --- /dev/null +++ b/src/VisVim/OleAut.h @@ -0,0 +1,73 @@ +#ifndef __OLEAUT_H__ +#define __OLEAUT_H__ + +class COleAutomationControl : public CObject +{ + public: + COleAutomationControl (); + ~COleAutomationControl (); + bool CreateObject (char* ProgId); + DISPID GetDispatchId (char* Name); + bool GetProperty (char* Name); + bool GetProperty (DISPID DispatchId); + bool PutProperty (char* Name, LPCTSTR Format, ...); + bool PutProperty (DISPID DispatchId, LPCTSTR Format, ...); + bool Method (char* Name, LPCTSTR Format = NULL, ...); + bool Method (DISPID DispatchId, LPCTSTR Format = NULL, ...); + void DeleteObject (); + void ErrDiag (); + bool IsCreated () + { + return m_pDispatch ? true : false; + } + bool IsAlive (); + HRESULT GetResult () + { + return m_hResult; + } + UINT GetErrArgNr () + { + return m_nErrArg; + } + EXCEPINFO* GetExceptionInfo () + { + return &m_ExceptionInfo; + } + LPVARIANT GetResultVariant () + { + return &m_VariantResult; + } + + protected: + bool Invoke (WORD Flags, char* Name, LPCTSTR Format, va_list ArgList); + bool Invoke (WORD Flags, DISPID DispatchId, LPCTSTR Format, va_list ArgList); + + protected: + IDispatch* m_pDispatch; + HRESULT m_hResult; + UINT m_nErrArg; + EXCEPINFO m_ExceptionInfo; + VARIANTARG m_VariantResult; +}; + +#ifdef UNICODE + #define FROM_OLE_STRING(str) str + #define FROM_OLE_STRING_BUF(str,buf) str + #define TO_OLE_STR(str) str + #define TO_OLE_STR_BUF(str,buf) str + #define MAX_OLE_STR 1 +#else + #define FROM_OLE_STR(str) ConvertToAnsi(str) + #define FROM_OLE_STR_BUF(str,buf) ConvertToAnsiBuf(str,buf) + char* ConvertToAnsi (OLECHAR* sUnicode); + char* ConvertToAnsiBuf (OLECHAR* sUnicode, char* Buf); + #define TO_OLE_STR(str) ConvertToUnicode(str) + #define TO_OLE_STR_BUF(str,buf) ConvertToUnicodeBuf(str,buf) + OLECHAR* ConvertToUnicode (char* sAscii); + OLECHAR* ConvertToUnicodeBuf (char* sAscii, OLECHAR* Buf); + // Maximum length of string that can be converted between Ansi & Unicode + #define MAX_OLE_STR 500 +#endif + + +#endif // __OLEAUT_H__ diff --git a/src/VisVim/README_VisVim.txt b/src/VisVim/README_VisVim.txt new file mode 100644 index 0000000..25b0fbd --- /dev/null +++ b/src/VisVim/README_VisVim.txt @@ -0,0 +1,326 @@ +=============================== +Visual Studio - Vim Integration +=============================== + +Copyright (C) 1997 Heiko Erhardt + +VisVim is a Visual Studio Add-In that allows Vim to be integrated +as the default text editor. It will be used instead of the Visual +Studio built-in editor when you double-click on a file or press F4 +after compiling (it will go to the proper line in the Vim buffer). +The file can be loaded exclusively by Vim or additionally to the +builtin Visual Studio editor (this option can be set in the VisVim +configuration dialog inside Visual Studio). +Vim does not replace the Visual Studio editor, it still runs in its +own window. + +VisVim is based upon VisEmacs by Christopher Payne +(Copyright (C) Christopher Payne 1997). + +Author: Heiko Erhardt +Based upon: VisEmacs by Christopher Payne +Version: 1.0 +Created: 23 Oct 1997 +Date: 23 Oct 1997 + +VisVim was originally GNU GPL licensed, as stated below. On March 21 2012 +Heiko Erhardt declared this work to be relicensed under the Vim license, as +stated in ../../runtime/doc/uganda.txt (or ":help uganda" in Vim). + +VisVim is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +VisVim is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + +Requirements +------------ + +VisVim works with the *OLE-enabled* version of Vim version 5.0 and higher +only!!! You must download the extra archive containing the OLE-enabled +executable from your Vim download site. When building your own Vim +executable, use the if_ole_vc.mak makefile (Vim 5.1 and higher). +VisVim needs DevStudio 5.0 or higher. It does not work with DevStudio 4.2. + + +Installation +------------ + +1) Close running instances of DevStudio. + +2) Copy VisVim.dll into a convenient directory like \vim, + \vim\lib, or \vim\addin + +3) Register the DLL using regsvr32.exe ... (Skip this on Windows 95/98) + Example: + > cd \vim\addin + > regsvr32 VisVim.dll + On NT, you should do this from an administrator account. + Before installing a new version of VisVim you should unregister + the old one using + > regsvr32 -unregister VisVim.dll + The batch files register.bat and unregister.bat can do that for you. + +3a) If you didn't do this yet: Register the OLE gvim: + > gvim -register + +4) Start Visual Studio and go to: + Tools + Customize... + Add-Ins and Macro Files + +5) Click on Browse, and point Visual Studio to your VisVim.dll file. + +6) Click the checkbox to indicate that you want to use the Add-In, and + Close the Customize dialog box. + +7) You should notice the VisVim Toolbar with the Vim Icon. + Click the first item of the toolbar to get to the options dialog. + + +Compiling VisVim +---------------- + +Two Options: + +1) Load the VisVim.mak file as a Workspace in Visual Studio and compile + +2) Use the MSVC command line compiler: + vcvars32 + nmake -f VisVim.mak + + +Using VisVim +------------ + +The VisVim DLL exposes several functions to the user. These functions are +accessible using the toolbar or by assigning hotkeys to them (see below). +The following functions are visible on the toolbar (from left to right): + +1. VisVim settings dialog + The settings you adjust here will be saved in the registry and + will be reloaded on program startup. + +2. Enable Vim + Enables Vim as Visual Studio editor. Control will be switched to Vim when: + - Clicking a file in the file view + - Clicking a compiler error message line + - Using the 'File-Open' Dialog + - Showing the current source line when encountering a debugger breakpoint. + - Using File-New + +3. Disable Vim + The internal Visual Studio editor will be used to edit files. + +4. Toggle enable state + Toggles the enable state of VisVim. Use this function if you want to have + one button only to activate/deactivate Vim. + +5. Load current file in Vim + Loads the file shown in the internal editor into Vim. Use this function if + you want the internal editor to stay active and just edit one file in Vim. + This command works always whether Vim is enabled as default editor or not. + +You cannot use DevStudio's debugger commands from inside Vim, so you should +disable Vim before running the debugger. + +You can customize the Vim toolbar itself or add the Vim buttons to other +toolbars. +To have fast access to the VisVim options dialog I suggest to create keyboard +shortcuts: + +1) Choose + Tools + Customize... + Keyboard +2) Choose Category:AddIns and Commands:VisVim. +3) Choose 'Main' as editor, enter each hotkey and press the Assign button. + I suggest: + VisVimDialogCmd Alt+Shift+V + VisVimEnableCmd Alt+Shift+E + VisVimDisableCmd Alt+Shift+D + VisVimToggleCmd Alt+Shift+T + VisVimLoadCmd Alt+Shift+G +4) Close the dialog + +Now a typical debugging example: + +Using "Alt+Shift+d" you turn off Vim before starting the debugger. +After hitting the breakpoint you single step through your application +using the internal source code editor and examine variables. +When you stumble across the line with the null pointer +assignment, just press "Alt+Shift+g", and correct the error in Vim. +Save the file, press Alt+Tab to return to DevStudio and press F7 to compile. +That's it. + + +Troubleshooting +--------------- + +1. When opening a file in DevStudio the file is opened in the DevStudio + editor and immediately vanishes. No Vim shows up. + Cause: Probably you don't have the OLE-enabled Vim or you didn't + register it. + Explanation: VisVim is notified by DevStudio if an 'open document' event + occurs. It then closes the document in the internal editor + and tries to start Vim. If Vim isn't properly OLE-registered, + this won't work. + Workaround: Download and install the OLE-enable version of Vim and + execute "gvim -register". + +2. Sometimes when clicking on a file, the file won't be opened by Vim but + instead the Visual Studio editor comes up. + Cause: The file was already loaded by the DevStudio editor. + Explanation: VisVim works by hooks exposed by Visual Studio. + Most of the functionality works from the OpenDocument hook. + If a document is already loaded in the Visual Studio editor, + no 'open document' event will be generated when clicking the + file in the file list. + Workaround: Close the document in Visual Studio first. + +3. I can't get VisVim to work. Either the Vim toolbar does not appear at all + or weird crashes happen. + Cause: The Visual Studio installation is messed up. + Explanation: I can't give you one. Ask M$. + Workaround: Reinstall DevStudio (I know this is brute, but in some cases + it helped). There was one case where the service pack 1 had + to be installed, too. + +4. If an instance of Vim is already running, VisVim will use that instance + and not start a new one. + Cause: This is proper OLE behaviour + Explanation: Some call it a bug, some a feature. That's just the way OLE + works. + +5. When being in insert mode in Vim and selecting a file in Visual Studio, + the Vim command :e ... is inserted as text instead of being executed. + Cause: You probably know... + Explanation: The Vim OLE automation interface interprets the VisVim + commands as if they were typed in by the user. + So if you're in insert mode Vim considers it to be text. + I decided against sending an ESC before the command because + it may cause a beep or at least a screen flash when noeb is + set. + Workaround: Get used to press ESC before switching to DevStudio. + +6. I'm tired of VisVim but I can't get rid of it. I can't delete it in + Tools-Customize-Add-Ins. + Cause: You can't delete an item you once added to the add-ins + list box. + Explanation: M$ just didn't put a 'delete' button in the dialog box. + Unfortunately there is no DEL key accellerator as well... + Workaround: You can't kill it, but you can knock it out: + 1. Uncheck the check box in front of 'Vim Developer Studio + Add-in'. + 2. Close Visual Studio. + 3. Delete VisVim.dll or move it somewhere it can't be found. + 4. Run Visual Studio. + 5. Tools -> Customize ->Add-ins and Macro-Files. + 6. A message appears: + ".../VisVim.dll" "This add-in no longer exists. It will + no longer be displayed." + That's it! + + +Change history +-------------- + +1.0a to 1.0 +----------- + +- All settings in the VisVim dialog are remembered between DevStudio sessions + by keeping them in the registry (HKEY_CURRENT_USER\Software\Vim\VisVim). +- Added an option to do a :cd before opening the file (having a file opened + by clicking it but finding out to be still in C:\Windows\system when trying to + open another file by ":e" can be annoying). Change directory can be + done to the source file's directory or it's parent directory. +- Added some explanations to the error message for the CO_E_CLASSSTRING error + ("Use OLE Vim and make sure to register..."). + +1.0 to 1.1a +----------- + +- The VisVim toolbar button now shows the new Vim icon instead of the old one. +- Made some changes to the documentation, added the troubleshooting chapter + and ToDo list. +- File-New-* now invokes Vim instead of the builtin editor if enabled. + +1.1 to 1.1b +----------- + +- Extended the VisVim toolbar to have multiple buttons instead of one. +- Moved the enable/disable commands from the settings dialog to the toolbar. +- Added the toggle enable/disable command +- Added the 'load current file' command. + +1.1b to 1.2 +----------- + +No new features, just some fine tuning: + +- Changed the GUID of the VisVim OLE interface to avoid conflicts with a + version of VisEmacs or VisVile on the same computer (Guy Gascoigne) +- Fixed a bug caused by a bug in the Developer Studio add-in code generator + (Clark Morgan) +- Fixed a memory leak (Clark Morgan) +- Added an option in the VisVim dialog to prepend ESC before the first command + that is sent to Vim. This will avoid inserting the command as text when Vim + is still in insert mode. +- An :update command is sent to Vim before any other command to update the + current file if it is modified, or else the following :cd or :e command will fail. + +1.2 to 1.3a +----------- + +- Fixed a bug caused by a missing EnableModeless() function call in VimLoad(). + This seems to reduce VisVim crashing DevStudio on some systems (it + occasionally still seems to happen, but it's more stable now). + (Vince Negri) +- Added support for the new CTRL-\ CTRL-N command of Vim 5.4a. + This prevents Vim from beeping when a VisVim command is executed and Vim is + not in insert mode. + + +ToDo List +--------- + +P1 is highest priority, P10 lowest + +P9 Switching to DevStudio using ALT-TAB may get annoying. Would be nice to + have the option to map ActivateApplication("Visual Studio") in Vim. + Vim DLLs would solve that problem. + +P8 Execute :tag command in Vim for word under cursor in DevStudio + +P7 Controlling the Visual Studio Debugger from inside Vim + See message above. Also a 'Debug' highlight group and a + command to highlight a certain line would be necessary. + +P6 Provide an option to open the current file in VisVim in + Visual Studio editor + Same as above message. A kind of two way OLE automation would have to be + established between VisVim and Vim. Also a 'Debug' highlight group and a + command to highlight a certain line would be necessary. + + +Known Problems +-------------- + +- Occasional memory corruptions in DevStudio may appear on some systems. + Reinstalling DevStudio helped in some cases. + The cause of these crashes is unclear; there is no way to debug this. + Recompiling VisVim with DevStudio SP3 didn't help. + I assume it's a problem deep inside the DevStudio add-in OLE interfaces. + This will hopefully be fixed with DevStudio 6. + + +Have fun! + +Heiko Erhardt +heiko.erhardt@gmx.net + diff --git a/src/VisVim/Reg.cpp b/src/VisVim/Reg.cpp new file mode 100644 index 0000000..b4378e5 --- /dev/null +++ b/src/VisVim/Reg.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" + +// Returns key for HKEY_CURRENT_USER\"Software"\Company\AppName +// creating it if it doesn't exist +// responsibility of the caller to call RegCloseKey() on the returned HKEY +// +HKEY GetAppKey (char* AppName) +{ + HKEY hAppKey = NULL; + HKEY hSoftKey = NULL; + if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software", 0, KEY_WRITE | KEY_READ, + &hSoftKey) == ERROR_SUCCESS) + { + DWORD Dummy; + RegCreateKeyEx (hSoftKey, AppName, 0, REG_NONE, + REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, NULL, + &hAppKey, &Dummy); + } + if (hSoftKey) + RegCloseKey (hSoftKey); + + return hAppKey; +} + +// Returns key for +// HKEY_CURRENT_USER\"Software"\RegistryKey\AppName\Section +// creating it if it doesn't exist. +// responsibility of the caller to call RegCloseKey () on the returned HKEY +// +HKEY GetSectionKey (HKEY hAppKey, LPCTSTR Section) +{ + HKEY hSectionKey = NULL; + DWORD Dummy; + RegCreateKeyEx (hAppKey, Section, 0, REG_NONE, + REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, + &hSectionKey, &Dummy); + return hSectionKey; +} + +int GetRegistryInt (HKEY hSectionKey, LPCTSTR Entry, int Default) +{ + DWORD Value; + DWORD Type; + DWORD Count = sizeof (DWORD); + if (RegQueryValueEx (hSectionKey, (LPTSTR) Entry, NULL, &Type, + (LPBYTE) &Value, &Count) == ERROR_SUCCESS) + return Value; + return Default; +} + +bool WriteRegistryInt (HKEY hSectionKey, char* Entry, int nValue) +{ + return RegSetValueEx (hSectionKey, Entry, NULL, REG_DWORD, + (LPBYTE) &nValue, sizeof (nValue)) == ERROR_SUCCESS; +} + diff --git a/src/VisVim/Register.bat b/src/VisVim/Register.bat new file mode 100644 index 0000000..baef50b --- /dev/null +++ b/src/VisVim/Register.bat @@ -0,0 +1 @@ +regsvr32.exe visvim.dll diff --git a/src/VisVim/Res/ToolbarL.bmp b/src/VisVim/Res/ToolbarL.bmp new file mode 100644 index 0000000..e11c66f Binary files /dev/null and b/src/VisVim/Res/ToolbarL.bmp differ diff --git a/src/VisVim/Res/ToolbarM.bmp b/src/VisVim/Res/ToolbarM.bmp new file mode 100644 index 0000000..22e15f4 Binary files /dev/null and b/src/VisVim/Res/ToolbarM.bmp differ diff --git a/src/VisVim/Resource.h b/src/VisVim/Resource.h new file mode 100644 index 0000000..1070091 --- /dev/null +++ b/src/VisVim/Resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by VisVim.rc +// +#define IDS_VISVIM_LONGNAME 1 +#define IDS_VISVIM_DESCRIPTION 2 +#define IDS_CMD_DIALOG 3 +#define IDS_CMD_ENABLE 4 +#define IDS_CMD_DISABLE 5 +#define IDS_CMD_TOGGLE 6 +#define IDS_CMD_LOAD 7 +#define IDR_TOOLBAR_MEDIUM 128 +#define IDR_TOOLBAR_LARGE 129 +#define IDD_ADDINMAIN 130 +#define IDC_DEVSTUDIO_EDITOR 1000 +#define IDC_CD_SOURCE_PATH 1001 +#define IDC_CD_SOURCE_PARENT 1002 +#define IDC_CD_NONE 1003 +#define IDC_NEW_TABS 1004 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1004 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/VisVim/StdAfx.cpp b/src/VisVim/StdAfx.cpp new file mode 100644 index 0000000..f4f6eb5 --- /dev/null +++ b/src/VisVim/StdAfx.cpp @@ -0,0 +1,6 @@ +// Stdafx.cpp : source file that includes just the standard includes +// VisEmacs.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" +#include "atlimpl.cpp" diff --git a/src/VisVim/StdAfx.h b/src/VisVim/StdAfx.h new file mode 100644 index 0000000..10bfdc0 --- /dev/null +++ b/src/VisVim/StdAfx.h @@ -0,0 +1,73 @@ +// Stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__AC72670E_2977_11D1_B2F3_006008040780__INCLUDED_) +#define AFX_STDAFX_H__AC72670E_2977_11D1_B2F3_006008040780__INCLUDED_ + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include + +#include +//You may derive a class from CComModule and use it if you want to override +//something, but do not change the name of _Module +extern CComModule _Module; + +#include + +// Developer Studio Object Model +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////////////////////////////////////////////////////////////// +// Debugging support + +// Use VERIFY_OK around all calls to the Developer Studio objects which +// you expect to return S_OK. +// In DEBUG builds of your add-in, VERIFY_OK displays an ASSERT dialog box +// if the expression returns an HRESULT other than S_OK. If the HRESULT +// is a success code, the ASSERT box will display that HRESULT. If it +// is a failure code, the ASSERT box will display that HRESULT plus the +// error description string provided by the object which raised the error. +// In RETAIL builds of your add-in, VERIFY_OK just evaluates the expression +// and ignores the returned HRESULT. + +#ifdef _DEBUG + +void GetLastErrorDescription (CComBSTR & bstr); // Defined in VisVim.cpp +#define VERIFY_OK(f) \ + { \ + HRESULT hr = (f); \ + if (hr != S_OK) \ + { \ + if (FAILED(hr)) \ + { \ + CComBSTR bstr; \ + GetLastErrorDescription(bstr); \ + _RPTF2(_CRT_ASSERT, "Object call returned %lx\n\n%S", hr, (BSTR) bstr); \ + } \ + else \ + _RPTF1(_CRT_ASSERT, "Object call returned %lx", hr); \ + } \ + } + +#else //_DEBUG + +#define VERIFY_OK(f) (f); + +#endif //_DEBUG + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__AC72670E_2977_11D1_B2F3_006008040780__INCLUDED) diff --git a/src/VisVim/UnRegist.bat b/src/VisVim/UnRegist.bat new file mode 100644 index 0000000..9ea105d --- /dev/null +++ b/src/VisVim/UnRegist.bat @@ -0,0 +1 @@ +regsvr32.exe -unregister visvim.dll diff --git a/src/VisVim/VisVim.cpp b/src/VisVim/VisVim.cpp new file mode 100644 index 0000000..222925a --- /dev/null +++ b/src/VisVim/VisVim.cpp @@ -0,0 +1,152 @@ +// VisVim.cpp : Defines the initialization routines for the DLL. +// + +#include "stdafx.h" +#include +#include "VisVim.h" +#include "DSAddIn.h" +#include "Commands.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; + +#endif + +CComModule _Module; + +BEGIN_OBJECT_MAP (ObjectMap) +OBJECT_ENTRY (CLSID_DSAddIn, CDSAddIn) +END_OBJECT_MAP () + +class CVisVimApp : public CWinApp +{ + public: + CVisVimApp (); + + //{{AFX_VIRTUAL(CVisVimApp) + public: + virtual BOOL InitInstance (); + virtual int ExitInstance (); + //}}AFX_VIRTUAL + + //{{AFX_MSG(CVisVimApp) + //}}AFX_MSG + DECLARE_MESSAGE_MAP () +}; + +BEGIN_MESSAGE_MAP (CVisVimApp, CWinApp) +//{{AFX_MSG_MAP(CVisVimApp) +//}}AFX_MSG_MAP +END_MESSAGE_MAP () + +// The one and only CVisVimApp object +CVisVimApp theApp; + +CVisVimApp::CVisVimApp () +{ +} + +BOOL CVisVimApp::InitInstance () +{ + _Module.Init (ObjectMap, m_hInstance); + return CWinApp::InitInstance (); +} + +int CVisVimApp::ExitInstance () +{ + _Module.Term (); + return CWinApp::ExitInstance (); +} + +// Special entry points required for inproc servers +// + +STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID * ppv) +{ + AFX_MANAGE_STATE (AfxGetStaticModuleState ()); + return _Module.GetClassObject (rclsid, riid, ppv); +} + +STDAPI DllCanUnloadNow (void) +{ + AFX_MANAGE_STATE (AfxGetStaticModuleState ()); + return (AfxDllCanUnloadNow () == S_OK && _Module.GetLockCount () == 0) + ? S_OK : S_FALSE; +} + +// By exporting DllRegisterServer, you can use regsvr32.exe +// +STDAPI DllRegisterServer (void) +{ + AFX_MANAGE_STATE (AfxGetStaticModuleState ()); + HRESULT hRes; + + // Registers object, typelib and all interfaces in typelib + hRes = _Module.RegisterServer (TRUE); + if (FAILED (hRes)) + // Hack: When this fails we might be a normal user, while the + // admin already registered the module. Returning S_OK then + // makes it work. When the module was never registered it + // will soon fail in another way. + // old code: return hRes; + return S_OK; + + _ATL_OBJMAP_ENTRY *pEntry = _Module.m_pObjMap; + CRegKey key; + LONG lRes = key.Open (HKEY_CLASSES_ROOT, _T ("CLSID")); + + if (lRes == ERROR_SUCCESS) + { + USES_CONVERSION; + LPOLESTR lpOleStr; + + StringFromCLSID (*pEntry->pclsid, &lpOleStr); + LPTSTR lpsz = OLE2T (lpOleStr); + + lRes = key.Open (key, lpsz); + if (lRes == ERROR_SUCCESS) + { + CString strDescription; + + strDescription.LoadString (IDS_VISVIM_DESCRIPTION); + key.SetKeyValue (_T ("Description"), strDescription); + } + CoTaskMemFree (lpOleStr); + } + + if (lRes != ERROR_SUCCESS) + hRes = HRESULT_FROM_WIN32 (lRes); + + return hRes; + +} + +// DllUnregisterServer - Removes entries from the system registry +// +STDAPI DllUnregisterServer (void) +{ + AFX_MANAGE_STATE (AfxGetStaticModuleState ()); + + HRESULT hRes = S_OK; + _Module.UnregisterServer (); + return hRes; +} + + +// Debugging support + +// GetLastErrorDescription is used in the implementation of the VERIFY_OK +// macro, defined in stdafx.h. + +#ifdef _DEBUG + +void GetLastErrorDescription (CComBSTR & bstr) +{ + CComPtr < IErrorInfo > pErrorInfo; + if (GetErrorInfo (0, &pErrorInfo) == S_OK) + pErrorInfo->GetDescription (&bstr); +} + +#endif //_DEBUG diff --git a/src/VisVim/VisVim.def b/src/VisVim/VisVim.def new file mode 100644 index 0000000..023d478 --- /dev/null +++ b/src/VisVim/VisVim.def @@ -0,0 +1,11 @@ +; VisVim.def : Declares the module parameters for the DLL. + +LIBRARY "VISVIM" +DESCRIPTION 'VISVIM Windows Dynamic Link Library' + +EXPORTS + ; Explicit exports can go here + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/src/VisVim/VisVim.dll b/src/VisVim/VisVim.dll new file mode 100644 index 0000000..017b417 Binary files /dev/null and b/src/VisVim/VisVim.dll differ diff --git a/src/VisVim/VisVim.h b/src/VisVim/VisVim.h new file mode 100644 index 0000000..7759778 --- /dev/null +++ b/src/VisVim/VisVim.h @@ -0,0 +1,33 @@ +// VisVIM.h : main header file for the VisVim DLL +// + +#if !defined(AFX_VISVIM_H__AC72670B_2977_11D1_B2F3_006008040780__INCLUDED_) +#define AFX_VISVIM_H__AC72670B_2977_11D1_B2F3_006008040780__INCLUDED_ + +#ifndef __AFXWIN_H__ +#error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // Main symbols + +#include +#include +#include +#include +#include + +// +// Prototypes +// + +HKEY GetAppKey (char* AppName); +HKEY GetSectionKey (HKEY hAppKey, LPCTSTR Section); +int GetRegistryInt (HKEY hSectionKey, LPCTSTR Entry, int Default); +bool WriteRegistryInt (HKEY hSectionKey, char* Entry, int nValue); +void ReportLastError (HRESULT Err); +void ReportInternalError (char* Fct); + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_VISVIM_H__AC72670B_2977_11D1_B2F3_006008040780__INCLUDED) diff --git a/src/VisVim/VisVim.mak b/src/VisVim/VisVim.mak new file mode 100644 index 0000000..30a9cf4 --- /dev/null +++ b/src/VisVim/VisVim.mak @@ -0,0 +1,205 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00 +# ** DO NOT EDIT ** +# +# When Who What +# 1999-08-01 Anon Original VisVim.dsp +# 2001-08-08 W.Briscoe Back-ported to a condensed VC4 Makefile +# Reduced inter-dependency of Release and Debug builds. +# + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +!IF "$(CFG)" == "" +CFG=VisVim - Win32 Release +!MESSAGE No configuration specified. Defaulting to VisVim - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "VisVim - Win32 Release" && "$(CFG)" != "VisVim - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "VisVim.mak" CFG="VisVim - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "VisVim - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "VisVim - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +DEL_TREE = rmdir /s /q +!ELSE +NULL=nul +DEL_TREE = deltree /y +!ENDIF +# Begin Project +# PROP Target_Last_Scanned "VisVim - Win32 Release" +# PROP Use_MFC 2 +CPP=cl.exe +RSC=rc.exe +LINK32=link.exe + +!IF "$(CFG)" == "VisVim - Win32 Release" + +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Target_Dir "" +OUTDIR=.\Release +INTDIR=.\Release +CPP_OBJS=.\Release/ + +# ADD CPP /MD /O2 /D "NDEBUG" /I.\Release +CPP_PROJ= /MD /O2 /D "NDEBUG" /I.\Release +# ADD RSC /d "NDEBUG +RSC_PROJ= /d "NDEBUG" +# ADD LINK32 /pdb:none +LINK32_FLAGS=/pdb:none + +!ELSEIF "$(CFG)" == "VisVim - Win32 Debug" + +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Target_Dir "" +OUTDIR=.\Debug +INTDIR=.\Debug +CPP_OBJS=.\Debug/ + +# ADD CPP /MDd /Gm /Zi /Od /D "_DEBUG" /I.\Debug +CPP_PROJ= /MDd /Gm /Zi /Od /D "_DEBUG" /I.\Debug /Fd"$(INTDIR)/" +MTL_PROJ= /D "_DEBUG" +# ADD RSC /d "_DEBUG +RSC_PROJ= /d "_DEBUG" +# ADD LINK32 /debug /pdbtype:sept /pdb:".\Debug/VisVim.pdb" +LINK32_FLAGS=/debug /pdbtype:sept /pdb:"$(OUTDIR)/VisVim.pdb" + +!ENDIF + +# ADD CPP /nologo /W3 /GX /D "WIN32" /D "_WINDOWS" /D "_WINDLL" /D "_AFXDLL" /D "_USRDLL" /c +CPP_PROJ=$(CPP_PROJ) /nologo /W3 /GX /D "WIN32" /D "_WINDOWS" /D "_WINDLL" /D "_AFXDLL" /D "_USRDLL" /c /Fo"$(INTDIR)/" +# ADD RSC /l 0x409 /d "_AFXDLL" +RSC_PROJ=$(RSC_PROJ) /l 0x409 /d "_AFXDLL" /fo"$(INTDIR)/VisVim.res" +# ADD LINK32 /nologo /subsystem:windows /dll /machine:I386 /incremental:no +LINK32_FLAGS=$(LINK32_FLAGS) /nologo /subsystem:windows /dll /machine:I386\ + /incremental:no /def:".\VisVim.def"\ + /out:"$(OUTDIR)/VisVim.dll" /implib:"$(OUTDIR)/VisVim.lib" + +ALL : "$(OUTDIR)\VisVim.dll" + +CLEAN : + -@if exist "$(INTDIR)/$(NULL)" $(DEL_TREE) "$(INTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +LINK32_OBJS= \ + "$(INTDIR)/VisVim.res" \ + "$(INTDIR)/VisVim.obj" \ + "$(INTDIR)/StdAfx.obj" \ + "$(INTDIR)/Reg.obj" \ + "$(INTDIR)/DSAddIn.obj" \ + "$(INTDIR)/OleAut.obj" \ + "$(INTDIR)/Commands.obj" + +"$(OUTDIR)\VisVim.dll" : "$(OUTDIR)" ".\VisVim.def" $(LINK32_OBJS) + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) + +{.}.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +{.}.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +# Begin Target + +# Name "VisVim - Win32 Release" +# Name "VisVim - Win32 Debug" + +# Begin Source File + +SOURCE=.\VisVim.cpp + +"$(INTDIR)\VisVim.obj" : $(SOURCE) "$(INTDIR)" + +# End Source File +# Begin Source File + +SOURCE=.\VisVim.def +# End Source File +# Begin Source File + +SOURCE=.\VisVim.odl + +!IF "$(CFG)" == "VisVim - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build + +"$(INTDIR)\VisVim.tlb" : $(SOURCE) "$(INTDIR)" + midl /nologo /mktyplib203 /win32 /tlb VisVim.tlb /h VSVTypes.h .\VisVim.odl /out .\Release /D "NDEBUG" + +# End Custom Build + +!ELSEIF "$(CFG)" == "VisVim - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build + +"$(INTDIR)\VisVim.tlb" : $(SOURCE) "$(INTDIR)" + midl /nologo /mktyplib203 /win32 /tlb VisVim.tlb /h VSVTypes.h .\VisVim.odl /out .\Debug /D "_DEBUG" + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp + +"$(INTDIR)\StdAfx.obj" : $(SOURCE) "$(INTDIR)" + +# End Source File +# Begin Source File + +SOURCE=.\VisVim.rc + +"$(INTDIR)\VisVim.res" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\VisVim.tlb" + $(RSC) $(RSC_PROJ) /i "$(INTDIR)" $(SOURCE) + +# End Source File +# Begin Source File + +SOURCE=.\Reg.cpp + +"$(INTDIR)\Reg.obj" : $(SOURCE) "$(INTDIR)" + +# End Source File +# Begin Source File + +SOURCE=.\DSAddIn.cpp + +"$(INTDIR)\DSAddIn.obj" : $(SOURCE) "$(INTDIR)" + +# End Source File +# Begin Source File + +SOURCE=.\OleAut.cpp + +"$(INTDIR)\OleAut.obj" : $(SOURCE) "$(INTDIR)" + +# End Source File +# Begin Source File + +SOURCE=.\Commands.cpp + +"$(INTDIR)\Commands.obj" : $(SOURCE) "$(INTDIR)" + +# End Source File +# End Target +# End Project diff --git a/src/VisVim/VisVim.odl b/src/VisVim/VisVim.odl new file mode 100644 index 0000000..0491b8f --- /dev/null +++ b/src/VisVim/VisVim.odl @@ -0,0 +1,61 @@ +// VisVim.odl : type library source for VisVim.dll + +// This file will be processed by the Make Type Library (mktyplib) tool to +// produce the type library (VisVim.tlb). + +[ uuid(AC726707-2977-11D1-B2F3-006008040780), version(1.0), + helpstring ("VisVim Developer Studio Add-in") ] +library VisVim +{ + importlib("stdole32.tlb"); + importlib("devshl.dll"); + importlib("ide\devdbg.pkg"); + + + // Dual interface for CCommands + // + // All commands that your add-in adds to DevStudio + // must appear in this interface. You may use the + // ClassView to add methods to this interface, which + // will cause stub implementations of those methods to + // appear in your CCommands class. + + [ uuid(AC726703-2977-11D1-B2F3-006008040780), + oleautomation, + dual + ] + + interface ICommands : IDispatch + { + // methods + [id(1)] + HRESULT VisVimDialog(); + HRESULT VisVimEnable(); + HRESULT VisVimDisable(); + HRESULT VisVimToggle(); + HRESULT VisVimLoad(); + }; + + // Class information for CCommands + + [ uuid(AC726704-2977-11D1-B2F3-006008040780) ] + coclass Commands + { + [default] interface ICommands; + }; + + [ hidden, uuid(AC726705-2977-11D1-B2F3-006008040780) ] + coclass ApplicationEvents + { + [default] interface IApplicationEvents; + } + + [ hidden, uuid(AC726706-2977-11D1-B2F3-006008040780) ] + coclass DebuggerEvents + { + [default] interface IDebuggerEvents; + } + + //{{AFX_APPEND_ODL}} + //}}AFX_APPEND_ODL}} +}; diff --git a/src/VisVim/VisVim.rc b/src/VisVim/VisVim.rc new file mode 100644 index 0000000..cf74b29 --- /dev/null +++ b/src/VisVim/VisVim.rc @@ -0,0 +1,202 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Englisch (USA) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "1 TYPELIB ""VisVim.tlb""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_TOOLBAR_MEDIUM BITMAP MOVEABLE PURE "res\\ToolbarM.bmp" +IDR_TOOLBAR_LARGE BITMAP MOVEABLE PURE "res\\ToolbarL.bmp" + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,1,0,1 + PRODUCTVERSION 1,1,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "VisVim DLL\0" + VALUE "FileVersion", "1, 1, 0, 1\0" + VALUE "InternalName", "VisVim\0" + VALUE "LegalCopyright", "Copyright (C) 1998\0" + VALUE "OriginalFilename", "VisVim.DLL\0" + VALUE "ProductName", "VisVim Dynamic Link Library\0" + VALUE "ProductVersion", "1, 1, 0, 1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ADDINMAIN DIALOG DISCARDABLE 0, 0, 178, 124 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Vim Add-In 1.4" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "&Open file in DevStudio editor simultaneously", + IDC_DEVSTUDIO_EDITOR,"Button",BS_AUTOCHECKBOX | WS_GROUP | + WS_TABSTOP,7,7,153,10 + CONTROL "Open files in new tabs", + IDC_NEW_TABS,"Button",BS_AUTOCHECKBOX | WS_GROUP | + WS_TABSTOP,7,21,153,10 + GROUPBOX "Current directory",IDC_STATIC,7,35,164,58,WS_GROUP + CONTROL "Set to &source file path",IDC_CD_SOURCE_PATH,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,17,49,85,10 + CONTROL "Set to &parent directory of source file path", + IDC_CD_SOURCE_PARENT,"Button",BS_AUTORADIOBUTTON,17,63, + 143,10 + CONTROL "Do ¬ change",IDC_CD_NONE,"Button",BS_AUTORADIOBUTTON, + 17,78,63,10 + DEFPUSHBUTTON "&Ok",IDOK,7,102,74,14,WS_GROUP + PUSHBUTTON "&Cancel",IDCANCEL,97,102,74,14,WS_GROUP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ADDINMAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 171 + TOPMARGIN, 7 + BOTTOMMARGIN, 117 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_VISVIM_LONGNAME "Vim Developer Studio Add-In" + IDS_VISVIM_DESCRIPTION "Allows integration of Vim as the text editor in Developer Studio." + IDS_CMD_DIALOG "\nVim Add-In Dialog\nDisplays the options dialog box of the Vim Add-In\nVim Add-In Dialog" + IDS_CMD_ENABLE "\nEnable Vim Add-In\nEnables Vim as Visual Studio editor\nEnable Vim Add-In" + IDS_CMD_DISABLE "\nDisable Vim Add-In\nDisables Vim as Visual Studio editor\nDisable Vim Add-In" + IDS_CMD_TOGGLE "\nToggle Vim Add-In State\nToggles the enable state of the Vim Add-In\nToggle Vim Add-In State" + IDS_CMD_LOAD "\nVim Add-In Load Document\nLoads the current document in Vim\nVim Add-In Load Document" +END + +#endif // Englisch (USA) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif +#include "afxres.rc" // Standard components +#endif +1 TYPELIB "VisVim.tlb" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/VisVim/VsReadMe.txt b/src/VisVim/VsReadMe.txt new file mode 100644 index 0000000..f480c6b --- /dev/null +++ b/src/VisVim/VsReadMe.txt @@ -0,0 +1,91 @@ +======================================================================== + DEVELOPER STUDIO ADD-IN : VisVim +======================================================================== + + +The Add-in Wizard has created this VisVim DLL for you. This DLL not only +demonstrates the basics of creating a Developer Studio add-in, but it is also +a starting point for writing your own add-in. + +An add-in mainly does two things. + (1) It adds commands to Developer Studio, which can then be tied + to keystrokes or toolbar buttons by the user or programmatically + by the add-in. + (2) It responds to events fired by Developer Studio. +In both cases, the add-in code has access to the full Developer Studio +Automation Object Model, and may manipulate those objects to affect the +behavior of Developer Studio. + +This file contains a summary of what you will find in each of the files that +make up your VisVim DLL. + + +VisVim.h + This is the main header file for the DLL. It declares the + CVisVimApp class. + +VisVim.cpp + This is the main DLL source file. It contains the class CVisVimApp. + It also contains the OLE entry points required of inproc servers. + +VisVim.odl + This file contains the Object Description Language source code for the + type library of your DLL. + +VisVim.rc + This is a listing of all of the Microsoft Windows resources that the + program uses. It includes the sample toolbar bitmap that is stored + in the RES subdirectory. This file can be directly edited in Microsoft + Developer Studio. + +res\VisVim.rc2 + This file contains resources that are not edited by Microsoft + Developer Studio. You should place all resources not + editable by the resource editor in this file. + +VisVim.def + This file contains information about the DLL that must be + provided to run with Microsoft Windows. It defines parameters + such as the name and description of the DLL. It also exports + functions from the DLL. + +VisVim.clw + This file contains information used by ClassWizard to edit existing + classes or add new classes. ClassWizard also uses this file to store + information needed to create and edit message maps and dialog data + maps and to create prototype member functions. + +///////////////////////////////////////////////////////////////////////////// +Add-in-specific files: + +DSAddIn.cpp, DSAddIn.h + These files contain the CDSAddIn class, which implements the + IDSAddIn interface. This interface contains handlers + for connecting and disconnecting the add-in. + +Commands.cpp, Commands.h + These files contain the CCommands class, which implements your + command dispatch interface. This interface contains one method + for each command you add to Developer Studio. + These files also contain stub implementations of handlers for all events + fired by the Developer Studio Application object. + +OleAut.cpp + These files contain a general OLE automation class used to communicate + with vim. + +Reg.cpp + These files contain functions to access the registry. + + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named VisVim.pch and a precompiled types file named StdAfx.obj. + +Resource.h + This is the standard header file, which defines new resource IDs. + Microsoft Developer Studio reads and updates this file. + diff --git a/src/alloc.h b/src/alloc.h new file mode 100644 index 0000000..8babde3 --- /dev/null +++ b/src/alloc.h @@ -0,0 +1,33 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * alloc.h: enumeration of alloc IDs. + * Each entry must be on exactly one line, GetAllocId() depends on that. + */ +typedef enum { + aid_none = 0, + aid_qf_dirname_start, + aid_qf_dirname_now, + aid_qf_namebuf, + aid_qf_module, + aid_qf_errmsg, + aid_qf_pattern, + aid_tagstack_items, + aid_tagstack_from, + aid_tagstack_details, + aid_sign_getdefined, + aid_sign_getplaced, + aid_sign_define_by_name, + aid_sign_getlist, + aid_sign_getplaced_dict, + aid_sign_getplaced_list, + aid_insert_sign, + aid_sign_getinfo, + aid_last +} alloc_id_T; diff --git a/src/arabic.c b/src/arabic.c new file mode 100644 index 0000000..6c15221 --- /dev/null +++ b/src/arabic.c @@ -0,0 +1,715 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * arabic.c: functions for Arabic language + * + * Author: Nadim Shaikli & Isam Bayazidi + */ + +#include "vim.h" + +#if defined(FEAT_ARABIC) || defined(PROTO) + +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) +{ + switch (cur_c) + { + case a_HAMZA: return a_s_HAMZA; + case a_ALEF_MADDA: return a_s_ALEF_MADDA; + case a_ALEF_HAMZA_ABOVE: return a_s_ALEF_HAMZA_ABOVE; + case a_WAW_HAMZA: return a_s_WAW_HAMZA; + case a_ALEF_HAMZA_BELOW: return a_s_ALEF_HAMZA_BELOW; + case a_YEH_HAMZA: return a_s_YEH_HAMZA; + case a_ALEF: return a_s_ALEF; + case a_TEH_MARBUTA: return a_s_TEH_MARBUTA; + case a_DAL: return a_s_DAL; + case a_THAL: return a_s_THAL; + case a_REH: return a_s_REH; + case a_ZAIN: return a_s_ZAIN; + case a_TATWEEL: return cur_c; /* exceptions */ + case a_WAW: return a_s_WAW; + case a_ALEF_MAKSURA: return a_s_ALEF_MAKSURA; + case a_BEH: return a_s_BEH; + case a_TEH: return a_s_TEH; + case a_THEH: return a_s_THEH; + case a_JEEM: return a_s_JEEM; + case a_HAH: return a_s_HAH; + case a_KHAH: return a_s_KHAH; + case a_SEEN: return a_s_SEEN; + case a_SHEEN: return a_s_SHEEN; + case a_SAD: return a_s_SAD; + case a_DAD: return a_s_DAD; + case a_TAH: return a_s_TAH; + case a_ZAH: return a_s_ZAH; + case a_AIN: return a_s_AIN; + case a_GHAIN: return a_s_GHAIN; + case a_FEH: return a_s_FEH; + case a_QAF: return a_s_QAF; + case a_KAF: return a_s_KAF; + case a_LAM: return a_s_LAM; + case a_MEEM: return a_s_MEEM; + case a_NOON: return a_s_NOON; + case a_HEH: return a_s_HEH; + case a_YEH: return a_s_YEH; + } + return 0; +} + + +/* + * Change shape - from ISO-8859-6/Isolated to Initial + */ + static int +chg_c_a2i(int cur_c) +{ + switch (cur_c) + { + case a_YEH_HAMZA: return a_i_YEH_HAMZA; + case a_HAMZA: /* exceptions */ + return a_s_HAMZA; + case a_ALEF_MADDA: /* exceptions */ + return a_s_ALEF_MADDA; + case a_ALEF_HAMZA_ABOVE: /* exceptions */ + return a_s_ALEF_HAMZA_ABOVE; + case a_WAW_HAMZA: /* exceptions */ + return a_s_WAW_HAMZA; + case a_ALEF_HAMZA_BELOW: /* exceptions */ + return a_s_ALEF_HAMZA_BELOW; + case a_ALEF: /* exceptions */ + return a_s_ALEF; + case a_TEH_MARBUTA: /* exceptions */ + return a_s_TEH_MARBUTA; + case a_DAL: /* exceptions */ + return a_s_DAL; + case a_THAL: /* exceptions */ + return a_s_THAL; + case a_REH: /* exceptions */ + return a_s_REH; + case a_ZAIN: /* exceptions */ + return a_s_ZAIN; + case a_TATWEEL: /* exceptions */ + return cur_c; + case a_WAW: /* exceptions */ + return a_s_WAW; + case a_ALEF_MAKSURA: /* exceptions */ + return a_s_ALEF_MAKSURA; + case a_BEH: return a_i_BEH; + case a_TEH: return a_i_TEH; + case a_THEH: return a_i_THEH; + case a_JEEM: return a_i_JEEM; + case a_HAH: return a_i_HAH; + case a_KHAH: return a_i_KHAH; + case a_SEEN: return a_i_SEEN; + case a_SHEEN: return a_i_SHEEN; + case a_SAD: return a_i_SAD; + case a_DAD: return a_i_DAD; + case a_TAH: return a_i_TAH; + case a_ZAH: return a_i_ZAH; + case a_AIN: return a_i_AIN; + case a_GHAIN: return a_i_GHAIN; + case a_FEH: return a_i_FEH; + case a_QAF: return a_i_QAF; + case a_KAF: return a_i_KAF; + case a_LAM: return a_i_LAM; + case a_MEEM: return a_i_MEEM; + case a_NOON: return a_i_NOON; + case a_HEH: return a_i_HEH; + case a_YEH: return a_i_YEH; + } + return 0; +} + + +/* + * Change shape - from ISO-8859-6/Isolated to Medial + */ + static int +chg_c_a2m(int cur_c) +{ + switch (cur_c) + { + case a_HAMZA: return a_s_HAMZA; /* exception */ + case a_ALEF_MADDA: return a_f_ALEF_MADDA; /* exception */ + case a_ALEF_HAMZA_ABOVE: return a_f_ALEF_HAMZA_ABOVE; /* exception */ + case a_WAW_HAMZA: return a_f_WAW_HAMZA; /* exception */ + case a_ALEF_HAMZA_BELOW: return a_f_ALEF_HAMZA_BELOW; /* exception */ + case a_YEH_HAMZA: return a_m_YEH_HAMZA; + case a_ALEF: return a_f_ALEF; /* exception */ + case a_BEH: return a_m_BEH; + case a_TEH_MARBUTA: return a_f_TEH_MARBUTA; /* exception */ + case a_TEH: return a_m_TEH; + case a_THEH: return a_m_THEH; + case a_JEEM: return a_m_JEEM; + case a_HAH: return a_m_HAH; + case a_KHAH: return a_m_KHAH; + case a_DAL: return a_f_DAL; /* exception */ + case a_THAL: return a_f_THAL; /* exception */ + case a_REH: return a_f_REH; /* exception */ + case a_ZAIN: return a_f_ZAIN; /* exception */ + case a_SEEN: return a_m_SEEN; + case a_SHEEN: return a_m_SHEEN; + case a_SAD: return a_m_SAD; + case a_DAD: return a_m_DAD; + case a_TAH: return a_m_TAH; + case a_ZAH: return a_m_ZAH; + case a_AIN: return a_m_AIN; + case a_GHAIN: return a_m_GHAIN; + case a_TATWEEL: return cur_c; /* exception */ + case a_FEH: return a_m_FEH; + case a_QAF: return a_m_QAF; + case a_KAF: return a_m_KAF; + case a_LAM: return a_m_LAM; + case a_MEEM: return a_m_MEEM; + case a_NOON: return a_m_NOON; + case a_HEH: return a_m_HEH; + case a_WAW: return a_f_WAW; /* exception */ + case a_ALEF_MAKSURA: return a_f_ALEF_MAKSURA; /* exception */ + case a_YEH: return a_m_YEH; + } + return 0; +} + + +/* + * Change shape - from ISO-8859-6/Isolated to final + */ + static int +chg_c_a2f(int cur_c) +{ + /* 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: return a_s_HAMZA; /* exception */ + case a_ALEF_MADDA: return a_f_ALEF_MADDA; + case a_ALEF_HAMZA_ABOVE: return a_f_ALEF_HAMZA_ABOVE; + case a_WAW_HAMZA: return a_f_WAW_HAMZA; + case a_ALEF_HAMZA_BELOW: return a_f_ALEF_HAMZA_BELOW; + case a_YEH_HAMZA: return a_f_YEH_HAMZA; + case a_ALEF: return a_f_ALEF; + case a_BEH: return a_f_BEH; + case a_TEH_MARBUTA: return a_f_TEH_MARBUTA; + case a_TEH: return a_f_TEH; + case a_THEH: return a_f_THEH; + case a_JEEM: return a_f_JEEM; + case a_HAH: return a_f_HAH; + case a_KHAH: return a_f_KHAH; + case a_DAL: return a_f_DAL; + case a_THAL: return a_f_THAL; + case a_REH: return a_f_REH; + case a_ZAIN: return a_f_ZAIN; + case a_SEEN: return a_f_SEEN; + case a_SHEEN: return a_f_SHEEN; + case a_SAD: return a_f_SAD; + case a_DAD: return a_f_DAD; + case a_TAH: return a_f_TAH; + case a_ZAH: return a_f_ZAH; + case a_AIN: return a_f_AIN; + case a_GHAIN: return a_f_GHAIN; + case a_TATWEEL: return cur_c; /* exception */ + case a_FEH: return a_f_FEH; + case a_QAF: return a_f_QAF; + case a_KAF: return a_f_KAF; + case a_LAM: return a_f_LAM; + case a_MEEM: return a_f_MEEM; + case a_NOON: return a_f_NOON; + case a_HEH: return a_f_HEH; + case a_WAW: return a_f_WAW; + case a_ALEF_MAKSURA: return a_f_ALEF_MAKSURA; + case a_YEH: return a_f_YEH; + } + return 0; +} + + +/* + * Change shape - from Initial to Medial + * This code is unreachable, because for the relevant characters ARABIC_CHAR() + * is FALSE; + */ +#if 0 + static int +chg_c_i2m(int cur_c) +{ + switch (cur_c) + { + case a_i_YEH_HAMZA: return a_m_YEH_HAMZA; + case a_i_BEH: return a_m_BEH; + case a_i_TEH: return a_m_TEH; + case a_i_THEH: return a_m_THEH; + case a_i_JEEM: return a_m_JEEM; + case a_i_HAH: return a_m_HAH; + case a_i_KHAH: return a_m_KHAH; + case a_i_SEEN: return a_m_SEEN; + case a_i_SHEEN: return a_m_SHEEN; + case a_i_SAD: return a_m_SAD; + case a_i_DAD: return a_m_DAD; + case a_i_TAH: return a_m_TAH; + case a_i_ZAH: return a_m_ZAH; + case a_i_AIN: return a_m_AIN; + case a_i_GHAIN: return a_m_GHAIN; + case a_i_FEH: return a_m_FEH; + case a_i_QAF: return a_m_QAF; + case a_i_KAF: return a_m_KAF; + case a_i_LAM: return a_m_LAM; + case a_i_MEEM: return a_m_MEEM; + case a_i_NOON: return a_m_NOON; + case a_i_HEH: return a_m_HEH; + case a_i_YEH: return a_m_YEH; + } + return 0; +} +#endif + + +/* + * Change shape - from Final to Medial + */ + static int +chg_c_f2m(int cur_c) +{ + 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: return a_m_YEH_HAMZA; + 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: + return cur_c; + case a_f_BEH: return a_m_BEH; + case a_f_TEH: return a_m_TEH; + case a_f_THEH: return a_m_THEH; + case a_f_JEEM: return a_m_JEEM; + case a_f_HAH: return a_m_HAH; + case a_f_KHAH: return a_m_KHAH; + case a_f_SEEN: return a_m_SEEN; + case a_f_SHEEN: return a_m_SHEEN; + case a_f_SAD: return a_m_SAD; + case a_f_DAD: return a_m_DAD; + case a_f_TAH: return a_m_TAH; + case a_f_ZAH: return a_m_ZAH; + case a_f_AIN: return a_m_AIN; + case a_f_GHAIN: return a_m_GHAIN; + case a_f_FEH: return a_m_FEH; + case a_f_QAF: return a_m_QAF; + case a_f_KAF: return a_m_KAF; + case a_f_LAM: return a_m_LAM; + case a_f_MEEM: return a_m_MEEM; + case a_f_NOON: return a_m_NOON; + case a_f_HEH: return a_m_HEH; + case a_f_YEH: return a_m_YEH; + + /* 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: + */ + } + return 0; +} + + +/* + * Change shape - from Combination (2 char) to an Isolated + */ + static int +chg_c_laa2i(int hid_c) +{ + switch (hid_c) + { + case a_ALEF_MADDA: return a_s_LAM_ALEF_MADDA_ABOVE; + case a_ALEF_HAMZA_ABOVE: return a_s_LAM_ALEF_HAMZA_ABOVE; + case a_ALEF_HAMZA_BELOW: return a_s_LAM_ALEF_HAMZA_BELOW; + case a_ALEF: return a_s_LAM_ALEF; + } + return 0; +} + + +/* + * Change shape - from Combination-Isolated to Final + */ + static int +chg_c_laa2f(int hid_c) +{ + switch (hid_c) + { + case a_ALEF_MADDA: return a_f_LAM_ALEF_MADDA_ABOVE; + case a_ALEF_HAMZA_ABOVE: return a_f_LAM_ALEF_HAMZA_ABOVE; + case a_ALEF_HAMZA_BELOW: return a_f_LAM_ALEF_HAMZA_BELOW; + case a_ALEF: return a_f_LAM_ALEF; + } + return 0; +} + +/* + * 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)) +#if 0 + curr_c = A_is_iso(c) ? chg_c_a2m(c) : chg_c_i2m(c); +#else + curr_c = A_is_iso(c) ? chg_c_a2m(c) : 0; +#endif + 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; +} + + +/* + * A_firstc_laa returns first character of LAA combination if it exists + */ + static int +A_firstc_laa( + int c, /* base character */ + int c1) /* first composing character */ +{ + 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); +} + +#endif /* FEAT_ARABIC */ diff --git a/src/arabic.h b/src/arabic.h new file mode 100644 index 0000000..cb5cbd1 --- /dev/null +++ b/src/arabic.h @@ -0,0 +1,258 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * 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) + * + * -- + * + * Author: Nadim Shaikli & Isam Bayazidi + * - (based on Unicode) + * + */ + +/* + * 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 + +/* Range of Arabic characters that might be shaped. */ +#define ARABIC_CHAR(c) ((c) >= a_HAMZA && (c) <= a_MINI_ALEF) diff --git a/src/ascii.h b/src/ascii.h new file mode 100644 index 0000000..769e1a0 --- /dev/null +++ b/src/ascii.h @@ -0,0 +1,186 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * Definitions of various common control characters. + * For EBCDIC we have to use different values. + */ + +#ifndef EBCDIC + +/* 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 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 + +#else + +/* EBCDIC */ + +/* IF_EB(ASCII_constant, EBCDIC_constant) */ +#define IF_EB(a, b) b + +/* + * Finding the position in the alphabet is not straightforward in EBCDIC. + * There are gaps in the code table. + * 'a' + 1 == 'b', but: 'i' + 7 == 'j' and 'r' + 8 == 's' + */ +#define CharOrd__(c) ((c) < ('j' - 'a') ? (c) : ((c) < ('s' - 'a') ? (c) - 7 : (c) - 7 - 8)) +#define CharOrdLow(x) (CharOrd__((x) - 'a')) +#define CharOrdUp(x) (CharOrd__((x) - 'A')) +#define CharOrd(x) (isupper(x) ? CharOrdUp(x) : CharOrdLow(x)) + +#define EBCDIC_CHAR_ADD_(x) ((x) < 0?'a':(x)>25?'z':"abcdefghijklmnopqrstuvwxyz"[x]) +#define EBCDIC_CHAR_ADD(c,s) (isupper(c) ? toupper(EBCDIC_CHAR_ADD_(CharOrdUp(c)+(s))) : EBCDIC_CHAR_ADD_(CharOrdLow(c)+(s))) + +#define R13_(c) ("abcdefghijklmnopqrstuvwxyz"[((c) + 13) % 26]) +#define ROT13(c, a) (isupper(c) ? toupper(R13_(CharOrdUp(c))) : R13_(CharOrdLow(c))) + +#define NUL '\000' +#define BELL '\x2f' +#define BS '\x16' +#define TAB '\x05' +#define NL '\x15' +#define NL_STR (char_u *)"\x15" +#define FF '\x0C' +#define CAR '\x0D' +#define ESC '\x27' +#define ESC_STR (char_u *)"\x27" +#define ESC_STR_nc "\x27" +#define DEL 0x07 +#define DEL_STR (char_u *)"\007" + +#define POUND 0xB1 + +#define CTRL_F_STR "\056" +#define CTRL_H_STR "\026" +#define CTRL_V_STR "\062" + +#define Ctrl_AT 0x00 /* @ */ +#define Ctrl_A 0x01 +#define Ctrl_B 0x02 +#define Ctrl_C 0x03 +#define Ctrl_D 0x37 +#define Ctrl_E 0x2D +#define Ctrl_F 0x2E +#define Ctrl_G 0x2F +#define Ctrl_H 0x16 +#define Ctrl_I 0x05 +#define Ctrl_J 0x15 +#define Ctrl_K 0x0B +#define Ctrl_L 0x0C +#define Ctrl_M 0x0D +#define Ctrl_N 0x0E +#define Ctrl_O 0x0F +#define Ctrl_P 0x10 +#define Ctrl_Q 0x11 +#define Ctrl_R 0x12 +#define Ctrl_S 0x13 +#define Ctrl_T 0x3C +#define Ctrl_U 0x3D +#define Ctrl_V 0x32 +#define Ctrl_W 0x26 +#define Ctrl_X 0x18 +#define Ctrl_Y 0x19 +#define Ctrl_Z 0x3F + /* CTRL- [ Left Square Bracket == ESC*/ +#define Ctrl_RSB 0x1D /* ] Right Square Bracket */ +#define Ctrl_BSL 0x1C /* \ BackSLash */ +#define Ctrl_HAT 0x1E /* ^ */ +#define Ctrl__ 0x1F + +#define Ctrl_chr(x) (CtrlTable[(x)]) +extern char CtrlTable[]; + +#define CtrlChar(x) ((x < ' ') ? CtrlCharTable[(x)] : 0) +extern char CtrlCharTable[]; + +#define MetaChar(x) ((x < ' ') ? MetaCharTable[(x)] : 0) +extern char MetaCharTable[]; + +#endif /* defined EBCDIC */ + +/* TODO: EBCDIC Code page dependent (here 1047) */ +#define CSI 0x9b /* Control Sequence Introducer */ +#define CSI_STR "\233" +#define DCS 0x90 /* Device Control String */ +#define OSC 0x9d /* Operating System Command */ +#define STERM 0x9c /* String Terminator */ + +/* + * Character that separates dir names in a path. + * For MS-DOS, WIN32 and OS/2 we use a backslash. A slash mostly works + * fine, but there are places where it doesn't (e.g. in a command name). + * For Acorn we use a dot. + */ +#ifdef BACKSLASH_IN_FILENAME +# define PATHSEP psepc +# define PATHSEPSTR pseps +#else +# define PATHSEP '/' +# define PATHSEPSTR "/" +#endif diff --git a/src/auto/configure b/src/auto/configure new file mode 100755 index 0000000..8052ceb --- /dev/null +++ b/src/auto/configure @@ -0,0 +1,16142 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= +PACKAGE_URL= + +ac_unique_file="vim.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +LINK_AS_NEEDED +DEPEND_CFLAGS_FILTER +MAKEMO +MSGFMT +INSTALL_TOOL_LANGS +INSTALL_LANGS +TAGPRG +HANGULIN_OBJ +HANGULIN_SRC +GUI_X_LIBS +GUITYPE +GUI_LIB_LOC +GUI_INC_LOC +NARROW_PROTO +MOTIF_LIBNAME +GRESOURCE_OBJ +GRESOURCE_SRC +UPDATE_DESKTOP_DATABASE +GTK_UPDATE_ICON_CACHE +GLIB_COMPILE_RESOURCES +GNOME_INCLUDEDIR +GNOME_LIBDIR +GNOME_LIBS +GTK_LIBNAME +GTK_LIBS +GTK_CFLAGS +PKG_CONFIG +X_LIB +X_EXTRA_LIBS +X_LIBS +X_PRE_LIBS +X_CFLAGS +XMKMF +xmkmfpath +TERM_OBJ +TERM_SRC +CHANNEL_OBJ +CHANNEL_SRC +NETBEANS_OBJ +NETBEANS_SRC +RUBY_LIBS +RUBY_CFLAGS +RUBY_PRO +RUBY_OBJ +RUBY_SRC +vi_cv_path_ruby +TCL_LIBS +TCL_CFLAGS +TCL_PRO +TCL_OBJ +TCL_SRC +vi_cv_path_tcl +PYTHON3_OBJ +PYTHON3_SRC +PYTHON3_CFLAGS +PYTHON3_LIBS +vi_cv_path_python3 +PYTHON_OBJ +PYTHON_SRC +PYTHON_CFLAGS +PYTHON_LIBS +vi_cv_path_python +PERL_LIBS +PERL_CFLAGS +PERL_PRO +PERL_OBJ +PERL_SRC +shrpenv +vi_cv_perl_xsubpp +vi_cv_perllib +vi_cv_path_perl +MZSCHEME_MZC +MZSCHEME_EXTRA +MZSCHEME_CFLAGS +MZSCHEME_LIBS +MZSCHEME_PRO +MZSCHEME_OBJ +MZSCHEME_SRC +vi_cv_path_mzscheme +LUA_CFLAGS +LUA_LIBS +LUA_PRO +LUA_OBJ +LUA_SRC +vi_cv_path_plain_lua +vi_cv_path_luajit +vi_cv_path_lua +compiledby +dogvimdiff +dovimdiff +QUOTESED +line_break +VIEWNAME +EXNAME +VIMNAME +OS_EXTRA_OBJ +OS_EXTRA_SRC +XCODE_SELECT +CPP_MM +CROSS_COMPILING +STRIP +AWK +FGREP +EGREP +GREP +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +SET_MAKE +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_fail_if_missing +enable_darwin +with_mac_arch +with_developer_dir +with_local_dir +with_vim_name +with_ex_name +with_view_name +with_global_runtime +with_modified_by +enable_smack +enable_selinux +with_features +with_compiledby +enable_xsmp +enable_xsmp_interact +enable_luainterp +with_lua_prefix +with_luajit +enable_mzschemeinterp +with_plthome +enable_perlinterp +enable_pythoninterp +with_python_command +with_python_config_dir +enable_python3interp +with_python3_command +with_python3_config_dir +enable_tclinterp +with_tclsh +enable_rubyinterp +with_ruby_command +enable_cscope +enable_netbeans +enable_channel +enable_terminal +enable_autoservername +enable_multibyte +enable_rightleft +enable_arabic +enable_farsi +enable_hangulinput +enable_xim +enable_fontset +with_x +enable_gui +enable_gtk2_check +enable_gnome_check +enable_gtk3_check +enable_motif_check +enable_athena_check +enable_nextaw_check +enable_carbon_check +enable_gtktest +with_gnome_includes +with_gnome_libs +with_gnome +enable_icon_cache_update +enable_desktop_database_update +with_motif_lib +with_tlib +enable_largefile +enable_acl +enable_gpm +enable_sysmouse +enable_nls +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +XMKMF' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +X features: + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-fail-if-missing Fail if dependencies on additional features + specified on the command line are missing. + --disable-darwin Disable Darwin (Mac OS X) support. + --disable-smack Do not check for Smack support. + --disable-selinux Do not check for SELinux support. + --disable-xsmp Disable XSMP session management + --disable-xsmp-interact Disable XSMP interaction + --enable-luainterp=OPTS Include Lua interpreter. default=no OPTS=no/yes/dynamic + --enable-mzschemeinterp Include MzScheme interpreter. + --enable-perlinterp=OPTS Include Perl interpreter. default=no OPTS=no/yes/dynamic + --enable-pythoninterp=OPTS Include Python interpreter. default=no OPTS=no/yes/dynamic + --enable-python3interp=OPTS Include Python3 interpreter. default=no OPTS=no/yes/dynamic + --enable-tclinterp=OPTS Include Tcl interpreter. default=no OPTS=no/yes/dynamic + --enable-rubyinterp=OPTS Include Ruby interpreter. default=no OPTS=no/yes/dynamic + --enable-cscope Include cscope interface. + --disable-netbeans Disable NetBeans integration support. + --disable-channel Disable process communication support. + --enable-terminal Enable terminal emulation support. + --enable-autoservername Automatically define servername at vim startup. + --enable-multibyte Include multibyte editing support. + --disable-rightleft Do not include Right-to-Left language support. + --disable-arabic Do not include Arabic language support. + --disable-farsi Do not include Farsi language support. + --enable-hangulinput Include Hangul input support. + --enable-xim Include XIM input support. + --enable-fontset Include X fontset output support. + --enable-gui=OPTS X11 GUI. default=auto OPTS=auto/no/gtk2/gnome2/gtk3/motif/athena/neXtaw/photon/carbon + --enable-gtk2-check If auto-select GUI, check for GTK+ 2 default=yes + --enable-gnome-check If GTK GUI, check for GNOME default=no + --enable-gtk3-check If auto-select GUI, check for GTK+ 3 default=yes + --enable-motif-check If auto-select GUI, check for Motif default=yes + --enable-athena-check If auto-select GUI, check for Athena default=yes + --enable-nextaw-check If auto-select GUI, check for neXtaw default=yes + --enable-carbon-check If auto-select GUI, check for Carbon default=yes + --disable-gtktest Do not try to compile and run a test GTK program + --disable-icon-cache-update update disabled + --disable-desktop-database-update update disabled + --disable-largefile omit support for large files + --disable-acl No check for ACL support. + --disable-gpm Don't use gpm (Linux mouse daemon). + --disable-sysmouse Don't use sysmouse (mouse in *BSD console). + --disable-nls Don't support NLS (gettext()). + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-mac-arch=ARCH current, intel, ppc or both + --with-developer-dir=PATH use PATH as location for Xcode developer tools + --with-local-dir=PATH search PATH instead of /usr/local for local libraries. + --without-local-dir do not search /usr/local for local libraries. + --with-vim-name=NAME what to call the Vim executable + --with-ex-name=NAME what to call the Ex executable + --with-view-name=NAME what to call the View executable + --with-global-runtime=DIR global runtime directory in 'runtimepath', comma-separated for multiple directories + --with-modified-by=NAME name of who modified a release version + --with-features=TYPE tiny, small, normal, big or huge (default: huge) + --with-compiledby=NAME name to show in :version message + --with-lua-prefix=PFX Prefix where Lua is installed. + --with-luajit Link with LuaJIT instead of Lua. + --with-plthome=PLTHOME Use PLTHOME. + --with-python-command=NAME name of the Python 2 command (default: python2 or python) + --with-python-config-dir=PATH Python's config directory (deprecated) + --with-python3-command=NAME name of the Python 3 command (default: python3 or python) + --with-python3-config-dir=PATH Python's config directory (deprecated) + --with-tclsh=PATH which tclsh to use (default: tclsh8.0) + --with-ruby-command=RUBY name of the Ruby command (default: ruby) + --with-x use the X Window System + --with-gnome-includes=DIR Specify location of GNOME headers + --with-gnome-libs=DIR Specify location of GNOME libs + --with-gnome Specify prefix for GNOME files + --with-motif-lib=STRING Library for Motif + --with-tlib=library terminal library to be used + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + XMKMF Path to xmkmf, Makefile generator for X Window System + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_find_uintX_t LINENO BITS VAR +# ------------------------------------ +# Finds an unsigned integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_uintX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +$as_echo_n "checking for uint$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + case $ac_type in #( + uint$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_uintX_t + +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 auto/config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>auto/config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_config_headers="$ac_config_headers auto/config.h:config.h.in" + + +$as_echo "#define UNIX 1" >>confdefs.h + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 +$as_echo_n "checking for $CC option to accept ISO C99... " >&6; } +if ${ac_cv_prog_cc_c99+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +#include + +// Check varargs macros. These examples are taken from C99 6.10.3.5. +#define debug(...) fprintf (stderr, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) +{ + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); +} + +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + your preprocessor is broken; +#endif +#if BIG_OK +#else + your preprocessor is broken; +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; + +struct incomplete_array +{ + int datasize; + double data[]; +}; + +struct named_init { + int number; + const wchar_t *name; + double average; +}; + +typedef const char *ccp; + +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\0'; ++i) + continue; + return 0; +} + +// Check varargs and va_copy. +static void +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); + + const char *str; + int number; + float fnumber; + + while (*format) + { + switch (*format++) + { + case 's': // string + str = va_arg (args_copy, const char *); + break; + case 'd': // int + number = va_arg (args_copy, int); + break; + case 'f': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); +} + +int +main () +{ + + // Check bool. + _Bool success = false; + + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; + + // Check varargs. + test_varargs ("s, d' f .", "string", 65, 34.234); + test_varargs_macros (); + + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; + + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; + + ni.number = 58; + + int dynamic_array[ni.number]; + dynamic_array[ni.number - 1] = 543; + + // work around unused variable warnings + return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' + || dynamic_array[ni.number - 1] != 543); + + ; + return 0; +} +_ACEOF +for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c99=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c99" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c99" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +$as_echo "$ac_cv_prog_cc_c99" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c99" != xno; then : + +fi + + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "" >/dev/null 2>&1; then : + +fi +rm -f conftest* + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing strerror" >&5 +$as_echo_n "checking for library containing strerror... " >&6; } +if ${ac_cv_search_strerror+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char strerror (); +int +main () +{ +return strerror (); + ; + return 0; +} +_ACEOF +for ac_lib in '' cposix; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_strerror=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_strerror+:} false; then : + break +fi +done +if ${ac_cv_search_strerror+:} false; then : + +else + ac_cv_search_strerror=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_strerror" >&5 +$as_echo "$ac_cv_search_strerror" >&6; } +ac_res=$ac_cv_search_strerror +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +# Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_STRIP" && ac_cv_prog_STRIP=":" +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 +$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } +if ${ac_cv_header_sys_wait_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_sys_wait_h=yes +else + ac_cv_header_sys_wait_h=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5 +$as_echo "$ac_cv_header_sys_wait_h" >&6; } +if test $ac_cv_header_sys_wait_h = yes; then + +$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h + +fi + + +if test x"$ac_cv_prog_cc_c99" != xno; then + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unsigned long long int" >&5 +$as_echo_n "checking for unsigned long long int... " >&6; } +if ${ac_cv_type_unsigned_long_long_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_type_unsigned_long_long_int=yes + if test "x${ac_cv_prog_cc_c99-no}" = xno; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + /* For now, do not test the preprocessor; as of 2007 there are too many + implementations with broken preprocessors. Perhaps this can + be revisited in 2012. In the meantime, code should not expect + #if to work with literals wider than 32 bits. */ + /* Test literals. */ + long long int ll = 9223372036854775807ll; + long long int nll = -9223372036854775807LL; + unsigned long long int ull = 18446744073709551615ULL; + /* Test constant expressions. */ + typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) + ? 1 : -1)]; + typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 + ? 1 : -1)]; + int i = 63; +int +main () +{ +/* Test availability of runtime routines for shift and division. */ + long long int llmax = 9223372036854775807ll; + unsigned long long int ullmax = 18446744073709551615ull; + return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) + | (llmax / ll) | (llmax % ll) + | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) + | (ullmax / ull) | (ullmax % ull)); + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +else + ac_cv_type_unsigned_long_long_int=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_unsigned_long_long_int" >&5 +$as_echo "$ac_cv_type_unsigned_long_long_int" >&6; } + if test $ac_cv_type_unsigned_long_long_int = yes; then + +$as_echo "#define HAVE_UNSIGNED_LONG_LONG_INT 1" >>confdefs.h + + fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long long int" >&5 +$as_echo_n "checking for long long int... " >&6; } +if ${ac_cv_type_long_long_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_type_long_long_int=yes + if test "x${ac_cv_prog_cc_c99-no}" = xno; then + ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int + if test $ac_cv_type_long_long_int = yes; then + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #ifndef LLONG_MAX + # define HALF \ + (1LL << (sizeof (long long int) * CHAR_BIT - 2)) + # define LLONG_MAX (HALF - 1 + HALF) + #endif +int +main () +{ +long long int n = 1; + int i; + for (i = 0; ; i++) + { + long long int m = n << i; + if (m >> i != n) + return 1; + if (LLONG_MAX / 2 < m) + break; + } + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_type_long_long_int=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_long_int" >&5 +$as_echo "$ac_cv_type_long_long_int" >&6; } + if test $ac_cv_type_long_long_int = yes; then + +$as_echo "#define HAVE_LONG_LONG_INT 1" >>confdefs.h + + fi + + if test "$ac_cv_type_long_long_int" = no; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Compiler does not support long long int +See \`config.log' for more details" "$LINENO" 5; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the compiler supports trailing commas" >&5 +$as_echo_n "checking if the compiler supports trailing commas... " >&6; } + trailing_commas=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + enum { + one, + }; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; trailing_commas=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test "$trailing_commas" = no; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Compiler does not support trailing comma in enum +See \`config.log' for more details" "$LINENO" 5; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the compiler supports C++ comments" >&5 +$as_echo_n "checking if the compiler supports C++ comments... " >&6; } + slash_comments=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +// C++ comments? + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; slash_comments=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test "$slash_comments" = no; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Compiler does not support C++ comments +See \`config.log' for more details" "$LINENO" 5; } + fi +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-fail-if-missing argument" >&5 +$as_echo_n "checking --enable-fail-if-missing argument... " >&6; } +# Check whether --enable-fail_if_missing was given. +if test "${enable_fail_if_missing+set}" = set; then : + enableval=$enable_fail_if_missing; fail_if_missing="yes" +else + fail_if_missing="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $fail_if_missing" >&5 +$as_echo "$fail_if_missing" >&6; } + +with_x_arg="$with_x" + +if test -z "$CFLAGS"; then + CFLAGS="-O" + test "$GCC" = yes && CFLAGS="-O2 -fno-strength-reduce -Wall" +fi +if test "$GCC" = yes; then + gccversion=`$CC -dumpversion` + if test "x$gccversion" = "x"; then + gccversion=`$CC --version | sed -e '2,$d' -e 's/darwin.//' -e 's/^[^0-9]*\([0-9]\.[0-9.]*\).*$/\1/g'` + fi + if test "$gccversion" = "3.0.1" -o "$gccversion" = "3.0.2" -o "$gccversion" = "4.0.1"; then + echo 'GCC [34].0.[12] has a bug in the optimizer, disabling "-O#"' + CFLAGS=`echo "$CFLAGS" | sed 's/-O[23456789]/-O/'` + else + if test "$gccversion" = "3.1" -o "$gccversion" = "3.2" -o "$gccversion" = "3.2.1" && `echo "$CFLAGS" | grep -v fno-strength-reduce >/dev/null`; then + echo 'GCC 3.1 and 3.2 have a bug in the optimizer, adding "-fno-strength-reduce"' + CFLAGS="$CFLAGS -fno-strength-reduce" + fi + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for clang version" >&5 +$as_echo_n "checking for clang version... " >&6; } +CLANG_VERSION_STRING=`$CC --version 2>/dev/null | sed -n -e 's/^.*clang[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$/\1/p'` +if test x"$CLANG_VERSION_STRING" != x"" ; then + CLANG_MAJOR=`echo "$CLANG_VERSION_STRING" | sed -n -e 's/\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*/\1/p'` + CLANG_MINOR=`echo "$CLANG_VERSION_STRING" | sed -n -e 's/[0-9][0-9]*\.\([0-9][0-9]*\)\.[0-9][0-9]*/\1/p'` + CLANG_REVISION=`echo "$CLANG_VERSION_STRING" | sed -n -e 's/[0-9][0-9]*\.[0-9][0-9]*\.\([0-9][0-9]*\)/\1/p'` + CLANG_VERSION=`expr $CLANG_MAJOR '*' 1000000 '+' $CLANG_MINOR '*' 1000 '+' $CLANG_REVISION` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CLANG_VERSION" >&5 +$as_echo "$CLANG_VERSION" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if clang supports -fno-strength-reduce" >&5 +$as_echo_n "checking if clang supports -fno-strength-reduce... " >&6; } + if test "$CLANG_VERSION" -ge 500002075 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS=`echo "$CFLAGS" | sed -e 's/-fno-strength-reduce/ /'` + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: N/A" >&5 +$as_echo "N/A" >&6; } +fi + +CROSS_COMPILING= +if test "$cross_compiling" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot compile a simple program; if not cross compiling check CC and CFLAGS" >&5 +$as_echo "cannot compile a simple program; if not cross compiling check CC and CFLAGS" >&6; } + CROSS_COMPILING=1 +fi + + +test "$GCC" = yes && CPP_MM=M; + +if test -f ./toolcheck; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for buggy tools..." >&5 +$as_echo "$as_me: checking for buggy tools..." >&6;} + sh ./toolcheck 1>&6 +fi + +OS_EXTRA_SRC=""; OS_EXTRA_OBJ="" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BeOS" >&5 +$as_echo_n "checking for BeOS... " >&6; } +case `uname` in + BeOS) OS_EXTRA_SRC=os_beos.c; OS_EXTRA_OBJ=objects/os_beos.o + BEOS=yes; { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; };; + *) BEOS=no; { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; };; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for QNX" >&5 +$as_echo_n "checking for QNX... " >&6; } +case `uname` in + QNX) OS_EXTRA_SRC=os_qnx.c; OS_EXTRA_OBJ=objects/os_qnx.o + test -z "$with_x" && with_x=no + QNX=yes; { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; };; + *) QNX=no; { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; };; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Darwin (Mac OS X)" >&5 +$as_echo_n "checking for Darwin (Mac OS X)... " >&6; } +if test "`(uname) 2>/dev/null`" = Darwin; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + MACOS_X=yes + CPPFLAGS="$CPPFLAGS -DMACOS_X" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-darwin argument" >&5 +$as_echo_n "checking --disable-darwin argument... " >&6; } + # Check whether --enable-darwin was given. +if test "${enable_darwin+set}" = set; then : + enableval=$enable_darwin; +else + enable_darwin="yes" +fi + + if test "$enable_darwin" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if Darwin files are there" >&5 +$as_echo_n "checking if Darwin files are there... " >&6; } + if test -f os_macosx.m; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, Darwin support disabled" >&5 +$as_echo "no, Darwin support disabled" >&6; } + enable_darwin=no + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, Darwin support excluded" >&5 +$as_echo "yes, Darwin support excluded" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-mac-arch argument" >&5 +$as_echo_n "checking --with-mac-arch argument... " >&6; } + +# Check whether --with-mac-arch was given. +if test "${with_mac_arch+set}" = set; then : + withval=$with_mac_arch; MACARCH="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MACARCH" >&5 +$as_echo "$MACARCH" >&6; } +else + MACARCH="current"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: defaulting to $MACARCH" >&5 +$as_echo "defaulting to $MACARCH" >&6; } +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-developer-dir argument" >&5 +$as_echo_n "checking --with-developer-dir argument... " >&6; } + +# Check whether --with-developer-dir was given. +if test "${with_developer_dir+set}" = set; then : + withval=$with_developer_dir; DEVELOPER_DIR="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DEVELOPER_DIR" >&5 +$as_echo "$DEVELOPER_DIR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not present" >&5 +$as_echo "not present" >&6; } +fi + + + if test "x$DEVELOPER_DIR" = "x"; then + # Extract the first word of "xcode-select", so it can be a program name with args. +set dummy xcode-select; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_XCODE_SELECT+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $XCODE_SELECT in + [\\/]* | ?:[\\/]*) + ac_cv_path_XCODE_SELECT="$XCODE_SELECT" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_XCODE_SELECT="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +XCODE_SELECT=$ac_cv_path_XCODE_SELECT +if test -n "$XCODE_SELECT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XCODE_SELECT" >&5 +$as_echo "$XCODE_SELECT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "x$XCODE_SELECT" != "x"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for developer dir using xcode-select" >&5 +$as_echo_n "checking for developer dir using xcode-select... " >&6; } + DEVELOPER_DIR=`$XCODE_SELECT -print-path` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DEVELOPER_DIR" >&5 +$as_echo "$DEVELOPER_DIR" >&6; } + else + DEVELOPER_DIR=/Developer + fi + fi + + if test "x$MACARCH" = "xboth"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 10.4 universal SDK" >&5 +$as_echo_n "checking for 10.4 universal SDK... " >&6; } + save_cppflags="$CPPFLAGS" + save_cflags="$CFLAGS" + save_ldflags="$LDFLAGS" + CFLAGS="$CFLAGS -isysroot $DEVELOPER_DIR/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 +$as_echo "found" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + CFLAGS="$save_cflags" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if Intel architecture is supported" >&5 +$as_echo_n "checking if Intel architecture is supported... " >&6; } + CPPFLAGS="$CPPFLAGS -arch i386" + LDFLAGS="$save_ldflags -arch i386" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; MACARCH="intel" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + MACARCH="ppc" + CPPFLAGS="$save_cppflags -arch ppc" + LDFLAGS="$save_ldflags -arch ppc" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + elif test "x$MACARCH" = "xintel"; then + CPPFLAGS="$CPPFLAGS -arch intel" + LDFLAGS="$LDFLAGS -arch intel" + elif test "x$MACARCH" = "xppc"; then + CPPFLAGS="$CPPFLAGS -arch ppc" + LDFLAGS="$LDFLAGS -arch ppc" + fi + + if test "$enable_darwin" = "yes"; then + MACOS_X_DARWIN=yes + OS_EXTRA_SRC="os_macosx.m os_mac_conv.c"; + OS_EXTRA_OBJ="objects/os_macosx.o objects/os_mac_conv.o" + CPPFLAGS="$CPPFLAGS -DMACOS_X_DARWIN" + + # On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +ac_fn_c_check_header_mongrel "$LINENO" "Carbon/Carbon.h" "ac_cv_header_Carbon_Carbon_h" "$ac_includes_default" +if test "x$ac_cv_header_Carbon_Carbon_h" = xyes; then : + CARBON=yes +fi + + + if test "x$CARBON" = "xyes"; then + if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3; then + with_x=no + fi + fi + fi + + if test "$MACARCH" = "intel" -o "$MACARCH" = "both"; then + CFLAGS=`echo "$CFLAGS" | sed 's/-O[23456789]/-Oz/'` + fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + +for ac_header in AvailabilityMacros.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "AvailabilityMacros.h" "ac_cv_header_AvailabilityMacros_h" "$ac_includes_default" +if test "x$ac_cv_header_AvailabilityMacros_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_AVAILABILITYMACROS_H 1 +_ACEOF + +fi + +done + + + + + +if test "$cross_compiling" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-local-dir argument" >&5 +$as_echo_n "checking --with-local-dir argument... " >&6; } + have_local_include='' + have_local_lib='' + +# Check whether --with-local-dir was given. +if test "${with_local_dir+set}" = set; then : + withval=$with_local_dir; + local_dir="$withval" + case "$withval" in + */*) ;; + no) + # avoid adding local dir to LDFLAGS and CPPFLAGS + have_local_include=yes + have_local_lib=yes + ;; + *) as_fn_error $? "must pass path argument to --with-local-dir" "$LINENO" 5 ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $local_dir" >&5 +$as_echo "$local_dir" >&6; } + +else + + local_dir=/usr/local + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to $local_dir" >&5 +$as_echo "Defaulting to $local_dir" >&6; } + +fi + + if test "$GCC" = yes -a "$local_dir" != no; then + echo 'void f(){}' > conftest.c + have_local_include=`${CC-cc} -c -v conftest.c 2>&1 | grep "${local_dir}/include"` + have_local_lib=`${CC-cc} -c -v conftest.c 2>&1 | grep "${local_dir}/lib"` + rm -f conftest.c conftest.o + fi + if test -z "$have_local_lib" -a -d "${local_dir}/lib"; then + tt=`echo "$LDFLAGS" | sed -e "s+-L${local_dir}/lib ++g" -e "s+-L${local_dir}/lib$++g"` + if test "$tt" = "$LDFLAGS"; then + LDFLAGS="$LDFLAGS -L${local_dir}/lib" + fi + fi + if test -z "$have_local_include" -a -d "${local_dir}/include"; then + tt=`echo "$CPPFLAGS" | sed -e "s+-I${local_dir}/include ++g" -e "s+-I${local_dir}/include$++g"` + if test "$tt" = "$CPPFLAGS"; then + CPPFLAGS="$CPPFLAGS -I${local_dir}/include" + fi + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-vim-name argument" >&5 +$as_echo_n "checking --with-vim-name argument... " >&6; } + +# Check whether --with-vim-name was given. +if test "${with_vim_name+set}" = set; then : + withval=$with_vim_name; VIMNAME="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $VIMNAME" >&5 +$as_echo "$VIMNAME" >&6; } +else + VIMNAME="vim"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to $VIMNAME" >&5 +$as_echo "Defaulting to $VIMNAME" >&6; } +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-ex-name argument" >&5 +$as_echo_n "checking --with-ex-name argument... " >&6; } + +# Check whether --with-ex-name was given. +if test "${with_ex_name+set}" = set; then : + withval=$with_ex_name; EXNAME="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $EXNAME" >&5 +$as_echo "$EXNAME" >&6; } +else + EXNAME="ex"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to ex" >&5 +$as_echo "Defaulting to ex" >&6; } +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-view-name argument" >&5 +$as_echo_n "checking --with-view-name argument... " >&6; } + +# Check whether --with-view-name was given. +if test "${with_view_name+set}" = set; then : + withval=$with_view_name; VIEWNAME="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $VIEWNAME" >&5 +$as_echo "$VIEWNAME" >&6; } +else + VIEWNAME="view"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to view" >&5 +$as_echo "Defaulting to view" >&6; } +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-global-runtime argument" >&5 +$as_echo_n "checking --with-global-runtime argument... " >&6; } + +# Check whether --with-global-runtime was given. +if test "${with_global_runtime+set}" = set; then : + withval=$with_global_runtime; RUNTIME_GLOBAL="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $withval" >&5 +$as_echo "$withval" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +if test "X$RUNTIME_GLOBAL" != "X"; then + RUNTIME_GLOBAL_AFTER=$(printf -- "$RUNTIME_GLOBAL\\n" | $AWK -F, 'BEGIN { comma=0 } { for (i = NF; i > 0; i--) { if (comma) { printf ",%s/after", $i } else { printf "%s/after", $i; comma=1 } } } END { printf "\n" }') + cat >>confdefs.h <<_ACEOF +#define RUNTIME_GLOBAL "$RUNTIME_GLOBAL" +_ACEOF + + cat >>confdefs.h <<_ACEOF +#define RUNTIME_GLOBAL_AFTER "$RUNTIME_GLOBAL_AFTER" +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-modified-by argument" >&5 +$as_echo_n "checking --with-modified-by argument... " >&6; } + +# Check whether --with-modified-by was given. +if test "${with_modified_by+set}" = set; then : + withval=$with_modified_by; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $withval" >&5 +$as_echo "$withval" >&6; }; cat >>confdefs.h <<_ACEOF +#define MODIFIED_BY "$withval" +_ACEOF + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if character set is EBCDIC" >&5 +$as_echo_n "checking if character set is EBCDIC... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + /* TryCompile function for CharSet. + Treat any failure as ASCII for compatibility with existing art. + Use compile-time rather than run-time tests for cross-compiler + tolerance. */ +#if '0'!=240 +make an error "Character set is not EBCDIC" +#endif + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # TryCompile action if true +cf_cv_ebcdic=yes +else + # TryCompile action if false +cf_cv_ebcdic=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# end of TryCompile ]) +# end of CacheVal CvEbcdic +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cf_cv_ebcdic" >&5 +$as_echo "$cf_cv_ebcdic" >&6; } +case "$cf_cv_ebcdic" in #(vi + yes) $as_echo "#define EBCDIC 1" >>confdefs.h + + line_break='"\\n"' + ;; + *) line_break='"\\012"';; +esac + + +if test "$cf_cv_ebcdic" = "yes"; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for z/OS Unix" >&5 +$as_echo_n "checking for z/OS Unix... " >&6; } +case `uname` in + OS/390) zOSUnix="yes"; + if test "$CC" = "cc"; then + ccm="$_CC_CCMODE" + ccn="CC" + else + if test "$CC" = "c89"; then + ccm="$_CC_C89MODE" + ccn="C89" + else + ccm=1 + fi + fi + if test "$ccm" != "1"; then + echo "" + echo "------------------------------------------" + echo " On z/OS Unix, the environment variable" + echo " _CC_${ccn}MODE must be set to \"1\"!" + echo " Do:" + echo " export _CC_${ccn}MODE=1" + echo " and then call configure again." + echo "------------------------------------------" + exit 1 + fi + # Set CFLAGS for configure process. + # This will be reset later for config.mk. + # Use haltonmsg to force error for missing H files. + CFLAGS="$CFLAGS -D_ALL_SOURCE -Wc,float(ieee),haltonmsg(3296)"; + LDFLAGS="$LDFLAGS -Wl,EDIT=NO" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + ;; + *) zOSUnix="no"; + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; +esac +fi + +if test "$zOSUnix" = "yes"; then + QUOTESED="sed -e 's/[\\\\\"]/\\\\\\\\&/g' -e 's/\\\\\\\\\"/\"/' -e 's/\\\\\\\\\";\$\$/\";/'" +else + QUOTESED="sed -e 's/[\\\\\"]/\\\\&/g' -e 's/\\\\\"/\"/' -e 's/\\\\\";\$\$/\";/'" +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-smack argument" >&5 +$as_echo_n "checking --disable-smack argument... " >&6; } +# Check whether --enable-smack was given. +if test "${enable_smack+set}" = set; then : + enableval=$enable_smack; +else + enable_smack="yes" +fi + +if test "$enable_smack" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ac_fn_c_check_header_mongrel "$LINENO" "linux/xattr.h" "ac_cv_header_linux_xattr_h" "$ac_includes_default" +if test "x$ac_cv_header_linux_xattr_h" = xyes; then : + true +else + enable_smack="no" +fi + + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +if test "$enable_smack" = "yes"; then + ac_fn_c_check_header_mongrel "$LINENO" "attr/xattr.h" "ac_cv_header_attr_xattr_h" "$ac_includes_default" +if test "x$ac_cv_header_attr_xattr_h" = xyes; then : + true +else + enable_smack="no" +fi + + +fi +if test "$enable_smack" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XATTR_NAME_SMACKEXEC in linux/xattr.h" >&5 +$as_echo_n "checking for XATTR_NAME_SMACKEXEC in linux/xattr.h... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "XATTR_NAME_SMACKEXEC" >/dev/null 2>&1; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; enable_smack="no" +fi +rm -f conftest* + +fi +if test "$enable_smack" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setxattr in -lattr" >&5 +$as_echo_n "checking for setxattr in -lattr... " >&6; } +if ${ac_cv_lib_attr_setxattr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lattr $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char setxattr (); +int +main () +{ +return setxattr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_attr_setxattr=yes +else + ac_cv_lib_attr_setxattr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_attr_setxattr" >&5 +$as_echo "$ac_cv_lib_attr_setxattr" >&6; } +if test "x$ac_cv_lib_attr_setxattr" = xyes; then : + LIBS="$LIBS -lattr" + found_smack="yes" + $as_echo "#define HAVE_SMACK 1" >>confdefs.h + +fi + +fi + +if test "x$found_smack" = "x"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-selinux argument" >&5 +$as_echo_n "checking --disable-selinux argument... " >&6; } + # Check whether --enable-selinux was given. +if test "${enable_selinux+set}" = set; then : + enableval=$enable_selinux; +else + enable_selinux="yes" +fi + + if test "$enable_selinux" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for is_selinux_enabled in -lselinux" >&5 +$as_echo_n "checking for is_selinux_enabled in -lselinux... " >&6; } +if ${ac_cv_lib_selinux_is_selinux_enabled+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lselinux $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char is_selinux_enabled (); +int +main () +{ +return is_selinux_enabled (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_selinux_is_selinux_enabled=yes +else + ac_cv_lib_selinux_is_selinux_enabled=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_selinux_is_selinux_enabled" >&5 +$as_echo "$ac_cv_lib_selinux_is_selinux_enabled" >&6; } +if test "x$ac_cv_lib_selinux_is_selinux_enabled" = xyes; then : + ac_fn_c_check_header_mongrel "$LINENO" "selinux/selinux.h" "ac_cv_header_selinux_selinux_h" "$ac_includes_default" +if test "x$ac_cv_header_selinux_selinux_h" = xyes; then : + LIBS="$LIBS -lselinux" + $as_echo "#define HAVE_SELINUX 1" >>confdefs.h + +fi + + +fi + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-features argument" >&5 +$as_echo_n "checking --with-features argument... " >&6; } + +# Check whether --with-features was given. +if test "${with_features+set}" = set; then : + withval=$with_features; features="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $features" >&5 +$as_echo "$features" >&6; } +else + features="huge"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to huge" >&5 +$as_echo "Defaulting to huge" >&6; } +fi + + +dovimdiff="" +dogvimdiff="" +case "$features" in + tiny) $as_echo "#define FEAT_TINY 1" >>confdefs.h + ;; + small) $as_echo "#define FEAT_SMALL 1" >>confdefs.h + ;; + normal) $as_echo "#define FEAT_NORMAL 1" >>confdefs.h + dovimdiff="installvimdiff"; + dogvimdiff="installgvimdiff" ;; + big) $as_echo "#define FEAT_BIG 1" >>confdefs.h + dovimdiff="installvimdiff"; + dogvimdiff="installgvimdiff" ;; + huge) $as_echo "#define FEAT_HUGE 1" >>confdefs.h + dovimdiff="installvimdiff"; + dogvimdiff="installgvimdiff" ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Sorry, $features is not supported" >&5 +$as_echo "Sorry, $features is not supported" >&6; } ;; +esac + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-compiledby argument" >&5 +$as_echo_n "checking --with-compiledby argument... " >&6; } + +# Check whether --with-compiledby was given. +if test "${with_compiledby+set}" = set; then : + withval=$with_compiledby; compiledby="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $withval" >&5 +$as_echo "$withval" >&6; } +else + compiledby=""; { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-xsmp argument" >&5 +$as_echo_n "checking --disable-xsmp argument... " >&6; } +# Check whether --enable-xsmp was given. +if test "${enable_xsmp+set}" = set; then : + enableval=$enable_xsmp; +else + enable_xsmp="yes" +fi + + +if test "$enable_xsmp" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-xsmp-interact argument" >&5 +$as_echo_n "checking --disable-xsmp-interact argument... " >&6; } + # Check whether --enable-xsmp-interact was given. +if test "${enable_xsmp_interact+set}" = set; then : + enableval=$enable_xsmp_interact; +else + enable_xsmp_interact="yes" +fi + + if test "$enable_xsmp_interact" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + $as_echo "#define USE_XSMP_INTERACT 1" >>confdefs.h + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-luainterp argument" >&5 +$as_echo_n "checking --enable-luainterp argument... " >&6; } +# Check whether --enable-luainterp was given. +if test "${enable_luainterp+set}" = set; then : + enableval=$enable_luainterp; +else + enable_luainterp="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_luainterp" >&5 +$as_echo "$enable_luainterp" >&6; } + +if test "$enable_luainterp" = "yes" -o "$enable_luainterp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + as_fn_error $? "cannot use Lua with tiny or small features" "$LINENO" 5 + fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-lua-prefix argument" >&5 +$as_echo_n "checking --with-lua-prefix argument... " >&6; } + +# Check whether --with-lua_prefix was given. +if test "${with_lua_prefix+set}" = set; then : + withval=$with_lua_prefix; with_lua_prefix="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_lua_prefix" >&5 +$as_echo "$with_lua_prefix" >&6; } +else + with_lua_prefix="";{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "X$with_lua_prefix" != "X"; then + vi_cv_path_lua_pfx="$with_lua_prefix" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking LUA_PREFIX environment var" >&5 +$as_echo_n "checking LUA_PREFIX environment var... " >&6; } + if test "X$LUA_PREFIX" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"$LUA_PREFIX\"" >&5 +$as_echo "\"$LUA_PREFIX\"" >&6; } + vi_cv_path_lua_pfx="$LUA_PREFIX" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not set, default to /usr" >&5 +$as_echo "not set, default to /usr" >&6; } + vi_cv_path_lua_pfx="/usr" + fi + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-luajit" >&5 +$as_echo_n "checking --with-luajit... " >&6; } + +# Check whether --with-luajit was given. +if test "${with_luajit+set}" = set; then : + withval=$with_luajit; vi_cv_with_luajit="$withval" +else + vi_cv_with_luajit="no" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_with_luajit" >&5 +$as_echo "$vi_cv_with_luajit" >&6; } + + LUA_INC= + if test "X$vi_cv_path_lua_pfx" != "X"; then + if test "x$vi_cv_with_luajit" != "xno"; then + # Extract the first word of "luajit", so it can be a program name with args. +set dummy luajit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_luajit+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_luajit in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_luajit="$vi_cv_path_luajit" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_luajit="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_luajit=$ac_cv_path_vi_cv_path_luajit +if test -n "$vi_cv_path_luajit"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_luajit" >&5 +$as_echo "$vi_cv_path_luajit" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "X$vi_cv_path_luajit" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking LuaJIT version" >&5 +$as_echo_n "checking LuaJIT version... " >&6; } +if ${vi_cv_version_luajit+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_version_luajit=`${vi_cv_path_luajit} -v 2>&1 | sed 's/LuaJIT \([0-9.]*\)\.[0-9]\(-[a-z0-9]*\)* .*/\1/'` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_version_luajit" >&5 +$as_echo "$vi_cv_version_luajit" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Lua version of LuaJIT" >&5 +$as_echo_n "checking Lua version of LuaJIT... " >&6; } +if ${vi_cv_version_lua_luajit+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_version_lua_luajit=`${vi_cv_path_luajit} -e "print(_VERSION)" | sed 's/.* //'` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_version_lua_luajit" >&5 +$as_echo "$vi_cv_version_lua_luajit" >&6; } + vi_cv_path_lua="$vi_cv_path_luajit" + vi_cv_version_lua="$vi_cv_version_lua_luajit" + fi + else + # Extract the first word of "lua", so it can be a program name with args. +set dummy lua; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_plain_lua+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_plain_lua in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_plain_lua="$vi_cv_path_plain_lua" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_plain_lua="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_plain_lua=$ac_cv_path_vi_cv_path_plain_lua +if test -n "$vi_cv_path_plain_lua"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_plain_lua" >&5 +$as_echo "$vi_cv_path_plain_lua" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "X$vi_cv_path_plain_lua" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Lua version" >&5 +$as_echo_n "checking Lua version... " >&6; } +if ${vi_cv_version_plain_lua+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_version_plain_lua=`${vi_cv_path_plain_lua} -e "print(_VERSION)" | sed 's/.* //'` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_version_plain_lua" >&5 +$as_echo "$vi_cv_version_plain_lua" >&6; } + fi + vi_cv_path_lua="$vi_cv_path_plain_lua" + vi_cv_version_lua="$vi_cv_version_plain_lua" + fi + if test "x$vi_cv_with_luajit" != "xno" && test "X$vi_cv_version_luajit" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if lua.h can be found in $vi_cv_path_lua_pfx/include/luajit-$vi_cv_version_luajit" >&5 +$as_echo_n "checking if lua.h can be found in $vi_cv_path_lua_pfx/include/luajit-$vi_cv_version_luajit... " >&6; } + if test -f "$vi_cv_path_lua_pfx/include/luajit-$vi_cv_version_luajit/lua.h"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + LUA_INC=/luajit-$vi_cv_version_luajit + fi + fi + if test "X$LUA_INC" = "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if lua.h can be found in $vi_cv_path_lua_pfx/include" >&5 +$as_echo_n "checking if lua.h can be found in $vi_cv_path_lua_pfx/include... " >&6; } + if test -f "$vi_cv_path_lua_pfx/include/lua.h"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if lua.h can be found in $vi_cv_path_lua_pfx/include/lua$vi_cv_version_lua" >&5 +$as_echo_n "checking if lua.h can be found in $vi_cv_path_lua_pfx/include/lua$vi_cv_version_lua... " >&6; } + if test -f "$vi_cv_path_lua_pfx/include/lua$vi_cv_version_lua/lua.h"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + LUA_INC=/lua$vi_cv_version_lua + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + vi_cv_path_lua_pfx= + fi + fi + fi + fi + + if test "X$vi_cv_path_lua_pfx" != "X"; then + if test "x$vi_cv_with_luajit" != "xno"; then + multiarch=`dpkg-architecture -qDEB_HOST_MULTIARCH 2> /dev/null` + if test "X$multiarch" != "X"; then + lib_multiarch="lib/${multiarch}" + else + lib_multiarch="lib" + fi + if test "X$vi_cv_version_lua" = "X"; then + LUA_LIBS="-L${vi_cv_path_lua_pfx}/${lib_multiarch} -lluajit" + else + LUA_LIBS="-L${vi_cv_path_lua_pfx}/${lib_multiarch} -lluajit-$vi_cv_version_lua" + fi + else + if test "X$LUA_INC" != "X"; then + LUA_LIBS="-L${vi_cv_path_lua_pfx}/lib -llua$vi_cv_version_lua" + else + LUA_LIBS="-L${vi_cv_path_lua_pfx}/lib -llua" + fi + fi + if test "$enable_luainterp" = "dynamic"; then + lua_ok="yes" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if link with ${LUA_LIBS} is sane" >&5 +$as_echo_n "checking if link with ${LUA_LIBS} is sane... " >&6; } + libs_save=$LIBS + LIBS="$LIBS $LUA_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; lua_ok="yes" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; lua_ok="no"; LUA_LIBS="" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$libs_save + fi + if test "x$lua_ok" = "xyes"; then + LUA_CFLAGS="-I${vi_cv_path_lua_pfx}/include${LUA_INC}" + LUA_SRC="if_lua.c" + LUA_OBJ="objects/if_lua.o" + LUA_PRO="if_lua.pro" + $as_echo "#define FEAT_LUA 1" >>confdefs.h + + fi + if test "$enable_luainterp" = "dynamic"; then + if test "x$vi_cv_with_luajit" != "xno"; then + luajit="jit" + fi + if test -f "${vi_cv_path_lua_pfx}/bin/cyglua-${vi_cv_version_lua}.dll"; then + vi_cv_dll_name_lua="cyglua-${vi_cv_version_lua}.dll" + else + if test "x$MACOS_X" = "xyes"; then + ext="dylib" + indexes="" + else + ext="so" + indexes=".0 .1 .2 .3 .4 .5 .6 .7 .8 .9" + multiarch=`dpkg-architecture -qDEB_HOST_MULTIARCH 2> /dev/null` + if test "X$multiarch" != "X"; then + lib_multiarch="lib/${multiarch}" + fi + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if liblua${luajit}*.${ext}* can be found in $vi_cv_path_lua_pfx" >&5 +$as_echo_n "checking if liblua${luajit}*.${ext}* can be found in $vi_cv_path_lua_pfx... " >&6; } + for subdir in "${lib_multiarch}" lib64 lib; do + if test -z "$subdir"; then + continue + fi + for sover in "${vi_cv_version_lua}.${ext}" "-${vi_cv_version_lua}.${ext}" \ + ".${vi_cv_version_lua}.${ext}" ".${ext}.${vi_cv_version_lua}"; do + for i in $indexes ""; do + if test -f "${vi_cv_path_lua_pfx}/${subdir}/liblua${luajit}${sover}$i"; then + sover2="$i" + break 3 + fi + done + done + sover="" + done + if test "X$sover" = "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + lua_ok="no" + vi_cv_dll_name_lua="liblua${luajit}.${ext}" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + lua_ok="yes" + vi_cv_dll_name_lua="liblua${luajit}${sover}$sover2" + fi + fi + $as_echo "#define DYNAMIC_LUA 1" >>confdefs.h + + LUA_LIBS="" + LUA_CFLAGS="-DDYNAMIC_LUA_DLL=\\\"${vi_cv_dll_name_lua}\\\" $LUA_CFLAGS" + fi + if test "X$LUA_CFLAGS$LUA_LIBS" != "X" && \ + test "x$MACOS_X" = "xyes" && test "x$vi_cv_with_luajit" != "xno" && \ + test "`(uname -m) 2>/dev/null`" = "x86_64"; then + LUA_LIBS="-pagezero_size 10000 -image_base 100000000 $LUA_LIBS" + fi + fi + if test "$fail_if_missing" = "yes" -a "$lua_ok" != "yes"; then + as_fn_error $? "could not configure lua" "$LINENO" 5 + fi + + + + + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-mzschemeinterp argument" >&5 +$as_echo_n "checking --enable-mzschemeinterp argument... " >&6; } +# Check whether --enable-mzschemeinterp was given. +if test "${enable_mzschemeinterp+set}" = set; then : + enableval=$enable_mzschemeinterp; +else + enable_mzschemeinterp="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_mzschemeinterp" >&5 +$as_echo "$enable_mzschemeinterp" >&6; } + +if test "$enable_mzschemeinterp" = "yes"; then + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-plthome argument" >&5 +$as_echo_n "checking --with-plthome argument... " >&6; } + +# Check whether --with-plthome was given. +if test "${with_plthome+set}" = set; then : + withval=$with_plthome; with_plthome="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_plthome" >&5 +$as_echo "$with_plthome" >&6; } +else + with_plthome="";{ $as_echo "$as_me:${as_lineno-$LINENO}: result: \"no\"" >&5 +$as_echo "\"no\"" >&6; } +fi + + + if test "X$with_plthome" != "X"; then + vi_cv_path_mzscheme_pfx="$with_plthome" + vi_cv_path_mzscheme="${vi_cv_path_mzscheme_pfx}/bin/mzscheme" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking PLTHOME environment var" >&5 +$as_echo_n "checking PLTHOME environment var... " >&6; } + if test "X$PLTHOME" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"$PLTHOME\"" >&5 +$as_echo "\"$PLTHOME\"" >&6; } + vi_cv_path_mzscheme_pfx="$PLTHOME" + vi_cv_path_mzscheme="${vi_cv_path_mzscheme_pfx}/bin/mzscheme" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not set" >&5 +$as_echo "not set" >&6; } + # Extract the first word of "mzscheme", so it can be a program name with args. +set dummy mzscheme; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_mzscheme+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_mzscheme in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_mzscheme="$vi_cv_path_mzscheme" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_mzscheme="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_mzscheme=$ac_cv_path_vi_cv_path_mzscheme +if test -n "$vi_cv_path_mzscheme"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_mzscheme" >&5 +$as_echo "$vi_cv_path_mzscheme" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + if test "X$vi_cv_path_mzscheme" != "X"; then + lsout=`ls -l $vi_cv_path_mzscheme` + if echo "$lsout" | grep -e '->' >/dev/null 2>/dev/null; then + vi_cv_path_mzscheme=`echo "$lsout" | sed 's/.*-> \(.*\)/\1/'` + fi + fi + + if test "X$vi_cv_path_mzscheme" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking MzScheme install prefix" >&5 +$as_echo_n "checking MzScheme install prefix... " >&6; } +if ${vi_cv_path_mzscheme_pfx+:} false; then : + $as_echo_n "(cached) " >&6 +else + echo "(display (simplify-path \ + (build-path (call-with-values \ + (lambda () (split-path (find-system-path (quote exec-file)))) \ + (lambda (base name must-be-dir?) base)) (quote up))))" > mzdirs.scm + vi_cv_path_mzscheme_pfx=`${vi_cv_path_mzscheme} -r mzdirs.scm | \ + sed -e 's+/$++'` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_mzscheme_pfx" >&5 +$as_echo "$vi_cv_path_mzscheme_pfx" >&6; } + rm -f mzdirs.scm + fi + fi + fi + + if test "X$vi_cv_path_mzscheme_pfx" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for racket include directory" >&5 +$as_echo_n "checking for racket include directory... " >&6; } + SCHEME_INC=`${vi_cv_path_mzscheme} -e '(require setup/dirs)(let ((p (find-include-dir))) (when (path? p) (display p)))'` + if test "X$SCHEME_INC" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${SCHEME_INC}" >&5 +$as_echo "${SCHEME_INC}" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if scheme.h can be found in $vi_cv_path_mzscheme_pfx/include" >&5 +$as_echo_n "checking if scheme.h can be found in $vi_cv_path_mzscheme_pfx/include... " >&6; } + if test -f "$vi_cv_path_mzscheme_pfx/include/scheme.h"; then + SCHEME_INC=${vi_cv_path_mzscheme_pfx}/include + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if scheme.h can be found in $vi_cv_path_mzscheme_pfx/include/plt" >&5 +$as_echo_n "checking if scheme.h can be found in $vi_cv_path_mzscheme_pfx/include/plt... " >&6; } + if test -f "$vi_cv_path_mzscheme_pfx/include/plt/scheme.h"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SCHEME_INC=${vi_cv_path_mzscheme_pfx}/include/plt + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if scheme.h can be found in $vi_cv_path_mzscheme_pfx/include/racket" >&5 +$as_echo_n "checking if scheme.h can be found in $vi_cv_path_mzscheme_pfx/include/racket... " >&6; } + if test -f "$vi_cv_path_mzscheme_pfx/include/racket/scheme.h"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SCHEME_INC=${vi_cv_path_mzscheme_pfx}/include/racket + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if scheme.h can be found in /usr/include/plt/" >&5 +$as_echo_n "checking if scheme.h can be found in /usr/include/plt/... " >&6; } + if test -f /usr/include/plt/scheme.h; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SCHEME_INC=/usr/include/plt + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if scheme.h can be found in /usr/include/racket/" >&5 +$as_echo_n "checking if scheme.h can be found in /usr/include/racket/... " >&6; } + if test -f /usr/include/racket/scheme.h; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SCHEME_INC=/usr/include/racket + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + vi_cv_path_mzscheme_pfx= + fi + fi + fi + fi + fi + fi + fi + + if test "X$vi_cv_path_mzscheme_pfx" != "X"; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for racket lib directory" >&5 +$as_echo_n "checking for racket lib directory... " >&6; } + SCHEME_LIB=`${vi_cv_path_mzscheme} -e '(require setup/dirs)(let ((p (find-lib-dir))) (when (path? p) (display p)))'` + if test "X$SCHEME_LIB" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${SCHEME_LIB}" >&5 +$as_echo "${SCHEME_LIB}" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + fi + + for path in "${vi_cv_path_mzscheme_pfx}/lib" "${SCHEME_LIB}"; do + if test "X$path" != "X"; then + if test "x$MACOS_X" = "xyes"; then + MZSCHEME_LIBS="-framework Racket" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libmzscheme3m.a"; then + MZSCHEME_LIBS="${path}/libmzscheme3m.a" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libracket3m.a"; then + MZSCHEME_LIBS="${path}/libracket3m.a" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libracket.a"; then + MZSCHEME_LIBS="${path}/libracket.a ${path}/libmzgc.a" + elif test -f "${path}/libmzscheme.a"; then + MZSCHEME_LIBS="${path}/libmzscheme.a ${path}/libmzgc.a" + else + if test -f "${path}/libmzscheme3m.so"; then + MZSCHEME_LIBS="-L${path} -lmzscheme3m" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libracket3m.so"; then + MZSCHEME_LIBS="-L${path} -lracket3m" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libracket.so"; then + MZSCHEME_LIBS="-L${path} -lracket -lmzgc" + else + if test "$path" != "$SCHEME_LIB"; then + continue + fi + MZSCHEME_LIBS="-L${path} -lmzscheme -lmzgc" + fi + if test "$GCC" = yes; then + MZSCHEME_LIBS="${MZSCHEME_LIBS} -Wl,-rpath -Wl,${path}" + elif test "`(uname) 2>/dev/null`" = SunOS && + uname -r | grep '^5' >/dev/null; then + MZSCHEME_LIBS="${MZSCHEME_LIBS} -R ${path}" + fi + fi + fi + if test "X$MZSCHEME_LIBS" != "X"; then + break + fi + done + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if racket requires -pthread" >&5 +$as_echo_n "checking if racket requires -pthread... " >&6; } + if test "X$SCHEME_LIB" != "X" && $FGREP -e -pthread "$SCHEME_LIB/buildinfo" >/dev/null ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + MZSCHEME_LIBS="${MZSCHEME_LIBS} -pthread" + MZSCHEME_CFLAGS="${MZSCHEME_CFLAGS} -pthread" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for racket config directory" >&5 +$as_echo_n "checking for racket config directory... " >&6; } + SCHEME_CONFIGDIR=`${vi_cv_path_mzscheme} -e '(require setup/dirs)(let ((p (find-config-dir))) (when (path? p) (display p)))'` + if test "X$SCHEME_CONFIGDIR" != "X"; then + MZSCHEME_CFLAGS="${MZSCHEME_CFLAGS} -DMZSCHEME_CONFIGDIR='\"${SCHEME_CONFIGDIR}\"'" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${SCHEME_CONFIGDIR}" >&5 +$as_echo "${SCHEME_CONFIGDIR}" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for racket collects directory" >&5 +$as_echo_n "checking for racket collects directory... " >&6; } + SCHEME_COLLECTS=`${vi_cv_path_mzscheme} -e '(require setup/dirs)(let ((p (find-collects-dir))) (when (path? p) (let-values (((base _1 _2) (split-path p))) (display base))))'` + if test "X$SCHEME_COLLECTS" = "X"; then + if test -d "$vi_cv_path_mzscheme_pfx/lib/plt/collects"; then + SCHEME_COLLECTS=$vi_cv_path_mzscheme_pfx/lib/plt/ + else + if test -d "$vi_cv_path_mzscheme_pfx/lib/racket/collects"; then + SCHEME_COLLECTS=$vi_cv_path_mzscheme_pfx/lib/racket/ + else + if test -d "$vi_cv_path_mzscheme_pfx/share/racket/collects"; then + SCHEME_COLLECTS=$vi_cv_path_mzscheme_pfx/share/racket/ + else + if test -d "$vi_cv_path_mzscheme_pfx/collects"; then + SCHEME_COLLECTS=$vi_cv_path_mzscheme_pfx/ + fi + fi + fi + fi + fi + if test "X$SCHEME_COLLECTS" != "X" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${SCHEME_COLLECTS}" >&5 +$as_echo "${SCHEME_COLLECTS}" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mzscheme_base.c" >&5 +$as_echo_n "checking for mzscheme_base.c... " >&6; } + if test -f "${SCHEME_COLLECTS}collects/scheme/base.ss" ; then + MZSCHEME_EXTRA="mzscheme_base.c" + MZSCHEME_MZC="${vi_cv_path_mzscheme_pfx}/bin/mzc" + MZSCHEME_MOD="++lib scheme/base" + else + if test -f "${SCHEME_COLLECTS}collects/scheme/base.rkt" ; then + MZSCHEME_EXTRA="mzscheme_base.c" + MZSCHEME_MZC="${vi_cv_path_mzscheme_pfx}/bin/mzc" + MZSCHEME_MOD="++lib scheme/base" + else + if test -f "${SCHEME_COLLECTS}collects/racket/base.rkt" ; then + MZSCHEME_EXTRA="mzscheme_base.c" + MZSCHEME_MZC="${vi_cv_path_mzscheme_pfx}/bin/raco ctool" + MZSCHEME_MOD="" + fi + fi + fi + if test "X$MZSCHEME_EXTRA" != "X" ; then + MZSCHEME_CFLAGS="${MZSCHEME_CFLAGS} -DINCLUDE_MZSCHEME_BASE" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: needed" >&5 +$as_echo "needed" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not needed" >&5 +$as_echo "not needed" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ffi_type_void in -lffi" >&5 +$as_echo_n "checking for ffi_type_void in -lffi... " >&6; } +if ${ac_cv_lib_ffi_ffi_type_void+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lffi $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ffi_type_void (); +int +main () +{ +return ffi_type_void (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ffi_ffi_type_void=yes +else + ac_cv_lib_ffi_ffi_type_void=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ffi_ffi_type_void" >&5 +$as_echo "$ac_cv_lib_ffi_ffi_type_void" >&6; } +if test "x$ac_cv_lib_ffi_ffi_type_void" = xyes; then : + MZSCHEME_LIBS="$MZSCHEME_LIBS -lffi" +fi + + + MZSCHEME_CFLAGS="${MZSCHEME_CFLAGS} -I${SCHEME_INC} \ + -DMZSCHEME_COLLECTS='\"${SCHEME_COLLECTS}collects\"'" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compile and link flags for MzScheme are sane" >&5 +$as_echo_n "checking if compile and link flags for MzScheme are sane... " >&6; } + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $MZSCHEME_CFLAGS" + LIBS="$LIBS $MZSCHEME_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; mzs_ok=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no: MZSCHEME DISABLED" >&5 +$as_echo "no: MZSCHEME DISABLED" >&6; }; mzs_ok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + LIBS=$libs_save + if test $mzs_ok = yes; then + MZSCHEME_SRC="if_mzsch.c" + MZSCHEME_OBJ="objects/if_mzsch.o" + MZSCHEME_PRO="if_mzsch.pro" + $as_echo "#define FEAT_MZSCHEME 1" >>confdefs.h + + else + MZSCHEME_CFLAGS= + MZSCHEME_LIBS= + MZSCHEME_EXTRA= + MZSCHEME_MZC= + fi + fi + + + + + + + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-perlinterp argument" >&5 +$as_echo_n "checking --enable-perlinterp argument... " >&6; } +# Check whether --enable-perlinterp was given. +if test "${enable_perlinterp+set}" = set; then : + enableval=$enable_perlinterp; +else + enable_perlinterp="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_perlinterp" >&5 +$as_echo "$enable_perlinterp" >&6; } +if test "$enable_perlinterp" = "yes" -o "$enable_perlinterp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + as_fn_error $? "cannot use Perl with tiny or small features" "$LINENO" 5 + fi + + # Extract the first word of "perl", so it can be a program name with args. +set dummy perl; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_perl+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_perl in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_perl="$vi_cv_path_perl" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_perl="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_perl=$ac_cv_path_vi_cv_path_perl +if test -n "$vi_cv_path_perl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_perl" >&5 +$as_echo "$vi_cv_path_perl" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "X$vi_cv_path_perl" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Perl version" >&5 +$as_echo_n "checking Perl version... " >&6; } + if $vi_cv_path_perl -e 'require 5.003_01' >/dev/null 2>/dev/null; then + eval `$vi_cv_path_perl -V:usethreads` + eval `$vi_cv_path_perl -V:libperl` + if test "X$usethreads" = "XUNKNOWN" -o "X$usethreads" = "Xundef"; then + badthreads=no + else + if $vi_cv_path_perl -e 'require 5.6.0' >/dev/null 2>/dev/null; then + eval `$vi_cv_path_perl -V:use5005threads` + if test "X$use5005threads" = "XUNKNOWN" -o "X$use5005threads" = "Xundef"; then + badthreads=no + else + badthreads=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: >>> Perl > 5.6 with 5.5 threads cannot be used <<<" >&5 +$as_echo ">>> Perl > 5.6 with 5.5 threads cannot be used <<<" >&6; } + fi + else + badthreads=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: >>> Perl 5.5 with threads cannot be used <<<" >&5 +$as_echo ">>> Perl 5.5 with threads cannot be used <<<" >&6; } + fi + fi + if test $badthreads = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: OK" >&5 +$as_echo "OK" >&6; } + eval `$vi_cv_path_perl -V:shrpenv` + if test "X$shrpenv" = "XUNKNOWN"; then # pre 5.003_04 + shrpenv="" + fi + vi_cv_perllib=`$vi_cv_path_perl -MConfig -e 'print $Config{privlibexp}'` + + vi_cv_perl_extutils=unknown_perl_extutils_path + for extutils_rel_path in ExtUtils vendor_perl/ExtUtils; do + xsubpp_path="$vi_cv_perllib/$extutils_rel_path/xsubpp" + if test -f "$xsubpp_path"; then + vi_cv_perl_xsubpp="$xsubpp_path" + fi + done + + perlcppflags=`$vi_cv_path_perl -Mlib=$srcdir -MExtUtils::Embed \ + -e 'ccflags;perl_inc;print"\n"' | sed -e 's/-fno[^ ]*//' \ + -e 's/-fdebug-prefix-map[^ ]*//g' \ + -e 's/-pipe //' \ + -e 's/-W[^ ]*//g' \ + -e 's/-D_FORTIFY_SOURCE=.//g'` + perllibs=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed -e 'ldopts' | \ + sed -e '/Warning/d' -e '/Note (probably harmless)/d' \ + -e 's/-bE:perl.exp//' -e 's/-lc //'` + perlldflags=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed \ + -e 'ccdlflags' | sed -e 's/-bE:perl.exp//'` + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compile and link flags for Perl are sane" >&5 +$as_echo_n "checking if compile and link flags for Perl are sane... " >&6; } + cflags_save=$CFLAGS + libs_save=$LIBS + ldflags_save=$LDFLAGS + CFLAGS="$CFLAGS $perlcppflags" + LIBS="$LIBS $perllibs" + perlldflags=`echo "$perlldflags" | sed -e 's/^ *//g'` + LDFLAGS="$perlldflags $LDFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; perl_ok=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no: PERL DISABLED" >&5 +$as_echo "no: PERL DISABLED" >&6; }; perl_ok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + LIBS=$libs_save + LDFLAGS=$ldflags_save + if test $perl_ok = yes; then + if test "X$perlcppflags" != "X"; then + PERL_CFLAGS=$perlcppflags + fi + if test "X$perlldflags" != "X"; then + if test "X`echo \"$LDFLAGS\" | $FGREP -e \"$perlldflags\"`" = "X"; then + LDFLAGS="$perlldflags $LDFLAGS" + fi + fi + PERL_LIBS=$perllibs + PERL_SRC="auto/if_perl.c if_perlsfio.c" + PERL_OBJ="objects/if_perl.o objects/if_perlsfio.o" + PERL_PRO="if_perl.pro if_perlsfio.pro" + $as_echo "#define FEAT_PERL 1" >>confdefs.h + + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: >>> too old; need Perl version 5.003_01 or later <<<" >&5 +$as_echo ">>> too old; need Perl version 5.003_01 or later <<<" >&6; } + fi + fi + + if test "x$MACOS_X" = "xyes"; then + dir=/System/Library/Perl + darwindir=$dir/darwin + if test -d $darwindir; then + PERL=/usr/bin/perl + else + dir=/System/Library/Perl/5.8.1 + darwindir=$dir/darwin-thread-multi-2level + if test -d $darwindir; then + PERL=/usr/bin/perl + fi + fi + if test -n "$PERL"; then + PERL_DIR="$dir" + PERL_CFLAGS="-DFEAT_PERL -I$darwindir/CORE" + PERL_OBJ="objects/if_perl.o objects/if_perlsfio.o $darwindir/auto/DynaLoader/DynaLoader.a" + PERL_LIBS="-L$darwindir/CORE -lperl" + fi + PERL_LIBS=`echo "$PERL_LIBS" | sed -e 's/-arch\ ppc//' -e 's/-arch\ i386//' -e 's/-arch\ x86_64//'` + PERL_CFLAGS=`echo "$PERL_CFLAGS" | sed -e 's/-arch\ ppc//' -e 's/-arch\ i386//' -e 's/-arch\ x86_64//'` + fi + if test "$enable_perlinterp" = "dynamic"; then + if test "$perl_ok" = "yes" -a "X$libperl" != "X"; then + $as_echo "#define DYNAMIC_PERL 1" >>confdefs.h + + PERL_CFLAGS="-DDYNAMIC_PERL_DLL=\\\"$libperl\\\" $PERL_CFLAGS" + fi + fi + + if test "$fail_if_missing" = "yes" -a "$perl_ok" != "yes"; then + as_fn_error $? "could not configure perl" "$LINENO" 5 + fi +fi + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-pythoninterp argument" >&5 +$as_echo_n "checking --enable-pythoninterp argument... " >&6; } +# Check whether --enable-pythoninterp was given. +if test "${enable_pythoninterp+set}" = set; then : + enableval=$enable_pythoninterp; +else + enable_pythoninterp="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_pythoninterp" >&5 +$as_echo "$enable_pythoninterp" >&6; } +if test "$enable_pythoninterp" = "yes" -o "$enable_pythoninterp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + as_fn_error $? "cannot use Python with tiny or small features" "$LINENO" 5 + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-python-command argument" >&5 +$as_echo_n "checking --with-python-command argument... " >&6; } + + +# Check whether --with-python-command was given. +if test "${with_python_command+set}" = set; then : + withval=$with_python_command; vi_cv_path_python="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python" >&5 +$as_echo "$vi_cv_path_python" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "X$vi_cv_path_python" = "X"; then + for ac_prog in python2 python +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_python+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_python in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_python="$vi_cv_path_python" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_python="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_python=$ac_cv_path_vi_cv_path_python +if test -n "$vi_cv_path_python"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python" >&5 +$as_echo "$vi_cv_path_python" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$vi_cv_path_python" && break +done + + fi + if test "X$vi_cv_path_python" != "X"; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python version" >&5 +$as_echo_n "checking Python version... " >&6; } +if ${vi_cv_var_python_version+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_var_python_version=` + ${vi_cv_path_python} -c 'import sys; print sys.version[:3]'` + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_var_python_version" >&5 +$as_echo "$vi_cv_var_python_version" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python is 2.3 or better" >&5 +$as_echo_n "checking Python is 2.3 or better... " >&6; } + if ${vi_cv_path_python} -c \ + "import sys; sys.exit(${vi_cv_var_python_version} < 2.3)" + then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yep" >&5 +$as_echo "yep" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's install prefix" >&5 +$as_echo_n "checking Python's install prefix... " >&6; } +if ${vi_cv_path_python_pfx+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_path_python_pfx=` + ${vi_cv_path_python} -c \ + "import sys; print sys.prefix"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python_pfx" >&5 +$as_echo "$vi_cv_path_python_pfx" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's execution prefix" >&5 +$as_echo_n "checking Python's execution prefix... " >&6; } +if ${vi_cv_path_python_epfx+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_path_python_epfx=` + ${vi_cv_path_python} -c \ + "import sys; print sys.exec_prefix"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python_epfx" >&5 +$as_echo "$vi_cv_path_python_epfx" >&6; } + + + if ${vi_cv_path_pythonpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_path_pythonpath=` + unset PYTHONPATH; + ${vi_cv_path_python} -c \ + "import sys, string; print string.join(sys.path,':')"` +fi + + + + +# Check whether --with-python-config-dir was given. +if test "${with_python_config_dir+set}" = set; then : + withval=$with_python_config_dir; vi_cv_path_python_conf="${withval}"; have_python_config_dir=1 +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's configuration directory" >&5 +$as_echo_n "checking Python's configuration directory... " >&6; } +if ${vi_cv_path_python_conf+:} false; then : + $as_echo_n "(cached) " >&6 +else + + vi_cv_path_python_conf= + d=`${vi_cv_path_python} -c "import distutils.sysconfig; print distutils.sysconfig.get_config_var('LIBPL')"` + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python_conf="$d" + else + for path in "${vi_cv_path_python_pfx}" "${vi_cv_path_python_epfx}"; do + for subdir in lib64 lib share; do + d="${path}/${subdir}/python${vi_cv_var_python_version}/config" + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python_conf="$d" + fi + done + done + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python_conf" >&5 +$as_echo "$vi_cv_path_python_conf" >&6; } + + PYTHON_CONFDIR="${vi_cv_path_python_conf}" + + if test "X$PYTHON_CONFDIR" = "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: can't find it!" >&5 +$as_echo "can't find it!" >&6; } + else + + if ${vi_cv_path_python_plibs+:} false; then : + $as_echo_n "(cached) " >&6 +else + + pwd=`pwd` + tmp_mkf="$pwd/config-PyMake$$" + cat -- "${PYTHON_CONFDIR}/Makefile" - <<'eof' >"${tmp_mkf}" +__: + @echo "python_BASEMODLIBS='$(BASEMODLIBS)'" + @echo "python_LIBS='$(LIBS)'" + @echo "python_SYSLIBS='$(SYSLIBS)'" + @echo "python_LINKFORSHARED='$(LINKFORSHARED)'" + @echo "python_DLLLIBRARY='$(DLLLIBRARY)'" + @echo "python_INSTSONAME='$(INSTSONAME)'" + @echo "python_PYTHONFRAMEWORK='$(PYTHONFRAMEWORK)'" + @echo "python_PYTHONFRAMEWORKPREFIX='$(PYTHONFRAMEWORKPREFIX)'" + @echo "python_PYTHONFRAMEWORKINSTALLDIR='$(PYTHONFRAMEWORKINSTALLDIR)'" +eof + eval "`cd ${PYTHON_CONFDIR} && make -f "${tmp_mkf}" __ | sed '/ directory /d'`" + rm -f -- "${tmp_mkf}" + if test "x$MACOS_X" = "xyes" && test -n "${python_PYTHONFRAMEWORK}" && ${vi_cv_path_python} -c \ + "import sys; sys.exit(${vi_cv_var_python_version} < 2.3)"; then + vi_cv_path_python_plibs="-framework Python" + if test "x${vi_cv_path_python}" != "x/usr/bin/python" && test -n "${python_PYTHONFRAMEWORKPREFIX}"; then + vi_cv_path_python_plibs="-F${python_PYTHONFRAMEWORKPREFIX} -framework Python" + fi + else + vi_cv_path_python_plibs="-L${PYTHON_CONFDIR} -lpython${vi_cv_var_python_version}" + if test -n "${python_LINKFORSHARED}" && test -n "${python_PYTHONFRAMEWORKPREFIX}"; then + python_link_symbol=`echo ${python_LINKFORSHARED} | sed 's/\([^ \t][^ \t]*[ \t][ \t]*[^ \t][^ \t]*\)[ \t].*/\1/'` + python_link_path=`echo ${python_LINKFORSHARED} | sed 's/\([^ \t][^ \t]*[ \t][ \t]*[^ \t][^ \t]*\)[ \t][ \t]*\(.*\)/\2/'` + if test -n "${python_link_path}" && ! test -x "${python_link_path}"; then + python_link_path="${python_PYTHONFRAMEWORKPREFIX}/${python_link_path}" + if test -n "${python_link_path}" && ! test -x "${python_link_path}"; then + python_link_path="${python_PYTHONFRAMEWORKINSTALLDIR}/Versions/${vi_cv_var_python_version}/${python_PYTHONFRAMEWORK}" + fi + python_LINKFORSHARED="${python_link_symbol} ${python_link_path}" + fi + fi + vi_cv_path_python_plibs="${vi_cv_path_python_plibs} ${python_BASEMODLIBS} ${python_LIBS} ${python_SYSLIBS} ${python_LINKFORSHARED}" + vi_cv_path_python_plibs=`echo $vi_cv_path_python_plibs | sed s/-ltermcap//` + fi + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's dll name" >&5 +$as_echo_n "checking Python's dll name... " >&6; } +if ${vi_cv_dll_name_python+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "X$python_DLLLIBRARY" != "X"; then + vi_cv_dll_name_python="$python_DLLLIBRARY" + else + vi_cv_dll_name_python="$python_INSTSONAME" + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_dll_name_python" >&5 +$as_echo "$vi_cv_dll_name_python" >&6; } + + PYTHON_LIBS="${vi_cv_path_python_plibs}" + if test "${vi_cv_path_python_pfx}" = "${vi_cv_path_python_epfx}"; then + PYTHON_CFLAGS="-I${vi_cv_path_python_pfx}/include/python${vi_cv_var_python_version}" + else + PYTHON_CFLAGS="-I${vi_cv_path_python_pfx}/include/python${vi_cv_var_python_version} -I${vi_cv_path_python_epfx}/include/python${vi_cv_var_python_version}" + fi + if test "X$have_python_config_dir" = "X1" -a "$enable_pythoninterp" = "dynamic"; then + PYTHON_CFLAGS="${PYTHON_CFLAGS} -DPYTHON_HOME='\"${vi_cv_path_python_pfx}\"'" + + fi + PYTHON_SRC="if_python.c" + PYTHON_OBJ="objects/if_python.o" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if -pthread should be used" >&5 +$as_echo_n "checking if -pthread should be used... " >&6; } + threadsafe_flag= + thread_lib= + if test "`(uname) 2>/dev/null`" != Darwin; then + test "$GCC" = yes && threadsafe_flag="-pthread" + if test "`(uname) 2>/dev/null`" = FreeBSD; then + threadsafe_flag="-D_THREAD_SAFE" + thread_lib="-pthread" + fi + if test "`(uname) 2>/dev/null`" = SunOS; then + threadsafe_flag="-pthreads" + fi + fi + libs_save_old=$LIBS + if test -n "$threadsafe_flag"; then + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $threadsafe_flag" + LIBS="$LIBS $thread_lib" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; PYTHON_CFLAGS="$PYTHON_CFLAGS $threadsafe_flag" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; LIBS=$libs_save_old + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compile and link flags for Python are sane" >&5 +$as_echo_n "checking if compile and link flags for Python are sane... " >&6; } + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON_CFLAGS" + LIBS="$LIBS $PYTHON_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; python_ok=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no: PYTHON DISABLED" >&5 +$as_echo "no: PYTHON DISABLED" >&6; }; python_ok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + LIBS=$libs_save + if test $python_ok = yes; then + $as_echo "#define FEAT_PYTHON 1" >>confdefs.h + + else + LIBS=$libs_save_old + PYTHON_SRC= + PYTHON_OBJ= + PYTHON_LIBS= + PYTHON_CFLAGS= + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: too old" >&5 +$as_echo "too old" >&6; } + fi + fi + + if test "$fail_if_missing" = "yes" -a "$python_ok" != "yes"; then + as_fn_error $? "could not configure python" "$LINENO" 5 + fi +fi + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-python3interp argument" >&5 +$as_echo_n "checking --enable-python3interp argument... " >&6; } +# Check whether --enable-python3interp was given. +if test "${enable_python3interp+set}" = set; then : + enableval=$enable_python3interp; +else + enable_python3interp="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_python3interp" >&5 +$as_echo "$enable_python3interp" >&6; } +if test "$enable_python3interp" = "yes" -o "$enable_python3interp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + as_fn_error $? "cannot use Python with tiny or small features" "$LINENO" 5 + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-python3-command argument" >&5 +$as_echo_n "checking --with-python3-command argument... " >&6; } + + +# Check whether --with-python3-command was given. +if test "${with_python3_command+set}" = set; then : + withval=$with_python3_command; vi_cv_path_python3="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python3" >&5 +$as_echo "$vi_cv_path_python3" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "X$vi_cv_path_python3" = "X"; then + for ac_prog in python3 python +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_python3+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_python3 in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_python3="$vi_cv_path_python3" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_python3="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_python3=$ac_cv_path_vi_cv_path_python3 +if test -n "$vi_cv_path_python3"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python3" >&5 +$as_echo "$vi_cv_path_python3" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$vi_cv_path_python3" && break +done + + fi + if test "X$vi_cv_path_python3" != "X"; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python version" >&5 +$as_echo_n "checking Python version... " >&6; } +if ${vi_cv_var_python3_version+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_var_python3_version=` + ${vi_cv_path_python3} -c 'import sys; print(sys.version[:3])'` + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_var_python3_version" >&5 +$as_echo "$vi_cv_var_python3_version" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python is 3.0 or better" >&5 +$as_echo_n "checking Python is 3.0 or better... " >&6; } + if ${vi_cv_path_python3} -c \ + "import sys; sys.exit(${vi_cv_var_python3_version} < 3.0)" + then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yep" >&5 +$as_echo "yep" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's abiflags" >&5 +$as_echo_n "checking Python's abiflags... " >&6; } +if ${vi_cv_var_python3_abiflags+:} false; then : + $as_echo_n "(cached) " >&6 +else + + vi_cv_var_python3_abiflags= + if ${vi_cv_path_python3} -c \ + "import sys; sys.exit(${vi_cv_var_python3_version} < 3.2)" + then + vi_cv_var_python3_abiflags=`${vi_cv_path_python3} -c \ + "import sys; print(sys.abiflags)"` + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_var_python3_abiflags" >&5 +$as_echo "$vi_cv_var_python3_abiflags" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's install prefix" >&5 +$as_echo_n "checking Python's install prefix... " >&6; } +if ${vi_cv_path_python3_pfx+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_path_python3_pfx=` + ${vi_cv_path_python3} -c \ + "import sys; print(sys.prefix)"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python3_pfx" >&5 +$as_echo "$vi_cv_path_python3_pfx" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's execution prefix" >&5 +$as_echo_n "checking Python's execution prefix... " >&6; } +if ${vi_cv_path_python3_epfx+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_path_python3_epfx=` + ${vi_cv_path_python3} -c \ + "import sys; print(sys.exec_prefix)"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python3_epfx" >&5 +$as_echo "$vi_cv_path_python3_epfx" >&6; } + + + if ${vi_cv_path_python3path+:} false; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_path_python3path=` + unset PYTHONPATH; + ${vi_cv_path_python3} -c \ + "import sys, string; print(':'.join(sys.path))"` +fi + + + + +# Check whether --with-python3-config-dir was given. +if test "${with_python3_config_dir+set}" = set; then : + withval=$with_python3_config_dir; vi_cv_path_python3_conf="${withval}"; have_python3_config_dir=1 +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's configuration directory" >&5 +$as_echo_n "checking Python's configuration directory... " >&6; } +if ${vi_cv_path_python3_conf+:} false; then : + $as_echo_n "(cached) " >&6 +else + + vi_cv_path_python3_conf= + config_dir="config-${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}" + d=`${vi_cv_path_python3} -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('LIBPL'))"` + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python3_conf="$d" + else + for path in "${vi_cv_path_python3_pfx}" "${vi_cv_path_python3_epfx}"; do + for subdir in lib64 lib share; do + d="${path}/${subdir}/python${vi_cv_var_python3_version}/${config_dir}" + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python3_conf="$d" + fi + done + done + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python3_conf" >&5 +$as_echo "$vi_cv_path_python3_conf" >&6; } + + PYTHON3_CONFDIR="${vi_cv_path_python3_conf}" + + if test "X$PYTHON3_CONFDIR" = "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: can't find it!" >&5 +$as_echo "can't find it!" >&6; } + else + + if ${vi_cv_path_python3_plibs+:} false; then : + $as_echo_n "(cached) " >&6 +else + + pwd=`pwd` + tmp_mkf="$pwd/config-PyMake$$" + cat -- "${PYTHON3_CONFDIR}/Makefile" - <<'eof' >"${tmp_mkf}" +__: + @echo "python3_BASEMODLIBS='$(BASEMODLIBS)'" + @echo "python3_LIBS='$(LIBS)'" + @echo "python3_SYSLIBS='$(SYSLIBS)'" + @echo "python3_DLLLIBRARY='$(DLLLIBRARY)'" + @echo "python3_INSTSONAME='$(INSTSONAME)'" +eof + eval "`cd ${PYTHON3_CONFDIR} && make -f "${tmp_mkf}" __ | sed '/ directory /d'`" + rm -f -- "${tmp_mkf}" + vi_cv_path_python3_plibs="-L${PYTHON3_CONFDIR} -lpython${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}" + vi_cv_path_python3_plibs="${vi_cv_path_python3_plibs} ${python3_BASEMODLIBS} ${python3_LIBS} ${python3_SYSLIBS}" + vi_cv_path_python3_plibs=`echo $vi_cv_path_python3_plibs | sed s/-ltermcap//` + vi_cv_path_python3_plibs=`echo $vi_cv_path_python3_plibs | sed s/-lffi//` + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python3's dll name" >&5 +$as_echo_n "checking Python3's dll name... " >&6; } +if ${vi_cv_dll_name_python3+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "X$python3_DLLLIBRARY" != "X"; then + vi_cv_dll_name_python3="$python3_DLLLIBRARY" + else + vi_cv_dll_name_python3="$python3_INSTSONAME" + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_dll_name_python3" >&5 +$as_echo "$vi_cv_dll_name_python3" >&6; } + + PYTHON3_LIBS="${vi_cv_path_python3_plibs}" + if test "${vi_cv_path_python3_pfx}" = "${vi_cv_path_python3_epfx}"; then + PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}" + else + PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags} -I${vi_cv_path_python3_epfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}" + fi + if test "X$have_python3_config_dir" = "X1" -a "$enable_python3interp" = "dynamic"; then + PYTHON3_CFLAGS="${PYTHON3_CFLAGS} -DPYTHON3_HOME='L\"${vi_cv_path_python3_pfx}\"'" + fi + PYTHON3_SRC="if_python3.c" + PYTHON3_OBJ="objects/if_python3.o" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if -pthread should be used" >&5 +$as_echo_n "checking if -pthread should be used... " >&6; } + threadsafe_flag= + thread_lib= + if test "`(uname) 2>/dev/null`" != Darwin; then + test "$GCC" = yes && threadsafe_flag="-pthread" + if test "`(uname) 2>/dev/null`" = FreeBSD; then + threadsafe_flag="-D_THREAD_SAFE" + thread_lib="-pthread" + fi + if test "`(uname) 2>/dev/null`" = SunOS; then + threadsafe_flag="-pthreads" + fi + fi + libs_save_old=$LIBS + if test -n "$threadsafe_flag"; then + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $threadsafe_flag" + LIBS="$LIBS $thread_lib" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; PYTHON3_CFLAGS="$PYTHON3_CFLAGS $threadsafe_flag" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; LIBS=$libs_save_old + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compile and link flags for Python 3 are sane" >&5 +$as_echo_n "checking if compile and link flags for Python 3 are sane... " >&6; } + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON3_CFLAGS" + LIBS="$LIBS $PYTHON3_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; python3_ok=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no: PYTHON3 DISABLED" >&5 +$as_echo "no: PYTHON3 DISABLED" >&6; }; python3_ok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + LIBS=$libs_save + if test "$python3_ok" = yes; then + $as_echo "#define FEAT_PYTHON3 1" >>confdefs.h + + else + LIBS=$libs_save_old + PYTHON3_SRC= + PYTHON3_OBJ= + PYTHON3_LIBS= + PYTHON3_CFLAGS= + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: too old" >&5 +$as_echo "too old" >&6; } + fi + fi + if test "$fail_if_missing" = "yes" -a "$python3_ok" != "yes"; then + as_fn_error $? "could not configure python3" "$LINENO" 5 + fi +fi + + + + + + +if test "$python_ok" = yes && test "$python3_ok" = yes; then + $as_echo "#define DYNAMIC_PYTHON 1" >>confdefs.h + + $as_echo "#define DYNAMIC_PYTHON3 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can do without RTLD_GLOBAL for Python" >&5 +$as_echo_n "checking whether we can do without RTLD_GLOBAL for Python... " >&6; } + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $PYTHON_CFLAGS" + libs_save=$LIBS + LIBS="-ldl $LIBS" + if test "$cross_compiling" = yes; then : + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run test program while cross compiling +See \`config.log' for more details" "$LINENO" 5; } +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + /* If this program fails, then RTLD_GLOBAL is needed. + * RTLD_GLOBAL will be used and then it is not possible to + * have both python versions enabled in the same vim instance. + * Only the first python version used will be switched on. + */ + + int no_rtl_global_needed_for(char *python_instsoname, char *prefix) + { + int needed = 0; + void* pylib = dlopen(python_instsoname, RTLD_LAZY|RTLD_LOCAL); + if (pylib != 0) + { + void (*pfx)(char *home) = dlsym(pylib, "Py_SetPythonHome"); + void (*init)(void) = dlsym(pylib, "Py_Initialize"); + int (*simple)(char*) = dlsym(pylib, "PyRun_SimpleString"); + void (*final)(void) = dlsym(pylib, "Py_Finalize"); + (*pfx)(prefix); + (*init)(); + needed = (*simple)("import termios") == -1; + (*final)(); + dlclose(pylib); + } + return !needed; + } + + int main(int argc, char** argv) + { + int not_needed = 0; + if (no_rtl_global_needed_for("${vi_cv_dll_name_python}", "${vi_cv_path_python_pfx}")) + not_needed = 1; + return !not_needed; + } +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; };$as_echo "#define PY_NO_RTLD_GLOBAL 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + CFLAGS=$cflags_save + LIBS=$libs_save + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can do without RTLD_GLOBAL for Python3" >&5 +$as_echo_n "checking whether we can do without RTLD_GLOBAL for Python3... " >&6; } + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $PYTHON3_CFLAGS" + libs_save=$LIBS + LIBS="-ldl $LIBS" + if test "$cross_compiling" = yes; then : + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run test program while cross compiling +See \`config.log' for more details" "$LINENO" 5; } +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + #include + /* If this program fails, then RTLD_GLOBAL is needed. + * RTLD_GLOBAL will be used and then it is not possible to + * have both python versions enabled in the same vim instance. + * Only the first python version used will be switched on. + */ + + int no_rtl_global_needed_for(char *python_instsoname, wchar_t *prefix) + { + int needed = 0; + void* pylib = dlopen(python_instsoname, RTLD_LAZY|RTLD_LOCAL); + if (pylib != 0) + { + void (*pfx)(wchar_t *home) = dlsym(pylib, "Py_SetPythonHome"); + void (*init)(void) = dlsym(pylib, "Py_Initialize"); + int (*simple)(char*) = dlsym(pylib, "PyRun_SimpleString"); + void (*final)(void) = dlsym(pylib, "Py_Finalize"); + (*pfx)(prefix); + (*init)(); + needed = (*simple)("import termios") == -1; + (*final)(); + dlclose(pylib); + } + return !needed; + } + + int main(int argc, char** argv) + { + int not_needed = 0; + if (no_rtl_global_needed_for("${vi_cv_dll_name_python3}", L"${vi_cv_path_python3_pfx}")) + not_needed = 1; + return !not_needed; + } +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; };$as_echo "#define PY3_NO_RTLD_GLOBAL 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + CFLAGS=$cflags_save + LIBS=$libs_save + + PYTHON_SRC="if_python.c" + PYTHON_OBJ="objects/if_python.o" + PYTHON_CFLAGS="$PYTHON_CFLAGS -DDYNAMIC_PYTHON_DLL=\\\"${vi_cv_dll_name_python}\\\"" + PYTHON_LIBS= + PYTHON3_SRC="if_python3.c" + PYTHON3_OBJ="objects/if_python3.o" + PYTHON3_CFLAGS="$PYTHON3_CFLAGS -DDYNAMIC_PYTHON3_DLL=\\\"${vi_cv_dll_name_python3}\\\"" + PYTHON3_LIBS= +elif test "$python_ok" = yes && test "$enable_pythoninterp" = "dynamic"; then + $as_echo "#define DYNAMIC_PYTHON 1" >>confdefs.h + + PYTHON_SRC="if_python.c" + PYTHON_OBJ="objects/if_python.o" + PYTHON_CFLAGS="$PYTHON_CFLAGS -DDYNAMIC_PYTHON_DLL=\\\"${vi_cv_dll_name_python}\\\"" + PYTHON_LIBS= +elif test "$python_ok" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if -fPIE can be added for Python" >&5 +$as_echo_n "checking if -fPIE can be added for Python... " >&6; } + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON_CFLAGS -fPIE" + LIBS="$LIBS $PYTHON_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; fpie_ok=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; fpie_ok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + LIBS=$libs_save + if test $fpie_ok = yes; then + PYTHON_CFLAGS="$PYTHON_CFLAGS -fPIE" + fi +elif test "$python3_ok" = yes && test "$enable_python3interp" = "dynamic"; then + $as_echo "#define DYNAMIC_PYTHON3 1" >>confdefs.h + + PYTHON3_SRC="if_python3.c" + PYTHON3_OBJ="objects/if_python3.o" + PYTHON3_CFLAGS="$PYTHON3_CFLAGS -DDYNAMIC_PYTHON3_DLL=\\\"${vi_cv_dll_name_python3}\\\"" + PYTHON3_LIBS= +elif test "$python3_ok" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if -fPIE can be added for Python3" >&5 +$as_echo_n "checking if -fPIE can be added for Python3... " >&6; } + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON3_CFLAGS -fPIE" + LIBS="$LIBS $PYTHON3_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; fpie_ok=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; fpie_ok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + LIBS=$libs_save + if test $fpie_ok = yes; then + PYTHON3_CFLAGS="$PYTHON3_CFLAGS -fPIE" + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-tclinterp argument" >&5 +$as_echo_n "checking --enable-tclinterp argument... " >&6; } +# Check whether --enable-tclinterp was given. +if test "${enable_tclinterp+set}" = set; then : + enableval=$enable_tclinterp; +else + enable_tclinterp="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_tclinterp" >&5 +$as_echo "$enable_tclinterp" >&6; } + +if test "$enable_tclinterp" = "yes" -o "$enable_tclinterp" = "dynamic"; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-tclsh argument" >&5 +$as_echo_n "checking --with-tclsh argument... " >&6; } + +# Check whether --with-tclsh was given. +if test "${with_tclsh+set}" = set; then : + withval=$with_tclsh; tclsh_name="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tclsh_name" >&5 +$as_echo "$tclsh_name" >&6; } +else + tclsh_name="tclsh8.5"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + # Extract the first word of "$tclsh_name", so it can be a program name with args. +set dummy $tclsh_name; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_tcl+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_tcl in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_tcl="$vi_cv_path_tcl" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_tcl="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_tcl=$ac_cv_path_vi_cv_path_tcl +if test -n "$vi_cv_path_tcl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_tcl" >&5 +$as_echo "$vi_cv_path_tcl" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + if test "X$vi_cv_path_tcl" = "X" -a $tclsh_name = "tclsh8.5"; then + tclsh_name="tclsh8.4" + # Extract the first word of "$tclsh_name", so it can be a program name with args. +set dummy $tclsh_name; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_tcl+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_tcl in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_tcl="$vi_cv_path_tcl" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_tcl="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_tcl=$ac_cv_path_vi_cv_path_tcl +if test -n "$vi_cv_path_tcl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_tcl" >&5 +$as_echo "$vi_cv_path_tcl" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi + if test "X$vi_cv_path_tcl" = "X" -a $tclsh_name = "tclsh8.4"; then + tclsh_name="tclsh8.2" + # Extract the first word of "$tclsh_name", so it can be a program name with args. +set dummy $tclsh_name; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_tcl+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_tcl in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_tcl="$vi_cv_path_tcl" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_tcl="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_tcl=$ac_cv_path_vi_cv_path_tcl +if test -n "$vi_cv_path_tcl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_tcl" >&5 +$as_echo "$vi_cv_path_tcl" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi + if test "X$vi_cv_path_tcl" = "X" -a $tclsh_name = "tclsh8.2"; then + tclsh_name="tclsh8.0" + # Extract the first word of "$tclsh_name", so it can be a program name with args. +set dummy $tclsh_name; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_tcl+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_tcl in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_tcl="$vi_cv_path_tcl" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_tcl="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_tcl=$ac_cv_path_vi_cv_path_tcl +if test -n "$vi_cv_path_tcl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_tcl" >&5 +$as_echo "$vi_cv_path_tcl" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi + if test "X$vi_cv_path_tcl" = "X"; then + tclsh_name="tclsh" + # Extract the first word of "$tclsh_name", so it can be a program name with args. +set dummy $tclsh_name; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_tcl+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_tcl in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_tcl="$vi_cv_path_tcl" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_tcl="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_tcl=$ac_cv_path_vi_cv_path_tcl +if test -n "$vi_cv_path_tcl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_tcl" >&5 +$as_echo "$vi_cv_path_tcl" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi + if test "X$vi_cv_path_tcl" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Tcl version" >&5 +$as_echo_n "checking Tcl version... " >&6; } + if echo 'exit [expr [info tclversion] < 8.0]' | "$vi_cv_path_tcl" - ; then + tclver=`echo 'puts [info tclversion]' | $vi_cv_path_tcl -` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tclver - OK" >&5 +$as_echo "$tclver - OK" >&6; }; + tclloc=`echo 'set l [info library];set i [string last lib $l];incr i -2;puts [string range $l 0 $i]' | $vi_cv_path_tcl -` + tcldll=`echo 'puts libtcl[info tclversion][info sharedlibextension]' | $vi_cv_path_tcl -` + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for location of Tcl include" >&5 +$as_echo_n "checking for location of Tcl include... " >&6; } + if test "x$MACOS_X" != "xyes"; then + tclinc="$tclloc/include $tclloc/include/tcl $tclloc/include/tcl$tclver /usr/local/include /usr/local/include/tcl$tclver /usr/include /usr/include/tcl$tclver" + else + tclinc="/System/Library/Frameworks/Tcl.framework/Headers" + fi + TCL_INC= + for try in $tclinc; do + if test -f "$try/tcl.h"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $try/tcl.h" >&5 +$as_echo "$try/tcl.h" >&6; } + TCL_INC=$try + break + fi + done + if test -z "$TCL_INC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } + SKIP_TCL=YES + fi + if test -z "$SKIP_TCL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for location of tclConfig.sh script" >&5 +$as_echo_n "checking for location of tclConfig.sh script... " >&6; } + if test "x$MACOS_X" != "xyes"; then + tclcnf=`echo $tclinc | sed s/include/lib/g` + tclcnf="$tclcnf `echo $tclinc | sed s/include/lib64/g`" + else + tclcnf="/System/Library/Frameworks/Tcl.framework" + fi + for try in $tclcnf; do + if test -f "$try/tclConfig.sh"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $try/tclConfig.sh" >&5 +$as_echo "$try/tclConfig.sh" >&6; } + . "$try/tclConfig.sh" + if test "$enable_tclinterp" = "dynamic"; then + TCL_LIBS=`eval echo "$TCL_STUB_LIB_SPEC $TCL_LIBS"` + else + TCL_LIBS=`eval echo "$TCL_LIB_SPEC $TCL_LIBS"` + fi + TCL_DEFS=`echo $TCL_DEFS | sed -e 's/\\\\ /\\\\X/g' | tr ' ' '\012' | sed -e '/^[^-]/d' -e '/^-[^D]/d' -e '/-D[^_]/d' -e 's/-D_/ -D_/' | tr '\012' ' ' | sed -e 's/\\\\X/\\\\ /g'` + break + fi + done + if test -z "$TCL_LIBS"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl library by myself" >&5 +$as_echo_n "checking for Tcl library by myself... " >&6; } + tcllib=`echo $tclinc | sed s/include/lib/g` + tcllib="$tcllib `echo $tclinc | sed s/include/lib64/g`" + for ext in .so .a ; do + for ver in "" $tclver ; do + for try in $tcllib ; do + trylib=tcl$ver$ext + if test -f "$try/lib$trylib" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $try/lib$trylib" >&5 +$as_echo "$try/lib$trylib" >&6; } + TCL_LIBS="-L\"$try\" -ltcl$ver -ldl -lm" + if test "`(uname) 2>/dev/null`" = SunOS && + uname -r | grep '^5' >/dev/null; then + TCL_LIBS="$TCL_LIBS -R $try" + fi + break 3 + fi + done + done + done + if test -z "$TCL_LIBS"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } + SKIP_TCL=YES + fi + fi + if test -z "$SKIP_TCL"; then + $as_echo "#define FEAT_TCL 1" >>confdefs.h + + TCL_SRC=if_tcl.c + TCL_OBJ=objects/if_tcl.o + TCL_PRO=if_tcl.pro + TCL_CFLAGS="-I$TCL_INC $TCL_DEFS" + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: too old; need Tcl version 8.0 or later" >&5 +$as_echo "too old; need Tcl version 8.0 or later" >&6; } + fi + fi + if test "$enable_tclinterp" = "dynamic"; then + if test "X$TCL_SRC" != "X" -a "X$tcldll" != "X"; then + $as_echo "#define DYNAMIC_TCL 1" >>confdefs.h + + TCL_CFLAGS="-DDYNAMIC_TCL_DLL=\\\"$tcldll\\\" -DDYNAMIC_TCL_VER=\\\"$tclver\\\" $TCL_CFLAGS" + fi + fi + if test "$fail_if_missing" = "yes" -a -z "$TCL_SRC"; then + as_fn_error $? "could not configure Tcl" "$LINENO" 5 + fi +fi + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-rubyinterp argument" >&5 +$as_echo_n "checking --enable-rubyinterp argument... " >&6; } +# Check whether --enable-rubyinterp was given. +if test "${enable_rubyinterp+set}" = set; then : + enableval=$enable_rubyinterp; +else + enable_rubyinterp="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_rubyinterp" >&5 +$as_echo "$enable_rubyinterp" >&6; } +if test "$enable_rubyinterp" = "yes" -o "$enable_rubyinterp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + as_fn_error $? "cannot use Ruby with tiny or small features" "$LINENO" 5 + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-ruby-command argument" >&5 +$as_echo_n "checking --with-ruby-command argument... " >&6; } + + +# Check whether --with-ruby-command was given. +if test "${with_ruby_command+set}" = set; then : + withval=$with_ruby_command; RUBY_CMD="$withval"; vi_cv_path_ruby="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY_CMD" >&5 +$as_echo "$RUBY_CMD" >&6; } +else + RUBY_CMD="ruby"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: defaulting to $RUBY_CMD" >&5 +$as_echo "defaulting to $RUBY_CMD" >&6; } +fi + + # Extract the first word of "$RUBY_CMD", so it can be a program name with args. +set dummy $RUBY_CMD; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_vi_cv_path_ruby+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_ruby in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_ruby="$vi_cv_path_ruby" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_vi_cv_path_ruby="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_ruby=$ac_cv_path_vi_cv_path_ruby +if test -n "$vi_cv_path_ruby"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_ruby" >&5 +$as_echo "$vi_cv_path_ruby" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "X$vi_cv_path_ruby" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Ruby version" >&5 +$as_echo_n "checking Ruby version... " >&6; } + if $vi_cv_path_ruby -e '(VERSION rescue RUBY_VERSION) >= "1.6.0" or exit 1' >/dev/null 2>/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: OK" >&5 +$as_echo "OK" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Ruby rbconfig" >&5 +$as_echo_n "checking Ruby rbconfig... " >&6; } + ruby_rbconfig="RbConfig" + if ! $vi_cv_path_ruby -r rbconfig -e 'RbConfig' >/dev/null 2>/dev/null; then + ruby_rbconfig="Config" + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ruby_rbconfig" >&5 +$as_echo "$ruby_rbconfig" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Ruby header files" >&5 +$as_echo_n "checking Ruby header files... " >&6; } + rubyhdrdir=`$vi_cv_path_ruby -r mkmf -e "print $ruby_rbconfig::CONFIG['rubyhdrdir'] || $ruby_rbconfig::CONFIG['archdir'] || \\$hdrdir" 2>/dev/null` + if test "X$rubyhdrdir" != "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $rubyhdrdir" >&5 +$as_echo "$rubyhdrdir" >&6; } + RUBY_CFLAGS="-I$rubyhdrdir" + rubyarchdir=`$vi_cv_path_ruby -r rbconfig -e "print ($ruby_rbconfig::CONFIG.has_key? 'rubyarchhdrdir') ? $ruby_rbconfig::CONFIG['rubyarchhdrdir'] : '$rubyhdrdir/'+$ruby_rbconfig::CONFIG['arch']"` + if test -d "$rubyarchdir"; then + RUBY_CFLAGS="$RUBY_CFLAGS -I$rubyarchdir" + fi + rubyversion=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig::CONFIG['ruby_version'].gsub(/\./, '')[0,2]"` + if test "X$rubyversion" = "X"; then + rubyversion=`$vi_cv_path_ruby -e "print ((VERSION rescue RUBY_VERSION)).gsub(/\./, '')[0,2]"` + fi + RUBY_CFLAGS="$RUBY_CFLAGS -DRUBY_VERSION=$rubyversion" + rubylibs=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig::CONFIG['LIBS']"` + if test "X$rubylibs" != "X"; then + RUBY_LIBS="$rubylibs" + fi + librubyarg=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig.expand($ruby_rbconfig::CONFIG['LIBRUBYARG'])"` + librubya=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig.expand($ruby_rbconfig::CONFIG['LIBRUBY_A'])"` + rubylibdir=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig.expand($ruby_rbconfig::CONFIG['libdir'])"` + if test -f "$rubylibdir/$librubya"; then + librubyarg="$librubyarg" + RUBY_LIBS="$RUBY_LIBS -L$rubylibdir" + elif test "$librubyarg" = "libruby.a"; then + librubyarg="-lruby" + RUBY_LIBS="$RUBY_LIBS -L$rubylibdir" + fi + + if test "X$librubyarg" != "X"; then + RUBY_LIBS="$librubyarg $RUBY_LIBS" + fi + rubyldflags=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig::CONFIG['LDFLAGS']"` + if test "X$rubyldflags" != "X"; then + rubyldflags=`echo "$rubyldflags" | sed -e 's/-arch\ ppc//' -e 's/-arch\ i386//' -e 's/-arch\ x86_64//'` + if test "X$rubyldflags" != "X"; then + if test "X`echo \"$LDFLAGS\" | $FGREP -e \"$rubyldflags\"`" = "X"; then + LDFLAGS="$rubyldflags $LDFLAGS" + fi + fi + fi + RUBY_SRC="if_ruby.c" + RUBY_OBJ="objects/if_ruby.o" + RUBY_PRO="if_ruby.pro" + $as_echo "#define FEAT_RUBY 1" >>confdefs.h + + if test "$enable_rubyinterp" = "dynamic"; then + libruby_soname=`$vi_cv_path_ruby -r rbconfig -e "puts $ruby_rbconfig::CONFIG['LIBRUBY_ALIASES'].split[0]"` + if test -z "$libruby_soname"; then + libruby_soname=`$vi_cv_path_ruby -r rbconfig -e "puts $ruby_rbconfig::CONFIG['LIBRUBY_SO']"` + fi + $as_echo "#define DYNAMIC_RUBY 1" >>confdefs.h + + RUBY_CFLAGS="-DDYNAMIC_RUBY_DLL=\\\"$libruby_soname\\\" -DDYNAMIC_RUBY_VER=$rubyversion $RUBY_CFLAGS" + RUBY_LIBS= + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found; disabling Ruby" >&5 +$as_echo "not found; disabling Ruby" >&6; } + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: too old; need Ruby version 1.6.0 or later" >&5 +$as_echo "too old; need Ruby version 1.6.0 or later" >&6; } + fi + fi + + if test "$fail_if_missing" = "yes" -a -z "$RUBY_OBJ"; then + as_fn_error $? "could not configure Ruby" "$LINENO" 5 + fi +fi + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-cscope argument" >&5 +$as_echo_n "checking --enable-cscope argument... " >&6; } +# Check whether --enable-cscope was given. +if test "${enable_cscope+set}" = set; then : + enableval=$enable_cscope; +else + enable_cscope="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_cscope" >&5 +$as_echo "$enable_cscope" >&6; } +if test "$enable_cscope" = "yes"; then + $as_echo "#define FEAT_CSCOPE 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-netbeans argument" >&5 +$as_echo_n "checking --disable-netbeans argument... " >&6; } +# Check whether --enable-netbeans was given. +if test "${enable_netbeans+set}" = set; then : + enableval=$enable_netbeans; +else + enable_netbeans="yes" +fi + +if test "$enable_netbeans" = "yes"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot use NetBeans with tiny or small features" >&5 +$as_echo "cannot use NetBeans with tiny or small features" >&6; } + enable_netbeans="no" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-channel argument" >&5 +$as_echo_n "checking --disable-channel argument... " >&6; } +# Check whether --enable-channel was given. +if test "${enable_channel+set}" = set; then : + enableval=$enable_channel; +else + enable_channel="yes" +fi + +if test "$enable_channel" = "yes"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot use channels with tiny or small features" >&5 +$as_echo "cannot use channels with tiny or small features" >&6; } + enable_channel="no" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi +else + if test "$enable_netbeans" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, netbeans also disabled" >&5 +$as_echo "yes, netbeans also disabled" >&6; } + enable_netbeans="no" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi +fi + +if test "$enable_channel" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 +$as_echo_n "checking for socket in -lsocket... " >&6; } +if ${ac_cv_lib_socket_socket+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char socket (); +int +main () +{ +return socket (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_socket_socket=yes +else + ac_cv_lib_socket_socket=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 +$as_echo "$ac_cv_lib_socket_socket" >&6; } +if test "x$ac_cv_lib_socket_socket" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSOCKET 1 +_ACEOF + + LIBS="-lsocket $LIBS" + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5 +$as_echo_n "checking for gethostbyname in -lnsl... " >&6; } +if ${ac_cv_lib_nsl_gethostbyname+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char gethostbyname (); +int +main () +{ +return gethostbyname (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_nsl_gethostbyname=yes +else + ac_cv_lib_nsl_gethostbyname=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5 +$as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; } +if test "x$ac_cv_lib_nsl_gethostbyname" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBNSL 1 +_ACEOF + + LIBS="-lnsl $LIBS" + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiling with process communication is possible" >&5 +$as_echo_n "checking whether compiling with process communication is possible... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + /* Check bitfields */ + struct nbbuf { + unsigned int initDone:1; + unsigned short signmaplen; + }; + +int +main () +{ + + /* Check creating a socket. */ + struct sockaddr_in server; + (void)socket(AF_INET, SOCK_STREAM, 0); + (void)htons(100); + (void)gethostbyname("microsoft.com"); + if (errno == ECONNREFUSED) + (void)connect(1, (struct sockaddr *)&server, sizeof(server)); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; enable_netbeans="no"; enable_channel="no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +if test "$enable_netbeans" = "yes"; then + $as_echo "#define FEAT_NETBEANS_INTG 1" >>confdefs.h + + NETBEANS_SRC="netbeans.c" + + NETBEANS_OBJ="objects/netbeans.o" + +fi +if test "$enable_channel" = "yes"; then + $as_echo "#define FEAT_JOB_CHANNEL 1" >>confdefs.h + + CHANNEL_SRC="channel.c" + + CHANNEL_OBJ="objects/channel.o" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-terminal argument" >&5 +$as_echo_n "checking --enable-terminal argument... " >&6; } +# Check whether --enable-terminal was given. +if test "${enable_terminal+set}" = set; then : + enableval=$enable_terminal; +else + enable_terminal="auto" +fi + +if test "$enable_terminal" = "yes" || test "$enable_terminal" = "auto" -a "x$features" = "xhuge" ; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot use terminal emulator with tiny or small features" >&5 +$as_echo "cannot use terminal emulator with tiny or small features" >&6; } + enable_terminal="no" + else + if test "$enable_terminal" = "auto"; then + enable_terminal="yes" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: defaulting to yes" >&5 +$as_echo "defaulting to yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi + fi +else + if test "$enable_terminal" = "auto"; then + enable_terminal="no" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: defaulting to no" >&5 +$as_echo "defaulting to no" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi +fi +if test "$enable_terminal" = "yes" -a "$enable_channel" = "yes"; then + $as_echo "#define FEAT_TERMINAL 1" >>confdefs.h + + TERM_SRC="libvterm/src/encoding.c libvterm/src/keyboard.c libvterm/src/mouse.c libvterm/src/parser.c libvterm/src/pen.c libvterm/src/termscreen.c libvterm/src/state.c libvterm/src/unicode.c libvterm/src/vterm.c" + + TERM_OBJ="objects/encoding.o objects/keyboard.o objects/mouse.o objects/parser.o objects/pen.o objects/termscreen.o objects/state.o objects/unicode.o objects/vterm.o" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-autoservername argument" >&5 +$as_echo_n "checking --enable-autoservername argument... " >&6; } +# Check whether --enable-autoservername was given. +if test "${enable_autoservername+set}" = set; then : + enableval=$enable_autoservername; +else + enable_autoservername="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_autoservername" >&5 +$as_echo "$enable_autoservername" >&6; } +if test "$enable_autoservername" = "yes"; then + $as_echo "#define FEAT_AUTOSERVERNAME 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-multibyte argument" >&5 +$as_echo_n "checking --enable-multibyte argument... " >&6; } +# Check whether --enable-multibyte was given. +if test "${enable_multibyte+set}" = set; then : + enableval=$enable_multibyte; +else + enable_multibyte="yes" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_multibyte" >&5 +$as_echo "$enable_multibyte" >&6; } +if test "$enable_multibyte" != "yes"; then + as_fn_error $? "The multi-byte feature can no longer be disabled. If you have + a problem with this, discuss on the Vim mailing list." "$LINENO" 5 +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-rightleft argument" >&5 +$as_echo_n "checking --disable-rightleft argument... " >&6; } +# Check whether --enable-rightleft was given. +if test "${enable_rightleft+set}" = set; then : + enableval=$enable_rightleft; +else + enable_rightleft="yes" +fi + +if test "$enable_rightleft" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define DISABLE_RIGHTLEFT 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-arabic argument" >&5 +$as_echo_n "checking --disable-arabic argument... " >&6; } +# Check whether --enable-arabic was given. +if test "${enable_arabic+set}" = set; then : + enableval=$enable_arabic; +else + enable_arabic="yes" +fi + +if test "$enable_arabic" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define DISABLE_ARABIC 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-farsi argument" >&5 +$as_echo_n "checking --disable-farsi argument... " >&6; } +# Check whether --enable-farsi was given. +if test "${enable_farsi+set}" = set; then : + enableval=$enable_farsi; +else + enable_farsi="yes" +fi + +if test "$enable_farsi" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define DISABLE_FARSI 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-hangulinput argument" >&5 +$as_echo_n "checking --enable-hangulinput argument... " >&6; } +# Check whether --enable-hangulinput was given. +if test "${enable_hangulinput+set}" = set; then : + enableval=$enable_hangulinput; +else + enable_hangulinput="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_hangulinput" >&5 +$as_echo "$enable_hangulinput" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-xim argument" >&5 +$as_echo_n "checking --enable-xim argument... " >&6; } +# Check whether --enable-xim was given. +if test "${enable_xim+set}" = set; then : + enableval=$enable_xim; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_xim" >&5 +$as_echo "$enable_xim" >&6; } +else + enable_xim="auto"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: defaulting to auto" >&5 +$as_echo "defaulting to auto" >&6; } +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-fontset argument" >&5 +$as_echo_n "checking --enable-fontset argument... " >&6; } +# Check whether --enable-fontset was given. +if test "${enable_fontset+set}" = set; then : + enableval=$enable_fontset; +else + enable_fontset="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_fontset" >&5 +$as_echo "$enable_fontset" >&6; } + +test -z "$with_x" && with_x=yes +test "${enable_gui-yes}" != no -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && with_x=yes +if test "$with_x" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: defaulting to: don't HAVE_X11" >&5 +$as_echo "defaulting to: don't HAVE_X11" >&6; } +else + + # Extract the first word of "xmkmf", so it can be a program name with args. +set dummy xmkmf; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_xmkmfpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $xmkmfpath in + [\\/]* | ?:[\\/]*) + ac_cv_path_xmkmfpath="$xmkmfpath" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_xmkmfpath="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +xmkmfpath=$ac_cv_path_xmkmfpath +if test -n "$xmkmfpath"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $xmkmfpath" >&5 +$as_echo "$xmkmfpath" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for X" >&5 +$as_echo_n "checking for X... " >&6; } + + +# Check whether --with-x was given. +if test "${with_x+set}" = set; then : + withval=$with_x; +fi + +# $have_x is `yes', `no', `disabled', or empty when we do not yet know. +if test "x$with_x" = xno; then + # The user explicitly disabled X. + have_x=disabled +else + case $x_includes,$x_libraries in #( + *\'*) as_fn_error $? "cannot use X directory names containing '" "$LINENO" 5;; #( + *,NONE | NONE,*) if ${ac_cv_have_x+:} false; then : + $as_echo_n "(cached) " >&6 +else + # One or both of the vars are not set, and there is no cached value. +ac_x_includes=no ac_x_libraries=no +rm -f -r conftest.dir +if mkdir conftest.dir; then + cd conftest.dir + cat >Imakefile <<'_ACEOF' +incroot: + @echo incroot='${INCROOT}' +usrlibdir: + @echo usrlibdir='${USRLIBDIR}' +libdir: + @echo libdir='${LIBDIR}' +_ACEOF + if (export CC; ${XMKMF-xmkmf}) >/dev/null 2>/dev/null && test -f Makefile; then + # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. + for ac_var in incroot usrlibdir libdir; do + eval "ac_im_$ac_var=\`\${MAKE-make} $ac_var 2>/dev/null | sed -n 's/^$ac_var=//p'\`" + done + # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. + for ac_extension in a so sl dylib la dll; do + if test ! -f "$ac_im_usrlibdir/libX11.$ac_extension" && + test -f "$ac_im_libdir/libX11.$ac_extension"; then + ac_im_usrlibdir=$ac_im_libdir; break + fi + done + # Screen out bogus values from the imake configuration. They are + # bogus both because they are the default anyway, and because + # using them would break gcc on systems where it needs fixed includes. + case $ac_im_incroot in + /usr/include) ac_x_includes= ;; + *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; + esac + case $ac_im_usrlibdir in + /usr/lib | /usr/lib64 | /lib | /lib64) ;; + *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; + esac + fi + cd .. + rm -f -r conftest.dir +fi + +# Standard set of common directories for X headers. +# Check X11 before X11Rn because it is often a symlink to the current release. +ac_x_header_dirs=' +/usr/X11/include +/usr/X11R7/include +/usr/X11R6/include +/usr/X11R5/include +/usr/X11R4/include + +/usr/include/X11 +/usr/include/X11R7 +/usr/include/X11R6 +/usr/include/X11R5 +/usr/include/X11R4 + +/usr/local/X11/include +/usr/local/X11R7/include +/usr/local/X11R6/include +/usr/local/X11R5/include +/usr/local/X11R4/include + +/usr/local/include/X11 +/usr/local/include/X11R7 +/usr/local/include/X11R6 +/usr/local/include/X11R5 +/usr/local/include/X11R4 + +/usr/X386/include +/usr/x386/include +/usr/XFree86/include/X11 + +/usr/include +/usr/local/include +/usr/unsupported/include +/usr/athena/include +/usr/local/x11r5/include +/usr/lpp/Xamples/include + +/usr/openwin/include +/usr/openwin/share/include' + +if test "$ac_x_includes" = no; then + # Guess where to find include files, by looking for Xlib.h. + # First, try using that file with no special directory specified. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # We can compile using X headers with no special include directory. +ac_x_includes= +else + for ac_dir in $ac_x_header_dirs; do + if test -r "$ac_dir/X11/Xlib.h"; then + ac_x_includes=$ac_dir + break + fi +done +fi +rm -f conftest.err conftest.i conftest.$ac_ext +fi # $ac_x_includes = no + +if test "$ac_x_libraries" = no; then + # Check for the libraries. + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS=$LIBS + LIBS="-lX11 $LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +XrmInitialize () + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + LIBS=$ac_save_LIBS +# We can link X programs with no special library path. +ac_x_libraries= +else + LIBS=$ac_save_LIBS +for ac_dir in `$as_echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` +do + # Don't even attempt the hair of trying to link an X program! + for ac_extension in a so sl dylib la dll; do + if test -r "$ac_dir/libX11.$ac_extension"; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi # $ac_x_libraries = no + +case $ac_x_includes,$ac_x_libraries in #( + no,* | *,no | *\'*) + # Didn't find X, or a directory has "'" in its name. + ac_cv_have_x="have_x=no";; #( + *) + # Record where we found X for the cache. + ac_cv_have_x="have_x=yes\ + ac_x_includes='$ac_x_includes'\ + ac_x_libraries='$ac_x_libraries'" +esac +fi +;; #( + *) have_x=yes;; + esac + eval "$ac_cv_have_x" +fi # $with_x != no + +if test "$have_x" != yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_x" >&5 +$as_echo "$have_x" >&6; } + no_x=yes +else + # If each of the values was on the command line, it overrides each guess. + test "x$x_includes" = xNONE && x_includes=$ac_x_includes + test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries + # Update the cache value to reflect the command line values. + ac_cv_have_x="have_x=yes\ + ac_x_includes='$x_includes'\ + ac_x_libraries='$x_libraries'" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: libraries $x_libraries, headers $x_includes" >&5 +$as_echo "libraries $x_libraries, headers $x_includes" >&6; } +fi + +if test "$no_x" = yes; then + # Not all programs may use this symbol, but it does not hurt to define it. + +$as_echo "#define X_DISPLAY_MISSING 1" >>confdefs.h + + X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS= +else + if test -n "$x_includes"; then + X_CFLAGS="$X_CFLAGS -I$x_includes" + fi + + # It would also be nice to do this for all -L options, not just this one. + if test -n "$x_libraries"; then + X_LIBS="$X_LIBS -L$x_libraries" + # For Solaris; some versions of Sun CC require a space after -R and + # others require no space. Words are not sufficient . . . . + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -R must be followed by a space" >&5 +$as_echo_n "checking whether -R must be followed by a space... " >&6; } + ac_xsave_LIBS=$LIBS; LIBS="$LIBS -R$x_libraries" + ac_xsave_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + X_LIBS="$X_LIBS -R$x_libraries" +else + LIBS="$ac_xsave_LIBS -R $x_libraries" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + X_LIBS="$X_LIBS -R $x_libraries" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: neither works" >&5 +$as_echo "neither works" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_c_werror_flag=$ac_xsave_c_werror_flag + LIBS=$ac_xsave_LIBS + fi + + # Check for system-dependent libraries X programs must link with. + # Do this before checking for the system-independent R6 libraries + # (-lICE), since we may need -lsocket or whatever for X linking. + + if test "$ISC" = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet" + else + # Martyn Johnson says this is needed for Ultrix, if the X + # libraries were built with DECnet support. And Karl Berry says + # the Alpha needs dnet_stub (dnet does not exist). + ac_xsave_LIBS="$LIBS"; LIBS="$LIBS $X_LIBS -lX11" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char XOpenDisplay (); +int +main () +{ +return XOpenDisplay (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dnet_ntoa in -ldnet" >&5 +$as_echo_n "checking for dnet_ntoa in -ldnet... " >&6; } +if ${ac_cv_lib_dnet_dnet_ntoa+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dnet_ntoa (); +int +main () +{ +return dnet_ntoa (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dnet_dnet_ntoa=yes +else + ac_cv_lib_dnet_dnet_ntoa=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dnet_dnet_ntoa" >&5 +$as_echo "$ac_cv_lib_dnet_dnet_ntoa" >&6; } +if test "x$ac_cv_lib_dnet_dnet_ntoa" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet" +fi + + if test $ac_cv_lib_dnet_dnet_ntoa = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dnet_ntoa in -ldnet_stub" >&5 +$as_echo_n "checking for dnet_ntoa in -ldnet_stub... " >&6; } +if ${ac_cv_lib_dnet_stub_dnet_ntoa+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet_stub $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dnet_ntoa (); +int +main () +{ +return dnet_ntoa (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dnet_stub_dnet_ntoa=yes +else + ac_cv_lib_dnet_stub_dnet_ntoa=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5 +$as_echo "$ac_cv_lib_dnet_stub_dnet_ntoa" >&6; } +if test "x$ac_cv_lib_dnet_stub_dnet_ntoa" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub" +fi + + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$ac_xsave_LIBS" + + # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT, + # to get the SysV transport functions. + # Chad R. Larson says the Pyramis MIS-ES running DC/OSx (SVR4) + # needs -lnsl. + # The nsl library prevents programs from opening the X display + # on Irix 5.2, according to T.E. Dickey. + # The functions gethostbyname, getservbyname, and inet_addr are + # in -lbsd on LynxOS 3.0.1/i386, according to Lars Hecking. + ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname" +if test "x$ac_cv_func_gethostbyname" = xyes; then : + +fi + + if test $ac_cv_func_gethostbyname = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5 +$as_echo_n "checking for gethostbyname in -lnsl... " >&6; } +if ${ac_cv_lib_nsl_gethostbyname+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char gethostbyname (); +int +main () +{ +return gethostbyname (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_nsl_gethostbyname=yes +else + ac_cv_lib_nsl_gethostbyname=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5 +$as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; } +if test "x$ac_cv_lib_nsl_gethostbyname" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl" +fi + + if test $ac_cv_lib_nsl_gethostbyname = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lbsd" >&5 +$as_echo_n "checking for gethostbyname in -lbsd... " >&6; } +if ${ac_cv_lib_bsd_gethostbyname+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char gethostbyname (); +int +main () +{ +return gethostbyname (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_bsd_gethostbyname=yes +else + ac_cv_lib_bsd_gethostbyname=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_gethostbyname" >&5 +$as_echo "$ac_cv_lib_bsd_gethostbyname" >&6; } +if test "x$ac_cv_lib_bsd_gethostbyname" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd" +fi + + fi + fi + + # lieder@skyler.mavd.honeywell.com says without -lsocket, + # socket/setsockopt and other routines are undefined under SCO ODT + # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary + # on later versions), says Simon Leinen: it contains gethostby* + # variants that don't use the name server (or something). -lsocket + # must be given before -lnsl if both are needed. We assume that + # if connect needs -lnsl, so does gethostbyname. + ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect" +if test "x$ac_cv_func_connect" = xyes; then : + +fi + + if test $ac_cv_func_connect = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for connect in -lsocket" >&5 +$as_echo_n "checking for connect in -lsocket... " >&6; } +if ${ac_cv_lib_socket_connect+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $X_EXTRA_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char connect (); +int +main () +{ +return connect (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_socket_connect=yes +else + ac_cv_lib_socket_connect=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_connect" >&5 +$as_echo "$ac_cv_lib_socket_connect" >&6; } +if test "x$ac_cv_lib_socket_connect" = xyes; then : + X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS" +fi + + fi + + # Guillermo Gomez says -lposix is necessary on A/UX. + ac_fn_c_check_func "$LINENO" "remove" "ac_cv_func_remove" +if test "x$ac_cv_func_remove" = xyes; then : + +fi + + if test $ac_cv_func_remove = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for remove in -lposix" >&5 +$as_echo_n "checking for remove in -lposix... " >&6; } +if ${ac_cv_lib_posix_remove+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lposix $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char remove (); +int +main () +{ +return remove (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_posix_remove=yes +else + ac_cv_lib_posix_remove=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_posix_remove" >&5 +$as_echo "$ac_cv_lib_posix_remove" >&6; } +if test "x$ac_cv_lib_posix_remove" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix" +fi + + fi + + # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay. + ac_fn_c_check_func "$LINENO" "shmat" "ac_cv_func_shmat" +if test "x$ac_cv_func_shmat" = xyes; then : + +fi + + if test $ac_cv_func_shmat = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shmat in -lipc" >&5 +$as_echo_n "checking for shmat in -lipc... " >&6; } +if ${ac_cv_lib_ipc_shmat+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lipc $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shmat (); +int +main () +{ +return shmat (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ipc_shmat=yes +else + ac_cv_lib_ipc_shmat=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ipc_shmat" >&5 +$as_echo "$ac_cv_lib_ipc_shmat" >&6; } +if test "x$ac_cv_lib_ipc_shmat" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc" +fi + + fi + fi + + # Check for libraries that X11R6 Xt/Xaw programs need. + ac_save_LDFLAGS=$LDFLAGS + test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries" + # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to + # check for ICE first), but we must link in the order -lSM -lICE or + # we get undefined symbols. So assume we have SM if we have ICE. + # These have to be linked with before -lX11, unlike the other + # libraries we check for below, so use a different variable. + # John Interrante, Karl Berry + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for IceConnectionNumber in -lICE" >&5 +$as_echo_n "checking for IceConnectionNumber in -lICE... " >&6; } +if ${ac_cv_lib_ICE_IceConnectionNumber+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lICE $X_EXTRA_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char IceConnectionNumber (); +int +main () +{ +return IceConnectionNumber (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ICE_IceConnectionNumber=yes +else + ac_cv_lib_ICE_IceConnectionNumber=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5 +$as_echo "$ac_cv_lib_ICE_IceConnectionNumber" >&6; } +if test "x$ac_cv_lib_ICE_IceConnectionNumber" = xyes; then : + X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE" +fi + + LDFLAGS=$ac_save_LDFLAGS + +fi + + + if test "$zOSUnix" = "yes"; then + CFLAGS="$CFLAGS -W c,dll" + LDFLAGS="$LDFLAGS -W l,dll" + X_EXTRA_LIBS="$X_EXTRA_LIBS -lSM -lICE -lXmu" + fi + + + if test -d "$x_includes" && test ! -d "$x_libraries"; then + x_libraries=`echo "$x_includes" | sed s/include/lib/` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Corrected X libraries to $x_libraries" >&5 +$as_echo "Corrected X libraries to $x_libraries" >&6; } + X_LIBS="$X_LIBS -L$x_libraries" + if test "`(uname) 2>/dev/null`" = SunOS && + uname -r | grep '^5' >/dev/null; then + X_LIBS="$X_LIBS -R $x_libraries" + fi + fi + + if test -d "$x_libraries" && test ! -d "$x_includes"; then + x_includes=`echo "$x_libraries" | sed s/lib/include/` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Corrected X includes to $x_includes" >&5 +$as_echo "Corrected X includes to $x_includes" >&6; } + X_CFLAGS="$X_CFLAGS -I$x_includes" + fi + + X_CFLAGS="`echo $X_CFLAGS\ | sed 's%-I/usr/include %%'`" + X_LIBS="`echo $X_LIBS\ | sed 's%-L/usr/lib %%'`" + X_LIBS="`echo $X_LIBS\ | sed -e 's%-R/usr/lib %%' -e 's%-R /usr/lib %%'`" + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if X11 header files can be found" >&5 +$as_echo_n "checking if X11 header files can be found... " >&6; } + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $X_CFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; no_x=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$cflags_save + + if test "${no_x-no}" = yes; then + with_x=no + else + $as_echo "#define HAVE_X11 1" >>confdefs.h + + X_LIB="-lXt -lX11"; + + + ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="-L$x_libraries $LDFLAGS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _XdmcpAuthDoIt in -lXdmcp" >&5 +$as_echo_n "checking for _XdmcpAuthDoIt in -lXdmcp... " >&6; } +if ${ac_cv_lib_Xdmcp__XdmcpAuthDoIt+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lXdmcp -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS -lXdmcp $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char _XdmcpAuthDoIt (); +int +main () +{ +return _XdmcpAuthDoIt (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_Xdmcp__XdmcpAuthDoIt=yes +else + ac_cv_lib_Xdmcp__XdmcpAuthDoIt=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xdmcp__XdmcpAuthDoIt" >&5 +$as_echo "$ac_cv_lib_Xdmcp__XdmcpAuthDoIt" >&6; } +if test "x$ac_cv_lib_Xdmcp__XdmcpAuthDoIt" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -lXdmcp" +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for IceOpenConnection in -lICE" >&5 +$as_echo_n "checking for IceOpenConnection in -lICE... " >&6; } +if ${ac_cv_lib_ICE_IceOpenConnection+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lICE $X_EXTRA_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char IceOpenConnection (); +int +main () +{ +return IceOpenConnection (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ICE_IceOpenConnection=yes +else + ac_cv_lib_ICE_IceOpenConnection=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ICE_IceOpenConnection" >&5 +$as_echo "$ac_cv_lib_ICE_IceOpenConnection" >&6; } +if test "x$ac_cv_lib_ICE_IceOpenConnection" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -lSM -lICE" +fi + + + LDFLAGS="$X_LIBS $ac_save_LDFLAGS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XpmCreatePixmapFromData in -lXpm" >&5 +$as_echo_n "checking for XpmCreatePixmapFromData in -lXpm... " >&6; } +if ${ac_cv_lib_Xpm_XpmCreatePixmapFromData+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lXpm -lXt $X_PRE_LIBS -lXpm -lX11 $X_EXTRA_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char XpmCreatePixmapFromData (); +int +main () +{ +return XpmCreatePixmapFromData (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_Xpm_XpmCreatePixmapFromData=yes +else + ac_cv_lib_Xpm_XpmCreatePixmapFromData=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xpm_XpmCreatePixmapFromData" >&5 +$as_echo "$ac_cv_lib_Xpm_XpmCreatePixmapFromData" >&6; } +if test "x$ac_cv_lib_Xpm_XpmCreatePixmapFromData" = xyes; then : + X_PRE_LIBS="$X_PRE_LIBS -lXpm" +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if X11 header files implicitly declare return values" >&5 +$as_echo_n "checking if X11 header files implicitly declare return values... " >&6; } + cflags_save=$CFLAGS + if test "$GCC" = yes; then + CFLAGS="$CFLAGS $X_CFLAGS -Werror" + else + CFLAGS="$CFLAGS $X_CFLAGS" + fi + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +else + CFLAGS="$CFLAGS -Wno-implicit-int" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; cflags_save="$cflags_save -Wno-implicit-int" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: test failed" >&5 +$as_echo "test failed" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$cflags_save + + LDFLAGS="$ac_save_LDFLAGS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of wchar_t is 2 bytes" >&5 +$as_echo_n "checking size of wchar_t is 2 bytes... " >&6; } + if ${ac_cv_small_wchar_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + as_fn_error $? "failed to compile test program" "$LINENO" 5 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#if STDC_HEADERS +# include +# include +#endif + main() + { + if (sizeof(wchar_t) <= 2) + exit(1); + exit(0); + } +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_small_wchar_t="no" +else + ac_cv_small_wchar_t="yes" +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_small_wchar_t" >&5 +$as_echo "$ac_cv_small_wchar_t" >&6; } + if test "x$ac_cv_small_wchar_t" = "xyes" ; then + $as_echo "#define SMALL_WCHAR_T 1" >>confdefs.h + + fi + + fi +fi + +if test "x$with_x" = xno -a "x$with_x_arg" = xyes; then + as_fn_error $? "could not configure X" "$LINENO" 5 +fi + +test "x$with_x" = xno -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && enable_gui=no + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-gui argument" >&5 +$as_echo_n "checking --enable-gui argument... " >&6; } +# Check whether --enable-gui was given. +if test "${enable_gui+set}" = set; then : + enableval=$enable_gui; +else + enable_gui="auto" +fi + + +enable_gui_canon=`echo "_$enable_gui" | \ + sed 's/[ _+-]//g;y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` + +SKIP_GTK2=YES +SKIP_GTK3=YES +SKIP_GNOME=YES +SKIP_MOTIF=YES +SKIP_ATHENA=YES +SKIP_NEXTAW=YES +SKIP_PHOTON=YES +SKIP_CARBON=YES +GUITYPE=NONE + +if test "x$QNX" = "xyes" -a "x$with_x" = "xno" ; then + SKIP_PHOTON= + case "$enable_gui_canon" in + no) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no GUI support" >&5 +$as_echo "no GUI support" >&6; } + SKIP_PHOTON=YES ;; + yes|""|auto) { $as_echo "$as_me:${as_lineno-$LINENO}: result: automatic GUI support" >&5 +$as_echo "automatic GUI support" >&6; } + gui_auto=yes ;; + photon) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Photon GUI support" >&5 +$as_echo "Photon GUI support" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Sorry, $enable_gui GUI is not supported" >&5 +$as_echo "Sorry, $enable_gui GUI is not supported" >&6; } + SKIP_PHOTON=YES ;; + esac + +elif test "x$MACOS_X" = "xyes" -a "x$with_x" = "xno" ; then + SKIP_CARBON= + case "$enable_gui_canon" in + no) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no GUI support" >&5 +$as_echo "no GUI support" >&6; } + SKIP_CARBON=YES ;; + yes|"") { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes - automatic GUI support" >&5 +$as_echo "yes - automatic GUI support" >&6; } + gui_auto=yes ;; + auto) { $as_echo "$as_me:${as_lineno-$LINENO}: result: auto - Carbon GUI is outdated - disable GUI support" >&5 +$as_echo "auto - Carbon GUI is outdated - disable GUI support" >&6; } + SKIP_CARBON=YES ;; + carbon) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Carbon GUI support" >&5 +$as_echo "Carbon GUI support" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Sorry, $enable_gui GUI is not supported" >&5 +$as_echo "Sorry, $enable_gui GUI is not supported" >&6; } + SKIP_CARBON=YES ;; + esac + +else + + case "$enable_gui_canon" in + no|none) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no GUI support" >&5 +$as_echo "no GUI support" >&6; } ;; + yes|""|auto) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes/auto - automatic GUI support" >&5 +$as_echo "yes/auto - automatic GUI support" >&6; } + gui_auto=yes + SKIP_GTK2= + SKIP_GNOME= + SKIP_MOTIF= + SKIP_ATHENA= + SKIP_NEXTAW= + SKIP_CARBON=;; + gtk2) { $as_echo "$as_me:${as_lineno-$LINENO}: result: GTK+ 2.x GUI support" >&5 +$as_echo "GTK+ 2.x GUI support" >&6; } + SKIP_GTK2=;; + gnome2) { $as_echo "$as_me:${as_lineno-$LINENO}: result: GNOME 2.x GUI support" >&5 +$as_echo "GNOME 2.x GUI support" >&6; } + SKIP_GNOME= + SKIP_GTK2=;; + gtk3) { $as_echo "$as_me:${as_lineno-$LINENO}: result: GTK+ 3.x GUI support" >&5 +$as_echo "GTK+ 3.x GUI support" >&6; } + SKIP_GTK3=;; + motif) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Motif GUI support" >&5 +$as_echo "Motif GUI support" >&6; } + SKIP_MOTIF=;; + athena) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Athena GUI support" >&5 +$as_echo "Athena GUI support" >&6; } + SKIP_ATHENA=;; + nextaw) { $as_echo "$as_me:${as_lineno-$LINENO}: result: neXtaw GUI support" >&5 +$as_echo "neXtaw GUI support" >&6; } + SKIP_NEXTAW=;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Sorry, $enable_gui GUI is not supported" >&5 +$as_echo "Sorry, $enable_gui GUI is not supported" >&6; } ;; + esac + +fi + +if test "x$SKIP_GTK2" != "xYES" -a "$enable_gui_canon" != "gtk2" \ + -a "$enable_gui_canon" != "gnome2"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for GTK+ 2" >&5 +$as_echo_n "checking whether or not to look for GTK+ 2... " >&6; } + # Check whether --enable-gtk2-check was given. +if test "${enable_gtk2_check+set}" = set; then : + enableval=$enable_gtk2_check; +else + enable_gtk2_check="yes" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gtk2_check" >&5 +$as_echo "$enable_gtk2_check" >&6; } + if test "x$enable_gtk2_check" = "xno"; then + SKIP_GTK2=YES + SKIP_GNOME=YES + fi +fi + +if test "x$SKIP_GNOME" != "xYES" -a "$enable_gui_canon" != "gnome2"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for GNOME" >&5 +$as_echo_n "checking whether or not to look for GNOME... " >&6; } + # Check whether --enable-gnome-check was given. +if test "${enable_gnome_check+set}" = set; then : + enableval=$enable_gnome_check; +else + enable_gnome_check="no" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gnome_check" >&5 +$as_echo "$enable_gnome_check" >&6; } + if test "x$enable_gnome_check" = "xno"; then + SKIP_GNOME=YES + fi +fi + +if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for GTK+ 3" >&5 +$as_echo_n "checking whether or not to look for GTK+ 3... " >&6; } + # Check whether --enable-gtk3-check was given. +if test "${enable_gtk3_check+set}" = set; then : + enableval=$enable_gtk3_check; +else + enable_gtk3_check="yes" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gtk3_check" >&5 +$as_echo "$enable_gtk3_check" >&6; } + if test "x$enable_gtk3_check" = "xno"; then + SKIP_GTK3=YES + fi +fi + +if test "x$SKIP_MOTIF" != "xYES" -a "$enable_gui_canon" != "motif"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for Motif" >&5 +$as_echo_n "checking whether or not to look for Motif... " >&6; } + # Check whether --enable-motif-check was given. +if test "${enable_motif_check+set}" = set; then : + enableval=$enable_motif_check; +else + enable_motif_check="yes" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_motif_check" >&5 +$as_echo "$enable_motif_check" >&6; } + if test "x$enable_motif_check" = "xno"; then + SKIP_MOTIF=YES + fi +fi + +if test "x$SKIP_ATHENA" != "xYES" -a "$enable_gui_canon" != "athena"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for Athena" >&5 +$as_echo_n "checking whether or not to look for Athena... " >&6; } + # Check whether --enable-athena-check was given. +if test "${enable_athena_check+set}" = set; then : + enableval=$enable_athena_check; +else + enable_athena_check="yes" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_athena_check" >&5 +$as_echo "$enable_athena_check" >&6; } + if test "x$enable_athena_check" = "xno"; then + SKIP_ATHENA=YES + fi +fi + +if test "x$SKIP_NEXTAW" != "xYES" -a "$enable_gui_canon" != "nextaw"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for neXtaw" >&5 +$as_echo_n "checking whether or not to look for neXtaw... " >&6; } + # Check whether --enable-nextaw-check was given. +if test "${enable_nextaw_check+set}" = set; then : + enableval=$enable_nextaw_check; +else + enable_nextaw_check="yes" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_nextaw_check" >&5 +$as_echo "$enable_nextaw_check" >&6; }; + if test "x$enable_nextaw_check" = "xno"; then + SKIP_NEXTAW=YES + fi +fi + +if test "x$SKIP_CARBON" != "xYES" -a "$enable_gui_canon" != "carbon"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for Carbon" >&5 +$as_echo_n "checking whether or not to look for Carbon... " >&6; } + # Check whether --enable-carbon-check was given. +if test "${enable_carbon_check+set}" = set; then : + enableval=$enable_carbon_check; +else + enable_carbon_check="yes" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_carbon_check" >&5 +$as_echo "$enable_carbon_check" >&6; }; + if test "x$enable_carbon_check" = "xno"; then + SKIP_CARBON=YES + fi +fi + + +if test "x$MACOS_X" = "xyes" -a -z "$SKIP_CARBON" -a "x$CARBON" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Carbon GUI" >&5 +$as_echo_n "checking for Carbon GUI... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; + GUITYPE=CARBONGUI + if test "$VIMNAME" = "vim"; then + VIMNAME=Vim + fi + + if test "x$MACARCH" = "xboth"; then + CPPFLAGS="$CPPFLAGS -I$DEVELOPER_DIR/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon" + else + CPPFLAGS="$CPPFLAGS -I$DEVELOPER_DIR/Headers/FlatCarbon" + fi + + if test x$prefix = xNONE; then + prefix=/Applications + fi + + datadir='${prefix}/Vim.app/Contents/Resources' + + SKIP_GTK2=YES; + SKIP_GNOME=YES; + SKIP_MOTIF=YES; + SKIP_ATHENA=YES; + SKIP_NEXTAW=YES; + SKIP_PHOTON=YES; + SKIP_CARBON=YES +fi + + + + + + + + +if test -z "$SKIP_GTK2"; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-gtktest argument" >&5 +$as_echo_n "checking --disable-gtktest argument... " >&6; } + # Check whether --enable-gtktest was given. +if test "${enable_gtktest+set}" = set; then : + enableval=$enable_gtktest; +else + enable_gtktest=yes +fi + + if test "x$enable_gtktest" = "xyes" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: gtk test enabled" >&5 +$as_echo "gtk test enabled" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: gtk test disabled" >&5 +$as_echo "gtk test disabled" >&6; } + fi + + if test "X$PKG_CONFIG" = "X"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="no" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + + fi + + if test "x$PKG_CONFIG" != "xno"; then + + if test "X$GTK_CONFIG" != "Xno" -o "X$PKG_CONFIG" != "Xno"; then + { + no_gtk="" + if (test "X$SKIP_GTK2" != "XYES" -a "X$PKG_CONFIG" != "Xno") \ + && $PKG_CONFIG --exists gtk+-2.0; then + { + min_gtk_version=2.2.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5 +$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; } + GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-2.0` + GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-2.0` + GTK_LIBS=`$PKG_CONFIG --libs gtk+-2.0` + gtk_major_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + gtk_minor_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + gtk_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + } + elif (test "X$SKIP_GTK3" != "XYES" -a "X$PKG_CONFIG" != "Xno") \ + && $PKG_CONFIG --exists gtk+-3.0; then + { + min_gtk_version=2.2.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5 +$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; } + + GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-3.0` + GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-3.0` + GTK_LIBS=`$PKG_CONFIG --libs gtk+-3.0` + gtk_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + gtk_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + gtk_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + } + else + no_gtk=yes + fi + + if test "x$enable_gtktest" = "xyes" -a "x$no_gtk" = "x"; then + { + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $GTK_CFLAGS" + LIBS="$LIBS $GTK_LIBS" + + rm -f conf.gtktest + if test "$cross_compiling" = yes; then : + echo $ac_n "cross compiling; assumed OK... $ac_c" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#if STDC_HEADERS +# include +# include +#endif + +int +main () +{ +int major, minor, micro; +char *tmp_version; + +system ("touch conf.gtktest"); + +/* HP/UX 9 (%@#!) writes to sscanf strings */ +tmp_version = g_strdup("$min_gtk_version"); +if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_gtk_version"); + exit(1); + } + +if ((gtk_major_version > major) || + ((gtk_major_version == major) && (gtk_minor_version > minor)) || + ((gtk_major_version == major) && (gtk_minor_version == minor) && + (gtk_micro_version >= micro))) +{ + return 0; +} +return 1; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + no_gtk=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + } + fi + if test "x$no_gtk" = x ; then + if test "x$enable_gtktest" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&5 +$as_echo "yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&5 +$as_echo "found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&6; } + fi + GUI_LIB_LOC="$GTK_LIBDIR" + GTK_LIBNAME="$GTK_LIBS" + GUI_INC_LOC="$GTK_CFLAGS" + else + { + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + GTK_CFLAGS="" + GTK_LIBS="" + : + if test "$fail_if_missing" = "yes" -a "X$gui_auto" != "Xyes"; then + as_fn_error $? "could not configure GTK" "$LINENO" 5 + fi + } + fi + } + else + GTK_CFLAGS="" + GTK_LIBS="" + : + fi + + + rm -f conf.gtktest + + if test "x$GTK_CFLAGS" != "x"; then + SKIP_GTK3=YES + SKIP_ATHENA=YES + SKIP_NEXTAW=YES + SKIP_MOTIF=YES + GUITYPE=GTK + + fi + fi + if test "x$GUITYPE" = "xGTK"; then + if test -z "$SKIP_GNOME"; then + { + + + + + + +# Check whether --with-gnome-includes was given. +if test "${with_gnome_includes+set}" = set; then : + withval=$with_gnome_includes; CFLAGS="$CFLAGS -I$withval" + +fi + + + +# Check whether --with-gnome-libs was given. +if test "${with_gnome_libs+set}" = set; then : + withval=$with_gnome_libs; LDFLAGS="$LDFLAGS -L$withval" gnome_prefix=$withval + +fi + + + +# Check whether --with-gnome was given. +if test "${with_gnome+set}" = set; then : + withval=$with_gnome; if test x$withval = xyes; then + want_gnome=yes + have_gnome=yes + else + if test "x$withval" = xno; then + want_gnome=no + else + want_gnome=yes + LDFLAGS="$LDFLAGS -L$withval/lib" + CFLAGS="$CFLAGS -I$withval/include" + gnome_prefix=$withval/lib + fi + fi +else + want_gnome=yes +fi + + + if test "x$want_gnome" = xyes; then + { + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libgnomeui-2.0" >&5 +$as_echo_n "checking for libgnomeui-2.0... " >&6; } + if $PKG_CONFIG --exists libgnomeui-2.0; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + GNOME_LIBS=`$PKG_CONFIG --libs-only-l libgnomeui-2.0` + GNOME_LIBDIR=`$PKG_CONFIG --libs-only-L libgnomeui-2.0` + GNOME_INCLUDEDIR=`$PKG_CONFIG --cflags libgnomeui-2.0` + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for FreeBSD" >&5 +$as_echo_n "checking for FreeBSD... " >&6; } + if test "`(uname) 2>/dev/null`" = FreeBSD; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + GNOME_INCLUDEDIR="$GNOME_INCLUDEDIR -D_THREAD_SAFE" + GNOME_LIBS="$GNOME_LIBS -pthread" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + have_gnome=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + if test "x" = xfail; then + as_fn_error $? "Could not find libgnomeui-2.0 via pkg-config" "$LINENO" 5 + fi + fi + } + fi + + if test "x$have_gnome" = xyes ; then + $as_echo "#define FEAT_GUI_GNOME 1" >>confdefs.h + + GUI_INC_LOC="$GUI_INC_LOC $GNOME_INCLUDEDIR" + GTK_LIBNAME="$GTK_LIBNAME $GNOME_LIBDIR $GNOME_LIBS" + fi + } + fi + fi +fi + + +if test -z "$SKIP_GTK3"; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-gtktest argument" >&5 +$as_echo_n "checking --disable-gtktest argument... " >&6; } + # Check whether --enable-gtktest was given. +if test "${enable_gtktest+set}" = set; then : + enableval=$enable_gtktest; +else + enable_gtktest=yes +fi + + if test "x$enable_gtktest" = "xyes" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: gtk test enabled" >&5 +$as_echo "gtk test enabled" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: gtk test disabled" >&5 +$as_echo "gtk test disabled" >&6; } + fi + + if test "X$PKG_CONFIG" = "X"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="no" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + + fi + + if test "x$PKG_CONFIG" != "xno"; then + + if test "X$GTK_CONFIG" != "Xno" -o "X$PKG_CONFIG" != "Xno"; then + { + no_gtk="" + if (test "X$SKIP_GTK2" != "XYES" -a "X$PKG_CONFIG" != "Xno") \ + && $PKG_CONFIG --exists gtk+-2.0; then + { + min_gtk_version=3.0.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5 +$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; } + GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-2.0` + GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-2.0` + GTK_LIBS=`$PKG_CONFIG --libs gtk+-2.0` + gtk_major_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + gtk_minor_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + gtk_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + } + elif (test "X$SKIP_GTK3" != "XYES" -a "X$PKG_CONFIG" != "Xno") \ + && $PKG_CONFIG --exists gtk+-3.0; then + { + min_gtk_version=3.0.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5 +$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; } + + GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-3.0` + GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-3.0` + GTK_LIBS=`$PKG_CONFIG --libs gtk+-3.0` + gtk_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + gtk_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + gtk_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + } + else + no_gtk=yes + fi + + if test "x$enable_gtktest" = "xyes" -a "x$no_gtk" = "x"; then + { + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $GTK_CFLAGS" + LIBS="$LIBS $GTK_LIBS" + + rm -f conf.gtktest + if test "$cross_compiling" = yes; then : + echo $ac_n "cross compiling; assumed OK... $ac_c" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#if STDC_HEADERS +# include +# include +#endif + +int +main () +{ +int major, minor, micro; +char *tmp_version; + +system ("touch conf.gtktest"); + +/* HP/UX 9 (%@#!) writes to sscanf strings */ +tmp_version = g_strdup("$min_gtk_version"); +if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_gtk_version"); + exit(1); + } + +if ((gtk_major_version > major) || + ((gtk_major_version == major) && (gtk_minor_version > minor)) || + ((gtk_major_version == major) && (gtk_minor_version == minor) && + (gtk_micro_version >= micro))) +{ + return 0; +} +return 1; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + no_gtk=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + } + fi + if test "x$no_gtk" = x ; then + if test "x$enable_gtktest" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&5 +$as_echo "yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&5 +$as_echo "found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&6; } + fi + GUI_LIB_LOC="$GTK_LIBDIR" + GTK_LIBNAME="$GTK_LIBS" + GUI_INC_LOC="$GTK_CFLAGS" + else + { + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + GTK_CFLAGS="" + GTK_LIBS="" + : + if test "$fail_if_missing" = "yes" -a "X$gui_auto" != "Xyes"; then + as_fn_error $? "could not configure GTK" "$LINENO" 5 + fi + } + fi + } + else + GTK_CFLAGS="" + GTK_LIBS="" + : + fi + + + rm -f conf.gtktest + + if test "x$GTK_CFLAGS" != "x"; then + SKIP_GTK2=YES + SKIP_GNOME=YES + SKIP_ATHENA=YES + SKIP_NEXTAW=YES + SKIP_MOTIF=YES + GUITYPE=GTK + + $as_echo "#define USE_GTK3 1" >>confdefs.h + + fi + fi +fi + +if test "x$GUITYPE" = "xGTK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking version of Gdk-Pixbuf" >&5 +$as_echo_n "checking version of Gdk-Pixbuf... " >&6; } + gdk_pixbuf_version=`$PKG_CONFIG --modversion gdk-pixbuf-2.0` + if test "x$gdk_pixbuf_version" != x ; then + gdk_pixbuf_version_minor=`echo $gdk_pixbuf_version | \ + sed -e 's/[0-9][0-9]*\.\([0-9][0-9]*\)\.[0-9][0-9]*/\1/'` + if test "x$gdk_pixbuf_version_minor" != x -a \ + $gdk_pixbuf_version_minor -ge 31 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: OK." >&5 +$as_echo "OK." >&6; } + # Extract the first word of "glib-compile-resources", so it can be a program name with args. +set dummy glib-compile-resources; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_GLIB_COMPILE_RESOURCES+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $GLIB_COMPILE_RESOURCES in + [\\/]* | ?:[\\/]*) + ac_cv_path_GLIB_COMPILE_RESOURCES="$GLIB_COMPILE_RESOURCES" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_GLIB_COMPILE_RESOURCES="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_GLIB_COMPILE_RESOURCES" && ac_cv_path_GLIB_COMPILE_RESOURCES="no" + ;; +esac +fi +GLIB_COMPILE_RESOURCES=$ac_cv_path_GLIB_COMPILE_RESOURCES +if test -n "$GLIB_COMPILE_RESOURCES"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GLIB_COMPILE_RESOURCES" >&5 +$as_echo "$GLIB_COMPILE_RESOURCES" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking glib-compile-resources" >&5 +$as_echo_n "checking glib-compile-resources... " >&6; } + if test "x$GLIB_COMPILE_RESOURCES" = xno ; then + GLIB_COMPILE_RESOURCES="" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot be found in PATH." >&5 +$as_echo "cannot be found in PATH." >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: usable." >&5 +$as_echo "usable." >&6; } + $as_echo "#define USE_GRESOURCE 1" >>confdefs.h + + GRESOURCE_SRC="auto/gui_gtk_gresources.c" + GRESOURCE_OBJ="objects/gui_gtk_gresources.o" + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not usable." >&5 +$as_echo "not usable." >&6; } + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot obtain from pkg_config." >&5 +$as_echo "cannot obtain from pkg_config." >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-icon-cache-update argument" >&5 +$as_echo_n "checking --disable-icon-cache-update argument... " >&6; } + # Check whether --enable-icon_cache_update was given. +if test "${enable_icon_cache_update+set}" = set; then : + enableval=$enable_icon_cache_update; +else + enable_icon_cache_update="yes" +fi + + if test "$enable_icon_cache_update" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not set" >&5 +$as_echo "not set" >&6; } + # Extract the first word of "gtk-update-icon-cache", so it can be a program name with args. +set dummy gtk-update-icon-cache; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_GTK_UPDATE_ICON_CACHE+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $GTK_UPDATE_ICON_CACHE in + [\\/]* | ?:[\\/]*) + ac_cv_path_GTK_UPDATE_ICON_CACHE="$GTK_UPDATE_ICON_CACHE" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_GTK_UPDATE_ICON_CACHE="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_GTK_UPDATE_ICON_CACHE" && ac_cv_path_GTK_UPDATE_ICON_CACHE="no" + ;; +esac +fi +GTK_UPDATE_ICON_CACHE=$ac_cv_path_GTK_UPDATE_ICON_CACHE +if test -n "$GTK_UPDATE_ICON_CACHE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GTK_UPDATE_ICON_CACHE" >&5 +$as_echo "$GTK_UPDATE_ICON_CACHE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "x$GTK_UPDATE_ICON_CACHE" = "xno" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found in PATH." >&5 +$as_echo "not found in PATH." >&6; } + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: update disabled" >&5 +$as_echo "update disabled" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-desktop-database-update argument" >&5 +$as_echo_n "checking --disable-desktop-database-update argument... " >&6; } + # Check whether --enable-desktop_database_update was given. +if test "${enable_desktop_database_update+set}" = set; then : + enableval=$enable_desktop_database_update; +else + enable_desktop_database_update="yes" +fi + + if test "$enable_desktop_database_update" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not set" >&5 +$as_echo "not set" >&6; } + # Extract the first word of "update-desktop-database", so it can be a program name with args. +set dummy update-desktop-database; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_UPDATE_DESKTOP_DATABASE+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $UPDATE_DESKTOP_DATABASE in + [\\/]* | ?:[\\/]*) + ac_cv_path_UPDATE_DESKTOP_DATABASE="$UPDATE_DESKTOP_DATABASE" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_UPDATE_DESKTOP_DATABASE="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_UPDATE_DESKTOP_DATABASE" && ac_cv_path_UPDATE_DESKTOP_DATABASE="no" + ;; +esac +fi +UPDATE_DESKTOP_DATABASE=$ac_cv_path_UPDATE_DESKTOP_DATABASE +if test -n "$UPDATE_DESKTOP_DATABASE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $UPDATE_DESKTOP_DATABASE" >&5 +$as_echo "$UPDATE_DESKTOP_DATABASE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "x$UPDATE_DESKTOP_DATABASE" = "xno" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found in PATH." >&5 +$as_echo "not found in PATH." >&6; } + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: update disabled" >&5 +$as_echo "update disabled" >&6; } + fi +fi + + + + + + + +if test -z "$SKIP_MOTIF"; then + gui_XXX="/usr/XXX/Motif* /usr/Motif*/XXX /usr/XXX /usr/shlib /usr/X11*/XXX /usr/XXX/X11* /usr/dt/XXX /local/Motif*/XXX /local/XXX/Motif* /usr/local/Motif*/XXX /usr/local/XXX/Motif* /usr/local/XXX /usr/local/X11*/XXX /usr/local/LessTif/Motif*/XXX $MOTIFHOME/XXX" + GUI_INC_LOC="`echo $GUI_INC_LOC|sed 's%-I%%g'`" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for location of Motif GUI includes" >&5 +$as_echo_n "checking for location of Motif GUI includes... " >&6; } + gui_includes="`echo $x_includes|sed 's%/^/^/*$%%'` `echo "$gui_XXX" | sed s/XXX/include/g` $GUI_INC_LOC" + GUI_INC_LOC= + for try in $gui_includes; do + if test -f "$try/Xm/Xm.h"; then + GUI_INC_LOC=$try + fi + done + if test -n "$GUI_INC_LOC"; then + if test "$GUI_INC_LOC" = /usr/include; then + GUI_INC_LOC= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: in default path" >&5 +$as_echo "in default path" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GUI_INC_LOC" >&5 +$as_echo "$GUI_INC_LOC" >&6; } + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } + SKIP_MOTIF=YES + fi +fi + + +if test -z "$SKIP_MOTIF"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-motif-lib argument" >&5 +$as_echo_n "checking --with-motif-lib argument... " >&6; } + +# Check whether --with-motif-lib was given. +if test "${with_motif_lib+set}" = set; then : + withval=$with_motif_lib; MOTIF_LIBNAME="${withval}" +fi + + + if test -n "$MOTIF_LIBNAME"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MOTIF_LIBNAME" >&5 +$as_echo "$MOTIF_LIBNAME" >&6; } + GUI_LIB_LOC= + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + + GUI_LIB_LOC="`echo $GUI_LIB_LOC|sed 's%-L%%g'`" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for location of Motif GUI libs" >&5 +$as_echo_n "checking for location of Motif GUI libs... " >&6; } + gui_libs="`echo $x_libraries|sed 's%/^/^/*$%%'` `echo "$gui_XXX" | sed s/XXX/lib/g` /usr/lib/i386-linux-gnu /usr/lib/x86_64-linux-gnu `echo "$GUI_INC_LOC" | sed s/include/lib/` $GUI_LIB_LOC" + GUI_LIB_LOC= + for try in $gui_libs; do + for libtry in "$try"/libXm.a "$try"/libXm.so* "$try"/libXm.sl "$try"/libXm.dylib; do + if test -f "$libtry"; then + GUI_LIB_LOC=$try + fi + done + done + if test -n "$GUI_LIB_LOC"; then + if test "$GUI_LIB_LOC" = /usr/lib \ + -o "$GUI_LIB_LOC" = /usr/lib/i386-linux-gnu \ + -o "$GUI_LIB_LOC" = /usr/lib/x86_64-linux-gnu; then + GUI_LIB_LOC= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: in default path" >&5 +$as_echo "in default path" >&6; } + else + if test -n "$GUI_LIB_LOC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GUI_LIB_LOC" >&5 +$as_echo "$GUI_LIB_LOC" >&6; } + if test "`(uname) 2>/dev/null`" = SunOS && + uname -r | grep '^5' >/dev/null; then + GUI_LIB_LOC="$GUI_LIB_LOC -R $GUI_LIB_LOC" + fi + fi + fi + MOTIF_LIBNAME=-lXm + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } + SKIP_MOTIF=YES + fi + fi +fi + +if test -z "$SKIP_MOTIF"; then + SKIP_ATHENA=YES + SKIP_NEXTAW=YES + GUITYPE=MOTIF + +fi + + +GUI_X_LIBS= + +if test -z "$SKIP_ATHENA"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if Athena header files can be found" >&5 +$as_echo_n "checking if Athena header files can be found... " >&6; } + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $X_CFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; SKIP_ATHENA=YES +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$cflags_save +fi + +if test -z "$SKIP_ATHENA"; then + GUITYPE=ATHENA +fi + +if test -z "$SKIP_NEXTAW"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if neXtaw header files can be found" >&5 +$as_echo_n "checking if neXtaw header files can be found... " >&6; } + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $X_CFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; SKIP_NEXTAW=YES +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$cflags_save +fi + +if test -z "$SKIP_NEXTAW"; then + GUITYPE=NEXTAW +fi + +if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF"; then + if test -n "$GUI_INC_LOC"; then + GUI_INC_LOC=-I"`echo $GUI_INC_LOC|sed 's%-I%%'`" + fi + if test -n "$GUI_LIB_LOC"; then + GUI_LIB_LOC=-L"`echo $GUI_LIB_LOC|sed 's%-L%%'`" + fi + + ldflags_save=$LDFLAGS + LDFLAGS="$X_LIBS $LDFLAGS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XShapeQueryExtension in -lXext" >&5 +$as_echo_n "checking for XShapeQueryExtension in -lXext... " >&6; } +if ${ac_cv_lib_Xext_XShapeQueryExtension+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lXext -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char XShapeQueryExtension (); +int +main () +{ +return XShapeQueryExtension (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_Xext_XShapeQueryExtension=yes +else + ac_cv_lib_Xext_XShapeQueryExtension=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xext_XShapeQueryExtension" >&5 +$as_echo "$ac_cv_lib_Xext_XShapeQueryExtension" >&6; } +if test "x$ac_cv_lib_Xext_XShapeQueryExtension" = xyes; then : + GUI_X_LIBS="-lXext" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for wslen in -lw" >&5 +$as_echo_n "checking for wslen in -lw... " >&6; } +if ${ac_cv_lib_w_wslen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lw $GUI_X_LIBS -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char wslen (); +int +main () +{ +return wslen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_w_wslen=yes +else + ac_cv_lib_w_wslen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_w_wslen" >&5 +$as_echo "$ac_cv_lib_w_wslen" >&6; } +if test "x$ac_cv_lib_w_wslen" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -lw" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlsym in -ldl" >&5 +$as_echo_n "checking for dlsym in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlsym+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $GUI_X_LIBS -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlsym (); +int +main () +{ +return dlsym (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlsym=yes +else + ac_cv_lib_dl_dlsym=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlsym" >&5 +$as_echo "$ac_cv_lib_dl_dlsym" >&6; } +if test "x$ac_cv_lib_dl_dlsym" = xyes; then : + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldl" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XmuCreateStippledPixmap in -lXmu" >&5 +$as_echo_n "checking for XmuCreateStippledPixmap in -lXmu... " >&6; } +if ${ac_cv_lib_Xmu_XmuCreateStippledPixmap+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lXmu $GUI_X_LIBS -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char XmuCreateStippledPixmap (); +int +main () +{ +return XmuCreateStippledPixmap (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_Xmu_XmuCreateStippledPixmap=yes +else + ac_cv_lib_Xmu_XmuCreateStippledPixmap=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xmu_XmuCreateStippledPixmap" >&5 +$as_echo "$ac_cv_lib_Xmu_XmuCreateStippledPixmap" >&6; } +if test "x$ac_cv_lib_Xmu_XmuCreateStippledPixmap" = xyes; then : + GUI_X_LIBS="-lXmu $GUI_X_LIBS" +fi + + if test -z "$SKIP_MOTIF"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XpEndJob in -lXp" >&5 +$as_echo_n "checking for XpEndJob in -lXp... " >&6; } +if ${ac_cv_lib_Xp_XpEndJob+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lXp $GUI_X_LIBS -lXm -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char XpEndJob (); +int +main () +{ +return XpEndJob (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_Xp_XpEndJob=yes +else + ac_cv_lib_Xp_XpEndJob=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xp_XpEndJob" >&5 +$as_echo "$ac_cv_lib_Xp_XpEndJob" >&6; } +if test "x$ac_cv_lib_Xp_XpEndJob" = xyes; then : + GUI_X_LIBS="-lXp $GUI_X_LIBS" +fi + + fi + LDFLAGS=$ldflags_save + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for extra X11 defines" >&5 +$as_echo_n "checking for extra X11 defines... " >&6; } + NARROW_PROTO= + rm -fr conftestdir + if mkdir conftestdir; then + cd conftestdir + cat > Imakefile <<'EOF' +acfindx: + @echo 'NARROW_PROTO="${PROTO_DEFINES}"' +EOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + fi + cd .. + rm -fr conftestdir + fi + if test -z "$NARROW_PROTO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NARROW_PROTO" >&5 +$as_echo "$NARROW_PROTO" >&6; } + fi + +fi + +if test "$enable_xsmp" = "yes"; then + cppflags_save=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + for ac_header in X11/SM/SMlib.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "X11/SM/SMlib.h" "ac_cv_header_X11_SM_SMlib_h" "$ac_includes_default" +if test "x$ac_cv_header_X11_SM_SMlib_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_X11_SM_SMLIB_H 1 +_ACEOF + +fi + +done + + CPPFLAGS=$cppflags_save +fi + + +if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2" -o -z "$SKIP_GTK3"; then + cppflags_save=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + for ac_header in X11/xpm.h X11/Sunkeysym.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + if test ! "$enable_xim" = "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XIMText in X11/Xlib.h" >&5 +$as_echo_n "checking for XIMText in X11/Xlib.h... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "XIMText" >/dev/null 2>&1; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no; xim has been disabled" >&5 +$as_echo "no; xim has been disabled" >&6; }; enable_xim="no" +fi +rm -f conftest* + + fi + CPPFLAGS=$cppflags_save + + if test "$enable_xim" = "auto" -a "$enable_hangulinput" != "yes" \ + -a "x$GUITYPE" != "xNONE" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: X GUI selected; xim has been enabled" >&5 +$as_echo "X GUI selected; xim has been enabled" >&6; } + enable_xim="yes" + fi +fi + +if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF"; then + cppflags_save=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for X11/Xmu/Editres.h" >&5 +$as_echo_n "checking for X11/Xmu/Editres.h... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int +main () +{ +int i; i = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define HAVE_X11_XMU_EDITRES_H 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CPPFLAGS=$cppflags_save +fi + +if test -z "$SKIP_MOTIF"; then + cppflags_save=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + if test "$zOSUnix" = "yes"; then + xmheader="Xm/Xm.h" + else + xmheader="Xm/Xm.h Xm/XpmP.h Xm/JoinSideT.h Xm/TraitP.h Xm/Manager.h + Xm/UnhighlightT.h Xm/Notebook.h" + fi + for ac_header in $xmheader +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + if test "x$ac_cv_header_Xm_XpmP_h" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XpmAttributes_21 in Xm/XpmP.h" >&5 +$as_echo_n "checking for XpmAttributes_21 in Xm/XpmP.h... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +XpmAttributes_21 attr; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define XPMATTRIBUTES_TYPE XpmAttributes_21" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; $as_echo "#define XPMATTRIBUTES_TYPE XpmAttributes" >>confdefs.h + + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + else + $as_echo "#define XPMATTRIBUTES_TYPE XpmAttributes" >>confdefs.h + + fi + CPPFLAGS=$cppflags_save +fi + +if test "x$GUITYPE" = "xNONE" -a "$enable_xim" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no GUI selected; xim has been disabled" >&5 +$as_echo "no GUI selected; xim has been disabled" >&6; } + enable_xim="no" +fi +if test "x$GUITYPE" = "xNONE" -a "$enable_fontset" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no GUI selected; fontset has been disabled" >&5 +$as_echo "no GUI selected; fontset has been disabled" >&6; } + enable_fontset="no" +fi +if test "x$GUITYPE:$enable_fontset" = "xGTK:yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: GTK+ 2 GUI selected; fontset has been disabled" >&5 +$as_echo "GTK+ 2 GUI selected; fontset has been disabled" >&6; } + enable_fontset="no" +fi + +if test -z "$SKIP_PHOTON"; then + GUITYPE=PHOTONGUI +fi + + + + + + +if test "$enable_workshop" = "yes" -a -n "$SKIP_MOTIF"; then + as_fn_error $? "cannot use workshop without Motif" "$LINENO" 5 +fi + +if test "$enable_xim" = "yes"; then + $as_echo "#define FEAT_XIM 1" >>confdefs.h + +fi +if test "$enable_fontset" = "yes"; then + $as_echo "#define FEAT_XFONTSET 1" >>confdefs.h + +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for /proc link to executable" >&5 +$as_echo_n "checking for /proc link to executable... " >&6; } +if test -L "/proc/self/exe"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: /proc/self/exe" >&5 +$as_echo "/proc/self/exe" >&6; } + $as_echo "#define PROC_EXE_LINK \"/proc/self/exe\"" >>confdefs.h + +elif test -L "/proc/self/path/a.out"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: /proc/self/path/a.out" >&5 +$as_echo "/proc/self/path/a.out" >&6; } + $as_echo "#define PROC_EXE_LINK \"/proc/self/path/a.out\"" >>confdefs.h + +elif test -L "/proc/curproc/file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: /proc/curproc/file" >&5 +$as_echo "/proc/curproc/file" >&6; } + $as_echo "#define PROC_EXE_LINK \"/proc/curproc/file\"" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CYGWIN or MSYS environment" >&5 +$as_echo_n "checking for CYGWIN or MSYS environment... " >&6; } +case `uname` in + CYGWIN*|MSYS*) CYGWIN=yes; { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CYGWIN clipboard support" >&5 +$as_echo_n "checking for CYGWIN clipboard support... " >&6; } + if test "x$with_x" = "xno" ; then + OS_EXTRA_SRC=winclip.c; OS_EXTRA_OBJ=objects/winclip.o + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define FEAT_CYGWIN_WIN32_CLIPBOARD 1" >>confdefs.h + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no - using X11" >&5 +$as_echo "no - using X11" >&6; } + fi ;; + + *) CYGWIN=no; { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; };; +esac + +if test "$enable_hangulinput" = "yes"; then + if test "x$GUITYPE" = "xNONE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no GUI selected; hangul input has been disabled" >&5 +$as_echo "no GUI selected; hangul input has been disabled" >&6; } + enable_hangulinput=no + else + $as_echo "#define FEAT_HANGULIN 1" >>confdefs.h + + HANGULIN_SRC=hangulin.c + + HANGULIN_OBJ=objects/hangulin.o + + fi +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether toupper is broken" >&5 +$as_echo_n "checking whether toupper is broken... " >&6; } +if ${vim_cv_toupper_broken+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + + as_fn_error $? "cross-compiling: please set 'vim_cv_toupper_broken'" "$LINENO" 5 + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include "confdefs.h" +#include +#if STDC_HEADERS +# include +# include +#endif +main() { exit(toupper('A') == 'A' && tolower('z') == 'z'); } + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + vim_cv_toupper_broken=yes + +else + + vim_cv_toupper_broken=no + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_toupper_broken" >&5 +$as_echo "$vim_cv_toupper_broken" >&6; } + +if test "x$vim_cv_toupper_broken" = "xyes" ; then + $as_echo "#define BROKEN_TOUPPER 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __DATE__ and __TIME__ work" >&5 +$as_echo_n "checking whether __DATE__ and __TIME__ work... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +printf("(" __DATE__ " " __TIME__ ")"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_DATE_TIME 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __attribute__((unused)) is allowed" >&5 +$as_echo_n "checking whether __attribute__((unused)) is allowed... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int x __attribute__((unused)); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_ATTRIBUTE_UNUSED 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +ac_fn_c_check_header_mongrel "$LINENO" "elf.h" "ac_cv_header_elf_h" "$ac_includes_default" +if test "x$ac_cv_header_elf_h" = xyes; then : + HAS_ELF=1 +fi + + +if test "$HAS_ELF" = 1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lelf" >&5 +$as_echo_n "checking for main in -lelf... " >&6; } +if ${ac_cv_lib_elf_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lelf $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_elf_main=yes +else + ac_cv_lib_elf_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_elf_main" >&5 +$as_echo "$ac_cv_lib_elf_main" >&6; } +if test "x$ac_cv_lib_elf_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBELF 1 +_ACEOF + + LIBS="-lelf $LIBS" + +fi + +fi + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do + as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 +$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; } +if eval \${$as_ac_Header+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include <$ac_hdr> + +int +main () +{ +if ((DIR *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$as_ac_Header=yes" +else + eval "$as_ac_Header=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$as_ac_Header + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 +_ACEOF + +ac_header_dirent=$ac_hdr; break +fi + +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 +$as_echo_n "checking for library containing opendir... " >&6; } +if ${ac_cv_search_opendir+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (); +int +main () +{ +return opendir (); + ; + return 0; +} +_ACEOF +for ac_lib in '' dir; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_opendir=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_opendir+:} false; then : + break +fi +done +if ${ac_cv_search_opendir+:} false; then : + +else + ac_cv_search_opendir=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 +$as_echo "$ac_cv_search_opendir" >&6; } +ac_res=$ac_cv_search_opendir +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 +$as_echo_n "checking for library containing opendir... " >&6; } +if ${ac_cv_search_opendir+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (); +int +main () +{ +return opendir (); + ; + return 0; +} +_ACEOF +for ac_lib in '' x; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_opendir=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_opendir+:} false; then : + break +fi +done +if ${ac_cv_search_opendir+:} false; then : + +else + ac_cv_search_opendir=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 +$as_echo "$ac_cv_search_opendir" >&6; } +ac_res=$ac_cv_search_opendir +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +fi + + +if test $ac_cv_header_sys_wait_h = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that defines union wait" >&5 +$as_echo_n "checking for sys/wait.h that defines union wait... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +union wait xx, yy; xx = yy + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h + + $as_echo "#define HAVE_UNION_WAIT 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +for ac_header in stdint.h stdlib.h string.h \ + sys/select.h sys/utsname.h termcap.h fcntl.h \ + sgtty.h sys/ioctl.h sys/time.h sys/types.h \ + termio.h iconv.h inttypes.h langinfo.h math.h \ + unistd.h stropts.h errno.h sys/resource.h \ + sys/systeminfo.h locale.h sys/stream.h termios.h \ + libc.h sys/statfs.h poll.h sys/poll.h pwd.h \ + utime.h sys/param.h sys/ptms.h libintl.h libgen.h \ + util/debug.h util/msg18n.h frame.h sys/acl.h \ + sys/access.h sys/sysinfo.h wchar.h wctype.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in sys/ptem.h +do : + ac_fn_c_check_header_compile "$LINENO" "sys/ptem.h" "ac_cv_header_sys_ptem_h" "#if defined HAVE_SYS_STREAM_H +# include +#endif +" +if test "x$ac_cv_header_sys_ptem_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_PTEM_H 1 +_ACEOF + +fi + +done + + +for ac_header in sys/sysctl.h +do : + ac_fn_c_check_header_compile "$LINENO" "sys/sysctl.h" "ac_cv_header_sys_sysctl_h" "#if defined HAVE_SYS_PARAM_H +# include +#endif +" +if test "x$ac_cv_header_sys_sysctl_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_SYSCTL_H 1 +_ACEOF + +fi + +done + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_np.h" >&5 +$as_echo_n "checking for pthread_np.h... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int +main () +{ +int i; i = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define HAVE_PTHREAD_NP_H 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +for ac_header in strings.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "strings.h" "ac_cv_header_strings_h" "$ac_includes_default" +if test "x$ac_cv_header_strings_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STRINGS_H 1 +_ACEOF + +fi + +done + +if test "x$MACOS_X" = "xyes"; then + $as_echo "#define NO_STRINGS_WITH_STRING_H 1" >>confdefs.h + +else + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if strings.h can be included after string.h" >&5 +$as_echo_n "checking if strings.h can be included after string.h... " >&6; } +cppflags_save=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $X_CFLAGS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if defined(_AIX) && !defined(_AIX51) && !defined(_NO_PROTO) +# define _NO_PROTO /* like in os_unix.h, causes conflict for AIX (Winn) */ + /* but don't do it on AIX 5.1 (Uribarri) */ +#endif +#ifdef HAVE_XM_XM_H +# include /* This breaks it for HP-UX 11 (Squassabia) */ +#endif +#ifdef HAVE_STRING_H +# include +#endif +#if defined(HAVE_STRINGS_H) +# include +#endif + +int +main () +{ +int i; i = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + $as_echo "#define NO_STRINGS_WITH_STRING_H 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +CPPFLAGS=$cppflags_save +fi + +if test $ac_cv_c_compiler_gnu = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC needs -traditional" >&5 +$as_echo_n "checking whether $CC needs -traditional... " >&6; } +if ${ac_cv_prog_gcc_traditional+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_pattern="Autoconf.*'x'" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then : + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then : + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_gcc_traditional" >&5 +$as_echo "$ac_cv_prog_gcc_traditional" >&6; } + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if ${ac_cv_c_const+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +#ifndef __cplusplus + /* Ultrix mips cc rejects this sort of thing. */ + typedef int charset[2]; + const charset cs = { 0, 0 }; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_const=yes +else + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +$as_echo "#define const /**/" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working volatile" >&5 +$as_echo_n "checking for working volatile... " >&6; } +if ${ac_cv_c_volatile+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +volatile int x; +int * volatile y = (int *) 0; +return !x && !y; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_volatile=yes +else + ac_cv_c_volatile=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_volatile" >&5 +$as_echo "$ac_cv_c_volatile" >&6; } +if test $ac_cv_c_volatile = no; then + +$as_echo "#define volatile /**/" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" +if test "x$ac_cv_type_mode_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define mode_t int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" +if test "x$ac_cv_type_pid_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 +$as_echo_n "checking for uid_t in sys/types.h... " >&6; } +if ${ac_cv_type_uid_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then : + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 +$as_echo "$ac_cv_type_uid_t" >&6; } +if test $ac_cv_type_uid_t = no; then + +$as_echo "#define uid_t int" >>confdefs.h + + +$as_echo "#define gid_t int" >>confdefs.h + +fi + +ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" +case $ac_cv_c_uint32_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT32_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint32_t $ac_cv_c_uint32_t +_ACEOF +;; + esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if ${ac_cv_header_time+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_time=yes +else + ac_cv_header_time=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "ino_t" "ac_cv_type_ino_t" "$ac_includes_default" +if test "x$ac_cv_type_ino_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define ino_t long +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "dev_t" "ac_cv_type_dev_t" "$ac_includes_default" +if test "x$ac_cv_type_dev_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define dev_t unsigned +_ACEOF + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if ${ac_cv_c_bigendian+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h +;; #( + no) + ;; #( + universal) + +$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rlim_t" >&5 +$as_echo_n "checking for rlim_t... " >&6; } +if eval "test \"`echo '$''{'ac_cv_type_rlim_t'+set}'`\" = set"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: (cached) $ac_cv_type_rlim_t" >&5 +$as_echo "(cached) $ac_cv_type_rlim_t" >&6; } +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#if STDC_HEADERS +# include +# include +#endif +#ifdef HAVE_SYS_RESOURCE_H +# include +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])rlim_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then : + ac_cv_type_rlim_t=yes +else + ac_cv_type_rlim_t=no +fi +rm -f conftest* + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_rlim_t" >&5 +$as_echo "$ac_cv_type_rlim_t" >&6; } +fi +if test $ac_cv_type_rlim_t = no; then + cat >> confdefs.h <<\EOF +#define rlim_t unsigned long +EOF +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stack_t" >&5 +$as_echo_n "checking for stack_t... " >&6; } +if eval "test \"`echo '$''{'ac_cv_type_stack_t'+set}'`\" = set"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: (cached) $ac_cv_type_stack_t" >&5 +$as_echo "(cached) $ac_cv_type_stack_t" >&6; } +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#if STDC_HEADERS +# include +# include +#endif +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "stack_t" >/dev/null 2>&1; then : + ac_cv_type_stack_t=yes +else + ac_cv_type_stack_t=no +fi +rm -f conftest* + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_stack_t" >&5 +$as_echo "$ac_cv_type_stack_t" >&6; } +fi +if test $ac_cv_type_stack_t = no; then + cat >> confdefs.h <<\EOF +#define stack_t struct sigaltstack +EOF +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stack_t has an ss_base field" >&5 +$as_echo_n "checking whether stack_t has an ss_base field... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#if STDC_HEADERS +# include +# include +#endif +#include +#include "confdefs.h" + +int +main () +{ +stack_t sigstk; sigstk.ss_base = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SS_BASE 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +olibs="$LIBS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-tlib argument" >&5 +$as_echo_n "checking --with-tlib argument... " >&6; } + +# Check whether --with-tlib was given. +if test "${with_tlib+set}" = set; then : + withval=$with_tlib; +fi + +if test -n "$with_tlib"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_tlib" >&5 +$as_echo "$with_tlib" >&6; } + LIBS="$LIBS -l$with_tlib" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for linking with $with_tlib library" >&5 +$as_echo_n "checking for linking with $with_tlib library... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: OK" >&5 +$as_echo "OK" >&6; } +else + as_fn_error $? "FAILED" "$LINENO" 5 +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + olibs="$LIBS" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: empty: automatic terminal library selection" >&5 +$as_echo "empty: automatic terminal library selection" >&6; } + case "`uname -s 2>/dev/null`" in + OSF1|SCO_SV) tlibs="tinfo ncurses curses termlib termcap";; + *) tlibs="tinfo ncurses termlib termcap curses";; + esac + for libname in $tlibs; do + as_ac_Lib=`$as_echo "ac_cv_lib_${libname}''_tgetent" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -l${libname}" >&5 +$as_echo_n "checking for tgetent in -l${libname}... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l${libname} $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tgetent (); +int +main () +{ +return tgetent (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_LIB${libname}" | $as_tr_cpp` 1 +_ACEOF + + LIBS="-l${libname} $LIBS" + +fi + + if test "x$olibs" != "x$LIBS"; then + if test "$cross_compiling" = yes; then : + res="FAIL" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_TERMCAP_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +main() {char *s; s=(char *)tgoto("%p1%d", 0, 1); exit(0); } +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + res="OK" +else + res="FAIL" +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + if test "$res" = "OK"; then + break + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $libname library is not usable" >&5 +$as_echo "$libname library is not usable" >&6; } + LIBS="$olibs" + fi + done + if test "x$olibs" = "x$LIBS"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no terminal library found" >&5 +$as_echo "no terminal library found" >&6; } + fi +fi + +if test "x$olibs" = "x$LIBS"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent()" >&5 +$as_echo_n "checking for tgetent()... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +char s[10000]; int res = tgetent(s, "thisterminaldoesnotexist"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + as_fn_error $? "NOT FOUND! + You need to install a terminal library; for example ncurses. + Or specify the name of the library with --with-tlib." "$LINENO" 5 +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we talk terminfo" >&5 +$as_echo_n "checking whether we talk terminfo... " >&6; } +if ${vim_cv_terminfo+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + + as_fn_error $? "cross-compiling: please set 'vim_cv_terminfo'" "$LINENO" 5 + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include "confdefs.h" +#ifdef HAVE_TERMCAP_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +main() +{char *s; s=(char *)tgoto("%p1%d", 0, 1); exit(!strcmp(s==0 ? "" : s, "1")); } + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + vim_cv_terminfo=no + +else + + vim_cv_terminfo=yes + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_terminfo" >&5 +$as_echo "$vim_cv_terminfo" >&6; } + +if test "x$vim_cv_terminfo" = "xyes" ; then + $as_echo "#define TERMINFO 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking what tgetent() returns for an unknown terminal" >&5 +$as_echo_n "checking what tgetent() returns for an unknown terminal... " >&6; } +if ${vim_cv_tgetent+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + + as_fn_error $? "failed to compile test program." "$LINENO" 5 + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include "confdefs.h" +#ifdef HAVE_TERMCAP_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +main() +{char s[10000]; int res = tgetent(s, "thisterminaldoesnotexist"); exit(res != 0); } + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + vim_cv_tgetent=zero + +else + + vim_cv_tgetent=non-zero + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_tgetent" >&5 +$as_echo "$vim_cv_tgetent" >&6; } + +if test "x$vim_cv_tgetent" = "xzero" ; then + $as_echo "#define TGETENT_ZERO_ERR 0" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether termcap.h contains ospeed" >&5 +$as_echo_n "checking whether termcap.h contains ospeed... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_TERMCAP_H +# include +#endif + +int +main () +{ +ospeed = 20000 + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_OSPEED 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ospeed can be extern" >&5 +$as_echo_n "checking whether ospeed can be extern... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_TERMCAP_H +# include +#endif +extern short ospeed; + +int +main () +{ +ospeed = 20000 + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define OSPEED_EXTERN 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether termcap.h contains UP, BC and PC" >&5 +$as_echo_n "checking whether termcap.h contains UP, BC and PC... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_TERMCAP_H +# include +#endif + +int +main () +{ +if (UP == 0 && BC == 0) PC = 1 + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_UP_BC_PC 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether UP, BC and PC can be extern" >&5 +$as_echo_n "checking whether UP, BC and PC can be extern... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_TERMCAP_H +# include +#endif +extern char *UP, *BC, PC; + +int +main () +{ +if (UP == 0 && BC == 0) PC = 1 + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define UP_BC_PC_EXTERN 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether tputs() uses outfuntype" >&5 +$as_echo_n "checking whether tputs() uses outfuntype... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_TERMCAP_H +# include +#endif + +int +main () +{ +extern int xx(); tputs("test", 1, (outfuntype)xx) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_OUTFUNTYPE 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/select.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether sys/select.h and sys/time.h may both be included... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define SYS_SELECT_WITH_SYS_TIME 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for /dev/ptc" >&5 +$as_echo_n "checking for /dev/ptc... " >&6; } +if test -r /dev/ptc; then + $as_echo "#define HAVE_DEV_PTC 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SVR4 ptys" >&5 +$as_echo_n "checking for SVR4 ptys... " >&6; } +if test -c /dev/ptmx ; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +ptsname(0);grantpt(0);unlockpt(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SVR4_PTYS 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ptyranges" >&5 +$as_echo_n "checking for ptyranges... " >&6; } +if test -d /dev/ptym ; then + pdir='/dev/ptym' +else + pdir='/dev' +fi +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef M_UNIX + yes; +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then : + ptys=`echo /dev/ptyp??` +else + ptys=`echo $pdir/pty??` +fi +rm -f conftest* + +if test "$ptys" != "$pdir/pty??" ; then + p0=`echo $ptys | tr ' ' '\012' | sed -e 's/^.*\(.\).$/\1/g' | sort -u | tr -d '\012'` + p1=`echo $ptys | tr ' ' '\012' | sed -e 's/^.*\(.\)$/\1/g' | sort -u | tr -d '\012'` + cat >>confdefs.h <<_ACEOF +#define PTYRANGE0 "$p0" +_ACEOF + + cat >>confdefs.h <<_ACEOF +#define PTYRANGE1 "$p1" +_ACEOF + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $p0 / $p1" >&5 +$as_echo "$p0 / $p1" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: don't know" >&5 +$as_echo "don't know" >&6; } +fi + +rm -f conftest_grp +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking default tty permissions/group" >&5 +$as_echo_n "checking default tty permissions/group... " >&6; } +if ${vim_cv_tty_group+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + + as_fn_error $? "cross-compiling: please set 'vim_cv_tty_group' and 'vim_cv_tty_mode'" "$LINENO" 5 + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include "confdefs.h" +#include +#if STDC_HEADERS +# include +# include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +main() +{ + struct stat sb; + char *x,*ttyname(); + int om, m; + FILE *fp; + + if (!(x = ttyname(0))) exit(1); + if (stat(x, &sb)) exit(1); + om = sb.st_mode; + if (om & 002) exit(0); + m = system("mesg y"); + if (m == -1 || m == 127) exit(1); + if (stat(x, &sb)) exit(1); + m = sb.st_mode; + if (chmod(x, om)) exit(1); + if (m & 002) exit(0); + if (sb.st_gid == getgid()) exit(1); + if (!(fp=fopen("conftest_grp", "w"))) + exit(1); + fprintf(fp, "%d\n", sb.st_gid); + fclose(fp); + exit(0); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + if test -f conftest_grp; then + vim_cv_tty_group=`cat conftest_grp` + if test "x$vim_cv_tty_mode" = "x" ; then + vim_cv_tty_mode=0620 + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: pty mode: $vim_cv_tty_mode, group: $vim_cv_tty_group" >&5 +$as_echo "pty mode: $vim_cv_tty_mode, group: $vim_cv_tty_group" >&6; } + else + vim_cv_tty_group=world + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ptys are world accessible" >&5 +$as_echo "ptys are world accessible" >&6; } + fi + +else + + vim_cv_tty_group=world + { $as_echo "$as_me:${as_lineno-$LINENO}: result: can't determine - assume ptys are world accessible" >&5 +$as_echo "can't determine - assume ptys are world accessible" >&6; } + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_tty_group" >&5 +$as_echo "$vim_cv_tty_group" >&6; } +rm -f conftest_grp + +if test "x$vim_cv_tty_group" != "xworld" ; then + cat >>confdefs.h <<_ACEOF +#define PTYGROUP $vim_cv_tty_group +_ACEOF + + if test "x$vim_cv_tty_mode" = "x" ; then + as_fn_error $? "It seems you're cross compiling and have 'vim_cv_tty_group' set, please also set the environment variable 'vim_cv_tty_mode' to the correct mode (probably 0620)" "$LINENO" 5 + else + $as_echo "#define PTYMODE 0620" >>confdefs.h + + fi +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5 +$as_echo_n "checking return type of signal handlers... " >&6; } +if ${ac_cv_type_signal+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_type_signal=int +else + ac_cv_type_signal=void +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5 +$as_echo "$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + +if test $ac_cv_type_signal = void; then + $as_echo "#define SIGRETURN return" >>confdefs.h + +else + $as_echo "#define SIGRETURN return 0" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct sigcontext" >&5 +$as_echo_n "checking for struct sigcontext... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +test_sig() +{ + struct sigcontext *scont; + scont = (struct sigcontext *)0; + return 1; +} +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define HAVE_SIGCONTEXT 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking getcwd implementation is broken" >&5 +$as_echo_n "checking getcwd implementation is broken... " >&6; } +if ${vim_cv_getcwd_broken+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + + as_fn_error $? "cross-compiling: please set 'vim_cv_getcwd_broken'" "$LINENO" 5 + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include "confdefs.h" +#ifdef HAVE_UNISTD_H +#include +#endif +char *dagger[] = { "IFS=pwd", 0 }; +main() +{ + char buffer[500]; + extern char **environ; + environ = dagger; + return getcwd(buffer, 500) ? 0 : 1; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + vim_cv_getcwd_broken=no + +else + + vim_cv_getcwd_broken=yes + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_getcwd_broken" >&5 +$as_echo "$vim_cv_getcwd_broken" >&6; } + +if test "x$vim_cv_getcwd_broken" = "xyes" ; then + $as_echo "#define BAD_GETCWD 1" >>confdefs.h + +fi + +for ac_func in fchdir fchown fchmod fsync getcwd getpseudotty \ + getpwent getpwnam getpwuid getrlimit gettimeofday getwd lstat \ + memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \ + getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \ + sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \ + strnicmp strpbrk strtol tgetent towlower towupper iswupper \ + usleep utime utimes mblen ftruncate unsetenv +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_header in sys/select.h sys/socket.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking types of arguments for select" >&5 +$as_echo_n "checking types of arguments for select... " >&6; } +if ${ac_cv_func_select_args+:} false; then : + $as_echo_n "(cached) " >&6 +else + for ac_arg234 in 'fd_set *' 'int *' 'void *'; do + for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do + for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +#ifdef HAVE_SYS_SELECT_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif + +int +main () +{ +extern int select ($ac_arg1, + $ac_arg234, $ac_arg234, $ac_arg234, + $ac_arg5); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done + done +done +# Provide a safe default value. +: "${ac_cv_func_select_args=int,int *,struct timeval *}" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_select_args" >&5 +$as_echo "$ac_cv_func_select_args" >&6; } +ac_save_IFS=$IFS; IFS=',' +set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` +IFS=$ac_save_IFS +shift + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG1 $1 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG234 ($2) +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG5 ($3) +_ACEOF + +rm -f conftest* + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5 +$as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; } +if ${ac_cv_sys_largefile_source+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include /* for off_t */ + #include +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_sys_largefile_source=no; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGEFILE_SOURCE 1 +#include /* for off_t */ + #include +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_sys_largefile_source=1; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_cv_sys_largefile_source=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_source" >&5 +$as_echo "$ac_cv_sys_largefile_source" >&6; } +case $ac_cv_sys_largefile_source in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source +_ACEOF +;; +esac +rm -rf conftest* + +# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug +# in glibc 2.1.3, but that breaks too many other things. +# If you want fseeko and ftello with glibc, upgrade to a fixed glibc. +if test $ac_cv_sys_largefile_source != unknown; then + +$as_echo "#define HAVE_FSEEKO 1" >>confdefs.h + +fi + + +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if ${ac_cv_sys_largefile_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if ${ac_cv_sys_file_offset_bits+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if ${ac_cv_sys_large_files+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi + + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for st_blksize" >&5 +$as_echo_n "checking for st_blksize... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ + struct stat st; + int n; + + stat("/", &st); + n = (int)st.st_blksize; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_ST_BLKSIZE 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat() ignores a trailing slash" >&5 +$as_echo_n "checking whether stat() ignores a trailing slash... " >&6; } +if ${vim_cv_stat_ignores_slash+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + + as_fn_error $? "cross-compiling: please set 'vim_cv_stat_ignores_slash'" "$LINENO" 5 + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include "confdefs.h" +#if STDC_HEADERS +# include +# include +#endif +#include +#include +main() {struct stat st; exit(stat("configure/", &st) != 0); } + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + vim_cv_stat_ignores_slash=yes + +else + + vim_cv_stat_ignores_slash=no + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_stat_ignores_slash" >&5 +$as_echo "$vim_cv_stat_ignores_slash" >&6; } + +if test "x$vim_cv_stat_ignores_slash" = "xyes" ; then + $as_echo "#define STAT_IGNORES_SLASH 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv_open()" >&5 +$as_echo_n "checking for iconv_open()... " >&6; } +save_LIBS="$LIBS" +LIBS="$LIBS -liconv" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_ICONV_H +# include +#endif + +int +main () +{ +iconv_open("fr", "to"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes; with -liconv" >&5 +$as_echo "yes; with -liconv" >&6; }; $as_echo "#define HAVE_ICONV 1" >>confdefs.h + +else + LIBS="$save_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_ICONV_H +# include +#endif + +int +main () +{ +iconv_open("fr", "to"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_ICONV 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for nl_langinfo(CODESET)" >&5 +$as_echo_n "checking for nl_langinfo(CODESET)... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_LANGINFO_H +# include +#endif + +int +main () +{ +char *cs = nl_langinfo(CODESET); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_NL_LANGINFO_CODESET 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strtod in -lm" >&5 +$as_echo_n "checking for strtod in -lm... " >&6; } +if ${ac_cv_lib_m_strtod+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char strtod (); +int +main () +{ +return strtod (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_m_strtod=yes +else + ac_cv_lib_m_strtod=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_strtod" >&5 +$as_echo "$ac_cv_lib_m_strtod" >&6; } +if test "x$ac_cv_lib_m_strtod" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBM 1 +_ACEOF + + LIBS="-lm $LIBS" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strtod() and other floating point functions" >&5 +$as_echo_n "checking for strtod() and other floating point functions... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_MATH_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif + +int +main () +{ +char *s; double d; + d = strtod("1.1", &s); + d = fabs(1.11); + d = ceil(1.11); + d = floor(1.11); + d = log10(1.11); + d = pow(1.11, 2.22); + d = sqrt(1.11); + d = sin(1.11); + d = cos(1.11); + d = atan(1.11); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_FLOAT_FUNCS 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for isinf()" >&5 +$as_echo_n "checking for isinf()... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_MATH_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif + +int +main () +{ +int r = isinf(1.11); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_ISINF 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for isnan()" >&5 +$as_echo_n "checking for isnan()... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_MATH_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif + +int +main () +{ +int r = isnan(1.11); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_ISNAN 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-acl argument" >&5 +$as_echo_n "checking --disable-acl argument... " >&6; } +# Check whether --enable-acl was given. +if test "${enable_acl+set}" = set; then : + enableval=$enable_acl; +else + enable_acl="yes" +fi + +if test "$enable_acl" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for acl_get_file in -lposix1e" >&5 +$as_echo_n "checking for acl_get_file in -lposix1e... " >&6; } +if ${ac_cv_lib_posix1e_acl_get_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lposix1e $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char acl_get_file (); +int +main () +{ +return acl_get_file (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_posix1e_acl_get_file=yes +else + ac_cv_lib_posix1e_acl_get_file=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_posix1e_acl_get_file" >&5 +$as_echo "$ac_cv_lib_posix1e_acl_get_file" >&6; } +if test "x$ac_cv_lib_posix1e_acl_get_file" = xyes; then : + LIBS="$LIBS -lposix1e" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for acl_get_file in -lacl" >&5 +$as_echo_n "checking for acl_get_file in -lacl... " >&6; } +if ${ac_cv_lib_acl_acl_get_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lacl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char acl_get_file (); +int +main () +{ +return acl_get_file (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_acl_acl_get_file=yes +else + ac_cv_lib_acl_acl_get_file=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_acl_acl_get_file" >&5 +$as_echo "$ac_cv_lib_acl_acl_get_file" >&6; } +if test "x$ac_cv_lib_acl_acl_get_file" = xyes; then : + LIBS="$LIBS -lacl" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgetxattr in -lattr" >&5 +$as_echo_n "checking for fgetxattr in -lattr... " >&6; } +if ${ac_cv_lib_attr_fgetxattr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lattr $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char fgetxattr (); +int +main () +{ +return fgetxattr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_attr_fgetxattr=yes +else + ac_cv_lib_attr_fgetxattr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_attr_fgetxattr" >&5 +$as_echo "$ac_cv_lib_attr_fgetxattr" >&6; } +if test "x$ac_cv_lib_attr_fgetxattr" = xyes; then : + LIBS="$LIBS -lattr" +fi + +fi + +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for POSIX ACL support" >&5 +$as_echo_n "checking for POSIX ACL support... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_ACL_H +# include +#endif +acl_t acl; +int +main () +{ +acl = acl_get_file("foo", ACL_TYPE_ACCESS); + acl_set_file("foo", ACL_TYPE_ACCESS, acl); + acl_free(acl); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_POSIX_ACL 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for acl_get in -lsec" >&5 +$as_echo_n "checking for acl_get in -lsec... " >&6; } +if ${ac_cv_lib_sec_acl_get+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsec $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char acl_get (); +int +main () +{ +return acl_get (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_sec_acl_get=yes +else + ac_cv_lib_sec_acl_get=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sec_acl_get" >&5 +$as_echo "$ac_cv_lib_sec_acl_get" >&6; } +if test "x$ac_cv_lib_sec_acl_get" = xyes; then : + LIBS="$LIBS -lsec"; $as_echo "#define HAVE_SOLARIS_ZFS_ACL 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Solaris ACL support" >&5 +$as_echo_n "checking for Solaris ACL support... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_ACL_H +# include +#endif +int +main () +{ +acl("foo", GETACLCNT, 0, NULL); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SOLARIS_ACL 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for AIX ACL support" >&5 +$as_echo_n "checking for AIX ACL support... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if STDC_HEADERS +# include +# include +#endif +#ifdef HAVE_SYS_ACL_H +# include +#endif +#ifdef HAVE_SYS_ACCESS_H +# include +#endif +#define _ALL_SOURCE + +#include + +int aclsize; +struct acl *aclent; +int +main () +{ +aclsize = sizeof(struct acl); + aclent = (void *)malloc(aclsize); + statacl("foo", STX_NORMAL, aclent, aclsize); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_AIX_ACL 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi + +if test "x$GTK_CFLAGS" != "x"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pango_shape_full" >&5 +$as_echo_n "checking for pango_shape_full... " >&6; } + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $GTK_CFLAGS" + LIBS="$LIBS $GTK_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + pango_shape_full(NULL, 0, NULL, 0, NULL, NULL); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_PANGO_SHAPE_FULL 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-gpm argument" >&5 +$as_echo_n "checking --disable-gpm argument... " >&6; } +# Check whether --enable-gpm was given. +if test "${enable_gpm+set}" = set; then : + enableval=$enable_gpm; +else + enable_gpm="yes" +fi + + +if test "$enable_gpm" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gpm" >&5 +$as_echo_n "checking for gpm... " >&6; } +if ${vi_cv_have_gpm+:} false; then : + $as_echo_n "(cached) " >&6 +else + olibs="$LIBS" ; LIBS="-lgpm" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include +int +main () +{ +Gpm_GetLibVersion(NULL); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + vi_cv_have_gpm=yes +else + vi_cv_have_gpm=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$olibs" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_have_gpm" >&5 +$as_echo "$vi_cv_have_gpm" >&6; } + if test $vi_cv_have_gpm = yes; then + LIBS="$LIBS -lgpm" + $as_echo "#define HAVE_GPM 1" >>confdefs.h + + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-sysmouse argument" >&5 +$as_echo_n "checking --disable-sysmouse argument... " >&6; } +# Check whether --enable-sysmouse was given. +if test "${enable_sysmouse+set}" = set; then : + enableval=$enable_sysmouse; +else + enable_sysmouse="yes" +fi + + +if test "$enable_sysmouse" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysmouse" >&5 +$as_echo_n "checking for sysmouse... " >&6; } +if ${vi_cv_have_sysmouse+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + #include +int +main () +{ +struct mouse_info mouse; + mouse.operation = MOUSE_MODE; + mouse.operation = MOUSE_SHOW; + mouse.u.mode.mode = 0; + mouse.u.mode.signal = SIGUSR2; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + vi_cv_have_sysmouse=yes +else + vi_cv_have_sysmouse=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_have_sysmouse" >&5 +$as_echo "$vi_cv_have_sysmouse" >&6; } + if test $vi_cv_have_sysmouse = yes; then + $as_echo "#define HAVE_SYSMOUSE 1" >>confdefs.h + + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for FD_CLOEXEC" >&5 +$as_echo_n "checking for FD_CLOEXEC... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#if HAVE_FCNTL_H +# include +#endif +int +main () +{ + int flag = FD_CLOEXEC; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_FD_CLOEXEC 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not usable" >&5 +$as_echo "not usable" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rename" >&5 +$as_echo_n "checking for rename... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +rename("this", "that") + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_RENAME 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysctl" >&5 +$as_echo_n "checking for sysctl... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ + int mib[2], r; + size_t len; + + mib[0] = CTL_HW; + mib[1] = HW_USERMEM; + len = sizeof(r); + (void)sysctl(mib, 2, &r, &len, (void *)0, (size_t)0); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SYSCTL 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not usable" >&5 +$as_echo "not usable" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysinfo" >&5 +$as_echo_n "checking for sysinfo... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ + struct sysinfo sinfo; + int t; + + (void)sysinfo(&sinfo); + t = sinfo.totalram; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SYSINFO 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not usable" >&5 +$as_echo "not usable" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysinfo.mem_unit" >&5 +$as_echo_n "checking for sysinfo.mem_unit... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ + struct sysinfo sinfo; + sinfo.mem_unit = 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SYSINFO_MEM_UNIT 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysconf" >&5 +$as_echo_n "checking for sysconf... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + (void)sysconf(_SC_PAGESIZE); + (void)sysconf(_SC_PHYS_PAGES); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SYSCONF 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not usable" >&5 +$as_echo "not usable" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 +$as_echo_n "checking size of int... " >&6; } +if ${ac_cv_sizeof_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_int" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (int) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_int=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 +$as_echo "$ac_cv_sizeof_int" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INT $ac_cv_sizeof_int +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 +$as_echo_n "checking size of long... " >&6; } +if ${ac_cv_sizeof_long+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_long" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (long) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_long=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5 +$as_echo "$ac_cv_sizeof_long" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG $ac_cv_sizeof_long +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of time_t" >&5 +$as_echo_n "checking size of time_t... " >&6; } +if ${ac_cv_sizeof_time_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_time_t" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (time_t) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_time_t=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_time_t" >&5 +$as_echo "$ac_cv_sizeof_time_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_TIME_T $ac_cv_sizeof_time_t +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of off_t" >&5 +$as_echo_n "checking size of off_t... " >&6; } +if ${ac_cv_sizeof_off_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (off_t))" "ac_cv_sizeof_off_t" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_off_t" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (off_t) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_off_t=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_off_t" >&5 +$as_echo "$ac_cv_sizeof_off_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_OFF_T $ac_cv_sizeof_off_t +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define VIM_SIZEOF_INT $ac_cv_sizeof_int +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define VIM_SIZEOF_LONG $ac_cv_sizeof_long +_ACEOF + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking uint32_t is 32 bits" >&5 +$as_echo_n "checking uint32_t is 32 bits... " >&6; } +if test "$cross_compiling" = yes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot check uint32_t when cross-compiling." >&5 +$as_echo "$as_me: WARNING: cannot check uint32_t when cross-compiling." >&2;} +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +main() { + uint32_t nr1 = (uint32_t)-1; + uint32_t nr2 = (uint32_t)0xffffffffUL; + if (sizeof(uint32_t) != 4 || nr1 != 0xffffffffUL || nr2 + 1 != 0) exit(1); + exit(0); +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +else + as_fn_error $? "WRONG! uint32_t not defined correctly." "$LINENO" 5 +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + +bcopy_test_prog=' +#include "confdefs.h" +#ifdef HAVE_STRING_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +main() { + char buf[10]; + strcpy(buf, "abcdefghi"); + mch_memmove(buf, buf + 2, 3); + if (strncmp(buf, "ababcf", 6)) + exit(1); + strcpy(buf, "abcdefghi"); + mch_memmove(buf + 2, buf, 3); + if (strncmp(buf, "cdedef", 6)) + exit(1); + exit(0); /* libc version works properly. */ +}' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether memmove handles overlaps" >&5 +$as_echo_n "checking whether memmove handles overlaps... " >&6; } +if ${vim_cv_memmove_handles_overlap+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + + as_fn_error $? "cross-compiling: please set 'vim_cv_memmove_handles_overlap'" "$LINENO" 5 + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define mch_memmove(s,d,l) memmove(d,s,l) $bcopy_test_prog +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + vim_cv_memmove_handles_overlap=yes + +else + + vim_cv_memmove_handles_overlap=no + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_memmove_handles_overlap" >&5 +$as_echo "$vim_cv_memmove_handles_overlap" >&6; } + +if test "x$vim_cv_memmove_handles_overlap" = "xyes" ; then + $as_echo "#define USEMEMMOVE 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether bcopy handles overlaps" >&5 +$as_echo_n "checking whether bcopy handles overlaps... " >&6; } +if ${vim_cv_bcopy_handles_overlap+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + + as_fn_error $? "cross-compiling: please set 'vim_cv_bcopy_handles_overlap'" "$LINENO" 5 + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define mch_bcopy(s,d,l) bcopy(d,s,l) $bcopy_test_prog +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + vim_cv_bcopy_handles_overlap=yes + +else + + vim_cv_bcopy_handles_overlap=no + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_bcopy_handles_overlap" >&5 +$as_echo "$vim_cv_bcopy_handles_overlap" >&6; } + + if test "x$vim_cv_bcopy_handles_overlap" = "xyes" ; then + $as_echo "#define USEBCOPY 1" >>confdefs.h + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether memcpy handles overlaps" >&5 +$as_echo_n "checking whether memcpy handles overlaps... " >&6; } +if ${vim_cv_memcpy_handles_overlap+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + + as_fn_error $? "cross-compiling: please set 'vim_cv_memcpy_handles_overlap'" "$LINENO" 5 + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define mch_memcpy(s,d,l) memcpy(d,s,l) $bcopy_test_prog +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + vim_cv_memcpy_handles_overlap=yes + +else + + vim_cv_memcpy_handles_overlap=no + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_memcpy_handles_overlap" >&5 +$as_echo "$vim_cv_memcpy_handles_overlap" >&6; } + + if test "x$vim_cv_memcpy_handles_overlap" = "xyes" ; then + $as_echo "#define USEMEMCPY 1" >>confdefs.h + + fi + fi +fi + + +if test "x$with_x" = "xyes"; then + cflags_save=$CFLAGS + libs_save=$LIBS + LIBS="$LIBS $X_LIBS $GUI_LIB_LOC $GUI_X_LIBS $X_PRE_LIBS $X_LIB $X_EXTRA_LIBS" + CFLAGS="$CFLAGS $X_CFLAGS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether X_LOCALE needed" >&5 +$as_echo_n "checking whether X_LOCALE needed... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char _Xsetlocale (); +int +main () +{ +return _Xsetlocale (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define X_LOCALE 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Xutf8SetWMProperties() can be used" >&5 +$as_echo_n "checking whether Xutf8SetWMProperties() can be used... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char Xutf8SetWMProperties (); +int +main () +{ +return Xutf8SetWMProperties (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define HAVE_XUTF8SETWMPROPERTIES 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + CFLAGS=$cflags_save + LIBS=$libs_save +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _xpg4_setrunelocale in -lxpg4" >&5 +$as_echo_n "checking for _xpg4_setrunelocale in -lxpg4... " >&6; } +if ${ac_cv_lib_xpg4__xpg4_setrunelocale+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lxpg4 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char _xpg4_setrunelocale (); +int +main () +{ +return _xpg4_setrunelocale (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_xpg4__xpg4_setrunelocale=yes +else + ac_cv_lib_xpg4__xpg4_setrunelocale=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xpg4__xpg4_setrunelocale" >&5 +$as_echo "$ac_cv_lib_xpg4__xpg4_setrunelocale" >&6; } +if test "x$ac_cv_lib_xpg4__xpg4_setrunelocale" = xyes; then : + LIBS="$LIBS -lxpg4" +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to create tags" >&5 +$as_echo_n "checking how to create tags... " >&6; } +test -f tags && mv tags tags.save +if (eval ctags --version /dev/null | grep Exuberant) < /dev/null 1>&5 2>&1; then + TAGPRG="ctags -I INIT+ --fields=+S" +elif (eval exctags --version /dev/null | grep Exuberant) < /dev/null 1>&5 2>&1; then + TAGPRG="exctags -I INIT+ --fields=+S" +elif (eval exuberant-ctags --version /dev/null | grep Exuberant) < /dev/null 1>&5 2>&1; then + TAGPRG="exuberant-ctags -I INIT+ --fields=+S" +else + TAGPRG="ctags" + (eval etags /dev/null) < /dev/null 1>&5 2>&1 && TAGPRG="etags" + (eval etags -c /dev/null) < /dev/null 1>&5 2>&1 && TAGPRG="etags -c" + (eval ctags /dev/null) < /dev/null 1>&5 2>&1 && TAGPRG="ctags" + (eval ctags -t /dev/null) < /dev/null 1>&5 2>&1 && TAGPRG="ctags -t" + (eval ctags -ts /dev/null) < /dev/null 1>&5 2>&1 && TAGPRG="ctags -ts" + (eval ctags -tvs /dev/null) < /dev/null 1>&5 2>&1 && TAGPRG="ctags -tvs" + (eval ctags -i+m /dev/null) < /dev/null 1>&5 2>&1 && TAGPRG="ctags -i+m" +fi +test -f tags.save && mv tags.save tags +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $TAGPRG" >&5 +$as_echo "$TAGPRG" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run man with a section nr" >&5 +$as_echo_n "checking how to run man with a section nr... " >&6; } +MANDEF="man" +(eval MANPAGER=cat PAGER=cat man -s 2 read) < /dev/null > /dev/null 2>&5 && MANDEF="man -s" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANDEF" >&5 +$as_echo "$MANDEF" >&6; } +if test "$MANDEF" = "man -s"; then + $as_echo "#define USEMAN_S 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-nls argument" >&5 +$as_echo_n "checking --disable-nls argument... " >&6; } +# Check whether --enable-nls was given. +if test "${enable_nls+set}" = set; then : + enableval=$enable_nls; +else + enable_nls="yes" +fi + + +if test "$enable_nls" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + + INSTALL_LANGS=install-languages + + INSTALL_TOOL_LANGS=install-tool-languages + + + # Extract the first word of "msgfmt", so it can be a program name with args. +set dummy msgfmt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MSGFMT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MSGFMT"; then + ac_cv_prog_MSGFMT="$MSGFMT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MSGFMT="msgfmt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MSGFMT=$ac_cv_prog_MSGFMT +if test -n "$MSGFMT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGFMT" >&5 +$as_echo "$MSGFMT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for NLS" >&5 +$as_echo_n "checking for NLS... " >&6; } + if test -f po/Makefile; then + have_gettext="no" + if test -n "$MSGFMT"; then + olibs=$LIBS + LIBS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +gettext("Test"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: gettext() works" >&5 +$as_echo "gettext() works" >&6; }; have_gettext="yes"; LIBS=$olibs +else + LIBS="-lintl" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +gettext("Test"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: gettext() works with -lintl" >&5 +$as_echo "gettext() works with -lintl" >&6; }; have_gettext="yes"; + LIBS="$olibs -lintl" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: gettext() doesn't work" >&5 +$as_echo "gettext() doesn't work" >&6; }; + LIBS=$olibs +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: msgfmt not found - disabled" >&5 +$as_echo "msgfmt not found - disabled" >&6; }; + fi + if test $have_gettext = "yes" -a "x$features" != "xtiny" -a "x$features" != "xsmall"; then + $as_echo "#define HAVE_GETTEXT 1" >>confdefs.h + + MAKEMO=yes + + for ac_func in bind_textdomain_codeset +do : + ac_fn_c_check_func "$LINENO" "bind_textdomain_codeset" "ac_cv_func_bind_textdomain_codeset" +if test "x$ac_cv_func_bind_textdomain_codeset" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_BIND_TEXTDOMAIN_CODESET 1 +_ACEOF + +fi +done + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _nl_msg_cat_cntr" >&5 +$as_echo_n "checking for _nl_msg_cat_cntr... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + extern int _nl_msg_cat_cntr; +int +main () +{ +++_nl_msg_cat_cntr; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_NL_MSG_CAT_CNTR 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no \"po/Makefile\" - disabled" >&5 +$as_echo "no \"po/Makefile\" - disabled" >&6; }; + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi + +ac_fn_c_check_header_mongrel "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + DLL=dlfcn.h +else + ac_fn_c_check_header_mongrel "$LINENO" "dl.h" "ac_cv_header_dl_h" "$ac_includes_default" +if test "x$ac_cv_header_dl_h" = xyes; then : + DLL=dl.h +fi + + +fi + + +if test x${DLL} = xdlfcn.h; then + +$as_echo "#define HAVE_DLFCN_H 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen()" >&5 +$as_echo_n "checking for dlopen()... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + extern void* dlopen(); + dlopen(); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; + +$as_echo "#define HAVE_DLOPEN 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen() in -ldl" >&5 +$as_echo_n "checking for dlopen() in -ldl... " >&6; } + olibs=$LIBS + LIBS="$LIBS -ldl" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + extern void* dlopen(); + dlopen(); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; + +$as_echo "#define HAVE_DLOPEN 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; + LIBS=$olibs +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlsym()" >&5 +$as_echo_n "checking for dlsym()... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + extern void* dlsym(); + dlsym(); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; + +$as_echo "#define HAVE_DLSYM 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlsym() in -ldl" >&5 +$as_echo_n "checking for dlsym() in -ldl... " >&6; } + olibs=$LIBS + LIBS="$LIBS -ldl" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + extern void* dlsym(); + dlsym(); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; + +$as_echo "#define HAVE_DLSYM 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; + LIBS=$olibs +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +elif test x${DLL} = xdl.h; then + +$as_echo "#define HAVE_DL_H 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load()" >&5 +$as_echo_n "checking for shl_load()... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + extern void* shl_load(); + shl_load(); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; + +$as_echo "#define HAVE_SHL_LOAD 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load() in -ldld" >&5 +$as_echo_n "checking for shl_load() in -ldld... " >&6; } + olibs=$LIBS + LIBS="$LIBS -ldld" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + extern void* shl_load(); + shl_load(); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; + +$as_echo "#define HAVE_SHL_LOAD 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; + LIBS=$olibs +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +for ac_header in setjmp.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "setjmp.h" "ac_cv_header_setjmp_h" "$ac_includes_default" +if test "x$ac_cv_header_setjmp_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SETJMP_H 1 +_ACEOF + +fi + +done + + +if test "x$MACOS_X" = "xyes" -a -n "$PERL"; then + if echo $LIBS | grep -e '-ldl' >/dev/null; then + LIBS=`echo $LIBS | sed s/-ldl//` + PERL_LIBS="$PERL_LIBS -ldl" + fi +fi + +if test "$MACOS_X" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need macOS frameworks" >&5 +$as_echo_n "checking whether we need macOS frameworks... " >&6; } + if test "$GUITYPE" = "CARBONGUI"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, we need Carbon" >&5 +$as_echo "yes, we need Carbon" >&6; } + LIBS="$LIBS -framework Carbon" + elif test "$MACOS_X_DARWIN" = "yes"; then + if test "$features" = "tiny"; then + OS_EXTRA_SRC=`echo "$OS_EXTRA_SRC" | sed -e 's+os_macosx.m++'` + OS_EXTRA_OBJ=`echo "$OS_EXTRA_OBJ" | sed -e 's+objects/os_macosx.o++'` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, we need CoreServices" >&5 +$as_echo "yes, we need CoreServices" >&6; } + LIBS="$LIBS -framework CoreServices" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, we need AppKit" >&5 +$as_echo "yes, we need AppKit" >&6; } + LIBS="$LIBS -framework AppKit" + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi +fi +if test "x$MACARCH" = "xboth" && test "x$GUITYPE" = "xCARBONGUI"; then + LDFLAGS="$LDFLAGS -isysroot $DEVELOPER_DIR/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc" +fi + +DEPEND_CFLAGS_FILTER= +if test "$GCC" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GCC 3 or later" >&5 +$as_echo_n "checking for GCC 3 or later... " >&6; } + gccmajor=`echo "$gccversion" | sed -e 's/^\([1-9]\)\..*$/\1/g'` + if test "$gccmajor" -gt "2"; then + DEPEND_CFLAGS_FILTER="| sed 's+-I */+-isystem /+g'" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need -D_FORTIFY_SOURCE=1" >&5 +$as_echo_n "checking whether we need -D_FORTIFY_SOURCE=1... " >&6; } + if test "$gccmajor" -gt "3"; then + CFLAGS=`echo "$CFLAGS" | sed -e 's/ *-Wp,-D_FORTIFY_SOURCE=.//g' -e 's/ *-D_FORTIFY_SOURCE=.//g' -e 's/ *-U_FORTIFY_SOURCE//g' -e 's/$/ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1/'` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need to force -D_FILE_OFFSET_BITS=64" >&5 +$as_echo_n "checking whether we need to force -D_FILE_OFFSET_BITS=64... " >&6; } +if echo "$CFLAGS $LUA_CFLAGS $MZSCHEME_CFLAGS $PERL_CFLAGS $PYTHON_CFLAGS $PYTHON3_CFLAGS $TCL_CFLAGS $RUBY_CFLAGS $GTK_CFLAGS" | grep -q D_FILE_OFFSET_BITS 2>/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define _FILE_OFFSET_BITS 64" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking linker --as-needed support" >&5 +$as_echo_n "checking linker --as-needed support... " >&6; } +LINK_AS_NEEDED= +# Check if linker supports --as-needed and --no-as-needed options +if $CC -Wl,--help 2>/dev/null | grep as-needed > /dev/null; then + LDFLAGS=`echo "$LDFLAGS" | sed -e 's/ *-Wl,--as-needed//g' | sed -e 's/$/ -Wl,--as-needed/'` + LINK_AS_NEEDED=yes +fi +if test "$LINK_AS_NEEDED" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +# IBM z/OS reset CFLAGS for config.mk +if test "$zOSUnix" = "yes"; then + CFLAGS="-D_ALL_SOURCE -Wc,float\(ieee\),dll" +fi + +ac_config_files="$ac_config_files auto/config.mk:config.mk.in" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>auto/config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "auto/config.h") CONFIG_HEADERS="$CONFIG_HEADERS auto/config.h:config.h.in" ;; + "auto/config.mk") CONFIG_FILES="$CONFIG_FILES auto/config.mk:config.mk.in" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>auto/config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + diff --git a/src/autocmd.c b/src/autocmd.c new file mode 100644 index 0000000..55650b4 --- /dev/null +++ b/src/autocmd.c @@ -0,0 +1,2579 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * autocmd.c: Autocommand related functions + */ + +#include "vim.h" + +/* + * 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: + * + * last_autopat[0] -----------------------------+ + * V + * 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 + * + * last_autopat[1] --------+ + * V + * 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 +#ifdef FEAT_EVAL + sctx_T script_ctx; // script context where defined +#endif + struct AutoCmd *next; // next AutoCmd in list +} AutoCmd; + +typedef struct AutoPat +{ + struct AutoPat *next; // Next AutoPat in AutoPat list; MUST + // be the first entry. + 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 + 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}, + {"CmdlineChanged", EVENT_CMDLINECHANGED}, + {"CmdlineEnter", EVENT_CMDLINEENTER}, + {"CmdlineLeave", EVENT_CMDLINELEAVE}, + {"CmdwinEnter", EVENT_CMDWINENTER}, + {"CmdwinLeave", EVENT_CMDWINLEAVE}, + {"CmdUndefined", EVENT_CMDUNDEFINED}, + {"ColorScheme", EVENT_COLORSCHEME}, + {"ColorSchemePre", EVENT_COLORSCHEMEPRE}, + {"CompleteDone", EVENT_COMPLETEDONE}, + {"CursorHold", EVENT_CURSORHOLD}, + {"CursorHoldI", EVENT_CURSORHOLDI}, + {"CursorMoved", EVENT_CURSORMOVED}, + {"CursorMovedI", EVENT_CURSORMOVEDI}, + {"DiffUpdated", EVENT_DIFFUPDATED}, + {"DirChanged", EVENT_DIRCHANGED}, + {"EncodingChanged", EVENT_ENCODINGCHANGED}, + {"ExitPre", EVENT_EXITPRE}, + {"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}, + {"MenuPopup", EVENT_MENUPOPUP}, + {"OptionSet", EVENT_OPTIONSET}, + {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST}, + {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE}, + {"QuitPre", EVENT_QUITPRE}, + {"RemoteReply", EVENT_REMOTEREPLY}, + {"SessionLoadPost", EVENT_SESSIONLOADPOST}, + {"ShellCmdPost", EVENT_SHELLCMDPOST}, + {"ShellFilterPost", EVENT_SHELLFILTERPOST}, + {"SourceCmd", EVENT_SOURCECMD}, + {"SourcePre", EVENT_SOURCEPRE}, + {"SourcePost", EVENT_SOURCEPOST}, + {"SpellFileMissing",EVENT_SPELLFILEMISSING}, + {"StdinReadPost", EVENT_STDINREADPOST}, + {"StdinReadPre", EVENT_STDINREADPRE}, + {"SwapExists", EVENT_SWAPEXISTS}, + {"Syntax", EVENT_SYNTAX}, + {"TabNew", EVENT_TABNEW}, + {"TabClosed", EVENT_TABCLOSED}, + {"TabEnter", EVENT_TABENTER}, + {"TabLeave", EVENT_TABLEAVE}, + {"TermChanged", EVENT_TERMCHANGED}, + {"TerminalOpen", EVENT_TERMINALOPEN}, + {"TermResponse", EVENT_TERMRESPONSE}, + {"TextChanged", EVENT_TEXTCHANGED}, + {"TextChangedI", EVENT_TEXTCHANGEDI}, + {"TextChangedP", EVENT_TEXTCHANGEDP}, + {"User", EVENT_USER}, + {"VimEnter", EVENT_VIMENTER}, + {"VimLeave", EVENT_VIMLEAVE}, + {"VimLeavePre", EVENT_VIMLEAVEPRE}, + {"WinNew", EVENT_WINNEW}, + {"WinEnter", EVENT_WINENTER}, + {"WinLeave", EVENT_WINLEAVE}, + {"VimResized", EVENT_VIMRESIZED}, + {"TextYankPost", EVENT_TEXTYANKPOST}, + {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, NULL +}; + +static AutoPat *last_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, NULL +}; + +#define AUGROUP_DEFAULT -1 // default autocmd group +#define AUGROUP_ERROR -2 // erroneous autocmd group +#define AUGROUP_ALL -3 // all autocmd groups + +/* + * 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]) +/* use get_deleted_augroup() to get this */ +static char_u *deleted_augroup = NULL; + +/* + * Set by the apply_autocmds_group function if the given event is equal to + * EVENT_FILETYPE. Used by the readfile function in order to determine if + * EVENT_BUFREADPOST triggered the EVENT_FILETYPE. + * + * Relying on this value requires one to reset it prior calling + * apply_autocmds_group. + */ +int au_did_filetype INIT(= FALSE); + +/* + * 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 char_u *event_nr2name(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 int au_find_group(char_u *name); + +static event_T last_event; +static int last_group; +static int autocmd_blocked = 0; /* block all autocmds */ + + static char_u * +get_deleted_augroup(void) +{ + if (deleted_augroup == NULL) + deleted_augroup = (char_u *)_("--Deleted--"); + return deleted_augroup; +} + +/* + * 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 *)get_deleted_augroup(), HL_ATTR(HLF_E)); + else + msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T)); + msg_puts(" "); + } + msg_puts_attr((char *)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); +#ifdef FEAT_EVAL + if (p_verbose > 0) + last_set_msg(ac->script_ctx); +#endif + 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) +{ + VIM_CLEAR(ap->pat); + 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) + VIM_CLEAR(ac->cmd); + 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; + vim_free(ac->cmd); + vim_free(ac); + } + else + prev_ac = &(ac->next); + } + + // remove the pattern if it has been marked for deletion + if (ap->pat == NULL) + { + if (ap->next == NULL) + { + if (prev_ap == &(first_autopat[(int)event])) + last_autopat[(int)event] = NULL; + else + // this depends on the "next" field being the first in + // the struct + last_autopat[(int)event] = (AutoPat *)prev_ap; + } + *prev_ap = ap->next; + vim_regfree(ap->reg_prog); + vim_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(_("auto-removing autocommand: %s "), + event_nr2name(event), buf->b_fnum); + verbose_leave(); + } + } + au_cleanup(); +} + +/* + * Add an autocmd group name. + * Return its 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) == FAIL) + return AUGROUP_ERROR; + + 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 + semsg(_("E367: No such group: \"%s\""), name); + else if (i == current_augroup) + emsg(_("E936: Cannot delete the current group")); + else + { + event_T event; + AutoPat *ap; + int in_use = FALSE; + + for (event = (event_T)0; (int)event < (int)NUM_EVENTS; + event = (event_T)((int)event + 1)) + { + for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) + if (ap->group == i && ap->pat != NULL) + { + give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE); + in_use = TRUE; + event = NUM_EVENTS; + break; + } + } + vim_free(AUGROUP_NAME(i)); + if (in_use) + { + AUGROUP_NAME(i) = get_deleted_augroup(); + } + else + AUGROUP_NAME(i) = NULL; + } +} + +/* + * Find the ID of an autocmd group name. + * Return its 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 && AUGROUP_NAME(i) != get_deleted_augroup() + && 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((char *)AUGROUP_NAME(i)); + msg_puts(" "); + } + } + msg_clr_eos(); + msg_end(); + } +} + +#if defined(EXITFREE) || defined(PROTO) + void +free_all_autocmds(void) +{ + int i; + char_u *s; + + for (current_augroup = -1; current_augroup < augroups.ga_len; + ++current_augroup) + do_autocmd((char_u *)"", TRUE); + + for (i = 0; i < augroups.ga_len; ++i) + { + s = ((char_u **)(augroups.ga_data))[i]; + if (s != get_deleted_augroup()) + vim_free(s); + } + ga_clear(&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 != '|'; ++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])) + { + semsg(_("E215: Illegal character after *: %s"), arg); + return NULL; + } + pat = arg + 1; + } + else + { + for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p) + { + if ((int)event_name2nr(pat, &p) >= (int)NUM_EVENTS) + { + if (have_group) + semsg(_("E216: No such event: %s"), pat); + else + semsg(_("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; +} + +# if defined(FEAT_SYN_HL) || defined(PROTO) + +/* + * 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); + vim_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); + vim_free(old_ei); + } +} +# endif /* FEAT_SYN_HL */ + +/* + * 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 autocommands associated with + * and . + * :autocmd Show the autocommands associated with + * . + * :autocmd Show all autocommands. + * :autocmd! Remove all autocommands associated with + * and , and add the command + * , for the current group. + * :autocmd! Remove all autocommands associated with + * and for the current group. + * :autocmd! Remove all autocommands associated with + * for the current group. + * :autocmd! Remove ALL autocommands 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_in, int forceit) +{ + char_u *arg = arg_in; + char_u *pat; + char_u *envpat = NULL; + char_u *cmd; + event_T event; + int need_free = FALSE; + int nested = FALSE; + int group; + + if (*arg == '|') + { + arg = (char_u *)""; + group = AUGROUP_ALL; // no argument, use all groups + } + else + { + /* + * 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; + + pat = skipwhite(pat); + if (*pat == '|') + { + pat = (char_u *)""; + cmd = (char_u *)""; + } + else + { + /* + * Scan over the pattern. Put a NUL at the end. + */ + 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--- Autocommands ---")); + + /* + * 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 || *arg == '|') + { + 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 && *arg != '|' && !VIM_ISWHITE(*arg)) + if (do_autocmd_event(event_name2nr(arg, &arg), pat, + nested, cmd, forceit, group) == FAIL) + break; + } + + if (need_free) + vim_free(cmd); + vim_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; + + for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p) + ; + 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 + vim_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 > pat && 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 >= 8 && 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) == 0) + // "" + 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. When adding a command it + * always goes at or after the last one, so start at the end. + */ + if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL) + prev_ap = &last_autopat[(int)event]; + else + 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)) + { + semsg(_("E680: : invalid buffer number "), + buflocal_nr); + return FAIL; + } + + ap = (AutoPat *)alloc((unsigned)sizeof(AutoPat)); + if (ap == NULL) + return FAIL; + ap->pat = vim_strnsave(pat, patlen); + ap->patlen = patlen; + if (ap->pat == NULL) + { + vim_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); + vim_free(reg_pat); + if (reg_pat == NULL || ap->reg_prog == NULL) + { + vim_free(ap->pat); + vim_free(ap); + return FAIL; + } + } + ap->cmds = NULL; + *prev_ap = ap; + last_autopat[(int)event] = 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)); + if (ac == NULL) + return FAIL; + ac->cmd = vim_strsave(cmd); +#ifdef FEAT_EVAL + ac->script_ctx = current_sctx; + ac->script_ctx.sc_lnum += sourcing_lnum; +#endif + if (ac->cmd == NULL) + { + vim_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? + int *did_something) +{ + char_u *fname; + int nothing_done = TRUE; + int group; + + if (did_something != NULL) + *did_something = FALSE; + + /* + * 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 && !ends_excmd(*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")); + if (did_something != NULL) + *did_something = !nothing_done; + +#ifdef FEAT_EVAL + return aborting() ? FAIL : OK; +#else + return OK; +#endif +} + +/* + * ":doautoall": execute autocommands for each loaded buffer. + */ + void +ex_doautoall(exarg_T *eap) +{ + int retval; + aco_save_T aco; + buf_T *buf; + bufref_T bufref; + char_u *arg = eap->arg; + int call_do_modelines = check_nomodeline(&arg); + int did_aucmd; + + /* + * 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_ALL_BUFFERS(buf) + { + if (buf->b_ml.ml_mfp != NULL) + { + // find a window for this buffer and save some values + aucmd_prepbuf(&aco, buf); + set_bufref(&bufref, buf); + + // execute the autocommands for this buffer + retval = do_doautocmd(arg, FALSE, &did_aucmd); + + if (call_do_modelines && did_aucmd) + { + // 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 || !bufref_valid(&bufref)) + 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". + */ + void +aucmd_prepbuf( + aco_save_T *aco, // structure to save values in + buf_T *buf) // new curbuf +{ + win_T *win; + int save_ea; +#ifdef FEAT_AUTOCHDIR + int save_acd; +#endif + + // Find a window that is for the new buffer + if (buf == curbuf) // be quick when buf is curbuf + win = curwin; + else + FOR_ALL_WINDOWS(win) + 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; + aco->save_prevwin = prevwin; + 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; +#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) + aucmd_win->w_s = &buf->b_s; +#endif + ++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(). + VIM_CLEAR(aucmd_win->w_localdir); + 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; + +#ifdef FEAT_AUTOCHDIR + // Prevent chdir() call in win_enter_ext(), through do_autochdir(). + save_acd = p_acd; + p_acd = FALSE; +#endif + + (void)win_split_ins(0, WSP_TOP, aucmd_win, 0); + (void)win_comp_pos(); // recompute window positions + p_ea = save_ea; +#ifdef FEAT_AUTOCHDIR + p_acd = save_acd; +#endif + unblock_autocmds(); + curwin = aucmd_win; + } + curbuf = buf; + aco->new_curwin = curwin; + set_bufref(&aco->new_curbuf, curbuf); +} + +/* + * Cleanup after executing autocommands for a (hidden) buffer. + * Restore the window as it was (if possible). + */ + 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 + + if (!valid_tabpage_win(curtab)) + // no valid window in current tabpage + close_tabpage(curtab); + + 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; + if (win_valid(aco->save_prevwin)) + prevwin = aco->save_prevwin; +#ifdef FEAT_EVAL + vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables + hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab +#endif + curbuf = curwin->w_buffer; + + vim_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; +#ifdef FEAT_DIFF + curwin->w_topfill = 0; +#endif + } +#if defined(FEAT_GUI) + // Hide the scrollbars from the aucmd_win and update. + gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_LEFT], FALSE); + gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE); + gui_may_update_scrollbars(); +#endif + } + 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.br_buf + && bufref_valid(&aco->new_curbuf) + && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL) + { +# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) + if (curwin->w_s == &curbuf->b_s) + curwin->w_s = &aco->new_curbuf.br_buf->b_s; +# endif + --curbuf->b_nwindows; + curbuf = aco->new_curbuf.br_buf; + curwin->w_buffer = curbuf; + ++curbuf->b_nwindows; + } + + curwin = aco->save_curwin; + curbuf = curwin->w_buffer; + if (win_valid(aco->save_prevwin)) + prevwin = aco->save_prevwin; + // In case the autocommand move the cursor to a position that that + // not exist in curbuf. + check_cursor(); + } + } +} + +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. + */ + 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; + +#ifdef FEAT_EVAL + if (should_abort(*retval)) + return FALSE; +#endif + + did_cmd = apply_autocmds_group(event, fname, fname_io, force, + AUGROUP_ALL, buf, NULL); + if (did_cmd +#ifdef FEAT_EVAL + && aborting() +#endif + ) + *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() + && reg_recording == 0 + && typebuf.tb_len == 0 +#ifdef FEAT_INS_EXPAND + && !ins_compl_active() +#endif + ) + { + 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); +} + +#if defined(FEAT_CONCEAL) || defined(PROTO) +/* + * Return TRUE when there is a CursorMovedI autocommand defined. + */ + int +has_cursormovedI(void) +{ + return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL); +} +#endif + +/* + * 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); +} + +#if defined(FEAT_INS_EXPAND) || defined(PROTO) +/* + * Return TRUE when there is a TextChangedP autocommand defined. + */ + int +has_textchangedP(void) +{ + return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL); +} +#endif + +/* + * Return TRUE when there is an InsertCharPre autocommand defined. + */ + int +has_insertcharpre(void) +{ + return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL); +} + +/* + * Return TRUE when there is an CmdUndefined autocommand defined. + */ + int +has_cmdundefined(void) +{ + return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL); +} + +/* + * Return TRUE when there is an FuncUndefined autocommand defined. + */ + int +has_funcundefined(void) +{ + return (first_autopat[(int)EVENT_FUNCUNDEFINED] != NULL); +} + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * Return TRUE when there is a TextYankPost autocommand defined. + */ + int +has_textyankpost(void) +{ + return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL); +} +#endif + +/* + * Execute autocommands for "event" and file name "fname". + * Return TRUE if some commands were executed. + */ + 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 UNUSED) // 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; +#ifdef FEAT_EVAL + sctx_T save_current_sctx; + funccal_entry_T funccal_entry; + char_u *save_cmdarg; + long save_cmdbang; +#endif + static int filechangeshell_busy = FALSE; +#ifdef FEAT_PROFILE + proftime_T wait_time; +#endif + int did_save_redobuff = FALSE; + save_redo_T save_redo; + int save_KeyTyped = KeyTyped; + + /* + * Quickly return if there are no autocommands for this event or + * autocommands are blocked. + */ + if (event == NUM_EVENTS || 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; + +#ifdef FEAT_EVAL + /* + * 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; +#endif + + /* + * 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 || event == EVENT_COLORSCHEMEPRE + || event == EVENT_OPTIONSET) + autocmd_fname = NULL; + else if (fname != NULL && !ends_excmd(*fname)) + 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 + { +#ifdef FEAT_SYN_HL + if (event == EVENT_SYNTAX) + fname = buf->b_p_syn; + else +#endif + 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 DirChanged + if (event == EVENT_FILETYPE + || event == EVENT_SYNTAX + || event == EVENT_CMDLINECHANGED + || event == EVENT_CMDLINEENTER + || event == EVENT_CMDLINELEAVE + || event == EVENT_CMDWINENTER + || event == EVENT_CMDWINLEAVE + || event == EVENT_CMDUNDEFINED + || event == EVENT_FUNCUNDEFINED + || event == EVENT_REMOTEREPLY + || event == EVENT_SPELLFILEMISSING + || event == EVENT_QUICKFIXCMDPRE + || event == EVENT_COLORSCHEME + || event == EVENT_COLORSCHEMEPRE + || event == EVENT_OPTIONSET + || event == EVENT_QUICKFIXCMDPOST + || event == EVENT_DIRCHANGED) + { + fname = vim_strsave(fname); + autocmd_fname_full = TRUE; // don't expand it later + } + else + fname = FullName_save(fname, FALSE); + } + if (fname == NULL) // out of memory + { + vim_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 + +#ifdef VMS + // remove version for correct match + if (sfname != NULL) + vms_remove_version(sfname); + vms_remove_version(fname); +#endif + + /* + * Set the name to be used for . + */ + autocmd_match = fname; + + + // Don't redraw while doing autocommands. + ++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 + +#ifdef FEAT_EVAL + save_current_sctx = current_sctx; + +# ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + prof_child_enter(&wait_time); // doesn't count for the caller itself +# endif + + // Don't use local function variables, if called from a function. + save_funccal(&funccal_entry); +#endif + + /* + * When starting to execute autocommands, save the search patterns. + */ + if (!autocmd_busy) + { + save_search_patterns(); +#ifdef FEAT_INS_EXPAND + if (!ins_compl_active()) +#endif + { + saveRedobuff(&save_redo); + did_save_redobuff = TRUE; + } + 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 = gettail(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; + +#ifdef FEAT_EVAL + // set v:cmdarg (only when there is a matching pattern) + save_cmdbang = (long)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 +#endif + 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); +#ifdef FEAT_EVAL + if (eap != NULL) + { + (void)set_cmdarg(NULL, save_cmdarg); + set_vim_var_nr(VV_CMDBANG, save_cmdbang); + } +#endif + // 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; + vim_free(sourcing_name); + sourcing_name = save_sourcing_name; + sourcing_lnum = save_sourcing_lnum; + vim_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; +#ifdef FEAT_EVAL + current_sctx = save_current_sctx; + restore_funccal(); +# ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + prof_child_exit(&wait_time); +# endif +#endif + KeyTyped = save_KeyTyped; + vim_free(fname); + vim_free(sfname); + --nesting; // see matching increment above + + /* + * When stopping to execute autocommands, restore the search patterns and + * the redo buffer. Free any buffers in the au_pending_free_buf list and + * free any windows in the au_pending_free_win list. + */ + if (!autocmd_busy) + { + restore_search_patterns(); + if (did_save_redobuff) + restoreRedobuff(&save_redo); + did_filetype = FALSE; + while (au_pending_free_buf != NULL) + { + buf_T *b = au_pending_free_buf->b_next; + vim_free(au_pending_free_buf); + au_pending_free_buf = b; + } + while (au_pending_free_win != NULL) + { + win_T *w = au_pending_free_win->w_next; + vim_free(au_pending_free_win); + au_pending_free_win = w; + } + } + + /* + * 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)) + { +#ifdef FEAT_TITLE + if (curbuf->b_changed != save_changed) + need_maketitle = TRUE; +#endif + 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); + + if (retval == OK && event == EVENT_FILETYPE) + au_did_filetype = TRUE; + + return retval; +} + +# ifdef FEAT_EVAL +static char_u *old_termresponse = NULL; +# endif + +/* + * Block triggering autocommands until unblock_autocmd() is called. + * Can be used recursively, so long as it's symmetric. + */ + void +block_autocmds(void) +{ +# ifdef FEAT_EVAL + // Remember the value of v:termresponse. + if (autocmd_blocked == 0) + old_termresponse = get_vim_var_str(VV_TERMRESPONSE); +# endif + ++autocmd_blocked; +} + + void +unblock_autocmds(void) +{ + --autocmd_blocked; + +# ifdef FEAT_EVAL + // 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); +# endif +} + +#if defined(FEAT_EVAL) && (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM)) \ + || defined(PROTO) + int +is_autocmd_blocked(void) +{ + return autocmd_blocked != 0; +} +#endif + +/* + * 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; + + VIM_CLEAR(sourcing_name); + + 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 Autocommands for \"%s\""); + sourcing_name = alloc((unsigned)(STRLEN(s) + + STRLEN(name) + ap->patlen + 1)); + if (sourcing_name != NULL) + { + sprintf((char *)sourcing_name, s, + (char *)name, (char *)ap->pat); + if (p_verbose >= 8) + { + verbose_enter(); + smsg(_("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 UNUSED, void *cookie, int indent UNUSED) +{ + 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(_("autocommand %s"), ac->cmd); + msg_puts("\n"); // don't overwrite this either + verbose_leave_scroll(); + } + retval = vim_strsave(ac->cmd); + autocmd_nested = ac->nested; +#ifdef FEAT_EVAL + current_sctx = ac->script_ctx; +#endif + 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 = gettail(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; + } + + vim_free(fname); +#ifdef BACKSLASH_IN_FILENAME + vim_free(sfname); +#endif + + return retval; +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) +/* + * Function given to ExpandGeneric() to obtain the list of autocommand group + * names. + */ + char_u * +get_augroup_name(expand_T *xp UNUSED, 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 || AUGROUP_NAME(idx) == get_deleted_augroup()) + // 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 UNUSED, int idx) +{ + if (idx < augroups.ga_len) // First list group names, if wanted + { + if (!include_groups || AUGROUP_NAME(idx) == NULL + || AUGROUP_NAME(idx) == get_deleted_augroup()) + return (char_u *)""; // skip deleted entries + return AUGROUP_NAME(idx); // return a name + } + return (char_u *)event_names[idx - augroups.ga_len].name; +} + +#endif // FEAT_CMDL_COMPL + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * 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: + vim_free(arg_save); + return retval; +} +#endif diff --git a/src/beval.c b/src/beval.c new file mode 100644 index 0000000..c5fa220 --- /dev/null +++ b/src/beval.c @@ -0,0 +1,281 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * Visual Workshop integration by Gordon Prieur + * + * 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. + */ + +#include "vim.h" + +#if defined(FEAT_BEVAL) || defined(PROTO) + +/* + * Get the text and position to be evaluated for "beval". + * If "getword" is true the returned text is not the whole line but the + * relevant word in allocated memory. + * Returns OK or FAIL. + */ + int +get_beval_info( + BalloonEval *beval, + int getword, + win_T **winp, + linenr_T *lnump, + char_u **textp, + int *colp) +{ + win_T *wp; + int row, col; + char_u *lbuf; + linenr_T lnum; + + *textp = NULL; +# ifdef FEAT_BEVAL_TERM +# ifdef FEAT_GUI + if (!gui.in_use) +# endif + { + row = mouse_row; + col = mouse_col; + } +# endif +# ifdef FEAT_GUI + if (gui.in_use) + { + row = Y_2_ROW(beval->y); + col = X_2_COL(beval->x); + } +#endif + wp = mouse_find_win(&row, &col); + if (wp != NULL && row >= 0 && row < wp->w_height && col < wp->w_width) + { + /* Found a window and the cursor is in the text. Now find the line + * number. */ + if (!mouse_comp_pos(wp, &row, &col, &lnum)) + { + /* Not past end of the file. */ + lbuf = ml_get_buf(wp->w_buffer, lnum, FALSE); + if (col <= win_linetabsize(wp, lbuf, (colnr_T)MAXCOL)) + { + /* Not past end of line. */ + if (getword) + { + /* For Netbeans we get the relevant part of the line + * instead of the whole line. */ + int len; + pos_T *spos = NULL, *epos = NULL; + + if (VIsual_active) + { + if (LT_POS(VIsual, curwin->w_cursor)) + { + spos = &VIsual; + epos = &curwin->w_cursor; + } + else + { + spos = &curwin->w_cursor; + epos = &VIsual; + } + } + + col = vcol2col(wp, lnum, col); + + if (VIsual_active + && wp->w_buffer == curwin->w_buffer + && (lnum == spos->lnum + ? col >= (int)spos->col + : lnum > spos->lnum) + && (lnum == epos->lnum + ? col <= (int)epos->col + : lnum < epos->lnum)) + { + /* Visual mode and pointing to the line with the + * Visual selection: return selected text, with a + * maximum of one line. */ + if (spos->lnum != epos->lnum || spos->col == epos->col) + return FAIL; + + lbuf = ml_get_buf(curwin->w_buffer, VIsual.lnum, FALSE); + len = epos->col - spos->col; + if (*p_sel != 'e') + len += MB_PTR2LEN(lbuf + epos->col); + lbuf = vim_strnsave(lbuf + spos->col, len); + lnum = spos->lnum; + col = spos->col; + } + else + { + /* Find the word under the cursor. */ + ++emsg_off; + len = find_ident_at_pos(wp, lnum, (colnr_T)col, &lbuf, + FIND_IDENT + FIND_STRING + FIND_EVAL); + --emsg_off; + if (len == 0) + return FAIL; + lbuf = vim_strnsave(lbuf, len); + } + } + + *winp = wp; + *lnump = lnum; + *textp = lbuf; + *colp = col; +#ifdef FEAT_VARTABS + vim_free(beval->vts); + beval->vts = tabstop_copy(wp->w_buffer->b_p_vts_array); +#endif + beval->ts = wp->w_buffer->b_p_ts; + return OK; + } + } + } + + return FAIL; +} + +/* + * Show a balloon with "mesg" or "list". + */ + void +post_balloon(BalloonEval *beval UNUSED, char_u *mesg, list_T *list UNUSED) +{ +# ifdef FEAT_BEVAL_TERM +# ifdef FEAT_GUI + if (!gui.in_use) +# endif + ui_post_balloon(mesg, list); +# endif +# ifdef FEAT_BEVAL_GUI + if (gui.in_use) + /* GUI can't handle a list */ + gui_mch_post_balloon(beval, mesg); +# endif +} + +/* + * Returns TRUE if the balloon eval has been enabled: + * 'ballooneval' for the GUI and 'balloonevalterm' for the terminal. + * Also checks if the screen isn't scrolled up. + */ + int +can_use_beval(void) +{ + return (0 +#ifdef FEAT_BEVAL_GUI + || (gui.in_use && p_beval) +#endif +#ifdef FEAT_BEVAL_TERM + || ( +# ifdef FEAT_GUI + !gui.in_use && +# endif + p_bevalterm) +#endif + ) && msg_scrolled == 0; +} + +/* + * Common code, invoked when the mouse is resting for a moment. + */ + void +general_beval_cb(BalloonEval *beval, int state UNUSED) +{ +#ifdef FEAT_EVAL + win_T *wp; + int col; + int use_sandbox; + linenr_T lnum; + char_u *text; + static char_u *result = NULL; + long winnr = 0; + char_u *bexpr; + buf_T *save_curbuf; + size_t len; + win_T *cw; +#endif + static int recursive = FALSE; + + /* Don't do anything when 'ballooneval' is off, messages scrolled the + * windows up or we have no beval area. */ + if (!can_use_beval() || beval == NULL) + return; + + /* Don't do this recursively. Happens when the expression evaluation + * takes a long time and invokes something that checks for CTRL-C typed. */ + if (recursive) + return; + recursive = TRUE; + +#ifdef FEAT_EVAL + if (get_beval_info(beval, TRUE, &wp, &lnum, &text, &col) == OK) + { + bexpr = (*wp->w_buffer->b_p_bexpr == NUL) ? p_bexpr + : wp->w_buffer->b_p_bexpr; + if (*bexpr != NUL) + { + /* Convert window pointer to number. */ + for (cw = firstwin; cw != wp; cw = cw->w_next) + ++winnr; + + set_vim_var_nr(VV_BEVAL_BUFNR, (long)wp->w_buffer->b_fnum); + set_vim_var_nr(VV_BEVAL_WINNR, winnr); + set_vim_var_nr(VV_BEVAL_WINID, wp->w_id); + set_vim_var_nr(VV_BEVAL_LNUM, (long)lnum); + set_vim_var_nr(VV_BEVAL_COL, (long)(col + 1)); + set_vim_var_string(VV_BEVAL_TEXT, text, -1); + vim_free(text); + + /* + * Temporarily change the curbuf, so that we can determine whether + * the buffer-local balloonexpr option was set insecurely. + */ + save_curbuf = curbuf; + curbuf = wp->w_buffer; + use_sandbox = was_set_insecurely((char_u *)"balloonexpr", + *curbuf->b_p_bexpr == NUL ? 0 : OPT_LOCAL); + curbuf = save_curbuf; + if (use_sandbox) + ++sandbox; + ++textlock; + + vim_free(result); + result = eval_to_string(bexpr, NULL, TRUE); + + /* Remove one trailing newline, it is added when the result was a + * list and it's hardly ever useful. If the user really wants a + * trailing newline he can add two and one remains. */ + if (result != NULL) + { + len = STRLEN(result); + if (len > 0 && result[len - 1] == NL) + result[len - 1] = NUL; + } + + if (use_sandbox) + --sandbox; + --textlock; + + set_vim_var_string(VV_BEVAL_TEXT, NULL, -1); + if (result != NULL && result[0] != NUL) + { + post_balloon(beval, result, NULL); + recursive = FALSE; + return; + } + } + } +#endif +#ifdef FEAT_NETBEANS_INTG + if (bevalServers & BEVAL_NETBEANS) + netbeans_beval_cb(beval, state); +#endif + + recursive = FALSE; +} + +#endif + diff --git a/src/beval.h b/src/beval.h new file mode 100644 index 0000000..21900c5 --- /dev/null +++ b/src/beval.h @@ -0,0 +1,91 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * Visual Workshop integration by Gordon Prieur + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + */ + +#if !defined(BEVAL__H) && (defined(FEAT_BEVAL) || defined(PROTO)) +#define BEVAL__H + +#ifdef FEAT_GUI_GTK +# ifdef USE_GTK3 +# include +# else +# include +# endif +#else +# if defined(FEAT_GUI_X11) +# include +# endif +#endif + +typedef enum +{ + ShS_NEUTRAL, /* nothing showing or pending */ + ShS_PENDING, /* data requested from debugger */ + ShS_UPDATE_PENDING, /* switching information displayed */ + ShS_SHOWING /* the balloon is being displayed */ +} BeState; + +typedef struct BalloonEvalStruct +{ +#ifdef FEAT_BEVAL_GUI +# ifdef FEAT_GUI_GTK + GtkWidget *target; /* widget we are monitoring */ + GtkWidget *balloonShell; + GtkWidget *balloonLabel; + unsigned int timerID; /* timer for run */ + BeState showState; /* tells us whats currently going on */ + int x; + int y; + unsigned int state; /* Button/Modifier key state */ +# else +# if !defined(FEAT_GUI_W32) + Widget target; /* widget we are monitoring */ + Widget balloonShell; + Widget balloonLabel; + XtIntervalId timerID; /* timer for run */ + BeState showState; /* tells us whats currently going on */ + XtAppContext appContext; /* used in event handler */ + Position x; + Position y; + Position x_root; + Position y_root; + int state; /* Button/Modifier key state */ +# else + HWND target; + HWND balloon; + int x; + int y; + BeState showState; /* tells us whats currently going on */ +# endif +# endif +# if !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_W32) + Dimension screen_width; /* screen width in pixels */ + Dimension screen_height; /* screen height in pixels */ +# endif + void (*msgCB)(struct BalloonEvalStruct *, int); + void *clientData; /* For callback */ +#endif + + int ts; // tabstop setting for this buffer +#ifdef FEAT_VARTABS + int *vts; // vartabstop setting for this buffer +#endif + char_u *msg; +#ifdef FEAT_GUI_W32 + void *tofree; +#endif +} BalloonEval; + +#define EVAL_OFFSET_X 15 /* displacement of beval topleft corner from pointer */ +#define EVAL_OFFSET_Y 10 + +#ifdef FEAT_BEVAL_GUI +# include "gui_beval.pro" +#endif + +#endif /* BEVAL__H and FEAT_BEVAL_GUI */ diff --git a/src/bigvim.bat b/src/bigvim.bat new file mode 100644 index 0000000..e82eabd --- /dev/null +++ b/src/bigvim.bat @@ -0,0 +1,5 @@ +:: command to build big Vim with OLE, Lua, Perl, Python, Racket, Ruby and Tcl +SET VCDIR="C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\" +SET TOOLDIR=E:\ +%VCDIR%nmake -f Make_mvc.mak GUI=yes OLE=yes LUA=%TOOLDIR%lua53 DYNAMIC_LUA=yes LUA_VER=53 PERL=%TOOLDIR%perl524 DYNAMIC_PERL=yes PERL_VER=524 PYTHON=%TOOLDIR%python27 DYNAMIC_PYTHON=yes PYTHON_VER=27 PYTHON3=%TOOLDIR%python36 DYNAMIC_PYTHON3=yes PYTHON3_VER=36 MZSCHEME=%TOOLDIR%Racket DYNAMIC_MZSCHEME=yes MZSCHEME_VER=3m_a36fs8 RUBY=%TOOLDIR%ruby24 DYNAMIC_RUBY=yes RUBY_VER=24 RUBY_API_VER_LONG=2.4.0 RUBY_MSVCRT_NAME=msvcrt TCL=%TOOLDIR%ActiveTcl TCL_VER=86 TCL_VER_LONG=8.6 DYNAMIC_TCL=yes TCL_DLL=tcl86t.dll %1 IME=yes CSCOPE=yes DIRECTX=yes + diff --git a/src/bigvim64.bat b/src/bigvim64.bat new file mode 100644 index 0000000..8d5fba0 --- /dev/null +++ b/src/bigvim64.bat @@ -0,0 +1,7 @@ +:: command to build big Vim 64 bit with OLE, Perl and Python. +:: First run: %VCDIR%\vcvarsall.bat x86_amd64 +:: Ruby and Tcl are excluded, doesn't seem to work. +SET VCDIR="C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\" +SET TOOLDIR=E:\ +%VCDIR%\bin\nmake -f Make_mvc.mak CPU=AMD64 GUI=yes OLE=yes PERL=E:\perl524 DYNAMIC_PERL=yes PERL_VER=524 PYTHON=%TOOLDIR%python27 DYNAMIC_PYTHON=yes PYTHON_VER=27 PYTHON3=%TOOLDIR%python35 DYNAMIC_PYTHON3=yes PYTHON3_VER=35 %1 IME=yes CSCOPE=yes + diff --git a/src/blob.c b/src/blob.c new file mode 100644 index 0000000..9dc7926 --- /dev/null +++ b/src/blob.c @@ -0,0 +1,258 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * blob.c: Blob support by Yasuhiro Matsumoto + */ + +#include "vim.h" + +#if defined(FEAT_EVAL) || defined(PROTO) + +/* + * Allocate an empty blob. + * Caller should take care of the reference count. + */ + blob_T * +blob_alloc(void) +{ + blob_T *blob = (blob_T *)alloc_clear(sizeof(blob_T)); + + if (blob != NULL) + ga_init2(&blob->bv_ga, 1, 100); + return blob; +} + +/* + * Allocate an empty blob for a return value, with reference count set. + * Returns OK or FAIL. + */ + int +rettv_blob_alloc(typval_T *rettv) +{ + blob_T *b = blob_alloc(); + + if (b == NULL) + return FAIL; + + rettv_blob_set(rettv, b); + return OK; +} + +/* + * Set a blob as the return value. + */ + void +rettv_blob_set(typval_T *rettv, blob_T *b) +{ + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = b; + if (b != NULL) + ++b->bv_refcount; +} + + int +blob_copy(typval_T *from, typval_T *to) +{ + int ret = OK; + + to->v_type = VAR_BLOB; + if (from->vval.v_blob == NULL) + to->vval.v_blob = NULL; + else if (rettv_blob_alloc(to) == FAIL) + ret = FAIL; + else + { + int len = from->vval.v_blob->bv_ga.ga_len; + + if (len > 0) + { + to->vval.v_blob->bv_ga.ga_data = + vim_memsave(from->vval.v_blob->bv_ga.ga_data, len); + if (to->vval.v_blob->bv_ga.ga_data == NULL) + len = 0; + } + to->vval.v_blob->bv_ga.ga_len = len; + } + return ret; +} + + void +blob_free(blob_T *b) +{ + ga_clear(&b->bv_ga); + vim_free(b); +} + +/* + * Unreference a blob: decrement the reference count and free it when it + * becomes zero. + */ + void +blob_unref(blob_T *b) +{ + if (b != NULL && --b->bv_refcount <= 0) + blob_free(b); +} + +/* + * Get the length of data. + */ + long +blob_len(blob_T *b) +{ + if (b == NULL) + return 0L; + return b->bv_ga.ga_len; +} + +/* + * Get byte "idx" in blob "b". + * Caller must check that "idx" is valid. + */ + int +blob_get(blob_T *b, int idx) +{ + return ((char_u*)b->bv_ga.ga_data)[idx]; +} + +/* + * Store one byte "c" in blob "b" at "idx". + * Caller must make sure that "idx" is valid. + */ + void +blob_set(blob_T *b, int idx, char_u c) +{ + ((char_u*)b->bv_ga.ga_data)[idx] = c; +} + +/* + * Return TRUE when two blobs have exactly the same values. + */ + int +blob_equal( + blob_T *b1, + blob_T *b2) +{ + int i; + int len1 = blob_len(b1); + int len2 = blob_len(b2); + + // empty and NULL are considered the same + if (len1 == 0 && len2 == 0) + return TRUE; + if (b1 == b2) + return TRUE; + if (len1 != len2) + return FALSE; + + for (i = 0; i < b1->bv_ga.ga_len; i++) + if (blob_get(b1, i) != blob_get(b2, i)) return FALSE; + return TRUE; +} + +/* + * Read "blob" from file "fd". + * Return OK or FAIL. + */ + int +read_blob(FILE *fd, blob_T *blob) +{ + struct stat st; + + if (fstat(fileno(fd), &st) < 0) + return FAIL; + if (ga_grow(&blob->bv_ga, st.st_size) == FAIL) + return FAIL; + blob->bv_ga.ga_len = st.st_size; + if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd) + < (size_t)blob->bv_ga.ga_len) + return FAIL; + return OK; +} + +/* + * Write "blob" to file "fd". + * Return OK or FAIL. + */ + int +write_blob(FILE *fd, blob_T *blob) +{ + if (fwrite(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd) + < (size_t)blob->bv_ga.ga_len) + { + emsg(_(e_write)); + return FAIL; + } + return OK; +} + +/* + * Convert a blob to a readable form: "0z00112233.44556677.8899" + */ + char_u * +blob2string(blob_T *blob, char_u **tofree, char_u *numbuf) +{ + int i; + garray_T ga; + + if (blob == NULL) + { + *tofree = NULL; + return (char_u *)"0z"; + } + + // Store bytes in the growarray. + ga_init2(&ga, 1, 4000); + ga_concat(&ga, (char_u *)"0z"); + for (i = 0; i < blob_len(blob); i++) + { + if (i > 0 && (i & 3) == 0) + ga_concat(&ga, (char_u *)"."); + vim_snprintf((char *)numbuf, NUMBUFLEN, "%02X", (int)blob_get(blob, i)); + ga_concat(&ga, numbuf); + } + *tofree = ga.ga_data; + return *tofree; +} + +/* + * Convert a string variable, in the format of blob2string(), to a blob. + * Return NULL when conversion failed. + */ + blob_T * +string2blob(char_u *str) +{ + blob_T *blob = blob_alloc(); + char_u *s = str; + + if (s[0] != '0' || (s[1] != 'z' && s[1] != 'Z')) + goto failed; + s += 2; + while (vim_isxdigit(*s)) + { + if (!vim_isxdigit(s[1])) + goto failed; + ga_append(&blob->bv_ga, (hex2nr(s[0]) << 4) + hex2nr(s[1])); + s += 2; + if (*s == '.' && vim_isxdigit(s[1])) + ++s; + } + if (*skipwhite(s) != NUL) + goto failed; // text after final digit + + ++blob->bv_refcount; + return blob; + +failed: + blob_free(blob); + return NULL; +} + +#endif /* defined(FEAT_EVAL) */ diff --git a/src/blowfish.c b/src/blowfish.c new file mode 100644 index 0000000..8ca0e28 --- /dev/null +++ b/src/blowfish.c @@ -0,0 +1,683 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + * + * There are two variants: + * - The old one "blowfish" has a flaw which makes it much easier to crack the + * key. To see this, make a text file with one line of 1000 "x" characters + * and write it encrypted. Use "xxd" to inspect the bytes in the file. You + * will see that a block of 8 bytes repeats 8 times. + * - The new one "blowfish2" is better. It uses an 8 byte CFB to avoid the + * repeats. + */ + +#include "vim.h" + +#if defined(FEAT_CRYPT) || defined(PROTO) + +#define ARRAY_LENGTH(A) (sizeof(A)/sizeof(A[0])) + +#define BF_BLOCK 8 +#define BF_BLOCK_MASK 7 +#define BF_MAX_CFB_LEN (8 * BF_BLOCK) + +typedef union { + UINT32_T ul[2]; + char_u uc[8]; +} block8; + +#if defined(WIN3264) + /* MS-Windows is always little endian */ +#else +# ifdef HAVE_CONFIG_H + /* in configure.ac AC_C_BIGENDIAN() defines WORDS_BIGENDIAN when needed */ +# else + error! + Please change this code to define WORDS_BIGENDIAN for big-endian machines. +# endif +#endif + +/* The state of encryption, referenced by cryptstate_T. */ +typedef struct { + UINT32_T pax[18]; /* P-array */ + UINT32_T sbx[4][256]; /* S-boxes */ + int randbyte_offset; + int update_offset; + char_u cfb_buffer[BF_MAX_CFB_LEN]; /* up to 64 bytes used */ + int cfb_len; /* size of cfb_buffer actually used */ +} bf_state_T; + + +/* Blowfish code */ +static UINT32_T pax_init[18] = { + 0x243f6a88u, 0x85a308d3u, 0x13198a2eu, + 0x03707344u, 0xa4093822u, 0x299f31d0u, + 0x082efa98u, 0xec4e6c89u, 0x452821e6u, + 0x38d01377u, 0xbe5466cfu, 0x34e90c6cu, + 0xc0ac29b7u, 0xc97c50ddu, 0x3f84d5b5u, + 0xb5470917u, 0x9216d5d9u, 0x8979fb1bu +}; + +static UINT32_T sbx_init[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 ^= bfs->pax[i]; \ + xr ^= ((bfs->sbx[0][xl >> 24] + \ + bfs->sbx[1][(xl & 0xFF0000) >> 16]) ^ \ + bfs->sbx[2][(xl & 0xFF00) >> 8]) + \ + bfs->sbx[3][xl & 0xFF]; + +#define F2(i) \ + xr ^= bfs->pax[i]; \ + xl ^= ((bfs->sbx[0][xr >> 24] + \ + bfs->sbx[1][(xr & 0xFF0000) >> 16]) ^ \ + bfs->sbx[2][(xr & 0xFF00) >> 8]) + \ + bfs->sbx[3][xr & 0xFF]; + + static void +bf_e_block( + bf_state_T *bfs, + 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 ^= bfs->pax[16]; + xr ^= bfs->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 +# define htonl2(x) +#endif + + static void +bf_e_cblock( + bf_state_T *bfs, + char_u *block) +{ + block8 bk; + + memcpy(bk.uc, block, 8); + htonl2(bk.ul[0]); + htonl2(bk.ul[1]); + bf_e_block(bfs, &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. + */ + static void +bf_key_init( + bf_state_T *bfs, + char_u *password, + char_u *salt, + int salt_len) +{ + int i, j, keypos = 0; + unsigned u; + UINT32_T val, data_l, data_r; + char_u *key; + int keylen; + + /* Process the key 1001 times. + * See http://en.wikipedia.org/wiki/Key_strengthening. */ + key = sha256_key(password, salt, salt_len); + for (i = 0; i < 1000; i++) + key = sha256_key(key, salt, salt_len); + + /* Convert the key from 64 hex chars to 32 binary chars. */ + keylen = (int)STRLEN(key) / 2; + if (keylen == 0) + { + iemsg(_("E831: bf_key_init() called with empty password")); + return; + } + for (i = 0; i < keylen; i++) + { + sscanf((char *)&key[i * 2], "%2x", &u); + key[i] = u; + } + + /* Use "key" to initialize the P-array ("pax") and S-boxes ("sbx") of + * Blowfish. */ + mch_memmove(bfs->sbx, sbx_init, 4 * 4 * 256); + + for (i = 0; i < 18; ++i) + { + val = 0; + for (j = 0; j < 4; ++j) + val = (val << 8) | key[keypos++ % keylen]; + bfs->pax[i] = pax_init[i] ^ val; + } + + data_l = data_r = 0; + for (i = 0; i < 18; i += 2) + { + bf_e_block(bfs, &data_l, &data_r); + bfs->pax[i + 0] = data_l; + bfs->pax[i + 1] = data_r; + } + + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 256; j += 2) + { + bf_e_block(bfs, &data_l, &data_r); + bfs->sbx[i][j + 0] = data_l; + bfs->sbx[i][j + 1] = data_r; + } + } +} + +/* + * Blowfish self-test for corrupted tables or instructions. + */ + static int +bf_check_tables( + UINT32_T pax[18], + UINT32_T sbx[4][256], + UINT32_T val) +{ + int i, j; + UINT32_T c = 0; + + for (i = 0; i < 18; i++) + c ^= pax[i]; + for (i = 0; i < 4; i++) + for (j = 0; j < 256; j++) + c ^= sbx[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 i, bn; + int err = 0; + block8 bk; + UINT32_T ui = 0xffffffffUL; + bf_state_T state; + + vim_memset(&state, 0, sizeof(bf_state_T)); + state.cfb_len = BF_MAX_CFB_LEN; + + /* We can't simply use sizeof(UINT32_T), it would generate a compiler + * warning. */ + if (ui != 0xffffffffUL || ui + 1 != 0) { + err++; + emsg(_("E820: sizeof(uint32_t) != 4")); + } + + if (!bf_check_tables(pax_init, sbx_init, 0x6ffa520a)) + err++; + + bn = ARRAY_LENGTH(bf_test_data); + for (i = 0; i < bn; i++) + { + bf_key_init(&state, (char_u *)(bf_test_data[i].password), + bf_test_data[i].salt, + (int)STRLEN(bf_test_data[i].salt)); + if (!bf_check_tables(state.pax, state.sbx, bf_test_data[i].keysum)) + err++; + + /* Don't modify bf_test_data[i].plaintxt, self test is idempotent. */ + memcpy(bk.uc, bf_test_data[i].plaintxt, 8); + bf_e_cblock(&state, 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; +} + +/* + * CFB: Cipher Feedback Mode. + */ + +/* + * Initialize with seed "seed[seed_len]". + */ + static void +bf_cfb_init( + bf_state_T *bfs, + char_u *seed, + int seed_len) +{ + int i, mi; + + bfs->randbyte_offset = bfs->update_offset = 0; + vim_memset(bfs->cfb_buffer, 0, bfs->cfb_len); + if (seed_len > 0) + { + mi = seed_len > bfs->cfb_len ? seed_len : bfs->cfb_len; + for (i = 0; i < mi; i++) + bfs->cfb_buffer[i % bfs->cfb_len] ^= seed[i % seed_len]; + } +} + +#define BF_CFB_UPDATE(bfs, c) { \ + bfs->cfb_buffer[bfs->update_offset] ^= (char_u)c; \ + if (++bfs->update_offset == bfs->cfb_len) \ + bfs->update_offset = 0; \ +} + +#define BF_RANBYTE(bfs, t) { \ + if ((bfs->randbyte_offset & BF_BLOCK_MASK) == 0) \ + bf_e_cblock(bfs, &(bfs->cfb_buffer[bfs->randbyte_offset])); \ + t = bfs->cfb_buffer[bfs->randbyte_offset]; \ + if (++bfs->randbyte_offset == bfs->cfb_len) \ + bfs->randbyte_offset = 0; \ +} + +/* + * Encrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + void +crypt_blowfish_encode( + cryptstate_T *state, + char_u *from, + size_t len, + char_u *to) +{ + bf_state_T *bfs = state->method_state; + size_t i; + int ztemp, t; + + for (i = 0; i < len; ++i) + { + ztemp = from[i]; + BF_RANBYTE(bfs, t); + BF_CFB_UPDATE(bfs, ztemp); + to[i] = t ^ ztemp; + } +} + +/* + * Decrypt "from[len]" into "to[len]". + */ + void +crypt_blowfish_decode( + cryptstate_T *state, + char_u *from, + size_t len, + char_u *to) +{ + bf_state_T *bfs = state->method_state; + size_t i; + int t; + + for (i = 0; i < len; ++i) + { + BF_RANBYTE(bfs, t); + to[i] = from[i] ^ t; + BF_CFB_UPDATE(bfs, to[i]); + } +} + + void +crypt_blowfish_init( + cryptstate_T *state, + char_u* key, + char_u* salt, + int salt_len, + char_u* seed, + int seed_len) +{ + bf_state_T *bfs = (bf_state_T *)alloc_clear(sizeof(bf_state_T)); + + state->method_state = bfs; + + /* "blowfish" uses a 64 byte buffer, causing it to repeat 8 byte groups 8 + * times. "blowfish2" uses a 8 byte buffer to avoid repeating. */ + bfs->cfb_len = state->method_nr == CRYPT_M_BF ? BF_MAX_CFB_LEN : BF_BLOCK; + + if (blowfish_self_test() == FAIL) + return; + + bf_key_init(bfs, key, salt, salt_len); + bf_cfb_init(bfs, seed, seed_len); +} + +/* + * 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; +} + +#endif /* FEAT_CRYPT */ diff --git a/src/buffer.c b/src/buffer.c new file mode 100644 index 0000000..98d505f --- /dev/null +++ b/src/buffer.c @@ -0,0 +1,5948 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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 "vim.h" + +static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, int ignore_case); +static char_u *fname_match(regmatch_T *rmp, char_u *name, int ignore_case); +static void buflist_setfpos(buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, int copy_options); +#ifdef UNIX +static buf_T *buflist_findname_stat(char_u *ffname, stat_T *st); +static int otherfile_buf(buf_T *buf, char_u *ffname, stat_T *stp); +static int buf_same_ino(buf_T *buf, stat_T *stp); +#else +static int otherfile_buf(buf_T *buf, char_u *ffname); +#endif +#ifdef FEAT_TITLE +static int value_changed(char_u *str, char_u **last); +#endif +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); + +#ifdef UNIX +# define dev_T dev_t +#else +# define dev_T unsigned +#endif + +#if defined(FEAT_QUICKFIX) +static char *msg_loclist = N_("[Location List]"); +static char *msg_qflist = N_("[Quickfix List]"); +#endif +static char *e_auabort = N_("E855: Autocommands caused command to abort"); + +/* Number of times free_buffer() was called. */ +static int buf_free_count = 0; + +/* Read data from buffer for retrying. */ + static int +read_buffer( + int read_stdin, /* read file from stdin, otherwise fifo */ + exarg_T *eap, /* for forced 'ff' and 'fenc' or NULL */ + int flags) /* extra flags for readfile() */ +{ + int retval = OK; + linenr_T line_count; + + /* + * Read from the buffer which the text is already filled in and append at + * the end. This makes it possible to retry when 'fileformat' or + * 'fileencoding' was guessed wrong. + */ + line_count = curbuf->b_ml.ml_line_count; + retval = readfile( + read_stdin ? NULL : curbuf->b_ffname, + read_stdin ? NULL : curbuf->b_fname, + (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; + + if (read_stdin) + { + /* Set or reset 'modified' before executing autocommands, so that + * it can be changed there. */ + if (!readonlymode && !BUFEMPTY()) + changed(); + else if (retval == OK) + unchanged(curbuf, FALSE); + + if (retval == OK) + { +#ifdef FEAT_EVAL + apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE, + curbuf, &retval); +#else + apply_autocmds(EVENT_STDINREADPOST, NULL, NULL, FALSE, curbuf); +#endif + } + } + return retval; +} + +/* + * 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; + bufref_T old_curbuf; +#ifdef FEAT_SYN_HL + long old_tw = curbuf->b_p_tw; +#endif + int read_fifo = FALSE; + + /* + * 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_ALL_BUFFERS(curbuf) + 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); +#ifdef FEAT_SYN_HL + if (old_tw != curbuf->b_p_tw) + check_colorcolumn(curwin); +#endif + return FAIL; + } + + /* The autocommands in readfile() may change the buffer, but only AFTER + * reading the file. */ + set_bufref(&old_curbuf, curbuf); + modified_was_set = FALSE; + + /* mark cursor position as being invalid */ + curwin->w_valid = 0; + + if (curbuf->b_ffname != NULL +#ifdef FEAT_NETBEANS_INTG + && netbeansReadFile +#endif + ) + { + int old_msg_silent = msg_silent; +#ifdef UNIX + int save_bin = curbuf->b_p_bin; + int perm; +#endif +#ifdef FEAT_NETBEANS_INTG + int oldFire = netbeansFireChanges; + + netbeansFireChanges = 0; +#endif +#ifdef UNIX + perm = mch_getperm(curbuf->b_ffname); + if (perm >= 0 && (S_ISFIFO(perm) + || S_ISSOCK(perm) +# ifdef OPEN_CHR_FILES + || (S_ISCHR(perm) && is_dev_fd_file(curbuf->b_ffname)) +# endif + )) + read_fifo = TRUE; + if (read_fifo) + curbuf->b_p_bin = TRUE; +#endif + if (shortmess(SHM_FILEINFO)) + msg_silent = 1; + retval = readfile(curbuf->b_ffname, curbuf->b_fname, + (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, + flags | READ_NEW | (read_fifo ? READ_FIFO : 0)); +#ifdef UNIX + if (read_fifo) + { + curbuf->b_p_bin = save_bin; + if (retval == OK) + retval = read_buffer(FALSE, eap, flags); + } +#endif + msg_silent = old_msg_silent; +#ifdef FEAT_NETBEANS_INTG + netbeansFireChanges = oldFire; +#endif + /* Help buffer is filtered. */ + if (bt_help(curbuf)) + fix_help_buffer(); + } + else if (read_stdin) + { + int save_bin = curbuf->b_p_bin; + + /* + * 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) + retval = read_buffer(TRUE, eap, flags); + } + + /* if first time loading this buffer, init b_chartab[] */ + if (curbuf->b_flags & BF_NEVERLOADED) + { + (void)buf_init_chartab(curbuf, FALSE); +#ifdef FEAT_CINDENT + parse_cino(curbuf); +#endif + } + + /* + * 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 autocommands. + */ + /* 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 */ +#ifdef FEAT_EVAL + || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL) +#endif + ) + changed(); + else if (retval == OK && !read_stdin && !read_fifo) + unchanged(curbuf, FALSE); + save_file_ff(curbuf); /* keep this fileformat */ + + /* Set last_changedtick to avoid triggering a TextChanged autocommand right + * after it was added. */ + curbuf->b_last_changedtick = CHANGEDTICK(curbuf); +#ifdef FEAT_INS_EXPAND + curbuf->b_last_changedtick_pum = CHANGEDTICK(curbuf); +#endif + + /* require "!" to overwrite the file, because it wasn't read completely */ +#ifdef FEAT_EVAL + if (aborting()) +#else + if (got_int) +#endif + curbuf->b_flags |= BF_READERR; + +#ifdef FEAT_FOLDING + /* Need to update automatic folding. Do this before the autocommands, + * they may use the fold info. */ + foldUpdateAll(curwin); +#endif + + /* need to set w_topline, unless some autocommand already did that. */ + if (!(curwin->w_valid & VALID_TOPLINE)) + { + curwin->w_topline = 1; +#ifdef FEAT_DIFF + curwin->w_topfill = 0; +#endif + } +#ifdef FEAT_EVAL + apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval); +#else + apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); +#endif + + if (retval == OK) + { + /* + * The autocommands may have changed the current buffer. Apply the + * modelines to the correct buffer, if it still exists and is loaded. + */ + if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->b_ml.ml_mfp != NULL) + { + aco_save_T aco; + + /* Go to the buffer that was opened. */ + aucmd_prepbuf(&aco, old_curbuf.br_buf); + do_modelines(0); + curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED); + +#ifdef FEAT_EVAL + apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf, + &retval); +#else + apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); +#endif + + /* restore curwin/curbuf and a few other things */ + aucmd_restbuf(&aco); + } + } + + return retval; +} + +/* + * Store "buf" in "bufref" and set the free count. + */ + void +set_bufref(bufref_T *bufref, buf_T *buf) +{ + bufref->br_buf = buf; + bufref->br_fnum = buf == NULL ? 0 : buf->b_fnum; + bufref->br_buf_free_count = buf_free_count; +} + +/* + * Return TRUE if "bufref->br_buf" points to the same buffer as when + * set_bufref() was called and it is a valid buffer. + * Only goes through the buffer list if buf_free_count changed. + * Also checks if b_fnum is still the same, a :bwipe followed by :new might get + * the same allocated memory, but it's a different buffer. + */ + int +bufref_valid(bufref_T *bufref) +{ + return bufref->br_buf_free_count == buf_free_count + ? TRUE : buf_valid(bufref->br_buf) + && bufref->br_fnum == bufref->br_buf->b_fnum; +} + +/* + * Return TRUE if "buf" points to a valid buffer (in the buffer list). + * This can be slow if there are many buffers, prefer using bufref_valid(). + */ + int +buf_valid(buf_T *buf) +{ + buf_T *bp; + + /* Assume that we more often have a recent buffer, start with the last + * one. */ + for (bp = lastbuf; bp != NULL; bp = bp->b_prev) + if (bp == buf) + return TRUE; + return FALSE; +} + +/* + * A hash table used to quickly lookup a buffer by its number. + */ +static hashtab_T buf_hashtab; + + static void +buf_hashtab_add(buf_T *buf) +{ + sprintf((char *)buf->b_key, "%x", buf->b_fnum); + if (hash_add(&buf_hashtab, buf->b_key) == FAIL) + emsg(_("E931: Buffer cannot be registered")); +} + + static void +buf_hashtab_remove(buf_T *buf) +{ + hashitem_T *hi = hash_find(&buf_hashtab, buf->b_key); + + if (!HASHITEM_EMPTY(hi)) + hash_remove(&buf_hashtab, hi); +} + +/* + * Return TRUE when buffer "buf" can be unloaded. + * Give an error message and return FALSE when the buffer is locked or the + * screen is being redrawn and the buffer is in a window. + */ + static int +can_unload_buffer(buf_T *buf) +{ + int can_unload = !buf->b_locked; + + if (can_unload && updating_screen) + { + win_T *wp; + + FOR_ALL_WINDOWS(wp) + if (wp->w_buffer == buf) + { + can_unload = FALSE; + break; + } + } + if (!can_unload) + emsg(_("E937: Attempt to delete a buffer that is in use")); + return can_unload; +} + +/* + * 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 UNUSED) +{ + int is_curbuf; + int nwindows; + bufref_T bufref; + int is_curwin = (curwin != NULL && curwin->w_buffer == buf); + win_T *the_curwin = curwin; + tabpage_T *the_curtab = curtab; + 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; + +#ifdef FEAT_TERMINAL + if (bt_terminal(buf) && (buf->b_nwindows == 1 || del_buf)) + { + if (term_job_running(buf->b_term)) + { + if (wipe_buf || unload_buf) + { + if (!can_unload_buffer(buf)) + return; + + /* Wiping out or unloading a terminal buffer kills the job. */ + free_terminal(buf); + } + else + { + /* The job keeps running, hide the buffer. */ + del_buf = FALSE; + unload_buf = FALSE; + } + } + else + { + /* A terminal buffer is wiped out if the job has finished. */ + del_buf = TRUE; + unload_buf = TRUE; + wipe_buf = TRUE; + } + } +#endif + + /* Disallow deleting the buffer when it is locked (already being closed or + * halfway a command that relies on it). Unloading is allowed. */ + if ((del_buf || wipe_buf) && !can_unload_buffer(buf)) + return; + + /* check no autocommands closed the window */ + if (win != NULL && win_valid_any_tab(win)) + { + /* 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); + } + + set_bufref(&bufref, buf); + + /* When the buffer is no longer in a window, trigger BufWinLeave */ + if (buf->b_nwindows == 1) + { + ++buf->b_locked; + if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, + FALSE, buf) + && !bufref_valid(&bufref)) + { + /* Autocommands deleted the buffer. */ +aucmd_abort: + emsg(_(e_auabort)); + return; + } + --buf->b_locked; + 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_locked; + if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, + FALSE, buf) + && !bufref_valid(&bufref)) + /* Autocommands deleted the buffer. */ + goto aucmd_abort; + --buf->b_locked; + if (abort_if_last && one_window()) + /* Autocommands made this the only window. */ + goto aucmd_abort; + } +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return; +#endif + } + + /* If the buffer was in curwin and the window has changed, go back to that + * window, if it still exists. This avoids that ":edit x" triggering a + * "tabnext" BufUnload autocmd leaves a window behind without a buffer. */ + if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) + { + block_autocmds(); + goto_tabpage_win(the_curtab, the_curwin); + unblock_autocmds(); + } + + nwindows = buf->b_nwindows; + + /* decrease the link count from windows (unless not in any window) */ + if (buf->b_nwindows > 0) + --buf->b_nwindows; + +#ifdef FEAT_DIFF + if (diffopt_hiddenoff() && !unload_buf && buf->b_nwindows == 0) + diff_buf_delete(buf); /* Clear 'diff' for hidden buffer. */ +#endif + + /* 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; + + /* When closing the current buffer stop Visual mode before freeing + * anything. */ + if (buf == curbuf && VIsual_active +#if defined(EXITFREE) + && !entered_free_all_mem +#endif + ) + end_visual_mode(); + + /* + * 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)); + + /* Autocommands may have deleted the buffer. */ + if (!bufref_valid(&bufref)) + return; +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return; +#endif + + /* + * 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; + + if (win_valid_any_tab(win) && win->w_buffer == buf) + win->w_buffer = NULL; /* make sure we don't use the buffer now */ + + /* 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; + + /* + * Remove the buffer from the list. + */ + if (wipe_buf) + { + if (buf->b_sfname != buf->b_ffname) + VIM_CLEAR(buf->b_sfname); + else + buf->b_sfname = NULL; + VIM_CLEAR(buf->b_ffname); + 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_shortname = FALSE; + 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 */ +#ifdef FEAT_NETBEANS_INTG + netbeans_deleted_all_lines(buf); +#endif +} + +/* + * buf_freeall() - free all things allocated for a buffer that are related to + * the file. Careful: get here with "curwin" NULL when exiting. + * 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); + bufref_T bufref; + int is_curwin = (curwin != NULL && curwin->w_buffer == buf); + win_T *the_curwin = curwin; + tabpage_T *the_curtab = curtab; + + /* Make sure the buffer isn't closed by autocommands. */ + ++buf->b_locked; + set_bufref(&bufref, buf); + if (buf->b_ml.ml_mfp != NULL) + { + if (apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, + FALSE, buf) + && !bufref_valid(&bufref)) + /* autocommands deleted the buffer */ + return; + } + if ((flags & BFA_DEL) && buf->b_p_bl) + { + if (apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname, + FALSE, buf) + && !bufref_valid(&bufref)) + /* autocommands deleted the buffer */ + return; + } + if (flags & BFA_WIPE) + { + if (apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname, + FALSE, buf) + && !bufref_valid(&bufref)) + /* autocommands deleted the buffer */ + return; + } + --buf->b_locked; + + /* If the buffer was in curwin and the window has changed, go back to that + * window, if it still exists. This avoids that ":edit x" triggering a + * "tabnext" BufUnload autocmd leaves a window behind without a buffer. */ + if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) + { + block_autocmds(); + goto_tabpage_win(the_curtab, the_curwin); + unblock_autocmds(); + } + +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return; +#endif + + /* + * 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; +#ifdef FEAT_DIFF + diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */ +#endif +#ifdef FEAT_SYN_HL + /* Remove any ownsyntax, unless exiting. */ + if (curwin != NULL && curwin->w_buffer == buf) + reset_synblock(curwin); +#endif + +#ifdef FEAT_FOLDING + /* 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); + } +#endif + +#ifdef FEAT_TCL + tcl_buffer_free(buf); +#endif + 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 */ + } +#ifdef FEAT_SYN_HL + syntax_clear(&buf->b_s); /* reset syntax info */ +#endif +#ifdef FEAT_TEXT_PROP + clear_buf_prop_types(buf); +#endif + 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) +{ + ++buf_free_count; + free_buffer_stuff(buf, TRUE); +#ifdef FEAT_EVAL + /* b:changedtick uses an item in buf_T, remove it now */ + dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di); + unref_var_dict(buf->b_vars); +#endif +#ifdef FEAT_LUA + lua_buffer_free(buf); +#endif +#ifdef FEAT_MZSCHEME + mzscheme_buffer_free(buf); +#endif +#ifdef FEAT_PERL + perl_buf_free(buf); +#endif +#ifdef FEAT_PYTHON + python_buffer_free(buf); +#endif +#ifdef FEAT_PYTHON3 + python3_buffer_free(buf); +#endif +#ifdef FEAT_RUBY + ruby_buffer_free(buf); +#endif +#ifdef FEAT_JOB_CHANNEL + channel_buffer_free(buf); +#endif +#ifdef FEAT_TERMINAL + free_terminal(buf); +#endif +#ifdef FEAT_JOB_CHANNEL + vim_free(buf->b_prompt_text); + free_callback(buf->b_prompt_callback, buf->b_prompt_partial); +#endif + + buf_hashtab_remove(buf); + + 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 + vim_free(buf); +} + +/* + * Initializes b:changedtick. + */ + static void +init_changedtick(buf_T *buf) +{ + dictitem_T *di = (dictitem_T *)&buf->b_ct_di; + + di->di_flags = DI_FLAGS_FIX | DI_FLAGS_RO; + di->di_tv.v_type = VAR_NUMBER; + di->di_tv.v_lock = VAR_FIXED; + di->di_tv.vval.v_number = 0; + +#ifdef FEAT_EVAL + STRCPY(buf->b_ct_di.di_key, "changedtick"); + (void)dict_add(buf->b_vars, di); +#endif +} + +/* + * 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); +#ifdef FEAT_SPELL + ga_clear(&buf->b_s.b_langp); +#endif + } +#ifdef FEAT_EVAL + { + varnumber_T tick = CHANGEDTICK(buf); + + vars_clear(&buf->b_vars->dv_hashtab); /* free all buffer variables */ + hash_init(&buf->b_vars->dv_hashtab); + init_changedtick(buf); + CHANGEDTICK(buf) = tick; + } +#endif +#ifdef FEAT_USR_CMDS + uc_clear(&buf->b_ucmds); /* clear local user commands */ +#endif +#ifdef FEAT_SIGNS + buf_delete_signs(buf, (char_u *)"*"); // delete any signs */ +#endif +#ifdef FEAT_NETBEANS_INTG + netbeans_file_killed(buf); +#endif +#ifdef FEAT_LOCALMAP + map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */ + map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */ +#endif + VIM_CLEAR(buf->b_start_fenc); +} + +/* + * 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); +#ifdef FEAT_FOLDING + deleteFoldRecurse(&wip->wi_folds); +#endif + } + vim_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(HAS_SWAP_EXISTS_ACTION) + bufref_T old_curbuf; + + set_bufref(&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(HAS_SWAP_EXISTS_ACTION) + if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') + { +# if defined(FEAT_EVAL) + cleanup_T cs; + + /* Reset the error/interrupt/exception state here so that + * aborting() returns FALSE when closing a window. */ + enter_cleanup(&cs); +# endif + + /* Quitting means closing the split window, nothing else. */ + win_close(curwin, TRUE); + swap_exists_action = SEA_NONE; + swap_exists_did_quit = TRUE; + +# if defined(FEAT_EVAL) + /* Restore the error/interrupt/exception state if not discarded by a + * new aborting error, interrupt, or uncaught exception. */ + leave_cleanup(&cs); +# endif + } + 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(bufref_T *old_curbuf) +{ +# if defined(FEAT_EVAL) + cleanup_T cs; +# endif +# ifdef FEAT_SYN_HL + long old_tw = curbuf->b_p_tw; +# endif + buf_T *buf; + + if (swap_exists_action == SEA_QUIT) + { +# if defined(FEAT_EVAL) + /* Reset the error/interrupt/exception state here so that + * aborting() returns FALSE when closing a buffer. */ + enter_cleanup(&cs); +# endif + + /* 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 (old_curbuf == NULL || !bufref_valid(old_curbuf) + || old_curbuf->br_buf == curbuf) + buf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); + else + buf = old_curbuf->br_buf; + if (buf != NULL) + { + int old_msg_silent = msg_silent; + + if (shortmess(SHM_FILEINFO)) + msg_silent = 1; // prevent fileinfo message + enter_buffer(buf); + // restore msg_silent, so that the command line will be shown + msg_silent = old_msg_silent; + +# ifdef FEAT_SYN_HL + if (old_tw != curbuf->b_p_tw) + check_colorcolumn(curwin); +# endif + } + /* If "old_curbuf" is NULL we are in big trouble here... */ + +# if defined(FEAT_EVAL) + /* Restore the error/interrupt/exception state if not discarded by a + * new aborting error, interrupt, or uncaught exception. */ + leave_cleanup(&cs); +# endif + } + else if (swap_exists_action == SEA_RECOVER) + { +# if defined(FEAT_EVAL) + /* Reset the error/interrupt/exception state here so that + * aborting() returns FALSE when closing a buffer. */ + enter_cleanup(&cs); +# endif + + /* 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); + +# if defined(FEAT_EVAL) + /* Restore the error/interrupt/exception state if not discarded by a + * new aborting error, interrupt, or uncaught exception. */ + leave_cleanup(&cs); +# endif + } + 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 * +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 *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 _(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 = (char *)IObuff; + } + else if (deleted >= p_report) + { + if (command == DOBUF_UNLOAD) + smsg(NGETTEXT("%d buffer unloaded", + "%d buffers unloaded", deleted), deleted); + else if (command == DOBUF_DEL) + smsg(NGETTEXT("%d buffer deleted", + "%d buffers deleted", deleted), deleted); + else + smsg(NGETTEXT("%d buffer wiped out", + "%d buffers wiped out", deleted), deleted); + } + } + + + return errormsg; +} + +/* + * 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; + bufref_T bufref; + + if (action == DOBUF_UNLOAD) + { + emsg(_("E90: Cannot unload last buffer")); + return FAIL; + } + + set_bufref(&bufref, buf); + 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 && bufref_valid(&bufref) && 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) + semsg(_(e_nobufnr), count); + } + else if (dir == FORWARD) + emsg(_("E87: Cannot go beyond last buffer")); + else + emsg(_("E88: Cannot go before first buffer")); + return FAIL; + } + +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + + /* + * delete buffer buf from memory and/or the list + */ + if (unload) + { + int forward; + bufref_T bufref; + + if (!can_unload_buffer(buf)) + return FAIL; + + set_bufref(&bufref, buf); + + /* 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 defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + if ((p_confirm || cmdmod.confirm) && p_write) + { + dialog_changed(buf, FALSE); + if (!bufref_valid(&bufref)) + /* 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 +#endif + { + semsg(_("E89: No write since last change for buffer %d (add ! to override)"), + buf->b_fnum); + return FAIL; + } + } + + /* When closing the current buffer stop Visual mode. */ + if (buf == curbuf && VIsual_active) + end_visual_mode(); + + /* + * If deleting the last (listed) buffer, make it empty. + * The last (listed) buffer cannot be unloaded. + */ + FOR_ALL_BUFFERS(bp) + 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_locked > 0) + && (!ONE_WINDOW || 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 && bufref_valid(&bufref) && 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.br_buf, 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.br_buf != NULL && bufref_valid(&au_new_curbuf)) + buf = au_new_curbuf.br_buf; +#ifdef FEAT_JUMPLIST + 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; + } + } +#endif + + 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_ALL_BUFFERS(buf) + 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 defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + if ((p_confirm || cmdmod.confirm) && p_write) + { + bufref_T bufref; + + set_bufref(&bufref, buf); + dialog_changed(curbuf, FALSE); + if (!bufref_valid(&bufref)) + /* Autocommand deleted buffer, oops! */ + return FAIL; + } + if (bufIsChanged(curbuf)) +#endif + { + no_write_message(); + return FAIL; + } + } + + /* Go to the other buffer. */ + set_curbuf(buf, action); + + if (action == DOBUF_SPLIT) + { + RESET_BINDING(curwin); /* reset 'scrollbind' and 'cursorbind' */ + } + +#if defined(FEAT_EVAL) + if (aborting()) /* autocmds may abort script processing */ + return FAIL; +#endif + + 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); +#ifdef FEAT_SYN_HL + long old_tw = curbuf->b_p_tw; +#endif + bufref_T newbufref; + bufref_T prevbufref; + + 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 and wipe out "buf" + */ + prevbuf = curbuf; + set_bufref(&prevbufref, prevbuf); + set_bufref(&newbufref, buf); + + /* Autocommands may delete the curren buffer and/or the buffer we wan to go + * to. In those cases don't close the buffer. */ + if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf) + || (bufref_valid(&prevbufref) + && bufref_valid(&newbufref) +#ifdef FEAT_EVAL + && !aborting() +#endif + )) + { +#ifdef FEAT_SYN_HL + if (prevbuf == curwin->w_buffer) + reset_synblock(curwin); +#endif + if (unload) + close_windows(prevbuf, FALSE); +#if defined(FEAT_EVAL) + if (bufref_valid(&prevbufref) && !aborting()) +#else + if (bufref_valid(&prevbufref)) +#endif + { + win_T *previouswin = curwin; + if (prevbuf == curbuf) + u_sync(FALSE); + close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf, + unload ? action : (action == DOBUF_GOTO + && !buf_hide(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 +#ifdef FEAT_EVAL + && !aborting() +#endif + ) || curwin->w_buffer == NULL) + { + enter_buffer(buf); +#ifdef FEAT_SYN_HL + if (old_tw != curbuf->b_p_tw) + check_colorcolumn(curwin); +#endif + } +} + +/* + * 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); +#ifdef FEAT_FOLDING + else + /* Remove all folds in the window. */ + clearFolding(curwin); + foldUpdateAll(curwin); /* update folds (later). */ +#endif + + /* Get the buffer in the current window. */ + curwin->w_buffer = buf; + curbuf = buf; + ++curbuf->b_nwindows; + +#ifdef FEAT_DIFF + if (curwin->w_p_diff) + diff_buf_add(curbuf); +#endif + +#ifdef FEAT_SYN_HL + curwin->w_s = &(curbuf->b_s); +#endif + + /* 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; + + buflist_setfpos(curbuf, curwin, curbuf->b_last_cursor.lnum, + curbuf->b_last_cursor.col, TRUE); + + /* 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; +#ifdef FEAT_DIFF + curwin->w_topfill = 0; +#endif + 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 */ +#ifdef FEAT_TITLE + maketitle(); +#endif + /* when autocmds didn't change it */ + if (curwin->w_topline == 1 && !curwin->w_topline_was_set) + scroll_cursor_halfway(FALSE); /* redisplay at correct position */ + +#ifdef FEAT_NETBEANS_INTG + /* Send fileOpened event because we've changed buffers. */ + netbeans_file_activated(curbuf); +#endif + + /* Change directories when the 'acd' option is set. */ + DO_AUTOCHDIR; + +#ifdef FEAT_KEYMAP + if (curbuf->b_kmap_state & KEYMAP_INIT) + (void)keymap_init(); +#endif +#ifdef FEAT_SPELL + /* 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); +#endif +#ifdef FEAT_VIMINFO + curbuf->b_last_used = vim_time(); +#endif + + redraw_later(NOT_VALID); +} + +#if defined(FEAT_AUTOCHDIR) || defined(PROTO) +/* + * Change to the directory of the current buffer. + * Don't do this while still starting up. + */ + void +do_autochdir(void) +{ + if ((starting == 0 || test_autochdir) + && curbuf->b_ffname != NULL + && vim_chdirfile(curbuf->b_ffname, "auto") == OK) + shorten_fnames(TRUE); +} +#endif + + void +no_write_message(void) +{ +#ifdef FEAT_TERMINAL + if (term_job_running(curbuf->b_term)) + emsg(_("E948: Job still running (add ! to end the job)")); + else +#endif + emsg(_("E37: No write since last change (add ! to override)")); +} + + void +no_write_message_nobang(buf_T *buf UNUSED) +{ +#ifdef FEAT_TERMINAL + if (term_job_running(buf->b_term)) + emsg(_("E948: Job still running")); + else +#endif + emsg(_("E37: No write since last change")); +} + +/* + * functions for dealing with the buffer list + */ + +static int top_file_num = 1; /* highest file number */ + +/* + * Return TRUE if the current buffer is empty, unnamed, unmodified and used in + * only one window. That means it can be re-used. + */ + int +curbuf_reusable(void) +{ + return (curbuf != NULL + && curbuf->b_ffname == NULL + && curbuf->b_nwindows <= 1 + && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY()) + && !curbufIsChanged()); +} + +/* + * 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. + * If (flags & BLN_NEW) is TRUE, don't use an existing buffer. + * If (flags & BLN_NOOPT) is TRUE, don't copy options from the current buffer + * if the buffer already exists. + * This is the ONLY way to create a new buffer. + */ + buf_T * +buflist_new( + char_u *ffname_arg, // full path of fname or relative + char_u *sfname_arg, // short fname or NULL + linenr_T lnum, // preferred cursor line + int flags) // BLN_ defines +{ + char_u *ffname = ffname_arg; + char_u *sfname = sfname_arg; + buf_T *buf; +#ifdef UNIX + stat_T st; +#endif + + if (top_file_num == 1) + hash_init(&buf_hashtab); + + fname_expand(curbuf, &ffname, &sfname); // will allocate ffname + + /* + * If file name already exists in the list, update the entry. + */ +#ifdef UNIX + /* On Unix we can use inode numbers when the file exists. Works better + * for hard links. */ + if (sfname == NULL || mch_stat((char *)sfname, &st) < 0) + st.st_dev = (dev_T)-1; +#endif + if (ffname != NULL && !(flags & (BLN_DUMMY | BLN_NEW)) && (buf = +#ifdef UNIX + buflist_findname_stat(ffname, &st) +#else + buflist_findname(ffname) +#endif + ) != NULL) + { + vim_free(ffname); + if (lnum != 0) + buflist_setfpos(buf, curwin, lnum, (colnr_T)0, FALSE); + + if ((flags & BLN_NOOPT) == 0) + /* 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) + { + bufref_T bufref; + + buf->b_p_bl = TRUE; + set_bufref(&bufref, buf); + if (!(flags & BLN_DUMMY)) + { + if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf) + && !bufref_valid(&bufref)) + 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_reusable()) + { + 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); +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return NULL; +#endif + 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 = (buf_T *)alloc_clear((unsigned)sizeof(buf_T)); + if (buf == NULL) + { + vim_free(ffname); + return NULL; + } +#ifdef FEAT_EVAL + /* init b: variables */ + buf->b_vars = dict_alloc(); + if (buf->b_vars == NULL) + { + vim_free(ffname); + vim_free(buf); + return NULL; + } + init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); +#endif + init_changedtick(buf); + } + + if (ffname != NULL) + { + buf->b_ffname = ffname; + buf->b_sfname = vim_strsave(sfname); + } + + clear_wininfo(buf); + buf->b_wininfo = (wininfo_T *)alloc_clear((unsigned)sizeof(wininfo_T)); + + if ((ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL)) + || buf->b_wininfo == NULL) + { + if (buf->b_sfname != buf->b_ffname) + VIM_CLEAR(buf->b_sfname); + else + buf->b_sfname = NULL; + VIM_CLEAR(buf->b_ffname); + 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 defined(FEAT_EVAL) + if (aborting()) /* autocmds may abort script processing */ + return NULL; +#endif + free_buffer_stuff(buf, FALSE); /* delete local variables et al. */ + + /* Init the options. */ + buf->b_p_initialized = FALSE; + buf_copy_options(buf, BCO_ENTER); + +#ifdef FEAT_KEYMAP + /* need to reload lmaps and set b:keymap_name */ + curbuf->b_kmap_state |= KEYMAP_INIT; +#endif + } + 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; + } + buf_hashtab_add(buf); + + /* + * 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; + +#ifdef FEAT_SYN_HL + hash_init(&buf->b_s.b_keywtab); + hash_init(&buf->b_s.b_keywtab_ic); +#endif + + buf->b_fname = buf->b_sfname; +#ifdef UNIX + if (st.st_dev == (dev_T)-1) + buf->b_dev_valid = FALSE; + else + { + buf->b_dev_valid = TRUE; + buf->b_dev = st.st_dev; + buf->b_ino = st.st_ino; + } +#endif + 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)) + { + bufref_T bufref; + + /* Tricky: these autocommands may change the buffer list. They could + * also split the window with re-using the one empty buffer. This may + * result in unexpectedly losing the empty buffer. */ + set_bufref(&bufref, buf); + if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf) + && !bufref_valid(&bufref)) + return NULL; + if (flags & BLN_LISTED) + { + if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf) + && !bufref_valid(&bufref)) + return NULL; + } +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return NULL; +#endif + } + + 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); + } +#ifdef FEAT_FIND_ID + clear_string_option(&buf->b_p_def); + clear_string_option(&buf->b_p_inc); +# ifdef FEAT_EVAL + clear_string_option(&buf->b_p_inex); +# endif +#endif +#if defined(FEAT_CINDENT) && defined(FEAT_EVAL) + clear_string_option(&buf->b_p_inde); + clear_string_option(&buf->b_p_indk); +#endif +#if defined(FEAT_BEVAL) && defined(FEAT_EVAL) + clear_string_option(&buf->b_p_bexpr); +#endif +#if defined(FEAT_CRYPT) + clear_string_option(&buf->b_p_cm); +#endif + clear_string_option(&buf->b_p_fp); +#if defined(FEAT_EVAL) + clear_string_option(&buf->b_p_fex); +#endif +#ifdef FEAT_CRYPT + clear_string_option(&buf->b_p_key); +#endif + 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); +#ifdef FEAT_VARTABS + clear_string_option(&buf->b_p_vsts); + if (buf->b_p_vsts_nopaste) + vim_free(buf->b_p_vsts_nopaste); + buf->b_p_vsts_nopaste = NULL; + if (buf->b_p_vsts_array) + vim_free(buf->b_p_vsts_array); + buf->b_p_vsts_array = NULL; + clear_string_option(&buf->b_p_vts); + if (buf->b_p_vts_array) + vim_free(buf->b_p_vts_array); + buf->b_p_vts_array = NULL; +#endif +#ifdef FEAT_KEYMAP + clear_string_option(&buf->b_p_keymap); + keymap_clear(&buf->b_kmap_ga); + ga_clear(&buf->b_kmap_ga); +#endif +#ifdef FEAT_COMMENTS + clear_string_option(&buf->b_p_com); +#endif +#ifdef FEAT_FOLDING + clear_string_option(&buf->b_p_cms); +#endif + clear_string_option(&buf->b_p_nf); +#ifdef FEAT_SYN_HL + clear_string_option(&buf->b_p_syn); + clear_string_option(&buf->b_s.b_syn_isk); +#endif +#ifdef FEAT_SPELL + 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); +#endif +#ifdef FEAT_SEARCHPATH + clear_string_option(&buf->b_p_sua); +#endif + clear_string_option(&buf->b_p_ft); +#ifdef FEAT_CINDENT + clear_string_option(&buf->b_p_cink); + clear_string_option(&buf->b_p_cino); +#endif +#if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT) + clear_string_option(&buf->b_p_cinw); +#endif +#ifdef FEAT_INS_EXPAND + clear_string_option(&buf->b_p_cpt); +#endif +#ifdef FEAT_COMPL_FUNC + clear_string_option(&buf->b_p_cfu); + clear_string_option(&buf->b_p_ofu); +#endif +#ifdef FEAT_QUICKFIX + clear_string_option(&buf->b_p_gp); + clear_string_option(&buf->b_p_mp); + clear_string_option(&buf->b_p_efm); +#endif + 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_tc); +#ifdef FEAT_INS_EXPAND + clear_string_option(&buf->b_p_dict); + clear_string_option(&buf->b_p_tsr); +#endif +#ifdef FEAT_TEXTOBJ + clear_string_option(&buf->b_p_qe); +#endif + buf->b_p_ar = -1; + buf->b_p_ul = NO_LOCAL_UNDOLEVEL; +#ifdef FEAT_LISP + clear_string_option(&buf->b_p_lw); +#endif + clear_string_option(&buf->b_p_bkc); + clear_string_option(&buf->b_p_menc); +} + +/* + * 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 + semsg(_("E92: Buffer %d 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", "vsplit" or "newtab" and the + * current buffer isn't empty: open new tab or window */ + if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB)) + && !BUFEMPTY()) + { + if (swb_flags & SWB_NEWTAB) + tabpage_new(); + else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0) + == FAIL) + return FAIL; + RESET_BINDING(curwin); + } + } + + ++RedrawingDisabled; + if (GETFILE_SUCCESS(getfile(buf->b_fnum, NULL, NULL, + (options & GETF_SETMARK), lnum, forceit))) + { + --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; + } +} + +#if defined(FEAT_QUICKFIX) || defined(FEAT_EVAL) || defined(PROTO) +/* + * 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); + vim_free(ffname); + } + return buf; +} +#endif + +/* + * 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) +{ +#ifdef UNIX + stat_T st; + + if (mch_stat((char *)ffname, &st) < 0) + st.st_dev = (dev_T)-1; + return buflist_findname_stat(ffname, &st); +} + +/* + * Same as buflist_findname(), but pass the stat structure to avoid getting it + * twice for the same file. + * Returns NULL if not found. + */ + static buf_T * +buflist_findname_stat( + char_u *ffname, + stat_T *stp) +{ +#endif + buf_T *buf; + + /* Start at the last buffer, expect to find a match sooner. */ + for (buf = lastbuf; buf != NULL; buf = buf->b_prev) + if ((buf->b_flags & BF_DUMMY) == 0 && !otherfile_buf(buf, ffname +#ifdef UNIX + , stp +#endif + )) + 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 UNUSED, /* find diff-mode buffers only */ + int curtab_only) /* find buffers in current tab only */ +{ + buf_T *buf; + 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; +#ifdef FEAT_DIFF + if (diffmode && !diff_mode_buf(buflist_findnr(match))) + match = -1; +#endif + } + + /* + * 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) + { + regmatch_T regmatch; + + /* may add '^' and '$' */ + if (toggledollar) + *patend = (attempt < 2) ? NUL : '$'; /* add/remove '$' */ + p = pat; + if (*p == '^' && !(attempt & 1)) /* add/remove '^' */ + ++p; + regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0); + if (regmatch.regprog == NULL) + { + vim_free(pat); + return -1; + } + + for (buf = lastbuf; buf != NULL; buf = buf->b_prev) + if (buf->b_p_bl == find_listed +#ifdef FEAT_DIFF + && (!diffmode || diff_mode_buf(buf)) +#endif + && buflist_match(®match, buf, FALSE) != NULL) + { + if (curtab_only) + { + /* Ignore the match if the buffer is not open in + * the current tab. */ + win_T *wp; + + FOR_ALL_WINDOWS(wp) + 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(regmatch.regprog); + 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; + } + + vim_free(pat); + } + + if (match == -2) + semsg(_("E93: More than one match for %s"), pattern); + else if (match < 0) + semsg(_("E94: No matching buffer for %s"), pattern); + return match; +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + +/* + * 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; + 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 = alloc((unsigned)STRLEN(pat) + 11); + if (patc == NULL) + return FAIL; + 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) + { + regmatch_T regmatch; + + if (attempt > 0 && patc == pat) + break; /* there was no anchor, no need to try again */ + regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC); + if (regmatch.regprog == NULL) + { + if (patc != pat) + vim_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_ALL_BUFFERS(buf) + { + if (!buf->b_p_bl) /* skip unlisted buffers */ + continue; + p = buflist_match(®match, buf, p_wic); + 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 = (char_u **)alloc((unsigned)(count * sizeof(char_u *))); + if (*file == NULL) + { + vim_regfree(regmatch.regprog); + if (patc != pat) + vim_free(patc); + return FAIL; + } + } + } + vim_regfree(regmatch.regprog); + if (count) /* match(es) found, break here */ + break; + } + + if (patc != pat) + vim_free(patc); + + *num_file = count; + return (count == 0 ? FAIL : OK); +} + +#endif /* FEAT_CMDL_COMPL */ + +/* + * Check for a match on the file name for buffer "buf" with regprog "prog". + */ + static char_u * +buflist_match( + regmatch_T *rmp, + buf_T *buf, + int ignore_case) /* when TRUE ignore case, when FALSE use 'fic' */ +{ + char_u *match; + + /* First try the short file name, then the long file name. */ + match = fname_match(rmp, buf->b_sfname, ignore_case); + if (match == NULL) + match = fname_match(rmp, buf->b_ffname, ignore_case); + + 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( + regmatch_T *rmp, + char_u *name, + int ignore_case) /* when TRUE ignore case, when FALSE use 'fic' */ +{ + char_u *match = NULL; + char_u *p; + + if (name != NULL) + { + /* Ignore case when 'fileignorecase' or the argument is set. */ + rmp->rm_ic = p_fic || ignore_case; + if (vim_regexec(rmp, 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(rmp, p, (colnr_T)0)) + match = name; + vim_free(p); + } + } + + return match; +} + +/* + * Find a file in the buffer list by buffer number. + */ + buf_T * +buflist_findnr(int nr) +{ + char_u key[VIM_SIZEOF_INT * 2 + 1]; + hashitem_T *hi; + + if (nr == 0) + nr = curwin->w_alt_fnum; + sprintf((char *)key, "%x", nr); + hi = hash_find(&buf_hashtab, key); + + if (!HASHITEM_EMPTY(hi)) + return (buf_T *)(hi->hi_key + - ((unsigned)(curbuf->b_key - (char_u *)curbuf))); + 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 = (wininfo_T *)alloc_clear((unsigned)sizeof(wininfo_T)); + if (wip == NULL) + return; + 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); +#ifdef FEAT_FOLDING + deleteFoldRecurse(&wip->wi_folds); +#endif + } + } + 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); +#ifdef FEAT_FOLDING + wip->wi_fold_manual = win->w_fold_manual; + cloneFoldGrowArray(&win->w_folds, &wip->wi_folds); +#endif + 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; +} + +#ifdef FEAT_DIFF +/* + * 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_ALL_WINDOWS(wp) + /* 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; +} +#endif + +/* + * 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 UNUSED) +{ + wininfo_T *wip; + + for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) + if (wip->wi_win == curwin +#ifdef FEAT_DIFF + && (!skip_diff_buffer || !wininfo_other_tab_diff(wip)) +#endif + ) + 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) + { +#ifdef FEAT_DIFF + if (skip_diff_buffer) + { + for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) + if (!wininfo_other_tab_diff(wip)) + break; + } + else +#endif + 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); +#ifdef FEAT_FOLDING + clearFolding(curwin); +#endif + + wip = find_wininfo(buf, TRUE); + if (wip != NULL && wip->wi_win != NULL + && wip->wi_win != curwin && wip->wi_win->w_buffer == buf) + { + /* The buffer is currently displayed in the window: use the actual + * option values instead of the saved (possibly outdated) values. */ + win_T *wp = wip->wi_win; + + copy_winopt(&wp->w_onebuf_opt, &curwin->w_onebuf_opt); +#ifdef FEAT_FOLDING + curwin->w_fold_manual = wp->w_fold_manual; + curwin->w_foldinvalid = TRUE; + cloneFoldGrowArray(&wp->w_folds, &curwin->w_folds); +#endif + } + else if (wip != NULL && wip->wi_optset) + { + /* the buffer was displayed in the current window earlier */ + copy_winopt(&wip->wi_opt, &curwin->w_onebuf_opt); +#ifdef FEAT_FOLDING + curwin->w_fold_manual = wip->wi_fold_manual; + curwin->w_foldinvalid = TRUE; + cloneFoldGrowArray(&wip->wi_folds, &curwin->w_folds); +#endif + } + else + copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt); + +#ifdef FEAT_FOLDING + /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */ + if (p_fdls >= 0) + curwin->w_p_fdl = p_fdls; +#endif +#ifdef FEAT_SYN_HL + check_colorcolumn(curwin); +#endif +} + +/* + * 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 = {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 known file names (for :files and :buffers command). + */ + void +buflist_list(exarg_T *eap) +{ + buf_T *buf; + int len; + int i; + int ro_char; + int changed_char; +#ifdef FEAT_TERMINAL + int job_running; + int job_none_open; +#endif + + for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next) + { +#ifdef FEAT_TERMINAL + job_running = term_job_running(buf->b_term); + job_none_open = job_running && term_none_open(buf->b_term); +#endif + /* skip unlisted buffers, unless ! was used */ + if ((!buf->b_p_bl && !eap->forceit && !vim_strchr(eap->arg, 'u')) + || (vim_strchr(eap->arg, 'u') && buf->b_p_bl) + || (vim_strchr(eap->arg, '+') + && ((buf->b_flags & BF_READERR) || !bufIsChanged(buf))) + || (vim_strchr(eap->arg, 'a') + && (buf->b_ml.ml_mfp == NULL || buf->b_nwindows == 0)) + || (vim_strchr(eap->arg, 'h') + && (buf->b_ml.ml_mfp == NULL || buf->b_nwindows != 0)) +#ifdef FEAT_TERMINAL + || (vim_strchr(eap->arg, 'R') + && (!job_running || (job_running && job_none_open))) + || (vim_strchr(eap->arg, '?') + && (!job_running || (job_running && !job_none_open))) + || (vim_strchr(eap->arg, 'F') + && (job_running || buf->b_term == NULL)) +#endif + || (vim_strchr(eap->arg, '-') && buf->b_p_ma) + || (vim_strchr(eap->arg, '=') && !buf->b_p_ro) + || (vim_strchr(eap->arg, 'x') && !(buf->b_flags & BF_READERR)) + || (vim_strchr(eap->arg, '%') && buf != curbuf) + || (vim_strchr(eap->arg, '#') + && (buf == curbuf || curwin->w_alt_fnum != buf->b_fnum))) + continue; + if (buf_spname(buf) != NULL) + vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1); + else + home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE); + if (message_filtered(NameBuff)) + continue; + + changed_char = (buf->b_flags & BF_READERR) ? 'x' + : (bufIsChanged(buf) ? '+' : ' '); +#ifdef FEAT_TERMINAL + if (term_job_running(buf->b_term)) + { + if (term_none_open(buf->b_term)) + ro_char = '?'; + else + ro_char = 'R'; + changed_char = ' '; /* bufIsChanged() returns TRUE to avoid + * closing, but it's not actually changed. */ + } + else if (buf->b_term != NULL) + ro_char = 'F'; + else +#endif + ro_char = !buf->b_p_ma ? '-' : (buf->b_p_ro ? '=' : ' '); + + msg_putchar('\n'); + 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'), + ro_char, + changed_char, + NameBuff); + if (len > IOSIZE - 20) + len = IOSIZE - 20; + + /* 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 %ld"), buf == curbuf ? curwin->w_cursor.lnum + : (long)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_arg", short file name to + * "sfname_arg". + * 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_arg, + char_u *sfname_arg, + int message) /* give message when buffer already exists */ +{ + char_u *ffname = ffname_arg; + char_u *sfname = sfname_arg; + buf_T *obuf = NULL; +#ifdef UNIX + stat_T st; +#endif + + if (ffname == NULL || *ffname == NUL) + { + /* Removing the name. */ + if (buf->b_sfname != buf->b_ffname) + VIM_CLEAR(buf->b_sfname); + else + buf->b_sfname = NULL; + VIM_CLEAR(buf->b_ffname); +#ifdef UNIX + st.st_dev = (dev_T)-1; +#endif + } + 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 + */ +#ifdef UNIX + if (mch_stat((char *)ffname, &st) < 0) + st.st_dev = (dev_T)-1; +#endif + if (!(buf->b_flags & BF_DUMMY)) +#ifdef UNIX + obuf = buflist_findname_stat(ffname, &st); +#else + obuf = buflist_findname(ffname); +#endif + 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")); + vim_free(ffname); + return FAIL; + } + /* delete from the list */ + close_buffer(NULL, obuf, DOBUF_WIPE, FALSE); + } + sfname = vim_strsave(sfname); + if (ffname == NULL || sfname == NULL) + { + vim_free(sfname); + vim_free(ffname); + return FAIL; + } +#ifdef USE_FNAME_CASE +# ifdef USE_LONG_FNAME + if (USE_LONG_FNAME) +# endif + fname_case(sfname, 0); /* set correct case for short file name */ +#endif + if (buf->b_sfname != buf->b_ffname) + vim_free(buf->b_sfname); + vim_free(buf->b_ffname); + buf->b_ffname = ffname; + buf->b_sfname = sfname; + } + buf->b_fname = buf->b_sfname; +#ifdef UNIX + if (st.st_dev == (dev_T)-1) + buf->b_dev_valid = FALSE; + else + { + buf->b_dev_valid = TRUE; + buf->b_dev = st.st_dev; + buf->b_ino = st.st_ino; + } +#endif + + buf->b_shortname = FALSE; + + 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) + { + if (buf->b_sfname != buf->b_ffname) + vim_free(buf->b_sfname); + vim_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 */ +#ifdef FEAT_TITLE + maketitle(); /* set window title */ +#endif + 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_ALL_BUFFERS(bp) + { + 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 mch_FullName()). + */ + int +otherfile(char_u *ffname) +{ + return otherfile_buf(curbuf, ffname +#ifdef UNIX + , NULL +#endif + ); +} + + static int +otherfile_buf( + buf_T *buf, + char_u *ffname +#ifdef UNIX + , stat_T *stp +#endif + ) +{ + /* no name is different */ + if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) + return TRUE; + if (fnamecmp(ffname, buf->b_ffname) == 0) + return FALSE; +#ifdef UNIX + { + stat_T st; + + /* If no stat_T given, get it now */ + if (stp == NULL) + { + if (!buf->b_dev_valid || mch_stat((char *)ffname, &st) < 0) + st.st_dev = (dev_T)-1; + stp = &st; + } + /* 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 + * mch_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, stp)) + { + buf_setino(buf); + if (buf_same_ino(buf, stp)) + return FALSE; + } + } +#endif + return TRUE; +} + +#if defined(UNIX) || defined(PROTO) +/* + * Set inode and device number for a buffer. + * Must always be called when b_fname is changed!. + */ + void +buf_setino(buf_T *buf) +{ + stat_T st; + + if (buf->b_fname != NULL && mch_stat((char *)buf->b_fname, &st) >= 0) + { + buf->b_dev_valid = TRUE; + buf->b_dev = st.st_dev; + buf->b_ino = st.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, + stat_T *stp) +{ + return (buf->b_dev_valid + && stp->st_dev == buf->b_dev + && stp->st_ino == buf->b_ino); +} +#endif + +/* + * 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 *p; + char *buffer; + size_t len; + + buffer = (char *)alloc(IOSIZE); + if (buffer == NULL) + return; + + if (fullname > 1) /* 2 CTRL-G: include buffer number */ + { + vim_snprintf(buffer, IOSIZE, "buf %d: ", curbuf->b_fnum); + p = buffer + STRLEN(buffer); + } + else + p = buffer; + + *p++ = '"'; + if (buf_spname(curbuf) != NULL) + vim_strncpy((char_u *)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, (char_u *)p, + (int)(IOSIZE - (p - buffer)), TRUE); + } + + vim_snprintf_add(buffer, IOSIZE, "\"%s%s%s%s%s%s", + curbufIsChanged() ? (shortmess(SHM_MOD) + ? " [+]" : _(" [Modified]")) : " ", + (curbuf->b_flags & BF_NOTEDITED) +#ifdef FEAT_QUICKFIX + && !bt_dontwrite(curbuf) +#endif + ? _("[Not edited]") : "", + (curbuf->b_flags & BF_NEW) +#ifdef FEAT_QUICKFIX + && !bt_dontwrite(curbuf) +#endif + ? _("[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(buffer, IOSIZE, "%s", _(no_lines_msg)); +#ifdef FEAT_CMDL_INFO + else if (p_ru) + /* Current line and column are already on the screen -- webb */ + vim_snprintf_add(buffer, IOSIZE, + NGETTEXT("%ld line --%d%%--", "%ld lines --%d%%--", + curbuf->b_ml.ml_line_count), + (long)curbuf->b_ml.ml_line_count, n); +#endif + else + { + vim_snprintf_add(buffer, IOSIZE, + _("line %ld of %ld --%d%%-- col "), + (long)curwin->w_cursor.lnum, + (long)curbuf->b_ml.ml_line_count, + n); + validate_virtcol(); + len = STRLEN(buffer); + col_print((char_u *)buffer + len, IOSIZE - len, + (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); + } + + (void)append_arg_number(curwin, (char_u *)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 = (char *)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((char_u *)p, 0); + } + + vim_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); +} + +#if defined(FEAT_TITLE) || defined(PROTO) +static char_u *lasttitle = NULL; +static char_u *lasticon = NULL; + +/* + * Put the file name in the title bar and icon of the window. + */ + void +maketitle(void) +{ + char_u *p; + char_u *title_str = NULL; + char_u *icon_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; // nothing to do + + if (p_title) + { + if (p_titlelen > 0) + { + maxlen = p_titlelen * Columns / 100; + if (maxlen < 10) + maxlen = 10; + } + + title_str = buf; + if (*p_titlestring != NUL) + { +#ifdef FEAT_STL_OPT + if (stl_syntax & STL_IN_TITLE) + { + int use_sandbox = FALSE; + int save_called_emsg = called_emsg; + +# ifdef FEAT_EVAL + use_sandbox = was_set_insecurely((char_u *)"titlestring", 0); +# endif + called_emsg = FALSE; + build_stl_str_hl(curwin, title_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 +#endif + title_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); +#ifdef FEAT_TERMINAL + else if (curbuf->b_term != NULL) + { + vim_strncpy(buf, term_get_status_text(curbuf->b_term), + SPACE_FOR_FNAME); + } +#endif + else + { + p = transstr(gettail(curbuf->b_fname)); + vim_strncpy(buf, p, SPACE_FOR_FNAME); + vim_free(p); + } + +#ifdef FEAT_TERMINAL + if (curbuf->b_term == NULL) +#endif + 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 +#ifdef FEAT_TERMINAL + && curbuf->b_term == NULL +#endif + ) + { + /* 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 = gettail_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)); + vim_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); + +#if defined(FEAT_CLIENTSERVER) + if (serverName != NULL) + { + STRCAT(buf, " - "); + vim_strcat(buf, serverName, IOSIZE); + } + else +#endif + 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 = value_changed(title_str, &lasttitle); + + if (p_icon) + { + icon_str = buf; + if (*p_iconstring != NUL) + { +#ifdef FEAT_STL_OPT + if (stl_syntax & STL_IN_ICON) + { + int use_sandbox = FALSE; + int save_called_emsg = called_emsg; + +# ifdef FEAT_EVAL + use_sandbox = was_set_insecurely((char_u *)"iconstring", 0); +# endif + called_emsg = FALSE; + build_stl_str_hl(curwin, icon_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 +#endif + icon_str = p_iconstring; + } + else + { + if (buf_spname(curbuf) != NULL) + p = buf_spname(curbuf); + else /* use file name only in icon */ + p = gettail(curbuf->b_ffname); + *icon_str = NUL; + /* Truncate name at 100 bytes. */ + len = (int)STRLEN(p); + if (len > 100) + { + len -= 100; + if (has_mbyte) + len += (*mb_tail_off)(p, p + len) + 1; + p += len; + } + STRCPY(icon_str, p); + trans_characters(icon_str, IOSIZE); + } + } + + mustset |= value_changed(icon_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 if resettitle() is to be called. + */ + static int +value_changed(char_u *str, char_u **last) +{ + if ((str == NULL) != (*last == NULL) + || (str != NULL && *last != NULL && STRCMP(str, *last) != 0)) + { + vim_free(*last); + if (str == NULL) + { + *last = NULL; + mch_restore_title( + last == &lasttitle ? SAVE_RESTORE_TITLE : SAVE_RESTORE_ICON); + } + 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) +{ + vim_free(lasttitle); + vim_free(lasticon); +} +# endif + +#endif /* FEAT_TITLE */ + +#if defined(FEAT_STL_OPT) || defined(FEAT_GUI_TABLINE) || defined(PROTO) +/* + * 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 UNUSED, /* "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) */ +{ + linenr_T lnum; + size_t len; + char_u *p; + char_u *s; + char_u *t; + int byteval; +#ifdef FEAT_EVAL + win_T *save_curwin; + buf_T *save_curbuf; +#endif + 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 group_end_userhl; + int group_start_userhl; + 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; + int save_must_redraw = must_redraw; + int save_redr_type = curwin->w_redr_type; + +#ifdef FEAT_EVAL + /* + * 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; + } +#endif + + if (fillchar == 0) + fillchar = ' '; + /* Can't handle a multi-byte fill character yet. */ + else if (mb_char2len(fillchar) > 1) + fillchar = '-'; + + // The cursor in windows other than the current one isn't always + // up-to-date, esp. because of autocommands and timers. + lnum = wp->w_cursor.lnum; + if (lnum > wp->w_buffer->b_ml.ml_line_count) + { + lnum = wp->w_buffer->b_ml.ml_line_count; + wp->w_cursor.lnum = lnum; + } + + // 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, 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. + len = STRLEN(p); + if (wp->w_cursor.col > (colnr_T)len) + { + // Line may have changed since checking the cursor column, or the lnum + // was adjusted above. + wp->w_cursor.col = (colnr_T)len; + wp->w_cursor.coladd = 0; + 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) + { + mch_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 and highlight group + * doesn't change */ + group_start_userhl = group_end_userhl = 0; + for (n = groupitem[groupdepth] - 1; n >= 0; n--) + { + if (item[n].type == Highlight) + { + group_start_userhl = group_end_userhl = item[n].minwid; + break; + } + } + for (n = groupitem[groupdepth] + 1; n < curitem; n++) + { + if (item[n].type == Normal) + break; + if (item[n].type == Highlight) + group_end_userhl = item[n].minwid; + } + if (n == curitem && group_start_userhl == group_end_userhl) + { + 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 = '<'; + mch_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 */ + mch_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 = gettail(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; + +#ifdef FEAT_EVAL + vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum); + set_internal_string_var((char_u *)"g:actual_curbuf", tmp); + + save_curbuf = curbuf; + save_curwin = curwin; + curwin = wp; + curbuf = wp->w_buffer; + + str = eval_to_string_safe(p, &t, use_sandbox); + + curwin = save_curwin; + curbuf = save_curbuf; + do_unlet((char_u *)"g:actual_curbuf", TRUE); + + if (str != NULL && *str != 0) + { + if (*skipdigits(str) == NUL) + { + num = atoi((char *)str); + VIM_CLEAR(str); + itemisflag = FALSE; + } + } +#endif + 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, (char_u *)"<%s>", tmp, TMPLEN)) + str = tmp; + break; + case STL_PAGENUM: +#if defined(FEAT_PRINTER) || defined(FEAT_GUI_TABLINE) + num = printer_page_num; +#else + num = 0; +#endif + break; + + case STL_BUFNO: + num = wp->w_buffer->b_fnum; + break; + + case STL_OFFSET_X: + base = 'X'; + /* FALLTHROUGH */ + case STL_OFFSET: +#ifdef FEAT_BYTEOFF + 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); +#endif + break; + + case STL_BYTEVAL_X: + base = 'X'; + /* FALLTHROUGH */ + 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; + +#if defined(FEAT_QUICKFIX) + 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; +#endif + + 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) + vim_free(str); + + if (num >= 0 || (!itemisflag && str && *str)) + prevchar_isflag = FALSE; /* Item not NULL, but not a flag */ + curitem++; + } + *p = NUL; + itemcnt = curitem; + +#ifdef FEAT_EVAL + if (usefmt != fmt) + vim_free(usefmt); +#endif + + 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; + } + + /* When inside update_screen we do not want redrawing a stausline, ruler, + * title, etc. to trigger another redraw, it may cause an endless loop. */ + if (updating_screen) + { + must_redraw = save_must_redraw; + curwin->w_redr_type = save_redr_type; + } + + return width; +} +#endif /* FEAT_STL_OPT */ + +#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 */ + + if (buflen < 3) /* need at least 3 chars for writing */ + return; + above = wp->w_topline - 1; +#ifdef FEAT_DIFF + above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill; + if (wp->w_topline == 1 && wp->w_topfill >= 1) + above = 0; /* All buffer lines are displayed and there is an + * indication of filler lines, that can be considered + * seeing all lines. */ +#endif + 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; +} + +/* + * If fname is not a full path, make it a full path. + * Returns pointer to allocated memory (NULL for failure). + */ + char_u * +fix_fname(char_u *fname) +{ + /* + * Force expanding the path always for Unix, because symbolic links may + * mess up the full path name, even though it starts with a '/'. + * Also expand when there is ".." in the file name, try to remove it, + * because "c:/src/../README" is equal to "c:/README". + * Similarly "c:/src//file" is equal to "c:/src/file". + * For MS-Windows also expand names like "longna~1" to "longname". + */ +#ifdef UNIX + return FullName_save(fname, TRUE); +#else + if (!vim_isAbsName(fname) + || strstr((char *)fname, "..") != NULL + || strstr((char *)fname, "//") != NULL +# ifdef BACKSLASH_IN_FILENAME + || strstr((char *)fname, "\\\\") != NULL +# endif +# if defined(MSWIN) + || vim_strchr(fname, '~') != NULL +# endif + ) + return FullName_save(fname, FALSE); + + fname = vim_strsave(fname); + +# ifdef USE_FNAME_CASE +# ifdef USE_LONG_FNAME + if (USE_LONG_FNAME) +# endif + { + if (fname != NULL) + fname_case(fname, 0); /* set correct case for file name */ + } +# endif + + return fname; +#endif +} + +/* + * Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL. + * "*ffname" becomes a pointer to allocated memory (or NULL). + * When resolving a link both "*sfname" and "*ffname" will point to the same + * allocated memory. + * The "*ffname" and "*sfname" pointer values on call will not be freed. + * Note that the resulting "*ffname" pointer should be considered not allocaed. + */ + void +fname_expand( + buf_T *buf UNUSED, + char_u **ffname, + char_u **sfname) +{ + if (*ffname == NULL) // no file name given, nothing to do + return; + if (*sfname == NULL) // 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) + { + vim_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 = alloc_clear((unsigned)opened_len); + if (opened == NULL) + return; + + /* 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; + +# ifdef FEAT_GUI + need_mouse_correct = TRUE; +# endif + + /* + * 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 + || fullpathcmp(alist_name(&AARGLIST(alist)[i]), + buf->b_ffname, TRUE) & FPC_SAME)) + { + 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 (buf_hide(buf) || forceit || buf->b_nwindows > 1 + || !bufIsChanged(buf)) + { + /* If the buffer was changed, and we would like to hide it, + * try autowriting. */ + if (!buf_hide(buf) && buf->b_nwindows <= 1 + && bufIsChanged(buf)) + { + bufref_T bufref; + + set_bufref(&bufref, buf); + + (void)autowrite(buf, FALSE); + + /* check if autocommands removed the window */ + if (!win_valid(wp) || !bufref_valid(&bufref)) + { + wpnext = firstwin; /* start all over... */ + continue; + } + } + /* don't close last window */ + if (ONE_WINDOW + && (first_tabpage->tp_next == NULL || !had_tab)) + use_firstwin = TRUE; + else + { + win_close(wp, !buf_hide(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, + ((buf_hide(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; + vim_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(); + +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + + /* + * 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)) && !ONE_WINDOW + && !(wp->w_closing || wp->w_buffer->b_locked > 0)) + { + 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_ALL_WINDOWS(wp) + 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) + { + bufref_T bufref; + + set_bufref(&bufref, buf); + + /* 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 (!bufref_valid(&bufref)) + { + /* 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) + { +# if defined(FEAT_EVAL) + cleanup_T cs; + + /* Reset the error/interrupt/exception state here so that + * aborting() returns FALSE when closing a window. */ + enter_cleanup(&cs); +# endif + + /* 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; + +# if defined(FEAT_EVAL) + /* Restore the error/interrupt/exception state if not + * discarded by a new aborting error, interrupt, or uncaught + * exception. */ + leave_cleanup(&cs); +# endif + } + else + handle_swap_exists(NULL); +#endif + } + + ui_breakcheck(); + if (got_int) + { + (void)vgetc(); /* only break the file loading, not the rest */ + break; + } +#ifdef FEAT_EVAL + /* Autocommands deleted the buffer or aborted script processing!!! */ + if (aborting()) + break; +#endif + /* 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 = (buf_hide(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, !buf_hide(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.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; +#ifdef FEAT_EVAL + sctx_T save_current_sctx; +#endif + + 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 "::" */ + { + int secure_save = secure; +#ifdef FEAT_EVAL + save_current_sctx = current_sctx; + current_sctx.sc_sid = SID_MODELINE; + current_sctx.sc_seq = 0; + current_sctx.sc_lnum = 0; +#endif + // Make sure no risky things are executed as a side effect. + ++secure; + + retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags); + + secure = secure_save; +#ifdef FEAT_EVAL + current_sctx = save_current_sctx; +#endif + 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; + + vim_free(linecopy); + } + return retval; +} + +#if defined(FEAT_VIMINFO) || defined(PROTO) + 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 = shorten_fname1(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); + } + } + vim_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 = alloc(LINE_BUF_LEN); + if (line == NULL) + return; + + FOR_ALL_TAB_WINDOWS(tp, win) + set_last_cursor(win); + + fputs(_("\n# Buffer list:\n"), fp); + FOR_ALL_BUFFERS(buf) + { + if (buf->b_fname == NULL + || !buf->b_p_bl +#ifdef FEAT_QUICKFIX + || bt_quickfix(buf) +#endif +#ifdef FEAT_TERMINAL + || bt_terminal(buf) +#endif + || 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%ld\t%d", + (long)buf->b_last_cursor.lnum, + buf->b_last_cursor.col); + viminfo_writestring(fp, line); + } + vim_free(line); +} +#endif + +/* + * Return TRUE if "buf" is a normal buffer, 'buftype' is empty. + */ + int +bt_normal(buf_T *buf) +{ + return buf != NULL && buf->b_p_bt[0] == NUL; +} + +#if defined(FEAT_QUICKFIX) || defined(PROTO) +/* + * Return TRUE if "buf" is the quickfix buffer. + */ + int +bt_quickfix(buf_T *buf) +{ + return buf != NULL && buf->b_p_bt[0] == 'q'; +} +#endif + +#if defined(FEAT_TERMINAL) || defined(PROTO) +/* + * Return TRUE if "buf" is a terminal buffer. + */ + int +bt_terminal(buf_T *buf) +{ + return buf != NULL && buf->b_p_bt[0] == 't'; +} +#endif + +/* + * Return TRUE if "buf" is a help buffer. + */ + int +bt_help(buf_T *buf) +{ + return buf != NULL && buf->b_help; +} + +/* + * Return TRUE if "buf" is a prompt buffer. + */ + int +bt_prompt(buf_T *buf) +{ + return buf != NULL && buf->b_p_bt[0] == 'p'; +} + +/* + * Return TRUE if "buf" is a "nofile", "acwrite", "terminal" or "prompt" + * buffer. This means the buffer name is not a file name. + */ + int +bt_nofile(buf_T *buf) +{ + return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f') + || buf->b_p_bt[0] == 'a' + || buf->b_p_bt[0] == 't' + || buf->b_p_bt[0] == 'p'); +} + +/* + * Return TRUE if "buf" is a "nowrite", "nofile", "terminal" or "prompt" + * buffer. + */ + int +bt_dontwrite(buf_T *buf) +{ + return buf != NULL && (buf->b_p_bt[0] == 'n' + || buf->b_p_bt[0] == 't' + || buf->b_p_bt[0] == 'p'); +} + +#if defined(FEAT_QUICKFIX) || defined(PROTO) + int +bt_dontwrite_msg(buf_T *buf) +{ + if (bt_dontwrite(buf)) + { + emsg(_("E382: Cannot write, 'buftype' option is set")); + return TRUE; + } + return FALSE; +} +#endif + +/* + * Return TRUE if the buffer should be hidden, according to 'hidden', ":hide" + * and 'bufhidden'. + */ + int +buf_hide(buf_T *buf) +{ + /* 'bufhidden' overrules 'hidden' and ":hide", check it first */ + switch (buf->b_p_bh[0]) + { + case 'u': /* "unload" */ + case 'w': /* "wipe" */ + case 'd': return FALSE; /* "delete" */ + case 'h': return TRUE; /* "hide" */ + } + return (p_hid || cmdmod.hide); +} + +/* + * Return special buffer name. + * Returns NULL when the buffer has a normal file name. + */ + char_u * +buf_spname(buf_T *buf) +{ +#if defined(FEAT_QUICKFIX) + 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); + } +#endif + + /* There is no _file_ when 'buftype' is "nofile", b_sfname + * contains the name as specified by the user. */ + if (bt_nofile(buf)) + { +#ifdef FEAT_TERMINAL + if (buf->b_term != NULL) + return term_get_status_text(buf->b_term); +#endif + if (buf->b_fname != NULL) + return buf->b_fname; +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(buf)) + return (char_u *)_("[Prompt]"); +#endif + return (char_u *)_("[Scratch]"); + } + + if (buf->b_fname == NULL) + return (char_u *)_("[No Name]"); + return NULL; +} + +#if defined(FEAT_JOB_CHANNEL) \ + || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \ + || defined(PROTO) +# define SWITCH_TO_WIN + +/* + * 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! + */ + void +switch_to_win_for_buf( + buf_T *buf, + win_T **save_curwinp, + tabpage_T **save_curtabp, + bufref_T *save_curbuf) +{ + win_T *wp; + tabpage_T *tp; + + if (find_win_for_buf(buf, &wp, &tp) == FAIL) + switch_buffer(save_curbuf, buf); + else if (switch_win(save_curwinp, save_curtabp, wp, tp, TRUE) == FAIL) + { + restore_win(*save_curwinp, *save_curtabp, TRUE); + switch_buffer(save_curbuf, buf); + } +} + + void +restore_win_for_buf( + win_T *save_curwin, + tabpage_T *save_curtab, + bufref_T *save_curbuf) +{ + if (save_curbuf->br_buf == NULL) + restore_win(save_curwin, save_curtab, TRUE); + else + restore_buffer(save_curbuf); +} +#endif + +#if defined(FEAT_QUICKFIX) || defined(SWITCH_TO_WIN) || defined(PROTO) +/* + * 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; +} +#endif + +/* + * 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. */ + if (prep_exarg(&ea, buf) == FAIL) + { + wipe_buffer(newbuf, FALSE); + return TRUE; + } + + /* 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; + } + } + } + vim_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 UNUSED) /* 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/channel.c b/src/channel.c new file mode 100644 index 0000000..484d013 --- /dev/null +++ b/src/channel.c @@ -0,0 +1,6100 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * Implements communication through a socket or any file handle. + */ + +#include "vim.h" + +#if defined(FEAT_JOB_CHANNEL) || defined(PROTO) + +/* TRUE when netbeans is running with a GUI. */ +#ifdef FEAT_GUI +# define CH_HAS_GUI (gui.in_use || gui.starting) +#endif + +/* Note: when making changes here also adjust configure.ac. */ +#ifdef WIN32 +/* WinSock API is separated from C API, thus we can't use read(), write(), + * errno... */ +# define SOCK_ERRNO errno = WSAGetLastError() +# undef ECONNREFUSED +# define ECONNREFUSED WSAECONNREFUSED +# undef EWOULDBLOCK +# define EWOULDBLOCK WSAEWOULDBLOCK +# undef EINPROGRESS +# define EINPROGRESS WSAEINPROGRESS +# ifdef EINTR +# undef EINTR +# endif +# define EINTR WSAEINTR +# define sock_write(sd, buf, len) send((SOCKET)sd, buf, len, 0) +# define sock_read(sd, buf, len) recv((SOCKET)sd, buf, len, 0) +# define sock_close(sd) closesocket((SOCKET)sd) +#else +# include +# include + +# include +# ifdef HAVE_LIBGEN_H +# include +# endif +# define SOCK_ERRNO +# define sock_write(sd, buf, len) write(sd, buf, len) +# define sock_read(sd, buf, len) read(sd, buf, len) +# define sock_close(sd) close(sd) +# define fd_read(fd, buf, len) read(fd, buf, len) +# define fd_write(sd, buf, len) write(sd, buf, len) +# define fd_close(sd) close(sd) +#endif + +static void channel_read(channel_T *channel, ch_part_T part, char *func); + +/* Whether a redraw is needed for appending a line to a buffer. */ +static int channel_need_redraw = FALSE; + +/* Whether we are inside channel_parse_messages() or another situation where it + * is safe to invoke callbacks. */ +static int safe_to_invoke_callback = 0; + +static char *part_names[] = {"sock", "out", "err", "in"}; + +#ifdef WIN32 + static int +fd_read(sock_T fd, char *buf, size_t len) +{ + HANDLE h = (HANDLE)fd; + DWORD nread; + + if (!ReadFile(h, buf, (DWORD)len, &nread, NULL)) + return -1; + return (int)nread; +} + + static int +fd_write(sock_T fd, char *buf, size_t len) +{ + size_t todo = len; + HANDLE h = (HANDLE)fd; + DWORD nwrite, size, done = 0; + OVERLAPPED ov; + + while (todo > 0) + { + if (todo > MAX_NAMED_PIPE_SIZE) + size = MAX_NAMED_PIPE_SIZE; + else + size = (DWORD)todo; + // If the pipe overflows while the job does not read the data, WriteFile + // will block forever. This abandons the write. + memset(&ov, 0, sizeof(ov)); + if (!WriteFile(h, buf + done, size, &nwrite, &ov)) + { + DWORD err = GetLastError(); + + if (err != ERROR_IO_PENDING) + return -1; + if (!GetOverlappedResult(h, &ov, &nwrite, FALSE)) + return -1; + FlushFileBuffers(h); + } + todo -= nwrite; + done += nwrite; + } + return (int)done; +} + + static void +fd_close(sock_T fd) +{ + HANDLE h = (HANDLE)fd; + + CloseHandle(h); +} +#endif + +/* Log file opened with ch_logfile(). */ +static FILE *log_fd = NULL; +#ifdef FEAT_RELTIME +static proftime_T log_start; +#endif + + void +ch_logfile(char_u *fname, char_u *opt) +{ + FILE *file = NULL; + + if (log_fd != NULL) + fclose(log_fd); + + if (*fname != NUL) + { + file = fopen((char *)fname, *opt == 'w' ? "w" : "a"); + if (file == NULL) + { + semsg(_(e_notopen), fname); + return; + } + } + log_fd = file; + + if (log_fd != NULL) + { + fprintf(log_fd, "==== start log session ====\n"); +#ifdef FEAT_RELTIME + profile_start(&log_start); +#endif + } +} + + int +ch_log_active(void) +{ + return log_fd != NULL; +} + + static void +ch_log_lead(const char *what, channel_T *ch, ch_part_T part) +{ + if (log_fd != NULL) + { +#ifdef FEAT_RELTIME + proftime_T log_now; + + profile_start(&log_now); + profile_sub(&log_now, &log_start); + fprintf(log_fd, "%s ", profile_msg(&log_now)); +#endif + if (ch != NULL) + { + if (part < PART_COUNT) + fprintf(log_fd, "%son %d(%s): ", + what, ch->ch_id, part_names[part]); + else + fprintf(log_fd, "%son %d: ", what, ch->ch_id); + } + else + fprintf(log_fd, "%s: ", what); + } +} + +static int did_log_msg = TRUE; + +#ifndef PROTO /* prototype is in vim.h */ + void +ch_log(channel_T *ch, const char *fmt, ...) +{ + if (log_fd != NULL) + { + va_list ap; + + ch_log_lead("", ch, PART_COUNT); + va_start(ap, fmt); + vfprintf(log_fd, fmt, ap); + va_end(ap); + fputc('\n', log_fd); + fflush(log_fd); + did_log_msg = TRUE; + } +} +#endif + + static void +ch_error(channel_T *ch, const char *fmt, ...) +#ifdef USE_PRINTF_FORMAT_ATTRIBUTE + __attribute__((format(printf, 2, 3))) +#endif + ; + + static void +ch_error(channel_T *ch, const char *fmt, ...) +{ + if (log_fd != NULL) + { + va_list ap; + + ch_log_lead("ERR ", ch, PART_COUNT); + va_start(ap, fmt); + vfprintf(log_fd, fmt, ap); + va_end(ap); + fputc('\n', log_fd); + fflush(log_fd); + did_log_msg = TRUE; + } +} + +#ifdef _WIN32 +# undef PERROR +# define PERROR(msg) (void)semsg("%s: %s", msg, strerror_win32(errno)) + + static char * +strerror_win32(int eno) +{ + static LPVOID msgbuf = NULL; + char_u *ptr; + + if (msgbuf) + { + LocalFree(msgbuf); + msgbuf = NULL; + } + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + eno, + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + (LPTSTR) &msgbuf, + 0, + NULL); + if (msgbuf != NULL) + /* chomp \r or \n */ + for (ptr = (char_u *)msgbuf; *ptr; ptr++) + switch (*ptr) + { + case '\r': + STRMOVE(ptr, ptr + 1); + ptr--; + break; + case '\n': + if (*(ptr + 1) == '\0') + *ptr = '\0'; + else + *ptr = ' '; + break; + } + return msgbuf; +} +#endif + +/* + * The list of all allocated channels. + */ +static channel_T *first_channel = NULL; +static int next_ch_id = 0; + +/* + * Allocate a new channel. The refcount is set to 1. + * The channel isn't actually used until it is opened. + * Returns NULL if out of memory. + */ + channel_T * +add_channel(void) +{ + ch_part_T part; + channel_T *channel = (channel_T *)alloc_clear((int)sizeof(channel_T)); + + if (channel == NULL) + return NULL; + + channel->ch_id = next_ch_id++; + ch_log(channel, "Created channel"); + + for (part = PART_SOCK; part < PART_COUNT; ++part) + { + channel->ch_part[part].ch_fd = INVALID_FD; +#ifdef FEAT_GUI_X11 + channel->ch_part[part].ch_inputHandler = (XtInputId)NULL; +#endif +#ifdef FEAT_GUI_GTK + channel->ch_part[part].ch_inputHandler = 0; +#endif + channel->ch_part[part].ch_timeout = 2000; + } + + if (first_channel != NULL) + { + first_channel->ch_prev = channel; + channel->ch_next = first_channel; + } + first_channel = channel; + + channel->ch_refcount = 1; + return channel; +} + + int +has_any_channel(void) +{ + return first_channel != NULL; +} + +/* + * Called when the refcount of a channel is zero. + * Return TRUE if "channel" has a callback and the associated job wasn't + * killed. + */ + static int +channel_still_useful(channel_T *channel) +{ + int has_sock_msg; + int has_out_msg; + int has_err_msg; + + /* If the job was killed the channel is not expected to work anymore. */ + if (channel->ch_job_killed && channel->ch_job == NULL) + return FALSE; + + /* If there is a close callback it may still need to be invoked. */ + if (channel->ch_close_cb != NULL) + return TRUE; + + /* If reading from or a buffer it's still useful. */ + if (channel->ch_part[PART_IN].ch_bufref.br_buf != NULL) + return TRUE; + + /* If there is no callback then nobody can get readahead. If the fd is + * closed and there is no readahead then the callback won't be called. */ + has_sock_msg = channel->ch_part[PART_SOCK].ch_fd != INVALID_FD + || channel->ch_part[PART_SOCK].ch_head.rq_next != NULL + || channel->ch_part[PART_SOCK].ch_json_head.jq_next != NULL; + has_out_msg = channel->ch_part[PART_OUT].ch_fd != INVALID_FD + || channel->ch_part[PART_OUT].ch_head.rq_next != NULL + || channel->ch_part[PART_OUT].ch_json_head.jq_next != NULL; + has_err_msg = channel->ch_part[PART_ERR].ch_fd != INVALID_FD + || channel->ch_part[PART_ERR].ch_head.rq_next != NULL + || channel->ch_part[PART_ERR].ch_json_head.jq_next != NULL; + return (channel->ch_callback != NULL && (has_sock_msg + || has_out_msg || has_err_msg)) + || ((channel->ch_part[PART_OUT].ch_callback != NULL + || channel->ch_part[PART_OUT].ch_bufref.br_buf != NULL) + && has_out_msg) + || ((channel->ch_part[PART_ERR].ch_callback != NULL + || channel->ch_part[PART_ERR].ch_bufref.br_buf != NULL) + && has_err_msg); +} + +/* + * Return TRUE if "channel" is closeable (i.e. all readable fds are closed). + */ + static int +channel_can_close(channel_T *channel) +{ + return channel->ch_to_be_closed == 0; +} + +/* + * Close a channel and free all its resources. + */ + static void +channel_free_contents(channel_T *channel) +{ + channel_close(channel, TRUE); + channel_clear(channel); + ch_log(channel, "Freeing channel"); +} + + static void +channel_free_channel(channel_T *channel) +{ + if (channel->ch_next != NULL) + channel->ch_next->ch_prev = channel->ch_prev; + if (channel->ch_prev == NULL) + first_channel = channel->ch_next; + else + channel->ch_prev->ch_next = channel->ch_next; + vim_free(channel); +} + + static void +channel_free(channel_T *channel) +{ + if (!in_free_unref_items) + { + if (safe_to_invoke_callback == 0) + channel->ch_to_be_freed = TRUE; + else + { + channel_free_contents(channel); + channel_free_channel(channel); + } + } +} + +/* + * Close a channel and free all its resources if there is no further action + * possible, there is no callback to be invoked or the associated job was + * killed. + * Return TRUE if the channel was freed. + */ + static int +channel_may_free(channel_T *channel) +{ + if (!channel_still_useful(channel)) + { + channel_free(channel); + return TRUE; + } + return FALSE; +} + +/* + * Decrement the reference count on "channel" and maybe free it when it goes + * down to zero. Don't free it if there is a pending action. + * Returns TRUE when the channel is no longer referenced. + */ + int +channel_unref(channel_T *channel) +{ + if (channel != NULL && --channel->ch_refcount <= 0) + return channel_may_free(channel); + return FALSE; +} + + int +free_unused_channels_contents(int copyID, int mask) +{ + int did_free = FALSE; + channel_T *ch; + + /* This is invoked from the garbage collector, which only runs at a safe + * point. */ + ++safe_to_invoke_callback; + + for (ch = first_channel; ch != NULL; ch = ch->ch_next) + if (!channel_still_useful(ch) + && (ch->ch_copyID & mask) != (copyID & mask)) + { + /* Free the channel and ordinary items it contains, but don't + * recurse into Lists, Dictionaries etc. */ + channel_free_contents(ch); + did_free = TRUE; + } + + --safe_to_invoke_callback; + return did_free; +} + + void +free_unused_channels(int copyID, int mask) +{ + channel_T *ch; + channel_T *ch_next; + + for (ch = first_channel; ch != NULL; ch = ch_next) + { + ch_next = ch->ch_next; + if (!channel_still_useful(ch) + && (ch->ch_copyID & mask) != (copyID & mask)) + { + /* Free the channel struct itself. */ + channel_free_channel(ch); + } + } +} + +#if defined(FEAT_GUI) || defined(PROTO) + +#if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) + static void +channel_read_fd(int fd) +{ + channel_T *channel; + ch_part_T part; + + channel = channel_fd2channel(fd, &part); + if (channel == NULL) + ch_error(NULL, "Channel for fd %d not found", fd); + else + channel_read(channel, part, "channel_read_fd"); +} +#endif + +/* + * Read a command from netbeans. + */ +#ifdef FEAT_GUI_X11 + static void +messageFromServerX11(XtPointer clientData, + int *unused1 UNUSED, + XtInputId *unused2 UNUSED) +{ + channel_read_fd((int)(long)clientData); +} +#endif + +#ifdef FEAT_GUI_GTK +# if GTK_CHECK_VERSION(3,0,0) + static gboolean +messageFromServerGtk3(GIOChannel *unused1 UNUSED, + GIOCondition unused2 UNUSED, + gpointer clientData) +{ + channel_read_fd(GPOINTER_TO_INT(clientData)); + return TRUE; /* Return FALSE instead in case the event source is to + * be removed after this function returns. */ +} +# else + static void +messageFromServerGtk2(gpointer clientData, + gint unused1 UNUSED, + GdkInputCondition unused2 UNUSED) +{ + channel_read_fd((int)(long)clientData); +} +# endif +#endif + + static void +channel_gui_register_one(channel_T *channel, ch_part_T part) +{ + if (!CH_HAS_GUI) + return; + + /* gets stuck in handling events for a not connected channel */ + if (channel->ch_keep_open) + return; + +# ifdef FEAT_GUI_X11 + /* Tell notifier we are interested in being called when there is input on + * the editor connection socket. */ + if (channel->ch_part[part].ch_inputHandler == (XtInputId)NULL) + { + ch_log(channel, "Registering part %s with fd %d", + part_names[part], channel->ch_part[part].ch_fd); + + channel->ch_part[part].ch_inputHandler = XtAppAddInput( + (XtAppContext)app_context, + channel->ch_part[part].ch_fd, + (XtPointer)(XtInputReadMask + XtInputExceptMask), + messageFromServerX11, + (XtPointer)(long)channel->ch_part[part].ch_fd); + } +# else +# ifdef FEAT_GUI_GTK + /* Tell gdk we are interested in being called when there is input on the + * editor connection socket. */ + if (channel->ch_part[part].ch_inputHandler == 0) + { + ch_log(channel, "Registering part %s with fd %d", + part_names[part], channel->ch_part[part].ch_fd); +# if GTK_CHECK_VERSION(3,0,0) + GIOChannel *chnnl = g_io_channel_unix_new( + (gint)channel->ch_part[part].ch_fd); + + channel->ch_part[part].ch_inputHandler = g_io_add_watch( + chnnl, + G_IO_IN|G_IO_HUP|G_IO_ERR|G_IO_PRI, + messageFromServerGtk3, + GINT_TO_POINTER(channel->ch_part[part].ch_fd)); + + g_io_channel_unref(chnnl); +# else + channel->ch_part[part].ch_inputHandler = gdk_input_add( + (gint)channel->ch_part[part].ch_fd, + (GdkInputCondition) + ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION), + messageFromServerGtk2, + (gpointer)(long)channel->ch_part[part].ch_fd); +# endif + } +# endif +# endif +} + + static void +channel_gui_register(channel_T *channel) +{ + if (channel->CH_SOCK_FD != INVALID_FD) + channel_gui_register_one(channel, PART_SOCK); + if (channel->CH_OUT_FD != INVALID_FD + && channel->CH_OUT_FD != channel->CH_SOCK_FD) + channel_gui_register_one(channel, PART_OUT); + if (channel->CH_ERR_FD != INVALID_FD + && channel->CH_ERR_FD != channel->CH_SOCK_FD + && channel->CH_ERR_FD != channel->CH_OUT_FD) + channel_gui_register_one(channel, PART_ERR); +} + +/* + * Register any of our file descriptors with the GUI event handling system. + * Called when the GUI has started. + */ + void +channel_gui_register_all(void) +{ + channel_T *channel; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + channel_gui_register(channel); +} + + static void +channel_gui_unregister_one(channel_T *channel, ch_part_T part) +{ +# ifdef FEAT_GUI_X11 + if (channel->ch_part[part].ch_inputHandler != (XtInputId)NULL) + { + ch_log(channel, "Unregistering part %s", part_names[part]); + XtRemoveInput(channel->ch_part[part].ch_inputHandler); + channel->ch_part[part].ch_inputHandler = (XtInputId)NULL; + } +# else +# ifdef FEAT_GUI_GTK + if (channel->ch_part[part].ch_inputHandler != 0) + { + ch_log(channel, "Unregistering part %s", part_names[part]); +# if GTK_CHECK_VERSION(3,0,0) + g_source_remove(channel->ch_part[part].ch_inputHandler); +# else + gdk_input_remove(channel->ch_part[part].ch_inputHandler); +# endif + channel->ch_part[part].ch_inputHandler = 0; + } +# endif +# endif +} + + static void +channel_gui_unregister(channel_T *channel) +{ + ch_part_T part; + + for (part = PART_SOCK; part < PART_IN; ++part) + channel_gui_unregister_one(channel, part); +} + +#endif + +static char *e_cannot_connect = N_("E902: Cannot connect to port"); + +/* + * Open a socket channel to "hostname":"port". + * "waittime" is the time in msec to wait for the connection. + * When negative wait forever. + * Returns the channel for success. + * Returns NULL for failure. + */ + channel_T * +channel_open( + char *hostname, + int port_in, + int waittime, + void (*nb_close_cb)(void)) +{ + int sd = -1; + struct sockaddr_in server; + struct hostent *host; +#ifdef WIN32 + u_short port = port_in; + u_long val = 1; +#else + int port = port_in; +#endif + channel_T *channel; + int ret; + +#ifdef WIN32 + channel_init_winsock(); +#endif + + channel = add_channel(); + if (channel == NULL) + { + ch_error(NULL, "Cannot allocate channel."); + return NULL; + } + + /* Get the server internet address and put into addr structure */ + /* fill in the socket address structure and connect to server */ + vim_memset((char *)&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_port = htons(port); + if ((host = gethostbyname(hostname)) == NULL) + { + ch_error(channel, "in gethostbyname() in channel_open()"); + PERROR(_("E901: gethostbyname() in channel_open()")); + channel_free(channel); + return NULL; + } + { + char *p; + + /* When using host->h_addr_list[0] directly ubsan warns for it to not + * be aligned. First copy the pointer to avoid that. */ + memcpy(&p, &host->h_addr_list[0], sizeof(p)); + memcpy((char *)&server.sin_addr, p, host->h_length); + } + + /* On Mac and Solaris a zero timeout almost never works. At least wait + * one millisecond. Let's do it for all systems, because we don't know why + * this is needed. */ + if (waittime == 0) + waittime = 1; + + /* + * For Unix we need to call connect() again after connect() failed. + * On Win32 one time is sufficient. + */ + while (TRUE) + { + long elapsed_msec = 0; + int waitnow; + + if (sd >= 0) + sock_close(sd); + sd = socket(AF_INET, SOCK_STREAM, 0); + if (sd == -1) + { + ch_error(channel, "in socket() in channel_open()."); + PERROR(_("E898: socket() in channel_open()")); + channel_free(channel); + return NULL; + } + + if (waittime >= 0) + { + /* Make connect() non-blocking. */ + if ( +#ifdef _WIN32 + ioctlsocket(sd, FIONBIO, &val) < 0 +#else + fcntl(sd, F_SETFL, O_NONBLOCK) < 0 +#endif + ) + { + SOCK_ERRNO; + ch_error(channel, + "channel_open: Connect failed with errno %d", errno); + sock_close(sd); + channel_free(channel); + return NULL; + } + } + + /* Try connecting to the server. */ + ch_log(channel, "Connecting to %s port %d", hostname, port); + ret = connect(sd, (struct sockaddr *)&server, sizeof(server)); + + if (ret == 0) + /* The connection could be established. */ + break; + + SOCK_ERRNO; + if (waittime < 0 || (errno != EWOULDBLOCK + && errno != ECONNREFUSED +#ifdef EINPROGRESS + && errno != EINPROGRESS +#endif + )) + { + ch_error(channel, + "channel_open: Connect failed with errno %d", errno); + PERROR(_(e_cannot_connect)); + sock_close(sd); + channel_free(channel); + return NULL; + } + + /* Limit the waittime to 50 msec. If it doesn't work within this + * time we close the socket and try creating it again. */ + waitnow = waittime > 50 ? 50 : waittime; + + /* If connect() didn't finish then try using select() to wait for the + * connection to be made. For Win32 always use select() to wait. */ +#ifndef WIN32 + if (errno != ECONNREFUSED) +#endif + { + struct timeval tv; + fd_set rfds; + fd_set wfds; +#ifndef WIN32 + int so_error = 0; + socklen_t so_error_len = sizeof(so_error); + struct timeval start_tv; + struct timeval end_tv; +#endif + FD_ZERO(&rfds); + FD_SET(sd, &rfds); + FD_ZERO(&wfds); + FD_SET(sd, &wfds); + + tv.tv_sec = waitnow / 1000; + tv.tv_usec = (waitnow % 1000) * 1000; +#ifndef WIN32 + gettimeofday(&start_tv, NULL); +#endif + ch_log(channel, + "Waiting for connection (waiting %d msec)...", waitnow); + ret = select((int)sd + 1, &rfds, &wfds, NULL, &tv); + + if (ret < 0) + { + SOCK_ERRNO; + ch_error(channel, + "channel_open: Connect failed with errno %d", errno); + PERROR(_(e_cannot_connect)); + sock_close(sd); + channel_free(channel); + return NULL; + } + +#ifdef WIN32 + /* On Win32: select() is expected to work and wait for up to + * "waitnow" msec for the socket to be open. */ + if (FD_ISSET(sd, &wfds)) + break; + elapsed_msec = waitnow; + if (waittime > 1 && elapsed_msec < waittime) + { + waittime -= elapsed_msec; + continue; + } +#else + /* On Linux-like systems: See socket(7) for the behavior + * After putting the socket in non-blocking mode, connect() will + * return EINPROGRESS, select() will not wait (as if writing is + * possible), need to use getsockopt() to check if the socket is + * actually able to connect. + * We detect a failure to connect when either read and write fds + * are set. Use getsockopt() to find out what kind of failure. */ + if (FD_ISSET(sd, &rfds) || FD_ISSET(sd, &wfds)) + { + ret = getsockopt(sd, + SOL_SOCKET, SO_ERROR, &so_error, &so_error_len); + if (ret < 0 || (so_error != 0 + && so_error != EWOULDBLOCK + && so_error != ECONNREFUSED +# ifdef EINPROGRESS + && so_error != EINPROGRESS +# endif + )) + { + ch_error(channel, + "channel_open: Connect failed with errno %d", + so_error); + PERROR(_(e_cannot_connect)); + sock_close(sd); + channel_free(channel); + return NULL; + } + } + + if (FD_ISSET(sd, &wfds) && so_error == 0) + /* Did not detect an error, connection is established. */ + break; + + gettimeofday(&end_tv, NULL); + elapsed_msec = (end_tv.tv_sec - start_tv.tv_sec) * 1000 + + (end_tv.tv_usec - start_tv.tv_usec) / 1000; +#endif + } + +#ifndef WIN32 + if (waittime > 1 && elapsed_msec < waittime) + { + /* The port isn't ready but we also didn't get an error. + * This happens when the server didn't open the socket + * yet. Select() may return early, wait until the remaining + * "waitnow" and try again. */ + waitnow -= elapsed_msec; + waittime -= elapsed_msec; + if (waitnow > 0) + { + mch_delay((long)waitnow, TRUE); + ui_breakcheck(); + waittime -= waitnow; + } + if (!got_int) + { + if (waittime <= 0) + /* give it one more try */ + waittime = 1; + continue; + } + /* we were interrupted, behave as if timed out */ + } +#endif + + /* We timed out. */ + ch_error(channel, "Connection timed out"); + sock_close(sd); + channel_free(channel); + return NULL; + } + + ch_log(channel, "Connection made"); + + if (waittime >= 0) + { +#ifdef _WIN32 + val = 0; + ioctlsocket(sd, FIONBIO, &val); +#else + (void)fcntl(sd, F_SETFL, 0); +#endif + } + + channel->CH_SOCK_FD = (sock_T)sd; + channel->ch_nb_close_cb = nb_close_cb; + channel->ch_hostname = (char *)vim_strsave((char_u *)hostname); + channel->ch_port = port_in; + channel->ch_to_be_closed |= (1U << PART_SOCK); + +#ifdef FEAT_GUI + channel_gui_register_one(channel, PART_SOCK); +#endif + + return channel; +} + +/* + * Implements ch_open(). + */ + channel_T * +channel_open_func(typval_T *argvars) +{ + char_u *address; + char_u *p; + char *rest; + int port; + jobopt_T opt; + channel_T *channel = NULL; + + address = tv_get_string(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN + && (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)) + { + emsg(_(e_invarg)); + return NULL; + } + + /* parse address */ + p = vim_strchr(address, ':'); + if (p == NULL) + { + semsg(_(e_invarg2), address); + return NULL; + } + *p++ = NUL; + port = strtol((char *)p, &rest, 10); + if (*address == NUL || port <= 0 || *rest != NUL) + { + p[-1] = ':'; + semsg(_(e_invarg2), address); + return NULL; + } + + /* parse options */ + clear_job_options(&opt); + opt.jo_mode = MODE_JSON; + opt.jo_timeout = 2000; + if (get_job_options(&argvars[1], &opt, + JO_MODE_ALL + JO_CB_ALL + JO_WAITTIME + JO_TIMEOUT_ALL, 0) == FAIL) + goto theend; + if (opt.jo_timeout < 0) + { + emsg(_(e_invarg)); + goto theend; + } + + channel = channel_open((char *)address, port, opt.jo_waittime, NULL); + if (channel != NULL) + { + opt.jo_set = JO_ALL; + channel_set_options(channel, &opt); + } +theend: + free_job_options(&opt); + return channel; +} + + static void +ch_close_part(channel_T *channel, ch_part_T part) +{ + sock_T *fd = &channel->ch_part[part].ch_fd; + + if (*fd != INVALID_FD) + { + if (part == PART_SOCK) + sock_close(*fd); + else + { + /* When using a pty the same FD is set on multiple parts, only + * close it when the last reference is closed. */ + if ((part == PART_IN || channel->CH_IN_FD != *fd) + && (part == PART_OUT || channel->CH_OUT_FD != *fd) + && (part == PART_ERR || channel->CH_ERR_FD != *fd)) + { +#ifdef WIN32 + if (channel->ch_named_pipe) + DisconnectNamedPipe((HANDLE)fd); +#endif + fd_close(*fd); + } + } + *fd = INVALID_FD; + + /* channel is closed, may want to end the job if it was the last */ + channel->ch_to_be_closed &= ~(1U << part); + } +} + + void +channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err) +{ + if (in != INVALID_FD) + { + ch_close_part(channel, PART_IN); + channel->CH_IN_FD = in; +# if defined(UNIX) + /* Do not end the job when all output channels are closed, wait until + * the job ended. */ + if (mch_isatty(in)) + channel->ch_to_be_closed |= (1U << PART_IN); +# endif + } + if (out != INVALID_FD) + { +# if defined(FEAT_GUI) + channel_gui_unregister_one(channel, PART_OUT); +# endif + ch_close_part(channel, PART_OUT); + channel->CH_OUT_FD = out; + channel->ch_to_be_closed |= (1U << PART_OUT); +# if defined(FEAT_GUI) + channel_gui_register_one(channel, PART_OUT); +# endif + } + if (err != INVALID_FD) + { +# if defined(FEAT_GUI) + channel_gui_unregister_one(channel, PART_ERR); +# endif + ch_close_part(channel, PART_ERR); + channel->CH_ERR_FD = err; + channel->ch_to_be_closed |= (1U << PART_ERR); +# if defined(FEAT_GUI) + channel_gui_register_one(channel, PART_ERR); +# endif + } +} + +/* + * Sets the job the channel is associated with and associated options. + * This does not keep a refcount, when the job is freed ch_job is cleared. + */ + void +channel_set_job(channel_T *channel, job_T *job, jobopt_T *options) +{ + channel->ch_job = job; + + channel_set_options(channel, options); + + if (job->jv_in_buf != NULL) + { + chanpart_T *in_part = &channel->ch_part[PART_IN]; + + set_bufref(&in_part->ch_bufref, job->jv_in_buf); + ch_log(channel, "reading from buffer '%s'", + (char *)in_part->ch_bufref.br_buf->b_ffname); + if (options->jo_set & JO_IN_TOP) + { + if (options->jo_in_top == 0 && !(options->jo_set & JO_IN_BOT)) + { + /* Special mode: send last-but-one line when appending a line + * to the buffer. */ + in_part->ch_bufref.br_buf->b_write_to_channel = TRUE; + in_part->ch_buf_append = TRUE; + in_part->ch_buf_top = + in_part->ch_bufref.br_buf->b_ml.ml_line_count + 1; + } + else + in_part->ch_buf_top = options->jo_in_top; + } + else + in_part->ch_buf_top = 1; + if (options->jo_set & JO_IN_BOT) + in_part->ch_buf_bot = options->jo_in_bot; + else + in_part->ch_buf_bot = in_part->ch_bufref.br_buf->b_ml.ml_line_count; + } +} + +/* + * Prepare buffer "buf" for writing channel output to. + */ + static void +prepare_buffer(buf_T *buf) +{ + buf_T *save_curbuf = curbuf; + + buf_copy_options(buf, BCO_ENTER); + curbuf = buf; +#ifdef FEAT_QUICKFIX + set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL); + set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); +#endif + if (curbuf->b_ml.ml_mfp == NULL) + ml_open(curbuf); + curbuf = save_curbuf; +} + +/* + * Find a buffer matching "name" or create a new one. + * Returns NULL if there is something very wrong (error already reported). + */ + static buf_T * +find_buffer(char_u *name, int err, int msg) +{ + buf_T *buf = NULL; + buf_T *save_curbuf = curbuf; + + if (name != NULL && *name != NUL) + { + buf = buflist_findname(name); + if (buf == NULL) + buf = buflist_findname_exp(name); + } + if (buf == NULL) + { + buf = buflist_new(name == NULL || *name == NUL ? NULL : name, + NULL, (linenr_T)0, BLN_LISTED | BLN_NEW); + if (buf == NULL) + return NULL; + prepare_buffer(buf); + + curbuf = buf; + if (msg) + ml_replace(1, (char_u *)(err ? "Reading from channel error..." + : "Reading from channel output..."), TRUE); + changed_bytes(1, 0); + curbuf = save_curbuf; + } + + return buf; +} + + static void +set_callback( + char_u **cbp, + partial_T **pp, + char_u *callback, + partial_T *partial) +{ + free_callback(*cbp, *pp); + if (callback != NULL && *callback != NUL) + { + if (partial != NULL) + *cbp = partial_name(partial); + else + { + *cbp = vim_strsave(callback); + func_ref(*cbp); + } + } + else + *cbp = NULL; + *pp = partial; + if (partial != NULL) + ++partial->pt_refcount; +} + +/* + * Set various properties from an "opt" argument. + */ + void +channel_set_options(channel_T *channel, jobopt_T *opt) +{ + ch_part_T part; + + if (opt->jo_set & JO_MODE) + for (part = PART_SOCK; part < PART_COUNT; ++part) + channel->ch_part[part].ch_mode = opt->jo_mode; + if (opt->jo_set & JO_IN_MODE) + channel->ch_part[PART_IN].ch_mode = opt->jo_in_mode; + if (opt->jo_set & JO_OUT_MODE) + channel->ch_part[PART_OUT].ch_mode = opt->jo_out_mode; + if (opt->jo_set & JO_ERR_MODE) + channel->ch_part[PART_ERR].ch_mode = opt->jo_err_mode; + channel->ch_nonblock = opt->jo_noblock; + + if (opt->jo_set & JO_TIMEOUT) + for (part = PART_SOCK; part < PART_COUNT; ++part) + channel->ch_part[part].ch_timeout = opt->jo_timeout; + if (opt->jo_set & JO_OUT_TIMEOUT) + channel->ch_part[PART_OUT].ch_timeout = opt->jo_out_timeout; + if (opt->jo_set & JO_ERR_TIMEOUT) + channel->ch_part[PART_ERR].ch_timeout = opt->jo_err_timeout; + if (opt->jo_set & JO_BLOCK_WRITE) + channel->ch_part[PART_IN].ch_block_write = 1; + + if (opt->jo_set & JO_CALLBACK) + set_callback(&channel->ch_callback, &channel->ch_partial, + opt->jo_callback, opt->jo_partial); + if (opt->jo_set & JO_OUT_CALLBACK) + set_callback(&channel->ch_part[PART_OUT].ch_callback, + &channel->ch_part[PART_OUT].ch_partial, + opt->jo_out_cb, opt->jo_out_partial); + if (opt->jo_set & JO_ERR_CALLBACK) + set_callback(&channel->ch_part[PART_ERR].ch_callback, + &channel->ch_part[PART_ERR].ch_partial, + opt->jo_err_cb, opt->jo_err_partial); + if (opt->jo_set & JO_CLOSE_CALLBACK) + set_callback(&channel->ch_close_cb, &channel->ch_close_partial, + opt->jo_close_cb, opt->jo_close_partial); + channel->ch_drop_never = opt->jo_drop_never; + + if ((opt->jo_set & JO_OUT_IO) && opt->jo_io[PART_OUT] == JIO_BUFFER) + { + buf_T *buf; + + /* writing output to a buffer. Default mode is NL. */ + if (!(opt->jo_set & JO_OUT_MODE)) + channel->ch_part[PART_OUT].ch_mode = MODE_NL; + if (opt->jo_set & JO_OUT_BUF) + { + buf = buflist_findnr(opt->jo_io_buf[PART_OUT]); + if (buf == NULL) + semsg(_(e_nobufnr), (long)opt->jo_io_buf[PART_OUT]); + } + else + { + int msg = TRUE; + + if (opt->jo_set2 & JO2_OUT_MSG) + msg = opt->jo_message[PART_OUT]; + buf = find_buffer(opt->jo_io_name[PART_OUT], FALSE, msg); + } + if (buf != NULL) + { + if (opt->jo_set & JO_OUT_MODIFIABLE) + channel->ch_part[PART_OUT].ch_nomodifiable = + !opt->jo_modifiable[PART_OUT]; + + if (!buf->b_p_ma && !channel->ch_part[PART_OUT].ch_nomodifiable) + { + emsg(_(e_modifiable)); + } + else + { + ch_log(channel, "writing out to buffer '%s'", + (char *)buf->b_ffname); + set_bufref(&channel->ch_part[PART_OUT].ch_bufref, buf); + // if the buffer was deleted or unloaded resurrect it + if (buf->b_ml.ml_mfp == NULL) + prepare_buffer(buf); + } + } + } + + if ((opt->jo_set & JO_ERR_IO) && (opt->jo_io[PART_ERR] == JIO_BUFFER + || (opt->jo_io[PART_ERR] == JIO_OUT && (opt->jo_set & JO_OUT_IO) + && opt->jo_io[PART_OUT] == JIO_BUFFER))) + { + buf_T *buf; + + /* writing err to a buffer. Default mode is NL. */ + if (!(opt->jo_set & JO_ERR_MODE)) + channel->ch_part[PART_ERR].ch_mode = MODE_NL; + if (opt->jo_io[PART_ERR] == JIO_OUT) + buf = channel->ch_part[PART_OUT].ch_bufref.br_buf; + else if (opt->jo_set & JO_ERR_BUF) + { + buf = buflist_findnr(opt->jo_io_buf[PART_ERR]); + if (buf == NULL) + semsg(_(e_nobufnr), (long)opt->jo_io_buf[PART_ERR]); + } + else + { + int msg = TRUE; + + if (opt->jo_set2 & JO2_ERR_MSG) + msg = opt->jo_message[PART_ERR]; + buf = find_buffer(opt->jo_io_name[PART_ERR], TRUE, msg); + } + if (buf != NULL) + { + if (opt->jo_set & JO_ERR_MODIFIABLE) + channel->ch_part[PART_ERR].ch_nomodifiable = + !opt->jo_modifiable[PART_ERR]; + if (!buf->b_p_ma && !channel->ch_part[PART_ERR].ch_nomodifiable) + { + emsg(_(e_modifiable)); + } + else + { + ch_log(channel, "writing err to buffer '%s'", + (char *)buf->b_ffname); + set_bufref(&channel->ch_part[PART_ERR].ch_bufref, buf); + // if the buffer was deleted or unloaded resurrect it + if (buf->b_ml.ml_mfp == NULL) + prepare_buffer(buf); + } + } + } + + channel->ch_part[PART_OUT].ch_io = opt->jo_io[PART_OUT]; + channel->ch_part[PART_ERR].ch_io = opt->jo_io[PART_ERR]; + channel->ch_part[PART_IN].ch_io = opt->jo_io[PART_IN]; +} + +/* + * Set the callback for "channel"/"part" for the response with "id". + */ + void +channel_set_req_callback( + channel_T *channel, + ch_part_T part, + char_u *callback, + partial_T *partial, + int id) +{ + cbq_T *head = &channel->ch_part[part].ch_cb_head; + cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T)); + + if (item != NULL) + { + item->cq_partial = partial; + if (partial != NULL) + { + ++partial->pt_refcount; + item->cq_callback = callback; + } + else + { + item->cq_callback = vim_strsave(callback); + func_ref(item->cq_callback); + } + item->cq_seq_nr = id; + item->cq_prev = head->cq_prev; + head->cq_prev = item; + item->cq_next = NULL; + if (item->cq_prev == NULL) + head->cq_next = item; + else + item->cq_prev->cq_next = item; + } +} + + static void +write_buf_line(buf_T *buf, linenr_T lnum, channel_T *channel) +{ + char_u *line = ml_get_buf(buf, lnum, FALSE); + int len = (int)STRLEN(line); + char_u *p; + int i; + + /* Need to make a copy to be able to append a NL. */ + if ((p = alloc(len + 2)) == NULL) + return; + memcpy((char *)p, (char *)line, len); + + if (channel->ch_write_text_mode) + p[len] = CAR; + else + { + for (i = 0; i < len; ++i) + if (p[i] == NL) + p[i] = NUL; + + p[len] = NL; + } + p[len + 1] = NUL; + channel_send(channel, PART_IN, p, len + 1, "write_buf_line"); + vim_free(p); +} + +/* + * Return TRUE if "channel" can be written to. + * Returns FALSE if the input is closed or the write would block. + */ + static int +can_write_buf_line(channel_T *channel) +{ + chanpart_T *in_part = &channel->ch_part[PART_IN]; + + if (in_part->ch_fd == INVALID_FD) + return FALSE; /* pipe was closed */ + + /* for testing: block every other attempt to write */ + if (in_part->ch_block_write == 1) + in_part->ch_block_write = -1; + else if (in_part->ch_block_write == -1) + in_part->ch_block_write = 1; + + /* TODO: Win32 implementation, probably using WaitForMultipleObjects() */ +#ifndef WIN32 + { +# if defined(HAVE_SELECT) + struct timeval tval; + fd_set wfds; + int ret; + + FD_ZERO(&wfds); + FD_SET((int)in_part->ch_fd, &wfds); + tval.tv_sec = 0; + tval.tv_usec = 0; + for (;;) + { + ret = select((int)in_part->ch_fd + 1, NULL, &wfds, NULL, &tval); +# ifdef EINTR + SOCK_ERRNO; + if (ret == -1 && errno == EINTR) + continue; +# endif + if (ret <= 0 || in_part->ch_block_write == 1) + { + if (ret > 0) + ch_log(channel, "FAKED Input not ready for writing"); + else + ch_log(channel, "Input not ready for writing"); + return FALSE; + } + break; + } +# else + struct pollfd fds; + + fds.fd = in_part->ch_fd; + fds.events = POLLOUT; + if (poll(&fds, 1, 0) <= 0) + { + ch_log(channel, "Input not ready for writing"); + return FALSE; + } + if (in_part->ch_block_write == 1) + { + ch_log(channel, "FAKED Input not ready for writing"); + return FALSE; + } +# endif + } +#endif + return TRUE; +} + +/* + * Write any buffer lines to the input channel. + */ + static void +channel_write_in(channel_T *channel) +{ + chanpart_T *in_part = &channel->ch_part[PART_IN]; + linenr_T lnum; + buf_T *buf = in_part->ch_bufref.br_buf; + int written = 0; + + if (buf == NULL || in_part->ch_buf_append) + return; /* no buffer or using appending */ + if (!bufref_valid(&in_part->ch_bufref) || buf->b_ml.ml_mfp == NULL) + { + /* buffer was wiped out or unloaded */ + ch_log(channel, "input buffer has been wiped out"); + in_part->ch_bufref.br_buf = NULL; + return; + } + + for (lnum = in_part->ch_buf_top; lnum <= in_part->ch_buf_bot + && lnum <= buf->b_ml.ml_line_count; ++lnum) + { + if (!can_write_buf_line(channel)) + break; + write_buf_line(buf, lnum, channel); + ++written; + } + + if (written == 1) + ch_log(channel, "written line %d to channel", (int)lnum - 1); + else if (written > 1) + ch_log(channel, "written %d lines to channel", written); + + in_part->ch_buf_top = lnum; + if (lnum > buf->b_ml.ml_line_count || lnum > in_part->ch_buf_bot) + { +#if defined(FEAT_TERMINAL) + /* Send CTRL-D or "eof_chars" to close stdin on MS-Windows. */ + if (channel->ch_job != NULL) + term_send_eof(channel); +#endif + + /* Writing is done, no longer need the buffer. */ + in_part->ch_bufref.br_buf = NULL; + ch_log(channel, "Finished writing all lines to channel"); + + /* Close the pipe/socket, so that the other side gets EOF. */ + ch_close_part(channel, PART_IN); + } + else + ch_log(channel, "Still %ld more lines to write", + (long)(buf->b_ml.ml_line_count - lnum + 1)); +} + +/* + * Handle buffer "buf" being freed, remove it from any channels. + */ + void +channel_buffer_free(buf_T *buf) +{ + channel_T *channel; + ch_part_T part; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + for (part = PART_SOCK; part < PART_COUNT; ++part) + { + chanpart_T *ch_part = &channel->ch_part[part]; + + if (ch_part->ch_bufref.br_buf == buf) + { + ch_log(channel, "%s buffer has been wiped out", + part_names[part]); + ch_part->ch_bufref.br_buf = NULL; + } + } +} + +/* + * Write any lines waiting to be written to "channel". + */ + static void +channel_write_input(channel_T *channel) +{ + chanpart_T *in_part = &channel->ch_part[PART_IN]; + + if (in_part->ch_writeque.wq_next != NULL) + channel_send(channel, PART_IN, (char_u *)"", 0, "channel_write_input"); + else if (in_part->ch_bufref.br_buf != NULL) + { + if (in_part->ch_buf_append) + channel_write_new_lines(in_part->ch_bufref.br_buf); + else + channel_write_in(channel); + } +} + +/* + * Write any lines waiting to be written to a channel. + */ + void +channel_write_any_lines(void) +{ + channel_T *channel; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + channel_write_input(channel); +} + +/* + * Write appended lines above the last one in "buf" to the channel. + */ + void +channel_write_new_lines(buf_T *buf) +{ + channel_T *channel; + int found_one = FALSE; + + /* There could be more than one channel for the buffer, loop over all of + * them. */ + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + { + chanpart_T *in_part = &channel->ch_part[PART_IN]; + linenr_T lnum; + int written = 0; + + if (in_part->ch_bufref.br_buf == buf && in_part->ch_buf_append) + { + if (in_part->ch_fd == INVALID_FD) + continue; /* pipe was closed */ + found_one = TRUE; + for (lnum = in_part->ch_buf_bot; lnum < buf->b_ml.ml_line_count; + ++lnum) + { + if (!can_write_buf_line(channel)) + break; + write_buf_line(buf, lnum, channel); + ++written; + } + + if (written == 1) + ch_log(channel, "written line %d to channel", (int)lnum - 1); + else if (written > 1) + ch_log(channel, "written %d lines to channel", written); + if (lnum < buf->b_ml.ml_line_count) + ch_log(channel, "Still %ld more lines to write", + (long)(buf->b_ml.ml_line_count - lnum)); + + in_part->ch_buf_bot = lnum; + } + } + if (!found_one) + buf->b_write_to_channel = FALSE; +} + +/* + * Invoke the "callback" on channel "channel". + * This does not redraw but sets channel_need_redraw; + */ + static void +invoke_callback(channel_T *channel, char_u *callback, partial_T *partial, + typval_T *argv) +{ + typval_T rettv; + int dummy; + + if (safe_to_invoke_callback == 0) + iemsg("INTERNAL: Invoking callback when it is not safe"); + + argv[0].v_type = VAR_CHANNEL; + argv[0].vval.v_channel = channel; + + call_func(callback, (int)STRLEN(callback), &rettv, 2, argv, NULL, + 0L, 0L, &dummy, TRUE, partial, NULL); + clear_tv(&rettv); + channel_need_redraw = TRUE; +} + +/* + * Return the first node from "channel"/"part" without removing it. + * Returns NULL if there is nothing. + */ + readq_T * +channel_peek(channel_T *channel, ch_part_T part) +{ + readq_T *head = &channel->ch_part[part].ch_head; + + return head->rq_next; +} + +/* + * Return a pointer to the first NL in "node". + * Skips over NUL characters. + * Returns NULL if there is no NL. + */ + char_u * +channel_first_nl(readq_T *node) +{ + char_u *buffer = node->rq_buffer; + long_u i; + + for (i = 0; i < node->rq_buflen; ++i) + if (buffer[i] == NL) + return buffer + i; + return NULL; +} + +/* + * Return the first buffer from channel "channel"/"part" and remove it. + * The caller must free it. + * Returns NULL if there is nothing. + */ + char_u * +channel_get(channel_T *channel, ch_part_T part, int *outlen) +{ + readq_T *head = &channel->ch_part[part].ch_head; + readq_T *node = head->rq_next; + char_u *p; + + if (node == NULL) + return NULL; + if (outlen != NULL) + *outlen += node->rq_buflen; + /* dispose of the node but keep the buffer */ + p = node->rq_buffer; + head->rq_next = node->rq_next; + if (node->rq_next == NULL) + head->rq_prev = NULL; + else + node->rq_next->rq_prev = NULL; + vim_free(node); + return p; +} + +/* + * Returns the whole buffer contents concatenated for "channel"/"part". + * Replaces NUL bytes with NL. + */ + static char_u * +channel_get_all(channel_T *channel, ch_part_T part, int *outlen) +{ + readq_T *head = &channel->ch_part[part].ch_head; + readq_T *node = head->rq_next; + long_u len = 0; + char_u *res; + char_u *p; + + // Concatenate everything into one buffer. + for (node = head->rq_next; node != NULL; node = node->rq_next) + len += node->rq_buflen; + res = lalloc(len + 1, TRUE); + if (res == NULL) + return NULL; + p = res; + for (node = head->rq_next; node != NULL; node = node->rq_next) + { + mch_memmove(p, node->rq_buffer, node->rq_buflen); + p += node->rq_buflen; + } + *p = NUL; + + // Free all buffers + do + { + p = channel_get(channel, part, NULL); + vim_free(p); + } while (p != NULL); + + if (outlen != NULL) + { + // Returning the length, keep NUL characters. + *outlen += len; + return res; + } + + // Turn all NUL into NL, so that the result can be used as a string. + p = res; + while (p < res + len) + { + if (*p == NUL) + *p = NL; +#ifdef WIN32 + else if (*p == 0x1b) + { + // crush the escape sequence OSC 0/1/2: ESC ]0; + if (p + 3 < res + len + && p[1] == ']' + && (p[2] == '0' || p[2] == '1' || p[2] == '2') + && p[3] == ';') + { + // '\a' becomes a NL + while (p < res + (len - 1) && *p != '\a') + ++p; + // BEL is zero width characters, suppress display mistake + // ConPTY (after 10.0.18317) requires advance checking + if (p[-1] == NUL) + p[-1] = 0x07; + } + } +#endif + ++p; + } + + return res; +} + +/* + * Consume "len" bytes from the head of "node". + * Caller must check these bytes are available. + */ + void +channel_consume(channel_T *channel, ch_part_T part, int len) +{ + readq_T *head = &channel->ch_part[part].ch_head; + readq_T *node = head->rq_next; + char_u *buf = node->rq_buffer; + + mch_memmove(buf, buf + len, node->rq_buflen - len); + node->rq_buflen -= len; +} + +/* + * Collapses the first and second buffer for "channel"/"part". + * Returns FAIL if that is not possible. + * When "want_nl" is TRUE collapse more buffers until a NL is found. + */ + int +channel_collapse(channel_T *channel, ch_part_T part, int want_nl) +{ + readq_T *head = &channel->ch_part[part].ch_head; + readq_T *node = head->rq_next; + readq_T *last_node; + readq_T *n; + char_u *newbuf; + char_u *p; + long_u len; + + if (node == NULL || node->rq_next == NULL) + return FAIL; + + last_node = node->rq_next; + len = node->rq_buflen + last_node->rq_buflen + 1; + if (want_nl) + while (last_node->rq_next != NULL + && channel_first_nl(last_node) == NULL) + { + last_node = last_node->rq_next; + len += last_node->rq_buflen; + } + + p = newbuf = alloc(len); + if (newbuf == NULL) + return FAIL; /* out of memory */ + mch_memmove(p, node->rq_buffer, node->rq_buflen); + p += node->rq_buflen; + vim_free(node->rq_buffer); + node->rq_buffer = newbuf; + for (n = node; n != last_node; ) + { + n = n->rq_next; + mch_memmove(p, n->rq_buffer, n->rq_buflen); + p += n->rq_buflen; + vim_free(n->rq_buffer); + } + node->rq_buflen = (long_u)(p - newbuf); + + /* dispose of the collapsed nodes and their buffers */ + for (n = node->rq_next; n != last_node; ) + { + n = n->rq_next; + vim_free(n->rq_prev); + } + node->rq_next = last_node->rq_next; + if (last_node->rq_next == NULL) + head->rq_prev = node; + else + last_node->rq_next->rq_prev = node; + vim_free(last_node); + return OK; +} + +/* + * Store "buf[len]" on "channel"/"part". + * When "prepend" is TRUE put in front, otherwise append at the end. + * Returns OK or FAIL. + */ + static int +channel_save(channel_T *channel, ch_part_T part, char_u *buf, int len, + int prepend, char *lead) +{ + readq_T *node; + readq_T *head = &channel->ch_part[part].ch_head; + char_u *p; + int i; + + node = (readq_T *)alloc(sizeof(readq_T)); + if (node == NULL) + return FAIL; /* out of memory */ + /* A NUL is added at the end, because netbeans code expects that. + * Otherwise a NUL may appear inside the text. */ + node->rq_buffer = alloc(len + 1); + if (node->rq_buffer == NULL) + { + vim_free(node); + return FAIL; /* out of memory */ + } + + if (channel->ch_part[part].ch_mode == MODE_NL) + { + /* Drop any CR before a NL. */ + p = node->rq_buffer; + for (i = 0; i < len; ++i) + if (buf[i] != CAR || i + 1 >= len || buf[i + 1] != NL) + *p++ = buf[i]; + *p = NUL; + node->rq_buflen = (long_u)(p - node->rq_buffer); + } + else + { + mch_memmove(node->rq_buffer, buf, len); + node->rq_buffer[len] = NUL; + node->rq_buflen = (long_u)len; + } + + if (prepend) + { + /* preend node to the head of the queue */ + node->rq_next = head->rq_next; + node->rq_prev = NULL; + if (head->rq_next == NULL) + head->rq_prev = node; + else + head->rq_next->rq_prev = node; + head->rq_next = node; + } + else + { + /* append node to the tail of the queue */ + node->rq_next = NULL; + node->rq_prev = head->rq_prev; + if (head->rq_prev == NULL) + head->rq_next = node; + else + head->rq_prev->rq_next = node; + head->rq_prev = node; + } + + if (ch_log_active() && lead != NULL) + { + ch_log_lead(lead, channel, part); + fprintf(log_fd, "'"); + vim_ignored = (int)fwrite(buf, len, 1, log_fd); + fprintf(log_fd, "'\n"); + } + return OK; +} + +/* + * Try to fill the buffer of "reader". + * Returns FALSE when nothing was added. + */ + static int +channel_fill(js_read_T *reader) +{ + channel_T *channel = (channel_T *)reader->js_cookie; + ch_part_T part = reader->js_cookie_arg; + char_u *next = channel_get(channel, part, NULL); + int keeplen; + int addlen; + char_u *p; + + if (next == NULL) + return FALSE; + + keeplen = reader->js_end - reader->js_buf; + if (keeplen > 0) + { + /* Prepend unused text. */ + addlen = (int)STRLEN(next); + p = alloc(keeplen + addlen + 1); + if (p == NULL) + { + vim_free(next); + return FALSE; + } + mch_memmove(p, reader->js_buf, keeplen); + mch_memmove(p + keeplen, next, addlen + 1); + vim_free(next); + next = p; + } + + vim_free(reader->js_buf); + reader->js_buf = next; + return TRUE; +} + +/* + * Use the read buffer of "channel"/"part" and parse a JSON message that is + * complete. The messages are added to the queue. + * Return TRUE if there is more to read. + */ + static int +channel_parse_json(channel_T *channel, ch_part_T part) +{ + js_read_T reader; + typval_T listtv; + jsonq_T *item; + chanpart_T *chanpart = &channel->ch_part[part]; + jsonq_T *head = &chanpart->ch_json_head; + int status; + int ret; + + if (channel_peek(channel, part) == NULL) + return FALSE; + + reader.js_buf = channel_get(channel, part, NULL); + reader.js_used = 0; + reader.js_fill = channel_fill; + reader.js_cookie = channel; + reader.js_cookie_arg = part; + + /* When a message is incomplete we wait for a short while for more to + * arrive. After the delay drop the input, otherwise a truncated string + * or list will make us hang. + * Do not generate error messages, they will be written in a channel log. */ + ++emsg_silent; + status = json_decode(&reader, &listtv, + chanpart->ch_mode == MODE_JS ? JSON_JS : 0); + --emsg_silent; + if (status == OK) + { + /* Only accept the response when it is a list with at least two + * items. */ + if (listtv.v_type != VAR_LIST || listtv.vval.v_list->lv_len < 2) + { + if (listtv.v_type != VAR_LIST) + ch_error(channel, "Did not receive a list, discarding"); + else + ch_error(channel, "Expected list with two items, got %d", + listtv.vval.v_list->lv_len); + clear_tv(&listtv); + } + else + { + item = (jsonq_T *)alloc((unsigned)sizeof(jsonq_T)); + if (item == NULL) + clear_tv(&listtv); + else + { + item->jq_no_callback = FALSE; + item->jq_value = alloc_tv(); + if (item->jq_value == NULL) + { + vim_free(item); + clear_tv(&listtv); + } + else + { + *item->jq_value = listtv; + item->jq_prev = head->jq_prev; + head->jq_prev = item; + item->jq_next = NULL; + if (item->jq_prev == NULL) + head->jq_next = item; + else + item->jq_prev->jq_next = item; + } + } + } + } + + if (status == OK) + chanpart->ch_wait_len = 0; + else if (status == MAYBE) + { + size_t buflen = STRLEN(reader.js_buf); + + if (chanpart->ch_wait_len < buflen) + { + /* First time encountering incomplete message or after receiving + * more (but still incomplete): set a deadline of 100 msec. */ + ch_log(channel, + "Incomplete message (%d bytes) - wait 100 msec for more", + (int)buflen); + reader.js_used = 0; + chanpart->ch_wait_len = buflen; +#ifdef WIN32 + chanpart->ch_deadline = GetTickCount() + 100L; +#else + gettimeofday(&chanpart->ch_deadline, NULL); + chanpart->ch_deadline.tv_usec += 100 * 1000; + if (chanpart->ch_deadline.tv_usec > 1000 * 1000) + { + chanpart->ch_deadline.tv_usec -= 1000 * 1000; + ++chanpart->ch_deadline.tv_sec; + } +#endif + } + else + { + int timeout; +#ifdef WIN32 + timeout = GetTickCount() > chanpart->ch_deadline; +#else + { + struct timeval now_tv; + + gettimeofday(&now_tv, NULL); + timeout = now_tv.tv_sec > chanpart->ch_deadline.tv_sec + || (now_tv.tv_sec == chanpart->ch_deadline.tv_sec + && now_tv.tv_usec > chanpart->ch_deadline.tv_usec); + } +#endif + if (timeout) + { + status = FAIL; + chanpart->ch_wait_len = 0; + ch_log(channel, "timed out"); + } + else + { + reader.js_used = 0; + ch_log(channel, "still waiting on incomplete message"); + } + } + } + + if (status == FAIL) + { + ch_error(channel, "Decoding failed - discarding input"); + ret = FALSE; + chanpart->ch_wait_len = 0; + } + else if (reader.js_buf[reader.js_used] != NUL) + { + /* Put the unread part back into the channel. */ + channel_save(channel, part, reader.js_buf + reader.js_used, + (int)(reader.js_end - reader.js_buf) - reader.js_used, + TRUE, NULL); + ret = status == MAYBE ? FALSE: TRUE; + } + else + ret = FALSE; + + vim_free(reader.js_buf); + return ret; +} + +/* + * Remove "node" from the queue that it is in. Does not free it. + */ + static void +remove_cb_node(cbq_T *head, cbq_T *node) +{ + if (node->cq_prev == NULL) + head->cq_next = node->cq_next; + else + node->cq_prev->cq_next = node->cq_next; + if (node->cq_next == NULL) + head->cq_prev = node->cq_prev; + else + node->cq_next->cq_prev = node->cq_prev; +} + +/* + * Remove "node" from the queue that it is in and free it. + * Caller should have freed or used node->jq_value. + */ + static void +remove_json_node(jsonq_T *head, jsonq_T *node) +{ + if (node->jq_prev == NULL) + head->jq_next = node->jq_next; + else + node->jq_prev->jq_next = node->jq_next; + if (node->jq_next == NULL) + head->jq_prev = node->jq_prev; + else + node->jq_next->jq_prev = node->jq_prev; + vim_free(node); +} + +/* + * Get a message from the JSON queue for channel "channel". + * When "id" is positive it must match the first number in the list. + * When "id" is zero or negative jut get the first message. But not the one + * with id ch_block_id. + * When "without_callback" is TRUE also get messages that were pushed back. + * Return OK when found and return the value in "rettv". + * Return FAIL otherwise. + */ + static int +channel_get_json( + channel_T *channel, + ch_part_T part, + int id, + int without_callback, + typval_T **rettv) +{ + jsonq_T *head = &channel->ch_part[part].ch_json_head; + jsonq_T *item = head->jq_next; + + while (item != NULL) + { + list_T *l = item->jq_value->vval.v_list; + typval_T *tv = &l->lv_first->li_tv; + + if ((without_callback || !item->jq_no_callback) + && ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id) + || (id <= 0 && (tv->v_type != VAR_NUMBER + || tv->vval.v_number == 0 + || tv->vval.v_number != channel->ch_part[part].ch_block_id)))) + { + *rettv = item->jq_value; + if (tv->v_type == VAR_NUMBER) + ch_log(channel, "Getting JSON message %ld", + (long)tv->vval.v_number); + remove_json_node(head, item); + return OK; + } + item = item->jq_next; + } + return FAIL; +} + +/* + * Put back "rettv" into the JSON queue, there was no callback for it. + * Takes over the values in "rettv". + */ + static void +channel_push_json(channel_T *channel, ch_part_T part, typval_T *rettv) +{ + jsonq_T *head = &channel->ch_part[part].ch_json_head; + jsonq_T *item = head->jq_next; + jsonq_T *newitem; + + if (head->jq_prev != NULL && head->jq_prev->jq_no_callback) + /* last item was pushed back, append to the end */ + item = NULL; + else while (item != NULL && item->jq_no_callback) + /* append after the last item that was pushed back */ + item = item->jq_next; + + newitem = (jsonq_T *)alloc((unsigned)sizeof(jsonq_T)); + if (newitem == NULL) + clear_tv(rettv); + else + { + newitem->jq_value = alloc_tv(); + if (newitem->jq_value == NULL) + { + vim_free(newitem); + clear_tv(rettv); + } + else + { + newitem->jq_no_callback = FALSE; + *newitem->jq_value = *rettv; + if (item == NULL) + { + /* append to the end */ + newitem->jq_prev = head->jq_prev; + head->jq_prev = newitem; + newitem->jq_next = NULL; + if (newitem->jq_prev == NULL) + head->jq_next = newitem; + else + newitem->jq_prev->jq_next = newitem; + } + else + { + /* append after "item" */ + newitem->jq_prev = item; + newitem->jq_next = item->jq_next; + item->jq_next = newitem; + if (newitem->jq_next == NULL) + head->jq_prev = newitem; + else + newitem->jq_next->jq_prev = newitem; + } + } + } +} + +#define CH_JSON_MAX_ARGS 4 + +/* + * Execute a command received over "channel"/"part" + * "argv[0]" is the command string. + * "argv[1]" etc. have further arguments, type is VAR_UNKNOWN if missing. + */ + static void +channel_exe_cmd(channel_T *channel, ch_part_T part, typval_T *argv) +{ + char_u *cmd = argv[0].vval.v_string; + char_u *arg; + int options = channel->ch_part[part].ch_mode == MODE_JS ? JSON_JS : 0; + + if (argv[1].v_type != VAR_STRING) + { + ch_error(channel, "received command with non-string argument"); + if (p_verbose > 2) + emsg(_("E903: received command with non-string argument")); + return; + } + arg = argv[1].vval.v_string; + if (arg == NULL) + arg = (char_u *)""; + + if (STRCMP(cmd, "ex") == 0) + { + int save_called_emsg = called_emsg; + + called_emsg = FALSE; + ch_log(channel, "Executing ex command '%s'", (char *)arg); + ++emsg_silent; + do_cmdline_cmd(arg); + --emsg_silent; + if (called_emsg) + ch_log(channel, "Ex command error: '%s'", + (char *)get_vim_var_str(VV_ERRMSG)); + called_emsg = save_called_emsg; + } + else if (STRCMP(cmd, "normal") == 0) + { + exarg_T ea; + + ch_log(channel, "Executing normal command '%s'", (char *)arg); + ea.arg = arg; + ea.addr_count = 0; + ea.forceit = TRUE; /* no mapping */ + ex_normal(&ea); + } + else if (STRCMP(cmd, "redraw") == 0) + { + exarg_T ea; + + ch_log(channel, "redraw"); + ea.forceit = *arg != NUL; + ex_redraw(&ea); + showruler(FALSE); + setcursor(); + out_flush_cursor(TRUE, FALSE); + } + else if (STRCMP(cmd, "expr") == 0 || STRCMP(cmd, "call") == 0) + { + int is_call = cmd[0] == 'c'; + int id_idx = is_call ? 3 : 2; + + if (argv[id_idx].v_type != VAR_UNKNOWN + && argv[id_idx].v_type != VAR_NUMBER) + { + ch_error(channel, "last argument for expr/call must be a number"); + if (p_verbose > 2) + emsg(_("E904: last argument for expr/call must be a number")); + } + else if (is_call && argv[2].v_type != VAR_LIST) + { + ch_error(channel, "third argument for call must be a list"); + if (p_verbose > 2) + emsg(_("E904: third argument for call must be a list")); + } + else + { + typval_T *tv = NULL; + typval_T res_tv; + typval_T err_tv; + char_u *json = NULL; + + /* Don't pollute the display with errors. */ + ++emsg_skip; + if (!is_call) + { + ch_log(channel, "Evaluating expression '%s'", (char *)arg); + tv = eval_expr(arg, NULL); + } + else + { + ch_log(channel, "Calling '%s'", (char *)arg); + if (func_call(arg, &argv[2], NULL, NULL, &res_tv) == OK) + tv = &res_tv; + } + + if (argv[id_idx].v_type == VAR_NUMBER) + { + int id = argv[id_idx].vval.v_number; + + if (tv != NULL) + json = json_encode_nr_expr(id, tv, options | JSON_NL); + if (tv == NULL || (json != NULL && *json == NUL)) + { + /* If evaluation failed or the result can't be encoded + * then return the string "ERROR". */ + vim_free(json); + err_tv.v_type = VAR_STRING; + err_tv.vval.v_string = (char_u *)"ERROR"; + json = json_encode_nr_expr(id, &err_tv, options | JSON_NL); + } + if (json != NULL) + { + channel_send(channel, + part == PART_SOCK ? PART_SOCK : PART_IN, + json, (int)STRLEN(json), (char *)cmd); + vim_free(json); + } + } + --emsg_skip; + if (tv == &res_tv) + clear_tv(tv); + else + free_tv(tv); + } + } + else if (p_verbose > 2) + { + ch_error(channel, "Received unknown command: %s", (char *)cmd); + semsg(_("E905: received unknown command: %s"), cmd); + } +} + +/* + * Invoke the callback at "cbhead". + * Does not redraw but sets channel_need_redraw. + */ + static void +invoke_one_time_callback( + channel_T *channel, + cbq_T *cbhead, + cbq_T *item, + typval_T *argv) +{ + ch_log(channel, "Invoking one-time callback %s", + (char *)item->cq_callback); + /* Remove the item from the list first, if the callback + * invokes ch_close() the list will be cleared. */ + remove_cb_node(cbhead, item); + invoke_callback(channel, item->cq_callback, item->cq_partial, argv); + free_callback(item->cq_callback, item->cq_partial); + vim_free(item); +} + + static void +append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T part) +{ + bufref_T save_curbuf = {NULL, 0, 0}; + win_T *save_curwin = NULL; + tabpage_T *save_curtab = NULL; + linenr_T lnum = buffer->b_ml.ml_line_count; + int save_write_to = buffer->b_write_to_channel; + chanpart_T *ch_part = &channel->ch_part[part]; + int save_p_ma = buffer->b_p_ma; + int empty = (buffer->b_ml.ml_flags & ML_EMPTY) ? 1 : 0; + + if (!buffer->b_p_ma && !ch_part->ch_nomodifiable) + { + if (!ch_part->ch_nomod_error) + { + ch_error(channel, "Buffer is not modifiable, cannot append"); + ch_part->ch_nomod_error = TRUE; + } + return; + } + + /* If the buffer is also used as input insert above the last + * line. Don't write these lines. */ + if (save_write_to) + { + --lnum; + buffer->b_write_to_channel = FALSE; + } + + /* Append to the buffer */ + ch_log(channel, "appending line %d to buffer", (int)lnum + 1 - empty); + + buffer->b_p_ma = TRUE; + + /* Save curbuf/curwin/curtab and make "buffer" the current buffer. */ + switch_to_win_for_buf(buffer, &save_curwin, &save_curtab, &save_curbuf); + + u_sync(TRUE); + /* ignore undo failure, undo is not very useful here */ + vim_ignored = u_save(lnum - empty, lnum + 1); + + if (empty) + { + /* The buffer is empty, replace the first (dummy) line. */ + ml_replace(lnum, msg, TRUE); + lnum = 0; + } + else + ml_append(lnum, msg, 0, FALSE); + appended_lines_mark(lnum, 1L); + + /* Restore curbuf/curwin/curtab */ + restore_win_for_buf(save_curwin, save_curtab, &save_curbuf); + + if (ch_part->ch_nomodifiable) + buffer->b_p_ma = FALSE; + else + buffer->b_p_ma = save_p_ma; + + if (buffer->b_nwindows > 0) + { + win_T *wp; + + FOR_ALL_WINDOWS(wp) + { + if (wp->w_buffer == buffer + && (save_write_to + ? wp->w_cursor.lnum == lnum + 1 + : (wp->w_cursor.lnum == lnum + && wp->w_cursor.col == 0))) + { + ++wp->w_cursor.lnum; + save_curwin = curwin; + curwin = wp; + curbuf = curwin->w_buffer; + scroll_cursor_bot(0, FALSE); + curwin = save_curwin; + curbuf = curwin->w_buffer; + } + } + redraw_buf_and_status_later(buffer, VALID); + channel_need_redraw = TRUE; + } + + if (save_write_to) + { + channel_T *ch; + + /* Find channels reading from this buffer and adjust their + * next-to-read line number. */ + buffer->b_write_to_channel = TRUE; + for (ch = first_channel; ch != NULL; ch = ch->ch_next) + { + chanpart_T *in_part = &ch->ch_part[PART_IN]; + + if (in_part->ch_bufref.br_buf == buffer) + in_part->ch_buf_bot = buffer->b_ml.ml_line_count; + } + } +} + + static void +drop_messages(channel_T *channel, ch_part_T part) +{ + char_u *msg; + + while ((msg = channel_get(channel, part, NULL)) != NULL) + { + ch_log(channel, "Dropping message '%s'", (char *)msg); + vim_free(msg); + } +} + +/* + * Invoke a callback for "channel"/"part" if needed. + * This does not redraw but sets channel_need_redraw when redraw is needed. + * Return TRUE when a message was handled, there might be another one. + */ + static int +may_invoke_callback(channel_T *channel, ch_part_T part) +{ + char_u *msg = NULL; + typval_T *listtv = NULL; + typval_T argv[CH_JSON_MAX_ARGS]; + int seq_nr = -1; + chanpart_T *ch_part = &channel->ch_part[part]; + ch_mode_T ch_mode = ch_part->ch_mode; + cbq_T *cbhead = &ch_part->ch_cb_head; + cbq_T *cbitem; + char_u *callback = NULL; + partial_T *partial = NULL; + buf_T *buffer = NULL; + char_u *p; + + if (channel->ch_nb_close_cb != NULL) + /* this channel is handled elsewhere (netbeans) */ + return FALSE; + + /* Use a message-specific callback, part callback or channel callback */ + for (cbitem = cbhead->cq_next; cbitem != NULL; cbitem = cbitem->cq_next) + if (cbitem->cq_seq_nr == 0) + break; + if (cbitem != NULL) + { + callback = cbitem->cq_callback; + partial = cbitem->cq_partial; + } + else if (ch_part->ch_callback != NULL) + { + callback = ch_part->ch_callback; + partial = ch_part->ch_partial; + } + else + { + callback = channel->ch_callback; + partial = channel->ch_partial; + } + + buffer = ch_part->ch_bufref.br_buf; + if (buffer != NULL && (!bufref_valid(&ch_part->ch_bufref) + || buffer->b_ml.ml_mfp == NULL)) + { + /* buffer was wiped out or unloaded */ + ch_log(channel, "%s buffer has been wiped out", part_names[part]); + ch_part->ch_bufref.br_buf = NULL; + buffer = NULL; + } + + if (ch_mode == MODE_JSON || ch_mode == MODE_JS) + { + listitem_T *item; + int argc = 0; + + /* Get any json message in the queue. */ + if (channel_get_json(channel, part, -1, FALSE, &listtv) == FAIL) + { + /* Parse readahead, return when there is still no message. */ + channel_parse_json(channel, part); + if (channel_get_json(channel, part, -1, FALSE, &listtv) == FAIL) + return FALSE; + } + + for (item = listtv->vval.v_list->lv_first; + item != NULL && argc < CH_JSON_MAX_ARGS; + item = item->li_next) + argv[argc++] = item->li_tv; + while (argc < CH_JSON_MAX_ARGS) + argv[argc++].v_type = VAR_UNKNOWN; + + if (argv[0].v_type == VAR_STRING) + { + /* ["cmd", arg] or ["cmd", arg, arg] or ["cmd", arg, arg, arg] */ + channel_exe_cmd(channel, part, argv); + free_tv(listtv); + return TRUE; + } + + if (argv[0].v_type != VAR_NUMBER) + { + ch_error(channel, + "Dropping message with invalid sequence number type"); + free_tv(listtv); + return FALSE; + } + seq_nr = argv[0].vval.v_number; + } + else if (channel_peek(channel, part) == NULL) + { + /* nothing to read on RAW or NL channel */ + return FALSE; + } + else + { + /* If there is no callback or buffer drop the message. */ + if (callback == NULL && buffer == NULL) + { + /* If there is a close callback it may use ch_read() to get the + * messages. */ + if (channel->ch_close_cb == NULL && !channel->ch_drop_never) + drop_messages(channel, part); + return FALSE; + } + + if (ch_mode == MODE_NL) + { + char_u *nl = NULL; + char_u *buf; + readq_T *node; + + /* See if we have a message ending in NL in the first buffer. If + * not try to concatenate the first and the second buffer. */ + while (TRUE) + { + node = channel_peek(channel, part); + nl = channel_first_nl(node); + if (nl != NULL) + break; + if (channel_collapse(channel, part, TRUE) == FAIL) + { + if (ch_part->ch_fd == INVALID_FD && node->rq_buflen > 0) + break; + return FALSE; /* incomplete message */ + } + } + buf = node->rq_buffer; + + if (nl == NULL) + { + /* Flush remaining message that is missing a NL. */ + char_u *new_buf; + + new_buf = vim_realloc(buf, node->rq_buflen + 1); + if (new_buf == NULL) + /* This might fail over and over again, should the message + * be dropped? */ + return FALSE; + buf = new_buf; + node->rq_buffer = buf; + nl = buf + node->rq_buflen++; + *nl = NUL; + } + + /* Convert NUL to NL, the internal representation. */ + for (p = buf; p < nl && p < buf + node->rq_buflen; ++p) + if (*p == NUL) + *p = NL; + + if (nl + 1 == buf + node->rq_buflen) + { + /* get the whole buffer, drop the NL */ + msg = channel_get(channel, part, NULL); + *nl = NUL; + } + else + { + /* Copy the message into allocated memory (excluding the NL) + * and remove it from the buffer (including the NL). */ + msg = vim_strnsave(buf, (int)(nl - buf)); + channel_consume(channel, part, (int)(nl - buf) + 1); + } + } + else + { + /* For a raw channel we don't know where the message ends, just + * get everything we have. + * Convert NUL to NL, the internal representation. */ + msg = channel_get_all(channel, part, NULL); + } + + if (msg == NULL) + return FALSE; /* out of memory (and avoids Coverity warning) */ + + argv[1].v_type = VAR_STRING; + argv[1].vval.v_string = msg; + } + + if (seq_nr > 0) + { + int done = FALSE; + + /* JSON or JS mode: invoke the one-time callback with the matching nr */ + for (cbitem = cbhead->cq_next; cbitem != NULL; cbitem = cbitem->cq_next) + if (cbitem->cq_seq_nr == seq_nr) + { + invoke_one_time_callback(channel, cbhead, cbitem, argv); + done = TRUE; + break; + } + if (!done) + { + if (channel->ch_drop_never) + { + /* message must be read with ch_read() */ + channel_push_json(channel, part, listtv); + listtv = NULL; + } + else + ch_log(channel, "Dropping message %d without callback", + seq_nr); + } + } + else if (callback != NULL || buffer != NULL) + { + if (buffer != NULL) + { + if (msg == NULL) + /* JSON or JS mode: re-encode the message. */ + msg = json_encode(listtv, ch_mode); + if (msg != NULL) + { +#ifdef FEAT_TERMINAL + if (buffer->b_term != NULL) + write_to_term(buffer, msg, channel); + else +#endif + append_to_buffer(buffer, msg, channel, part); + } + } + + if (callback != NULL) + { + if (cbitem != NULL) + invoke_one_time_callback(channel, cbhead, cbitem, argv); + else + { + /* invoke the channel callback */ + ch_log(channel, "Invoking channel callback %s", + (char *)callback); + invoke_callback(channel, callback, partial, argv); + } + } + } + else + ch_log(channel, "Dropping message %d", seq_nr); + + if (listtv != NULL) + free_tv(listtv); + vim_free(msg); + + return TRUE; +} + +#if defined(FEAT_NETBEANS_INTG) || defined(PROTO) +/* + * Return TRUE when channel "channel" is open for writing to. + * Also returns FALSE or invalid "channel". + */ + int +channel_can_write_to(channel_T *channel) +{ + return channel != NULL && (channel->CH_SOCK_FD != INVALID_FD + || channel->CH_IN_FD != INVALID_FD); +} +#endif + +/* + * Return TRUE when channel "channel" is open for reading or writing. + * Also returns FALSE for invalid "channel". + */ + int +channel_is_open(channel_T *channel) +{ + return channel != NULL && (channel->CH_SOCK_FD != INVALID_FD + || channel->CH_IN_FD != INVALID_FD + || channel->CH_OUT_FD != INVALID_FD + || channel->CH_ERR_FD != INVALID_FD); +} + +/* + * Return TRUE if "channel" has JSON or other typeahead. + */ + int +channel_has_readahead(channel_T *channel, ch_part_T part) +{ + ch_mode_T ch_mode = channel->ch_part[part].ch_mode; + + if (ch_mode == MODE_JSON || ch_mode == MODE_JS) + { + jsonq_T *head = &channel->ch_part[part].ch_json_head; + jsonq_T *item = head->jq_next; + + return item != NULL; + } + return channel_peek(channel, part) != NULL; +} + +/* + * Return a string indicating the status of the channel. + * If "req_part" is not negative check that part. + */ + char * +channel_status(channel_T *channel, int req_part) +{ + ch_part_T part; + int has_readahead = FALSE; + + if (channel == NULL) + return "fail"; + if (req_part == PART_OUT) + { + if (channel->CH_OUT_FD != INVALID_FD) + return "open"; + if (channel_has_readahead(channel, PART_OUT)) + has_readahead = TRUE; + } + else if (req_part == PART_ERR) + { + if (channel->CH_ERR_FD != INVALID_FD) + return "open"; + if (channel_has_readahead(channel, PART_ERR)) + has_readahead = TRUE; + } + else + { + if (channel_is_open(channel)) + return "open"; + for (part = PART_SOCK; part < PART_IN; ++part) + if (channel_has_readahead(channel, part)) + { + has_readahead = TRUE; + break; + } + } + + if (has_readahead) + return "buffered"; + return "closed"; +} + + static void +channel_part_info(channel_T *channel, dict_T *dict, char *name, ch_part_T part) +{ + chanpart_T *chanpart = &channel->ch_part[part]; + char namebuf[20]; /* longest is "sock_timeout" */ + size_t tail; + char *status; + char *s = ""; + + vim_strncpy((char_u *)namebuf, (char_u *)name, 4); + STRCAT(namebuf, "_"); + tail = STRLEN(namebuf); + + STRCPY(namebuf + tail, "status"); + if (chanpart->ch_fd != INVALID_FD) + status = "open"; + else if (channel_has_readahead(channel, part)) + status = "buffered"; + else + status = "closed"; + dict_add_string(dict, namebuf, (char_u *)status); + + STRCPY(namebuf + tail, "mode"); + switch (chanpart->ch_mode) + { + case MODE_NL: s = "NL"; break; + case MODE_RAW: s = "RAW"; break; + case MODE_JSON: s = "JSON"; break; + case MODE_JS: s = "JS"; break; + } + dict_add_string(dict, namebuf, (char_u *)s); + + STRCPY(namebuf + tail, "io"); + if (part == PART_SOCK) + s = "socket"; + else switch (chanpart->ch_io) + { + case JIO_NULL: s = "null"; break; + case JIO_PIPE: s = "pipe"; break; + case JIO_FILE: s = "file"; break; + case JIO_BUFFER: s = "buffer"; break; + case JIO_OUT: s = "out"; break; + } + dict_add_string(dict, namebuf, (char_u *)s); + + STRCPY(namebuf + tail, "timeout"); + dict_add_number(dict, namebuf, chanpart->ch_timeout); +} + + void +channel_info(channel_T *channel, dict_T *dict) +{ + dict_add_number(dict, "id", channel->ch_id); + dict_add_string(dict, "status", (char_u *)channel_status(channel, -1)); + + if (channel->ch_hostname != NULL) + { + dict_add_string(dict, "hostname", (char_u *)channel->ch_hostname); + dict_add_number(dict, "port", channel->ch_port); + channel_part_info(channel, dict, "sock", PART_SOCK); + } + else + { + channel_part_info(channel, dict, "out", PART_OUT); + channel_part_info(channel, dict, "err", PART_ERR); + channel_part_info(channel, dict, "in", PART_IN); + } +} + +/* + * Close channel "channel". + * Trigger the close callback if "invoke_close_cb" is TRUE. + * Does not clear the buffers. + */ + void +channel_close(channel_T *channel, int invoke_close_cb) +{ + ch_log(channel, "Closing channel"); + +#ifdef FEAT_GUI + channel_gui_unregister(channel); +#endif + + ch_close_part(channel, PART_SOCK); + ch_close_part(channel, PART_IN); + ch_close_part(channel, PART_OUT); + ch_close_part(channel, PART_ERR); + + if (invoke_close_cb) + { + ch_part_T part; + + /* Invoke callbacks and flush buffers before the close callback. */ + if (channel->ch_close_cb != NULL) + ch_log(channel, + "Invoking callbacks and flushing buffers before closing"); + for (part = PART_SOCK; part < PART_IN; ++part) + { + if (channel->ch_close_cb != NULL + || channel->ch_part[part].ch_bufref.br_buf != NULL) + { + /* Increment the refcount to avoid the channel being freed + * halfway. */ + ++channel->ch_refcount; + if (channel->ch_close_cb == NULL) + ch_log(channel, "flushing %s buffers before closing", + part_names[part]); + while (may_invoke_callback(channel, part)) + ; + --channel->ch_refcount; + } + } + + if (channel->ch_close_cb != NULL) + { + typval_T argv[1]; + typval_T rettv; + int dummy; + + /* Increment the refcount to avoid the channel being freed + * halfway. */ + ++channel->ch_refcount; + ch_log(channel, "Invoking close callback %s", + (char *)channel->ch_close_cb); + argv[0].v_type = VAR_CHANNEL; + argv[0].vval.v_channel = channel; + call_func(channel->ch_close_cb, (int)STRLEN(channel->ch_close_cb), + &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, + channel->ch_close_partial, NULL); + clear_tv(&rettv); + channel_need_redraw = TRUE; + + /* the callback is only called once */ + free_callback(channel->ch_close_cb, channel->ch_close_partial); + channel->ch_close_cb = NULL; + channel->ch_close_partial = NULL; + + if (channel_need_redraw) + { + channel_need_redraw = FALSE; + redraw_after_callback(TRUE); + } + + if (!channel->ch_drop_never) + /* any remaining messages are useless now */ + for (part = PART_SOCK; part < PART_IN; ++part) + drop_messages(channel, part); + + --channel->ch_refcount; + } + } + + channel->ch_nb_close_cb = NULL; + +#ifdef FEAT_TERMINAL + term_channel_closed(channel); +#endif +} + +/* + * Close the "in" part channel "channel". + */ + void +channel_close_in(channel_T *channel) +{ + ch_close_part(channel, PART_IN); +} + + static void +remove_from_writeque(writeq_T *wq, writeq_T *entry) +{ + ga_clear(&entry->wq_ga); + wq->wq_next = entry->wq_next; + if (wq->wq_next == NULL) + wq->wq_prev = NULL; + else + wq->wq_next->wq_prev = NULL; + vim_free(entry); +} + +/* + * Clear the read buffer on "channel"/"part". + */ + static void +channel_clear_one(channel_T *channel, ch_part_T part) +{ + chanpart_T *ch_part = &channel->ch_part[part]; + jsonq_T *json_head = &ch_part->ch_json_head; + cbq_T *cb_head = &ch_part->ch_cb_head; + + while (channel_peek(channel, part) != NULL) + vim_free(channel_get(channel, part, NULL)); + + while (cb_head->cq_next != NULL) + { + cbq_T *node = cb_head->cq_next; + + remove_cb_node(cb_head, node); + free_callback(node->cq_callback, node->cq_partial); + vim_free(node); + } + + while (json_head->jq_next != NULL) + { + free_tv(json_head->jq_next->jq_value); + remove_json_node(json_head, json_head->jq_next); + } + + free_callback(ch_part->ch_callback, ch_part->ch_partial); + ch_part->ch_callback = NULL; + ch_part->ch_partial = NULL; + + while (ch_part->ch_writeque.wq_next != NULL) + remove_from_writeque(&ch_part->ch_writeque, + ch_part->ch_writeque.wq_next); +} + +/* + * Clear all the read buffers on "channel". + */ + void +channel_clear(channel_T *channel) +{ + ch_log(channel, "Clearing channel"); + VIM_CLEAR(channel->ch_hostname); + channel_clear_one(channel, PART_SOCK); + channel_clear_one(channel, PART_OUT); + channel_clear_one(channel, PART_ERR); + channel_clear_one(channel, PART_IN); + free_callback(channel->ch_callback, channel->ch_partial); + channel->ch_callback = NULL; + channel->ch_partial = NULL; + free_callback(channel->ch_close_cb, channel->ch_close_partial); + channel->ch_close_cb = NULL; + channel->ch_close_partial = NULL; +} + +#if defined(EXITFREE) || defined(PROTO) + void +channel_free_all(void) +{ + channel_T *channel; + + ch_log(NULL, "channel_free_all()"); + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + channel_clear(channel); +} +#endif + + +/* Sent when the netbeans channel is found closed when reading. */ +#define DETACH_MSG_RAW "DETACH\n" + +/* Buffer size for reading incoming messages. */ +#define MAXMSGSIZE 4096 + +#if defined(HAVE_SELECT) +/* + * Add write fds where we are waiting for writing to be possible. + */ + static int +channel_fill_wfds(int maxfd_arg, fd_set *wfds) +{ + int maxfd = maxfd_arg; + channel_T *ch; + + for (ch = first_channel; ch != NULL; ch = ch->ch_next) + { + chanpart_T *in_part = &ch->ch_part[PART_IN]; + + if (in_part->ch_fd != INVALID_FD + && (in_part->ch_bufref.br_buf != NULL + || in_part->ch_writeque.wq_next != NULL)) + { + FD_SET((int)in_part->ch_fd, wfds); + if ((int)in_part->ch_fd >= maxfd) + maxfd = (int)in_part->ch_fd + 1; + } + } + return maxfd; +} +#else +/* + * Add write fds where we are waiting for writing to be possible. + */ + static int +channel_fill_poll_write(int nfd_in, struct pollfd *fds) +{ + int nfd = nfd_in; + channel_T *ch; + + for (ch = first_channel; ch != NULL; ch = ch->ch_next) + { + chanpart_T *in_part = &ch->ch_part[PART_IN]; + + if (in_part->ch_fd != INVALID_FD + && (in_part->ch_bufref.br_buf != NULL + || in_part->ch_writeque.wq_next != NULL)) + { + in_part->ch_poll_idx = nfd; + fds[nfd].fd = in_part->ch_fd; + fds[nfd].events = POLLOUT; + ++nfd; + } + else + in_part->ch_poll_idx = -1; + } + return nfd; +} +#endif + +typedef enum { + CW_READY, + CW_NOT_READY, + CW_ERROR +} channel_wait_result; + +/* + * Check for reading from "fd" with "timeout" msec. + * Return CW_READY when there is something to read. + * Return CW_NOT_READY when there is nothing to read. + * Return CW_ERROR when there is an error. + */ + static channel_wait_result +channel_wait(channel_T *channel, sock_T fd, int timeout) +{ + if (timeout > 0) + ch_log(channel, "Waiting for up to %d msec", timeout); + +# ifdef WIN32 + if (fd != channel->CH_SOCK_FD) + { + DWORD nread; + int sleep_time; + DWORD deadline = GetTickCount() + timeout; + int delay = 1; + + /* reading from a pipe, not a socket */ + while (TRUE) + { + int r = PeekNamedPipe((HANDLE)fd, NULL, 0, NULL, &nread, NULL); + + if (r && nread > 0) + return CW_READY; + + if (channel->ch_named_pipe) + { + DisconnectNamedPipe((HANDLE)fd); + ConnectNamedPipe((HANDLE)fd, NULL); + } + else if (r == 0) + return CW_ERROR; + + /* perhaps write some buffer lines */ + channel_write_any_lines(); + + sleep_time = deadline - GetTickCount(); + if (sleep_time <= 0) + break; + /* Wait for a little while. Very short at first, up to 10 msec + * after looping a few times. */ + if (sleep_time > delay) + sleep_time = delay; + Sleep(sleep_time); + delay = delay * 2; + if (delay > 10) + delay = 10; + } + } + else +#endif + { +#if defined(HAVE_SELECT) + struct timeval tval; + fd_set rfds; + fd_set wfds; + int ret; + int maxfd; + + tval.tv_sec = timeout / 1000; + tval.tv_usec = (timeout % 1000) * 1000; + for (;;) + { + FD_ZERO(&rfds); + FD_SET((int)fd, &rfds); + + /* Write lines to a pipe when a pipe can be written to. Need to + * set this every time, some buffers may be done. */ + maxfd = (int)fd + 1; + FD_ZERO(&wfds); + maxfd = channel_fill_wfds(maxfd, &wfds); + + ret = select(maxfd, &rfds, &wfds, NULL, &tval); +# ifdef EINTR + SOCK_ERRNO; + if (ret == -1 && errno == EINTR) + continue; +# endif + if (ret > 0) + { + if (FD_ISSET(fd, &rfds)) + return CW_READY; + channel_write_any_lines(); + continue; + } + break; + } +#else + for (;;) + { + struct pollfd fds[MAX_OPEN_CHANNELS + 1]; + int nfd = 1; + + fds[0].fd = fd; + fds[0].events = POLLIN; + nfd = channel_fill_poll_write(nfd, fds); + if (poll(fds, nfd, timeout) > 0) + { + if (fds[0].revents & POLLIN) + return CW_READY; + channel_write_any_lines(); + continue; + } + break; + } +#endif + } + return CW_NOT_READY; +} + + static void +ch_close_part_on_error( + channel_T *channel, ch_part_T part, int is_err, char *func) +{ + char msg[] = "%s(): Read %s from ch_part[%d], closing"; + + if (is_err) + /* Do not call emsg(), most likely the other end just exited. */ + ch_error(channel, msg, func, "error", part); + else + ch_log(channel, msg, func, "EOF", part); + + /* Queue a "DETACH" netbeans message in the command queue in order to + * terminate the netbeans session later. Do not end the session here + * directly as we may be running in the context of a call to + * netbeans_parse_messages(): + * netbeans_parse_messages + * -> autocmd triggered while processing the netbeans cmd + * -> ui_breakcheck + * -> gui event loop or select loop + * -> channel_read() + * Only send "DETACH" for a netbeans channel. + */ + if (channel->ch_nb_close_cb != NULL) + channel_save(channel, PART_SOCK, (char_u *)DETACH_MSG_RAW, + (int)STRLEN(DETACH_MSG_RAW), FALSE, "PUT "); + + /* When reading is not possible close this part of the channel. Don't + * close the channel yet, there may be something to read on another part. + * When stdout and stderr use the same FD we get the error only on one of + * them, also close the other. */ + if (part == PART_OUT || part == PART_ERR) + { + ch_part_T other = part == PART_OUT ? PART_ERR : PART_OUT; + + if (channel->ch_part[part].ch_fd == channel->ch_part[other].ch_fd) + ch_close_part(channel, other); + } + ch_close_part(channel, part); + +#ifdef FEAT_GUI + /* Stop listening to GUI events right away. */ + channel_gui_unregister_one(channel, part); +#endif +} + + static void +channel_close_now(channel_T *channel) +{ + ch_log(channel, "Closing channel because all readable fds are closed"); + if (channel->ch_nb_close_cb != NULL) + (*channel->ch_nb_close_cb)(); + channel_close(channel, TRUE); +} + +/* + * Read from channel "channel" for as long as there is something to read. + * "part" is PART_SOCK, PART_OUT or PART_ERR. + * The data is put in the read queue. No callbacks are invoked here. + */ + static void +channel_read(channel_T *channel, ch_part_T part, char *func) +{ + static char_u *buf = NULL; + int len = 0; + int readlen = 0; + sock_T fd; + int use_socket = FALSE; + + fd = channel->ch_part[part].ch_fd; + if (fd == INVALID_FD) + { + ch_error(channel, "channel_read() called while %s part is closed", + part_names[part]); + return; + } + use_socket = fd == channel->CH_SOCK_FD; + + /* Allocate a buffer to read into. */ + if (buf == NULL) + { + buf = alloc(MAXMSGSIZE); + if (buf == NULL) + return; /* out of memory! */ + } + + /* Keep on reading for as long as there is something to read. + * Use select() or poll() to avoid blocking on a message that is exactly + * MAXMSGSIZE long. */ + for (;;) + { + if (channel_wait(channel, fd, 0) != CW_READY) + break; + if (use_socket) + len = sock_read(fd, (char *)buf, MAXMSGSIZE); + else + len = fd_read(fd, (char *)buf, MAXMSGSIZE); + if (len <= 0) + break; /* error or nothing more to read */ + + /* Store the read message in the queue. */ + channel_save(channel, part, buf, len, FALSE, "RECV "); + readlen += len; + if (len < MAXMSGSIZE) + break; /* did read everything that's available */ + } + + /* Reading a disconnection (readlen == 0), or an error. */ + if (readlen <= 0) + { + if (!channel->ch_keep_open) + ch_close_part_on_error(channel, part, (len < 0), func); + } +#if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK) + else if (CH_HAS_GUI && gtk_main_level() > 0) + /* signal the main loop that there is something to read */ + gtk_main_quit(); +#endif +} + +/* + * Read from RAW or NL "channel"/"part". Blocks until there is something to + * read or the timeout expires. + * When "raw" is TRUE don't block waiting on a NL. + * Returns what was read in allocated memory. + * Returns NULL in case of error or timeout. + */ + static char_u * +channel_read_block( + channel_T *channel, ch_part_T part, int timeout, int raw, int *outlen) +{ + char_u *buf; + char_u *msg; + ch_mode_T mode = channel->ch_part[part].ch_mode; + sock_T fd = channel->ch_part[part].ch_fd; + char_u *nl; + readq_T *node; + + ch_log(channel, "Blocking %s read, timeout: %d msec", + mode == MODE_RAW ? "RAW" : "NL", timeout); + + while (TRUE) + { + node = channel_peek(channel, part); + if (node != NULL) + { + if (mode == MODE_RAW || (mode == MODE_NL + && channel_first_nl(node) != NULL)) + /* got a complete message */ + break; + if (channel_collapse(channel, part, mode == MODE_NL) == OK) + continue; + /* If not blocking or nothing more is coming then return what we + * have. */ + if (raw || fd == INVALID_FD) + break; + } + + /* Wait for up to the channel timeout. */ + if (fd == INVALID_FD) + return NULL; + if (channel_wait(channel, fd, timeout) != CW_READY) + { + ch_log(channel, "Timed out"); + return NULL; + } + channel_read(channel, part, "channel_read_block"); + } + + /* We have a complete message now. */ + if (mode == MODE_RAW || outlen != NULL) + { + msg = channel_get_all(channel, part, outlen); + } + else + { + char_u *p; + + buf = node->rq_buffer; + nl = channel_first_nl(node); + + /* Convert NUL to NL, the internal representation. */ + for (p = buf; (nl == NULL || p < nl) && p < buf + node->rq_buflen; ++p) + if (*p == NUL) + *p = NL; + + if (nl == NULL) + { + /* must be a closed channel with missing NL */ + msg = channel_get(channel, part, NULL); + } + else if (nl + 1 == buf + node->rq_buflen) + { + /* get the whole buffer */ + msg = channel_get(channel, part, NULL); + *nl = NUL; + } + else + { + /* Copy the message into allocated memory and remove it from the + * buffer. */ + msg = vim_strnsave(buf, (int)(nl - buf)); + channel_consume(channel, part, (int)(nl - buf) + 1); + } + } + if (ch_log_active()) + ch_log(channel, "Returning %d bytes", (int)STRLEN(msg)); + return msg; +} + +/* + * Read one JSON message with ID "id" from "channel"/"part" and store the + * result in "rettv". + * When "id" is -1 accept any message; + * Blocks until the message is received or the timeout is reached. + */ + static int +channel_read_json_block( + channel_T *channel, + ch_part_T part, + int timeout_arg, + int id, + typval_T **rettv) +{ + int more; + sock_T fd; + int timeout; + chanpart_T *chanpart = &channel->ch_part[part]; + + ch_log(channel, "Reading JSON"); + if (id != -1) + chanpart->ch_block_id = id; + for (;;) + { + more = channel_parse_json(channel, part); + + /* search for message "id" */ + if (channel_get_json(channel, part, id, TRUE, rettv) == OK) + { + chanpart->ch_block_id = 0; + return OK; + } + + if (!more) + { + /* Handle any other messages in the queue. If done some more + * messages may have arrived. */ + if (channel_parse_messages()) + continue; + + /* Wait for up to the timeout. If there was an incomplete message + * use the deadline for that. */ + timeout = timeout_arg; + if (chanpart->ch_wait_len > 0) + { +#ifdef WIN32 + timeout = chanpart->ch_deadline - GetTickCount() + 1; +#else + { + struct timeval now_tv; + + gettimeofday(&now_tv, NULL); + timeout = (chanpart->ch_deadline.tv_sec + - now_tv.tv_sec) * 1000 + + (chanpart->ch_deadline.tv_usec + - now_tv.tv_usec) / 1000 + + 1; + } +#endif + if (timeout < 0) + { + /* Something went wrong, channel_parse_json() didn't + * discard message. Cancel waiting. */ + chanpart->ch_wait_len = 0; + timeout = timeout_arg; + } + else if (timeout > timeout_arg) + timeout = timeout_arg; + } + fd = chanpart->ch_fd; + if (fd == INVALID_FD + || channel_wait(channel, fd, timeout) != CW_READY) + { + if (timeout == timeout_arg) + { + if (fd != INVALID_FD) + ch_log(channel, "Timed out"); + break; + } + } + else + channel_read(channel, part, "channel_read_json_block"); + } + } + chanpart->ch_block_id = 0; + return FAIL; +} + +/* + * Common for ch_read() and ch_readraw(). + */ + void +common_channel_read(typval_T *argvars, typval_T *rettv, int raw, int blob) +{ + channel_T *channel; + ch_part_T part = PART_COUNT; + jobopt_T opt; + int mode; + int timeout; + int id = -1; + typval_T *listtv = NULL; + + /* return an empty string by default */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + clear_job_options(&opt); + if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID, 0) + == FAIL) + goto theend; + + if (opt.jo_set & JO_PART) + part = opt.jo_part; + channel = get_channel_arg(&argvars[0], TRUE, TRUE, part); + if (channel != NULL) + { + if (part == PART_COUNT) + part = channel_part_read(channel); + mode = channel_get_mode(channel, part); + timeout = channel_get_timeout(channel, part); + if (opt.jo_set & JO_TIMEOUT) + timeout = opt.jo_timeout; + + if (blob) + { + int outlen = 0; + char_u *p = channel_read_block(channel, part, + timeout, TRUE, &outlen); + if (p != NULL) + { + blob_T *b = blob_alloc(); + + if (b != NULL) + { + b->bv_ga.ga_len = outlen; + if (ga_grow(&b->bv_ga, outlen) == FAIL) + blob_free(b); + else + { + memcpy(b->bv_ga.ga_data, p, outlen); + rettv_blob_set(rettv, b); + } + } + vim_free(p); + } + } + else if (raw || mode == MODE_RAW || mode == MODE_NL) + rettv->vval.v_string = channel_read_block(channel, part, + timeout, raw, NULL); + else + { + if (opt.jo_set & JO_ID) + id = opt.jo_id; + channel_read_json_block(channel, part, timeout, id, &listtv); + if (listtv != NULL) + { + *rettv = *listtv; + vim_free(listtv); + } + else + { + rettv->v_type = VAR_SPECIAL; + rettv->vval.v_number = VVAL_NONE; + } + } + } + +theend: + free_job_options(&opt); +} + +# if defined(WIN32) || defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) \ + || defined(PROTO) +/* + * Lookup the channel from the socket. Set "partp" to the fd index. + * Returns NULL when the socket isn't found. + */ + channel_T * +channel_fd2channel(sock_T fd, ch_part_T *partp) +{ + channel_T *channel; + ch_part_T part; + + if (fd != INVALID_FD) + for (channel = first_channel; channel != NULL; + channel = channel->ch_next) + { + for (part = PART_SOCK; part < PART_IN; ++part) + if (channel->ch_part[part].ch_fd == fd) + { + *partp = part; + return channel; + } + } + return NULL; +} +# endif + +# if defined(WIN32) || defined(FEAT_GUI) || defined(PROTO) +/* + * Check the channels for anything that is ready to be read. + * The data is put in the read queue. + * if "only_keep_open" is TRUE only check channels where ch_keep_open is set. + */ + void +channel_handle_events(int only_keep_open) +{ + channel_T *channel; + ch_part_T part; + sock_T fd; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + { + if (only_keep_open && !channel->ch_keep_open) + continue; + + /* check the socket and pipes */ + for (part = PART_SOCK; part < PART_IN; ++part) + { + fd = channel->ch_part[part].ch_fd; + if (fd != INVALID_FD) + { + int r = channel_wait(channel, fd, 0); + + if (r == CW_READY) + channel_read(channel, part, "channel_handle_events"); + else if (r == CW_ERROR) + ch_close_part_on_error(channel, part, TRUE, + "channel_handle_events"); + } + } + } +} +# endif + +# if defined(FEAT_GUI) || defined(PROTO) +/* + * Return TRUE when there is any channel with a keep_open flag. + */ + int +channel_any_keep_open() +{ + channel_T *channel; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + if (channel->ch_keep_open) + return TRUE; + return FALSE; +} +# endif + +/* + * Set "channel"/"part" to non-blocking. + * Only works for sockets and pipes. + */ + void +channel_set_nonblock(channel_T *channel, ch_part_T part) +{ + chanpart_T *ch_part = &channel->ch_part[part]; + int fd = ch_part->ch_fd; + + if (fd != INVALID_FD) + { +#ifdef _WIN32 + u_long val = 1; + + ioctlsocket(fd, FIONBIO, &val); +#else + (void)fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + ch_part->ch_nonblocking = TRUE; + } +} + +/* + * Write "buf" (NUL terminated string) to "channel"/"part". + * When "fun" is not NULL an error message might be given. + * Return FAIL or OK. + */ + int +channel_send( + channel_T *channel, + ch_part_T part, + char_u *buf_arg, + int len_arg, + char *fun) +{ + int res; + sock_T fd; + chanpart_T *ch_part = &channel->ch_part[part]; + int did_use_queue = FALSE; + + fd = ch_part->ch_fd; + if (fd == INVALID_FD) + { + if (!channel->ch_error && fun != NULL) + { + ch_error(channel, "%s(): write while not connected", fun); + semsg(_("E630: %s(): write while not connected"), fun); + } + channel->ch_error = TRUE; + return FAIL; + } + + if (channel->ch_nonblock && !ch_part->ch_nonblocking) + channel_set_nonblock(channel, part); + + if (ch_log_active()) + { + ch_log_lead("SEND ", channel, part); + fprintf(log_fd, "'"); + vim_ignored = (int)fwrite(buf_arg, len_arg, 1, log_fd); + fprintf(log_fd, "'\n"); + fflush(log_fd); + did_log_msg = TRUE; + } + + for (;;) + { + writeq_T *wq = &ch_part->ch_writeque; + char_u *buf; + int len; + + if (wq->wq_next != NULL) + { + /* first write what was queued */ + buf = wq->wq_next->wq_ga.ga_data; + len = wq->wq_next->wq_ga.ga_len; + did_use_queue = TRUE; + } + else + { + if (len_arg == 0) + /* nothing to write, called from channel_select_check() */ + return OK; + buf = buf_arg; + len = len_arg; + } + + if (part == PART_SOCK) + res = sock_write(fd, (char *)buf, len); + else + { + res = fd_write(fd, (char *)buf, len); +#ifdef WIN32 + if (channel->ch_named_pipe && res < 0) + { + DisconnectNamedPipe((HANDLE)fd); + ConnectNamedPipe((HANDLE)fd, NULL); + } +#endif + } + if (res < 0 && (errno == EWOULDBLOCK +#ifdef EAGAIN + || errno == EAGAIN +#endif + )) + res = 0; /* nothing got written */ + + if (res >= 0 && ch_part->ch_nonblocking) + { + writeq_T *entry = wq->wq_next; + + if (did_use_queue) + ch_log(channel, "Sent %d bytes now", res); + if (res == len) + { + /* Wrote all the buf[len] bytes. */ + if (entry != NULL) + { + /* Remove the entry from the write queue. */ + remove_from_writeque(wq, entry); + continue; + } + if (did_use_queue) + ch_log(channel, "Write queue empty"); + } + else + { + /* Wrote only buf[res] bytes, can't write more now. */ + if (entry != NULL) + { + if (res > 0) + { + /* Remove the bytes that were written. */ + mch_memmove(entry->wq_ga.ga_data, + (char *)entry->wq_ga.ga_data + res, + len - res); + entry->wq_ga.ga_len -= res; + } + buf = buf_arg; + len = len_arg; + } + else + { + buf += res; + len -= res; + } + ch_log(channel, "Adding %d bytes to the write queue", len); + + /* Append the not written bytes of the argument to the write + * buffer. Limit entries to 4000 bytes. */ + if (wq->wq_prev != NULL + && wq->wq_prev->wq_ga.ga_len + len < 4000) + { + writeq_T *last = wq->wq_prev; + + /* append to the last entry */ + if (ga_grow(&last->wq_ga, len) == OK) + { + mch_memmove((char *)last->wq_ga.ga_data + + last->wq_ga.ga_len, + buf, len); + last->wq_ga.ga_len += len; + } + } + else + { + writeq_T *last = (writeq_T *)alloc((int)sizeof(writeq_T)); + + if (last != NULL) + { + last->wq_prev = wq->wq_prev; + last->wq_next = NULL; + if (wq->wq_prev == NULL) + wq->wq_next = last; + else + wq->wq_prev->wq_next = last; + wq->wq_prev = last; + ga_init2(&last->wq_ga, 1, 1000); + if (ga_grow(&last->wq_ga, len) == OK) + { + mch_memmove(last->wq_ga.ga_data, buf, len); + last->wq_ga.ga_len = len; + } + } + } + } + } + else if (res != len) + { + if (!channel->ch_error && fun != NULL) + { + ch_error(channel, "%s(): write failed", fun); + semsg(_("E631: %s(): write failed"), fun); + } + channel->ch_error = TRUE; + return FAIL; + } + + channel->ch_error = FALSE; + return OK; + } +} + +/* + * Common for "ch_sendexpr()" and "ch_sendraw()". + * Returns the channel if the caller should read the response. + * Sets "part_read" to the read fd. + * Otherwise returns NULL. + */ + static channel_T * +send_common( + typval_T *argvars, + char_u *text, + int len, + int id, + int eval, + jobopt_T *opt, + char *fun, + ch_part_T *part_read) +{ + channel_T *channel; + ch_part_T part_send; + + clear_job_options(opt); + channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0); + if (channel == NULL) + return NULL; + part_send = channel_part_send(channel); + *part_read = channel_part_read(channel); + + if (get_job_options(&argvars[2], opt, JO_CALLBACK + JO_TIMEOUT, 0) == FAIL) + return NULL; + + /* Set the callback. An empty callback means no callback and not reading + * the response. With "ch_evalexpr()" and "ch_evalraw()" a callback is not + * allowed. */ + if (opt->jo_callback != NULL && *opt->jo_callback != NUL) + { + if (eval) + { + semsg(_("E917: Cannot use a callback with %s()"), fun); + return NULL; + } + channel_set_req_callback(channel, *part_read, + opt->jo_callback, opt->jo_partial, id); + } + + if (channel_send(channel, part_send, text, len, fun) == OK + && opt->jo_callback == NULL) + return channel; + return NULL; +} + +/* + * common for "ch_evalexpr()" and "ch_sendexpr()" + */ + void +ch_expr_common(typval_T *argvars, typval_T *rettv, int eval) +{ + char_u *text; + typval_T *listtv; + channel_T *channel; + int id; + ch_mode_T ch_mode; + ch_part_T part_send; + ch_part_T part_read; + jobopt_T opt; + int timeout; + + /* return an empty string by default */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0); + if (channel == NULL) + return; + part_send = channel_part_send(channel); + + ch_mode = channel_get_mode(channel, part_send); + if (ch_mode == MODE_RAW || ch_mode == MODE_NL) + { + emsg(_("E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel")); + return; + } + + id = ++channel->ch_last_msg_id; + text = json_encode_nr_expr(id, &argvars[1], + (ch_mode == MODE_JS ? JSON_JS : 0) | JSON_NL); + if (text == NULL) + return; + + channel = send_common(argvars, text, (int)STRLEN(text), id, eval, &opt, + eval ? "ch_evalexpr" : "ch_sendexpr", &part_read); + vim_free(text); + if (channel != NULL && eval) + { + if (opt.jo_set & JO_TIMEOUT) + timeout = opt.jo_timeout; + else + timeout = channel_get_timeout(channel, part_read); + if (channel_read_json_block(channel, part_read, timeout, id, &listtv) + == OK) + { + list_T *list = listtv->vval.v_list; + + /* Move the item from the list and then change the type to + * avoid the value being freed. */ + *rettv = list->lv_last->li_tv; + list->lv_last->li_tv.v_type = VAR_NUMBER; + free_tv(listtv); + } + } + free_job_options(&opt); +} + +/* + * common for "ch_evalraw()" and "ch_sendraw()" + */ + void +ch_raw_common(typval_T *argvars, typval_T *rettv, int eval) +{ + char_u buf[NUMBUFLEN]; + char_u *text; + int len; + channel_T *channel; + ch_part_T part_read; + jobopt_T opt; + int timeout; + + /* return an empty string by default */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (argvars[1].v_type == VAR_BLOB) + { + text = argvars[1].vval.v_blob->bv_ga.ga_data; + len = argvars[1].vval.v_blob->bv_ga.ga_len; + } + else + { + text = tv_get_string_buf(&argvars[1], buf); + len = (int)STRLEN(text); + } + channel = send_common(argvars, text, len, 0, eval, &opt, + eval ? "ch_evalraw" : "ch_sendraw", &part_read); + if (channel != NULL && eval) + { + if (opt.jo_set & JO_TIMEOUT) + timeout = opt.jo_timeout; + else + timeout = channel_get_timeout(channel, part_read); + rettv->vval.v_string = channel_read_block(channel, part_read, + timeout, TRUE, NULL); + } + free_job_options(&opt); +} + +# define KEEP_OPEN_TIME 20 /* msec */ + +# if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO) +/* + * Add open channels to the poll struct. + * Return the adjusted struct index. + * The type of "fds" is hidden to avoid problems with the function proto. + */ + int +channel_poll_setup(int nfd_in, void *fds_in, int *towait) +{ + int nfd = nfd_in; + channel_T *channel; + struct pollfd *fds = fds_in; + ch_part_T part; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + { + for (part = PART_SOCK; part < PART_IN; ++part) + { + chanpart_T *ch_part = &channel->ch_part[part]; + + if (ch_part->ch_fd != INVALID_FD) + { + if (channel->ch_keep_open) + { + /* For unknown reason poll() returns immediately for a + * keep-open channel. Instead of adding it to the fds add + * a short timeout and check, like polling. */ + if (*towait < 0 || *towait > KEEP_OPEN_TIME) + *towait = KEEP_OPEN_TIME; + } + else + { + ch_part->ch_poll_idx = nfd; + fds[nfd].fd = ch_part->ch_fd; + fds[nfd].events = POLLIN; + nfd++; + } + } + else + channel->ch_part[part].ch_poll_idx = -1; + } + } + + nfd = channel_fill_poll_write(nfd, fds); + + return nfd; +} + +/* + * The type of "fds" is hidden to avoid problems with the function proto. + */ + int +channel_poll_check(int ret_in, void *fds_in) +{ + int ret = ret_in; + channel_T *channel; + struct pollfd *fds = fds_in; + ch_part_T part; + int idx; + chanpart_T *in_part; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + { + for (part = PART_SOCK; part < PART_IN; ++part) + { + idx = channel->ch_part[part].ch_poll_idx; + + if (ret > 0 && idx != -1 && (fds[idx].revents & POLLIN)) + { + channel_read(channel, part, "channel_poll_check"); + --ret; + } + else if (channel->ch_part[part].ch_fd != INVALID_FD + && channel->ch_keep_open) + { + /* polling a keep-open channel */ + channel_read(channel, part, "channel_poll_check_keep_open"); + } + } + + in_part = &channel->ch_part[PART_IN]; + idx = in_part->ch_poll_idx; + if (ret > 0 && idx != -1 && (fds[idx].revents & POLLOUT)) + { + channel_write_input(channel); + --ret; + } + } + + return ret; +} +# endif /* UNIX && !HAVE_SELECT */ + +# if (!defined(WIN32) && defined(HAVE_SELECT)) || defined(PROTO) + +/* + * The "fd_set" type is hidden to avoid problems with the function proto. + */ + int +channel_select_setup( + int maxfd_in, + void *rfds_in, + void *wfds_in, + struct timeval *tv, + struct timeval **tvp) +{ + int maxfd = maxfd_in; + channel_T *channel; + fd_set *rfds = rfds_in; + fd_set *wfds = wfds_in; + ch_part_T part; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + { + for (part = PART_SOCK; part < PART_IN; ++part) + { + sock_T fd = channel->ch_part[part].ch_fd; + + if (fd != INVALID_FD) + { + if (channel->ch_keep_open) + { + /* For unknown reason select() returns immediately for a + * keep-open channel. Instead of adding it to the rfds add + * a short timeout and check, like polling. */ + if (*tvp == NULL || tv->tv_sec > 0 + || tv->tv_usec > KEEP_OPEN_TIME * 1000) + { + *tvp = tv; + tv->tv_sec = 0; + tv->tv_usec = KEEP_OPEN_TIME * 1000; + } + } + else + { + FD_SET((int)fd, rfds); + if (maxfd < (int)fd) + maxfd = (int)fd; + } + } + } + } + + maxfd = channel_fill_wfds(maxfd, wfds); + + return maxfd; +} + +/* + * The "fd_set" type is hidden to avoid problems with the function proto. + */ + int +channel_select_check(int ret_in, void *rfds_in, void *wfds_in) +{ + int ret = ret_in; + channel_T *channel; + fd_set *rfds = rfds_in; + fd_set *wfds = wfds_in; + ch_part_T part; + chanpart_T *in_part; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + { + for (part = PART_SOCK; part < PART_IN; ++part) + { + sock_T fd = channel->ch_part[part].ch_fd; + + if (ret > 0 && fd != INVALID_FD && FD_ISSET(fd, rfds)) + { + channel_read(channel, part, "channel_select_check"); + FD_CLR(fd, rfds); + --ret; + } + else if (fd != INVALID_FD && channel->ch_keep_open) + { + /* polling a keep-open channel */ + channel_read(channel, part, "channel_select_check_keep_open"); + } + } + + in_part = &channel->ch_part[PART_IN]; + if (ret > 0 && in_part->ch_fd != INVALID_FD + && FD_ISSET(in_part->ch_fd, wfds)) + { + /* Clear the flag first, ch_fd may change in channel_write_input(). */ + FD_CLR(in_part->ch_fd, wfds); + channel_write_input(channel); + --ret; + } + } + + return ret; +} +# endif /* !WIN32 && HAVE_SELECT */ + +/* + * Execute queued up commands. + * Invoked from the main loop when it's safe to execute received commands. + * Return TRUE when something was done. + */ + int +channel_parse_messages(void) +{ + channel_T *channel = first_channel; + int ret = FALSE; + int r; + ch_part_T part = PART_SOCK; +#ifdef ELAPSED_FUNC + elapsed_T start_tv; + + ELAPSED_INIT(start_tv); +#endif + + ++safe_to_invoke_callback; + + /* Only do this message when another message was given, otherwise we get + * lots of them. */ + if (did_log_msg) + { + ch_log(NULL, "looking for messages on channels"); + did_log_msg = FALSE; + } + while (channel != NULL) + { + if (channel_can_close(channel)) + { + channel->ch_to_be_closed = (1U << PART_COUNT); + channel_close_now(channel); + /* channel may have been freed, start over */ + channel = first_channel; + continue; + } + if (channel->ch_to_be_freed || channel->ch_killing) + { + channel_free(channel); + /* channel has been freed, start over */ + channel = first_channel; + continue; + } + if (channel->ch_refcount == 0 && !channel_still_useful(channel)) + { + /* channel is no longer useful, free it */ + channel_free(channel); + channel = first_channel; + part = PART_SOCK; + continue; + } + if (channel->ch_part[part].ch_fd != INVALID_FD + || channel_has_readahead(channel, part)) + { + /* Increase the refcount, in case the handler causes the channel + * to be unreferenced or closed. */ + ++channel->ch_refcount; + r = may_invoke_callback(channel, part); + if (r == OK) + ret = TRUE; + if (channel_unref(channel) || (r == OK +#ifdef ELAPSED_FUNC + /* Limit the time we loop here to 100 msec, otherwise + * Vim becomes unresponsive when the callback takes + * more than a bit of time. */ + && ELAPSED_FUNC(start_tv) < 100L +#endif + )) + { + /* channel was freed or something was done, start over */ + channel = first_channel; + part = PART_SOCK; + continue; + } + } + if (part < PART_ERR) + ++part; + else + { + channel = channel->ch_next; + part = PART_SOCK; + } + } + + if (channel_need_redraw) + { + channel_need_redraw = FALSE; + redraw_after_callback(TRUE); + } + + --safe_to_invoke_callback; + + return ret; +} + +/* + * Return TRUE if any channel has readahead. That means we should not block on + * waiting for input. + */ + int +channel_any_readahead(void) +{ + channel_T *channel = first_channel; + ch_part_T part = PART_SOCK; + + while (channel != NULL) + { + if (channel_has_readahead(channel, part)) + return TRUE; + if (part < PART_ERR) + ++part; + else + { + channel = channel->ch_next; + part = PART_SOCK; + } + } + return FALSE; +} + +/* + * Mark references to lists used in channels. + */ + int +set_ref_in_channel(int copyID) +{ + int abort = FALSE; + channel_T *channel; + typval_T tv; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + if (channel_still_useful(channel)) + { + tv.v_type = VAR_CHANNEL; + tv.vval.v_channel = channel; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); + } + return abort; +} + +/* + * Return the "part" to write to for "channel". + */ + ch_part_T +channel_part_send(channel_T *channel) +{ + if (channel->CH_SOCK_FD == INVALID_FD) + return PART_IN; + return PART_SOCK; +} + +/* + * Return the default "part" to read from for "channel". + */ + ch_part_T +channel_part_read(channel_T *channel) +{ + if (channel->CH_SOCK_FD == INVALID_FD) + return PART_OUT; + return PART_SOCK; +} + +/* + * Return the mode of "channel"/"part" + * If "channel" is invalid returns MODE_JSON. + */ + ch_mode_T +channel_get_mode(channel_T *channel, ch_part_T part) +{ + if (channel == NULL) + return MODE_JSON; + return channel->ch_part[part].ch_mode; +} + +/* + * Return the timeout of "channel"/"part" + */ + int +channel_get_timeout(channel_T *channel, ch_part_T part) +{ + return channel->ch_part[part].ch_timeout; +} + + static int +handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo) +{ + char_u *val = tv_get_string(item); + + opt->jo_set |= jo; + if (STRCMP(val, "nl") == 0) + *modep = MODE_NL; + else if (STRCMP(val, "raw") == 0) + *modep = MODE_RAW; + else if (STRCMP(val, "js") == 0) + *modep = MODE_JS; + else if (STRCMP(val, "json") == 0) + *modep = MODE_JSON; + else + { + semsg(_(e_invarg2), val); + return FAIL; + } + return OK; +} + + static int +handle_io(typval_T *item, ch_part_T part, jobopt_T *opt) +{ + char_u *val = tv_get_string(item); + + opt->jo_set |= JO_OUT_IO << (part - PART_OUT); + if (STRCMP(val, "null") == 0) + opt->jo_io[part] = JIO_NULL; + else if (STRCMP(val, "pipe") == 0) + opt->jo_io[part] = JIO_PIPE; + else if (STRCMP(val, "file") == 0) + opt->jo_io[part] = JIO_FILE; + else if (STRCMP(val, "buffer") == 0) + opt->jo_io[part] = JIO_BUFFER; + else if (STRCMP(val, "out") == 0 && part == PART_ERR) + opt->jo_io[part] = JIO_OUT; + else + { + semsg(_(e_invarg2), val); + return FAIL; + } + return OK; +} + +/* + * Clear a jobopt_T before using it. + */ + void +clear_job_options(jobopt_T *opt) +{ + vim_memset(opt, 0, sizeof(jobopt_T)); +} + +/* + * Free any members of a jobopt_T. + */ + void +free_job_options(jobopt_T *opt) +{ + if (opt->jo_partial != NULL) + partial_unref(opt->jo_partial); + else if (opt->jo_callback != NULL) + func_unref(opt->jo_callback); + if (opt->jo_out_partial != NULL) + partial_unref(opt->jo_out_partial); + else if (opt->jo_out_cb != NULL) + func_unref(opt->jo_out_cb); + if (opt->jo_err_partial != NULL) + partial_unref(opt->jo_err_partial); + else if (opt->jo_err_cb != NULL) + func_unref(opt->jo_err_cb); + if (opt->jo_close_partial != NULL) + partial_unref(opt->jo_close_partial); + else if (opt->jo_close_cb != NULL) + func_unref(opt->jo_close_cb); + if (opt->jo_exit_partial != NULL) + partial_unref(opt->jo_exit_partial); + else if (opt->jo_exit_cb != NULL) + func_unref(opt->jo_exit_cb); + if (opt->jo_env != NULL) + dict_unref(opt->jo_env); +} + +/* + * Get the PART_ number from the first character of an option name. + */ + static int +part_from_char(int c) +{ + return c == 'i' ? PART_IN : c == 'o' ? PART_OUT: PART_ERR; +} + +/* + * Get the option entries from the dict in "tv", parse them and put the result + * in "opt". + * Only accept JO_ options in "supported" and JO2_ options in "supported2". + * If an option value is invalid return FAIL. + */ + int +get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) +{ + typval_T *item; + char_u *val; + dict_T *dict; + int todo; + hashitem_T *hi; + ch_part_T part; + + if (tv->v_type == VAR_UNKNOWN) + return OK; + if (tv->v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return FAIL; + } + dict = tv->vval.v_dict; + if (dict == NULL) + return OK; + + todo = (int)dict->dv_hashtab.ht_used; + for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi) + if (!HASHITEM_EMPTY(hi)) + { + item = &dict_lookup(hi)->di_tv; + + if (STRCMP(hi->hi_key, "mode") == 0) + { + if (!(supported & JO_MODE)) + break; + if (handle_mode(item, opt, &opt->jo_mode, JO_MODE) == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "in_mode") == 0) + { + if (!(supported & JO_IN_MODE)) + break; + if (handle_mode(item, opt, &opt->jo_in_mode, JO_IN_MODE) + == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "out_mode") == 0) + { + if (!(supported & JO_OUT_MODE)) + break; + if (handle_mode(item, opt, &opt->jo_out_mode, JO_OUT_MODE) + == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "err_mode") == 0) + { + if (!(supported & JO_ERR_MODE)) + break; + if (handle_mode(item, opt, &opt->jo_err_mode, JO_ERR_MODE) + == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "noblock") == 0) + { + if (!(supported & JO_MODE)) + break; + opt->jo_noblock = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "in_io") == 0 + || STRCMP(hi->hi_key, "out_io") == 0 + || STRCMP(hi->hi_key, "err_io") == 0) + { + if (!(supported & JO_OUT_IO)) + break; + if (handle_io(item, part_from_char(*hi->hi_key), opt) == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "in_name") == 0 + || STRCMP(hi->hi_key, "out_name") == 0 + || STRCMP(hi->hi_key, "err_name") == 0) + { + part = part_from_char(*hi->hi_key); + + if (!(supported & JO_OUT_IO)) + break; + opt->jo_set |= JO_OUT_NAME << (part - PART_OUT); + opt->jo_io_name[part] = + tv_get_string_buf_chk(item, opt->jo_io_name_buf[part]); + } + else if (STRCMP(hi->hi_key, "pty") == 0) + { + if (!(supported & JO_MODE)) + break; + opt->jo_pty = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "in_buf") == 0 + || STRCMP(hi->hi_key, "out_buf") == 0 + || STRCMP(hi->hi_key, "err_buf") == 0) + { + part = part_from_char(*hi->hi_key); + + if (!(supported & JO_OUT_IO)) + break; + opt->jo_set |= JO_OUT_BUF << (part - PART_OUT); + opt->jo_io_buf[part] = tv_get_number(item); + if (opt->jo_io_buf[part] <= 0) + { + semsg(_(e_invargNval), hi->hi_key, tv_get_string(item)); + return FAIL; + } + if (buflist_findnr(opt->jo_io_buf[part]) == NULL) + { + semsg(_(e_nobufnr), (long)opt->jo_io_buf[part]); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "out_modifiable") == 0 + || STRCMP(hi->hi_key, "err_modifiable") == 0) + { + part = part_from_char(*hi->hi_key); + + if (!(supported & JO_OUT_IO)) + break; + opt->jo_set |= JO_OUT_MODIFIABLE << (part - PART_OUT); + opt->jo_modifiable[part] = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "out_msg") == 0 + || STRCMP(hi->hi_key, "err_msg") == 0) + { + part = part_from_char(*hi->hi_key); + + if (!(supported & JO_OUT_IO)) + break; + opt->jo_set2 |= JO2_OUT_MSG << (part - PART_OUT); + opt->jo_message[part] = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "in_top") == 0 + || STRCMP(hi->hi_key, "in_bot") == 0) + { + linenr_T *lp; + + if (!(supported & JO_OUT_IO)) + break; + if (hi->hi_key[3] == 't') + { + lp = &opt->jo_in_top; + opt->jo_set |= JO_IN_TOP; + } + else + { + lp = &opt->jo_in_bot; + opt->jo_set |= JO_IN_BOT; + } + *lp = tv_get_number(item); + if (*lp < 0) + { + semsg(_(e_invargNval), hi->hi_key, tv_get_string(item)); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "channel") == 0) + { + if (!(supported & JO_OUT_IO)) + break; + opt->jo_set |= JO_CHANNEL; + if (item->v_type != VAR_CHANNEL) + { + semsg(_(e_invargval), "channel"); + return FAIL; + } + opt->jo_channel = item->vval.v_channel; + } + else if (STRCMP(hi->hi_key, "callback") == 0) + { + if (!(supported & JO_CALLBACK)) + break; + opt->jo_set |= JO_CALLBACK; + opt->jo_callback = get_callback(item, &opt->jo_partial); + if (opt->jo_callback == NULL) + { + semsg(_(e_invargval), "callback"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "out_cb") == 0) + { + if (!(supported & JO_OUT_CALLBACK)) + break; + opt->jo_set |= JO_OUT_CALLBACK; + opt->jo_out_cb = get_callback(item, &opt->jo_out_partial); + if (opt->jo_out_cb == NULL) + { + semsg(_(e_invargval), "out_cb"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "err_cb") == 0) + { + if (!(supported & JO_ERR_CALLBACK)) + break; + opt->jo_set |= JO_ERR_CALLBACK; + opt->jo_err_cb = get_callback(item, &opt->jo_err_partial); + if (opt->jo_err_cb == NULL) + { + semsg(_(e_invargval), "err_cb"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "close_cb") == 0) + { + if (!(supported & JO_CLOSE_CALLBACK)) + break; + opt->jo_set |= JO_CLOSE_CALLBACK; + opt->jo_close_cb = get_callback(item, &opt->jo_close_partial); + if (opt->jo_close_cb == NULL) + { + semsg(_(e_invargval), "close_cb"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "drop") == 0) + { + int never = FALSE; + val = tv_get_string(item); + + if (STRCMP(val, "never") == 0) + never = TRUE; + else if (STRCMP(val, "auto") != 0) + { + semsg(_(e_invargNval), "drop", val); + return FAIL; + } + opt->jo_drop_never = never; + } + else if (STRCMP(hi->hi_key, "exit_cb") == 0) + { + if (!(supported & JO_EXIT_CB)) + break; + opt->jo_set |= JO_EXIT_CB; + opt->jo_exit_cb = get_callback(item, &opt->jo_exit_partial); + if (opt->jo_exit_cb == NULL) + { + semsg(_(e_invargval), "exit_cb"); + return FAIL; + } + } +#ifdef FEAT_TERMINAL + else if (STRCMP(hi->hi_key, "term_name") == 0) + { + if (!(supported2 & JO2_TERM_NAME)) + break; + opt->jo_set2 |= JO2_TERM_NAME; + opt->jo_term_name = tv_get_string_chk(item); + if (opt->jo_term_name == NULL) + { + semsg(_(e_invargval), "term_name"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "term_finish") == 0) + { + if (!(supported2 & JO2_TERM_FINISH)) + break; + val = tv_get_string(item); + if (STRCMP(val, "open") != 0 && STRCMP(val, "close") != 0) + { + semsg(_(e_invargNval), "term_finish", val); + return FAIL; + } + opt->jo_set2 |= JO2_TERM_FINISH; + opt->jo_term_finish = *val; + } + else if (STRCMP(hi->hi_key, "term_opencmd") == 0) + { + char_u *p; + + if (!(supported2 & JO2_TERM_OPENCMD)) + break; + opt->jo_set2 |= JO2_TERM_OPENCMD; + p = opt->jo_term_opencmd = tv_get_string_chk(item); + if (p != NULL) + { + /* Must have %d and no other %. */ + p = vim_strchr(p, '%'); + if (p != NULL && (p[1] != 'd' + || vim_strchr(p + 2, '%') != NULL)) + p = NULL; + } + if (p == NULL) + { + semsg(_(e_invargval), "term_opencmd"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "eof_chars") == 0) + { + char_u *p; + + if (!(supported2 & JO2_EOF_CHARS)) + break; + opt->jo_set2 |= JO2_EOF_CHARS; + p = opt->jo_eof_chars = tv_get_string_chk(item); + if (p == NULL) + { + semsg(_(e_invargval), "eof_chars"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "term_rows") == 0) + { + if (!(supported2 & JO2_TERM_ROWS)) + break; + opt->jo_set2 |= JO2_TERM_ROWS; + opt->jo_term_rows = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "term_cols") == 0) + { + if (!(supported2 & JO2_TERM_COLS)) + break; + opt->jo_set2 |= JO2_TERM_COLS; + opt->jo_term_cols = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "vertical") == 0) + { + if (!(supported2 & JO2_VERTICAL)) + break; + opt->jo_set2 |= JO2_VERTICAL; + opt->jo_vertical = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "curwin") == 0) + { + if (!(supported2 & JO2_CURWIN)) + break; + opt->jo_set2 |= JO2_CURWIN; + opt->jo_curwin = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "hidden") == 0) + { + if (!(supported2 & JO2_HIDDEN)) + break; + opt->jo_set2 |= JO2_HIDDEN; + opt->jo_hidden = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "norestore") == 0) + { + if (!(supported2 & JO2_NORESTORE)) + break; + opt->jo_set2 |= JO2_NORESTORE; + opt->jo_term_norestore = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "term_kill") == 0) + { + if (!(supported2 & JO2_TERM_KILL)) + break; + opt->jo_set2 |= JO2_TERM_KILL; + opt->jo_term_kill = tv_get_string_chk(item); + } + else if (STRCMP(hi->hi_key, "term_mode") == 0) + { + char_u *p; + + if (!(supported2 & JO2_TERM_MODE)) + break; + opt->jo_set2 |= JO2_TERM_MODE; + p = tv_get_string_chk(item); + if (p == NULL) + { + semsg(_(e_invargval), "term_mode"); + return FAIL; + } + // Allow empty string, "winpty", "conpty". + if (!(*p == NUL || STRCMP(p, "winpty") == 0 + || STRCMP(p, "conpty") == 0)) + { + semsg(_(e_invargval), "term_mode"); + return FAIL; + } + opt->jo_term_mode = p[0]; + } +# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + else if (STRCMP(hi->hi_key, "ansi_colors") == 0) + { + int n = 0; + listitem_T *li; + long_u rgb[16]; + + if (!(supported2 & JO2_ANSI_COLORS)) + break; + + if (item == NULL || item->v_type != VAR_LIST + || item->vval.v_list == NULL) + { + semsg(_(e_invargval), "ansi_colors"); + return FAIL; + } + + li = item->vval.v_list->lv_first; + for (; li != NULL && n < 16; li = li->li_next, n++) + { + char_u *color_name; + guicolor_T guicolor; + + color_name = tv_get_string_chk(&li->li_tv); + if (color_name == NULL) + return FAIL; + + guicolor = GUI_GET_COLOR(color_name); + if (guicolor == INVALCOLOR) + return FAIL; + + rgb[n] = GUI_MCH_GET_RGB(guicolor); + } + + if (n != 16 || li != NULL) + { + semsg(_(e_invargval), "ansi_colors"); + return FAIL; + } + + opt->jo_set2 |= JO2_ANSI_COLORS; + memcpy(opt->jo_ansi_colors, rgb, sizeof(rgb)); + } +# endif +#endif + else if (STRCMP(hi->hi_key, "env") == 0) + { + if (!(supported2 & JO2_ENV)) + break; + if (item->v_type != VAR_DICT) + { + semsg(_(e_invargval), "env"); + return FAIL; + } + opt->jo_set2 |= JO2_ENV; + opt->jo_env = item->vval.v_dict; + if (opt->jo_env != NULL) + ++opt->jo_env->dv_refcount; + } + else if (STRCMP(hi->hi_key, "cwd") == 0) + { + if (!(supported2 & JO2_CWD)) + break; + opt->jo_cwd = tv_get_string_buf_chk(item, opt->jo_cwd_buf); + if (opt->jo_cwd == NULL || !mch_isdir(opt->jo_cwd) +#ifndef WIN32 // Win32 directories don't have the concept of "executable" + || mch_access((char *)opt->jo_cwd, X_OK) != 0 +#endif + ) + { + semsg(_(e_invargval), "cwd"); + return FAIL; + } + opt->jo_set2 |= JO2_CWD; + } + else if (STRCMP(hi->hi_key, "waittime") == 0) + { + if (!(supported & JO_WAITTIME)) + break; + opt->jo_set |= JO_WAITTIME; + opt->jo_waittime = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "timeout") == 0) + { + if (!(supported & JO_TIMEOUT)) + break; + opt->jo_set |= JO_TIMEOUT; + opt->jo_timeout = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "out_timeout") == 0) + { + if (!(supported & JO_OUT_TIMEOUT)) + break; + opt->jo_set |= JO_OUT_TIMEOUT; + opt->jo_out_timeout = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "err_timeout") == 0) + { + if (!(supported & JO_ERR_TIMEOUT)) + break; + opt->jo_set |= JO_ERR_TIMEOUT; + opt->jo_err_timeout = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "part") == 0) + { + if (!(supported & JO_PART)) + break; + opt->jo_set |= JO_PART; + val = tv_get_string(item); + if (STRCMP(val, "err") == 0) + opt->jo_part = PART_ERR; + else if (STRCMP(val, "out") == 0) + opt->jo_part = PART_OUT; + else + { + semsg(_(e_invargNval), "part", val); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "id") == 0) + { + if (!(supported & JO_ID)) + break; + opt->jo_set |= JO_ID; + opt->jo_id = tv_get_number(item); + } + else if (STRCMP(hi->hi_key, "stoponexit") == 0) + { + if (!(supported & JO_STOPONEXIT)) + break; + opt->jo_set |= JO_STOPONEXIT; + opt->jo_stoponexit = tv_get_string_buf_chk(item, + opt->jo_soe_buf); + if (opt->jo_stoponexit == NULL) + { + semsg(_(e_invargval), "stoponexit"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "block_write") == 0) + { + if (!(supported & JO_BLOCK_WRITE)) + break; + opt->jo_set |= JO_BLOCK_WRITE; + opt->jo_block_write = tv_get_number(item); + } + else + break; + --todo; + } + if (todo > 0) + { + semsg(_(e_invarg2), hi->hi_key); + return FAIL; + } + + return OK; +} + +/* + * Get the channel from the argument. + * Returns NULL if the handle is invalid. + * When "check_open" is TRUE check that the channel can be used. + * When "reading" is TRUE "check_open" considers typeahead useful. + * "part" is used to check typeahead, when PART_COUNT use the default part. + */ + channel_T * +get_channel_arg(typval_T *tv, int check_open, int reading, ch_part_T part) +{ + channel_T *channel = NULL; + int has_readahead = FALSE; + + if (tv->v_type == VAR_JOB) + { + if (tv->vval.v_job != NULL) + channel = tv->vval.v_job->jv_channel; + } + else if (tv->v_type == VAR_CHANNEL) + { + channel = tv->vval.v_channel; + } + else + { + semsg(_(e_invarg2), tv_get_string(tv)); + return NULL; + } + if (channel != NULL && reading) + has_readahead = channel_has_readahead(channel, + part != PART_COUNT ? part : channel_part_read(channel)); + + if (check_open && (channel == NULL || (!channel_is_open(channel) + && !(reading && has_readahead)))) + { + emsg(_("E906: not an open channel")); + return NULL; + } + return channel; +} + +static job_T *first_job = NULL; + + static void +job_free_contents(job_T *job) +{ + int i; + + ch_log(job->jv_channel, "Freeing job"); + if (job->jv_channel != NULL) + { + /* The link from the channel to the job doesn't count as a reference, + * thus don't decrement the refcount of the job. The reference from + * the job to the channel does count the reference, decrement it and + * NULL the reference. We don't set ch_job_killed, unreferencing the + * job doesn't mean it stops running. */ + job->jv_channel->ch_job = NULL; + channel_unref(job->jv_channel); + } + mch_clear_job(job); + + vim_free(job->jv_tty_in); + vim_free(job->jv_tty_out); + vim_free(job->jv_stoponexit); +#ifdef UNIX + vim_free(job->jv_termsig); +#endif + free_callback(job->jv_exit_cb, job->jv_exit_partial); + if (job->jv_argv != NULL) + { + for (i = 0; job->jv_argv[i] != NULL; i++) + vim_free(job->jv_argv[i]); + vim_free(job->jv_argv); + } +} + +/* + * Remove "job" from the list of jobs. + */ + static void +job_unlink(job_T *job) +{ + if (job->jv_next != NULL) + job->jv_next->jv_prev = job->jv_prev; + if (job->jv_prev == NULL) + first_job = job->jv_next; + else + job->jv_prev->jv_next = job->jv_next; +} + + static void +job_free_job(job_T *job) +{ + job_unlink(job); + vim_free(job); +} + + static void +job_free(job_T *job) +{ + if (!in_free_unref_items) + { + job_free_contents(job); + job_free_job(job); + } +} + +job_T *jobs_to_free = NULL; + +/* + * Put "job" in a list to be freed later, when it's no longer referenced. + */ + static void +job_free_later(job_T *job) +{ + job_unlink(job); + job->jv_next = jobs_to_free; + jobs_to_free = job; +} + + static void +free_jobs_to_free_later(void) +{ + job_T *job; + + while (jobs_to_free != NULL) + { + job = jobs_to_free; + jobs_to_free = job->jv_next; + job_free_contents(job); + vim_free(job); + } +} + +#if defined(EXITFREE) || defined(PROTO) + void +job_free_all(void) +{ + while (first_job != NULL) + job_free(first_job); + free_jobs_to_free_later(); + +# ifdef FEAT_TERMINAL + free_unused_terminals(); +# endif +} +#endif + +/* + * Return TRUE if we need to check if the process of "job" has ended. + */ + static int +job_need_end_check(job_T *job) +{ + return job->jv_status == JOB_STARTED + && (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL); +} + +/* + * Return TRUE if the channel of "job" is still useful. + */ + static int +job_channel_still_useful(job_T *job) +{ + return job->jv_channel != NULL && channel_still_useful(job->jv_channel); +} + +/* + * Return TRUE if the channel of "job" is closeable. + */ + static int +job_channel_can_close(job_T *job) +{ + return job->jv_channel != NULL && channel_can_close(job->jv_channel); +} + +/* + * Return TRUE if the job should not be freed yet. Do not free the job when + * it has not ended yet and there is a "stoponexit" flag, an exit callback + * or when the associated channel will do something with the job output. + */ + static int +job_still_useful(job_T *job) +{ + return job_need_end_check(job) || job_channel_still_useful(job); +} + +#if defined(GUI_MAY_FORK) || defined(PROTO) +/* + * Return TRUE when there is any running job that we care about. + */ + int +job_any_running() +{ + job_T *job; + + for (job = first_job; job != NULL; job = job->jv_next) + if (job_still_useful(job)) + { + ch_log(NULL, "GUI not forking because a job is running"); + return TRUE; + } + return FALSE; +} +#endif + +#if !defined(USE_ARGV) || defined(PROTO) +/* + * Escape one argument for an external command. + * Returns the escaped string in allocated memory. NULL when out of memory. + */ + static char_u * +win32_escape_arg(char_u *arg) +{ + int slen, dlen; + int escaping = 0; + int i; + char_u *s, *d; + char_u *escaped_arg; + int has_spaces = FALSE; + + /* First count the number of extra bytes required. */ + slen = (int)STRLEN(arg); + dlen = slen; + for (s = arg; *s != NUL; MB_PTR_ADV(s)) + { + if (*s == '"' || *s == '\\') + ++dlen; + if (*s == ' ' || *s == '\t') + has_spaces = TRUE; + } + + if (has_spaces) + dlen += 2; + + if (dlen == slen) + return vim_strsave(arg); + + /* Allocate memory for the result and fill it. */ + escaped_arg = alloc(dlen + 1); + if (escaped_arg == NULL) + return NULL; + memset(escaped_arg, 0, dlen+1); + + d = escaped_arg; + + if (has_spaces) + *d++ = '"'; + + for (s = arg; *s != NUL;) + { + switch (*s) + { + case '"': + for (i = 0; i < escaping; i++) + *d++ = '\\'; + escaping = 0; + *d++ = '\\'; + *d++ = *s++; + break; + case '\\': + escaping++; + *d++ = *s++; + break; + default: + escaping = 0; + MB_COPY_CHAR(s, d); + break; + } + } + + /* add terminating quote and finish with a NUL */ + if (has_spaces) + { + for (i = 0; i < escaping; i++) + *d++ = '\\'; + *d++ = '"'; + } + *d = NUL; + + return escaped_arg; +} + +/* + * Build a command line from a list, taking care of escaping. + * The result is put in gap->ga_data. + * Returns FAIL when out of memory. + */ + int +win32_build_cmd(list_T *l, garray_T *gap) +{ + listitem_T *li; + char_u *s; + + for (li = l->lv_first; li != NULL; li = li->li_next) + { + s = tv_get_string_chk(&li->li_tv); + if (s == NULL) + return FAIL; + s = win32_escape_arg(s); + if (s == NULL) + return FAIL; + ga_concat(gap, s); + vim_free(s); + if (li->li_next != NULL) + ga_append(gap, ' '); + } + return OK; +} +#endif + +/* + * NOTE: Must call job_cleanup() only once right after the status of "job" + * changed to JOB_ENDED (i.e. after job_status() returned "dead" first or + * mch_detect_ended_job() returned non-NULL). + * If the job is no longer used it will be removed from the list of jobs, and + * deleted a bit later. + */ + void +job_cleanup(job_T *job) +{ + if (job->jv_status != JOB_ENDED) + return; + + /* Ready to cleanup the job. */ + job->jv_status = JOB_FINISHED; + + /* When only channel-in is kept open, close explicitly. */ + if (job->jv_channel != NULL) + ch_close_part(job->jv_channel, PART_IN); + + if (job->jv_exit_cb != NULL) + { + typval_T argv[3]; + typval_T rettv; + int dummy; + + /* Invoke the exit callback. Make sure the refcount is > 0. */ + ch_log(job->jv_channel, "Invoking exit callback %s", job->jv_exit_cb); + ++job->jv_refcount; + argv[0].v_type = VAR_JOB; + argv[0].vval.v_job = job; + argv[1].v_type = VAR_NUMBER; + argv[1].vval.v_number = job->jv_exitval; + call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb), + &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, + job->jv_exit_partial, NULL); + clear_tv(&rettv); + --job->jv_refcount; + channel_need_redraw = TRUE; + } + + if (job->jv_channel != NULL + && job->jv_channel->ch_anonymous_pipe && !job->jv_channel->ch_killing) + { + ++safe_to_invoke_callback; + channel_free_contents(job->jv_channel); + job->jv_channel->ch_job = NULL; + job->jv_channel = NULL; + --safe_to_invoke_callback; + } + + // Do not free the job in case the close callback of the associated channel + // isn't invoked yet and may get information by job_info(). + if (job->jv_refcount == 0 && !job_channel_still_useful(job)) + // The job was already unreferenced and the associated channel was + // detached, now that it ended it can be freed. However, a caller might + // still use it, thus free it a bit later. + job_free_later(job); +} + +/* + * Mark references in jobs that are still useful. + */ + int +set_ref_in_job(int copyID) +{ + int abort = FALSE; + job_T *job; + typval_T tv; + + for (job = first_job; job != NULL; job = job->jv_next) + if (job_still_useful(job)) + { + tv.v_type = VAR_JOB; + tv.vval.v_job = job; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); + } + return abort; +} + +/* + * Dereference "job". Note that after this "job" may have been freed. + */ + void +job_unref(job_T *job) +{ + if (job != NULL && --job->jv_refcount <= 0) + { + /* Do not free the job if there is a channel where the close callback + * may get the job info. */ + if (!job_channel_still_useful(job)) + { + /* Do not free the job when it has not ended yet and there is a + * "stoponexit" flag or an exit callback. */ + if (!job_need_end_check(job)) + { + job_free(job); + } + else if (job->jv_channel != NULL) + { + /* Do remove the link to the channel, otherwise it hangs + * around until Vim exits. See job_free() for refcount. */ + ch_log(job->jv_channel, "detaching channel from job"); + job->jv_channel->ch_job = NULL; + channel_unref(job->jv_channel); + job->jv_channel = NULL; + } + } + } +} + + int +free_unused_jobs_contents(int copyID, int mask) +{ + int did_free = FALSE; + job_T *job; + + for (job = first_job; job != NULL; job = job->jv_next) + if ((job->jv_copyID & mask) != (copyID & mask) + && !job_still_useful(job)) + { + /* Free the channel and ordinary items it contains, but don't + * recurse into Lists, Dictionaries etc. */ + job_free_contents(job); + did_free = TRUE; + } + return did_free; +} + + void +free_unused_jobs(int copyID, int mask) +{ + job_T *job; + job_T *job_next; + + for (job = first_job; job != NULL; job = job_next) + { + job_next = job->jv_next; + if ((job->jv_copyID & mask) != (copyID & mask) + && !job_still_useful(job)) + { + /* Free the job struct itself. */ + job_free_job(job); + } + } +} + +/* + * Allocate a job. Sets the refcount to one and sets options default. + */ + job_T * +job_alloc(void) +{ + job_T *job; + + job = (job_T *)alloc_clear(sizeof(job_T)); + if (job != NULL) + { + job->jv_refcount = 1; + job->jv_stoponexit = vim_strsave((char_u *)"term"); + + if (first_job != NULL) + { + first_job->jv_prev = job; + job->jv_next = first_job; + } + first_job = job; + } + return job; +} + + void +job_set_options(job_T *job, jobopt_T *opt) +{ + if (opt->jo_set & JO_STOPONEXIT) + { + vim_free(job->jv_stoponexit); + if (opt->jo_stoponexit == NULL || *opt->jo_stoponexit == NUL) + job->jv_stoponexit = NULL; + else + job->jv_stoponexit = vim_strsave(opt->jo_stoponexit); + } + if (opt->jo_set & JO_EXIT_CB) + { + free_callback(job->jv_exit_cb, job->jv_exit_partial); + if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL) + { + job->jv_exit_cb = NULL; + job->jv_exit_partial = NULL; + } + else + { + job->jv_exit_partial = opt->jo_exit_partial; + if (job->jv_exit_partial != NULL) + { + job->jv_exit_cb = opt->jo_exit_cb; + ++job->jv_exit_partial->pt_refcount; + } + else + { + job->jv_exit_cb = vim_strsave(opt->jo_exit_cb); + func_ref(job->jv_exit_cb); + } + } + } +} + +/* + * Called when Vim is exiting: kill all jobs that have the "stoponexit" flag. + */ + void +job_stop_on_exit(void) +{ + job_T *job; + + for (job = first_job; job != NULL; job = job->jv_next) + if (job->jv_status == JOB_STARTED && job->jv_stoponexit != NULL) + mch_signal_job(job, job->jv_stoponexit); +} + +/* + * Return TRUE when there is any job that has an exit callback and might exit, + * which means job_check_ended() should be called more often. + */ + int +has_pending_job(void) +{ + job_T *job; + + for (job = first_job; job != NULL; job = job->jv_next) + /* Only should check if the channel has been closed, if the channel is + * open the job won't exit. */ + if ((job->jv_status == JOB_STARTED && !job_channel_still_useful(job)) + || (job->jv_status == JOB_FINISHED + && job_channel_can_close(job))) + return TRUE; + return FALSE; +} + +#define MAX_CHECK_ENDED 8 + +/* + * Called once in a while: check if any jobs that seem useful have ended. + * Returns TRUE if a job did end. + */ + int +job_check_ended(void) +{ + int i; + int did_end = FALSE; + + // be quick if there are no jobs to check + if (first_job == NULL) + return did_end; + + for (i = 0; i < MAX_CHECK_ENDED; ++i) + { + // NOTE: mch_detect_ended_job() must only return a job of which the + // status was just set to JOB_ENDED. + job_T *job = mch_detect_ended_job(first_job); + + if (job == NULL) + break; + did_end = TRUE; + job_cleanup(job); // may add "job" to jobs_to_free + } + + // Actually free jobs that were cleaned up. + free_jobs_to_free_later(); + + if (channel_need_redraw) + { + channel_need_redraw = FALSE; + redraw_after_callback(TRUE); + } + return did_end; +} + +/* + * Create a job and return it. Implements job_start(). + * "argv_arg" is only for Unix. + * When "argv_arg" is NULL then "argvars" is used. + * The returned job has a refcount of one. + * Returns NULL when out of memory. + */ + job_T * +job_start( + typval_T *argvars, + char **argv_arg, + jobopt_T *opt_arg, + int is_terminal UNUSED) +{ + job_T *job; + char_u *cmd = NULL; + char **argv = NULL; + int argc = 0; +#if defined(UNIX) +# define USE_ARGV + int i; +#else + garray_T ga; +#endif + jobopt_T opt; + ch_part_T part; + + job = job_alloc(); + if (job == NULL) + return NULL; + + job->jv_status = JOB_FAILED; +#ifndef USE_ARGV + ga_init2(&ga, (int)sizeof(char*), 20); +#endif + + if (opt_arg != NULL) + opt = *opt_arg; + else + { + /* Default mode is NL. */ + clear_job_options(&opt); + opt.jo_mode = MODE_NL; + if (get_job_options(&argvars[1], &opt, + JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL + JO_STOPONEXIT + + JO_EXIT_CB + JO_OUT_IO + JO_BLOCK_WRITE, + JO2_ENV + JO2_CWD) == FAIL) + goto theend; + } + + /* Check that when io is "file" that there is a file name. */ + for (part = PART_OUT; part < PART_COUNT; ++part) + if ((opt.jo_set & (JO_OUT_IO << (part - PART_OUT))) + && opt.jo_io[part] == JIO_FILE + && (!(opt.jo_set & (JO_OUT_NAME << (part - PART_OUT))) + || *opt.jo_io_name[part] == NUL)) + { + emsg(_("E920: _io file requires _name to be set")); + goto theend; + } + + if ((opt.jo_set & JO_IN_IO) && opt.jo_io[PART_IN] == JIO_BUFFER) + { + buf_T *buf = NULL; + + /* check that we can find the buffer before starting the job */ + if (opt.jo_set & JO_IN_BUF) + { + buf = buflist_findnr(opt.jo_io_buf[PART_IN]); + if (buf == NULL) + semsg(_(e_nobufnr), (long)opt.jo_io_buf[PART_IN]); + } + else if (!(opt.jo_set & JO_IN_NAME)) + { + emsg(_("E915: in_io buffer requires in_buf or in_name to be set")); + } + else + buf = buflist_find_by_name(opt.jo_io_name[PART_IN], FALSE); + if (buf == NULL) + goto theend; + if (buf->b_ml.ml_mfp == NULL) + { + char_u numbuf[NUMBUFLEN]; + char_u *s; + + if (opt.jo_set & JO_IN_BUF) + { + sprintf((char *)numbuf, "%d", opt.jo_io_buf[PART_IN]); + s = numbuf; + } + else + s = opt.jo_io_name[PART_IN]; + semsg(_("E918: buffer must be loaded: %s"), s); + goto theend; + } + job->jv_in_buf = buf; + } + + job_set_options(job, &opt); + +#ifdef USE_ARGV + if (argv_arg != NULL) + { + /* Make a copy of argv_arg for job->jv_argv. */ + for (i = 0; argv_arg[i] != NULL; i++) + argc++; + argv = (char **)alloc(sizeof(char *) * (argc + 1)); + if (argv == NULL) + goto theend; + for (i = 0; i < argc; i++) + argv[i] = (char *)vim_strsave((char_u *)argv_arg[i]); + argv[argc] = NULL; + } + else +#endif + if (argvars[0].v_type == VAR_STRING) + { + /* Command is a string. */ + cmd = argvars[0].vval.v_string; + if (cmd == NULL || *cmd == NUL) + { + emsg(_(e_invarg)); + goto theend; + } + + if (build_argv_from_string(cmd, &argv, &argc) == FAIL) + goto theend; + } + else if (argvars[0].v_type != VAR_LIST + || argvars[0].vval.v_list == NULL + || argvars[0].vval.v_list->lv_len < 1) + { + emsg(_(e_invarg)); + goto theend; + } + else + { + list_T *l = argvars[0].vval.v_list; + + if (build_argv_from_list(l, &argv, &argc) == FAIL) + goto theend; +#ifndef USE_ARGV + if (win32_build_cmd(l, &ga) == FAIL) + goto theend; + cmd = ga.ga_data; +#endif + } + + /* Save the command used to start the job. */ + job->jv_argv = argv; + +#ifdef USE_ARGV + if (ch_log_active()) + { + garray_T ga; + + ga_init2(&ga, (int)sizeof(char), 200); + for (i = 0; i < argc; ++i) + { + if (i > 0) + ga_concat(&ga, (char_u *)" "); + ga_concat(&ga, (char_u *)argv[i]); + } + ch_log(NULL, "Starting job: %s", (char *)ga.ga_data); + ga_clear(&ga); + } + mch_job_start(argv, job, &opt, is_terminal); +#else + ch_log(NULL, "Starting job: %s", (char *)cmd); + mch_job_start((char *)cmd, job, &opt); +#endif + + /* If the channel is reading from a buffer, write lines now. */ + if (job->jv_channel != NULL) + channel_write_in(job->jv_channel); + +theend: +#ifndef USE_ARGV + vim_free(ga.ga_data); +#endif + if (argv != job->jv_argv) + vim_free(argv); + free_job_options(&opt); + return job; +} + +/* + * Get the status of "job" and invoke the exit callback when needed. + * The returned string is not allocated. + */ + char * +job_status(job_T *job) +{ + char *result; + + if (job->jv_status >= JOB_ENDED) + /* No need to check, dead is dead. */ + result = "dead"; + else if (job->jv_status == JOB_FAILED) + result = "fail"; + else + { + result = mch_job_status(job); + if (job->jv_status == JOB_ENDED) + job_cleanup(job); + } + return result; +} + +/* + * Implementation of job_info(). + */ + void +job_info(job_T *job, dict_T *dict) +{ + dictitem_T *item; + varnumber_T nr; + list_T *l; + int i; + + dict_add_string(dict, "status", (char_u *)job_status(job)); + + item = dictitem_alloc((char_u *)"channel"); + if (item == NULL) + return; + item->di_tv.v_type = VAR_CHANNEL; + item->di_tv.vval.v_channel = job->jv_channel; + if (job->jv_channel != NULL) + ++job->jv_channel->ch_refcount; + if (dict_add(dict, item) == FAIL) + dictitem_free(item); + +#ifdef UNIX + nr = job->jv_pid; +#else + nr = job->jv_proc_info.dwProcessId; +#endif + dict_add_number(dict, "process", nr); + dict_add_string(dict, "tty_in", job->jv_tty_in); + dict_add_string(dict, "tty_out", job->jv_tty_out); + + dict_add_number(dict, "exitval", job->jv_exitval); + dict_add_string(dict, "exit_cb", job->jv_exit_cb); + dict_add_string(dict, "stoponexit", job->jv_stoponexit); +#ifdef UNIX + dict_add_string(dict, "termsig", job->jv_termsig); +#endif + + l = list_alloc(); + if (l != NULL) + { + dict_add_list(dict, "cmd", l); + if (job->jv_argv != NULL) + for (i = 0; job->jv_argv[i] != NULL; i++) + list_append_string(l, (char_u *)job->jv_argv[i], -1); + } +} + +/* + * Implementation of job_info() to return info for all jobs. + */ + void +job_info_all(list_T *l) +{ + job_T *job; + typval_T tv; + + for (job = first_job; job != NULL; job = job->jv_next) + { + tv.v_type = VAR_JOB; + tv.vval.v_job = job; + + if (list_append_tv(l, &tv) != OK) + return; + } +} + +/* + * Send a signal to "job". Implements job_stop(). + * When "type" is not NULL use this for the type. + * Otherwise use argvars[1] for the type. + */ + int +job_stop(job_T *job, typval_T *argvars, char *type) +{ + char_u *arg; + + if (type != NULL) + arg = (char_u *)type; + else if (argvars[1].v_type == VAR_UNKNOWN) + arg = (char_u *)""; + else + { + arg = tv_get_string_chk(&argvars[1]); + if (arg == NULL) + { + emsg(_(e_invarg)); + return 0; + } + } + if (job->jv_status == JOB_FAILED) + { + ch_log(job->jv_channel, "Job failed to start, job_stop() skipped"); + return 0; + } + if (job->jv_status == JOB_ENDED) + { + ch_log(job->jv_channel, "Job has already ended, job_stop() skipped"); + return 0; + } + ch_log(job->jv_channel, "Stopping job with '%s'", (char *)arg); + if (mch_signal_job(job, arg) == FAIL) + return 0; + + /* Assume that only "kill" will kill the job. */ + if (job->jv_channel != NULL && STRCMP(arg, "kill") == 0) + job->jv_channel->ch_job_killed = TRUE; + + /* We don't try freeing the job, obviously the caller still has a + * reference to it. */ + return 1; +} + + void +invoke_prompt_callback(void) +{ + typval_T rettv; + int dummy; + typval_T argv[2]; + char_u *text; + char_u *prompt; + linenr_T lnum = curbuf->b_ml.ml_line_count; + + // Add a new line for the prompt before invoking the callback, so that + // text can always be inserted above the last line. + ml_append(lnum, (char_u *)"", 0, FALSE); + curwin->w_cursor.lnum = lnum + 1; + curwin->w_cursor.col = 0; + + if (curbuf->b_prompt_callback == NULL || *curbuf->b_prompt_callback == NUL) + return; + text = ml_get(lnum); + prompt = prompt_text(); + if (STRLEN(text) >= STRLEN(prompt)) + text += STRLEN(prompt); + argv[0].v_type = VAR_STRING; + argv[0].vval.v_string = vim_strsave(text); + argv[1].v_type = VAR_UNKNOWN; + + call_func(curbuf->b_prompt_callback, + (int)STRLEN(curbuf->b_prompt_callback), + &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, + curbuf->b_prompt_partial, NULL); + clear_tv(&argv[0]); + clear_tv(&rettv); +} + +/* + * Return TRUE when the interrupt callback was invoked. + */ + int +invoke_prompt_interrupt(void) +{ + typval_T rettv; + int dummy; + typval_T argv[1]; + + if (curbuf->b_prompt_interrupt == NULL + || *curbuf->b_prompt_interrupt == NUL) + return FALSE; + argv[0].v_type = VAR_UNKNOWN; + + got_int = FALSE; // don't skip executing commands + call_func(curbuf->b_prompt_interrupt, + (int)STRLEN(curbuf->b_prompt_interrupt), + &rettv, 0, argv, NULL, 0L, 0L, &dummy, TRUE, + curbuf->b_prompt_int_partial, NULL); + clear_tv(&rettv); + return TRUE; +} + +#endif /* FEAT_JOB_CHANNEL */ diff --git a/src/charset.c b/src/charset.c new file mode 100644 index 0000000..5b091ec --- /dev/null +++ b/src/charset.c @@ -0,0 +1,2093 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +#include "vim.h" + +#if defined(HAVE_WCHAR_H) +# include /* for towupper() and towlower() */ +#endif +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))) + +/* table used below, see init_chartab() for an explanation */ +static char_u g_chartab[256]; + +/* + * Flags for g_chartab[]. + */ +#define CT_CELL_MASK 0x07 /* mask: nr of display cells (1, 2 or 4) */ +#define CT_PRINT_CHAR 0x10 /* flag: set for printable chars */ +#define CT_ID_CHAR 0x20 /* flag: set for ID chars */ +#define CT_FNAME_CHAR 0x40 /* flag: set for file name chars */ + +/* + * Fill g_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 g_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 g_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); +} + + int +buf_init_chartab( + buf_T *buf, + int global) /* FALSE: only set buf->b_chartab[] */ +{ + 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 < ' ') + g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2; +#ifdef EBCDIC + while (c < 255) +#else + while (c <= '~') +#endif + g_chartab[c++] = 1 + CT_PRINT_CHAR; +#ifdef FEAT_FKMAP + if (p_altkeymap) + { + while (c < YE) + g_chartab[c++] = 1 + CT_PRINT_CHAR; + } +#endif + while (c < 256) + { + /* UTF-8: bytes 0xa0 - 0xff are printable (latin1) */ + if (enc_utf8 && c >= 0xa0) + g_chartab[c++] = CT_PRINT_CHAR + 1; + /* euc-jp characters starting with 0x8e are single width */ + else if (enc_dbcs == DBCS_JPNU && c == 0x8e) + g_chartab[c++] = CT_PRINT_CHAR + 1; + /* other double-byte chars can be printable AND double-width */ + else if (enc_dbcs != 0 && MB_BYTE2LEN(c) == 2) + g_chartab[c++] = CT_PRINT_CHAR + 2; + else + /* the rest is unprintable by default */ + g_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)) + g_chartab[c] |= CT_FNAME_CHAR; + } + + /* + * Init word char flags all to FALSE + */ + vim_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); + } + +#ifdef FEAT_LISP + /* + * In lisp mode the '-' character is included in keywords. + */ + if (buf->b_p_lisp) + SET_CHARTAB(buf, '-'); +#endif + + /* 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) + p = p_isi; /* first round: 'isident' */ + else if (i == 1) + p = p_isp; /* second round: 'isprint' */ + else if (i == 2) + p = p_isf; /* third round: 'isfname' */ + else /* i == 3 */ + p = buf->b_p_isk; /* fourth round: 'iskeyword' */ + + 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 || MB_ISLOWER(c) || MB_ISUPPER(c) +#ifdef FEAT_FKMAP + || (p_altkeymap && (F_isalpha(c) || F_isdigit(c))) +#endif + ) + { + if (i == 0) /* (re)set ID flag */ + { + if (tilde) + g_chartab[c] &= ~CT_ID_CHAR; + else + g_chartab[c] |= CT_ID_CHAR; + } + else if (i == 1) /* (re)set printable */ + { + if ((c < ' ' +#ifndef EBCDIC + || c > '~' +#endif +#ifdef FEAT_FKMAP + || (p_altkeymap + && (F_isalpha(c) || F_isdigit(c))) +#endif + // For double-byte we keep the cell width, so + // that we can detect it from the first byte. + ) && !(enc_dbcs && MB_BYTE2LEN(c) == 2)) + { + if (tilde) + { + g_chartab[c] = (g_chartab[c] & ~CT_CELL_MASK) + + ((dy_flags & DY_UHEX) ? 4 : 2); + g_chartab[c] &= ~CT_PRINT_CHAR; + } + else + { + g_chartab[c] = (g_chartab[c] & ~CT_CELL_MASK) + 1; + g_chartab[c] |= CT_PRINT_CHAR; + } + } + } + else if (i == 2) /* (re)set fname flag */ + { + if (tilde) + g_chartab[c] &= ~CT_FNAME_CHAR; + else + g_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. + */ + 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; + mch_memmove(buf + trs_len, buf + 1, (size_t)len); + } + mch_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. + */ + char_u * +transstr(char_u *s) +{ + char_u *res; + char_u *p; + int l, len, c; + char_u hexbuf[11]; + + if (has_mbyte) + { + /* Compute the length of the result, taking account of unprintable + * multi-byte characters. */ + 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 += (int)STRLEN(hexbuf); + } + } + else + { + l = byte2cells(*p++); + if (l > 0) + len += l; + else + len += 4; /* illegal byte sequence */ + } + } + res = alloc((unsigned)(len + 1)); + } + else + res = alloc((unsigned)(vim_strsize(s) + 1)); + if (res != NULL) + { + *res = NUL; + p = s; + while (*p != NUL) + { + if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) + { + c = (*mb_ptr2char)(p); + if (vim_isprintc(c)) + STRNCAT(res, p, l); /* append printable multi-byte char */ + else + transchar_hex(res + STRLEN(res), c); + p += l; + } + else + STRCAT(res, transchar_byte(*p++)); + } + } + return res; +} +#endif + +#if defined(FEAT_SYN_HL) || 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]". + */ + 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_init2(&ga, 1, 10); + if (ga_grow(&ga, len + 1) == FAIL) + return NULL; + mch_memmove(ga.ga_data, str, (size_t)len); + ga.ga_len = len; + } + else + { + if (len >= buflen) /* Ugly! */ + len = buflen - 1; + mch_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) == FAIL + : 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; +} +#endif + +/* + * Catch 22: g_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 g_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]; + + char_u * +transchar(int c) +{ + int i; + + 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 && ( +#ifdef EBCDIC + (c >= 64 && c < 255) +#else + (c >= ' ' && c <= '~') +#endif +#ifdef FEAT_FKMAP + || (p_altkeymap && F_ischar(c)) +#endif + )) || (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. + */ + 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. + */ + void +transchar_nonprint(char_u *buf, int c) +{ + if (c == NL) + c = NUL; /* we use newline in place of a NUL */ + else if (c == CAR && get_fileformat(curbuf) == EOL_MAC) + c = NL; /* we use CR in place of NL in this case */ + + if (dy_flags & DY_UHEX) /* 'display' has "uhex" */ + transchar_hex(buf, c); + +#ifdef EBCDIC + /* For EBCDIC only the characters 0-63 and 255 are not printable */ + else if (CtrlChar(c) != 0 || c == DEL) +#else + else if (c <= 0x7f) /* 0x00 - 0x1f and 0x7f */ +#endif + { + buf[0] = '^'; +#ifdef EBCDIC + if (c == DEL) + buf[1] = '?'; /* DEL displayed as ^? */ + else + buf[1] = CtrlChar(c); +#else + buf[1] = c ^ 0x40; /* DEL displayed as ^? */ +#endif + + buf[2] = NUL; + } + else if (enc_utf8 && c >= 0x80) + { + transchar_hex(buf, c); + } +#ifndef EBCDIC + else if (c >= ' ' + 0x80 && c <= '~' + 0x80) /* 0xa0 - 0xfe */ + { + buf[0] = '|'; + buf[1] = c - 0x80; + buf[2] = NUL; + } +#else + else if (c < 64) + { + buf[0] = '~'; + buf[1] = MetaChar(c); + buf[2] = NUL; + } +#endif + 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] = '~'; +#ifdef EBCDIC + buf[1] = '?'; /* 0xff displayed as ~? */ +#else + buf[1] = (c - 0x80) ^ 0x40; /* 0xff displayed as ~? */ +#endif + buf[2] = NUL; + } +} + + 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. + */ + 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. + */ + int +byte2cells(int b) +{ + if (enc_utf8 && b >= 0x80) + return 0; + return (g_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>". + */ + 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 (g_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>". + */ + 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 (g_chartab[*p] & CT_CELL_MASK); +} + +/* + * Return the number of character cells string "s" will take on the screen, + * counting TABs as two characters: "^I". + */ + 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". + */ + int +vim_strnsize(char_u *s, int len) +{ + 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. + */ + +#ifdef FEAT_VARTABS +# define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \ + if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \ + { \ + return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_array); \ + } \ + else \ + return ptr2cells(p); +#else +# 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); +#endif + + int +chartabsize(char_u *p, colnr_T col) +{ + RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, p, col) +} + +#ifdef FEAT_LINEBREAK + static int +win_chartabsize(win_T *wp, char_u *p, colnr_T col) +{ + RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, p, col) +} +#endif + +/* + * Return the number of characters the string 's' will take on the screen, + * taking into account the size of a tab. + */ + int +linetabsize(char_u *s) +{ + return linetabsize_col(0, s); +} + +/* + * Like linetabsize(), but starting at column "startcol". + */ + int +linetabsize_col(int startcol, char_u *s) +{ + colnr_T col = startcol; + char_u *line = s; /* pointer to start of line, for breakindent */ + + while (*s != NUL) + col += lbr_chartabsize_adv(line, &s, col); + return (int)col; +} + +/* + * Like linetabsize(), but for a given window instead of the current one. + */ + int +win_linetabsize(win_T *wp, char_u *line, colnr_T len) +{ + colnr_T col = 0; + char_u *s; + + for (s = line; *s != NUL && (len == MAXCOL || s < line + len); + MB_PTR_ADV(s)) + col += win_lbr_chartabsize(wp, line, s, col, NULL); + return (int)col; +} + +/* + * Return TRUE if 'c' is a normal identifier character: + * Letters and characters from the 'isident' option. + */ + int +vim_isIDc(int c) +{ + return (c > 0 && c < 0x100 && (g_chartab[c] & CT_ID_CHAR)); +} + +/* + * return TRUE if 'c' is a keyword character: Letters and characters from + * 'iskeyword' option for the current buffer. + * For multi-byte characters mb_get_class() is used (builtin rules). + */ + 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_buf(c, buf) >= 2; + return FALSE; + } + return (c > 0 && GET_CHARTAB(buf, c) != 0); +} + +/* + * Just like vim_iswordc() but uses a pointer to the (multi-byte) character. + */ + int +vim_iswordp(char_u *p) +{ + return vim_iswordp_buf(p, curbuf); +} + + int +vim_iswordp_buf(char_u *p, buf_T *buf) +{ + int c = *p; + + if (has_mbyte && MB_BYTE2LEN(c) > 1) + c = (*mb_ptr2char)(p); + return vim_iswordc_buf(c, buf); +} + +/* + * return TRUE if 'c' is a valid file-name character + * Assume characters above 0x100 are valid (multi-byte). + */ + int +vim_isfilec(int c) +{ + return (c >= 0x100 || (c > 0 && (g_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. + */ + 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. + */ + int +vim_isprintc(int c) +{ + if (enc_utf8 && c >= 0x100) + return utf_printable(c); + return (c >= 0x100 || (c > 0 && (g_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. + */ + 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 && (g_chartab[c] & CT_PRINT_CHAR))); +} + +/* + * like chartabsize(), but also check for line breaks on the screen + */ + int +lbr_chartabsize( + char_u *line UNUSED, /* start of the line */ + unsigned char *s, + colnr_T col) +{ +#ifdef FEAT_LINEBREAK + if (!curwin->w_p_lbr && *p_sbr == NUL && !curwin->w_p_bri) + { +#endif + if (curwin->w_p_wrap) + return win_nolbr_chartabsize(curwin, s, col, NULL); + RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, s, col) +#ifdef FEAT_LINEBREAK + } + return win_lbr_chartabsize(curwin, line == NULL ? s : line, s, col, NULL); +#endif +} + +/* + * Call lbr_chartabsize() and advance the pointer. + */ + int +lbr_chartabsize_adv( + char_u *line, /* start of the line */ + char_u **s, + colnr_T col) +{ + int retval; + + retval = lbr_chartabsize(line, *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. + */ + int +win_lbr_chartabsize( + win_T *wp, + char_u *line UNUSED, /* start of the line */ + char_u *s, + colnr_T col, + int *headp UNUSED) +{ +#ifdef FEAT_LINEBREAK + int c; + int size; + colnr_T col2; + colnr_T col_adj = 0; /* col + screen size of tab */ + colnr_T colmax; + int added; + int mb_added = 0; + int numberextra; + char_u *ps; + int tab_corr = (*s == TAB); + int n; + + /* + * No 'linebreak', 'showbreak' and 'breakindent': return quickly. + */ + if (!wp->w_p_lbr && !wp->w_p_bri && *p_sbr == NUL) +#endif + { + if (wp->w_p_wrap) + return win_nolbr_chartabsize(wp, s, col, headp); + RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, col) + } + +#ifdef FEAT_LINEBREAK + /* + * First get normal size, without 'linebreak' + */ + size = win_chartabsize(wp, s, col); + c = *s; + if (tab_corr) + col_adj = size - 1; + + /* + * 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((int)s[1]) + && 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)(wp->w_width - numberextra - col_adj); + if (col >= colmax) + { + colmax += col_adj; + n = colmax + win_col_off2(wp); + if (n > 0) + colmax += (((col - colmax) / n) + 1) * n - col_adj; + } + + for (;;) + { + ps = s; + MB_PTR_ADV(s); + c = *s; + if (!(c != NUL + && (VIM_ISBREAK(c) + || (!VIM_ISBREAK(c) + && (col2 == col || !VIM_ISBREAK((int)*ps)))))) + break; + + col2 += win_chartabsize(wp, s, col2); + if (col2 >= colmax) /* doesn't fit */ + { + size = colmax - col + col_adj; + tab_corr = FALSE; + break; + } + } + } + else if (has_mbyte && size == 2 && MB_BYTE2LEN(*s) > 1 + && wp->w_p_wrap && in_win_border(wp, col)) + { + ++size; /* Count the ">" in the last column. */ + mb_added = 1; + } + + /* + * May have to add something for 'breakindent' and/or 'showbreak' + * string at start of line. + * Set *headp to the size of what we add. + */ + added = 0; + if ((*p_sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && col != 0) + { + colnr_T sbrlen = 0; + int numberwidth = win_col_off(wp); + + numberextra = numberwidth; + col += numberextra + mb_added; + if (col >= (colnr_T)wp->w_width) + { + col -= wp->w_width; + numberextra = wp->w_width - (numberextra - win_col_off2(wp)); + if (col >= numberextra && numberextra > 0) + col %= numberextra; + if (*p_sbr != NUL) + { + sbrlen = (colnr_T)MB_CHARLEN(p_sbr); + if (col >= sbrlen) + col -= sbrlen; + } + if (col >= numberextra && numberextra > 0) + col = col % numberextra; + else if (col > 0 && numberextra > 0) + col += numberwidth - win_col_off2(wp); + + numberwidth -= win_col_off2(wp); + } + if (col == 0 || col + size + sbrlen > (colnr_T)wp->w_width) + { + added = 0; + if (*p_sbr != NUL) + { + if (size + sbrlen + numberwidth > (colnr_T)wp->w_width) + { + /* calculate effective window width */ + int width = (colnr_T)wp->w_width - sbrlen - numberwidth; + int prev_width = col ? ((colnr_T)wp->w_width - (sbrlen + col)) : 0; + if (width == 0) + width = (colnr_T)wp->w_width; + added += ((size - prev_width) / width) * vim_strsize(p_sbr); + if ((size - prev_width) % width) + /* wrapped, add another length of 'sbr' */ + added += vim_strsize(p_sbr); + } + else + added += vim_strsize(p_sbr); + } + if (wp->w_p_bri) + added += get_breakindent_win(wp, line); + + size += added; + if (col != 0) + added = 0; + } + } + if (headp != NULL) + *headp = added + mb_added; + return size; +#endif +} + +/* + * 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. + */ + 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)) + { +# ifdef FEAT_VARTABS + return tabstop_padding(col, wp->w_buffer->b_p_ts, + wp->w_buffer->b_p_vts_array); +# else + n = wp->w_buffer->b_p_ts; + return (int)(n - (col % n)); +# endif + } + 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". + */ + 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 = wp->w_width - 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! + */ + 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 */ + char_u *line; /* start of the line */ + int incr; + int head; +#ifdef FEAT_VARTABS + int *vts = wp->w_buffer->b_p_vts_array; +#endif + int ts = wp->w_buffer->b_p_ts; + int c; + + vcol = 0; + line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); + if (pos->col == MAXCOL) + posptr = NULL; /* continue until the NUL */ + else + { + /* Special check for an empty line, which can happen on exit, when + * ml_get_buf() always returns an empty string. */ + if (*ptr == NUL) + pos->col = 0; + posptr = ptr + pos->col; + if (has_mbyte) + /* always start on the first byte */ + posptr -= (*mb_head_off)(line, posptr); + } + + /* + * This function is used very often, do some speed optimizations. + * When 'list', 'linebreak', 'showbreak' and 'breakindent' 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) +#ifdef FEAT_LINEBREAK + && !wp->w_p_lbr && *p_sbr == NUL && !wp->w_p_bri +#endif + ) + { + for (;;) + { + head = 0; + c = *ptr; + /* make sure we don't go past the end of the line */ + if (c == NUL) + { + incr = 1; /* NUL at end of line only takes one column */ + break; + } + /* A tab gets expanded, depending on the current column */ + if (c == TAB) +#ifdef FEAT_VARTABS + incr = tabstop_padding(vcol, ts, vts); +#else + incr = ts - (vcol % ts); +#endif + 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 = g_chartab[c] & CT_CELL_MASK; + + /* 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 = g_chartab[c] & CT_CELL_MASK; + } + + 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, line, ptr, vcol, &head); + /* make sure we don't go past the end of the line */ + if (*ptr == NUL) + { + incr = 1; /* NUL at end of line only takes one column */ + 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(*pos, VIsual))) + ) + *cursor = vcol + incr - 1; /* cursor at end */ + else + *cursor = vcol + head; /* cursor at start */ + } +} + +/* + * Get virtual cursor column in the current window, pretending 'list' is off. + */ + colnr_T +getvcol_nolist(pos_T *posp) +{ + int list_save = curwin->w_p_list; + colnr_T vcol; + + curwin->w_p_list = FALSE; + if (posp->coladd) + getvvcol(curwin, posp, NULL, &vcol, NULL); + else + getvcol(curwin, posp, NULL, &vcol, NULL); + curwin->w_p_list = list_save; + return vcol; +} + +/* + * Get virtual column in virtual mode. + */ + 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. + */ + void +getvcols( + win_T *wp, + pos_T *pos1, + pos_T *pos2, + colnr_T *left, + colnr_T *right) +{ + colnr_T from1, from2, to1, to2; + + if (LT_POSP(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'. + */ + char_u * +skipwhite(char_u *q) +{ + char_u *p = q; + + while (VIM_ISWHITE(*p)) /* skip to next non-white */ + ++p; + return p; +} + +/* + * getwhitecols: return the number of whitespace + * columns (bytes) at the start of a given line + */ + int +getwhitecols_curline() +{ + return getwhitecols(ml_get_curline()); +} + + int +getwhitecols(char_u *p) +{ + return skipwhite(p) - p; +} + +/* + * skip over digits + */ + char_u * +skipdigits(char_u *q) +{ + char_u *p = q; + + while (VIM_ISDIGIT(*p)) /* skip to next non-digit */ + ++p; + return p; +} + +#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO) +/* + * skip over binary digits + */ + char_u * +skipbin(char_u *q) +{ + char_u *p = q; + + while (vim_isbdigit(*p)) /* skip to next non-digit */ + ++p; + return p; +} + +/* + * skip over 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; +} +#endif + +/* + * skip to bin digit (or NUL after the string) + */ + char_u * +skiptobin(char_u *q) +{ + char_u *p = q; + + while (*p != NUL && !vim_isbdigit(*p)) /* skip to next digit */ + ++p; + return p; +} + +/* + * skip to 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) + */ + 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. + */ + 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. + */ + int +vim_isxdigit(int c) +{ + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); +} + +/* + * Corollary of vim_isdigit and vim_isxdigit() that can handle + * characters > 0x100. + */ + int +vim_isbdigit(int c) +{ + return (c == '0' || c == '1'); +} + +/* + * 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) + { +#ifdef HAVE_ISWLOWER + if (has_mbyte) + return iswlower(c); +#endif + /* 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) + { +#ifdef HAVE_ISWUPPER + if (has_mbyte) + return iswupper(c); +#endif + /* 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 || !(cmp_flags & CMP_KEEPASCII)) + { + if (enc_utf8) + return utf_toupper(c); + if (c >= 0x100) + { +#ifdef HAVE_TOWUPPER + if (has_mbyte) + return towupper(c); +#endif + /* toupper() can't handle these chars and may crash */ + return c; + } + if (enc_latin1like) + return latin1upper[c]; + } + if (c < 0x80 && (cmp_flags & CMP_KEEPASCII)) + return TOUPPER_ASC(c); + return TOUPPER_LOC(c); +} + + int +vim_tolower(int c) +{ + if (c <= '@') + return c; + if (c >= 0x80 || !(cmp_flags & CMP_KEEPASCII)) + { + if (enc_utf8) + return utf_tolower(c); + if (c >= 0x100) + { +#ifdef HAVE_TOWLOWER + if (has_mbyte) + return towlower(c); +#endif + /* tolower() can't handle these chars and may crash */ + return c; + } + if (enc_latin1like) + return latin1lower[c]; + } + if (c < 0x80 && (cmp_flags & CMP_KEEPASCII)) + return TOLOWER_ASC(c); + return TOLOWER_LOC(c); +} + +/* + * skiptowhite: skip over text until ' ' or '\t' or NUL. + */ + char_u * +skiptowhite(char_u *p) +{ + while (*p != ' ' && *p != '\t' && *p != NUL) + ++p; + return p; +} + +/* + * skiptowhite_esc: Like skiptowhite(), but also skip escaped chars + */ + 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! + */ + long +getdigits(char_u **pp) +{ + char_u *p; + long retval; + + p = *pp; + retval = atol((char *)p); + if (*p == '-') /* skip negative sign */ + ++p; + p = skipdigits(p); /* skip to next non-digit */ + *pp = p; + return retval; +} + +/* + * Return TRUE if "lbuf" is empty or only contains blanks. + */ + int +vim_isblankline(char_u *lbuf) +{ + char_u *p; + + p = skipwhite(lbuf); + return (*p == NUL || *p == '\r' || *p == '\n'); +} + +/* + * Convert a string into a long and/or unsigned long, taking care of + * hexadecimal, octal, and binary numbers. Accepts a '-' sign. + * If "prep" is not NULL, returns a flag to indicate the type of the number: + * 0 decimal + * '0' octal + * 'B' bin + * 'b' bin + * '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 "what" contains STR2NR_BIN recognize binary numbers + * If "what" contains STR2NR_OCT recognize octal numbers + * If "what" contains STR2NR_HEX recognize hex numbers + * If "what" contains STR2NR_FORCE always assume bin/oct/hex. + * If maxlen > 0, check at a maximum maxlen chars. + */ + void +vim_str2nr( + char_u *start, + int *prep, /* return: type of number 0 = decimal, 'x' + or 'X' is hex, '0' = octal, 'b' or 'B' + is bin */ + int *len, /* return: detected length of number */ + int what, /* what numbers to recognize */ + varnumber_T *nptr, /* return: signed result */ + uvarnumber_T *unptr, /* return: unsigned result */ + int maxlen) /* max length of string to check */ +{ + char_u *ptr = start; + int pre = 0; /* default is decimal */ + int negative = FALSE; + uvarnumber_T un = 0; + int n; + + if (ptr[0] == '-') + { + negative = TRUE; + ++ptr; + } + + /* Recognize hex, octal, and bin. */ + if (ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9' + && (maxlen == 0 || maxlen > 1)) + { + pre = ptr[1]; + if ((what & STR2NR_HEX) + && (pre == 'X' || pre == 'x') && vim_isxdigit(ptr[2]) + && (maxlen == 0 || maxlen > 2)) + /* hexadecimal */ + ptr += 2; + else if ((what & STR2NR_BIN) + && (pre == 'B' || pre == 'b') && vim_isbdigit(ptr[2]) + && (maxlen == 0 || maxlen > 2)) + /* binary */ + ptr += 2; + else + { + /* decimal or octal, default is decimal */ + pre = 0; + if (what & STR2NR_OCT) + { + /* Don't interpret "0", "08" or "0129" as octal. */ + for (n = 1; n != maxlen && VIM_ISDIGIT(ptr[n]); ++n) + { + if (ptr[n] > '7') + { + pre = 0; /* can't be octal */ + break; + } + pre = '0'; /* assume octal */ + } + } + } + } + + /* + * Do the string-to-numeric conversion "manually" to avoid sscanf quirks. + */ + n = 1; + if (pre == 'B' || pre == 'b' || what == STR2NR_BIN + STR2NR_FORCE) + { + /* bin */ + if (pre != 0) + n += 2; /* skip over "0b" */ + while ('0' <= *ptr && *ptr <= '1') + { + /* avoid ubsan error for overflow */ + if (un <= UVARNUM_MAX / 2) + un = 2 * un + (uvarnumber_T)(*ptr - '0'); + else + un = UVARNUM_MAX; + ++ptr; + if (n++ == maxlen) + break; + } + } + else if (pre == '0' || what == STR2NR_OCT + STR2NR_FORCE) + { + /* octal */ + while ('0' <= *ptr && *ptr <= '7') + { + /* avoid ubsan error for overflow */ + if (un <= UVARNUM_MAX / 8) + un = 8 * un + (uvarnumber_T)(*ptr - '0'); + else + un = UVARNUM_MAX; + ++ptr; + if (n++ == maxlen) + break; + } + } + else if (pre != 0 || what == STR2NR_HEX + STR2NR_FORCE) + { + /* hex */ + if (pre != 0) + n += 2; /* skip over "0x" */ + while (vim_isxdigit(*ptr)) + { + /* avoid ubsan error for overflow */ + if (un <= UVARNUM_MAX / 16) + un = 16 * un + (uvarnumber_T)hex2nr(*ptr); + else + un = UVARNUM_MAX; + ++ptr; + if (n++ == maxlen) + break; + } + } + else + { + /* decimal */ + while (VIM_ISDIGIT(*ptr)) + { + uvarnumber_T digit = (uvarnumber_T)(*ptr - '0'); + + /* avoid ubsan error for overflow */ + if (un < UVARNUM_MAX / 10 + || (un == UVARNUM_MAX / 10 && digit <= UVARNUM_MAX % 10)) + un = 10 * un + digit; + else + un = UVARNUM_MAX; + ++ptr; + if (n++ == maxlen) + break; + } + } + + if (prep != NULL) + *prep = pre; + if (len != NULL) + *len = (int)(ptr - start); + if (nptr != NULL) + { + if (negative) /* account for leading '-' for decimal numbers */ + { + /* avoid ubsan error for overflow */ + if (un > VARNUM_MAX) + *nptr = VARNUM_MIN; + else + *nptr = -(varnumber_T)un; + } + else + { + if (un > VARNUM_MAX) + un = VARNUM_MAX; + *nptr = (varnumber_T)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'. + */ + 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(PROTO) +/* + * Convert two hex characters to a byte. + * Return -1 if one of the characters 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 + +/* + * Return TRUE if "str" starts with a backslash that should be removed. + * For MS-DOS, WIN32 and OS/2 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. + */ + 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 + return (str[0] == '\\' && str[1] != NUL); +#endif +} + +/* + * Halve the number of backslashes in a file name argument. + * For MS-DOS we only do this if the character after the backslash + * is not a normal file character. + */ + 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. + */ + char_u * +backslash_halve_save(char_u *p) +{ + char_u *res; + + res = vim_strsave(p); + if (res == NULL) + return p; + backslash_halve(res); + return res; +} + +#if (defined(EBCDIC) && defined(FEAT_POSTSCRIPT)) || defined(PROTO) +/* + * Table for EBCDIC to ASCII conversion unashamedly taken from xxd.c! + * The first 64 entries have been added to map control characters defined in + * ascii.h + */ +static char_u ebcdic2ascii_tab[256] = +{ + 0000, 0001, 0002, 0003, 0004, 0011, 0006, 0177, + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, + 0020, 0021, 0022, 0023, 0024, 0012, 0010, 0027, + 0030, 0031, 0032, 0033, 0033, 0035, 0036, 0037, + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, + 0247, 0250, 0325, 0056, 0074, 0050, 0053, 0174, + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0041, 0044, 0052, 0051, 0073, 0176, + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0313, 0054, 0045, 0137, 0076, 0077, + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, + 0161, 0162, 0136, 0314, 0315, 0316, 0317, 0320, + 0321, 0345, 0163, 0164, 0165, 0166, 0167, 0170, + 0171, 0172, 0322, 0323, 0324, 0133, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0135, 0346, 0347, + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377 +}; + +/* + * Convert a buffer worth of characters from EBCDIC to ASCII. Only useful if + * wanting 7-bit ASCII characters out the other end. + */ + void +ebcdic2ascii(char_u *buffer, int len) +{ + int i; + + for (i = 0; i < len; i++) + buffer[i] = ebcdic2ascii_tab[buffer[i]]; +} +#endif diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 0000000..d1aaf70 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,491 @@ +/* + * config.h.in. Originally generated automatically from configure.ac by + * autoheader and manually changed after that. + */ + +/* Define if we have EBCDIC code */ +#undef EBCDIC + +/* Define unless no X support found */ +#undef HAVE_X11 + +/* Define when terminfo support found */ +#undef TERMINFO + +/* Define when termcap.h contains ospeed */ +#undef HAVE_OSPEED + +/* Define when ospeed can be extern */ +#undef OSPEED_EXTERN + +/* Define when termcap.h contains UP, BC and PC */ +#undef HAVE_UP_BC_PC + +/* Define when UP, BC and PC can be extern */ +#undef UP_BC_PC_EXTERN + +/* Define when termcap.h defines outfuntype */ +#undef HAVE_OUTFUNTYPE + +/* Define when __DATE__ " " __TIME__ can be used */ +#undef HAVE_DATE_TIME + +/* Define when __attribute__((unused)) can be used */ +#undef HAVE_ATTRIBUTE_UNUSED + +/* defined always when using configure */ +#undef UNIX + +/* Defined to the size of an int */ +#undef VIM_SIZEOF_INT + +/* Defined to the size of a long */ +#undef VIM_SIZEOF_LONG + +/* Defined to the size of off_t */ +#undef SIZEOF_OFF_T + +/* Defined to the size of time_t */ +#undef SIZEOF_TIME_T + +/* Define when wchar_t is only 2 bytes. */ +#undef SMALL_WCHAR_T + +/* + * If we cannot trust one of the following from the libraries, we use our + * own safe but probably slower vim_memmove(). + */ +#undef USEBCOPY +#undef USEMEMMOVE +#undef USEMEMCPY + +/* Define when "man -s 2" is to be used */ +#undef USEMAN_S + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define to empty if the keyword does not work. */ +#undef volatile + +/* Define to `int' if doesn't define. */ +#undef mode_t + +/* Define to `long' if doesn't define. */ +#undef off_t + +/* Define to `long' if doesn't define. */ +#undef pid_t + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define to `unsigned int' or other type that is 32 bit. */ +#undef uint32_t + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define to `long' if doesn't define. */ +#undef ino_t + +/* Define to `unsigned' if doesn't define. */ +#undef dev_t + +/* Define on big-endian machines */ +#undef WORDS_BIGENDIAN + +/* Define to `unsigned long' if doesn't define. */ +#undef rlim_t + +/* Define to `struct sigaltstack' if doesn't define. */ +#undef stack_t + +/* Define if stack_t has the ss_base field. */ +#undef HAVE_SS_BASE + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if you can safely include both and . */ +#undef SYS_SELECT_WITH_SYS_TIME + +/* Define to a typecast for select() arguments 2, 3 and 4. */ +#undef SELECT_TYPE_ARG234 + +/* Define if you have /dev/ptc */ +#undef HAVE_DEV_PTC + +/* Define if you have Sys4 ptys */ +#undef HAVE_SVR4_PTYS + +/* Define to range of pty names to try */ +#undef PTYRANGE0 +#undef PTYRANGE1 + +/* Define mode for pty */ +#undef PTYMODE + +/* Define group for pty */ +#undef PTYGROUP + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define as the command at the end of signal handlers ("" or "return 0;"). */ +#undef SIGRETURN + +/* Define if struct sigcontext is present */ +#undef HAVE_SIGCONTEXT + +/* Define if touuper/tolower only work on lower/upercase characters */ +#undef BROKEN_TOUPPER + +/* Define if stat() ignores a trailing slash */ +#undef STAT_IGNORES_SLASH + +/* Define if tgetstr() has a second argument that is (char *) */ +#undef TGETSTR_CHAR_P + +/* Define if tgetent() returns zero for an error */ +#undef TGETENT_ZERO_ERR + +/* Define if the getcwd() function should not be used. */ +#undef BAD_GETCWD + +/* Define if you the function: */ +#undef HAVE_FCHDIR +#undef HAVE_FCHOWN +#undef HAVE_FCHMOD +#undef HAVE_FLOAT_FUNCS +#undef HAVE_FSEEKO +#undef HAVE_FSYNC +#undef HAVE_FTRUNCATE +#undef HAVE_GETCWD +#undef HAVE_GETPGID +#undef HAVE_GETPSEUDOTTY +#undef HAVE_GETPWENT +#undef HAVE_GETPWNAM +#undef HAVE_GETPWUID +#undef HAVE_GETRLIMIT +#undef HAVE_GETTIMEOFDAY +#undef HAVE_GETWD +#undef HAVE_ICONV +#undef HAVE_LSTAT +#undef HAVE_MEMSET +#undef HAVE_MKDTEMP +#undef HAVE_NANOSLEEP +#undef HAVE_NL_LANGINFO_CODESET +#undef HAVE_OPENDIR +#undef HAVE_PUTENV +#undef HAVE_QSORT +#undef HAVE_READLINK +#undef HAVE_RENAME +#undef HAVE_SELECT +#undef HAVE_SELINUX +#undef HAVE_SETENV +#undef HAVE_SETPGID +#undef HAVE_SETSID +#undef HAVE_SIGACTION +#undef HAVE_SIGALTSTACK +#undef HAVE_SIGSET +#undef HAVE_SIGSETJMP +#undef HAVE_SIGSTACK +#undef HAVE_SIGPROCMASK +#undef HAVE_SIGVEC +#undef HAVE_SMACK +#undef HAVE_STRCASECMP +#undef HAVE_STRERROR +#undef HAVE_STRFTIME +#undef HAVE_STRICMP +#undef HAVE_STRNCASECMP +#undef HAVE_STRNICMP +#undef HAVE_STRPBRK +#undef HAVE_STRTOL +#undef HAVE_ST_BLKSIZE +#undef HAVE_SYSCONF +#undef HAVE_SYSCTL +#undef HAVE_SYSINFO +#undef HAVE_SYSINFO_MEM_UNIT +#undef HAVE_TGETENT +#undef HAVE_TOWLOWER +#undef HAVE_TOWUPPER +#undef HAVE_ISWUPPER +#undef HAVE_UNSETENV +#undef HAVE_USLEEP +#undef HAVE_UTIME +#undef HAVE_BIND_TEXTDOMAIN_CODESET +#undef HAVE_MBLEN + +/* Define, if needed, for accessing large files. */ +#undef _LARGE_FILES +#undef _FILE_OFFSET_BITS +#undef _LARGEFILE_SOURCE + +/* Define if you do not have utime(), but do have the utimes() function. */ +#undef HAVE_UTIMES + +/* Define if you have the header file: */ +#undef HAVE_DIRENT_H +#undef HAVE_ERRNO_H +#undef HAVE_FCNTL_H +#undef HAVE_FRAME_H +#undef HAVE_ICONV_H +#undef HAVE_INTTYPES_H +#undef HAVE_LANGINFO_H +#undef HAVE_LIBC_H +#undef HAVE_LIBGEN_H +#undef HAVE_LIBINTL_H +#undef HAVE_LOCALE_H +#undef HAVE_MATH_H +#undef HAVE_NDIR_H +#undef HAVE_POLL_H +#undef HAVE_PTHREAD_NP_H +#undef HAVE_PWD_H +#undef HAVE_SETJMP_H +#undef HAVE_SGTTY_H +#undef HAVE_STDINT_H +#undef HAVE_STRINGS_H +#undef HAVE_STROPTS_H +#undef HAVE_SYS_ACCESS_H +#undef HAVE_SYS_ACL_H +#undef HAVE_SYS_DIR_H +#undef HAVE_SYS_IOCTL_H +#undef HAVE_SYS_NDIR_H +#undef HAVE_SYS_PARAM_H +#undef HAVE_SYS_POLL_H +#undef HAVE_SYS_PTEM_H +#undef HAVE_SYS_PTMS_H +#undef HAVE_SYS_RESOURCE_H +#undef HAVE_SYS_SELECT_H +#undef HAVE_SYS_STATFS_H +#undef HAVE_SYS_STREAM_H +#undef HAVE_SYS_SYSCTL_H +#undef HAVE_SYS_SYSINFO_H +#undef HAVE_SYS_SYSTEMINFO_H +#undef HAVE_SYS_TIME_H +#undef HAVE_SYS_TYPES_H +#undef HAVE_SYS_UTSNAME_H +#undef HAVE_TERMCAP_H +#undef HAVE_TERMIOS_H +#undef HAVE_TERMIO_H +#undef HAVE_WCHAR_H +#undef HAVE_WCTYPE_H +#undef HAVE_UNISTD_H +#undef HAVE_UTIL_DEBUG_H +#undef HAVE_UTIL_MSGI18N_H +#undef HAVE_UTIME_H +#undef HAVE_X11_SUNKEYSYM_H +#undef HAVE_XM_XM_H +#undef HAVE_XM_XPMP_H +#undef HAVE_XM_TRAITP_H +#undef HAVE_XM_MANAGER_H +#undef HAVE_XM_UNHIGHLIGHTT_H +#undef HAVE_XM_JOINSIDET_H +#undef HAVE_XM_NOTEBOOK_H +#undef HAVE_X11_XPM_H +#undef HAVE_X11_XMU_EDITRES_H +#undef HAVE_X11_SM_SMLIB_H + +/* Define to the type of the XpmAttributes type. */ +#undef XPMATTRIBUTES_TYPE + +/* Define if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define if you have a that is not POSIX.1 compatible. */ +#undef HAVE_UNION_WAIT + +/* This is currently unused in vim: */ +/* Define if you have the ANSI C header files. */ +/* #undef STDC_HEADERS */ + +/* instead, we check a few STDC things ourselves */ +#undef HAVE_STDLIB_H +#undef HAVE_STRING_H + +/* Define if strings.h cannot be included when strings.h already is */ +#undef NO_STRINGS_WITH_STRING_H + +/* Define if you want tiny features. */ +#undef FEAT_TINY + +/* Define if you want small features. */ +#undef FEAT_SMALL + +/* Define if you want normal features. */ +#undef FEAT_NORMAL + +/* Define if you want big features. */ +#undef FEAT_BIG + +/* Define if you want huge features. */ +#undef FEAT_HUGE + +/* Define if you want to include the Lua interpreter. */ +#undef FEAT_LUA + +/* Define for linking via dlopen() or LoadLibrary() */ +#undef DYNAMIC_LUA + +/* Define if you want to include the MzScheme interpreter. */ +#undef FEAT_MZSCHEME + +/* Define if you want to include the Perl interpreter. */ +#undef FEAT_PERL + +/* Define for linking via dlopen() or LoadLibrary() */ +#undef DYNAMIC_PERL + +/* Define if you want to include the Python interpreter. */ +#undef FEAT_PYTHON + +/* Define if you want to include the Python3 interpreter. */ +#undef FEAT_PYTHON3 + +/* Define for linking via dlopen() or LoadLibrary() */ +#undef DYNAMIC_PYTHON + +/* Define for linking via dlopen() or LoadLibrary() */ +#undef DYNAMIC_PYTHON3 + +/* Define if dynamic python does not require RTLD_GLOBAL */ +#undef PY_NO_RTLD_GLOBAL + +/* Define if dynamic python3 does not require RTLD_GLOBAL */ +#undef PY3_NO_RTLD_GLOBAL + +/* Define if you want to include the Ruby interpreter. */ +#undef FEAT_RUBY + +/* Define for linking via dlopen() or LoadLibrary() */ +#undef DYNAMIC_RUBY + +/* Define if you want to include the Tcl interpreter. */ +#undef FEAT_TCL + +/* Define for linking via dlopen() or LoadLibrary() */ +#undef DYNAMIC_TCL + +/* Define if you want to add support for ACL */ +#undef HAVE_POSIX_ACL +#undef HAVE_SOLARIS_ZFS_ACL +#undef HAVE_SOLARIS_ACL +#undef HAVE_AIX_ACL + +/* Define if pango_shape_full() is available. */ +#undef HAVE_PANGO_SHAPE_FULL + +/* Define if you want to add support of GPM (Linux console mouse daemon) */ +#undef HAVE_GPM + +/* Define if you want to add support of sysmouse (*BSD console mouse) */ +#undef HAVE_SYSMOUSE + +/* Define if you want to include the Cscope interface. */ +#undef FEAT_CSCOPE + +/* Define if you don't want to include right-left support. */ +#undef DISABLE_RIGHTLEFT + +/* Define if you don't want to include Farsi support. */ +#undef DISABLE_FARSI + +/* Define if you don't want to include Arabic support. */ +#undef DISABLE_ARABIC + +/* Define if you want to always define a server name at vim startup. */ +#undef FEAT_AUTOSERVERNAME + +/* Define if you want to include fontset support. */ +#undef FEAT_XFONTSET + +/* Define if you want to include XIM support. */ +#undef FEAT_XIM + +/* Define if you want to include Hangul input support. */ +#undef FEAT_HANGULIN + +/* Define if you use GTK and want GNOME support. */ +#undef FEAT_GUI_GNOME + +/* Define if you use KDE and want KDE Toolbar support. */ +#undef FEAT_KDETOOLBAR + +/* Define if your X has own locale library */ +#undef X_LOCALE + +/* Define if we have dlfcn.h. */ +#undef HAVE_DLFCN_H + +/* Define if there is a working gettext(). */ +#undef HAVE_GETTEXT + +/* Define if _nl_msg_cat_cntr is present. */ +#undef HAVE_NL_MSG_CAT_CNTR + +/* Define if we have dlopen() */ +#undef HAVE_DLOPEN + +/* Define if we have dlsym() */ +#undef HAVE_DLSYM + +/* Define if we have dl.h. */ +#undef HAVE_DL_H + +/* Define if we have shl_load() */ +#undef HAVE_SHL_LOAD + +/* Define if you want to include NetBeans integration. */ +#undef FEAT_NETBEANS_INTG + +/* Define if you want to include process communication. */ +#undef FEAT_JOB_CHANNEL + +/* Define if you want to include terminal emulator support. */ +#undef FEAT_TERMINAL + +// Define default global runtime path. +#undef RUNTIME_GLOBAL + +// Define default global runtime after path. +#undef RUNTIME_GLOBAL_AFTER + +/* Define name of who modified a released Vim */ +#undef MODIFIED_BY + +/* Define if you want XSMP interaction as well as vanilla swapfile safety */ +#undef USE_XSMP_INTERACT + +/* Define if fcntl()'s F_SETFD command knows about FD_CLOEXEC */ +#undef HAVE_FD_CLOEXEC + +/* Define if /proc/self/exe or similar can be read */ +#undef PROC_EXE_LINK + +/* Define if you want Cygwin to use the WIN32 clipboard, not compatible with X11*/ +#undef FEAT_CYGWIN_WIN32_CLIPBOARD + +/* Define if we have AvailabilityMacros.h on Mac OS X */ +#undef HAVE_AVAILABILITYMACROS_H + +/* Define if Xutf8SetWMProperties() is in an X library. */ +#undef HAVE_XUTF8SETWMPROPERTIES + +/* Define if GResource is used to load icons */ +#undef USE_GRESOURCE + +/* Define if GTK+ GUI is to be linked against GTK+ 3 */ +#undef USE_GTK3 + +/* Define if we have isinf() */ +#undef HAVE_ISINF + +/* Define if we have isnan() */ +#undef HAVE_ISNAN + +/* Define to inline symbol or empty */ +#undef inline diff --git a/src/config.mk.dist b/src/config.mk.dist new file mode 100644 index 0000000..8a584c2 --- /dev/null +++ b/src/config.mk.dist @@ -0,0 +1,5 @@ +the first target to make vim is: reconfig +srcdir = . +VIMNAME = vim +EXNAME = ex +VIEWNAME = view diff --git a/src/config.mk.in b/src/config.mk.in new file mode 100644 index 0000000..b5d1ebd --- /dev/null +++ b/src/config.mk.in @@ -0,0 +1,175 @@ +# +# config.mk.in -- autoconf template for Vim on Unix vim:ts=8:sw=8: +# +# DO NOT EDIT config.mk!! It will be overwritten by configure. +# Edit Makefile and run "make" or run ./configure with other arguments. +# +# Configure does not edit the makefile directly. This method is not the +# standard use of GNU autoconf, but it has two advantages: +# a) The user can override every choice made by configure. +# b) Modifications to the makefile are not lost when configure is run. +# +# I hope this is worth being nonstandard. jw. + +@SET_MAKE@ + +VIMNAME = @VIMNAME@ +EXNAME = @EXNAME@ +VIEWNAME = @VIEWNAME@ + +CC = @CC@ +DEFS = @DEFS@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +srcdir = @srcdir@ +VPATH = @srcdir@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +TAGPRG = @TAGPRG@ + +CPP = @CPP@ +CPP_MM = @CPP_MM@ +DEPEND_CFLAGS_FILTER = @DEPEND_CFLAGS_FILTER@ +LINK_AS_NEEDED = @LINK_AS_NEEDED@ +X_CFLAGS = @X_CFLAGS@ +X_LIBS_DIR = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIB@ + +LUA_LIBS = @LUA_LIBS@ +LUA_SRC = @LUA_SRC@ +LUA_OBJ = @LUA_OBJ@ +LUA_CFLAGS = @LUA_CFLAGS@ +LUA_PRO = @LUA_PRO@ + +MZSCHEME_LIBS = @MZSCHEME_LIBS@ +MZSCHEME_SRC = @MZSCHEME_SRC@ +MZSCHEME_OBJ = @MZSCHEME_OBJ@ +MZSCHEME_CFLAGS = @MZSCHEME_CFLAGS@ +MZSCHEME_PRO = @MZSCHEME_PRO@ +MZSCHEME_EXTRA = @MZSCHEME_EXTRA@ +MZSCHEME_MZC = @MZSCHEME_MZC@ + +PERL = @vi_cv_path_perl@ +PERLLIB = @vi_cv_perllib@ +PERL_XSUBPP = @vi_cv_perl_xsubpp@ +PERL_LIBS = @PERL_LIBS@ +SHRPENV = @shrpenv@ +PERL_SRC = @PERL_SRC@ +PERL_OBJ = @PERL_OBJ@ +PERL_PRO = @PERL_PRO@ +PERL_CFLAGS = @PERL_CFLAGS@ + +PYTHON_SRC = @PYTHON_SRC@ +PYTHON_OBJ = @PYTHON_OBJ@ +PYTHON_CFLAGS = @PYTHON_CFLAGS@ +PYTHON_LIBS = @PYTHON_LIBS@ + +PYTHON3_SRC = @PYTHON3_SRC@ +PYTHON3_OBJ = @PYTHON3_OBJ@ +PYTHON3_CFLAGS = @PYTHON3_CFLAGS@ +PYTHON3_LIBS = @PYTHON3_LIBS@ + +TCL = @vi_cv_path_tcl@ +TCL_SRC = @TCL_SRC@ +TCL_OBJ = @TCL_OBJ@ +TCL_PRO = @TCL_PRO@ +TCL_CFLAGS = @TCL_CFLAGS@ +TCL_LIBS = @TCL_LIBS@ + +HANGULIN_SRC = @HANGULIN_SRC@ +HANGULIN_OBJ = @HANGULIN_OBJ@ + +NETBEANS_SRC = @NETBEANS_SRC@ +NETBEANS_OBJ = @NETBEANS_OBJ@ +CHANNEL_SRC = @CHANNEL_SRC@ +CHANNEL_OBJ = @CHANNEL_OBJ@ +TERM_SRC = @TERM_SRC@ +TERM_OBJ = @TERM_OBJ@ + +RUBY = @vi_cv_path_ruby@ +RUBY_SRC = @RUBY_SRC@ +RUBY_OBJ = @RUBY_OBJ@ +RUBY_PRO = @RUBY_PRO@ +RUBY_CFLAGS = @RUBY_CFLAGS@ +RUBY_LIBS = @RUBY_LIBS@ + +AWK = @AWK@ + +STRIP = @STRIP@ + +EXEEXT = @EXEEXT@ +CROSS_COMPILING = @CROSS_COMPILING@ + +COMPILEDBY = @compiledby@ + +INSTALLVIMDIFF = @dovimdiff@ +INSTALLGVIMDIFF = @dogvimdiff@ +INSTALL_LANGS = @INSTALL_LANGS@ +INSTALL_TOOL_LANGS = @INSTALL_TOOL_LANGS@ + +### sed command to fix quotes while creating pathdef.c +QUOTESED = @QUOTESED@ + +### Line break character as octal number for "tr" +NL = @line_break@ + +### Top directory for everything +prefix = @prefix@ + +### Top directory for the binary +exec_prefix = @exec_prefix@ + +### Prefix for location of data files +BINDIR = @bindir@ + +### For autoconf 2.60 and later (avoid a warning) +datarootdir = @datarootdir@ + +### Prefix for location of data files +DATADIR = @datadir@ + +### Prefix for location of man pages +MANDIR = @mandir@ + +### Do we have a GUI +GUI_INC_LOC = @GUI_INC_LOC@ +GUI_LIB_LOC = @GUI_LIB_LOC@ +GUI_SRC = $(@GUITYPE@_SRC) +GUI_OBJ = $(@GUITYPE@_OBJ) +GUI_DEFS = $(@GUITYPE@_DEFS) +GUI_IPATH = $(@GUITYPE@_IPATH) +GUI_LIBS_DIR = $(@GUITYPE@_LIBS_DIR) +GUI_LIBS1 = $(@GUITYPE@_LIBS1) +GUI_LIBS2 = $(@GUITYPE@_LIBS2) +GUI_INSTALL = $(@GUITYPE@_INSTALL) +GUI_TARGETS = $(@GUITYPE@_TARGETS) +GUI_MAN_TARGETS = $(@GUITYPE@_MAN_TARGETS) +GUI_TESTTARGET = $(@GUITYPE@_TESTTARGET) +GUI_TESTARG = $(@GUITYPE@_TESTARG) +GUI_BUNDLE = $(@GUITYPE@_BUNDLE) +NARROW_PROTO = @NARROW_PROTO@ +GUI_X_LIBS = @GUI_X_LIBS@ +MOTIF_LIBNAME = @MOTIF_LIBNAME@ +GTK_LIBNAME = @GTK_LIBNAME@ + +GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@ +GRESOURCE_SRC = @GRESOURCE_SRC@ +GRESOURCE_OBJ = @GRESOURCE_OBJ@ + +GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@ +UPDATE_DESKTOP_DATABASE = @UPDATE_DESKTOP_DATABASE@ + +### Any OS dependent extra source and object file +OS_EXTRA_SRC = @OS_EXTRA_SRC@ +OS_EXTRA_OBJ = @OS_EXTRA_OBJ@ + +### If the *.po files are to be translated to *.mo files. +MAKEMO = @MAKEMO@ + +MSGFMT = @MSGFMT@ + +# Make sure that "make first" will run "make all" once configure has done its +# work. This is needed when using the Makefile in the top directory. +first: all diff --git a/src/configure b/src/configure new file mode 100755 index 0000000..d8595a5 --- /dev/null +++ b/src/configure @@ -0,0 +1,10 @@ +#! /bin/sh +# run the automatically generated configure script +CONFIG_STATUS=auto/config.status \ + auto/configure "$@" --srcdir="${srcdir:-.}" --cache-file=auto/config.cache +result=$? + +# Stupid autoconf 2.5x causes this file to be left behind. +if test -f configure.lineno; then rm -f configure.lineno; fi + +exit $result diff --git a/src/configure.ac b/src/configure.ac new file mode 100644 index 0000000..2b7725b --- /dev/null +++ b/src/configure.ac @@ -0,0 +1,4486 @@ +dnl configure.ac: autoconf script for Vim + +dnl Process this file with autoconf 2.12 or 2.13 to produce "configure". +dnl Should also work with autoconf 2.54 and later. + +AC_INIT(vim.h) +AC_CONFIG_HEADER(auto/config.h:config.h.in) + +dnl Being able to run configure means the system is Unix (compatible). +AC_DEFINE(UNIX) +AC_PROG_MAKE_SET + +dnl Checks for programs. +AC_PROG_CC_C99 dnl required by almost everything +AC_PROG_CPP dnl required by header file checks +AC_PROGRAM_EGREP dnl required by AC_EGREP_CPP +AC_PROG_FGREP dnl finds working grep -F +AC_ISC_POSIX dnl required by AC_C_CROSS +AC_PROG_AWK dnl required for "make html" in ../doc + +dnl Don't strip if we don't have it +AC_CHECK_PROG(STRIP, strip, strip, :) + +dnl Check for extension of executables +AC_EXEEXT + +dnl Check for standard headers. We don't use this in Vim but other stuff +dnl in autoconf needs it, where it uses STDC_HEADERS. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT + +dnl Check that the C99 features that Vim uses are supported: +if test x"$ac_cv_prog_cc_c99" != xno; then + dnl If the compiler doesn't explicitly support C99, then check + dnl for the specific features Vim uses + + AC_TYPE_LONG_LONG_INT + if test "$ac_cv_type_long_long_int" = no; then + AC_MSG_FAILURE([Compiler does not support long long int]) + fi + + AC_MSG_CHECKING([if the compiler supports trailing commas]) + trailing_commas=no + AC_TRY_COMPILE([], [ + enum { + one, + };], + [AC_MSG_RESULT(yes); trailing_commas=yes], + [AC_MSG_RESULT(no)]) + if test "$trailing_commas" = no; then + AC_MSG_FAILURE([Compiler does not support trailing comma in enum]) + fi + + AC_MSG_CHECKING([if the compiler supports C++ comments]) + slash_comments=no + AC_TRY_COMPILE([], + [// C++ comments?], + [AC_MSG_RESULT(yes); slash_comments=yes], + [AC_MSG_RESULT(no)]) + if test "$slash_comments" = no; then + AC_MSG_FAILURE([Compiler does not support C++ comments]) + fi +fi + +dnl Check for the flag that fails if stuff are missing. + +AC_MSG_CHECKING(--enable-fail-if-missing argument) +AC_ARG_ENABLE(fail_if_missing, + [ --enable-fail-if-missing Fail if dependencies on additional features + specified on the command line are missing.], + [fail_if_missing="yes"], + [fail_if_missing="no"]) +AC_MSG_RESULT($fail_if_missing) + +dnl Keep original value to check later. +with_x_arg="$with_x" + +dnl Set default value for CFLAGS if none is defined or it's empty +if test -z "$CFLAGS"; then + CFLAGS="-O" + test "$GCC" = yes && CFLAGS="-O2 -fno-strength-reduce -Wall" +fi +if test "$GCC" = yes; then + dnl method that should work for nearly all versions + gccversion=`$CC -dumpversion` + if test "x$gccversion" = "x"; then + dnl old method; fall-back for when -dumpversion doesn't work + gccversion=`$CC --version | sed -e '2,$d' -e 's/darwin.//' -e 's/^[[^0-9]]*\([[0-9]]\.[[0-9.]]*\).*$/\1/g'` + fi + dnl version 4.0.1 was reported to cause trouble on Macintosh by Marcin Dalecki + if test "$gccversion" = "3.0.1" -o "$gccversion" = "3.0.2" -o "$gccversion" = "4.0.1"; then + echo 'GCC [[34]].0.[[12]] has a bug in the optimizer, disabling "-O#"' + CFLAGS=`echo "$CFLAGS" | sed 's/-O[[23456789]]/-O/'` + else + if test "$gccversion" = "3.1" -o "$gccversion" = "3.2" -o "$gccversion" = "3.2.1" && `echo "$CFLAGS" | grep -v fno-strength-reduce >/dev/null`; then + echo 'GCC 3.1 and 3.2 have a bug in the optimizer, adding "-fno-strength-reduce"' + CFLAGS="$CFLAGS -fno-strength-reduce" + fi + fi +fi + +dnl clang-500.2.75 or around has abandoned -f[no-]strength-reduce and issues a +dnl warning when that flag is passed to. Accordingly, adjust CFLAGS based on +dnl the version number of the clang in use. +dnl Note that this does not work to get the version of clang 3.1 or 3.2. +AC_MSG_CHECKING(for clang version) +CLANG_VERSION_STRING=`$CC --version 2>/dev/null | sed -n -e 's/^.*clang[[^0-9]]*\([[0-9]][[0-9]]*\.[[0-9]][[0-9]]*\.[[0-9]][[0-9]]*\).*$/\1/p'` +if test x"$CLANG_VERSION_STRING" != x"" ; then + CLANG_MAJOR=`echo "$CLANG_VERSION_STRING" | sed -n -e 's/\([[0-9]][[0-9]]*\)\.[[0-9]][[0-9]]*\.[[0-9]][[0-9]]*/\1/p'` + CLANG_MINOR=`echo "$CLANG_VERSION_STRING" | sed -n -e 's/[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\)\.[[0-9]][[0-9]]*/\1/p'` + CLANG_REVISION=`echo "$CLANG_VERSION_STRING" | sed -n -e 's/[[0-9]][[0-9]]*\.[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\)/\1/p'` + CLANG_VERSION=`expr $CLANG_MAJOR '*' 1000000 '+' $CLANG_MINOR '*' 1000 '+' $CLANG_REVISION` + AC_MSG_RESULT($CLANG_VERSION) + dnl If you find the same issue with versions earlier than 500.2.75, + dnl change the constant 500002075 below appropriately. To get the + dnl integer corresponding to a version number, refer to the + dnl definition of CLANG_VERSION above. + AC_MSG_CHECKING(if clang supports -fno-strength-reduce) + if test "$CLANG_VERSION" -ge 500002075 ; then + AC_MSG_RESULT(no) + CFLAGS=`echo "$CFLAGS" | sed -e 's/-fno-strength-reduce/ /'` + else + AC_MSG_RESULT(yes) + fi +else + AC_MSG_RESULT(N/A) +fi + +dnl If configure thinks we are cross compiling, there might be something +dnl wrong with the CC or CFLAGS settings, give a useful warning message +CROSS_COMPILING= +if test "$cross_compiling" = yes; then + AC_MSG_RESULT([cannot compile a simple program; if not cross compiling check CC and CFLAGS]) + CROSS_COMPILING=1 +fi +AC_SUBST(CROSS_COMPILING) + +dnl gcc-cpp has the wonderful -MM option to produce nicer dependencies. +dnl But gcc 3.1 changed the meaning! See near the end. +test "$GCC" = yes && CPP_MM=M; AC_SUBST(CPP_MM) + +if test -f ./toolcheck; then + AC_CHECKING(for buggy tools) + sh ./toolcheck 1>&AC_FD_MSG +fi + +OS_EXTRA_SRC=""; OS_EXTRA_OBJ="" + +dnl Check for BeOS, which needs an extra source file +AC_MSG_CHECKING(for BeOS) +case `uname` in + BeOS) OS_EXTRA_SRC=os_beos.c; OS_EXTRA_OBJ=objects/os_beos.o + BEOS=yes; AC_MSG_RESULT(yes);; + *) BEOS=no; AC_MSG_RESULT(no);; +esac + +dnl If QNX is found, assume we don't want to use Xphoton +dnl unless it was specifically asked for (--with-x) +AC_MSG_CHECKING(for QNX) +case `uname` in + QNX) OS_EXTRA_SRC=os_qnx.c; OS_EXTRA_OBJ=objects/os_qnx.o + test -z "$with_x" && with_x=no + QNX=yes; AC_MSG_RESULT(yes);; + *) QNX=no; AC_MSG_RESULT(no);; +esac + +dnl Check for Darwin and MacOS X +dnl We do a check for MacOS X in the very beginning because there +dnl are a lot of other things we need to change besides GUI stuff +AC_MSG_CHECKING([for Darwin (Mac OS X)]) +if test "`(uname) 2>/dev/null`" = Darwin; then + AC_MSG_RESULT(yes) + MACOS_X=yes + CPPFLAGS="$CPPFLAGS -DMACOS_X" + + AC_MSG_CHECKING(--disable-darwin argument) + AC_ARG_ENABLE(darwin, + [ --disable-darwin Disable Darwin (Mac OS X) support.], + , [enable_darwin="yes"]) + if test "$enable_darwin" = "yes"; then + AC_MSG_RESULT(no) + AC_MSG_CHECKING(if Darwin files are there) + if test -f os_macosx.m; then + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT([no, Darwin support disabled]) + enable_darwin=no + fi + else + AC_MSG_RESULT([yes, Darwin support excluded]) + fi + + AC_MSG_CHECKING(--with-mac-arch argument) + AC_ARG_WITH(mac-arch, [ --with-mac-arch=ARCH current, intel, ppc or both], + MACARCH="$withval"; AC_MSG_RESULT($MACARCH), + MACARCH="current"; AC_MSG_RESULT(defaulting to $MACARCH)) + + AC_MSG_CHECKING(--with-developer-dir argument) + AC_ARG_WITH(developer-dir, [ --with-developer-dir=PATH use PATH as location for Xcode developer tools], + DEVELOPER_DIR="$withval"; AC_MSG_RESULT($DEVELOPER_DIR), + AC_MSG_RESULT(not present)) + + if test "x$DEVELOPER_DIR" = "x"; then + AC_PATH_PROG(XCODE_SELECT, xcode-select) + if test "x$XCODE_SELECT" != "x"; then + AC_MSG_CHECKING(for developer dir using xcode-select) + DEVELOPER_DIR=`$XCODE_SELECT -print-path` + AC_MSG_RESULT([$DEVELOPER_DIR]) + else + DEVELOPER_DIR=/Developer + fi + fi + + if test "x$MACARCH" = "xboth"; then + AC_MSG_CHECKING(for 10.4 universal SDK) + dnl There is a terrible inconsistency (but we appear to get away with it): + dnl $CFLAGS uses the 10.4u SDK library for the headers, while $CPPFLAGS + dnl doesn't, because "gcc -E" doesn't grok it. That means the configure + dnl tests using the preprocessor are actually done with the wrong header + dnl files. $LDFLAGS is set at the end, because configure uses it together + dnl with $CFLAGS and we can only have one -sysroot argument. + save_cppflags="$CPPFLAGS" + save_cflags="$CFLAGS" + save_ldflags="$LDFLAGS" + CFLAGS="$CFLAGS -isysroot $DEVELOPER_DIR/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc" + AC_TRY_LINK([ ], [ ], + AC_MSG_RESULT(found, will make universal binary), + + AC_MSG_RESULT(not found) + CFLAGS="$save_cflags" + AC_MSG_CHECKING(if Intel architecture is supported) + CPPFLAGS="$CPPFLAGS -arch i386" + LDFLAGS="$save_ldflags -arch i386" + AC_TRY_LINK([ ], [ ], + AC_MSG_RESULT(yes); MACARCH="intel", + AC_MSG_RESULT(no, using PowerPC) + MACARCH="ppc" + CPPFLAGS="$save_cppflags -arch ppc" + LDFLAGS="$save_ldflags -arch ppc")) + elif test "x$MACARCH" = "xintel"; then + CPPFLAGS="$CPPFLAGS -arch intel" + LDFLAGS="$LDFLAGS -arch intel" + elif test "x$MACARCH" = "xppc"; then + CPPFLAGS="$CPPFLAGS -arch ppc" + LDFLAGS="$LDFLAGS -arch ppc" + fi + + if test "$enable_darwin" = "yes"; then + MACOS_X_DARWIN=yes + OS_EXTRA_SRC="os_macosx.m os_mac_conv.c"; + OS_EXTRA_OBJ="objects/os_macosx.o objects/os_mac_conv.o" + dnl TODO: use -arch i386 on Intel machines + dnl Removed -no-cpp-precomp, only for very old compilers. + CPPFLAGS="$CPPFLAGS -DMACOS_X_DARWIN" + + dnl If Carbon is found, assume we don't want X11 + dnl unless it was specifically asked for (--with-x) + dnl or Motif, Athena or GTK GUI is used. + AC_CHECK_HEADER(Carbon/Carbon.h, CARBON=yes) + if test "x$CARBON" = "xyes"; then + if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3; then + with_x=no + fi + fi + fi + + dnl Avoid a bug with -O2 with gcc 4.0.1. Symptom: malloc() reports double + dnl free. This happens in expand_filename(), because the optimizer swaps + dnl two blocks of code, both using "repl", that can't be swapped. + if test "$MACARCH" = "intel" -o "$MACARCH" = "both"; then + CFLAGS=`echo "$CFLAGS" | sed 's/-O[[23456789]]/-Oz/'` + fi + +else + AC_MSG_RESULT(no) +fi + +dnl Mac OS X 10.9+ no longer include AvailabilityMacros.h in Carbon +dnl so we need to include it to have access to version macros. +AC_CHECK_HEADERS(AvailabilityMacros.h) + +AC_SUBST(OS_EXTRA_SRC) +AC_SUBST(OS_EXTRA_OBJ) + +dnl Add /usr/local/lib to $LDFLAGS and /usr/local/include to CFLAGS. +dnl Only when the directory exists and it wasn't there yet. +dnl For gcc don't do this when it is already in the default search path. +dnl Skip all of this when cross-compiling. +if test "$cross_compiling" = no; then + AC_MSG_CHECKING(--with-local-dir argument) + have_local_include='' + have_local_lib='' + AC_ARG_WITH([local-dir], [ --with-local-dir=PATH search PATH instead of /usr/local for local libraries. + --without-local-dir do not search /usr/local for local libraries.], [ + local_dir="$withval" + case "$withval" in + */*) ;; + no) + # avoid adding local dir to LDFLAGS and CPPFLAGS + have_local_include=yes + have_local_lib=yes + ;; + *) AC_MSG_ERROR(must pass path argument to --with-local-dir) ;; + esac + AC_MSG_RESULT($local_dir) + ], [ + local_dir=/usr/local + AC_MSG_RESULT(Defaulting to $local_dir) + ]) + if test "$GCC" = yes -a "$local_dir" != no; then + echo 'void f(){}' > conftest.c + dnl Removed -no-cpp-precomp, only needed for OS X 10.2 (Ben Fowler) + have_local_include=`${CC-cc} -c -v conftest.c 2>&1 | grep "${local_dir}/include"` + have_local_lib=`${CC-cc} -c -v conftest.c 2>&1 | grep "${local_dir}/lib"` + rm -f conftest.c conftest.o + fi + if test -z "$have_local_lib" -a -d "${local_dir}/lib"; then + tt=`echo "$LDFLAGS" | sed -e "s+-L${local_dir}/lib ++g" -e "s+-L${local_dir}/lib$++g"` + if test "$tt" = "$LDFLAGS"; then + LDFLAGS="$LDFLAGS -L${local_dir}/lib" + fi + fi + if test -z "$have_local_include" -a -d "${local_dir}/include"; then + tt=`echo "$CPPFLAGS" | sed -e "s+-I${local_dir}/include ++g" -e "s+-I${local_dir}/include$++g"` + if test "$tt" = "$CPPFLAGS"; then + CPPFLAGS="$CPPFLAGS -I${local_dir}/include" + fi + fi +fi + +AC_MSG_CHECKING(--with-vim-name argument) +AC_ARG_WITH(vim-name, [ --with-vim-name=NAME what to call the Vim executable], + VIMNAME="$withval"; AC_MSG_RESULT($VIMNAME), + VIMNAME="vim"; AC_MSG_RESULT(Defaulting to $VIMNAME)) +AC_SUBST(VIMNAME) +AC_MSG_CHECKING(--with-ex-name argument) +AC_ARG_WITH(ex-name, [ --with-ex-name=NAME what to call the Ex executable], + EXNAME="$withval"; AC_MSG_RESULT($EXNAME), + EXNAME="ex"; AC_MSG_RESULT(Defaulting to ex)) +AC_SUBST(EXNAME) +AC_MSG_CHECKING(--with-view-name argument) +AC_ARG_WITH(view-name, [ --with-view-name=NAME what to call the View executable], + VIEWNAME="$withval"; AC_MSG_RESULT($VIEWNAME), + VIEWNAME="view"; AC_MSG_RESULT(Defaulting to view)) +AC_SUBST(VIEWNAME) + +AC_MSG_CHECKING(--with-global-runtime argument) +AC_ARG_WITH(global-runtime, [ --with-global-runtime=DIR global runtime directory in 'runtimepath', comma-separated for multiple directories], + RUNTIME_GLOBAL="$withval"; AC_MSG_RESULT($withval), + AC_MSG_RESULT(no)) + +if test "X$RUNTIME_GLOBAL" != "X"; then + RUNTIME_GLOBAL_AFTER=$(printf -- "$RUNTIME_GLOBAL\\n" | $AWK -F, 'BEGIN { comma=0 } { for (i = NF; i > 0; i--) { if (comma) { printf ",%s/after", $i } else { printf "%s/after", $i; comma=1 } } } END { printf "\n" }') + AC_DEFINE_UNQUOTED(RUNTIME_GLOBAL, "$RUNTIME_GLOBAL") + AC_DEFINE_UNQUOTED(RUNTIME_GLOBAL_AFTER, "$RUNTIME_GLOBAL_AFTER") +fi + +AC_MSG_CHECKING(--with-modified-by argument) +AC_ARG_WITH(modified-by, [ --with-modified-by=NAME name of who modified a release version], + AC_MSG_RESULT($withval); AC_DEFINE_UNQUOTED(MODIFIED_BY, "$withval"), + AC_MSG_RESULT(no)) + +dnl Check for EBCDIC stolen from the LYNX port to z/OS Unix +AC_MSG_CHECKING(if character set is EBCDIC) +AC_TRY_COMPILE([ ], +[ /* TryCompile function for CharSet. + Treat any failure as ASCII for compatibility with existing art. + Use compile-time rather than run-time tests for cross-compiler + tolerance. */ +#if '0'!=240 +make an error "Character set is not EBCDIC" +#endif ], +[ # TryCompile action if true +cf_cv_ebcdic=yes ], +[ # TryCompile action if false +cf_cv_ebcdic=no]) +# end of TryCompile ]) +# end of CacheVal CvEbcdic +AC_MSG_RESULT($cf_cv_ebcdic) +case "$cf_cv_ebcdic" in #(vi + yes) AC_DEFINE(EBCDIC) + line_break='"\\n"' + ;; + *) line_break='"\\012"';; +esac +AC_SUBST(line_break) + +if test "$cf_cv_ebcdic" = "yes"; then +dnl If we have EBCDIC we most likely have z/OS Unix, let's test it! +AC_MSG_CHECKING(for z/OS Unix) +case `uname` in + OS/390) zOSUnix="yes"; + dnl If using cc the environment variable _CC_CCMODE must be + dnl set to "1", so that some compiler extensions are enabled. + dnl If using c89 the environment variable is named _CC_C89MODE. + dnl Note: compile with c89 never tested. + if test "$CC" = "cc"; then + ccm="$_CC_CCMODE" + ccn="CC" + else + if test "$CC" = "c89"; then + ccm="$_CC_C89MODE" + ccn="C89" + else + ccm=1 + fi + fi + if test "$ccm" != "1"; then + echo "" + echo "------------------------------------------" + echo " On z/OS Unix, the environment variable" + echo " _CC_${ccn}MODE must be set to \"1\"!" + echo " Do:" + echo " export _CC_${ccn}MODE=1" + echo " and then call configure again." + echo "------------------------------------------" + exit 1 + fi + # Set CFLAGS for configure process. + # This will be reset later for config.mk. + # Use haltonmsg to force error for missing H files. + CFLAGS="$CFLAGS -D_ALL_SOURCE -Wc,float(ieee),haltonmsg(3296)"; + LDFLAGS="$LDFLAGS -Wl,EDIT=NO" + AC_MSG_RESULT(yes) + ;; + *) zOSUnix="no"; + AC_MSG_RESULT(no) + ;; +esac +fi + +dnl Set QUOTESED. Needs additional backslashes on zOS +if test "$zOSUnix" = "yes"; then + QUOTESED="sed -e 's/[[\\\\\"]]/\\\\\\\\&/g' -e 's/\\\\\\\\\"/\"/' -e 's/\\\\\\\\\";\$\$/\";/'" +else + QUOTESED="sed -e 's/[[\\\\\"]]/\\\\&/g' -e 's/\\\\\"/\"/' -e 's/\\\\\";\$\$/\";/'" +fi +AC_SUBST(QUOTESED) + + +dnl Link with -lsmack for Smack stuff; if not found +AC_MSG_CHECKING(--disable-smack argument) +AC_ARG_ENABLE(smack, + [ --disable-smack Do not check for Smack support.], + , enable_smack="yes") +if test "$enable_smack" = "yes"; then + AC_MSG_RESULT(no) + AC_CHECK_HEADER([linux/xattr.h], true, enable_smack="no") +else + AC_MSG_RESULT(yes) +fi +if test "$enable_smack" = "yes"; then + AC_CHECK_HEADER([attr/xattr.h], true, enable_smack="no") +fi +if test "$enable_smack" = "yes"; then + AC_MSG_CHECKING(for XATTR_NAME_SMACKEXEC in linux/xattr.h) + AC_EGREP_CPP(XATTR_NAME_SMACKEXEC, [#include ], + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no); enable_smack="no") +fi +if test "$enable_smack" = "yes"; then + AC_CHECK_LIB(attr, setxattr, + [LIBS="$LIBS -lattr" + found_smack="yes" + AC_DEFINE(HAVE_SMACK)]) +fi + +dnl When smack was found don't search for SELinux +if test "x$found_smack" = "x"; then + dnl Link with -lselinux for SELinux stuff; if not found + AC_MSG_CHECKING(--disable-selinux argument) + AC_ARG_ENABLE(selinux, + [ --disable-selinux Do not check for SELinux support.], + , enable_selinux="yes") + if test "$enable_selinux" = "yes"; then + AC_MSG_RESULT(no) + AC_CHECK_LIB(selinux, is_selinux_enabled, + [AC_CHECK_HEADER(selinux/selinux.h, + [LIBS="$LIBS -lselinux" + AC_DEFINE(HAVE_SELINUX)])]) + else + AC_MSG_RESULT(yes) + fi +fi + +dnl Check user requested features. + +AC_MSG_CHECKING(--with-features argument) +AC_ARG_WITH(features, [ --with-features=TYPE tiny, small, normal, big or huge (default: huge)], + features="$withval"; AC_MSG_RESULT($features), + features="huge"; AC_MSG_RESULT(Defaulting to huge)) + +dovimdiff="" +dogvimdiff="" +case "$features" in + tiny) AC_DEFINE(FEAT_TINY) ;; + small) AC_DEFINE(FEAT_SMALL) ;; + normal) AC_DEFINE(FEAT_NORMAL) dovimdiff="installvimdiff"; + dogvimdiff="installgvimdiff" ;; + big) AC_DEFINE(FEAT_BIG) dovimdiff="installvimdiff"; + dogvimdiff="installgvimdiff" ;; + huge) AC_DEFINE(FEAT_HUGE) dovimdiff="installvimdiff"; + dogvimdiff="installgvimdiff" ;; + *) AC_MSG_RESULT([Sorry, $features is not supported]) ;; +esac + +AC_SUBST(dovimdiff) +AC_SUBST(dogvimdiff) + +AC_MSG_CHECKING(--with-compiledby argument) +AC_ARG_WITH(compiledby, [ --with-compiledby=NAME name to show in :version message], + compiledby="$withval"; AC_MSG_RESULT($withval), + compiledby=""; AC_MSG_RESULT(no)) +AC_SUBST(compiledby) + +AC_MSG_CHECKING(--disable-xsmp argument) +AC_ARG_ENABLE(xsmp, + [ --disable-xsmp Disable XSMP session management], + , enable_xsmp="yes") + +if test "$enable_xsmp" = "yes"; then + AC_MSG_RESULT(no) + AC_MSG_CHECKING(--disable-xsmp-interact argument) + AC_ARG_ENABLE(xsmp-interact, + [ --disable-xsmp-interact Disable XSMP interaction], + , enable_xsmp_interact="yes") + if test "$enable_xsmp_interact" = "yes"; then + AC_MSG_RESULT(no) + AC_DEFINE(USE_XSMP_INTERACT) + else + AC_MSG_RESULT(yes) + fi +else + AC_MSG_RESULT(yes) +fi + +dnl Check for Lua feature. +AC_MSG_CHECKING(--enable-luainterp argument) +AC_ARG_ENABLE(luainterp, + [ --enable-luainterp[=OPTS] Include Lua interpreter. [default=no] [OPTS=no/yes/dynamic]], , + [enable_luainterp="no"]) +AC_MSG_RESULT($enable_luainterp) + +if test "$enable_luainterp" = "yes" -o "$enable_luainterp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + AC_MSG_ERROR([cannot use Lua with tiny or small features]) + fi + + dnl -- find the lua executable + AC_SUBST(vi_cv_path_lua) + + AC_MSG_CHECKING(--with-lua-prefix argument) + AC_ARG_WITH(lua_prefix, + [ --with-lua-prefix=PFX Prefix where Lua is installed.], + with_lua_prefix="$withval"; AC_MSG_RESULT($with_lua_prefix), + with_lua_prefix="";AC_MSG_RESULT(no)) + + if test "X$with_lua_prefix" != "X"; then + vi_cv_path_lua_pfx="$with_lua_prefix" + else + AC_MSG_CHECKING(LUA_PREFIX environment var) + if test "X$LUA_PREFIX" != "X"; then + AC_MSG_RESULT("$LUA_PREFIX") + vi_cv_path_lua_pfx="$LUA_PREFIX" + else + AC_MSG_RESULT([not set, default to /usr]) + vi_cv_path_lua_pfx="/usr" + fi + fi + + AC_MSG_CHECKING(--with-luajit) + AC_ARG_WITH(luajit, + [ --with-luajit Link with LuaJIT instead of Lua.], + [vi_cv_with_luajit="$withval"], + [vi_cv_with_luajit="no"]) + AC_MSG_RESULT($vi_cv_with_luajit) + + LUA_INC= + if test "X$vi_cv_path_lua_pfx" != "X"; then + if test "x$vi_cv_with_luajit" != "xno"; then + dnl -- try to find LuaJIT executable + AC_PATH_PROG(vi_cv_path_luajit, luajit) + if test "X$vi_cv_path_luajit" != "X"; then + dnl -- find LuaJIT version + AC_CACHE_CHECK(LuaJIT version, vi_cv_version_luajit, + [ vi_cv_version_luajit=`${vi_cv_path_luajit} -v 2>&1 | sed 's/LuaJIT \([[0-9.]]*\)\.[[0-9]]\(-[[a-z0-9]]*\)* .*/\1/'` ]) + AC_CACHE_CHECK(Lua version of LuaJIT, vi_cv_version_lua_luajit, + [ vi_cv_version_lua_luajit=`${vi_cv_path_luajit} -e "print(_VERSION)" | sed 's/.* //'` ]) + vi_cv_path_lua="$vi_cv_path_luajit" + vi_cv_version_lua="$vi_cv_version_lua_luajit" + fi + else + dnl -- try to find Lua executable + AC_PATH_PROG(vi_cv_path_plain_lua, lua) + if test "X$vi_cv_path_plain_lua" != "X"; then + dnl -- find Lua version + AC_CACHE_CHECK(Lua version, vi_cv_version_plain_lua, + [ vi_cv_version_plain_lua=`${vi_cv_path_plain_lua} -e "print(_VERSION)" | sed 's/.* //'` ]) + fi + vi_cv_path_lua="$vi_cv_path_plain_lua" + vi_cv_version_lua="$vi_cv_version_plain_lua" + fi + if test "x$vi_cv_with_luajit" != "xno" && test "X$vi_cv_version_luajit" != "X"; then + AC_MSG_CHECKING(if lua.h can be found in $vi_cv_path_lua_pfx/include/luajit-$vi_cv_version_luajit) + if test -f "$vi_cv_path_lua_pfx/include/luajit-$vi_cv_version_luajit/lua.h"; then + AC_MSG_RESULT(yes) + LUA_INC=/luajit-$vi_cv_version_luajit + fi + fi + if test "X$LUA_INC" = "X"; then + AC_MSG_CHECKING(if lua.h can be found in $vi_cv_path_lua_pfx/include) + if test -f "$vi_cv_path_lua_pfx/include/lua.h"; then + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + AC_MSG_CHECKING(if lua.h can be found in $vi_cv_path_lua_pfx/include/lua$vi_cv_version_lua) + if test -f "$vi_cv_path_lua_pfx/include/lua$vi_cv_version_lua/lua.h"; then + AC_MSG_RESULT(yes) + LUA_INC=/lua$vi_cv_version_lua + else + AC_MSG_RESULT(no) + vi_cv_path_lua_pfx= + fi + fi + fi + fi + + if test "X$vi_cv_path_lua_pfx" != "X"; then + if test "x$vi_cv_with_luajit" != "xno"; then + multiarch=`dpkg-architecture -qDEB_HOST_MULTIARCH 2> /dev/null` + if test "X$multiarch" != "X"; then + lib_multiarch="lib/${multiarch}" + else + lib_multiarch="lib" + fi + if test "X$vi_cv_version_lua" = "X"; then + LUA_LIBS="-L${vi_cv_path_lua_pfx}/${lib_multiarch} -lluajit" + else + LUA_LIBS="-L${vi_cv_path_lua_pfx}/${lib_multiarch} -lluajit-$vi_cv_version_lua" + fi + else + if test "X$LUA_INC" != "X"; then + dnl Test alternate location using version + LUA_LIBS="-L${vi_cv_path_lua_pfx}/lib -llua$vi_cv_version_lua" + else + LUA_LIBS="-L${vi_cv_path_lua_pfx}/lib -llua" + fi + fi + if test "$enable_luainterp" = "dynamic"; then + lua_ok="yes" + else + AC_MSG_CHECKING([if link with ${LUA_LIBS} is sane]) + libs_save=$LIBS + LIBS="$LIBS $LUA_LIBS" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); lua_ok="yes", + AC_MSG_RESULT(no); lua_ok="no"; LUA_LIBS="") + LIBS=$libs_save + fi + if test "x$lua_ok" = "xyes"; then + LUA_CFLAGS="-I${vi_cv_path_lua_pfx}/include${LUA_INC}" + LUA_SRC="if_lua.c" + LUA_OBJ="objects/if_lua.o" + LUA_PRO="if_lua.pro" + AC_DEFINE(FEAT_LUA) + fi + if test "$enable_luainterp" = "dynamic"; then + if test "x$vi_cv_with_luajit" != "xno"; then + luajit="jit" + fi + if test -f "${vi_cv_path_lua_pfx}/bin/cyglua-${vi_cv_version_lua}.dll"; then + vi_cv_dll_name_lua="cyglua-${vi_cv_version_lua}.dll" + else + if test "x$MACOS_X" = "xyes"; then + ext="dylib" + indexes="" + else + ext="so" + indexes=".0 .1 .2 .3 .4 .5 .6 .7 .8 .9" + multiarch=`dpkg-architecture -qDEB_HOST_MULTIARCH 2> /dev/null` + if test "X$multiarch" != "X"; then + lib_multiarch="lib/${multiarch}" + fi + fi + dnl Determine the sover for the current version, but fallback to + dnl liblua${vi_cv_version_lua}.so if no sover-versioned file is found. + AC_MSG_CHECKING(if liblua${luajit}*.${ext}* can be found in $vi_cv_path_lua_pfx) + for subdir in "${lib_multiarch}" lib64 lib; do + if test -z "$subdir"; then + continue + fi + for sover in "${vi_cv_version_lua}.${ext}" "-${vi_cv_version_lua}.${ext}" \ + ".${vi_cv_version_lua}.${ext}" ".${ext}.${vi_cv_version_lua}"; do + for i in $indexes ""; do + if test -f "${vi_cv_path_lua_pfx}/${subdir}/liblua${luajit}${sover}$i"; then + sover2="$i" + break 3 + fi + done + done + sover="" + done + if test "X$sover" = "X"; then + AC_MSG_RESULT(no) + lua_ok="no" + vi_cv_dll_name_lua="liblua${luajit}.${ext}" + else + AC_MSG_RESULT(yes) + lua_ok="yes" + vi_cv_dll_name_lua="liblua${luajit}${sover}$sover2" + fi + fi + AC_DEFINE(DYNAMIC_LUA) + LUA_LIBS="" + LUA_CFLAGS="-DDYNAMIC_LUA_DLL=\\\"${vi_cv_dll_name_lua}\\\" $LUA_CFLAGS" + fi + if test "X$LUA_CFLAGS$LUA_LIBS" != "X" && \ + test "x$MACOS_X" = "xyes" && test "x$vi_cv_with_luajit" != "xno" && \ + test "`(uname -m) 2>/dev/null`" = "x86_64"; then + dnl OSX/x64 requires these flags. See http://luajit.org/install.html + LUA_LIBS="-pagezero_size 10000 -image_base 100000000 $LUA_LIBS" + fi + fi + if test "$fail_if_missing" = "yes" -a "$lua_ok" != "yes"; then + AC_MSG_ERROR([could not configure lua]) + fi + AC_SUBST(LUA_SRC) + AC_SUBST(LUA_OBJ) + AC_SUBST(LUA_PRO) + AC_SUBST(LUA_LIBS) + AC_SUBST(LUA_CFLAGS) +fi + + +dnl Check for MzScheme feature. +AC_MSG_CHECKING(--enable-mzschemeinterp argument) +AC_ARG_ENABLE(mzschemeinterp, + [ --enable-mzschemeinterp Include MzScheme interpreter.], , + [enable_mzschemeinterp="no"]) +AC_MSG_RESULT($enable_mzschemeinterp) + +if test "$enable_mzschemeinterp" = "yes"; then + dnl -- find the mzscheme executable + AC_SUBST(vi_cv_path_mzscheme) + + AC_MSG_CHECKING(--with-plthome argument) + AC_ARG_WITH(plthome, + [ --with-plthome=PLTHOME Use PLTHOME.], + with_plthome="$withval"; AC_MSG_RESULT($with_plthome), + with_plthome="";AC_MSG_RESULT("no")) + + if test "X$with_plthome" != "X"; then + vi_cv_path_mzscheme_pfx="$with_plthome" + vi_cv_path_mzscheme="${vi_cv_path_mzscheme_pfx}/bin/mzscheme" + else + AC_MSG_CHECKING(PLTHOME environment var) + if test "X$PLTHOME" != "X"; then + AC_MSG_RESULT("$PLTHOME") + vi_cv_path_mzscheme_pfx="$PLTHOME" + vi_cv_path_mzscheme="${vi_cv_path_mzscheme_pfx}/bin/mzscheme" + else + AC_MSG_RESULT(not set) + dnl -- try to find MzScheme executable + AC_PATH_PROG(vi_cv_path_mzscheme, mzscheme) + + dnl resolve symbolic link, the executable is often elsewhere and there + dnl are no links for the include files. + if test "X$vi_cv_path_mzscheme" != "X"; then + lsout=`ls -l $vi_cv_path_mzscheme` + if echo "$lsout" | grep -e '->' >/dev/null 2>/dev/null; then + vi_cv_path_mzscheme=`echo "$lsout" | sed 's/.*-> \(.*\)/\1/'` + fi + fi + + if test "X$vi_cv_path_mzscheme" != "X"; then + dnl -- find where MzScheme thinks it was installed + AC_CACHE_CHECK(MzScheme install prefix,vi_cv_path_mzscheme_pfx, + dnl different versions of MzScheme differ in command line processing + dnl use universal approach + echo "(display (simplify-path \ + (build-path (call-with-values \ + (lambda () (split-path (find-system-path (quote exec-file)))) \ + (lambda (base name must-be-dir?) base)) (quote up))))" > mzdirs.scm + dnl Remove a trailing slash + [ vi_cv_path_mzscheme_pfx=`${vi_cv_path_mzscheme} -r mzdirs.scm | \ + sed -e 's+/$++'` ]) + rm -f mzdirs.scm + fi + fi + fi + + if test "X$vi_cv_path_mzscheme_pfx" != "X"; then + AC_MSG_CHECKING(for racket include directory) + SCHEME_INC=`${vi_cv_path_mzscheme} -e '(require setup/dirs)(let ((p (find-include-dir))) (when (path? p) (display p)))'` + if test "X$SCHEME_INC" != "X"; then + AC_MSG_RESULT(${SCHEME_INC}) + else + AC_MSG_RESULT(not found) + AC_MSG_CHECKING(if scheme.h can be found in $vi_cv_path_mzscheme_pfx/include) + if test -f "$vi_cv_path_mzscheme_pfx/include/scheme.h"; then + SCHEME_INC=${vi_cv_path_mzscheme_pfx}/include + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + AC_MSG_CHECKING(if scheme.h can be found in $vi_cv_path_mzscheme_pfx/include/plt) + if test -f "$vi_cv_path_mzscheme_pfx/include/plt/scheme.h"; then + AC_MSG_RESULT(yes) + SCHEME_INC=${vi_cv_path_mzscheme_pfx}/include/plt + else + AC_MSG_RESULT(no) + AC_MSG_CHECKING(if scheme.h can be found in $vi_cv_path_mzscheme_pfx/include/racket) + if test -f "$vi_cv_path_mzscheme_pfx/include/racket/scheme.h"; then + AC_MSG_RESULT(yes) + SCHEME_INC=${vi_cv_path_mzscheme_pfx}/include/racket + else + AC_MSG_RESULT(no) + AC_MSG_CHECKING(if scheme.h can be found in /usr/include/plt/) + if test -f /usr/include/plt/scheme.h; then + AC_MSG_RESULT(yes) + SCHEME_INC=/usr/include/plt + else + AC_MSG_RESULT(no) + AC_MSG_CHECKING(if scheme.h can be found in /usr/include/racket/) + if test -f /usr/include/racket/scheme.h; then + AC_MSG_RESULT(yes) + SCHEME_INC=/usr/include/racket + else + AC_MSG_RESULT(no) + vi_cv_path_mzscheme_pfx= + fi + fi + fi + fi + fi + fi + fi + + if test "X$vi_cv_path_mzscheme_pfx" != "X"; then + + AC_MSG_CHECKING(for racket lib directory) + SCHEME_LIB=`${vi_cv_path_mzscheme} -e '(require setup/dirs)(let ((p (find-lib-dir))) (when (path? p) (display p)))'` + if test "X$SCHEME_LIB" != "X"; then + AC_MSG_RESULT(${SCHEME_LIB}) + else + AC_MSG_RESULT(not found) + fi + + for path in "${vi_cv_path_mzscheme_pfx}/lib" "${SCHEME_LIB}"; do + if test "X$path" != "X"; then + if test "x$MACOS_X" = "xyes"; then + MZSCHEME_LIBS="-framework Racket" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libmzscheme3m.a"; then + MZSCHEME_LIBS="${path}/libmzscheme3m.a" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libracket3m.a"; then + MZSCHEME_LIBS="${path}/libracket3m.a" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libracket.a"; then + MZSCHEME_LIBS="${path}/libracket.a ${path}/libmzgc.a" + elif test -f "${path}/libmzscheme.a"; then + MZSCHEME_LIBS="${path}/libmzscheme.a ${path}/libmzgc.a" + else + dnl Using shared objects + if test -f "${path}/libmzscheme3m.so"; then + MZSCHEME_LIBS="-L${path} -lmzscheme3m" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libracket3m.so"; then + MZSCHEME_LIBS="-L${path} -lracket3m" + MZSCHEME_CFLAGS="-DMZ_PRECISE_GC" + elif test -f "${path}/libracket.so"; then + MZSCHEME_LIBS="-L${path} -lracket -lmzgc" + else + dnl try next until last + if test "$path" != "$SCHEME_LIB"; then + continue + fi + MZSCHEME_LIBS="-L${path} -lmzscheme -lmzgc" + fi + if test "$GCC" = yes; then + dnl Make Vim remember the path to the library. For when it's not in + dnl $LD_LIBRARY_PATH. + MZSCHEME_LIBS="${MZSCHEME_LIBS} -Wl,-rpath -Wl,${path}" + elif test "`(uname) 2>/dev/null`" = SunOS && + uname -r | grep '^5' >/dev/null; then + MZSCHEME_LIBS="${MZSCHEME_LIBS} -R ${path}" + fi + fi + fi + if test "X$MZSCHEME_LIBS" != "X"; then + break + fi + done + + AC_MSG_CHECKING([if racket requires -pthread]) + if test "X$SCHEME_LIB" != "X" && $FGREP -e -pthread "$SCHEME_LIB/buildinfo" >/dev/null ; then + AC_MSG_RESULT(yes) + MZSCHEME_LIBS="${MZSCHEME_LIBS} -pthread" + MZSCHEME_CFLAGS="${MZSCHEME_CFLAGS} -pthread" + else + AC_MSG_RESULT(no) + fi + + AC_MSG_CHECKING(for racket config directory) + SCHEME_CONFIGDIR=`${vi_cv_path_mzscheme} -e '(require setup/dirs)(let ((p (find-config-dir))) (when (path? p) (display p)))'` + if test "X$SCHEME_CONFIGDIR" != "X"; then + MZSCHEME_CFLAGS="${MZSCHEME_CFLAGS} -DMZSCHEME_CONFIGDIR='\"${SCHEME_CONFIGDIR}\"'" + AC_MSG_RESULT(${SCHEME_CONFIGDIR}) + else + AC_MSG_RESULT(not found) + fi + + AC_MSG_CHECKING(for racket collects directory) + SCHEME_COLLECTS=`${vi_cv_path_mzscheme} -e '(require setup/dirs)(let ((p (find-collects-dir))) (when (path? p) (let-values (((base _1 _2) (split-path p))) (display base))))'` + if test "X$SCHEME_COLLECTS" = "X"; then + if test -d "$vi_cv_path_mzscheme_pfx/lib/plt/collects"; then + SCHEME_COLLECTS=$vi_cv_path_mzscheme_pfx/lib/plt/ + else + if test -d "$vi_cv_path_mzscheme_pfx/lib/racket/collects"; then + SCHEME_COLLECTS=$vi_cv_path_mzscheme_pfx/lib/racket/ + else + if test -d "$vi_cv_path_mzscheme_pfx/share/racket/collects"; then + SCHEME_COLLECTS=$vi_cv_path_mzscheme_pfx/share/racket/ + else + if test -d "$vi_cv_path_mzscheme_pfx/collects"; then + SCHEME_COLLECTS=$vi_cv_path_mzscheme_pfx/ + fi + fi + fi + fi + fi + if test "X$SCHEME_COLLECTS" != "X" ; then + AC_MSG_RESULT(${SCHEME_COLLECTS}) + else + AC_MSG_RESULT(not found) + fi + + AC_MSG_CHECKING(for mzscheme_base.c) + if test -f "${SCHEME_COLLECTS}collects/scheme/base.ss" ; then + MZSCHEME_EXTRA="mzscheme_base.c" + MZSCHEME_MZC="${vi_cv_path_mzscheme_pfx}/bin/mzc" + MZSCHEME_MOD="++lib scheme/base" + else + if test -f "${SCHEME_COLLECTS}collects/scheme/base.rkt" ; then + MZSCHEME_EXTRA="mzscheme_base.c" + MZSCHEME_MZC="${vi_cv_path_mzscheme_pfx}/bin/mzc" + MZSCHEME_MOD="++lib scheme/base" + else + if test -f "${SCHEME_COLLECTS}collects/racket/base.rkt" ; then + MZSCHEME_EXTRA="mzscheme_base.c" + MZSCHEME_MZC="${vi_cv_path_mzscheme_pfx}/bin/raco ctool" + MZSCHEME_MOD="" + fi + fi + fi + if test "X$MZSCHEME_EXTRA" != "X" ; then + dnl need to generate bytecode for MzScheme base + MZSCHEME_CFLAGS="${MZSCHEME_CFLAGS} -DINCLUDE_MZSCHEME_BASE" + AC_MSG_RESULT(needed) + else + AC_MSG_RESULT(not needed) + fi + + dnl On Ubuntu this fixes "undefined reference to symbol 'ffi_type_void'". + AC_CHECK_LIB(ffi, ffi_type_void, [MZSCHEME_LIBS="$MZSCHEME_LIBS -lffi"]) + + MZSCHEME_CFLAGS="${MZSCHEME_CFLAGS} -I${SCHEME_INC} \ + -DMZSCHEME_COLLECTS='\"${SCHEME_COLLECTS}collects\"'" + + dnl Test that we can compile a simple program with these CFLAGS and LIBS. + AC_MSG_CHECKING([if compile and link flags for MzScheme are sane]) + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $MZSCHEME_CFLAGS" + LIBS="$LIBS $MZSCHEME_LIBS" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); mzs_ok=yes, + AC_MSG_RESULT(no: MZSCHEME DISABLED); mzs_ok=no) + CFLAGS=$cflags_save + LIBS=$libs_save + if test $mzs_ok = yes; then + MZSCHEME_SRC="if_mzsch.c" + MZSCHEME_OBJ="objects/if_mzsch.o" + MZSCHEME_PRO="if_mzsch.pro" + AC_DEFINE(FEAT_MZSCHEME) + else + MZSCHEME_CFLAGS= + MZSCHEME_LIBS= + MZSCHEME_EXTRA= + MZSCHEME_MZC= + fi + fi + AC_SUBST(MZSCHEME_SRC) + AC_SUBST(MZSCHEME_OBJ) + AC_SUBST(MZSCHEME_PRO) + AC_SUBST(MZSCHEME_LIBS) + AC_SUBST(MZSCHEME_CFLAGS) + AC_SUBST(MZSCHEME_EXTRA) + AC_SUBST(MZSCHEME_MZC) +fi + + +AC_MSG_CHECKING(--enable-perlinterp argument) +AC_ARG_ENABLE(perlinterp, + [ --enable-perlinterp[=OPTS] Include Perl interpreter. [default=no] [OPTS=no/yes/dynamic]], , + [enable_perlinterp="no"]) +AC_MSG_RESULT($enable_perlinterp) +if test "$enable_perlinterp" = "yes" -o "$enable_perlinterp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + AC_MSG_ERROR([cannot use Perl with tiny or small features]) + fi + AC_SUBST(vi_cv_path_perl) + AC_PATH_PROG(vi_cv_path_perl, perl) + if test "X$vi_cv_path_perl" != "X"; then + AC_MSG_CHECKING(Perl version) + if $vi_cv_path_perl -e 'require 5.003_01' >/dev/null 2>/dev/null; then + eval `$vi_cv_path_perl -V:usethreads` + eval `$vi_cv_path_perl -V:libperl` + if test "X$usethreads" = "XUNKNOWN" -o "X$usethreads" = "Xundef"; then + badthreads=no + else + if $vi_cv_path_perl -e 'require 5.6.0' >/dev/null 2>/dev/null; then + eval `$vi_cv_path_perl -V:use5005threads` + if test "X$use5005threads" = "XUNKNOWN" -o "X$use5005threads" = "Xundef"; then + badthreads=no + else + badthreads=yes + AC_MSG_RESULT(>>> Perl > 5.6 with 5.5 threads cannot be used <<<) + fi + else + badthreads=yes + AC_MSG_RESULT(>>> Perl 5.5 with threads cannot be used <<<) + fi + fi + if test $badthreads = no; then + AC_MSG_RESULT(OK) + eval `$vi_cv_path_perl -V:shrpenv` + if test "X$shrpenv" = "XUNKNOWN"; then # pre 5.003_04 + shrpenv="" + fi + vi_cv_perllib=`$vi_cv_path_perl -MConfig -e 'print $Config{privlibexp}'` + AC_SUBST(vi_cv_perllib) + vi_cv_perl_extutils=unknown_perl_extutils_path + for extutils_rel_path in ExtUtils vendor_perl/ExtUtils; do + xsubpp_path="$vi_cv_perllib/$extutils_rel_path/xsubpp" + if test -f "$xsubpp_path"; then + vi_cv_perl_xsubpp="$xsubpp_path" + fi + done + AC_SUBST(vi_cv_perl_xsubpp) + dnl Remove "-fno-something", it breaks using cproto. + dnl Remove "-fdebug-prefix-map", it isn't supported by clang. + dnl Remove "FORTIFY_SOURCE", it will be defined twice. + dnl remove -pipe and -Wxxx, it confuses cproto + perlcppflags=`$vi_cv_path_perl -Mlib=$srcdir -MExtUtils::Embed \ + -e 'ccflags;perl_inc;print"\n"' | sed -e 's/-fno[[^ ]]*//' \ + -e 's/-fdebug-prefix-map[[^ ]]*//g' \ + -e 's/-pipe //' \ + -e 's/-W[[^ ]]*//g' \ + -e 's/-D_FORTIFY_SOURCE=.//g'` + dnl Remove "-lc", it breaks on FreeBSD when using "-pthread". + perllibs=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed -e 'ldopts' | \ + sed -e '/Warning/d' -e '/Note (probably harmless)/d' \ + -e 's/-bE:perl.exp//' -e 's/-lc //'` + dnl Don't add perl lib to $LIBS: if it's not in LD_LIBRARY_PATH + dnl a test in configure may fail because of that. + perlldflags=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed \ + -e 'ccdlflags' | sed -e 's/-bE:perl.exp//'` + + dnl check that compiling a simple program still works with the flags + dnl added for Perl. + AC_MSG_CHECKING([if compile and link flags for Perl are sane]) + cflags_save=$CFLAGS + libs_save=$LIBS + ldflags_save=$LDFLAGS + CFLAGS="$CFLAGS $perlcppflags" + LIBS="$LIBS $perllibs" + perlldflags=`echo "$perlldflags" | sed -e 's/^ *//g'` + LDFLAGS="$perlldflags $LDFLAGS" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); perl_ok=yes, + AC_MSG_RESULT(no: PERL DISABLED); perl_ok=no) + CFLAGS=$cflags_save + LIBS=$libs_save + LDFLAGS=$ldflags_save + if test $perl_ok = yes; then + if test "X$perlcppflags" != "X"; then + PERL_CFLAGS=$perlcppflags + fi + if test "X$perlldflags" != "X"; then + if test "X`echo \"$LDFLAGS\" | $FGREP -e \"$perlldflags\"`" = "X"; then + LDFLAGS="$perlldflags $LDFLAGS" + fi + fi + PERL_LIBS=$perllibs + PERL_SRC="auto/if_perl.c if_perlsfio.c" + PERL_OBJ="objects/if_perl.o objects/if_perlsfio.o" + PERL_PRO="if_perl.pro if_perlsfio.pro" + AC_DEFINE(FEAT_PERL) + fi + fi + else + AC_MSG_RESULT(>>> too old; need Perl version 5.003_01 or later <<<) + fi + fi + + if test "x$MACOS_X" = "xyes"; then + dnl Mac OS X 10.2 or later + dir=/System/Library/Perl + darwindir=$dir/darwin + if test -d $darwindir; then + PERL=/usr/bin/perl + else + dnl Mac OS X 10.3 + dir=/System/Library/Perl/5.8.1 + darwindir=$dir/darwin-thread-multi-2level + if test -d $darwindir; then + PERL=/usr/bin/perl + fi + fi + if test -n "$PERL"; then + PERL_DIR="$dir" + PERL_CFLAGS="-DFEAT_PERL -I$darwindir/CORE" + PERL_OBJ="objects/if_perl.o objects/if_perlsfio.o $darwindir/auto/DynaLoader/DynaLoader.a" + PERL_LIBS="-L$darwindir/CORE -lperl" + fi + dnl Perl on Mac OS X 10.5 adds "-arch" flags but these should only + dnl be included if requested by passing --with-mac-arch to + dnl configure, so strip these flags first (if present) + PERL_LIBS=`echo "$PERL_LIBS" | sed -e 's/-arch\ ppc//' -e 's/-arch\ i386//' -e 's/-arch\ x86_64//'` + PERL_CFLAGS=`echo "$PERL_CFLAGS" | sed -e 's/-arch\ ppc//' -e 's/-arch\ i386//' -e 's/-arch\ x86_64//'` + fi + if test "$enable_perlinterp" = "dynamic"; then + if test "$perl_ok" = "yes" -a "X$libperl" != "X"; then + AC_DEFINE(DYNAMIC_PERL) + PERL_CFLAGS="-DDYNAMIC_PERL_DLL=\\\"$libperl\\\" $PERL_CFLAGS" + fi + fi + + if test "$fail_if_missing" = "yes" -a "$perl_ok" != "yes"; then + AC_MSG_ERROR([could not configure perl]) + fi +fi +AC_SUBST(shrpenv) +AC_SUBST(PERL_SRC) +AC_SUBST(PERL_OBJ) +AC_SUBST(PERL_PRO) +AC_SUBST(PERL_CFLAGS) +AC_SUBST(PERL_LIBS) + +AC_MSG_CHECKING(--enable-pythoninterp argument) +AC_ARG_ENABLE(pythoninterp, + [ --enable-pythoninterp[=OPTS] Include Python interpreter. [default=no] [OPTS=no/yes/dynamic]], , + [enable_pythoninterp="no"]) +AC_MSG_RESULT($enable_pythoninterp) +if test "$enable_pythoninterp" = "yes" -o "$enable_pythoninterp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + AC_MSG_ERROR([cannot use Python with tiny or small features]) + fi + + dnl -- find the python executable + AC_MSG_CHECKING(--with-python-command argument) + AC_SUBST(vi_cv_path_python) + AC_ARG_WITH(python-command, [ --with-python-command=NAME name of the Python 2 command (default: python2 or python)], + vi_cv_path_python="$withval"; AC_MSG_RESULT($vi_cv_path_python), + AC_MSG_RESULT(no)) + + if test "X$vi_cv_path_python" = "X"; then + AC_PATH_PROGS(vi_cv_path_python, python2 python) + fi + if test "X$vi_cv_path_python" != "X"; then + + dnl -- get its version number + AC_CACHE_CHECK(Python version,vi_cv_var_python_version, + [[vi_cv_var_python_version=` + ${vi_cv_path_python} -c 'import sys; print sys.version[:3]'` + ]]) + + dnl -- it must be at least version 2.3 + AC_MSG_CHECKING(Python is 2.3 or better) + if ${vi_cv_path_python} -c \ + "import sys; sys.exit(${vi_cv_var_python_version} < 2.3)" + then + AC_MSG_RESULT(yep) + + dnl -- find where python thinks it was installed + AC_CACHE_CHECK(Python's install prefix,vi_cv_path_python_pfx, + [ vi_cv_path_python_pfx=` + ${vi_cv_path_python} -c \ + "import sys; print sys.prefix"` ]) + + dnl -- and where it thinks it runs + AC_CACHE_CHECK(Python's execution prefix,vi_cv_path_python_epfx, + [ vi_cv_path_python_epfx=` + ${vi_cv_path_python} -c \ + "import sys; print sys.exec_prefix"` ]) + + dnl -- python's internal library path + + AC_CACHE_VAL(vi_cv_path_pythonpath, + [ vi_cv_path_pythonpath=` + unset PYTHONPATH; + ${vi_cv_path_python} -c \ + "import sys, string; print string.join(sys.path,':')"` ]) + + dnl -- where the Python implementation library archives are + + AC_ARG_WITH(python-config-dir, + [ --with-python-config-dir=PATH Python's config directory (deprecated)], + [ vi_cv_path_python_conf="${withval}"; have_python_config_dir=1 ] ) + + AC_CACHE_CHECK(Python's configuration directory,vi_cv_path_python_conf, + [ + vi_cv_path_python_conf= + d=`${vi_cv_path_python} -c "import distutils.sysconfig; print distutils.sysconfig.get_config_var('LIBPL')"` + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python_conf="$d" + else + for path in "${vi_cv_path_python_pfx}" "${vi_cv_path_python_epfx}"; do + for subdir in lib64 lib share; do + d="${path}/${subdir}/python${vi_cv_var_python_version}/config" + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python_conf="$d" + fi + done + done + fi + ]) + + PYTHON_CONFDIR="${vi_cv_path_python_conf}" + + if test "X$PYTHON_CONFDIR" = "X"; then + AC_MSG_RESULT([can't find it!]) + else + + dnl -- we need to examine Python's config/Makefile too + dnl see what the interpreter is built from + AC_CACHE_VAL(vi_cv_path_python_plibs, + [ + pwd=`pwd` + tmp_mkf="$pwd/config-PyMake$$" + cat -- "${PYTHON_CONFDIR}/Makefile" - <<'eof' >"${tmp_mkf}" +__: + @echo "python_BASEMODLIBS='$(BASEMODLIBS)'" + @echo "python_LIBS='$(LIBS)'" + @echo "python_SYSLIBS='$(SYSLIBS)'" + @echo "python_LINKFORSHARED='$(LINKFORSHARED)'" + @echo "python_DLLLIBRARY='$(DLLLIBRARY)'" + @echo "python_INSTSONAME='$(INSTSONAME)'" + @echo "python_PYTHONFRAMEWORK='$(PYTHONFRAMEWORK)'" + @echo "python_PYTHONFRAMEWORKPREFIX='$(PYTHONFRAMEWORKPREFIX)'" + @echo "python_PYTHONFRAMEWORKINSTALLDIR='$(PYTHONFRAMEWORKINSTALLDIR)'" +eof + dnl -- delete the lines from make about Entering/Leaving directory + eval "`cd ${PYTHON_CONFDIR} && make -f "${tmp_mkf}" __ | sed '/ directory /d'`" + rm -f -- "${tmp_mkf}" + if test "x$MACOS_X" = "xyes" && test -n "${python_PYTHONFRAMEWORK}" && ${vi_cv_path_python} -c \ + "import sys; sys.exit(${vi_cv_var_python_version} < 2.3)"; then + vi_cv_path_python_plibs="-framework Python" + if test "x${vi_cv_path_python}" != "x/usr/bin/python" && test -n "${python_PYTHONFRAMEWORKPREFIX}"; then + vi_cv_path_python_plibs="-F${python_PYTHONFRAMEWORKPREFIX} -framework Python" + fi + else + vi_cv_path_python_plibs="-L${PYTHON_CONFDIR} -lpython${vi_cv_var_python_version}" + dnl -- Check if the path contained in python_LINKFORSHARED is + dnl usable for vim build. If not, make and try other + dnl candidates. + if test -n "${python_LINKFORSHARED}" && test -n "${python_PYTHONFRAMEWORKPREFIX}"; then + python_link_symbol=`echo ${python_LINKFORSHARED} | sed 's/\([[^ \t]][[^ \t]]*[[ \t]][[ \t]]*[[^ \t]][[^ \t]]*\)[[ \t]].*/\1/'` + python_link_path=`echo ${python_LINKFORSHARED} | sed 's/\([[^ \t]][[^ \t]]*[[ \t]][[ \t]]*[[^ \t]][[^ \t]]*\)[[ \t]][[ \t]]*\(.*\)/\2/'` + if test -n "${python_link_path}" && ! test -x "${python_link_path}"; then + dnl -- The path looks relative. Guess the absolute one using + dnl the prefix and try that. + python_link_path="${python_PYTHONFRAMEWORKPREFIX}/${python_link_path}" + if test -n "${python_link_path}" && ! test -x "${python_link_path}"; then + dnl -- A last resort. + python_link_path="${python_PYTHONFRAMEWORKINSTALLDIR}/Versions/${vi_cv_var_python_version}/${python_PYTHONFRAMEWORK}" + dnl -- No check is done. The last word is left to the + dnl "sanity" test on link flags that follows shortly. + fi + python_LINKFORSHARED="${python_link_symbol} ${python_link_path}" + fi + fi + vi_cv_path_python_plibs="${vi_cv_path_python_plibs} ${python_BASEMODLIBS} ${python_LIBS} ${python_SYSLIBS} ${python_LINKFORSHARED}" + dnl remove -ltermcap, it can conflict with an earlier -lncurses + vi_cv_path_python_plibs=`echo $vi_cv_path_python_plibs | sed s/-ltermcap//` + fi + ]) + AC_CACHE_CHECK(Python's dll name,vi_cv_dll_name_python, + [ + if test "X$python_DLLLIBRARY" != "X"; then + vi_cv_dll_name_python="$python_DLLLIBRARY" + else + vi_cv_dll_name_python="$python_INSTSONAME" + fi + ]) + + PYTHON_LIBS="${vi_cv_path_python_plibs}" + if test "${vi_cv_path_python_pfx}" = "${vi_cv_path_python_epfx}"; then + PYTHON_CFLAGS="-I${vi_cv_path_python_pfx}/include/python${vi_cv_var_python_version}" + else + PYTHON_CFLAGS="-I${vi_cv_path_python_pfx}/include/python${vi_cv_var_python_version} -I${vi_cv_path_python_epfx}/include/python${vi_cv_var_python_version}" + fi + if test "X$have_python_config_dir" = "X1" -a "$enable_pythoninterp" = "dynamic"; then + dnl Define PYTHON_HOME if --with-python-config-dir was used + PYTHON_CFLAGS="${PYTHON_CFLAGS} -DPYTHON_HOME='\"${vi_cv_path_python_pfx}\"'" + + fi + PYTHON_SRC="if_python.c" + PYTHON_OBJ="objects/if_python.o" + + dnl On FreeBSD linking with "-pthread" is required to use threads. + dnl _THREAD_SAFE must be used for compiling then. + dnl The "-pthread" is added to $LIBS, so that the following check for + dnl sigaltstack() will look in libc_r (it's there in libc!). + dnl Otherwise, when using GCC, try adding -pthread to $CFLAGS. GCC + dnl will then define target-specific defines, e.g., -D_REENTRANT. + dnl Don't do this for Mac OSX, -pthread will generate a warning. + AC_MSG_CHECKING([if -pthread should be used]) + threadsafe_flag= + thread_lib= + dnl if test "x$MACOS_X" != "xyes"; then + if test "`(uname) 2>/dev/null`" != Darwin; then + test "$GCC" = yes && threadsafe_flag="-pthread" + if test "`(uname) 2>/dev/null`" = FreeBSD; then + threadsafe_flag="-D_THREAD_SAFE" + thread_lib="-pthread" + fi + if test "`(uname) 2>/dev/null`" = SunOS; then + threadsafe_flag="-pthreads" + fi + fi + libs_save_old=$LIBS + if test -n "$threadsafe_flag"; then + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $threadsafe_flag" + LIBS="$LIBS $thread_lib" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); PYTHON_CFLAGS="$PYTHON_CFLAGS $threadsafe_flag", + AC_MSG_RESULT(no); LIBS=$libs_save_old + ) + CFLAGS=$cflags_save + else + AC_MSG_RESULT(no) + fi + + dnl Check that compiling a simple program still works with the flags + dnl added for Python. + AC_MSG_CHECKING([if compile and link flags for Python are sane]) + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON_CFLAGS" + LIBS="$LIBS $PYTHON_LIBS" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); python_ok=yes, + AC_MSG_RESULT(no: PYTHON DISABLED); python_ok=no) + CFLAGS=$cflags_save + LIBS=$libs_save + if test $python_ok = yes; then + AC_DEFINE(FEAT_PYTHON) + else + LIBS=$libs_save_old + PYTHON_SRC= + PYTHON_OBJ= + PYTHON_LIBS= + PYTHON_CFLAGS= + fi + fi + else + AC_MSG_RESULT(too old) + fi + fi + + if test "$fail_if_missing" = "yes" -a "$python_ok" != "yes"; then + AC_MSG_ERROR([could not configure python]) + fi +fi + +AC_SUBST(PYTHON_LIBS) +AC_SUBST(PYTHON_CFLAGS) +AC_SUBST(PYTHON_SRC) +AC_SUBST(PYTHON_OBJ) + + +AC_MSG_CHECKING(--enable-python3interp argument) +AC_ARG_ENABLE(python3interp, + [ --enable-python3interp[=OPTS] Include Python3 interpreter. [default=no] [OPTS=no/yes/dynamic]], , + [enable_python3interp="no"]) +AC_MSG_RESULT($enable_python3interp) +if test "$enable_python3interp" = "yes" -o "$enable_python3interp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + AC_MSG_ERROR([cannot use Python with tiny or small features]) + fi + + dnl -- find the python3 executable + AC_MSG_CHECKING(--with-python3-command argument) + AC_SUBST(vi_cv_path_python3) + AC_ARG_WITH(python3-command, [ --with-python3-command=NAME name of the Python 3 command (default: python3 or python)], + vi_cv_path_python3="$withval"; AC_MSG_RESULT($vi_cv_path_python3), + AC_MSG_RESULT(no)) + + if test "X$vi_cv_path_python3" = "X"; then + AC_PATH_PROGS(vi_cv_path_python3, python3 python) + fi + if test "X$vi_cv_path_python3" != "X"; then + + dnl -- get its version number + AC_CACHE_CHECK(Python version,vi_cv_var_python3_version, + [[vi_cv_var_python3_version=` + ${vi_cv_path_python3} -c 'import sys; print(sys.version[:3])'` + ]]) + + dnl -- it must be at least version 3 + AC_MSG_CHECKING(Python is 3.0 or better) + if ${vi_cv_path_python3} -c \ + "import sys; sys.exit(${vi_cv_var_python3_version} < 3.0)" + then + AC_MSG_RESULT(yep) + + dnl -- get abiflags for python 3.2 or higher (PEP 3149) + AC_CACHE_CHECK(Python's abiflags,vi_cv_var_python3_abiflags, + [ + vi_cv_var_python3_abiflags= + if ${vi_cv_path_python3} -c \ + "import sys; sys.exit(${vi_cv_var_python3_version} < 3.2)" + then + vi_cv_var_python3_abiflags=`${vi_cv_path_python3} -c \ + "import sys; print(sys.abiflags)"` + fi ]) + + dnl -- find where python3 thinks it was installed + AC_CACHE_CHECK(Python's install prefix,vi_cv_path_python3_pfx, + [ vi_cv_path_python3_pfx=` + ${vi_cv_path_python3} -c \ + "import sys; print(sys.prefix)"` ]) + + dnl -- and where it thinks it runs + AC_CACHE_CHECK(Python's execution prefix,vi_cv_path_python3_epfx, + [ vi_cv_path_python3_epfx=` + ${vi_cv_path_python3} -c \ + "import sys; print(sys.exec_prefix)"` ]) + + dnl -- python3's internal library path + + AC_CACHE_VAL(vi_cv_path_python3path, + [ vi_cv_path_python3path=` + unset PYTHONPATH; + ${vi_cv_path_python3} -c \ + "import sys, string; print(':'.join(sys.path))"` ]) + + dnl -- where the Python implementation library archives are + + AC_ARG_WITH(python3-config-dir, + [ --with-python3-config-dir=PATH Python's config directory (deprecated)], + [ vi_cv_path_python3_conf="${withval}"; have_python3_config_dir=1 ] ) + + AC_CACHE_CHECK(Python's configuration directory,vi_cv_path_python3_conf, + [ + vi_cv_path_python3_conf= + config_dir="config-${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}" + d=`${vi_cv_path_python3} -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('LIBPL'))"` + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python3_conf="$d" + else + for path in "${vi_cv_path_python3_pfx}" "${vi_cv_path_python3_epfx}"; do + for subdir in lib64 lib share; do + d="${path}/${subdir}/python${vi_cv_var_python3_version}/${config_dir}" + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python3_conf="$d" + fi + done + done + fi + ]) + + PYTHON3_CONFDIR="${vi_cv_path_python3_conf}" + + if test "X$PYTHON3_CONFDIR" = "X"; then + AC_MSG_RESULT([can't find it!]) + else + + dnl -- we need to examine Python's config/Makefile too + dnl see what the interpreter is built from + AC_CACHE_VAL(vi_cv_path_python3_plibs, + [ + pwd=`pwd` + tmp_mkf="$pwd/config-PyMake$$" + cat -- "${PYTHON3_CONFDIR}/Makefile" - <<'eof' >"${tmp_mkf}" +__: + @echo "python3_BASEMODLIBS='$(BASEMODLIBS)'" + @echo "python3_LIBS='$(LIBS)'" + @echo "python3_SYSLIBS='$(SYSLIBS)'" + @echo "python3_DLLLIBRARY='$(DLLLIBRARY)'" + @echo "python3_INSTSONAME='$(INSTSONAME)'" +eof + dnl -- delete the lines from make about Entering/Leaving directory + eval "`cd ${PYTHON3_CONFDIR} && make -f "${tmp_mkf}" __ | sed '/ directory /d'`" + rm -f -- "${tmp_mkf}" + vi_cv_path_python3_plibs="-L${PYTHON3_CONFDIR} -lpython${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}" + vi_cv_path_python3_plibs="${vi_cv_path_python3_plibs} ${python3_BASEMODLIBS} ${python3_LIBS} ${python3_SYSLIBS}" + dnl remove -ltermcap, it can conflict with an earlier -lncurses + vi_cv_path_python3_plibs=`echo $vi_cv_path_python3_plibs | sed s/-ltermcap//` + vi_cv_path_python3_plibs=`echo $vi_cv_path_python3_plibs | sed s/-lffi//` + ]) + AC_CACHE_CHECK(Python3's dll name,vi_cv_dll_name_python3, + [ + if test "X$python3_DLLLIBRARY" != "X"; then + vi_cv_dll_name_python3="$python3_DLLLIBRARY" + else + vi_cv_dll_name_python3="$python3_INSTSONAME" + fi + ]) + + PYTHON3_LIBS="${vi_cv_path_python3_plibs}" + if test "${vi_cv_path_python3_pfx}" = "${vi_cv_path_python3_epfx}"; then + PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}" + else + PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags} -I${vi_cv_path_python3_epfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}" + fi + if test "X$have_python3_config_dir" = "X1" -a "$enable_python3interp" = "dynamic"; then + dnl Define PYTHON3_HOME if --with-python-config-dir was used + PYTHON3_CFLAGS="${PYTHON3_CFLAGS} -DPYTHON3_HOME='L\"${vi_cv_path_python3_pfx}\"'" + fi + PYTHON3_SRC="if_python3.c" + PYTHON3_OBJ="objects/if_python3.o" + + dnl On FreeBSD linking with "-pthread" is required to use threads. + dnl _THREAD_SAFE must be used for compiling then. + dnl The "-pthread" is added to $LIBS, so that the following check for + dnl sigaltstack() will look in libc_r (it's there in libc!). + dnl Otherwise, when using GCC, try adding -pthread to $CFLAGS. GCC + dnl will then define target-specific defines, e.g., -D_REENTRANT. + dnl Don't do this for Mac OSX, -pthread will generate a warning. + AC_MSG_CHECKING([if -pthread should be used]) + threadsafe_flag= + thread_lib= + dnl if test "x$MACOS_X" != "xyes"; then + if test "`(uname) 2>/dev/null`" != Darwin; then + test "$GCC" = yes && threadsafe_flag="-pthread" + if test "`(uname) 2>/dev/null`" = FreeBSD; then + threadsafe_flag="-D_THREAD_SAFE" + thread_lib="-pthread" + fi + if test "`(uname) 2>/dev/null`" = SunOS; then + threadsafe_flag="-pthreads" + fi + fi + libs_save_old=$LIBS + if test -n "$threadsafe_flag"; then + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $threadsafe_flag" + LIBS="$LIBS $thread_lib" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); PYTHON3_CFLAGS="$PYTHON3_CFLAGS $threadsafe_flag", + AC_MSG_RESULT(no); LIBS=$libs_save_old + ) + CFLAGS=$cflags_save + else + AC_MSG_RESULT(no) + fi + + dnl check that compiling a simple program still works with the flags + dnl added for Python. + AC_MSG_CHECKING([if compile and link flags for Python 3 are sane]) + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON3_CFLAGS" + LIBS="$LIBS $PYTHON3_LIBS" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); python3_ok=yes, + AC_MSG_RESULT(no: PYTHON3 DISABLED); python3_ok=no) + CFLAGS=$cflags_save + LIBS=$libs_save + if test "$python3_ok" = yes; then + AC_DEFINE(FEAT_PYTHON3) + else + LIBS=$libs_save_old + PYTHON3_SRC= + PYTHON3_OBJ= + PYTHON3_LIBS= + PYTHON3_CFLAGS= + fi + fi + else + AC_MSG_RESULT(too old) + fi + fi + if test "$fail_if_missing" = "yes" -a "$python3_ok" != "yes"; then + AC_MSG_ERROR([could not configure python3]) + fi +fi + +AC_SUBST(PYTHON3_LIBS) +AC_SUBST(PYTHON3_CFLAGS) +AC_SUBST(PYTHON3_SRC) +AC_SUBST(PYTHON3_OBJ) + +dnl if python2.x and python3.x are enabled one can only link in code +dnl with dlopen(), dlsym(), dlclose() +if test "$python_ok" = yes && test "$python3_ok" = yes; then + AC_DEFINE(DYNAMIC_PYTHON) + AC_DEFINE(DYNAMIC_PYTHON3) + AC_MSG_CHECKING(whether we can do without RTLD_GLOBAL for Python) + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $PYTHON_CFLAGS" + libs_save=$LIBS + dnl -ldl must go first to make this work on Archlinux (Roland Puntaier) + LIBS="-ldl $LIBS" + AC_RUN_IFELSE([AC_LANG_SOURCE([ + #include + /* If this program fails, then RTLD_GLOBAL is needed. + * RTLD_GLOBAL will be used and then it is not possible to + * have both python versions enabled in the same vim instance. + * Only the first python version used will be switched on. + */ + + int no_rtl_global_needed_for(char *python_instsoname, char *prefix) + { + int needed = 0; + void* pylib = dlopen(python_instsoname, RTLD_LAZY|RTLD_LOCAL); + if (pylib != 0) + { + void (*pfx)(char *home) = dlsym(pylib, "Py_SetPythonHome"); + void (*init)(void) = dlsym(pylib, "Py_Initialize"); + int (*simple)(char*) = dlsym(pylib, "PyRun_SimpleString"); + void (*final)(void) = dlsym(pylib, "Py_Finalize"); + (*pfx)(prefix); + (*init)(); + needed = (*simple)("import termios") == -1; + (*final)(); + dlclose(pylib); + } + return !needed; + } + + int main(int argc, char** argv) + { + int not_needed = 0; + if (no_rtl_global_needed_for("${vi_cv_dll_name_python}", "${vi_cv_path_python_pfx}")) + not_needed = 1; + return !not_needed; + }])], + [AC_MSG_RESULT(yes);AC_DEFINE(PY_NO_RTLD_GLOBAL)], [AC_MSG_RESULT(no)]) + + CFLAGS=$cflags_save + LIBS=$libs_save + + AC_MSG_CHECKING(whether we can do without RTLD_GLOBAL for Python3) + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $PYTHON3_CFLAGS" + libs_save=$LIBS + dnl -ldl must go first to make this work on Archlinux (Roland Puntaier) + LIBS="-ldl $LIBS" + AC_RUN_IFELSE([AC_LANG_SOURCE([ + #include + #include + /* If this program fails, then RTLD_GLOBAL is needed. + * RTLD_GLOBAL will be used and then it is not possible to + * have both python versions enabled in the same vim instance. + * Only the first python version used will be switched on. + */ + + int no_rtl_global_needed_for(char *python_instsoname, wchar_t *prefix) + { + int needed = 0; + void* pylib = dlopen(python_instsoname, RTLD_LAZY|RTLD_LOCAL); + if (pylib != 0) + { + void (*pfx)(wchar_t *home) = dlsym(pylib, "Py_SetPythonHome"); + void (*init)(void) = dlsym(pylib, "Py_Initialize"); + int (*simple)(char*) = dlsym(pylib, "PyRun_SimpleString"); + void (*final)(void) = dlsym(pylib, "Py_Finalize"); + (*pfx)(prefix); + (*init)(); + needed = (*simple)("import termios") == -1; + (*final)(); + dlclose(pylib); + } + return !needed; + } + + int main(int argc, char** argv) + { + int not_needed = 0; + if (no_rtl_global_needed_for("${vi_cv_dll_name_python3}", L"${vi_cv_path_python3_pfx}")) + not_needed = 1; + return !not_needed; + }])], + [AC_MSG_RESULT(yes);AC_DEFINE(PY3_NO_RTLD_GLOBAL)], [AC_MSG_RESULT(no)]) + + CFLAGS=$cflags_save + LIBS=$libs_save + + PYTHON_SRC="if_python.c" + PYTHON_OBJ="objects/if_python.o" + PYTHON_CFLAGS="$PYTHON_CFLAGS -DDYNAMIC_PYTHON_DLL=\\\"${vi_cv_dll_name_python}\\\"" + PYTHON_LIBS= + PYTHON3_SRC="if_python3.c" + PYTHON3_OBJ="objects/if_python3.o" + PYTHON3_CFLAGS="$PYTHON3_CFLAGS -DDYNAMIC_PYTHON3_DLL=\\\"${vi_cv_dll_name_python3}\\\"" + PYTHON3_LIBS= +elif test "$python_ok" = yes && test "$enable_pythoninterp" = "dynamic"; then + AC_DEFINE(DYNAMIC_PYTHON) + PYTHON_SRC="if_python.c" + PYTHON_OBJ="objects/if_python.o" + PYTHON_CFLAGS="$PYTHON_CFLAGS -DDYNAMIC_PYTHON_DLL=\\\"${vi_cv_dll_name_python}\\\"" + PYTHON_LIBS= +elif test "$python_ok" = yes; then + dnl Check that adding -fPIE works. It may be needed when using a static + dnl Python library. + AC_MSG_CHECKING([if -fPIE can be added for Python]) + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON_CFLAGS -fPIE" + LIBS="$LIBS $PYTHON_LIBS" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); fpie_ok=yes, + AC_MSG_RESULT(no); fpie_ok=no) + CFLAGS=$cflags_save + LIBS=$libs_save + if test $fpie_ok = yes; then + PYTHON_CFLAGS="$PYTHON_CFLAGS -fPIE" + fi +elif test "$python3_ok" = yes && test "$enable_python3interp" = "dynamic"; then + AC_DEFINE(DYNAMIC_PYTHON3) + PYTHON3_SRC="if_python3.c" + PYTHON3_OBJ="objects/if_python3.o" + PYTHON3_CFLAGS="$PYTHON3_CFLAGS -DDYNAMIC_PYTHON3_DLL=\\\"${vi_cv_dll_name_python3}\\\"" + PYTHON3_LIBS= +elif test "$python3_ok" = yes; then + dnl Check that adding -fPIE works. It may be needed when using a static + dnl Python library. + AC_MSG_CHECKING([if -fPIE can be added for Python3]) + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON3_CFLAGS -fPIE" + LIBS="$LIBS $PYTHON3_LIBS" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); fpie_ok=yes, + AC_MSG_RESULT(no); fpie_ok=no) + CFLAGS=$cflags_save + LIBS=$libs_save + if test $fpie_ok = yes; then + PYTHON3_CFLAGS="$PYTHON3_CFLAGS -fPIE" + fi +fi + +AC_MSG_CHECKING(--enable-tclinterp argument) +AC_ARG_ENABLE(tclinterp, + [ --enable-tclinterp[=OPTS] Include Tcl interpreter. [default=no] [OPTS=no/yes/dynamic]], , + [enable_tclinterp="no"]) +AC_MSG_RESULT($enable_tclinterp) + +if test "$enable_tclinterp" = "yes" -o "$enable_tclinterp" = "dynamic"; then + + dnl on FreeBSD tclsh is a silly script, look for tclsh8.[5420] + AC_MSG_CHECKING(--with-tclsh argument) + AC_ARG_WITH(tclsh, [ --with-tclsh=PATH which tclsh to use (default: tclsh8.0)], + tclsh_name="$withval"; AC_MSG_RESULT($tclsh_name), + tclsh_name="tclsh8.5"; AC_MSG_RESULT(no)) + AC_PATH_PROG(vi_cv_path_tcl, $tclsh_name) + AC_SUBST(vi_cv_path_tcl) + + dnl when no specific version specified, also try 8.4, 8.2 and 8.0 + if test "X$vi_cv_path_tcl" = "X" -a $tclsh_name = "tclsh8.5"; then + tclsh_name="tclsh8.4" + AC_PATH_PROG(vi_cv_path_tcl, $tclsh_name) + fi + if test "X$vi_cv_path_tcl" = "X" -a $tclsh_name = "tclsh8.4"; then + tclsh_name="tclsh8.2" + AC_PATH_PROG(vi_cv_path_tcl, $tclsh_name) + fi + if test "X$vi_cv_path_tcl" = "X" -a $tclsh_name = "tclsh8.2"; then + tclsh_name="tclsh8.0" + AC_PATH_PROG(vi_cv_path_tcl, $tclsh_name) + fi + dnl still didn't find it, try without version number + if test "X$vi_cv_path_tcl" = "X"; then + tclsh_name="tclsh" + AC_PATH_PROG(vi_cv_path_tcl, $tclsh_name) + fi + if test "X$vi_cv_path_tcl" != "X"; then + AC_MSG_CHECKING(Tcl version) + if echo 'exit [[expr [info tclversion] < 8.0]]' | "$vi_cv_path_tcl" - ; then + tclver=`echo 'puts [[info tclversion]]' | $vi_cv_path_tcl -` + AC_MSG_RESULT($tclver - OK); + tclloc=`echo 'set l [[info library]];set i [[string last lib $l]];incr i -2;puts [[string range $l 0 $i]]' | $vi_cv_path_tcl -` + tcldll=`echo 'puts libtcl[[info tclversion]][[info sharedlibextension]]' | $vi_cv_path_tcl -` + + AC_MSG_CHECKING(for location of Tcl include) + if test "x$MACOS_X" != "xyes"; then + tclinc="$tclloc/include $tclloc/include/tcl $tclloc/include/tcl$tclver /usr/local/include /usr/local/include/tcl$tclver /usr/include /usr/include/tcl$tclver" + else + dnl For Mac OS X 10.3, use the OS-provided framework location + tclinc="/System/Library/Frameworks/Tcl.framework/Headers" + fi + TCL_INC= + for try in $tclinc; do + if test -f "$try/tcl.h"; then + AC_MSG_RESULT($try/tcl.h) + TCL_INC=$try + break + fi + done + if test -z "$TCL_INC"; then + AC_MSG_RESULT() + SKIP_TCL=YES + fi + if test -z "$SKIP_TCL"; then + AC_MSG_CHECKING(for location of tclConfig.sh script) + if test "x$MACOS_X" != "xyes"; then + tclcnf=`echo $tclinc | sed s/include/lib/g` + tclcnf="$tclcnf `echo $tclinc | sed s/include/lib64/g`" + else + dnl For Mac OS X 10.3, use the OS-provided framework location + tclcnf="/System/Library/Frameworks/Tcl.framework" + fi + for try in $tclcnf; do + if test -f "$try/tclConfig.sh"; then + AC_MSG_RESULT($try/tclConfig.sh) + . "$try/tclConfig.sh" + dnl use eval, because tcl 8.2 includes ${TCL_DBGX} + if test "$enable_tclinterp" = "dynamic"; then + TCL_LIBS=`eval echo "$TCL_STUB_LIB_SPEC $TCL_LIBS"` + else + TCL_LIBS=`eval echo "$TCL_LIB_SPEC $TCL_LIBS"` + fi + dnl Use $TCL_DEFS for -D_THREAD_SAFE et al. But only use the + dnl "-D_ABC" items. Watch out for -DFOO=long\ long. + TCL_DEFS=`echo $TCL_DEFS | sed -e 's/\\\\ /\\\\X/g' | tr ' ' '\012' | sed -e '/^[[^-]]/d' -e '/^-[[^D]]/d' -e '/-D[[^_]]/d' -e 's/-D_/ -D_/' | tr '\012' ' ' | sed -e 's/\\\\X/\\\\ /g'` + break + fi + done + if test -z "$TCL_LIBS"; then + AC_MSG_RESULT() + AC_MSG_CHECKING(for Tcl library by myself) + tcllib=`echo $tclinc | sed s/include/lib/g` + tcllib="$tcllib `echo $tclinc | sed s/include/lib64/g`" + for ext in .so .a ; do + for ver in "" $tclver ; do + for try in $tcllib ; do + trylib=tcl$ver$ext + if test -f "$try/lib$trylib" ; then + AC_MSG_RESULT($try/lib$trylib) + TCL_LIBS="-L\"$try\" -ltcl$ver -ldl -lm" + if test "`(uname) 2>/dev/null`" = SunOS && + uname -r | grep '^5' >/dev/null; then + TCL_LIBS="$TCL_LIBS -R $try" + fi + break 3 + fi + done + done + done + if test -z "$TCL_LIBS"; then + AC_MSG_RESULT() + SKIP_TCL=YES + fi + fi + if test -z "$SKIP_TCL"; then + AC_DEFINE(FEAT_TCL) + TCL_SRC=if_tcl.c + TCL_OBJ=objects/if_tcl.o + TCL_PRO=if_tcl.pro + TCL_CFLAGS="-I$TCL_INC $TCL_DEFS" + fi + fi + else + AC_MSG_RESULT(too old; need Tcl version 8.0 or later) + fi + fi + if test "$enable_tclinterp" = "dynamic"; then + if test "X$TCL_SRC" != "X" -a "X$tcldll" != "X"; then + AC_DEFINE(DYNAMIC_TCL) + TCL_CFLAGS="-DDYNAMIC_TCL_DLL=\\\"$tcldll\\\" -DDYNAMIC_TCL_VER=\\\"$tclver\\\" $TCL_CFLAGS" + fi + fi + if test "$fail_if_missing" = "yes" -a -z "$TCL_SRC"; then + AC_MSG_ERROR([could not configure Tcl]) + fi +fi +AC_SUBST(TCL_SRC) +AC_SUBST(TCL_OBJ) +AC_SUBST(TCL_PRO) +AC_SUBST(TCL_CFLAGS) +AC_SUBST(TCL_LIBS) + +AC_MSG_CHECKING(--enable-rubyinterp argument) +AC_ARG_ENABLE(rubyinterp, + [ --enable-rubyinterp[=OPTS] Include Ruby interpreter. [default=no] [OPTS=no/yes/dynamic]], , + [enable_rubyinterp="no"]) +AC_MSG_RESULT($enable_rubyinterp) +if test "$enable_rubyinterp" = "yes" -o "$enable_rubyinterp" = "dynamic"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + AC_MSG_ERROR([cannot use Ruby with tiny or small features]) + fi + + AC_MSG_CHECKING(--with-ruby-command argument) + AC_SUBST(vi_cv_path_ruby) + AC_ARG_WITH(ruby-command, [ --with-ruby-command=RUBY name of the Ruby command (default: ruby)], + RUBY_CMD="$withval"; vi_cv_path_ruby="$withval"; AC_MSG_RESULT($RUBY_CMD), + RUBY_CMD="ruby"; AC_MSG_RESULT(defaulting to $RUBY_CMD)) + AC_PATH_PROG(vi_cv_path_ruby, $RUBY_CMD) + if test "X$vi_cv_path_ruby" != "X"; then + AC_MSG_CHECKING(Ruby version) + if $vi_cv_path_ruby -e '(VERSION rescue RUBY_VERSION) >= "1.6.0" or exit 1' >/dev/null 2>/dev/null; then + AC_MSG_RESULT(OK) + AC_MSG_CHECKING(Ruby rbconfig) + ruby_rbconfig="RbConfig" + if ! $vi_cv_path_ruby -r rbconfig -e 'RbConfig' >/dev/null 2>/dev/null; then + ruby_rbconfig="Config" + fi + AC_MSG_RESULT($ruby_rbconfig) + AC_MSG_CHECKING(Ruby header files) + rubyhdrdir=`$vi_cv_path_ruby -r mkmf -e "print $ruby_rbconfig::CONFIG[['rubyhdrdir']] || $ruby_rbconfig::CONFIG[['archdir']] || \\$hdrdir" 2>/dev/null` + if test "X$rubyhdrdir" != "X"; then + AC_MSG_RESULT($rubyhdrdir) + RUBY_CFLAGS="-I$rubyhdrdir" + rubyarchdir=`$vi_cv_path_ruby -r rbconfig -e "print ($ruby_rbconfig::CONFIG.has_key? 'rubyarchhdrdir') ? $ruby_rbconfig::CONFIG[['rubyarchhdrdir']] : '$rubyhdrdir/'+$ruby_rbconfig::CONFIG[['arch']]"` + if test -d "$rubyarchdir"; then + RUBY_CFLAGS="$RUBY_CFLAGS -I$rubyarchdir" + fi + rubyversion=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig::CONFIG[['ruby_version']].gsub(/\./, '')[[0,2]]"` + if test "X$rubyversion" = "X"; then + rubyversion=`$vi_cv_path_ruby -e "print ((VERSION rescue RUBY_VERSION)).gsub(/\./, '')[[0,2]]"` + fi + RUBY_CFLAGS="$RUBY_CFLAGS -DRUBY_VERSION=$rubyversion" + rubylibs=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig::CONFIG[['LIBS']]"` + if test "X$rubylibs" != "X"; then + RUBY_LIBS="$rubylibs" + fi + librubyarg=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig.expand($ruby_rbconfig::CONFIG[['LIBRUBYARG']])"` + librubya=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig.expand($ruby_rbconfig::CONFIG[['LIBRUBY_A']])"` + rubylibdir=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig.expand($ruby_rbconfig::CONFIG[['libdir']])"` + if test -f "$rubylibdir/$librubya"; then + librubyarg="$librubyarg" + RUBY_LIBS="$RUBY_LIBS -L$rubylibdir" + elif test "$librubyarg" = "libruby.a"; then + dnl required on Mac OS 10.3 where libruby.a doesn't exist + librubyarg="-lruby" + RUBY_LIBS="$RUBY_LIBS -L$rubylibdir" + fi + + if test "X$librubyarg" != "X"; then + RUBY_LIBS="$librubyarg $RUBY_LIBS" + fi + rubyldflags=`$vi_cv_path_ruby -r rbconfig -e "print $ruby_rbconfig::CONFIG[['LDFLAGS']]"` + if test "X$rubyldflags" != "X"; then + dnl Ruby on Mac OS X 10.5 adds "-arch" flags but these should only + dnl be included if requested by passing --with-mac-arch to + dnl configure, so strip these flags first (if present) + rubyldflags=`echo "$rubyldflags" | sed -e 's/-arch\ ppc//' -e 's/-arch\ i386//' -e 's/-arch\ x86_64//'` + if test "X$rubyldflags" != "X"; then + if test "X`echo \"$LDFLAGS\" | $FGREP -e \"$rubyldflags\"`" = "X"; then + LDFLAGS="$rubyldflags $LDFLAGS" + fi + fi + fi + RUBY_SRC="if_ruby.c" + RUBY_OBJ="objects/if_ruby.o" + RUBY_PRO="if_ruby.pro" + AC_DEFINE(FEAT_RUBY) + if test "$enable_rubyinterp" = "dynamic"; then + libruby_soname=`$vi_cv_path_ruby -r rbconfig -e "puts $ruby_rbconfig::CONFIG[['LIBRUBY_ALIASES']].split[[0]]"` + if test -z "$libruby_soname"; then + libruby_soname=`$vi_cv_path_ruby -r rbconfig -e "puts $ruby_rbconfig::CONFIG[['LIBRUBY_SO']]"` + fi + AC_DEFINE(DYNAMIC_RUBY) + RUBY_CFLAGS="-DDYNAMIC_RUBY_DLL=\\\"$libruby_soname\\\" -DDYNAMIC_RUBY_VER=$rubyversion $RUBY_CFLAGS" + RUBY_LIBS= + fi + else + AC_MSG_RESULT(not found; disabling Ruby) + fi + else + AC_MSG_RESULT(too old; need Ruby version 1.6.0 or later) + fi + fi + + if test "$fail_if_missing" = "yes" -a -z "$RUBY_OBJ"; then + AC_MSG_ERROR([could not configure Ruby]) + fi +fi +AC_SUBST(RUBY_SRC) +AC_SUBST(RUBY_OBJ) +AC_SUBST(RUBY_PRO) +AC_SUBST(RUBY_CFLAGS) +AC_SUBST(RUBY_LIBS) + +AC_MSG_CHECKING(--enable-cscope argument) +AC_ARG_ENABLE(cscope, + [ --enable-cscope Include cscope interface.], , + [enable_cscope="no"]) +AC_MSG_RESULT($enable_cscope) +if test "$enable_cscope" = "yes"; then + AC_DEFINE(FEAT_CSCOPE) +fi + +AC_MSG_CHECKING(--disable-netbeans argument) +AC_ARG_ENABLE(netbeans, + [ --disable-netbeans Disable NetBeans integration support.], + , [enable_netbeans="yes"]) +if test "$enable_netbeans" = "yes"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + AC_MSG_RESULT([cannot use NetBeans with tiny or small features]) + enable_netbeans="no" + else + AC_MSG_RESULT(no) + fi +else + AC_MSG_RESULT(yes) +fi + +AC_MSG_CHECKING(--disable-channel argument) +AC_ARG_ENABLE(channel, + [ --disable-channel Disable process communication support.], + , [enable_channel="yes"]) +if test "$enable_channel" = "yes"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + AC_MSG_RESULT([cannot use channels with tiny or small features]) + enable_channel="no" + else + AC_MSG_RESULT(no) + fi +else + if test "$enable_netbeans" = "yes"; then + AC_MSG_RESULT([yes, netbeans also disabled]) + enable_netbeans="no" + else + AC_MSG_RESULT(yes) + fi +fi + +if test "$enable_channel" = "yes"; then + dnl On Solaris we need the socket and nsl library. + AC_CHECK_LIB(socket, socket) + AC_CHECK_LIB(nsl, gethostbyname) + AC_MSG_CHECKING(whether compiling with process communication is possible) + AC_TRY_LINK([ +#include +#include +#include +#include +#include +#include +#include +#include +#include + /* Check bitfields */ + struct nbbuf { + unsigned int initDone:1; + unsigned short signmaplen; + }; + ], [ + /* Check creating a socket. */ + struct sockaddr_in server; + (void)socket(AF_INET, SOCK_STREAM, 0); + (void)htons(100); + (void)gethostbyname("microsoft.com"); + if (errno == ECONNREFUSED) + (void)connect(1, (struct sockaddr *)&server, sizeof(server)); + ], + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no); enable_netbeans="no"; enable_channel="no") +fi +if test "$enable_netbeans" = "yes"; then + AC_DEFINE(FEAT_NETBEANS_INTG) + NETBEANS_SRC="netbeans.c" + AC_SUBST(NETBEANS_SRC) + NETBEANS_OBJ="objects/netbeans.o" + AC_SUBST(NETBEANS_OBJ) +fi +if test "$enable_channel" = "yes"; then + AC_DEFINE(FEAT_JOB_CHANNEL) + CHANNEL_SRC="channel.c" + AC_SUBST(CHANNEL_SRC) + CHANNEL_OBJ="objects/channel.o" + AC_SUBST(CHANNEL_OBJ) +fi + +AC_MSG_CHECKING(--enable-terminal argument) +AC_ARG_ENABLE(terminal, + [ --enable-terminal Enable terminal emulation support.], + , [enable_terminal="auto"]) +if test "$enable_terminal" = "yes" || test "$enable_terminal" = "auto" -a "x$features" = "xhuge" ; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + AC_MSG_RESULT([cannot use terminal emulator with tiny or small features]) + enable_terminal="no" + else + if test "$enable_terminal" = "auto"; then + enable_terminal="yes" + AC_MSG_RESULT(defaulting to yes) + else + AC_MSG_RESULT(yes) + fi + fi +else + if test "$enable_terminal" = "auto"; then + enable_terminal="no" + AC_MSG_RESULT(defaulting to no) + else + AC_MSG_RESULT(no) + fi +fi +if test "$enable_terminal" = "yes" -a "$enable_channel" = "yes"; then + AC_DEFINE(FEAT_TERMINAL) + TERM_SRC="libvterm/src/encoding.c libvterm/src/keyboard.c libvterm/src/mouse.c libvterm/src/parser.c libvterm/src/pen.c libvterm/src/termscreen.c libvterm/src/state.c libvterm/src/unicode.c libvterm/src/vterm.c" + AC_SUBST(TERM_SRC) + TERM_OBJ="objects/encoding.o objects/keyboard.o objects/mouse.o objects/parser.o objects/pen.o objects/termscreen.o objects/state.o objects/unicode.o objects/vterm.o" + AC_SUBST(TERM_OBJ) +fi + +AC_MSG_CHECKING(--enable-autoservername argument) +AC_ARG_ENABLE(autoservername, + [ --enable-autoservername Automatically define servername at vim startup.], , + [enable_autoservername="no"]) +AC_MSG_RESULT($enable_autoservername) +if test "$enable_autoservername" = "yes"; then + AC_DEFINE(FEAT_AUTOSERVERNAME) +fi + +AC_MSG_CHECKING(--enable-multibyte argument) +AC_ARG_ENABLE(multibyte, + [ --enable-multibyte Include multibyte editing support.], , + [enable_multibyte="yes"]) +AC_MSG_RESULT($enable_multibyte) +if test "$enable_multibyte" != "yes"; then + AC_MSG_ERROR([The multi-byte feature can no longer be disabled. If you have + a problem with this, discuss on the Vim mailing list.]) +fi + +dnl Right-to-Left language support for Vim will be included with big features, +dnl unless ENABLE_RIGHTLEFT is undefined. +AC_MSG_CHECKING(--disable-rightleft argument) +AC_ARG_ENABLE(rightleft, + [ --disable-rightleft Do not include Right-to-Left language support.], + , [enable_rightleft="yes"]) +if test "$enable_rightleft" = "yes"; then + AC_MSG_RESULT(no) +else + AC_MSG_RESULT(yes) + AC_DEFINE(DISABLE_RIGHTLEFT) +fi + +dnl Arabic language support for Vim will be included with big features, +dnl unless ENABLE_ARABIC is undefined. +AC_MSG_CHECKING(--disable-arabic argument) +AC_ARG_ENABLE(arabic, + [ --disable-arabic Do not include Arabic language support.], + , [enable_arabic="yes"]) +if test "$enable_arabic" = "yes"; then + AC_MSG_RESULT(no) +else + AC_MSG_RESULT(yes) + AC_DEFINE(DISABLE_ARABIC) +fi + +dnl Farsi language support for vim will be included with big features, +dnl unless ENABLE_FARSI is undefined. +AC_MSG_CHECKING(--disable-farsi argument) +AC_ARG_ENABLE(farsi, + [ --disable-farsi Do not include Farsi language support.], + , [enable_farsi="yes"]) +if test "$enable_farsi" = "yes"; then + AC_MSG_RESULT(no) +else + AC_MSG_RESULT(yes) + AC_DEFINE(DISABLE_FARSI) +fi + +AC_MSG_CHECKING(--enable-hangulinput argument) +AC_ARG_ENABLE(hangulinput, + [ --enable-hangulinput Include Hangul input support.], , + [enable_hangulinput="no"]) +AC_MSG_RESULT($enable_hangulinput) + +AC_MSG_CHECKING(--enable-xim argument) +AC_ARG_ENABLE(xim, + [ --enable-xim Include XIM input support.], + AC_MSG_RESULT($enable_xim), + [enable_xim="auto"; AC_MSG_RESULT(defaulting to auto)]) + +AC_MSG_CHECKING(--enable-fontset argument) +AC_ARG_ENABLE(fontset, + [ --enable-fontset Include X fontset output support.], , + [enable_fontset="no"]) +AC_MSG_RESULT($enable_fontset) +dnl defining FEAT_XFONTSET is delayed, so that it can be disabled for no GUI + +test -z "$with_x" && with_x=yes +test "${enable_gui-yes}" != no -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && with_x=yes +if test "$with_x" = no; then + AC_MSG_RESULT(defaulting to: don't HAVE_X11) +else + dnl Do this check early, so that its failure can override user requests. + + AC_PATH_PROG(xmkmfpath, xmkmf) + + AC_PATH_XTRA + + dnl On z/OS Unix the X libraries are DLLs. To use them the code must + dnl be compiled with a special option. + dnl Also add SM, ICE and Xmu to X_EXTRA_LIBS. + if test "$zOSUnix" = "yes"; then + CFLAGS="$CFLAGS -W c,dll" + LDFLAGS="$LDFLAGS -W l,dll" + X_EXTRA_LIBS="$X_EXTRA_LIBS -lSM -lICE -lXmu" + fi + + dnl On my HPUX system the X include dir is found, but the lib dir not. + dnl This is a desparate try to fix this. + + if test -d "$x_includes" && test ! -d "$x_libraries"; then + x_libraries=`echo "$x_includes" | sed s/include/lib/` + AC_MSG_RESULT(Corrected X libraries to $x_libraries) + X_LIBS="$X_LIBS -L$x_libraries" + if test "`(uname) 2>/dev/null`" = SunOS && + uname -r | grep '^5' >/dev/null; then + X_LIBS="$X_LIBS -R $x_libraries" + fi + fi + + if test -d "$x_libraries" && test ! -d "$x_includes"; then + x_includes=`echo "$x_libraries" | sed s/lib/include/` + AC_MSG_RESULT(Corrected X includes to $x_includes) + X_CFLAGS="$X_CFLAGS -I$x_includes" + fi + + dnl Remove "-I/usr/include " from X_CFLAGS, should not be needed. + X_CFLAGS="`echo $X_CFLAGS\ | sed 's%-I/usr/include %%'`" + dnl Remove "-L/usr/lib " from X_LIBS, should not be needed. + X_LIBS="`echo $X_LIBS\ | sed 's%-L/usr/lib %%'`" + dnl Same for "-R/usr/lib ". + X_LIBS="`echo $X_LIBS\ | sed -e 's%-R/usr/lib %%' -e 's%-R /usr/lib %%'`" + + + dnl Check if the X11 header files are correctly installed. On some systems + dnl Xlib.h includes files that don't exist. On some systems X11/Intrinsic.h + dnl is missing. + AC_MSG_CHECKING(if X11 header files can be found) + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $X_CFLAGS" + AC_TRY_COMPILE([#include +#include ], , + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no); no_x=yes) + CFLAGS=$cflags_save + + if test "${no_x-no}" = yes; then + with_x=no + else + AC_DEFINE(HAVE_X11) + X_LIB="-lXt -lX11"; + AC_SUBST(X_LIB) + + ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="-L$x_libraries $LDFLAGS" + + dnl Check for -lXdmcp (needed on SunOS 4.1.4) + dnl For HP-UX 10.20 it must be before -lSM -lICE + AC_CHECK_LIB(Xdmcp, _XdmcpAuthDoIt, [X_EXTRA_LIBS="$X_EXTRA_LIBS -lXdmcp"],, + [-lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS -lXdmcp]) + + dnl Some systems need -lnsl -lsocket when testing for ICE. + dnl The check above doesn't do this, try here (again). Also needed to get + dnl them after Xdmcp. link.sh will remove them when not needed. + dnl Check for other function than above to avoid the cached value + AC_CHECK_LIB(ICE, IceOpenConnection, + [X_EXTRA_LIBS="$X_EXTRA_LIBS -lSM -lICE"],, [$X_EXTRA_LIBS]) + + dnl Check for -lXpm (needed for some versions of Motif) + LDFLAGS="$X_LIBS $ac_save_LDFLAGS" + AC_CHECK_LIB(Xpm, XpmCreatePixmapFromData, [X_PRE_LIBS="$X_PRE_LIBS -lXpm"],, + [-lXt $X_PRE_LIBS -lXpm -lX11 $X_EXTRA_LIBS]) + + dnl Check that the X11 header files don't use implicit declarations + AC_MSG_CHECKING(if X11 header files implicitly declare return values) + cflags_save=$CFLAGS + dnl -Werror is GCC only, others like Solaris Studio might not like it + if test "$GCC" = yes; then + CFLAGS="$CFLAGS $X_CFLAGS -Werror" + else + CFLAGS="$CFLAGS $X_CFLAGS" + fi + AC_TRY_COMPILE([#include ], , + AC_MSG_RESULT(no), + CFLAGS="$CFLAGS -Wno-implicit-int" + AC_TRY_COMPILE([#include ], , + AC_MSG_RESULT(yes); cflags_save="$cflags_save -Wno-implicit-int", + AC_MSG_RESULT(test failed) + ) + ) + CFLAGS=$cflags_save + + LDFLAGS="$ac_save_LDFLAGS" + + AC_MSG_CHECKING(size of wchar_t is 2 bytes) + AC_CACHE_VAL(ac_cv_small_wchar_t, + [AC_TRY_RUN([ +#include +#if STDC_HEADERS +# include +# include +#endif + main() + { + if (sizeof(wchar_t) <= 2) + exit(1); + exit(0); + }], + ac_cv_small_wchar_t="no", + ac_cv_small_wchar_t="yes", + AC_MSG_ERROR(failed to compile test program))]) + AC_MSG_RESULT($ac_cv_small_wchar_t) + if test "x$ac_cv_small_wchar_t" = "xyes" ; then + AC_DEFINE(SMALL_WCHAR_T) + fi + + fi +fi + +dnl Check if --with-x was given but it doesn't work. +if test "x$with_x" = xno -a "x$with_x_arg" = xyes; then + AC_MSG_ERROR([could not configure X]) +fi + +test "x$with_x" = xno -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && enable_gui=no + +AC_MSG_CHECKING(--enable-gui argument) +AC_ARG_ENABLE(gui, + [ --enable-gui[=OPTS] X11 GUI. [default=auto] [OPTS=auto/no/gtk2/gnome2/gtk3/motif/athena/neXtaw/photon/carbon]], , enable_gui="auto") + +dnl Canonicalize the --enable-gui= argument so that it can be easily compared. +dnl Do not use character classes for portability with old tools. +enable_gui_canon=`echo "_$enable_gui" | \ + sed 's/[[ _+-]]//g;y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` + +dnl Skip everything by default. +SKIP_GTK2=YES +SKIP_GTK3=YES +SKIP_GNOME=YES +SKIP_MOTIF=YES +SKIP_ATHENA=YES +SKIP_NEXTAW=YES +SKIP_PHOTON=YES +SKIP_CARBON=YES +GUITYPE=NONE + +if test "x$QNX" = "xyes" -a "x$with_x" = "xno" ; then + SKIP_PHOTON= + case "$enable_gui_canon" in + no) AC_MSG_RESULT(no GUI support) + SKIP_PHOTON=YES ;; + yes|""|auto) AC_MSG_RESULT(automatic GUI support) + gui_auto=yes ;; + photon) AC_MSG_RESULT(Photon GUI support) ;; + *) AC_MSG_RESULT([Sorry, $enable_gui GUI is not supported]) + SKIP_PHOTON=YES ;; + esac + +elif test "x$MACOS_X" = "xyes" -a "x$with_x" = "xno" ; then + SKIP_CARBON= + case "$enable_gui_canon" in + no) AC_MSG_RESULT(no GUI support) + SKIP_CARBON=YES ;; + yes|"") AC_MSG_RESULT(yes - automatic GUI support) + gui_auto=yes ;; + auto) AC_MSG_RESULT(auto - Carbon GUI is outdated - disable GUI support) + SKIP_CARBON=YES ;; + carbon) AC_MSG_RESULT(Carbon GUI support) ;; + *) AC_MSG_RESULT([Sorry, $enable_gui GUI is not supported]) + SKIP_CARBON=YES ;; + esac + +else + + case "$enable_gui_canon" in + no|none) AC_MSG_RESULT(no GUI support) ;; + yes|""|auto) AC_MSG_RESULT(yes/auto - automatic GUI support) + gui_auto=yes + SKIP_GTK2= + SKIP_GNOME= + SKIP_MOTIF= + SKIP_ATHENA= + SKIP_NEXTAW= + SKIP_CARBON=;; + gtk2) AC_MSG_RESULT(GTK+ 2.x GUI support) + SKIP_GTK2=;; + gnome2) AC_MSG_RESULT(GNOME 2.x GUI support) + SKIP_GNOME= + SKIP_GTK2=;; + gtk3) AC_MSG_RESULT(GTK+ 3.x GUI support) + SKIP_GTK3=;; + motif) AC_MSG_RESULT(Motif GUI support) + SKIP_MOTIF=;; + athena) AC_MSG_RESULT(Athena GUI support) + SKIP_ATHENA=;; + nextaw) AC_MSG_RESULT(neXtaw GUI support) + SKIP_NEXTAW=;; + *) AC_MSG_RESULT([Sorry, $enable_gui GUI is not supported]) ;; + esac + +fi + +if test "x$SKIP_GTK2" != "xYES" -a "$enable_gui_canon" != "gtk2" \ + -a "$enable_gui_canon" != "gnome2"; then + AC_MSG_CHECKING(whether or not to look for GTK+ 2) + AC_ARG_ENABLE(gtk2-check, + [ --enable-gtk2-check If auto-select GUI, check for GTK+ 2 [default=yes]], + , enable_gtk2_check="yes") + AC_MSG_RESULT($enable_gtk2_check) + if test "x$enable_gtk2_check" = "xno"; then + SKIP_GTK2=YES + SKIP_GNOME=YES + fi +fi + +if test "x$SKIP_GNOME" != "xYES" -a "$enable_gui_canon" != "gnome2"; then + AC_MSG_CHECKING(whether or not to look for GNOME) + AC_ARG_ENABLE(gnome-check, + [ --enable-gnome-check If GTK GUI, check for GNOME [default=no]], + , enable_gnome_check="no") + AC_MSG_RESULT($enable_gnome_check) + if test "x$enable_gnome_check" = "xno"; then + SKIP_GNOME=YES + fi +fi + +if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then + AC_MSG_CHECKING(whether or not to look for GTK+ 3) + AC_ARG_ENABLE(gtk3-check, + [ --enable-gtk3-check If auto-select GUI, check for GTK+ 3 [default=yes]], + , enable_gtk3_check="yes") + AC_MSG_RESULT($enable_gtk3_check) + if test "x$enable_gtk3_check" = "xno"; then + SKIP_GTK3=YES + fi +fi + +if test "x$SKIP_MOTIF" != "xYES" -a "$enable_gui_canon" != "motif"; then + AC_MSG_CHECKING(whether or not to look for Motif) + AC_ARG_ENABLE(motif-check, + [ --enable-motif-check If auto-select GUI, check for Motif [default=yes]], + , enable_motif_check="yes") + AC_MSG_RESULT($enable_motif_check) + if test "x$enable_motif_check" = "xno"; then + SKIP_MOTIF=YES + fi +fi + +if test "x$SKIP_ATHENA" != "xYES" -a "$enable_gui_canon" != "athena"; then + AC_MSG_CHECKING(whether or not to look for Athena) + AC_ARG_ENABLE(athena-check, + [ --enable-athena-check If auto-select GUI, check for Athena [default=yes]], + , enable_athena_check="yes") + AC_MSG_RESULT($enable_athena_check) + if test "x$enable_athena_check" = "xno"; then + SKIP_ATHENA=YES + fi +fi + +if test "x$SKIP_NEXTAW" != "xYES" -a "$enable_gui_canon" != "nextaw"; then + AC_MSG_CHECKING(whether or not to look for neXtaw) + AC_ARG_ENABLE(nextaw-check, + [ --enable-nextaw-check If auto-select GUI, check for neXtaw [default=yes]], + , enable_nextaw_check="yes") + AC_MSG_RESULT($enable_nextaw_check); + if test "x$enable_nextaw_check" = "xno"; then + SKIP_NEXTAW=YES + fi +fi + +if test "x$SKIP_CARBON" != "xYES" -a "$enable_gui_canon" != "carbon"; then + AC_MSG_CHECKING(whether or not to look for Carbon) + AC_ARG_ENABLE(carbon-check, + [ --enable-carbon-check If auto-select GUI, check for Carbon [default=yes]], + , enable_carbon_check="yes") + AC_MSG_RESULT($enable_carbon_check); + if test "x$enable_carbon_check" = "xno"; then + SKIP_CARBON=YES + fi +fi + + +if test "x$MACOS_X" = "xyes" -a -z "$SKIP_CARBON" -a "x$CARBON" = "xyes"; then + AC_MSG_CHECKING(for Carbon GUI) + dnl already did the check, just give the message + AC_MSG_RESULT(yes); + GUITYPE=CARBONGUI + if test "$VIMNAME" = "vim"; then + VIMNAME=Vim + fi + + if test "x$MACARCH" = "xboth"; then + CPPFLAGS="$CPPFLAGS -I$DEVELOPER_DIR/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon" + else + CPPFLAGS="$CPPFLAGS -I$DEVELOPER_DIR/Headers/FlatCarbon" + fi + + dnl Default install directory is not /usr/local + if test x$prefix = xNONE; then + prefix=/Applications + fi + + dnl Sorry for the hard coded default + datadir='${prefix}/Vim.app/Contents/Resources' + + dnl skip everything else + SKIP_GTK2=YES; + SKIP_GNOME=YES; + SKIP_MOTIF=YES; + SKIP_ATHENA=YES; + SKIP_NEXTAW=YES; + SKIP_PHOTON=YES; + SKIP_CARBON=YES +fi + +dnl define an autoconf function to check for a specified version of GTK, and +dnl try to compile/link a GTK program. +dnl +dnl AM_PATH_GTK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for GTK, and define GTK_CFLAGS, GTK_LIBDIR and GTK_LIBS +dnl +AC_DEFUN(AM_PATH_GTK, +[ + if test "X$GTK_CONFIG" != "Xno" -o "X$PKG_CONFIG" != "Xno"; then + { + no_gtk="" + if (test "X$SKIP_GTK2" != "XYES" -a "X$PKG_CONFIG" != "Xno") \ + && $PKG_CONFIG --exists gtk+-2.0; then + { + min_gtk_version=ifelse([$1], ,2.2.0,$1) + AC_MSG_CHECKING(for GTK - version >= $min_gtk_version) + dnl We should be using PKG_CHECK_MODULES() instead of this hack. + dnl But I guess the dependency on pkgconfig.m4 is not wanted or + dnl something like that. + GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-2.0` + GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-2.0` + GTK_LIBS=`$PKG_CONFIG --libs gtk+-2.0` + gtk_major_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` + gtk_minor_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` + gtk_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` + } + elif (test "X$SKIP_GTK3" != "XYES" -a "X$PKG_CONFIG" != "Xno") \ + && $PKG_CONFIG --exists gtk+-3.0; then + { + min_gtk_version=ifelse([$1], ,3.0.0,$1) + AC_MSG_CHECKING(for GTK - version >= $min_gtk_version) + + GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-3.0` + GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-3.0` + GTK_LIBS=`$PKG_CONFIG --libs gtk+-3.0` + gtk_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` + gtk_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` + gtk_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` + } + else + no_gtk=yes + fi + + if test "x$enable_gtktest" = "xyes" -a "x$no_gtk" = "x"; then + { + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $GTK_CFLAGS" + LIBS="$LIBS $GTK_LIBS" + + dnl + dnl Now check if the installed GTK is sufficiently new. + dnl + rm -f conf.gtktest + AC_TRY_RUN([ +#include +#include +#if STDC_HEADERS +# include +# include +#endif + +int +main () +{ +int major, minor, micro; +char *tmp_version; + +system ("touch conf.gtktest"); + +/* HP/UX 9 (%@#!) writes to sscanf strings */ +tmp_version = g_strdup("$min_gtk_version"); +if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_gtk_version"); + exit(1); + } + +if ((gtk_major_version > major) || + ((gtk_major_version == major) && (gtk_minor_version > minor)) || + ((gtk_major_version == major) && (gtk_minor_version == minor) && + (gtk_micro_version >= micro))) +{ + return 0; +} +return 1; +} +],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + } + fi + if test "x$no_gtk" = x ; then + if test "x$enable_gtktest" = "xyes"; then + AC_MSG_RESULT(yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version) + else + AC_MSG_RESULT(found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version) + fi + ifelse([$2], , :, [$2]) + else + { + AC_MSG_RESULT(no) + GTK_CFLAGS="" + GTK_LIBS="" + ifelse([$3], , :, [$3]) + if test "$fail_if_missing" = "yes" -a "X$gui_auto" != "Xyes"; then + AC_MSG_ERROR([could not configure GTK]) + fi + } + fi + } + else + GTK_CFLAGS="" + GTK_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(GTK_CFLAGS) + AC_SUBST(GTK_LIBS) + rm -f conf.gtktest +]) + +dnl --------------------------------------------------------------------------- +dnl gnome +dnl --------------------------------------------------------------------------- +AC_DEFUN([GNOME_INIT_HOOK], +[ + AC_SUBST(GNOME_LIBS) + AC_SUBST(GNOME_LIBDIR) + AC_SUBST(GNOME_INCLUDEDIR) + + AC_ARG_WITH(gnome-includes, + [ --with-gnome-includes=DIR Specify location of GNOME headers], + [CFLAGS="$CFLAGS -I$withval"] + ) + + AC_ARG_WITH(gnome-libs, + [ --with-gnome-libs=DIR Specify location of GNOME libs], + [LDFLAGS="$LDFLAGS -L$withval" gnome_prefix=$withval] + ) + + AC_ARG_WITH(gnome, + [ --with-gnome Specify prefix for GNOME files], + if test x$withval = xyes; then + want_gnome=yes + ifelse([$1], [], :, [$1]) + else + if test "x$withval" = xno; then + want_gnome=no + else + want_gnome=yes + LDFLAGS="$LDFLAGS -L$withval/lib" + CFLAGS="$CFLAGS -I$withval/include" + gnome_prefix=$withval/lib + fi + fi, + want_gnome=yes) + + if test "x$want_gnome" = xyes; then + { + AC_MSG_CHECKING(for libgnomeui-2.0) + if $PKG_CONFIG --exists libgnomeui-2.0; then + AC_MSG_RESULT(yes) + GNOME_LIBS=`$PKG_CONFIG --libs-only-l libgnomeui-2.0` + GNOME_LIBDIR=`$PKG_CONFIG --libs-only-L libgnomeui-2.0` + GNOME_INCLUDEDIR=`$PKG_CONFIG --cflags libgnomeui-2.0` + + dnl On FreeBSD we need -pthread but pkg-config doesn't include it. + dnl This might not be the right way but it works for me... + AC_MSG_CHECKING(for FreeBSD) + if test "`(uname) 2>/dev/null`" = FreeBSD; then + AC_MSG_RESULT(yes, adding -pthread) + GNOME_INCLUDEDIR="$GNOME_INCLUDEDIR -D_THREAD_SAFE" + GNOME_LIBS="$GNOME_LIBS -pthread" + else + AC_MSG_RESULT(no) + fi + $1 + else + AC_MSG_RESULT(not found) + if test "x$2" = xfail; then + AC_MSG_ERROR(Could not find libgnomeui-2.0 via pkg-config) + fi + fi + } + fi +]) + +AC_DEFUN([GNOME_INIT],[ + GNOME_INIT_HOOK([],fail) +]) + + +dnl --------------------------------------------------------------------------- +dnl Check for GTK2. If it fails, then continue on for Motif as before... +dnl --------------------------------------------------------------------------- +if test -z "$SKIP_GTK2"; then + + AC_MSG_CHECKING(--disable-gtktest argument) + AC_ARG_ENABLE(gtktest, [ --disable-gtktest Do not try to compile and run a test GTK program], + , enable_gtktest=yes) + if test "x$enable_gtktest" = "xyes" ; then + AC_MSG_RESULT(gtk test enabled) + else + AC_MSG_RESULT(gtk test disabled) + fi + + if test "X$PKG_CONFIG" = "X"; then + AC_PATH_TOOL(PKG_CONFIG, pkg-config, no) + fi + + if test "x$PKG_CONFIG" != "xno"; then + dnl First try finding version 2.2.0 or later. The 2.0.x series has + dnl problems (bold fonts, --remote doesn't work). + AM_PATH_GTK(2.2.0, + [GUI_LIB_LOC="$GTK_LIBDIR" + GTK_LIBNAME="$GTK_LIBS" + GUI_INC_LOC="$GTK_CFLAGS"], ) + if test "x$GTK_CFLAGS" != "x"; then + SKIP_GTK3=YES + SKIP_ATHENA=YES + SKIP_NEXTAW=YES + SKIP_MOTIF=YES + GUITYPE=GTK + AC_SUBST(GTK_LIBNAME) + fi + fi + if test "x$GUITYPE" = "xGTK"; then + dnl + dnl if GTK exists, then check for GNOME. + dnl + if test -z "$SKIP_GNOME"; then + { + GNOME_INIT_HOOK([have_gnome=yes]) + if test "x$have_gnome" = xyes ; then + AC_DEFINE(FEAT_GUI_GNOME) + GUI_INC_LOC="$GUI_INC_LOC $GNOME_INCLUDEDIR" + GTK_LIBNAME="$GTK_LIBNAME $GNOME_LIBDIR $GNOME_LIBS" + fi + } + fi + fi +fi + + +dnl --------------------------------------------------------------------------- +dnl Check for GTK3. +dnl --------------------------------------------------------------------------- +if test -z "$SKIP_GTK3"; then + + AC_MSG_CHECKING(--disable-gtktest argument) + AC_ARG_ENABLE(gtktest, [ --disable-gtktest Do not try to compile and run a test GTK program], + , enable_gtktest=yes) + if test "x$enable_gtktest" = "xyes" ; then + AC_MSG_RESULT(gtk test enabled) + else + AC_MSG_RESULT(gtk test disabled) + fi + + if test "X$PKG_CONFIG" = "X"; then + AC_PATH_TOOL(PKG_CONFIG, pkg-config, no) + fi + + if test "x$PKG_CONFIG" != "xno"; then + AM_PATH_GTK(3.0.0, + [GUI_LIB_LOC="$GTK_LIBDIR" + GTK_LIBNAME="$GTK_LIBS" + GUI_INC_LOC="$GTK_CFLAGS"], ) + if test "x$GTK_CFLAGS" != "x"; then + SKIP_GTK2=YES + SKIP_GNOME=YES + SKIP_ATHENA=YES + SKIP_NEXTAW=YES + SKIP_MOTIF=YES + GUITYPE=GTK + AC_SUBST(GTK_LIBNAME) + AC_DEFINE(USE_GTK3) + fi + fi +fi + +dnl Check the version of Gdk-Pixbuf. If the version is 2.31 or later and +dnl glib-compile-resources is found in PATH, use GResource. +if test "x$GUITYPE" = "xGTK"; then + AC_MSG_CHECKING([version of Gdk-Pixbuf]) + gdk_pixbuf_version=`$PKG_CONFIG --modversion gdk-pixbuf-2.0` + if test "x$gdk_pixbuf_version" != x ; then + gdk_pixbuf_version_minor=`echo $gdk_pixbuf_version | \ + sed -e 's/[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\)\.[[0-9]][[0-9]]*/\1/'` + if test "x$gdk_pixbuf_version_minor" != x -a \ + $gdk_pixbuf_version_minor -ge 31 ; then + AC_MSG_RESULT([OK.]) + AC_PATH_PROG(GLIB_COMPILE_RESOURCES,[glib-compile-resources],no) + AC_MSG_CHECKING([glib-compile-resources]) + if test "x$GLIB_COMPILE_RESOURCES" = xno ; then + GLIB_COMPILE_RESOURCES="" + AC_MSG_RESULT([cannot be found in PATH.]) + else + AC_MSG_RESULT([usable.]) + AC_DEFINE(USE_GRESOURCE) + GRESOURCE_SRC="auto/gui_gtk_gresources.c" + GRESOURCE_OBJ="objects/gui_gtk_gresources.o" + fi + else + AC_MSG_RESULT([not usable.]) + fi + else + AC_MSG_RESULT([cannot obtain from pkg_config.]) + fi + + AC_MSG_CHECKING([--disable-icon-cache-update argument]) + AC_ARG_ENABLE(icon_cache_update, + [ --disable-icon-cache-update update disabled], + [], + [enable_icon_cache_update="yes"]) + if test "$enable_icon_cache_update" = "yes"; then + AC_MSG_RESULT([not set]) + AC_PATH_PROG(GTK_UPDATE_ICON_CACHE,[gtk-update-icon-cache],no) + if test "x$GTK_UPDATE_ICON_CACHE" = "xno" ; then + AC_MSG_RESULT([not found in PATH.]) + fi + else + AC_MSG_RESULT([update disabled]) + fi + + AC_MSG_CHECKING([--disable-desktop-database-update argument]) + AC_ARG_ENABLE(desktop_database_update, + [ --disable-desktop-database-update update disabled], + [], + [enable_desktop_database_update="yes"]) + if test "$enable_desktop_database_update" = "yes"; then + AC_MSG_RESULT([not set]) + AC_PATH_PROG(UPDATE_DESKTOP_DATABASE,[update-desktop-database],no) + if test "x$UPDATE_DESKTOP_DATABASE" = "xno" ; then + AC_MSG_RESULT([not found in PATH.]) + fi + else + AC_MSG_RESULT([update disabled]) + fi +fi +AC_SUBST(GLIB_COMPILE_RESOURCES) +AC_SUBST(GRESOURCE_SRC) +AC_SUBST(GRESOURCE_OBJ) +AC_SUBST(GTK_UPDATE_ICON_CACHE) +AC_SUBST(UPDATE_DESKTOP_DATABASE) + +dnl Check for Motif include files location. +dnl The LAST one found is used, this makes the highest version to be used, +dnl e.g. when Motif1.2 and Motif2.0 are both present. + +if test -z "$SKIP_MOTIF"; then + gui_XXX="/usr/XXX/Motif* /usr/Motif*/XXX /usr/XXX /usr/shlib /usr/X11*/XXX /usr/XXX/X11* /usr/dt/XXX /local/Motif*/XXX /local/XXX/Motif* /usr/local/Motif*/XXX /usr/local/XXX/Motif* /usr/local/XXX /usr/local/X11*/XXX /usr/local/LessTif/Motif*/XXX $MOTIFHOME/XXX" + dnl Remove "-I" from before $GUI_INC_LOC if it's there + GUI_INC_LOC="`echo $GUI_INC_LOC|sed 's%-I%%g'`" + + AC_MSG_CHECKING(for location of Motif GUI includes) + gui_includes="`echo $x_includes|sed 's%/[^/][^/]*$%%'` `echo "$gui_XXX" | sed s/XXX/include/g` $GUI_INC_LOC" + GUI_INC_LOC= + for try in $gui_includes; do + if test -f "$try/Xm/Xm.h"; then + GUI_INC_LOC=$try + fi + done + if test -n "$GUI_INC_LOC"; then + if test "$GUI_INC_LOC" = /usr/include; then + GUI_INC_LOC= + AC_MSG_RESULT(in default path) + else + AC_MSG_RESULT($GUI_INC_LOC) + fi + else + AC_MSG_RESULT() + SKIP_MOTIF=YES + fi +fi + +dnl Check for Motif library files location. In the same order as the include +dnl files, to avoid a mixup if several versions are present + +if test -z "$SKIP_MOTIF"; then + AC_MSG_CHECKING(--with-motif-lib argument) + AC_ARG_WITH(motif-lib, + [ --with-motif-lib=STRING Library for Motif ], + [ MOTIF_LIBNAME="${withval}" ] ) + + if test -n "$MOTIF_LIBNAME"; then + AC_MSG_RESULT($MOTIF_LIBNAME) + GUI_LIB_LOC= + else + AC_MSG_RESULT(no) + + dnl Remove "-L" from before $GUI_LIB_LOC if it's there + GUI_LIB_LOC="`echo $GUI_LIB_LOC|sed 's%-L%%g'`" + + dnl Ubuntu has libXm.so in /usr/lib/i386-linux-gnu and elsewhere. The + dnl linker will figure out which one to use, we only check if one exists. + AC_MSG_CHECKING(for location of Motif GUI libs) + gui_libs="`echo $x_libraries|sed 's%/[^/][^/]*$%%'` `echo "$gui_XXX" | sed s/XXX/lib/g` /usr/lib/i386-linux-gnu /usr/lib/x86_64-linux-gnu `echo "$GUI_INC_LOC" | sed s/include/lib/` $GUI_LIB_LOC" + GUI_LIB_LOC= + for try in $gui_libs; do + for libtry in "$try"/libXm.a "$try"/libXm.so* "$try"/libXm.sl "$try"/libXm.dylib; do + if test -f "$libtry"; then + GUI_LIB_LOC=$try + fi + done + done + if test -n "$GUI_LIB_LOC"; then + dnl Remove /usr/lib, it causes trouble on some systems + if test "$GUI_LIB_LOC" = /usr/lib \ + -o "$GUI_LIB_LOC" = /usr/lib/i386-linux-gnu \ + -o "$GUI_LIB_LOC" = /usr/lib/x86_64-linux-gnu; then + GUI_LIB_LOC= + AC_MSG_RESULT(in default path) + else + if test -n "$GUI_LIB_LOC"; then + AC_MSG_RESULT($GUI_LIB_LOC) + if test "`(uname) 2>/dev/null`" = SunOS && + uname -r | grep '^5' >/dev/null; then + GUI_LIB_LOC="$GUI_LIB_LOC -R $GUI_LIB_LOC" + fi + fi + fi + MOTIF_LIBNAME=-lXm + else + AC_MSG_RESULT() + SKIP_MOTIF=YES + fi + fi +fi + +if test -z "$SKIP_MOTIF"; then + SKIP_ATHENA=YES + SKIP_NEXTAW=YES + GUITYPE=MOTIF + AC_SUBST(MOTIF_LIBNAME) +fi + +dnl Check if the Athena files can be found + +GUI_X_LIBS= + +if test -z "$SKIP_ATHENA"; then + AC_MSG_CHECKING(if Athena header files can be found) + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $X_CFLAGS" + AC_TRY_COMPILE([ +#include +#include ], , + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no); SKIP_ATHENA=YES ) + CFLAGS=$cflags_save +fi + +if test -z "$SKIP_ATHENA"; then + GUITYPE=ATHENA +fi + +if test -z "$SKIP_NEXTAW"; then + AC_MSG_CHECKING(if neXtaw header files can be found) + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $X_CFLAGS" + AC_TRY_COMPILE([ +#include +#include ], , + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no); SKIP_NEXTAW=YES ) + CFLAGS=$cflags_save +fi + +if test -z "$SKIP_NEXTAW"; then + GUITYPE=NEXTAW +fi + +if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF"; then + dnl Prepend -I and -L to $GUI_INC_LOC and $GUI_LIB_LOC if not empty + dnl Avoid adding it when it twice + if test -n "$GUI_INC_LOC"; then + GUI_INC_LOC=-I"`echo $GUI_INC_LOC|sed 's%-I%%'`" + fi + if test -n "$GUI_LIB_LOC"; then + GUI_LIB_LOC=-L"`echo $GUI_LIB_LOC|sed 's%-L%%'`" + fi + + dnl Check for -lXext and then for -lXmu + ldflags_save=$LDFLAGS + LDFLAGS="$X_LIBS $LDFLAGS" + AC_CHECK_LIB(Xext, XShapeQueryExtension, [GUI_X_LIBS="-lXext"],, + [-lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS]) + dnl For Solaris we need -lw and -ldl before linking with -lXmu works. + AC_CHECK_LIB(w, wslen, [X_EXTRA_LIBS="$X_EXTRA_LIBS -lw"],, + [$GUI_X_LIBS -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS]) + AC_CHECK_LIB(dl, dlsym, [X_EXTRA_LIBS="$X_EXTRA_LIBS -ldl"],, + [$GUI_X_LIBS -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS]) + AC_CHECK_LIB(Xmu, XmuCreateStippledPixmap, [GUI_X_LIBS="-lXmu $GUI_X_LIBS"],, + [$GUI_X_LIBS -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS]) + if test -z "$SKIP_MOTIF"; then + AC_CHECK_LIB(Xp, XpEndJob, [GUI_X_LIBS="-lXp $GUI_X_LIBS"],, + [$GUI_X_LIBS -lXm -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS]) + fi + LDFLAGS=$ldflags_save + + dnl Execute xmkmf to figure out if -DNARROWPROTO is needed. + AC_MSG_CHECKING(for extra X11 defines) + NARROW_PROTO= + rm -fr conftestdir + if mkdir conftestdir; then + cd conftestdir + cat > Imakefile <<'EOF' +acfindx: + @echo 'NARROW_PROTO="${PROTO_DEFINES}"' +EOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + fi + cd .. + rm -fr conftestdir + fi + if test -z "$NARROW_PROTO"; then + AC_MSG_RESULT(no) + else + AC_MSG_RESULT($NARROW_PROTO) + fi + AC_SUBST(NARROW_PROTO) +fi + +dnl Look for XSMP support - but don't necessarily restrict it to X11 GUIs +dnl use the X11 include path +if test "$enable_xsmp" = "yes"; then + cppflags_save=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + AC_CHECK_HEADERS(X11/SM/SMlib.h) + CPPFLAGS=$cppflags_save +fi + + +if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2" -o -z "$SKIP_GTK3"; then + dnl Check for X11/xpm.h and X11/Sunkeysym.h with the GUI include path + cppflags_save=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + AC_CHECK_HEADERS(X11/xpm.h X11/Sunkeysym.h) + + dnl automatically disable XIM when XIMtext isn't in X11/Xlib.h + if test ! "$enable_xim" = "no"; then + AC_MSG_CHECKING(for XIMText in X11/Xlib.h) + AC_EGREP_CPP(XIMText, [#include ], + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no; xim has been disabled); enable_xim="no") + fi + CPPFLAGS=$cppflags_save + + dnl automatically enable XIM when hangul input isn't enabled + if test "$enable_xim" = "auto" -a "$enable_hangulinput" != "yes" \ + -a "x$GUITYPE" != "xNONE" ; then + AC_MSG_RESULT(X GUI selected; xim has been enabled) + enable_xim="yes" + fi +fi + +if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF"; then + cppflags_save=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $X_CFLAGS" +dnl Xmu/Editres.h may exist but can only be used after including Intrinsic.h + AC_MSG_CHECKING([for X11/Xmu/Editres.h]) + AC_TRY_COMPILE([ +#include +#include ], + [int i; i = 0;], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_X11_XMU_EDITRES_H), + AC_MSG_RESULT(no)) + CPPFLAGS=$cppflags_save +fi + +dnl Only use the Xm directory when compiling Motif, don't use it for Athena +if test -z "$SKIP_MOTIF"; then + cppflags_save=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + if test "$zOSUnix" = "yes"; then + xmheader="Xm/Xm.h" + else + xmheader="Xm/Xm.h Xm/XpmP.h Xm/JoinSideT.h Xm/TraitP.h Xm/Manager.h + Xm/UnhighlightT.h Xm/Notebook.h" + fi + AC_CHECK_HEADERS($xmheader) + + if test "x$ac_cv_header_Xm_XpmP_h" = "xyes"; then + dnl Solaris uses XpmAttributes_21, very annoying. + AC_MSG_CHECKING([for XpmAttributes_21 in Xm/XpmP.h]) + AC_TRY_COMPILE([#include ], [XpmAttributes_21 attr;], + AC_MSG_RESULT(yes); AC_DEFINE(XPMATTRIBUTES_TYPE, XpmAttributes_21), + AC_MSG_RESULT(no); AC_DEFINE(XPMATTRIBUTES_TYPE, XpmAttributes) + ) + else + AC_DEFINE(XPMATTRIBUTES_TYPE, XpmAttributes) + fi + CPPFLAGS=$cppflags_save +fi + +if test "x$GUITYPE" = "xNONE" -a "$enable_xim" = "yes"; then + AC_MSG_RESULT(no GUI selected; xim has been disabled) + enable_xim="no" +fi +if test "x$GUITYPE" = "xNONE" -a "$enable_fontset" = "yes"; then + AC_MSG_RESULT(no GUI selected; fontset has been disabled) + enable_fontset="no" +fi +if test "x$GUITYPE:$enable_fontset" = "xGTK:yes"; then + AC_MSG_RESULT(GTK+ 2 GUI selected; fontset has been disabled) + enable_fontset="no" +fi + +if test -z "$SKIP_PHOTON"; then + GUITYPE=PHOTONGUI +fi + +AC_SUBST(GUI_INC_LOC) +AC_SUBST(GUI_LIB_LOC) +AC_SUBST(GUITYPE) +AC_SUBST(GUI_X_LIBS) + +if test "$enable_workshop" = "yes" -a -n "$SKIP_MOTIF"; then + AC_MSG_ERROR([cannot use workshop without Motif]) +fi + +dnl defining FEAT_XIM and FEAT_XFONTSET is delayed, so that they can be disabled +if test "$enable_xim" = "yes"; then + AC_DEFINE(FEAT_XIM) +fi +if test "$enable_fontset" = "yes"; then + AC_DEFINE(FEAT_XFONTSET) +fi + + +dnl --------------------------------------------------------------------------- +dnl end of GUI-checking +dnl --------------------------------------------------------------------------- + +AC_MSG_CHECKING([for /proc link to executable]) +if test -L "/proc/self/exe"; then + dnl Linux + AC_MSG_RESULT([/proc/self/exe]) + AC_DEFINE(PROC_EXE_LINK, "/proc/self/exe") +elif test -L "/proc/self/path/a.out"; then + dnl Solaris + AC_MSG_RESULT([/proc/self/path/a.out]) + AC_DEFINE(PROC_EXE_LINK, "/proc/self/path/a.out") +elif test -L "/proc/curproc/file"; then + dnl FreeBSD + AC_MSG_RESULT([/proc/curproc/file]) + AC_DEFINE(PROC_EXE_LINK, "/proc/curproc/file") +else + AC_MSG_RESULT(no) +fi + +dnl Check for Cygwin, which needs an extra source file if not using X11 +AC_MSG_CHECKING(for CYGWIN or MSYS environment) +case `uname` in + CYGWIN*|MSYS*) CYGWIN=yes; AC_MSG_RESULT(yes) + AC_MSG_CHECKING(for CYGWIN clipboard support) + if test "x$with_x" = "xno" ; then + OS_EXTRA_SRC=winclip.c; OS_EXTRA_OBJ=objects/winclip.o + AC_MSG_RESULT(yes) + AC_DEFINE(FEAT_CYGWIN_WIN32_CLIPBOARD) + else + AC_MSG_RESULT(no - using X11) + fi ;; + + *) CYGWIN=no; AC_MSG_RESULT(no);; +esac + +dnl Only really enable hangul input when GUI and XFONTSET are available +if test "$enable_hangulinput" = "yes"; then + if test "x$GUITYPE" = "xNONE"; then + AC_MSG_RESULT(no GUI selected; hangul input has been disabled) + enable_hangulinput=no + else + AC_DEFINE(FEAT_HANGULIN) + HANGULIN_SRC=hangulin.c + AC_SUBST(HANGULIN_SRC) + HANGULIN_OBJ=objects/hangulin.o + AC_SUBST(HANGULIN_OBJ) + fi +fi + +dnl Checks for libraries and include files. + +AC_CACHE_CHECK([whether toupper is broken], [vim_cv_toupper_broken], + [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include "confdefs.h" +#include +#if STDC_HEADERS +# include +# include +#endif +main() { exit(toupper('A') == 'A' && tolower('z') == 'z'); } + ]])],[ + vim_cv_toupper_broken=yes + ],[ + vim_cv_toupper_broken=no + ],[ + AC_MSG_ERROR(cross-compiling: please set 'vim_cv_toupper_broken') + ])]) + +if test "x$vim_cv_toupper_broken" = "xyes" ; then + AC_DEFINE(BROKEN_TOUPPER) +fi + +AC_MSG_CHECKING(whether __DATE__ and __TIME__ work) +AC_TRY_COMPILE([#include ], [printf("(" __DATE__ " " __TIME__ ")");], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_DATE_TIME), + AC_MSG_RESULT(no)) + +AC_MSG_CHECKING(whether __attribute__((unused)) is allowed) +AC_TRY_COMPILE([#include ], [int x __attribute__((unused));], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_ATTRIBUTE_UNUSED), + AC_MSG_RESULT(no)) + +dnl Checks for header files. +AC_CHECK_HEADER(elf.h, HAS_ELF=1) +dnl AC_CHECK_HEADER(dwarf.h, SVR4=1) +if test "$HAS_ELF" = 1; then + AC_CHECK_LIB(elf, main) +fi + +AC_HEADER_DIRENT + +dnl If sys/wait.h is not found it might still exist but not be POSIX +dnl compliant. In that case we define HAVE_UNION_WAIT (for NeXT) +if test $ac_cv_header_sys_wait_h = no; then + AC_MSG_CHECKING([for sys/wait.h that defines union wait]) + AC_TRY_COMPILE([#include ], + [union wait xx, yy; xx = yy], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SYS_WAIT_H) + AC_DEFINE(HAVE_UNION_WAIT), + AC_MSG_RESULT(no)) +fi + +AC_CHECK_HEADERS(stdint.h stdlib.h string.h \ + sys/select.h sys/utsname.h termcap.h fcntl.h \ + sgtty.h sys/ioctl.h sys/time.h sys/types.h \ + termio.h iconv.h inttypes.h langinfo.h math.h \ + unistd.h stropts.h errno.h sys/resource.h \ + sys/systeminfo.h locale.h sys/stream.h termios.h \ + libc.h sys/statfs.h poll.h sys/poll.h pwd.h \ + utime.h sys/param.h sys/ptms.h libintl.h libgen.h \ + util/debug.h util/msg18n.h frame.h sys/acl.h \ + sys/access.h sys/sysinfo.h wchar.h wctype.h) + +dnl sys/ptem.h depends on sys/stream.h on Solaris +AC_CHECK_HEADERS(sys/ptem.h, [], [], +[#if defined HAVE_SYS_STREAM_H +# include +#endif]) + +dnl sys/sysctl.h depends on sys/param.h on OpenBSD +AC_CHECK_HEADERS(sys/sysctl.h, [], [], +[#if defined HAVE_SYS_PARAM_H +# include +#endif]) + + +dnl pthread_np.h may exist but can only be used after including pthread.h +AC_MSG_CHECKING([for pthread_np.h]) +AC_TRY_COMPILE([ +#include +#include ], + [int i; i = 0;], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_PTHREAD_NP_H), + AC_MSG_RESULT(no)) + +AC_CHECK_HEADERS(strings.h) +if test "x$MACOS_X" = "xyes"; then + dnl The strings.h file on OS/X contains a warning and nothing useful. + AC_DEFINE(NO_STRINGS_WITH_STRING_H) +else + +dnl Check if strings.h and string.h can both be included when defined. +AC_MSG_CHECKING([if strings.h can be included after string.h]) +cppflags_save=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $X_CFLAGS" +AC_TRY_COMPILE([ +#if defined(_AIX) && !defined(_AIX51) && !defined(_NO_PROTO) +# define _NO_PROTO /* like in os_unix.h, causes conflict for AIX (Winn) */ + /* but don't do it on AIX 5.1 (Uribarri) */ +#endif +#ifdef HAVE_XM_XM_H +# include /* This breaks it for HP-UX 11 (Squassabia) */ +#endif +#ifdef HAVE_STRING_H +# include +#endif +#if defined(HAVE_STRINGS_H) +# include +#endif + ], [int i; i = 0;], + AC_MSG_RESULT(yes), + AC_DEFINE(NO_STRINGS_WITH_STRING_H) + AC_MSG_RESULT(no)) +CPPFLAGS=$cppflags_save +fi + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_PROG_GCC_TRADITIONAL +AC_C_CONST +AC_C_VOLATILE +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_UID_T +AC_TYPE_UINT32_T + +AC_HEADER_TIME +AC_CHECK_TYPE(ino_t, long) +AC_CHECK_TYPE(dev_t, unsigned) +AC_C_BIGENDIAN(,,,) +AC_C_INLINE + +AC_MSG_CHECKING(for rlim_t) +if eval "test \"`echo '$''{'ac_cv_type_rlim_t'+set}'`\" = set"; then + AC_MSG_RESULT([(cached) $ac_cv_type_rlim_t]) +else + AC_EGREP_CPP(dnl +changequote(<<,>>)dnl +<<(^|[^a-zA-Z_0-9])rlim_t[^a-zA-Z_0-9]>>dnl +changequote([,]), + [ +#include +#if STDC_HEADERS +# include +# include +#endif +#ifdef HAVE_SYS_RESOURCE_H +# include +#endif + ], ac_cv_type_rlim_t=yes, ac_cv_type_rlim_t=no) + AC_MSG_RESULT($ac_cv_type_rlim_t) +fi +if test $ac_cv_type_rlim_t = no; then + cat >> confdefs.h <<\EOF +#define rlim_t unsigned long +EOF +fi + +AC_MSG_CHECKING(for stack_t) +if eval "test \"`echo '$''{'ac_cv_type_stack_t'+set}'`\" = set"; then + AC_MSG_RESULT([(cached) $ac_cv_type_stack_t]) +else + AC_EGREP_CPP(stack_t, + [ +#include +#if STDC_HEADERS +# include +# include +#endif +#include + ], ac_cv_type_stack_t=yes, ac_cv_type_stack_t=no) + AC_MSG_RESULT($ac_cv_type_stack_t) +fi +if test $ac_cv_type_stack_t = no; then + cat >> confdefs.h <<\EOF +#define stack_t struct sigaltstack +EOF +fi + +dnl BSDI uses ss_base while others use ss_sp for the stack pointer. +AC_MSG_CHECKING(whether stack_t has an ss_base field) +AC_TRY_COMPILE([ +#include +#if STDC_HEADERS +# include +# include +#endif +#include +#include "confdefs.h" + ], [stack_t sigstk; sigstk.ss_base = 0; ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SS_BASE), + AC_MSG_RESULT(no)) + +olibs="$LIBS" +AC_MSG_CHECKING(--with-tlib argument) +AC_ARG_WITH(tlib, [ --with-tlib=library terminal library to be used ],) +if test -n "$with_tlib"; then + AC_MSG_RESULT($with_tlib) + LIBS="$LIBS -l$with_tlib" + AC_MSG_CHECKING(for linking with $with_tlib library) + AC_TRY_LINK([], [], AC_MSG_RESULT(OK), AC_MSG_ERROR(FAILED)) + dnl Need to check for tgetent() below. + olibs="$LIBS" +else + AC_MSG_RESULT([empty: automatic terminal library selection]) + dnl On HP-UX 10.10 termcap or termlib should be used instead of + dnl curses, because curses is much slower. + dnl Newer versions of ncurses are preferred over anything, except + dnl when tinfo has been split off, it contains all we need. + dnl Older versions of ncurses have bugs, get a new one! + dnl Digital Unix (OSF1) should use curses (Ronald Schild). + dnl On SCO Openserver should prefer termlib (Roger Cornelius). + case "`uname -s 2>/dev/null`" in + OSF1|SCO_SV) tlibs="tinfo ncurses curses termlib termcap";; + *) tlibs="tinfo ncurses termlib termcap curses";; + esac + for libname in $tlibs; do + AC_CHECK_LIB(${libname}, tgetent,,) + if test "x$olibs" != "x$LIBS"; then + dnl It's possible that a library is found but it doesn't work + dnl e.g., shared library that cannot be found + dnl compile and run a test program to be sure + AC_TRY_RUN([ +#ifdef HAVE_TERMCAP_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +main() {char *s; s=(char *)tgoto("%p1%d", 0, 1); exit(0); }], + res="OK", res="FAIL", res="FAIL") + if test "$res" = "OK"; then + break + fi + AC_MSG_RESULT($libname library is not usable) + LIBS="$olibs" + fi + done + if test "x$olibs" = "x$LIBS"; then + AC_MSG_RESULT(no terminal library found) + fi +fi + +if test "x$olibs" = "x$LIBS"; then + AC_MSG_CHECKING([for tgetent()]) + AC_TRY_LINK([], + [char s[10000]; int res = tgetent(s, "thisterminaldoesnotexist");], + AC_MSG_RESULT(yes), + AC_MSG_ERROR([NOT FOUND! + You need to install a terminal library; for example ncurses. + Or specify the name of the library with --with-tlib.])) +fi + +AC_CACHE_CHECK([whether we talk terminfo], [vim_cv_terminfo], + [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include "confdefs.h" +#ifdef HAVE_TERMCAP_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +main() +{char *s; s=(char *)tgoto("%p1%d", 0, 1); exit(!strcmp(s==0 ? "" : s, "1")); } + ]])],[ + vim_cv_terminfo=no + ],[ + vim_cv_terminfo=yes + ],[ + AC_MSG_ERROR(cross-compiling: please set 'vim_cv_terminfo') + ]) + ]) + +if test "x$vim_cv_terminfo" = "xyes" ; then + AC_DEFINE(TERMINFO) +fi + +AC_CACHE_CHECK([what tgetent() returns for an unknown terminal], [vim_cv_tgetent], + [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include "confdefs.h" +#ifdef HAVE_TERMCAP_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +main() +{char s[10000]; int res = tgetent(s, "thisterminaldoesnotexist"); exit(res != 0); } + ]])],[ + vim_cv_tgetent=zero + ],[ + vim_cv_tgetent=non-zero + ],[ + AC_MSG_ERROR(failed to compile test program.) + ]) + ]) + +if test "x$vim_cv_tgetent" = "xzero" ; then + AC_DEFINE(TGETENT_ZERO_ERR, 0) +fi + +AC_MSG_CHECKING(whether termcap.h contains ospeed) +AC_TRY_LINK([ +#ifdef HAVE_TERMCAP_H +# include +#endif + ], [ospeed = 20000], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_OSPEED), + [AC_MSG_RESULT(no) + AC_MSG_CHECKING(whether ospeed can be extern) + AC_TRY_LINK([ +#ifdef HAVE_TERMCAP_H +# include +#endif +extern short ospeed; + ], [ospeed = 20000], + AC_MSG_RESULT(yes); AC_DEFINE(OSPEED_EXTERN), + AC_MSG_RESULT(no))] + ) + +AC_MSG_CHECKING([whether termcap.h contains UP, BC and PC]) +AC_TRY_LINK([ +#ifdef HAVE_TERMCAP_H +# include +#endif + ], [if (UP == 0 && BC == 0) PC = 1], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_UP_BC_PC), + [AC_MSG_RESULT(no) + AC_MSG_CHECKING([whether UP, BC and PC can be extern]) + AC_TRY_LINK([ +#ifdef HAVE_TERMCAP_H +# include +#endif +extern char *UP, *BC, PC; + ], [if (UP == 0 && BC == 0) PC = 1], + AC_MSG_RESULT(yes); AC_DEFINE(UP_BC_PC_EXTERN), + AC_MSG_RESULT(no))] + ) + +AC_MSG_CHECKING(whether tputs() uses outfuntype) +AC_TRY_COMPILE([ +#ifdef HAVE_TERMCAP_H +# include +#endif + ], [extern int xx(); tputs("test", 1, (outfuntype)xx)], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_OUTFUNTYPE), + AC_MSG_RESULT(no)) + +dnl On some SCO machines sys/select redefines struct timeval +AC_MSG_CHECKING([whether sys/select.h and sys/time.h may both be included]) +AC_TRY_COMPILE([ +#include +#include +#include ], , + AC_MSG_RESULT(yes) + AC_DEFINE(SYS_SELECT_WITH_SYS_TIME), + AC_MSG_RESULT(no)) + +dnl AC_DECL_SYS_SIGLIST + +dnl Checks for pty.c (copied from screen) ========================== +AC_MSG_CHECKING(for /dev/ptc) +if test -r /dev/ptc; then + AC_DEFINE(HAVE_DEV_PTC) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +AC_MSG_CHECKING(for SVR4 ptys) +if test -c /dev/ptmx ; then + AC_TRY_LINK([], [ptsname(0);grantpt(0);unlockpt(0);], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SVR4_PTYS), + AC_MSG_RESULT(no)) +else + AC_MSG_RESULT(no) +fi + +AC_MSG_CHECKING(for ptyranges) +if test -d /dev/ptym ; then + pdir='/dev/ptym' +else + pdir='/dev' +fi +dnl SCO uses ptyp%d +AC_EGREP_CPP(yes, +[#ifdef M_UNIX + yes; +#endif + ], ptys=`echo /dev/ptyp??`, ptys=`echo $pdir/pty??`) +dnl if test -c /dev/ptyp19; then +dnl ptys=`echo /dev/ptyp??` +dnl else +dnl ptys=`echo $pdir/pty??` +dnl fi +if test "$ptys" != "$pdir/pty??" ; then + p0=`echo $ptys | tr ' ' '\012' | sed -e 's/^.*\(.\).$/\1/g' | sort -u | tr -d '\012'` + p1=`echo $ptys | tr ' ' '\012' | sed -e 's/^.*\(.\)$/\1/g' | sort -u | tr -d '\012'` + AC_DEFINE_UNQUOTED(PTYRANGE0,"$p0") + AC_DEFINE_UNQUOTED(PTYRANGE1,"$p1") + AC_MSG_RESULT([$p0 / $p1]) +else + AC_MSG_RESULT([don't know]) +fi + +dnl **** pty mode/group handling **** +dnl +dnl support provided by Luke Mewburn , 931222 +rm -f conftest_grp +AC_CACHE_CHECK([default tty permissions/group], [vim_cv_tty_group], + [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include "confdefs.h" +#include +#if STDC_HEADERS +# include +# include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +main() +{ + struct stat sb; + char *x,*ttyname(); + int om, m; + FILE *fp; + + if (!(x = ttyname(0))) exit(1); + if (stat(x, &sb)) exit(1); + om = sb.st_mode; + if (om & 002) exit(0); + m = system("mesg y"); + if (m == -1 || m == 127) exit(1); + if (stat(x, &sb)) exit(1); + m = sb.st_mode; + if (chmod(x, om)) exit(1); + if (m & 002) exit(0); + if (sb.st_gid == getgid()) exit(1); + if (!(fp=fopen("conftest_grp", "w"))) + exit(1); + fprintf(fp, "%d\n", sb.st_gid); + fclose(fp); + exit(0); +} + ]])],[ + if test -f conftest_grp; then + vim_cv_tty_group=`cat conftest_grp` + if test "x$vim_cv_tty_mode" = "x" ; then + vim_cv_tty_mode=0620 + fi + AC_MSG_RESULT([pty mode: $vim_cv_tty_mode, group: $vim_cv_tty_group]) + else + vim_cv_tty_group=world + AC_MSG_RESULT([ptys are world accessible]) + fi + ],[ + vim_cv_tty_group=world + AC_MSG_RESULT([can't determine - assume ptys are world accessible]) + ],[ + AC_MSG_ERROR(cross-compiling: please set 'vim_cv_tty_group' and 'vim_cv_tty_mode') + ]) + ]) +rm -f conftest_grp + +if test "x$vim_cv_tty_group" != "xworld" ; then + AC_DEFINE_UNQUOTED(PTYGROUP,$vim_cv_tty_group) + if test "x$vim_cv_tty_mode" = "x" ; then + AC_MSG_ERROR([It seems you're cross compiling and have 'vim_cv_tty_group' set, please also set the environment variable 'vim_cv_tty_mode' to the correct mode (probably 0620)]) + else + AC_DEFINE(PTYMODE, 0620) + fi +fi + +dnl Checks for library functions. =================================== + +AC_TYPE_SIGNAL + +dnl find out what to use at the end of a signal function +if test $ac_cv_type_signal = void; then + AC_DEFINE(SIGRETURN, [return]) +else + AC_DEFINE(SIGRETURN, [return 0]) +fi + +dnl check if struct sigcontext is defined (used for SGI only) +AC_MSG_CHECKING(for struct sigcontext) +AC_TRY_COMPILE([ +#include +test_sig() +{ + struct sigcontext *scont; + scont = (struct sigcontext *)0; + return 1; +} ], , + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SIGCONTEXT), + AC_MSG_RESULT(no)) + +dnl tricky stuff: try to find out if getcwd() is implemented with +dnl system("sh -c pwd") +AC_CACHE_CHECK([getcwd implementation is broken], [vim_cv_getcwd_broken], + [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include "confdefs.h" +#ifdef HAVE_UNISTD_H +#include +#endif +char *dagger[] = { "IFS=pwd", 0 }; +main() +{ + char buffer[500]; + extern char **environ; + environ = dagger; + return getcwd(buffer, 500) ? 0 : 1; +} + ]])],[ + vim_cv_getcwd_broken=no + ],[ + vim_cv_getcwd_broken=yes + ],[ + AC_MSG_ERROR(cross-compiling: please set 'vim_cv_getcwd_broken') + ]) + ]) + +if test "x$vim_cv_getcwd_broken" = "xyes" ; then + AC_DEFINE(BAD_GETCWD) +fi + +dnl Check for functions in one big call, to reduce the size of configure. +dnl Can only be used for functions that do not require any include. +AC_CHECK_FUNCS(fchdir fchown fchmod fsync getcwd getpseudotty \ + getpwent getpwnam getpwuid getrlimit gettimeofday getwd lstat \ + memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \ + getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \ + sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \ + strnicmp strpbrk strtol tgetent towlower towupper iswupper \ + usleep utime utimes mblen ftruncate unsetenv) +AC_FUNC_SELECT_ARGTYPES +AC_FUNC_FSEEKO + +dnl define _LARGE_FILES, _FILE_OFFSET_BITS and _LARGEFILE_SOURCE when +dnl appropriate, so that off_t is 64 bits when needed. +AC_SYS_LARGEFILE + +dnl fstatfs() can take 2 to 4 arguments, try to use st_blksize if possible +AC_MSG_CHECKING(for st_blksize) +AC_TRY_COMPILE( +[#include +#include ], +[ struct stat st; + int n; + + stat("/", &st); + n = (int)st.st_blksize;], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_ST_BLKSIZE), + AC_MSG_RESULT(no)) + +AC_CACHE_CHECK([whether stat() ignores a trailing slash], [vim_cv_stat_ignores_slash], + [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include "confdefs.h" +#if STDC_HEADERS +# include +# include +#endif +#include +#include +main() {struct stat st; exit(stat("configure/", &st) != 0); } + ]])],[ + vim_cv_stat_ignores_slash=yes + ],[ + vim_cv_stat_ignores_slash=no + ],[ + AC_MSG_ERROR(cross-compiling: please set 'vim_cv_stat_ignores_slash') + ]) + ]) + +if test "x$vim_cv_stat_ignores_slash" = "xyes" ; then + AC_DEFINE(STAT_IGNORES_SLASH) +fi + +dnl Link with iconv for charset translation, if not found without library. +dnl check for iconv() requires including iconv.h +dnl Add "-liconv" when possible; Solaris has iconv but use GNU iconv when it +dnl has been installed. +AC_MSG_CHECKING(for iconv_open()) +save_LIBS="$LIBS" +LIBS="$LIBS -liconv" +AC_TRY_LINK([ +#ifdef HAVE_ICONV_H +# include +#endif + ], [iconv_open("fr", "to");], + AC_MSG_RESULT(yes; with -liconv); AC_DEFINE(HAVE_ICONV), + LIBS="$save_LIBS" + AC_TRY_LINK([ +#ifdef HAVE_ICONV_H +# include +#endif + ], [iconv_open("fr", "to");], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_ICONV), + AC_MSG_RESULT(no))) + + +AC_MSG_CHECKING(for nl_langinfo(CODESET)) +AC_TRY_LINK([ +#ifdef HAVE_LANGINFO_H +# include +#endif +], [char *cs = nl_langinfo(CODESET);], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_NL_LANGINFO_CODESET), + AC_MSG_RESULT(no)) + +dnl Need various functions for floating point support. Only enable +dnl floating point when they are all present. +AC_CHECK_LIB(m, strtod) +AC_MSG_CHECKING([for strtod() and other floating point functions]) +AC_TRY_LINK([ +#ifdef HAVE_MATH_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +], [char *s; double d; + d = strtod("1.1", &s); + d = fabs(1.11); + d = ceil(1.11); + d = floor(1.11); + d = log10(1.11); + d = pow(1.11, 2.22); + d = sqrt(1.11); + d = sin(1.11); + d = cos(1.11); + d = atan(1.11); + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_FLOAT_FUNCS), + AC_MSG_RESULT(no)) + +dnl isinf() and isnan() need to include header files and may need -lm. +AC_MSG_CHECKING([for isinf()]) +AC_TRY_LINK([ +#ifdef HAVE_MATH_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +], [int r = isinf(1.11); ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_ISINF), + AC_MSG_RESULT(no)) + +AC_MSG_CHECKING([for isnan()]) +AC_TRY_LINK([ +#ifdef HAVE_MATH_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +], [int r = isnan(1.11); ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_ISNAN), + AC_MSG_RESULT(no)) + +dnl Link with -lposix1e for ACL stuff; if not found, try -lacl for SGI +dnl when -lacl works, also try to use -lattr (required for Debian). +dnl On Solaris, use the acl_get/set functions in libsec, if present. +AC_MSG_CHECKING(--disable-acl argument) +AC_ARG_ENABLE(acl, + [ --disable-acl No check for ACL support.], + , [enable_acl="yes"]) +if test "$enable_acl" = "yes"; then + AC_MSG_RESULT(no) + AC_CHECK_LIB(posix1e, acl_get_file, [LIBS="$LIBS -lposix1e"], + AC_CHECK_LIB(acl, acl_get_file, [LIBS="$LIBS -lacl" + AC_CHECK_LIB(attr, fgetxattr, LIBS="$LIBS -lattr",,)],,),) + + AC_MSG_CHECKING(for POSIX ACL support) + AC_TRY_LINK([ +#include +#ifdef HAVE_SYS_ACL_H +# include +#endif +acl_t acl;], [acl = acl_get_file("foo", ACL_TYPE_ACCESS); + acl_set_file("foo", ACL_TYPE_ACCESS, acl); + acl_free(acl);], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_POSIX_ACL), + AC_MSG_RESULT(no)) + + AC_CHECK_LIB(sec, acl_get, [LIBS="$LIBS -lsec"; AC_DEFINE(HAVE_SOLARIS_ZFS_ACL)], + AC_MSG_CHECKING(for Solaris ACL support) + AC_TRY_LINK([ +#ifdef HAVE_SYS_ACL_H +# include +#endif], [acl("foo", GETACLCNT, 0, NULL); + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SOLARIS_ACL), + AC_MSG_RESULT(no))) + + AC_MSG_CHECKING(for AIX ACL support) + AC_TRY_LINK([ +#if STDC_HEADERS +# include +# include +#endif +#ifdef HAVE_SYS_ACL_H +# include +#endif +#ifdef HAVE_SYS_ACCESS_H +# include +#endif +#define _ALL_SOURCE + +#include + +int aclsize; +struct acl *aclent;], [aclsize = sizeof(struct acl); + aclent = (void *)malloc(aclsize); + statacl("foo", STX_NORMAL, aclent, aclsize); + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_AIX_ACL), + AC_MSG_RESULT(no)) +else + AC_MSG_RESULT(yes) +fi + +if test "x$GTK_CFLAGS" != "x"; then + dnl pango_shape_full() is new, fall back to pango_shape(). + AC_MSG_CHECKING(for pango_shape_full) + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $GTK_CFLAGS" + LIBS="$LIBS $GTK_LIBS" + AC_TRY_LINK( + [#include ], + [ pango_shape_full(NULL, 0, NULL, 0, NULL, NULL); ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_PANGO_SHAPE_FULL), + AC_MSG_RESULT(no)) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" +fi + +AC_MSG_CHECKING(--disable-gpm argument) +AC_ARG_ENABLE(gpm, + [ --disable-gpm Don't use gpm (Linux mouse daemon).], , + [enable_gpm="yes"]) + +if test "$enable_gpm" = "yes"; then + AC_MSG_RESULT(no) + dnl Checking if gpm support can be compiled + AC_CACHE_CHECK([for gpm], vi_cv_have_gpm, + [olibs="$LIBS" ; LIBS="-lgpm"] + AC_TRY_LINK( + [#include + #include ], + [Gpm_GetLibVersion(NULL);], + dnl Configure defines HAVE_GPM, if it is defined feature.h defines + dnl FEAT_MOUSE_GPM if mouse support is included + [vi_cv_have_gpm=yes], + [vi_cv_have_gpm=no]) + [LIBS="$olibs"] + ) + if test $vi_cv_have_gpm = yes; then + LIBS="$LIBS -lgpm" + AC_DEFINE(HAVE_GPM) + fi +else + AC_MSG_RESULT(yes) +fi + +AC_MSG_CHECKING(--disable-sysmouse argument) +AC_ARG_ENABLE(sysmouse, + [ --disable-sysmouse Don't use sysmouse (mouse in *BSD console).], , + [enable_sysmouse="yes"]) + +if test "$enable_sysmouse" = "yes"; then + AC_MSG_RESULT(no) + dnl Checking if sysmouse support can be compiled + dnl Configure defines HAVE_SYSMOUSE, if it is defined feature.h + dnl defines FEAT_SYSMOUSE if mouse support is included + AC_CACHE_CHECK([for sysmouse], vi_cv_have_sysmouse, + AC_TRY_LINK( + [#include + #include + #include ], + [struct mouse_info mouse; + mouse.operation = MOUSE_MODE; + mouse.operation = MOUSE_SHOW; + mouse.u.mode.mode = 0; + mouse.u.mode.signal = SIGUSR2;], + [vi_cv_have_sysmouse=yes], + [vi_cv_have_sysmouse=no]) + ) + if test $vi_cv_have_sysmouse = yes; then + AC_DEFINE(HAVE_SYSMOUSE) + fi +else + AC_MSG_RESULT(yes) +fi + +dnl make sure the FD_CLOEXEC flag for fcntl()'s F_SETFD command is known +AC_MSG_CHECKING(for FD_CLOEXEC) +AC_TRY_COMPILE( +[#if HAVE_FCNTL_H +# include +#endif], +[ int flag = FD_CLOEXEC;], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_FD_CLOEXEC), + AC_MSG_RESULT(not usable)) + +dnl rename needs to be checked separately to work on Nextstep with cc +AC_MSG_CHECKING(for rename) +AC_TRY_LINK([#include ], [rename("this", "that")], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_RENAME), + AC_MSG_RESULT(no)) + +dnl sysctl() may exist but not the arguments we use +AC_MSG_CHECKING(for sysctl) +AC_TRY_COMPILE( +[#include +#include ], +[ int mib[2], r; + size_t len; + + mib[0] = CTL_HW; + mib[1] = HW_USERMEM; + len = sizeof(r); + (void)sysctl(mib, 2, &r, &len, (void *)0, (size_t)0); + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL), + AC_MSG_RESULT(not usable)) + +dnl sysinfo() may exist but not be Linux compatible +AC_MSG_CHECKING(for sysinfo) +AC_TRY_COMPILE( +[#include +#include ], +[ struct sysinfo sinfo; + int t; + + (void)sysinfo(&sinfo); + t = sinfo.totalram; + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSINFO), + AC_MSG_RESULT(not usable)) + +dnl struct sysinfo may have the mem_unit field or not +AC_MSG_CHECKING(for sysinfo.mem_unit) +AC_TRY_COMPILE( +[#include +#include ], +[ struct sysinfo sinfo; + sinfo.mem_unit = 1; + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSINFO_MEM_UNIT), + AC_MSG_RESULT(no)) + +dnl sysconf() may exist but not support what we want to use +AC_MSG_CHECKING(for sysconf) +AC_TRY_COMPILE( +[#include ], +[ (void)sysconf(_SC_PAGESIZE); + (void)sysconf(_SC_PHYS_PAGES); + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCONF), + AC_MSG_RESULT(not usable)) + +AC_CHECK_SIZEOF([int]) +AC_CHECK_SIZEOF([long]) +AC_CHECK_SIZEOF([time_t]) +AC_CHECK_SIZEOF([off_t]) + +dnl Use different names to avoid clashing with other header files. +AC_DEFINE_UNQUOTED(VIM_SIZEOF_INT, [$ac_cv_sizeof_int]) +AC_DEFINE_UNQUOTED(VIM_SIZEOF_LONG, [$ac_cv_sizeof_long]) + +dnl Make sure that uint32_t is really 32 bits unsigned. +AC_MSG_CHECKING([uint32_t is 32 bits]) +AC_TRY_RUN([ +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +main() { + uint32_t nr1 = (uint32_t)-1; + uint32_t nr2 = (uint32_t)0xffffffffUL; + if (sizeof(uint32_t) != 4 || nr1 != 0xffffffffUL || nr2 + 1 != 0) exit(1); + exit(0); +}], +AC_MSG_RESULT(ok), +AC_MSG_ERROR([WRONG! uint32_t not defined correctly.]), +AC_MSG_WARN([cannot check uint32_t when cross-compiling.])) + +dnl Check for memmove() before bcopy(), makes memmove() be used when both are +dnl present, fixes problem with incompatibility between Solaris 2.4 and 2.5. + +[bcopy_test_prog=' +#include "confdefs.h" +#ifdef HAVE_STRING_H +# include +#endif +#if STDC_HEADERS +# include +# include +#endif +main() { + char buf[10]; + strcpy(buf, "abcdefghi"); + mch_memmove(buf, buf + 2, 3); + if (strncmp(buf, "ababcf", 6)) + exit(1); + strcpy(buf, "abcdefghi"); + mch_memmove(buf + 2, buf, 3); + if (strncmp(buf, "cdedef", 6)) + exit(1); + exit(0); /* libc version works properly. */ +}'] + +AC_CACHE_CHECK([whether memmove handles overlaps],[vim_cv_memmove_handles_overlap], + [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[#define mch_memmove(s,d,l) memmove(d,s,l) $bcopy_test_prog]])], + [ + vim_cv_memmove_handles_overlap=yes + ],[ + vim_cv_memmove_handles_overlap=no + ],[ + AC_MSG_ERROR(cross-compiling: please set 'vim_cv_memmove_handles_overlap') + ]) + ]) + +if test "x$vim_cv_memmove_handles_overlap" = "xyes" ; then + AC_DEFINE(USEMEMMOVE) +else + AC_CACHE_CHECK([whether bcopy handles overlaps],[vim_cv_bcopy_handles_overlap], + [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[#define mch_bcopy(s,d,l) bcopy(d,s,l) $bcopy_test_prog]])], + [ + vim_cv_bcopy_handles_overlap=yes + ],[ + vim_cv_bcopy_handles_overlap=no + ],[ + AC_MSG_ERROR(cross-compiling: please set 'vim_cv_bcopy_handles_overlap') + ]) + ]) + + if test "x$vim_cv_bcopy_handles_overlap" = "xyes" ; then + AC_DEFINE(USEBCOPY) + else + AC_CACHE_CHECK([whether memcpy handles overlaps],[vim_cv_memcpy_handles_overlap], + [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[#define mch_memcpy(s,d,l) memcpy(d,s,l) $bcopy_test_prog]])], + [ + vim_cv_memcpy_handles_overlap=yes + ],[ + vim_cv_memcpy_handles_overlap=no + ],[ + AC_MSG_ERROR(cross-compiling: please set 'vim_cv_memcpy_handles_overlap') + ]) + ]) + + if test "x$vim_cv_memcpy_handles_overlap" = "xyes" ; then + AC_DEFINE(USEMEMCPY) + fi + fi +fi + + +dnl Check for multibyte locale functions +dnl Find out if _Xsetlocale() is supported by libX11. +dnl Check if X_LOCALE should be defined. +if test "x$with_x" = "xyes"; then + cflags_save=$CFLAGS + libs_save=$LIBS + LIBS="$LIBS $X_LIBS $GUI_LIB_LOC $GUI_X_LIBS $X_PRE_LIBS $X_LIB $X_EXTRA_LIBS" + CFLAGS="$CFLAGS $X_CFLAGS" + + AC_MSG_CHECKING(whether X_LOCALE needed) + AC_TRY_COMPILE([#include ],, + AC_TRY_LINK_FUNC([_Xsetlocale], [AC_MSG_RESULT(yes) + AC_DEFINE(X_LOCALE)], AC_MSG_RESULT(no)), + AC_MSG_RESULT(no)) + + AC_MSG_CHECKING(whether Xutf8SetWMProperties() can be used) + AC_TRY_LINK_FUNC([Xutf8SetWMProperties], [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_XUTF8SETWMPROPERTIES)], AC_MSG_RESULT(no)) + + CFLAGS=$cflags_save + LIBS=$libs_save +fi + +dnl Link with xpg4, it is said to make Korean locale working +AC_CHECK_LIB(xpg4, _xpg4_setrunelocale, [LIBS="$LIBS -lxpg4"],,) + +dnl Check how we can run ctags. Default to "ctags" when nothing works. +dnl Use --version to detect Exuberant ctags (preferred) +dnl Add --fields=+S to get function signatures for omni completion. +dnl -t for typedefs (many ctags have this) +dnl -s for static functions (Elvis ctags only?) +dnl -v for variables. Dangerous, most ctags take this for 'vgrind style'. +dnl -i+m to test for older Exuberant ctags +AC_MSG_CHECKING(how to create tags) +test -f tags && mv tags tags.save +if (eval ctags --version /dev/null | grep Exuberant) < /dev/null 1>&AC_FD_CC 2>&1; then + TAGPRG="ctags -I INIT+ --fields=+S" +elif (eval exctags --version /dev/null | grep Exuberant) < /dev/null 1>&AC_FD_CC 2>&1; then + TAGPRG="exctags -I INIT+ --fields=+S" +elif (eval exuberant-ctags --version /dev/null | grep Exuberant) < /dev/null 1>&AC_FD_CC 2>&1; then + TAGPRG="exuberant-ctags -I INIT+ --fields=+S" +else + TAGPRG="ctags" + (eval etags /dev/null) < /dev/null 1>&AC_FD_CC 2>&1 && TAGPRG="etags" + (eval etags -c /dev/null) < /dev/null 1>&AC_FD_CC 2>&1 && TAGPRG="etags -c" + (eval ctags /dev/null) < /dev/null 1>&AC_FD_CC 2>&1 && TAGPRG="ctags" + (eval ctags -t /dev/null) < /dev/null 1>&AC_FD_CC 2>&1 && TAGPRG="ctags -t" + (eval ctags -ts /dev/null) < /dev/null 1>&AC_FD_CC 2>&1 && TAGPRG="ctags -ts" + (eval ctags -tvs /dev/null) < /dev/null 1>&AC_FD_CC 2>&1 && TAGPRG="ctags -tvs" + (eval ctags -i+m /dev/null) < /dev/null 1>&AC_FD_CC 2>&1 && TAGPRG="ctags -i+m" +fi +test -f tags.save && mv tags.save tags +AC_MSG_RESULT($TAGPRG) AC_SUBST(TAGPRG) + +dnl Check how we can run man with a section number +AC_MSG_CHECKING(how to run man with a section nr) +MANDEF="man" +(eval MANPAGER=cat PAGER=cat man -s 2 read) < /dev/null > /dev/null 2>&AC_FD_CC && MANDEF="man -s" +AC_MSG_RESULT($MANDEF) +if test "$MANDEF" = "man -s"; then + AC_DEFINE(USEMAN_S) +fi + +dnl Check if gettext() is working and if it needs -lintl +dnl We take care to base this on an empty LIBS: on some systems libelf would be +dnl in LIBS and implicitly take along libintl. The final LIBS would then not +dnl contain libintl, and the link step would fail due to -Wl,--as-needed. +AC_MSG_CHECKING(--disable-nls argument) +AC_ARG_ENABLE(nls, + [ --disable-nls Don't support NLS (gettext()).], , + [enable_nls="yes"]) + +if test "$enable_nls" = "yes"; then + AC_MSG_RESULT(no) + + INSTALL_LANGS=install-languages + AC_SUBST(INSTALL_LANGS) + INSTALL_TOOL_LANGS=install-tool-languages + AC_SUBST(INSTALL_TOOL_LANGS) + + AC_CHECK_PROG(MSGFMT, msgfmt, msgfmt, ) + AC_MSG_CHECKING([for NLS]) + if test -f po/Makefile; then + have_gettext="no" + if test -n "$MSGFMT"; then + olibs=$LIBS + LIBS="" + AC_TRY_LINK( + [#include ], + [gettext("Test");], + AC_MSG_RESULT([gettext() works]); have_gettext="yes"; LIBS=$olibs, + LIBS="-lintl" + AC_TRY_LINK( + [#include ], + [gettext("Test");], + AC_MSG_RESULT([gettext() works with -lintl]); have_gettext="yes"; + LIBS="$olibs -lintl", + AC_MSG_RESULT([gettext() doesn't work]); + LIBS=$olibs)) + else + AC_MSG_RESULT([msgfmt not found - disabled]); + fi + if test $have_gettext = "yes" -a "x$features" != "xtiny" -a "x$features" != "xsmall"; then + AC_DEFINE(HAVE_GETTEXT) + MAKEMO=yes + AC_SUBST(MAKEMO) + dnl this was added in GNU gettext 0.10.36 + AC_CHECK_FUNCS(bind_textdomain_codeset) + dnl _nl_msg_cat_cntr is required for GNU gettext + AC_MSG_CHECKING([for _nl_msg_cat_cntr]) + AC_TRY_LINK( + [#include + extern int _nl_msg_cat_cntr;], + [++_nl_msg_cat_cntr;], + AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_NL_MSG_CAT_CNTR), + AC_MSG_RESULT([no])) + fi + else + AC_MSG_RESULT([no "po/Makefile" - disabled]); + fi +else + AC_MSG_RESULT(yes) +fi + +dnl Check for dynamic linking loader +AC_CHECK_HEADER(dlfcn.h, DLL=dlfcn.h, [AC_CHECK_HEADER(dl.h, DLL=dl.h)]) +if test x${DLL} = xdlfcn.h; then + AC_DEFINE(HAVE_DLFCN_H, 1, [ Define if we have dlfcn.h. ]) + AC_MSG_CHECKING([for dlopen()]) + AC_TRY_LINK(,[ + extern void* dlopen(); + dlopen(); + ], + AC_MSG_RESULT(yes); + AC_DEFINE(HAVE_DLOPEN, 1, [ Define if we have dlopen() ]), + AC_MSG_RESULT(no); + AC_MSG_CHECKING([for dlopen() in -ldl]) + olibs=$LIBS + LIBS="$LIBS -ldl" + AC_TRY_LINK(,[ + extern void* dlopen(); + dlopen(); + ], + AC_MSG_RESULT(yes); + AC_DEFINE(HAVE_DLOPEN, 1, [ Define if we have dlopen() ]), + AC_MSG_RESULT(no); + LIBS=$olibs)) + dnl ReliantUNIX has dlopen() in libc but everything else in libdl + dnl ick :-) + AC_MSG_CHECKING([for dlsym()]) + AC_TRY_LINK(,[ + extern void* dlsym(); + dlsym(); + ], + AC_MSG_RESULT(yes); + AC_DEFINE(HAVE_DLSYM, 1, [ Define if we have dlsym() ]), + AC_MSG_RESULT(no); + AC_MSG_CHECKING([for dlsym() in -ldl]) + olibs=$LIBS + LIBS="$LIBS -ldl" + AC_TRY_LINK(,[ + extern void* dlsym(); + dlsym(); + ], + AC_MSG_RESULT(yes); + AC_DEFINE(HAVE_DLSYM, 1, [ Define if we have dlsym() ]), + AC_MSG_RESULT(no); + LIBS=$olibs)) +elif test x${DLL} = xdl.h; then + AC_DEFINE(HAVE_DL_H, 1, [ Define if we have dl.h. ]) + AC_MSG_CHECKING([for shl_load()]) + AC_TRY_LINK(,[ + extern void* shl_load(); + shl_load(); + ], + AC_MSG_RESULT(yes); + AC_DEFINE(HAVE_SHL_LOAD, 1, [ Define if we have shl_load() ]), + AC_MSG_RESULT(no); + AC_MSG_CHECKING([for shl_load() in -ldld]) + olibs=$LIBS + LIBS="$LIBS -ldld" + AC_TRY_LINK(,[ + extern void* shl_load(); + shl_load(); + ], + AC_MSG_RESULT(yes); + AC_DEFINE(HAVE_SHL_LOAD, 1, [ Define if we have shl_load() ]), + AC_MSG_RESULT(no); + LIBS=$olibs)) +fi +AC_CHECK_HEADERS(setjmp.h) + +if test "x$MACOS_X" = "xyes" -a -n "$PERL"; then + dnl -ldl must come after DynaLoader.a + if echo $LIBS | grep -e '-ldl' >/dev/null; then + LIBS=`echo $LIBS | sed s/-ldl//` + PERL_LIBS="$PERL_LIBS -ldl" + fi +fi + +if test "$MACOS_X" = "yes"; then + AC_MSG_CHECKING([whether we need macOS frameworks]) + if test "$GUITYPE" = "CARBONGUI"; then + AC_MSG_RESULT([yes, we need Carbon]) + LIBS="$LIBS -framework Carbon" + elif test "$MACOS_X_DARWIN" = "yes"; then + if test "$features" = "tiny"; then + dnl Since no FEAT_CLIPBOARD, no longer need for os_macosx.m. + OS_EXTRA_SRC=`echo "$OS_EXTRA_SRC" | sed -e 's+os_macosx.m++'` + OS_EXTRA_OBJ=`echo "$OS_EXTRA_OBJ" | sed -e 's+objects/os_macosx.o++'` + AC_MSG_RESULT([yes, we need CoreServices]) + LIBS="$LIBS -framework CoreServices" + else + AC_MSG_RESULT([yes, we need AppKit]) + LIBS="$LIBS -framework AppKit" + fi + else + AC_MSG_RESULT([no]) + fi +fi +if test "x$MACARCH" = "xboth" && test "x$GUITYPE" = "xCARBONGUI"; then + LDFLAGS="$LDFLAGS -isysroot $DEVELOPER_DIR/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc" +fi + +dnl gcc 3.1 changed the meaning of -MM. The only solution appears to be to +dnl use "-isystem" instead of "-I" for all non-Vim include dirs. +dnl But only when making dependencies, cproto and lint don't take "-isystem". +dnl Mac gcc returns "powerpc-apple-darwin8-gcc-4.0.1 (GCC)...", need to allow +dnl the number before the version number. +DEPEND_CFLAGS_FILTER= +if test "$GCC" = yes; then + AC_MSG_CHECKING(for GCC 3 or later) + gccmajor=`echo "$gccversion" | sed -e 's/^\([[1-9]]\)\..*$/\1/g'` + if test "$gccmajor" -gt "2"; then + DEPEND_CFLAGS_FILTER="| sed 's+-I */+-isystem /+g'" + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi + dnl -D_FORTIFY_SOURCE=2 crashes Vim on strcpy(buf, "000") when buf is + dnl declared as char x[1] but actually longer. Introduced in gcc 4.0. + dnl Also remove duplicate _FORTIFY_SOURCE arguments. + dnl And undefine it first to avoid a warning. + AC_MSG_CHECKING(whether we need -D_FORTIFY_SOURCE=1) + if test "$gccmajor" -gt "3"; then + CFLAGS=`echo "$CFLAGS" | sed -e 's/ *-Wp,-D_FORTIFY_SOURCE=.//g' -e 's/ *-D_FORTIFY_SOURCE=.//g' -e 's/ *-U_FORTIFY_SOURCE//g' -e 's/$/ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1/'` + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi +fi +AC_SUBST(DEPEND_CFLAGS_FILTER) + +dnl On some systems AC_SYS_LARGEFILE determines that -D_FILE_OFFSET_BITS=64 +dnl isn't required, but the CFLAGS for some of the libraries we're using +dnl include the define. Since the define changes the size of some datatypes +dnl (e.g. ino_t and off_t), all of Vim's modules must be compiled with a +dnl consistent value. It's therefore safest to force the use of the define +dnl if it's present in any of the *_CFLAGS variables. +AC_MSG_CHECKING(whether we need to force -D_FILE_OFFSET_BITS=64) +if echo "$CFLAGS $LUA_CFLAGS $MZSCHEME_CFLAGS $PERL_CFLAGS $PYTHON_CFLAGS $PYTHON3_CFLAGS $TCL_CFLAGS $RUBY_CFLAGS $GTK_CFLAGS" | grep -q D_FILE_OFFSET_BITS 2>/dev/null; then + AC_MSG_RESULT(yes) + AC_DEFINE(_FILE_OFFSET_BITS, 64) +else + AC_MSG_RESULT(no) +fi + +dnl link.sh tries to avoid overlinking in a hackish way. +dnl At least GNU ld supports --as-needed which provides the same functionality +dnl at linker level. Let's use it. +AC_MSG_CHECKING(linker --as-needed support) +LINK_AS_NEEDED= +# Check if linker supports --as-needed and --no-as-needed options +if $CC -Wl,--help 2>/dev/null | grep as-needed > /dev/null; then + LDFLAGS=`echo "$LDFLAGS" | sed -e 's/ *-Wl,--as-needed//g' | sed -e 's/$/ -Wl,--as-needed/'` + LINK_AS_NEEDED=yes +fi +if test "$LINK_AS_NEEDED" = yes; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_SUBST(LINK_AS_NEEDED) + +# IBM z/OS reset CFLAGS for config.mk +if test "$zOSUnix" = "yes"; then + CFLAGS="-D_ALL_SOURCE -Wc,float\(ieee\),dll" +fi + +dnl write output files +AC_OUTPUT(auto/config.mk:config.mk.in) + +dnl vim: set sw=2 tw=78 fo+=l: diff --git a/src/create_cmdidxs.vim b/src/create_cmdidxs.vim new file mode 100644 index 0000000..c306ccb --- /dev/null +++ b/src/create_cmdidxs.vim @@ -0,0 +1,81 @@ +" This script generates the tables cmdidxs1[] and cmdidxs2[][] which, +" given a Ex command, determine the first value to probe to find +" a matching command in cmdnames[] based on the first character +" and the first 2 characters of the command. +" This is used to speed up lookup in cmdnames[]. +" +" Script should be run every time new Ex commands are added in Vim, +" from the src/vim directory, since it reads commands from "ex_cmds.h". + +let cmds = [] +let skipped_cmds = 0 + +for line in readfile('ex_cmds.h') + if line =~ '^EX(CMD_' + let m = matchlist(line, '^EX(CMD_\S*,\s*"\([a-z][^"]*\)"') + if len(m) >= 2 + let cmds += [ m[1] ] + else + let skipped_cmds += 1 + endif + endif +endfor + +let cmdidxs1 = {} +let cmdidxs2 = {} + +for i in range(len(cmds) - 1, 0, -1) + let cmd = cmds[i] + let c1 = cmd[0] " First character of command + let c2 = cmd[1] " Second character of command (if any) + + let cmdidxs1{c1} = i + if c2 >= 'a' && c2 <= 'z' + let cmdidxs2{c1}{c2} = i + endif +endfor + +let output = [ '/* Automatically generated code by create_cmdidxs.vim' ] +let output += [ ' *' ] +let output += [ ' * Table giving the index of the first command in cmdnames[] to lookup' ] +let output += [ ' * based on the first letter of a command.' ] +let output += [ ' */' ] +let output += [ 'static const unsigned short cmdidxs1[26] =' ] +let output += [ '{' ] + +let a_to_z = map(range(char2nr('a'), char2nr('z')), 'nr2char(v:val)') +for c1 in a_to_z + let line = ' /* ' . c1 . ' */ ' . cmdidxs1{c1} . ((c1 == 'z') ? '' : ',') + let output += [ line ] +endfor +let output += [ '};' ] +let output += [ '' ] +let output += [ '/*' ] +let output += [ ' * Table giving the index of the first command in cmdnames[] to lookup' ] +let output += [ ' * based on the first 2 letters of a command.' ] +let output += [ ' * Values in cmdidxs2[c1][c2] are relative to cmdidxs1[c1] so that they' ] +let output += [ ' * fit in a byte.' ] +let output += [ ' */' ] +let output += [ 'static const unsigned char cmdidxs2[26][26] =' ] +let output += [ '{ /* a b c d e f g h i j k l m n o p q r s t u v w x y z */' ] + +for c1 in a_to_z + let line = ' /* ' . c1 . ' */ {' + for c2 in a_to_z + if exists('cmdidxs2{c1}{c2}') + let line .= printf('%3d', cmdidxs2{c1}{c2} - cmdidxs1{c1}) + else + let line .= ' 0' + endif + let line .= (c2 == 'z') ? '' : ',' + endfor + let line .= ' }' . ((c1 == 'z') ? '' : ',') + let output += [ line ] +endfor + +let output += [ '};' ] +let output += [ '' ] +let output += [ 'static const int command_count = ' . (len(cmds) + skipped_cmds) . ';' ] + +call writefile(output, "ex_cmdidxs.h") +quit diff --git a/src/crypt.c b/src/crypt.c new file mode 100644 index 0000000..47617e9 --- /dev/null +++ b/src/crypt.c @@ -0,0 +1,605 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * crypt.c: Generic encryption support. + */ +#include "vim.h" + +#if defined(FEAT_CRYPT) || defined(PROTO) +/* + * Optional encryption support. + * Mohsin Ahmed, mosh@sasi.com, 1998-09-24 + * Based on zip/crypt sources. + * Refactored by David Leadbeater, 2014. + * + * 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. + */ + +typedef struct { + char *name; /* encryption name as used in 'cryptmethod' */ + char *magic; /* magic bytes stored in file header */ + int salt_len; /* length of salt, or 0 when not using salt */ + int seed_len; /* length of seed, or 0 when not using salt */ +#ifdef CRYPT_NOT_INPLACE + int works_inplace; /* encryption/decryption can be done in-place */ +#endif + int whole_undofile; /* whole undo file is encrypted */ + + /* Optional function pointer for a self-test. */ + int (* self_test_fn)(); + + /* Function pointer for initializing encryption/decription. */ + void (* init_fn)(cryptstate_T *state, char_u *key, + char_u *salt, int salt_len, char_u *seed, int seed_len); + + /* Function pointers for encoding/decoding from one buffer into another. + * Optional, however, these or the _buffer ones should be configured. */ + void (*encode_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u *to); + void (*decode_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u *to); + + /* Function pointers for encoding and decoding, can buffer data if needed. + * Optional (however, these or the above should be configured). */ + long (*encode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u **newptr); + long (*decode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u **newptr); + + /* Function pointers for in-place encoding and decoding, used for + * crypt_*_inplace(). "from" and "to" arguments will be equal. + * These may be the same as decode_fn and encode_fn above, however an + * algorithm may implement them in a way that is not interchangeable with + * the crypt_(en|de)code() interface (for example because it wishes to add + * padding to files). + * This method is used for swap and undo files which have a rigid format. + */ + void (*encode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, + char_u *p2); + void (*decode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, + char_u *p2); +} cryptmethod_T; + +/* index is method_nr of cryptstate_T, CRYPT_M_* */ +static cryptmethod_T cryptmethods[CRYPT_M_COUNT] = { + /* PK_Zip; very weak */ + { + "zip", + "VimCrypt~01!", + 0, + 0, +#ifdef CRYPT_NOT_INPLACE + TRUE, +#endif + FALSE, + NULL, + crypt_zip_init, + crypt_zip_encode, crypt_zip_decode, + NULL, NULL, + crypt_zip_encode, crypt_zip_decode, + }, + + /* Blowfish/CFB + SHA-256 custom key derivation; implementation issues. */ + { + "blowfish", + "VimCrypt~02!", + 8, + 8, +#ifdef CRYPT_NOT_INPLACE + TRUE, +#endif + FALSE, + blowfish_self_test, + crypt_blowfish_init, + crypt_blowfish_encode, crypt_blowfish_decode, + NULL, NULL, + crypt_blowfish_encode, crypt_blowfish_decode, + }, + + /* Blowfish/CFB + SHA-256 custom key derivation; fixed. */ + { + "blowfish2", + "VimCrypt~03!", + 8, + 8, +#ifdef CRYPT_NOT_INPLACE + TRUE, +#endif + TRUE, + blowfish_self_test, + crypt_blowfish_init, + crypt_blowfish_encode, crypt_blowfish_decode, + NULL, NULL, + crypt_blowfish_encode, crypt_blowfish_decode, + }, + + /* NOTE: when adding a new method, use some random bytes for the magic key, + * to avoid that a text file is recognized as encrypted. */ +}; + +#define CRYPT_MAGIC_LEN 12 /* cannot change */ +static char crypt_magic_head[] = "VimCrypt~"; + +/* + * Return int value for crypt method name. + * 0 for "zip", the old method. Also for any non-valid value. + * 1 for "blowfish". + * 2 for "blowfish2". + */ + int +crypt_method_nr_from_name(char_u *name) +{ + int i; + + for (i = 0; i < CRYPT_M_COUNT; ++i) + if (STRCMP(name, cryptmethods[i].name) == 0) + return i; + return 0; +} + +/* + * 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. + */ + int +crypt_method_nr_from_magic(char *ptr, int len) +{ + int i; + + if (len < CRYPT_MAGIC_LEN) + return -1; + + for (i = 0; i < CRYPT_M_COUNT; i++) + if (memcmp(ptr, cryptmethods[i].magic, 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; +} + +#ifdef CRYPT_NOT_INPLACE +/* + * Return TRUE if the crypt method for "method_nr" can be done in-place. + */ + int +crypt_works_inplace(cryptstate_T *state) +{ + return cryptmethods[state->method_nr].works_inplace; +} +#endif + +/* + * Get the crypt method for buffer "buf" as a number. + */ + int +crypt_get_method_nr(buf_T *buf) +{ + return crypt_method_nr_from_name(*buf->b_p_cm == NUL ? p_cm : buf->b_p_cm); +} + +/* + * Return TRUE when the buffer uses an encryption method that encrypts the + * whole undo file, not only the text. + */ + int +crypt_whole_undofile(int method_nr) +{ + return cryptmethods[method_nr].whole_undofile; +} + +/* + * Get crypt method specifc length of the file header in bytes. + */ + int +crypt_get_header_len(int method_nr) +{ + return CRYPT_MAGIC_LEN + + cryptmethods[method_nr].salt_len + + cryptmethods[method_nr].seed_len; +} + +/* + * Set the crypt method for buffer "buf" to "method_nr" using the int value as + * returned by crypt_method_nr_from_name(). + */ + void +crypt_set_cm_option(buf_T *buf, int method_nr) +{ + free_string_option(buf->b_p_cm); + buf->b_p_cm = vim_strsave((char_u *)cryptmethods[method_nr].name); +} + +/* + * If the crypt method for the current buffer has a self-test, run it and + * return OK/FAIL. + */ + int +crypt_self_test(void) +{ + int method_nr = crypt_get_method_nr(curbuf); + + if (cryptmethods[method_nr].self_test_fn == NULL) + return OK; + return cryptmethods[method_nr].self_test_fn(); +} + +/* + * Allocate a crypt state and initialize it. + */ + cryptstate_T * +crypt_create( + int method_nr, + char_u *key, + char_u *salt, + int salt_len, + char_u *seed, + int seed_len) +{ + cryptstate_T *state = (cryptstate_T *)alloc((int)sizeof(cryptstate_T)); + + state->method_nr = method_nr; + cryptmethods[method_nr].init_fn(state, key, salt, salt_len, seed, seed_len); + return state; +} + +/* + * Allocate a crypt state from a file header and initialize it. + * Assumes that header contains at least the number of bytes that + * crypt_get_header_len() returns for "method_nr". + */ + cryptstate_T * +crypt_create_from_header( + int method_nr, + char_u *key, + char_u *header) +{ + char_u *salt = NULL; + char_u *seed = NULL; + int salt_len = cryptmethods[method_nr].salt_len; + int seed_len = cryptmethods[method_nr].seed_len; + + if (salt_len > 0) + salt = header + CRYPT_MAGIC_LEN; + if (seed_len > 0) + seed = header + CRYPT_MAGIC_LEN + salt_len; + + return crypt_create(method_nr, key, salt, salt_len, seed, seed_len); +} + +/* + * Read the crypt method specific header data from "fp". + * Return an allocated cryptstate_T or NULL on error. + */ + cryptstate_T * +crypt_create_from_file(FILE *fp, char_u *key) +{ + int method_nr; + int header_len; + char magic_buffer[CRYPT_MAGIC_LEN]; + char_u *buffer; + cryptstate_T *state; + + if (fread(magic_buffer, CRYPT_MAGIC_LEN, 1, fp) != 1) + return NULL; + method_nr = crypt_method_nr_from_magic(magic_buffer, CRYPT_MAGIC_LEN); + if (method_nr < 0) + return NULL; + + header_len = crypt_get_header_len(method_nr); + if ((buffer = alloc(header_len)) == NULL) + return NULL; + mch_memmove(buffer, magic_buffer, CRYPT_MAGIC_LEN); + if (header_len > CRYPT_MAGIC_LEN + && fread(buffer + CRYPT_MAGIC_LEN, + header_len - CRYPT_MAGIC_LEN, 1, fp) != 1) + { + vim_free(buffer); + return NULL; + } + + state = crypt_create_from_header(method_nr, key, buffer); + vim_free(buffer); + return state; +} + +/* + * Allocate a cryptstate_T for writing and initialize it with "key". + * Allocates and fills in the header and stores it in "header", setting + * "header_len". The header may include salt and seed, depending on + * cryptmethod. Caller must free header. + * Returns the state or NULL on failure. + */ + cryptstate_T * +crypt_create_for_writing( + int method_nr, + char_u *key, + char_u **header, + int *header_len) +{ + int len = crypt_get_header_len(method_nr); + char_u *salt = NULL; + char_u *seed = NULL; + int salt_len = cryptmethods[method_nr].salt_len; + int seed_len = cryptmethods[method_nr].seed_len; + cryptstate_T *state; + + *header_len = len; + *header = alloc(len); + if (*header == NULL) + return NULL; + + mch_memmove(*header, cryptmethods[method_nr].magic, CRYPT_MAGIC_LEN); + if (salt_len > 0 || seed_len > 0) + { + if (salt_len > 0) + salt = *header + CRYPT_MAGIC_LEN; + if (seed_len > 0) + seed = *header + CRYPT_MAGIC_LEN + salt_len; + + /* TODO: Should this be crypt method specific? (Probably not worth + * it). sha2_seed is pretty bad for large amounts of entropy, so make + * that into something which is suitable for anything. */ + sha2_seed(salt, salt_len, seed, seed_len); + } + + state = crypt_create(method_nr, key, salt, salt_len, seed, seed_len); + if (state == NULL) + VIM_CLEAR(*header); + return state; +} + +/* + * Free the crypt state. + */ + void +crypt_free_state(cryptstate_T *state) +{ + vim_free(state->method_state); + vim_free(state); +} + +#ifdef CRYPT_NOT_INPLACE +/* + * Encode "from[len]" and store the result in a newly allocated buffer, which + * is stored in "newptr". + * Return number of bytes in "newptr", 0 for need more or -1 on error. + */ + long +crypt_encode_alloc( + cryptstate_T *state, + char_u *from, + size_t len, + char_u **newptr) +{ + cryptmethod_T *method = &cryptmethods[state->method_nr]; + + if (method->encode_buffer_fn != NULL) + /* Has buffer function, pass through. */ + return method->encode_buffer_fn(state, from, len, newptr); + if (len == 0) + /* Not buffering, just return EOF. */ + return (long)len; + + *newptr = alloc((long)len); + if (*newptr == NULL) + return -1; + method->encode_fn(state, from, len, *newptr); + return (long)len; +} + +/* + * Decrypt "ptr[len]" and store the result in a newly allocated buffer, which + * is stored in "newptr". + * Return number of bytes in "newptr", 0 for need more or -1 on error. + */ + long +crypt_decode_alloc( + cryptstate_T *state, + char_u *ptr, + long len, + char_u **newptr) +{ + cryptmethod_T *method = &cryptmethods[state->method_nr]; + + if (method->decode_buffer_fn != NULL) + /* Has buffer function, pass through. */ + return method->decode_buffer_fn(state, ptr, len, newptr); + + if (len == 0) + /* Not buffering, just return EOF. */ + return len; + + *newptr = alloc(len); + if (*newptr == NULL) + return -1; + method->decode_fn(state, ptr, len, *newptr); + return len; +} +#endif + +/* + * Encrypting "from[len]" into "to[len]". + */ + void +crypt_encode( + cryptstate_T *state, + char_u *from, + size_t len, + char_u *to) +{ + cryptmethods[state->method_nr].encode_fn(state, from, len, to); +} + +#if 0 // unused +/* + * decrypting "from[len]" into "to[len]". + */ + void +crypt_decode( + cryptstate_T *state, + char_u *from, + size_t len, + char_u *to) +{ + cryptmethods[state->method_nr].decode_fn(state, from, len, to); +} +#endif + +/* + * Simple inplace encryption, modifies "buf[len]" in place. + */ + void +crypt_encode_inplace( + cryptstate_T *state, + char_u *buf, + size_t len) +{ + cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len, buf); +} + +/* + * Simple inplace decryption, modifies "buf[len]" in place. + */ + void +crypt_decode_inplace( + cryptstate_T *state, + char_u *buf, + size_t len) +{ + cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len, buf); +} + +/* + * Free an allocated crypt key. Clear the text to make sure it doesn't stay + * in memory anywhere. + */ + void +crypt_free_key(char_u *key) +{ + char_u *p; + + if (key != NULL) + { + for (p = key; *p != NUL; ++p) + *p = 0; + vim_free(key); + } +} + +/* + * Check the crypt method and give a warning if it's outdated. + */ + void +crypt_check_method(int method) +{ + if (method < CRYPT_M_BF2) + { + msg_scroll = TRUE; + msg(_("Warning: Using a weak encryption method; see :help 'cm'")); + } +} + + void +crypt_check_current_method(void) +{ + crypt_check_method(crypt_get_method_nr(curbuf)); +} + +/* + * Ask the user for a crypt key. + * When "store" is TRUE, the new key is stored in the 'key' option, and the + * 'key' option value is returned: Don't free it. + * When "store" is FALSE, the typed key is returned in allocated memory. + * Returns NULL on failure. + */ + char_u * +crypt_get_key( + int store, + int twice) /* Ask for the key twice. */ +{ + char_u *p1, *p2 = NULL; + int round; + + for (round = 0; ; ++round) + { + cmdline_star = TRUE; + cmdline_row = msg_row; + p1 = getcmdline_prompt(NUL, round == 0 + ? (char_u *)_("Enter encryption key: ") + : (char_u *)_("Enter same key again: "), 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!")); + crypt_free_key(p1); + crypt_free_key(p2); + p2 = NULL; + round = -1; /* do it again */ + continue; + } + + if (store) + { + set_option_value((char_u *)"key", 0L, p1, OPT_LOCAL); + crypt_free_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; + + crypt_free_key(p2); + return p1; +} + + +/* + * Append a message to IObuff for the encryption/decryption method being used. + */ + void +crypt_append_msg( + buf_T *buf) +{ + if (crypt_get_method_nr(buf) == 0) + STRCAT(IObuff, _("[crypted]")); + else + { + STRCAT(IObuff, "["); + STRCAT(IObuff, *buf->b_p_cm == NUL ? p_cm : buf->b_p_cm); + STRCAT(IObuff, "]"); + } +} + +#endif /* FEAT_CRYPT */ diff --git a/src/crypt_zip.c b/src/crypt_zip.c new file mode 100644 index 0000000..ed17d95 --- /dev/null +++ b/src/crypt_zip.c @@ -0,0 +1,152 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * crypt_zip.c: Zip encryption support. + */ +#include "vim.h" + +#if defined(FEAT_CRYPT) || defined(PROTO) +/* + * 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. + */ + +/* Need a type that should be 32 bits. 64 also works but wastes space. */ +typedef unsigned int u32_T; /* int is at least 32 bits */ + +/* The state of encryption, referenced by cryptstate_T. */ +typedef struct { + u32_T keys[3]; +} zip_state_T; + + +static u32_T crc_32_table[256]; + +/* + * Fill the CRC table, if not done already. + */ + static void +make_crc_tab(void) +{ + u32_T s, t, v; + static int done = FALSE; + + if (done) + return; + for (t = 0; t < 256; t++) + { + v = t; + for (s = 0; s < 8; s++) + v = (v >> 1) ^ ((v & 1) * (u32_T)0xedb88320L); + crc_32_table[t] = v; + } + done = TRUE; +} + +#define CRC32(c, b) (crc_32_table[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) + +/* + * Return the next byte in the pseudo-random sequence. + */ +#define DECRYPT_BYTE_ZIP(keys, t) { \ + short_u temp = (short_u)keys[2] | 2; \ + t = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff); \ +} + +/* + * Update the encryption keys with the next byte of plain text. + */ +#define UPDATE_KEYS_ZIP(keys, 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)); \ +} + +/* + * Initialize for encryption/decryption. + */ + void +crypt_zip_init( + cryptstate_T *state, + char_u *key, + char_u *salt UNUSED, + int salt_len UNUSED, + char_u *seed UNUSED, + int seed_len UNUSED) +{ + char_u *p; + zip_state_T *zs; + + zs = (zip_state_T *)alloc(sizeof(zip_state_T)); + state->method_state = zs; + + make_crc_tab(); + zs->keys[0] = 305419896L; + zs->keys[1] = 591751049L; + zs->keys[2] = 878082192L; + for (p = key; *p != NUL; ++p) + { + UPDATE_KEYS_ZIP(zs->keys, (int)*p); + } +} + +/* + * Encrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + void +crypt_zip_encode( + cryptstate_T *state, + char_u *from, + size_t len, + char_u *to) +{ + zip_state_T *zs = state->method_state; + size_t i; + int ztemp, t; + + for (i = 0; i < len; ++i) + { + ztemp = from[i]; + DECRYPT_BYTE_ZIP(zs->keys, t); + UPDATE_KEYS_ZIP(zs->keys, ztemp); + to[i] = t ^ ztemp; + } +} + +/* + * Decrypt "from[len]" into "to[len]". + */ + void +crypt_zip_decode( + cryptstate_T *state, + char_u *from, + size_t len, + char_u *to) +{ + zip_state_T *zs = state->method_state; + size_t i; + short_u temp; + + for (i = 0; i < len; ++i) + { + temp = (short_u)zs->keys[2] | 2; + temp = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff); + UPDATE_KEYS_ZIP(zs->keys, to[i] = from[i] ^ temp); + } +} + +#endif /* FEAT_CRYPT */ diff --git a/src/dehqx.py b/src/dehqx.py new file mode 100644 index 0000000..00e8f9f --- /dev/null +++ b/src/dehqx.py @@ -0,0 +1,45 @@ +# Python script to get both the data and resource fork from a BinHex encoded +# file. +# Author: MURAOKA Taro +# Last Change: 2018 Mar 27 +# +# Copyright (C) 2003,12 MURAOKA Taro +# THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. + +import sys +import binhex + +input = sys.argv[1] +conv = binhex.HexBin(input) +info = conv.FInfo +out = conv.FName +out_data = out +out_rsrc = out + '.rsrcfork' + +# This uses the print statement on Python 2, print function on Python 3. +#print('out_rsrc=' + out_rsrc) +print('In file: ' + input) + +outfile = open(out_data, 'wb') +print(' Out data fork: ' + out_data) +while 1: + d = conv.read(128000) + if not d: break + outfile.write(d) +outfile.close() +conv.close_data() + +d = conv.read_rsrc(128000) +if d: + print(' Out rsrc fork: ' + out_rsrc) + outfile = open(out_rsrc, 'wb') + outfile.write(d) + while 1: + d = conv.read_rsrc(128000) + if not d: break + outfile.write(d) + outfile.close() + +conv.close() + +# vim:set ts=8 sts=4 sw=4 et: diff --git a/src/dict.c b/src/dict.c new file mode 100644 index 0000000..91c6e55 --- /dev/null +++ b/src/dict.c @@ -0,0 +1,920 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * dict.c: Dictionary support + */ + +#include "vim.h" + +#if defined(FEAT_EVAL) || defined(PROTO) + +/* List head for garbage collection. Although there can be a reference loop + * from partial to dict to partial, we don't need to keep track of the partial, + * since it will get freed when the dict is unused and gets freed. */ +static dict_T *first_dict = NULL; /* list of all dicts */ + +/* + * Allocate an empty header for a dictionary. + */ + dict_T * +dict_alloc(void) +{ + dict_T *d; + + d = (dict_T *)alloc(sizeof(dict_T)); + if (d != NULL) + { + /* 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; +} + +/* + * dict_alloc() with an ID for alloc_fail(). + */ + dict_T * +dict_alloc_id(alloc_id_T id UNUSED) +{ +#ifdef FEAT_EVAL + if (alloc_fail_id == id && alloc_does_fail((long_u)sizeof(list_T))) + return NULL; +#endif + return (dict_alloc()); +} + + dict_T * +dict_alloc_lock(int lock) +{ + dict_T *d = dict_alloc(); + + if (d != NULL) + d->dv_lock = lock; + return d; +} + +/* + * Allocate an empty dict for a return value. + * Returns OK or FAIL. + */ + int +rettv_dict_alloc(typval_T *rettv) +{ + dict_T *d = dict_alloc_lock(0); + + if (d == NULL) + return FAIL; + + rettv_dict_set(rettv, d); + return OK; +} + +/* + * Set a dictionary as the return value + */ + void +rettv_dict_set(typval_T *rettv, dict_T *d) +{ + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = d; + if (d != NULL) + ++d->dv_refcount; +} + +/* + * Free a Dictionary, including all non-container items it contains. + * Ignores the reference count. + */ + void +dict_free_contents(dict_T *d) +{ + int todo; + hashitem_T *hi; + dictitem_T *di; + + /* 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); + dictitem_free(di); + --todo; + } + } + + /* The hashtab is still locked, it has to be re-initialized anyway */ + hash_clear(&d->dv_hashtab); +} + + static void +dict_free_dict(dict_T *d) +{ + /* 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; + vim_free(d); +} + + static void +dict_free(dict_T *d) +{ + if (!in_free_unref_items) + { + dict_free_contents(d); + dict_free_dict(d); + } +} + +/* + * 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); +} + +/* + * Go through the list of dicts and free items without the copyID. + * Returns TRUE if something was freed. + */ + int +dict_free_nonref(int copyID) +{ + dict_T *dd; + int did_free = FALSE; + + for (dd = first_dict; dd != NULL; dd = dd->dv_used_next) + 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_contents(dd); + did_free = TRUE; + } + return did_free; +} + + void +dict_free_items(int copyID) +{ + dict_T *dd, *dd_next; + + for (dd = first_dict; dd != NULL; dd = dd_next) + { + dd_next = dd->dv_used_next; + if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) + dict_free_dict(dd); + } +} + +/* + * Allocate a Dictionary item. + * The "key" is copied to the new item. + * Note that the type and 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 = DI_FLAGS_ALLOC; + di->di_tv.v_lock = 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 = DI_FLAGS_ALLOC; + copy_tv(&org->di_tv, &di->di_tv); + } + return di; +} + +/* + * Remove item "item" from Dictionary "dict" and free it. + */ + 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)) + internal_error("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); + if (item->di_flags & DI_FLAGS_ALLOC) + vim_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. + */ + 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) + { + vim_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 entry to dictionary "d". + * Returns FAIL when out of memory and when key already exists. + */ + int +dict_add_number(dict_T *d, char *key, varnumber_T nr) +{ + dictitem_T *item; + + item = dictitem_alloc((char_u *)key); + if (item == NULL) + return FAIL; + item->di_tv.v_type = VAR_NUMBER; + item->di_tv.vval.v_number = nr; + if (dict_add(d, item) == FAIL) + { + dictitem_free(item); + return FAIL; + } + return OK; +} + +/* + * Add a string entry to dictionary "d". + * Returns FAIL when out of memory and when key already exists. + */ + int +dict_add_string(dict_T *d, char *key, char_u *str) +{ + return dict_add_string_len(d, key, str, -1); +} + +/* + * Add a string entry to dictionary "d". + * "str" will be copied to allocated memory. + * When "len" is -1 use the whole string, otherwise only this many bytes. + * Returns FAIL when out of memory and when key already exists. + */ + int +dict_add_string_len(dict_T *d, char *key, char_u *str, int len) +{ + dictitem_T *item; + char_u *val = NULL; + + item = dictitem_alloc((char_u *)key); + if (item == NULL) + return FAIL; + item->di_tv.v_type = VAR_STRING; + if (str != NULL) + { + if (len == -1) + val = vim_strsave(str); + else + val = vim_strnsave(str, len); + } + item->di_tv.vval.v_string = val; + 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_type = VAR_LIST; + item->di_tv.vval.v_list = list; + ++list->lv_refcount; + if (dict_add(d, item) == FAIL) + { + dictitem_free(item); + return FAIL; + } + return OK; +} + +/* + * Add a dict entry to dictionary "d". + * Returns FAIL when out of memory and when key already exists. + */ + int +dict_add_dict(dict_T *d, char *key, dict_T *dict) +{ + dictitem_T *item; + + item = dictitem_alloc((char_u *)key); + if (item == NULL) + return FAIL; + item->di_tv.v_type = VAR_DICT; + item->di_tv.vval.v_dict = dict; + ++dict->dv_refcount; + if (dict_add(d, item) == FAIL) + { + dictitem_free(item); + return FAIL; + } + return OK; +} + +/* + * Get the number of items in a Dictionary. + */ + 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 (d == NULL) + return NULL; + 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); + vim_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. + * When FALSE a shared buffer is used, can only be used once! + * Returns NULL if the entry doesn't exist or out of memory. + */ + char_u * +dict_get_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 = tv_get_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. + */ + varnumber_T +dict_get_number(dict_T *d, char_u *key) +{ + dictitem_T *di; + + di = dict_find(d, key, -1); + if (di == NULL) + return 0; + return tv_get_number(&di->di_tv); +} + +/* + * Return an allocated string with the string representation of a Dictionary. + * May return NULL. + */ + char_u * +dict2string(typval_T *tv, int copyID, int restore_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_init2(&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); + vim_free(tofree); + } + ga_concat(&ga, (char_u *)": "); + s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID, + FALSE, restore_copyID, TRUE); + if (s != NULL) + ga_concat(&ga, s); + vim_free(tofree); + if (s == NULL || did_echo_string_emsg) + break; + line_breakcheck(); + + } + } + if (todo > 0) + { + vim_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}. + */ + int +dict_get_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 != ':') + { + semsg(_("E720: Missing colon in Dictionary: %s"), *arg); + clear_tv(&tvkey); + goto failret; + } + if (evaluate) + { + key = tv_get_string_buf_chk(&tvkey, buf); + if (key == NULL) + { + /* "key" is NULL when tv_get_string_buf_chk() gave an errmsg */ + 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) + { + semsg(_("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 != ',') + { + semsg(_("E722: Missing comma in Dictionary: %s"), *arg); + goto failret; + } + *arg = skipwhite(*arg + 1); + } + + if (**arg != '}') + { + semsg(_("E723: Missing end of Dictionary '}': %s"), *arg); +failret: + if (evaluate) + dict_free(d); + return FAIL; + } + + *arg = skipwhite(*arg + 1); + if (evaluate) + rettv_dict_set(rettv, d); + + return OK; +} + +/* + * 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; + char_u *arg_errmsg = (char_u *)N_("extend() argument"); + + 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') + { + semsg(_("E737: Key already exists: %s"), hi2->hi_key); + break; + } + else if (*action == 'f' && HI2DI(hi2) != di1) + { + if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) + || var_check_ro(di1->di_flags, arg_errmsg, TRUE)) + break; + clear_tv(&di1->di_tv); + copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); + } + } + } +} + +/* + * Return the dictitem that an entry in a hashtable points to. + */ + dictitem_T * +dict_lookup(hashitem_T *hi) +{ + return HI2DI(hi); +} + +/* + * Return TRUE when two dictionaries have exactly the same key/values. + */ + 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 TRUE; + 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; +} + +/* + * Turn a dict into a list: + * "what" == 0: list of keys + * "what" == 1: list of values + * "what" == 2: list of items + */ + 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; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + 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); + } + } + } +} + +/* + * Make each item in the dict readonly (not the value of the item). + */ + void +dict_set_items_ro(dict_T *di) +{ + int todo = (int)di->dv_hashtab.ht_used; + hashitem_T *hi; + + /* Set readonly */ + for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi) + { + if (HASHITEM_EMPTY(hi)) + continue; + --todo; + HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + } +} + +#endif /* defined(FEAT_EVAL) */ diff --git a/src/diff.c b/src/diff.c new file mode 100644 index 0000000..d368f96 --- /dev/null +++ b/src/diff.c @@ -0,0 +1,3221 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * diff.c: code for diff'ing two, three or four buffers. + * + * There are three ways to diff: + * - Shell out to an external diff program, using files. + * - Use the compiled-in xdiff library. + * - Let 'diffexpr' do the work, using files. + */ + +#include "vim.h" +#include "xdiff/xdiff.h" + +#if defined(FEAT_DIFF) || defined(PROTO) + +static int diff_busy = FALSE; // using diff structs, don't change them +static int diff_need_update = FALSE; // ex_diffupdate needs to be called + +/* flags obtained from the 'diffopt' option */ +#define DIFF_FILLER 0x001 // display filler lines +#define DIFF_IBLANK 0x002 // ignore empty lines +#define DIFF_ICASE 0x004 // ignore case +#define DIFF_IWHITE 0x008 // ignore change in white space +#define DIFF_IWHITEALL 0x010 // ignore all white space changes +#define DIFF_IWHITEEOL 0x020 // ignore change in white space at EOL +#define DIFF_HORIZONTAL 0x040 // horizontal splits +#define DIFF_VERTICAL 0x080 // vertical splits +#define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden +#define DIFF_INTERNAL 0x200 // use internal xdiff algorithm +#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL) +static int diff_flags = DIFF_INTERNAL | DIFF_FILLER; + +static long diff_algorithm = 0; + +#define LBUFLEN 50 /* length of line in diff file */ + +static int diff_a_works = MAYBE; /* TRUE when "diff -a" works, FALSE when it + doesn't work, MAYBE when not checked yet */ +#if defined(MSWIN) +static int diff_bin_works = MAYBE; /* TRUE when "diff --binary" works, FALSE + when it doesn't work, MAYBE when not + checked yet */ +#endif + +// used for diff input +typedef struct { + char_u *din_fname; // used for external diff + mmfile_t din_mmfile; // used for internal diff +} diffin_T; + +// used for diff result +typedef struct { + char_u *dout_fname; // used for external diff + garray_T dout_ga; // used for internal diff +} diffout_T; + +// two diff inputs and one result +typedef struct { + diffin_T dio_orig; // original file input + diffin_T dio_new; // new file input + diffout_T dio_diff; // diff result + int dio_internal; // using internal diff +} diffio_T; + +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 check_external_diff(diffio_T *diffio); +static int diff_file(diffio_T *diffio); +static int diff_equal_entry(diff_T *dp, int idx1, int idx2); +static int diff_cmp(char_u *s1, char_u *s2); +#ifdef FEAT_FOLDING +static void diff_fold_update(diff_T *dp, int skip_idx); +#endif +static void diff_read(int idx_orig, int idx_new, diffout_T *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); +static int parse_diff_ed(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new); +static int parse_diff_unified(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new); +static int xdiff_out(void *priv, mmbuffer_t *mb, int nbuf); + +#ifndef USE_CR +# define tag_fgets vim_fgets +#endif + +/* + * Called when deleting or unloading a buffer: No longer make a diff with it. + */ + void +diff_buf_delete(buf_T *buf) +{ + int i; + tabpage_T *tp; + + FOR_ALL_TABPAGES(tp) + { + 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. + */ + void +diff_buf_adjust(win_T *win) +{ + win_T *wp; + int i; + + if (!win->w_p_diff) + { + /* When there is no window showing a diff for this buffer, remove + * it from the diffs. */ + FOR_ALL_WINDOWS(wp) + if (wp->w_buffer == win->w_buffer && wp->w_p_diff) + break; + if (wp == NULL) + { + 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. + */ + void +diff_buf_add(buf_T *buf) +{ + int i; + + if (diff_buf_idx(buf) != DB_COUNT) + return; /* It's already there. */ + + 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; + } + + semsg(_("E96: Cannot diff more than %d buffers"), DB_COUNT); +} + +/* + * Remove all buffers to make diffs for. + */ + static void +diff_buf_clear(void) +{ + int i; + + for (i = 0; i < DB_COUNT; ++i) + if (curtab->tp_diffbuf[i] != NULL) + { + curtab->tp_diffbuf[i] = NULL; + curtab->tp_diff_invalid = TRUE; + diff_redraw(TRUE); + } +} + +/* + * Find buffer "buf" in the list of diff buffers for the current tab page. + * 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". + * 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. + */ + void +diff_invalidate(buf_T *buf) +{ + tabpage_T *tp; + int i; + + FOR_ALL_TABPAGES(tp) + { + 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". + */ + void +diff_mark_adjust( + linenr_T line1, + linenr_T line2, + long amount, + long amount_after) +{ + int idx; + tabpage_T *tp; + + /* Handle all tab pages that use the current buffer in a diff. */ + FOR_ALL_TABPAGES(tp) + { + 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. + */ + static void +diff_mark_adjust_tp( + tabpage_T *tp, + int idx, + linenr_T line1, + linenr_T line2, + long amount, + long amount_after) +{ + diff_T *dp; + diff_T *dprev; + diff_T *dnext; + int i; + int inserted, deleted; + int n, off; + linenr_T last; + linenr_T lnum_deleted = line1; /* lnum of remaining deletion */ + int check_unchanged; + + if (diff_internal()) + { + // Will update diffs before redrawing. Set _invalid to update the + // diffs themselves, set _update to also update folds properly just + // before redrawing. + // Do update marks here, it is needed for :%diffput. + tp->tp_diff_invalid = TRUE; + tp->tp_diff_update = TRUE; + } + + 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; + } + + dprev = NULL; + dp = tp->tp_first_diff; + 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) + { + dnext = diff_alloc_new(tp, dprev, dp); + if (dnext == NULL) + return; + + dnext->df_lnum[idx] = line1; + dnext->df_count[idx] = inserted; + 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) + break; /* nothing left to change */ + dp->df_lnum[idx] += amount_after; + } + else + { + 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; + } + } + + 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]) + { + 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; + vim_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. */ + for (i = 0; i < DB_COUNT; ++i) + if (tp->tp_diffbuf[i] != NULL && dp->df_count[i] != 0) + break; + if (i == DB_COUNT) + { + dnext = dp->df_next; + vim_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". + */ + static diff_T * +diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp) +{ + diff_T *dnew; + + dnew = (diff_T *)alloc((unsigned)sizeof(diff_T)); + if (dnew != NULL) + { + 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. + */ + static void +diff_check_unchanged(tabpage_T *tp, diff_T *dp) +{ + int i_org; + int i_new; + int off_org, off_new; + char_u *line_org; + int dir = FORWARD; + + /* Find the first buffers, use it as the original, compare the other + * buffer lines against this one. */ + for (i_org = 0; i_org < DB_COUNT; ++i_org) + if (tp->tp_diffbuf[i_org] != NULL) + break; + if (i_org == DB_COUNT) /* safety check */ + return; + + if (diff_check_sanity(tp, dp) == FAIL) + return; + + /* First check lines at the top, then at the bottom. */ + off_org = 0; + off_new = 0; + 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; + 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; + 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; + } + vim_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. + */ + 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. + */ + static void +diff_redraw( + int dofold) // also recompute the folds +{ + win_T *wp; + int n; + + FOR_ALL_WINDOWS(wp) + if (wp->w_p_diff) + { + redraw_win_later(wp, SOME_VALID); +#ifdef FEAT_FOLDING + if (dofold && foldmethodIsDiff(wp)) + foldUpdateAll(wp); +#endif + /* A change may have made filler lines invalid, need to take care + * of that for other windows. */ + 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; + check_topfill(wp, FALSE); + } + } +} + + static void +clear_diffin(diffin_T *din) +{ + if (din->din_fname == NULL) + { + vim_free(din->din_mmfile.ptr); + din->din_mmfile.ptr = NULL; + } + else + mch_remove(din->din_fname); +} + + static void +clear_diffout(diffout_T *dout) +{ + if (dout->dout_fname == NULL) + ga_clear_strings(&dout->dout_ga); + else + mch_remove(dout->dout_fname); +} + +/* + * Write buffer "buf" to a memory buffer. + * Return FAIL for failure. + */ + static int +diff_write_buffer(buf_T *buf, diffin_T *din) +{ + linenr_T lnum; + char_u *s; + long len = 0; + char_u *ptr; + + // xdiff requires one big block of memory with all the text. + for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) + len += (long)STRLEN(ml_get_buf(buf, lnum, FALSE)) + 1; + ptr = lalloc(len, TRUE); + if (ptr == NULL) + { + // Allocating memory failed. This can happen, because we try to read + // the whole buffer text into memory. Set the failed flag, the diff + // will be retried with external diff. The flag is never reset. + buf->b_diff_failed = TRUE; + if (p_verbose > 0) + { + verbose_enter(); + smsg(_("Not enough memory to use internal diff for buffer \"%s\""), + buf->b_fname); + verbose_leave(); + } + return FAIL; + } + din->din_mmfile.ptr = (char *)ptr; + din->din_mmfile.size = len; + + len = 0; + for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) + { + for (s = ml_get_buf(buf, lnum, FALSE); *s != NUL; ) + { + if (diff_flags & DIFF_ICASE) + { + int c; + int orig_len; + char_u cbuf[MB_MAXBYTES + 1]; + + // xdiff doesn't support ignoring case, fold-case the text. + c = PTR2CHAR(s); + c = enc_utf8 ? utf_fold(c) : MB_TOLOWER(c); + orig_len = MB_PTR2LEN(s); + if (mb_char2bytes(c, cbuf) != orig_len) + // TODO: handle byte length difference + mch_memmove(ptr + len, s, orig_len); + else + mch_memmove(ptr + len, cbuf, orig_len); + + s += orig_len; + len += orig_len; + } + else + ptr[len++] = *s++; + } + ptr[len++] = NL; + } + return OK; +} + +/* + * Write buffer "buf" to file or memory buffer. + * Return FAIL for failure. + */ + static int +diff_write(buf_T *buf, diffin_T *din) +{ + int r; + char_u *save_ff; + + if (din->din_fname == NULL) + return diff_write_buffer(buf, din); + + // Always use 'fileformat' set to "unix". + save_ff = buf->b_p_ff; + buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); + r = buf_write(buf, din->din_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; +} + +/* + * Update the diffs for all buffers involved. + */ + static void +diff_try_update( + diffio_T *dio, + int idx_orig, + exarg_T *eap) // "eap" can be NULL +{ + buf_T *buf; + int idx_new; + + if (dio->dio_internal) + { + ga_init2(&dio->dio_diff.dout_ga, sizeof(char *), 1000); + } + else + { + // We need three temp file names. + dio->dio_orig.din_fname = vim_tempname('o', TRUE); + dio->dio_new.din_fname = vim_tempname('n', TRUE); + dio->dio_diff.dout_fname = vim_tempname('d', TRUE); + if (dio->dio_orig.din_fname == NULL + || dio->dio_new.din_fname == NULL + || dio->dio_diff.dout_fname == NULL) + goto theend; + } + + // Check external diff is actually working. + if (!dio->dio_internal && check_external_diff(dio) == FAIL) + goto theend; + + // :diffupdate! + if (eap != NULL && eap->forceit) + for (idx_new = idx_orig; idx_new < DB_COUNT; ++idx_new) + { + buf = curtab->tp_diffbuf[idx_new]; + if (buf_valid(buf)) + buf_check_timestamp(buf, FALSE); + } + + // Write the first buffer to a tempfile or mmfile_t. + buf = curtab->tp_diffbuf[idx_orig]; + if (diff_write(buf, &dio->dio_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 = curtab->tp_diffbuf[idx_new]; + if (buf == NULL || buf->b_ml.ml_mfp == NULL) + continue; // skip buffer that isn't loaded + + // Write the other buffer and diff with the first one. + if (diff_write(buf, &dio->dio_new) == FAIL) + continue; + if (diff_file(dio) == FAIL) + continue; + + // Read the diff output and add each entry to the diff list. + diff_read(idx_orig, idx_new, &dio->dio_diff); + + clear_diffin(&dio->dio_new); + clear_diffout(&dio->dio_diff); + } + clear_diffin(&dio->dio_orig); + +theend: + vim_free(dio->dio_orig.din_fname); + vim_free(dio->dio_new.din_fname); + vim_free(dio->dio_diff.dout_fname); +} + +/* + * Return TRUE if the options are set to use the internal diff library. + * Note that if the internal diff failed for one of the buffers, the external + * diff will be used anyway. + */ + int +diff_internal(void) +{ + return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL; +} + +/* + * Return TRUE if the internal diff failed for one of the diff buffers. + */ + static int +diff_internal_failed(void) +{ + int idx; + + // Only need to do something when there is another buffer. + for (idx = 0; idx < DB_COUNT; ++idx) + if (curtab->tp_diffbuf[idx] != NULL + && curtab->tp_diffbuf[idx]->b_diff_failed) + return TRUE; + return FALSE; +} + +/* + * Completely update the diffs for the buffers involved. + * When using the external "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). + */ + void +ex_diffupdate(exarg_T *eap) // "eap" can be NULL +{ + int idx_orig; + int idx_new; + diffio_T diffio; + int had_diffs = curtab->tp_first_diff != NULL; + + if (diff_busy) + { + diff_need_update = TRUE; + return; + } + + // Delete all diffblocks. + diff_clear(curtab); + curtab->tp_diff_invalid = FALSE; + + // Use the first buffer as the original text. + for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) + if (curtab->tp_diffbuf[idx_orig] != NULL) + break; + if (idx_orig == DB_COUNT) + goto theend; + + // Only need to do something when there is another buffer. + 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) + goto theend; + + // Only use the internal method if it did not fail for one of the buffers. + vim_memset(&diffio, 0, sizeof(diffio)); + diffio.dio_internal = diff_internal() && !diff_internal_failed(); + + diff_try_update(&diffio, idx_orig, eap); + if (diffio.dio_internal && diff_internal_failed()) + { + // Internal diff failed, use external diff instead. + vim_memset(&diffio, 0, sizeof(diffio)); + diff_try_update(&diffio, idx_orig, eap); + } + + // force updating cursor position on screen + curwin->w_valid_cursor.lnum = 0; + +theend: + // A redraw is needed if there were diffs and they were cleared, or there + // are diffs now, which means they got updated. + if (had_diffs || curtab->tp_first_diff != NULL) + { + diff_redraw(TRUE); + apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, FALSE, curbuf); + } +} + +/* + * 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. + */ + static int +check_external_diff(diffio_T *diffio) +{ + FILE *fd; + int ok; + int io_error = FALSE; + + // May try twice, first with "-a" and then without. + for (;;) + { + ok = FALSE; + fd = mch_fopen((char *)diffio->dio_orig.din_fname, "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 *)diffio->dio_new.din_fname, "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); + fd = NULL; + if (diff_file(diffio) == OK) + fd = mch_fopen((char *)diffio->dio_diff.dout_fname, "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); + } + mch_remove(diffio->dio_diff.dout_fname); + mch_remove(diffio->dio_new.din_fname); + } + mch_remove(diffio->dio_orig.din_fname); + } + +#ifdef FEAT_EVAL + /* When using 'diffexpr' break here. */ + if (*p_dex != NUL) + break; +#endif + +#if defined(MSWIN) + /* If the "-a" argument works, also check if "--binary" works. */ + if (ok && diff_a_works == MAYBE && diff_bin_works == MAYBE) + { + diff_a_works = TRUE; + diff_bin_works = TRUE; + continue; + } + if (!ok && diff_a_works == TRUE && diff_bin_works == TRUE) + { + /* Tried --binary, but it failed. "-a" works though. */ + diff_bin_works = FALSE; + ok = TRUE; + } +#endif + + /* 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; +#if defined(MSWIN) + diff_bin_works = MAYBE; +#endif + return FAIL; + } + return OK; +} + +/* + * Invoke the xdiff function. + */ + static int +diff_file_internal(diffio_T *diffio) +{ + xpparam_t param; + xdemitconf_t emit_cfg; + xdemitcb_t emit_cb; + + vim_memset(¶m, 0, sizeof(param)); + vim_memset(&emit_cfg, 0, sizeof(emit_cfg)); + vim_memset(&emit_cb, 0, sizeof(emit_cb)); + + param.flags = diff_algorithm; + + if (diff_flags & DIFF_IWHITE) + param.flags |= XDF_IGNORE_WHITESPACE_CHANGE; + if (diff_flags & DIFF_IWHITEALL) + param.flags |= XDF_IGNORE_WHITESPACE; + if (diff_flags & DIFF_IWHITEEOL) + param.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; + if (diff_flags & DIFF_IBLANK) + param.flags |= XDF_IGNORE_BLANK_LINES; + + emit_cfg.ctxlen = 0; // don't need any diff_context here + emit_cb.priv = &diffio->dio_diff; + emit_cb.outf = xdiff_out; + if (xdl_diff(&diffio->dio_orig.din_mmfile, + &diffio->dio_new.din_mmfile, + ¶m, &emit_cfg, &emit_cb) < 0) + { + emsg(_("E960: Problem creating the internal diff")); + return FAIL; + } + return OK; +} + +/* + * Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff". + * return OK or FAIL; + */ + static int +diff_file(diffio_T *dio) +{ + char_u *cmd; + size_t len; + char_u *tmp_orig = dio->dio_orig.din_fname; + char_u *tmp_new = dio->dio_new.din_fname; + char_u *tmp_diff = dio->dio_diff.dout_fname; + +#ifdef FEAT_EVAL + if (*p_dex != NUL) + { + // Use 'diffexpr' to generate the diff file. + eval_diff(tmp_orig, tmp_new, tmp_diff); + return OK; + } + else +#endif + // Use xdiff for generating the diff. + if (dio->dio_internal) + { + return diff_file_internal(dio); + } + else + { + len = STRLEN(tmp_orig) + STRLEN(tmp_new) + + STRLEN(tmp_diff) + STRLEN(p_srr) + 27; + cmd = alloc((unsigned)len); + if (cmd == NULL) + return FAIL; + + // We don't want $DIFF_OPTIONS to get in the way. + if (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%s%s %s", + diff_a_works == FALSE ? "" : "-a ", +#if defined(MSWIN) + diff_bin_works == TRUE ? "--binary " : "", +#else + "", +#endif + (diff_flags & DIFF_IWHITE) ? "-b " : "", + (diff_flags & DIFF_IWHITEALL) ? "-w " : "", + (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "", + (diff_flags & DIFF_IBLANK) ? "-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, SHELL_FILTER|SHELL_SILENT|SHELL_DOOUT); + unblock_autocmds(); + vim_free(cmd); + return OK; + } +} + +/* + * 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). + */ + void +ex_diffpatch(exarg_T *eap) +{ + char_u *tmp_orig; /* name of original temp file */ + char_u *tmp_new; /* name of patched temp file */ + char_u *buf = NULL; + size_t buflen; + 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 FEAT_BROWSE + char_u *browseFile = NULL; + int browse_flag = cmdmod.browse; +#endif + stat_T st; + char_u *esc_name = NULL; + +#ifdef FEAT_BROWSE + if (cmdmod.browse) + { + browseFile = do_browse(0, (char_u *)_("Patch file"), + eap->arg, NULL, NULL, + (char_u *)_(BROWSE_FILTER_ALL_FILES), NULL); + if (browseFile == NULL) + return; /* operation cancelled */ + eap->arg = browseFile; + cmdmod.browse = FALSE; /* don't let do_ecmd() browse again */ + } +#endif + + /* We need two temp file names. */ + tmp_orig = vim_tempname('o', FALSE); + tmp_new = vim_tempname('n', FALSE); + 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 + esc_name = vim_strsave_shellescape( +# ifdef UNIX + fullname != NULL ? fullname : +# endif + eap->arg, TRUE, TRUE); + if (esc_name == NULL) + goto theend; + buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16; + buf = alloc((unsigned)buflen); + if (buf == NULL) + goto theend; + +#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 (mch_dirname(dirbuf, MAXPATHL) != OK || mch_chdir((char *)dirbuf) != 0) + dirbuf[0] = NUL; + else + { +# ifdef TEMPDIRNAMES + if (vim_tempdir != NULL) + vim_ignored = mch_chdir((char *)vim_tempdir); + else +# endif + vim_ignored = mch_chdir("/tmp"); + shorten_fnames(TRUE); + } +#endif + +#ifdef FEAT_EVAL + if (*p_pex != NUL) + /* Use 'patchexpr' to generate the new file. */ + eval_patch(tmp_orig, +# ifdef UNIX + fullname != NULL ? fullname : +# endif + eap->arg, tmp_new); + else +#endif + { + /* Build the patch command and execute it. Ignore errors. Switch to + * cooked mode to allow the user to respond to prompts. */ + vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s", + tmp_new, tmp_orig, esc_name); + block_autocmds(); /* Avoid ShellCmdPost stuff */ + (void)call_shell(buf, SHELL_FILTER | SHELL_COOKED); + unblock_autocmds(); + } + +#ifdef UNIX + if (dirbuf[0] != NUL) + { + if (mch_chdir((char *)dirbuf) != 0) + emsg(_(e_prev_dir)); + shorten_fnames(TRUE); + } +#endif + + /* patch probably has written over the screen */ + redraw_later(CLEAR); + + /* Delete any .orig or .rej file created. */ + STRCPY(buf, tmp_new); + STRCAT(buf, ".orig"); + mch_remove(buf); + STRCPY(buf, tmp_new); + STRCAT(buf, ".rej"); + mch_remove(buf); + + /* Only continue if the output file was created. */ + if (mch_stat((char *)tmp_new, &st) < 0 || st.st_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"); + } + +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + /* 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) + mch_remove(tmp_orig); + vim_free(tmp_orig); + if (tmp_new != NULL) + mch_remove(tmp_new); + vim_free(tmp_new); + vim_free(newname); + vim_free(buf); +#ifdef UNIX + vim_free(fullname); +#endif + vim_free(esc_name); +#ifdef FEAT_BROWSE + vim_free(browseFile); + cmdmod.browse = browse_flag; +#endif +} + +/* + * Split the window and edit another file, setting options to show the diffs. + */ + void +ex_diffsplit(exarg_T *eap) +{ + win_T *old_curwin = curwin; + bufref_T old_curbuf; + + set_bufref(&old_curbuf, curbuf); +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + /* Need to compute w_fraction when no redraw happened yet. */ + validate_cursor(); + set_fraction(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); + + if (curwin != old_curwin) /* split must have worked */ + { + /* Set 'diff', 'scrollbind' on and 'wrap' off. */ + diff_win_options(curwin, TRUE); + if (win_valid(old_curwin)) + { + diff_win_options(old_curwin, TRUE); + + if (bufref_valid(&old_curbuf)) + /* Move the cursor position to that of the old window. */ + curwin->w_cursor.lnum = diff_get_corresponding_line( + old_curbuf.br_buf, old_curwin->w_cursor.lnum); + } + /* Now that lines are folded scroll to show the cursor at the same + * relative position. */ + scroll_to_fraction(curwin, curwin->w_height); + } + } +} + +/* + * Set options to show diffs for the current window. + */ + void +ex_diffthis(exarg_T *eap UNUSED) +{ + /* Set 'diff', 'scrollbind' on and 'wrap' off. */ + diff_win_options(curwin, TRUE); +} + + static void +set_diff_option(win_T *wp, int value) +{ + win_T *old_curwin = curwin; + + curwin = wp; + curbuf = curwin->w_buffer; + ++curbuf_lock; + set_option_value((char_u *)"diff", (long)value, NULL, OPT_LOCAL); + --curbuf_lock; + curwin = old_curwin; + curbuf = curwin->w_buffer; +} + +/* + * Set options in window "wp" for diff mode. + */ + void +diff_win_options( + win_T *wp, + int addbuf) /* Add buffer to diff. */ +{ +# ifdef FEAT_FOLDING + win_T *old_curwin = curwin; + + /* close the manually opened folds */ + curwin = wp; + newFoldLevel(); + curwin = old_curwin; +# endif + + /* Use 'scrollbind' and 'cursorbind' when available */ + if (!wp->w_p_diff) + wp->w_p_scb_save = wp->w_p_scb; + wp->w_p_scb = TRUE; + if (!wp->w_p_diff) + wp->w_p_crb_save = wp->w_p_crb; + wp->w_p_crb = TRUE; + if (!wp->w_p_diff) + wp->w_p_wrap_save = wp->w_p_wrap; + wp->w_p_wrap = FALSE; +# ifdef FEAT_FOLDING + curwin = wp; + curbuf = curwin->w_buffer; + if (!wp->w_p_diff) + { + if (wp->w_p_diff_saved) + free_string_option(wp->w_p_fdm_save); + 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) + { + 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); +# endif + if (vim_strchr(p_sbo, 'h') == NULL) + do_cmdline_cmd((char_u *)"set sbo+=hor"); + /* Save the current values, to be restored in ex_diffoff(). */ + wp->w_p_diff_saved = TRUE; + + set_diff_option(wp, 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. + */ + void +ex_diffoff(exarg_T *eap) +{ + win_T *wp; + int diffwin = FALSE; + + FOR_ALL_WINDOWS(wp) + { + if (eap->forceit ? wp->w_p_diff : wp == curwin) + { + /* Set 'diff' off. If option values were saved in + * diff_win_options(), restore the ones whose settings seem to have + * been left over from diff mode. */ + set_diff_option(wp, FALSE); + + if (wp->w_p_diff_saved) + { + + if (wp->w_p_scb) + wp->w_p_scb = wp->w_p_scb_save; + if (wp->w_p_crb) + wp->w_p_crb = wp->w_p_crb_save; + if (!wp->w_p_wrap) + wp->w_p_wrap = wp->w_p_wrap_save; +#ifdef FEAT_FOLDING + free_string_option(wp->w_p_fdm); + wp->w_p_fdm = vim_strsave( + *wp->w_p_fdm_save ? wp->w_p_fdm_save : (char_u*)"manual"); + + if (wp->w_p_fdc == diff_foldcolumn) + wp->w_p_fdc = wp->w_p_fdc_save; + if (wp->w_p_fdl == 0) + wp->w_p_fdl = wp->w_p_fdl_save; + + /* Only restore 'foldenable' when 'foldmethod' is not + * "manual", otherwise we continue to show the diff folds. */ + if (wp->w_p_fen) + wp->w_p_fen = foldmethodIsManual(wp) ? FALSE + : wp->w_p_fen_save; + + foldUpdateAll(wp); +#endif + } + /* remove filler lines */ + wp->w_topfill = 0; + + /* make sure topline is not halfway a fold and cursor is + * invalidated */ + changed_window_setting_win(wp); + + /* Note: 'sbo' is not restored, it's a global option. */ + diff_buf_adjust(wp); + } + diffwin |= wp->w_p_diff; + } + + /* Also remove hidden buffers from the list. */ + if (eap->forceit) + diff_buf_clear(); + + /* 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. + */ + static void +diff_read( + int idx_orig, // idx of original file + int idx_new, // idx of new file + diffout_T *dout) // diff output +{ + FILE *fd = NULL; + int line_idx = 0; + diff_T *dprev = NULL; + diff_T *dp = curtab->tp_first_diff; + diff_T *dn, *dpl; + char_u linebuf[LBUFLEN]; /* only need to hold the diff line */ + char_u *line; + long off; + int i; + linenr_T lnum_orig, lnum_new; + long count_orig, count_new; + int notset = TRUE; /* block "*dp" not set yet */ + enum { + DIFF_ED, + DIFF_UNIFIED, + DIFF_NONE + } diffstyle = DIFF_NONE; + + if (dout->dout_fname == NULL) + { + diffstyle = DIFF_UNIFIED; + } + else + { + fd = mch_fopen((char *)dout->dout_fname, "r"); + if (fd == NULL) + { + emsg(_("E98: Cannot read diff output")); + return; + } + } + + for (;;) + { + if (fd == NULL) + { + if (line_idx >= dout->dout_ga.ga_len) + break; // did last line + line = ((char_u **)dout->dout_ga.ga_data)[line_idx++]; + } + else + { + if (tag_fgets(linebuf, LBUFLEN, fd)) + break; // end of file + line = linebuf; + } + + if (diffstyle == DIFF_NONE) + { + // Determine diff style. + // ed like diff looks like this: + // {first}[,{last}]c{first}[,{last}] + // {first}a{first}[,{last}] + // {first}[,{last}]d{first} + // + // unified diff looks like this: + // --- file1 2018-03-20 13:23:35.783153140 +0100 + // +++ file2 2018-03-20 13:23:41.183156066 +0100 + // @@ -1,3 +1,5 @@ + if (isdigit(*line)) + diffstyle = DIFF_ED; + else if ((STRNCMP(line, "@@ ", 3) == 0)) + diffstyle = DIFF_UNIFIED; + else if ((STRNCMP(line, "--- ", 4) == 0) + && (tag_fgets(linebuf, LBUFLEN, fd) == 0) + && (STRNCMP(line, "+++ ", 4) == 0) + && (tag_fgets(linebuf, LBUFLEN, fd) == 0) + && (STRNCMP(line, "@@ ", 3) == 0)) + diffstyle = DIFF_UNIFIED; + else + // Format not recognized yet, skip over this line. Cygwin diff + // may put a warning at the start of the file. + continue; + } + + if (diffstyle == DIFF_ED) + { + if (!isdigit(*line)) + continue; // not the start of a diff block + if (parse_diff_ed(line, &lnum_orig, &count_orig, + &lnum_new, &count_new) == FAIL) + continue; + } + else if (diffstyle == DIFF_UNIFIED) + { + if (STRNCMP(line, "@@ ", 3) != 0) + continue; // not the start of a diff block + if (parse_diff_unified(line, &lnum_orig, &count_orig, + &lnum_new, &count_new) == FAIL) + continue; + } + else + { + emsg(_("E959: Invalid diff format.")); + break; + } + + // 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; + vim_free(dn); + dn = dpl; + } + } + else + { + // Allocate a new diffblock. + dp = diff_alloc_new(curtab, dprev, dp); + if (dp == NULL) + goto done; + + 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; + } + +done: + if (fd != NULL) + fclose(fd); +} + +/* + * Copy an entry at "dp" from "idx_orig" to "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". + */ + void +diff_clear(tabpage_T *tp) +{ + diff_T *p, *next_p; + + for (p = tp->tp_first_diff; p != NULL; p = next_p) + { + next_p = p->df_next; + vim_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. + */ + 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) + ex_diffupdate(NULL); /* update after a big change */ + + if (curtab->tp_first_diff == NULL || !wp->w_p_diff) /* no diffs at all */ + 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) + return 0; /* no diffs for buffer "buf" */ + +#ifdef FEAT_FOLDING + /* A closed fold never has filler lines. */ + if (hasFoldingWin(wp, lnum, NULL, NULL, TRUE, NULL)) + return 0; +#endif + + /* 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]) + return -1; /* nr of lines changed. */ + 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. + */ + static int +diff_equal_entry(diff_T *dp, int idx1, int idx2) +{ + int i; + char_u *line; + int cmp; + + if (dp->df_count[idx1] != dp->df_count[idx2]) + return FALSE; + if (diff_check_sanity(curtab, dp) == FAIL) + return FALSE; + for (i = 0; i < dp->df_count[idx1]; ++i) + { + line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1], + dp->df_lnum[idx1] + i, FALSE)); + if (line == NULL) + return FALSE; + cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2], + dp->df_lnum[idx2] + i, FALSE)); + vim_free(line); + if (cmp != 0) + return FALSE; + } + return TRUE; +} + +/* + * Compare the characters at "p1" and "p2". If they are equal (possibly + * ignoring case) return TRUE and set "len" to the number of bytes. + */ + static int +diff_equal_char(char_u *p1, char_u *p2, int *len) +{ + int l = (*mb_ptr2len)(p1); + + if (l != (*mb_ptr2len)(p2)) + return FALSE; + 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)))) + return FALSE; + *len = l; + } + else + { + if ((*p1 != *p2) + && (!(diff_flags & DIFF_ICASE) + || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) + return FALSE; + *len = 1; + } + return TRUE; +} + +/* + * Compare strings "s1" and "s2" according to 'diffopt'. + * Return non-zero when they are different. + */ + static int +diff_cmp(char_u *s1, char_u *s2) +{ + char_u *p1, *p2; + int l; + + if ((diff_flags & DIFF_IBLANK) + && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) + return 0; + + if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0) + return STRCMP(s1, s2); + if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) + return MB_STRICMP(s1, s2); + + p1 = s1; + p2 = s2; + + // Ignore white space changes and possibly ignore case. + while (*p1 != NUL && *p2 != NUL) + { + if (((diff_flags & DIFF_IWHITE) + && VIM_ISWHITE(*p1) && VIM_ISWHITE(*p2)) + || ((diff_flags & DIFF_IWHITEALL) + && (VIM_ISWHITE(*p1) || VIM_ISWHITE(*p2)))) + { + p1 = skipwhite(p1); + p2 = skipwhite(p2); + } + else + { + if (!diff_equal_char(p1, p2, &l)) + break; + p1 += l; + p2 += l; + } + } + + // 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". + */ + int +diff_check_fill(win_T *wp, linenr_T lnum) +{ + int n; + + /* be quick when there are no filler lines */ + if (!(diff_flags & DIFF_FILLER)) + return 0; + 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. + */ + void +diff_set_topline(win_T *fromwin, win_T *towin) +{ + buf_T *frombuf = fromwin->w_buffer; + linenr_T lnum = fromwin->w_topline; + int fromidx; + int toidx; + diff_T *dp; + int max_count; + int i; + + fromidx = diff_buf_idx(frombuf); + if (fromidx == DB_COUNT) + return; /* safety check */ + + if (curtab->tp_diff_invalid) + ex_diffupdate(NULL); /* update after a big change */ + + 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". */ + toidx = diff_buf_idx(towin->w_buffer); + if (toidx == DB_COUNT) + return; /* safety check */ + + 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); +#ifdef FEAT_FOLDING + (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline, + NULL, TRUE, NULL); +#endif +} + +/* + * This is called when 'diffopt' is changed. + */ + int +diffopt_changed(void) +{ + char_u *p; + int diff_context_new = 6; + int diff_flags_new = 0; + int diff_foldcolumn_new = 2; + long diff_algorithm_new = 0; + long diff_indent_heuristic = 0; + tabpage_T *tp; + + 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, "iblank", 6) == 0) + { + p += 6; + diff_flags_new |= DIFF_IBLANK; + } + else if (STRNCMP(p, "icase", 5) == 0) + { + p += 5; + diff_flags_new |= DIFF_ICASE; + } + else if (STRNCMP(p, "iwhiteall", 9) == 0) + { + p += 9; + diff_flags_new |= DIFF_IWHITEALL; + } + else if (STRNCMP(p, "iwhiteeol", 9) == 0) + { + p += 9; + diff_flags_new |= DIFF_IWHITEEOL; + } + 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); + } + else if (STRNCMP(p, "hiddenoff", 9) == 0) + { + p += 9; + diff_flags_new |= DIFF_HIDDEN_OFF; + } + else if (STRNCMP(p, "indent-heuristic", 16) == 0) + { + p += 16; + diff_indent_heuristic = XDF_INDENT_HEURISTIC; + } + else if (STRNCMP(p, "internal", 8) == 0) + { + p += 8; + diff_flags_new |= DIFF_INTERNAL; + } + else if (STRNCMP(p, "algorithm:", 10) == 0) + { + p += 10; + if (STRNCMP(p, "myers", 5) == 0) + { + p += 5; + diff_algorithm_new = 0; + } + else if (STRNCMP(p, "minimal", 7) == 0) + { + p += 7; + diff_algorithm_new = XDF_NEED_MINIMAL; + } + else if (STRNCMP(p, "patience", 8) == 0) + { + p += 8; + diff_algorithm_new = XDF_PATIENCE_DIFF; + } + else if (STRNCMP(p, "histogram", 9) == 0) + { + p += 9; + diff_algorithm_new = XDF_HISTOGRAM_DIFF; + } + else + return FAIL; + } + + if (*p != ',' && *p != NUL) + return FAIL; + if (*p == ',') + ++p; + } + + diff_algorithm_new |= diff_indent_heuristic; + + /* Can't have both "horizontal" and "vertical". */ + if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) + return FAIL; + + // If flags were added or removed, or the algorithm was changed, need to + // update the diff. + if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new) + FOR_ALL_TABPAGES(tp) + tp->tp_diff_invalid = TRUE; + + diff_flags = diff_flags_new; + diff_context = diff_context_new; + diff_foldcolumn = diff_foldcolumn_new; + diff_algorithm = diff_algorithm_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". + */ + int +diffopt_horizontal(void) +{ + return (diff_flags & DIFF_HORIZONTAL) != 0; +} + +/* + * Return TRUE if 'diffopt' contains "hiddenoff". + */ + int +diffopt_hiddenoff(void) +{ + return (diff_flags & DIFF_HIDDEN_OFF) != 0; +} + +/* + * Find the difference within a changed line. + * Returns TRUE if the line was added, no other buffer has it. + */ + int +diff_find_change( + win_T *wp, + linenr_T lnum, + int *startp, /* first char of the change */ + int *endp) /* last char of the change */ +{ + char_u *line_org; + char_u *line_new; + int i; + int si_org, si_new; + int ei_org, ei_new; + diff_T *dp; + int idx; + int off; + int added = TRUE; + char_u *p1, *p2; + int l; + + /* Make a copy of the line, the next ml_get() will invalidate it. */ + line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE)); + if (line_org == NULL) + return FALSE; + + idx = diff_buf_idx(wp->w_buffer); + if (idx == DB_COUNT) /* cannot happen */ + { + vim_free(line_org); + return FALSE; + } + + /* 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 || diff_check_sanity(curtab, dp) == FAIL) + { + vim_free(line_org); + return FALSE; + } + + off = lnum - dp->df_lnum[idx]; + + 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])) + || ((diff_flags & DIFF_IWHITEALL) + && (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 (!diff_equal_char(line_org + si_org, line_new + si_new, + &l)) + break; + si_org += l; + si_new += l; + } + } + 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])) + || ((diff_flags & DIFF_IWHITEALL) + && (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 + { + p1 = line_org + ei_org; + p2 = line_new + ei_new; + p1 -= (*mb_head_off)(line_org, p1); + p2 -= (*mb_head_off)(line_new, p2); + if (!diff_equal_char(p1, p2, &l)) + break; + ei_org -= l; + ei_new -= l; + } + } + if (*endp < ei_org) + *endp = ei_org; + } + } + + vim_free(line_org); + return added; +} + +#if defined(FEAT_FOLDING) || defined(PROTO) +/* + * Return TRUE if line "lnum" is not close to a diff block, this line should + * be in a fold. + * Return FALSE if there are no diff blocks at all in this window. + */ + int +diff_infold(win_T *wp, linenr_T lnum) +{ + int i; + int idx = -1; + int other = FALSE; + diff_T *dp; + + /* Return if 'diff' isn't set. */ + if (!wp->w_p_diff) + return FALSE; + + 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) + ex_diffupdate(NULL); /* update after a big change */ + + /* 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; +} +#endif + +/* + * "dp" and "do" commands. + */ + void +nv_diffgetput(int put, long count) +{ + exarg_T ea; + char_u buf[30]; + +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf)) + { + vim_beep(BO_OPER); + return; + } +#endif + if (count == 0) + ea.arg = (char_u *)""; + else + { + vim_snprintf((char *)buf, 30, "%ld", count); + ea.arg = buf; + } + 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" + * ":diffput" + */ + 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 idx_cur; + int idx_other; + int idx_from; + int idx_to; + 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; + + /* Find the current buffer in the list of diff buffers. */ + 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) + return; /* error message already given */ + } + buf = buflist_findnr(i); + if (buf == NULL) + { + semsg(_("E102: Can't find buffer \"%s\""), eap->arg); + return; + } + if (buf == curbuf) + return; /* nothing to do */ + idx_other = diff_buf_idx(buf); + if (idx_other == DB_COUNT) + { + semsg(_("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")); + goto theend; + } + } + + dprev = NULL; + for (dp = curtab->tp_first_diff; dp != NULL; ) + { + if (dp->df_lnum[idx_cur] > eap->line2 + off) + break; /* past the range that was specified */ + + 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 = BUFEMPTY(); + 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; + + 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); + vim_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. */ +#ifdef FEAT_FOLDING + diff_fold_update(dfree, idx_to); +#endif + vim_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); + } + +theend: + diff_busy = FALSE; + if (diff_need_update) + ex_diffupdate(NULL); + + // Check that the cursor is on a valid character and update its + // position. When there were filler lines the topline has become + // invalid. + check_cursor(); + changed_line_abv_curs(); + + if (diff_need_update) + // redraw already done by ex_diffupdate() + diff_need_update = FALSE; + else + { + // Also need to redraw the other buffers. + diff_redraw(FALSE); + apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, FALSE, curbuf); + } +} + +#ifdef FEAT_FOLDING +/* + * Update folds for all diff buffers for entry "dp". + * Skip buffer with index "skip_idx". + * When there are no diffs, all folds are removed. + */ + static void +diff_fold_update(diff_T *dp, int skip_idx) +{ + int i; + win_T *wp; + + FOR_ALL_WINDOWS(wp) + 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]); +} +#endif + +/* + * Return TRUE if buffer "buf" is in diff-mode. + */ + int +diff_mode_buf(buf_T *buf) +{ + tabpage_T *tp; + + FOR_ALL_TABPAGES(tp) + if (diff_buf_idx_tp(buf, tp) != DB_COUNT) + return TRUE; + return FALSE; +} + +/* + * Move "count" times in direction "dir" to the next diff block. + * Return FAIL if there isn't such a diff block. + */ + int +diff_move_to(int dir, long count) +{ + int idx; + linenr_T lnum = curwin->w_cursor.lnum; + diff_T *dp; + + idx = diff_buf_idx(curbuf); + if (idx == DB_COUNT || curtab->tp_first_diff == NULL) + return FAIL; + + if (curtab->tp_diff_invalid) + ex_diffupdate(NULL); /* update after a big change */ + + 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; + + 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; +} + +/* + * Return the line number in the current window that is closest to "lnum1" in + * "buf1" in diff mode. + */ + static linenr_T +diff_get_corresponding_line_int( + buf_T *buf1, + linenr_T lnum1) +{ + int idx1; + int idx2; + diff_T *dp; + int baseline = 0; + + idx1 = diff_buf_idx(buf1); + idx2 = diff_buf_idx(curbuf); + if (idx1 == DB_COUNT || idx2 == DB_COUNT || curtab->tp_first_diff == NULL) + return lnum1; + + if (curtab->tp_diff_invalid) + ex_diffupdate(NULL); /* update after a big change */ + + 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) + return lnum1 - baseline; + 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; + } + if ( (dp->df_lnum[idx1] == lnum1) + && (dp->df_count[idx1] == 0) + && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum) + && ((dp->df_lnum[idx2] + dp->df_count[idx2]) + > curwin->w_cursor.lnum)) + /* + * 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 curwin->w_cursor.lnum; + 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 */ + return lnum1 - baseline; +} + +/* + * Return the line number in the current window that is closest to "lnum1" in + * "buf1" in diff mode. Checks the line number to be valid. + */ + linenr_T +diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1) +{ + linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1); + + /* don't end up past the end of the file */ + if (lnum > curbuf->b_ml.ml_line_count) + return curbuf->b_ml.ml_line_count; + return lnum; +} + +/* + * 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) + ex_diffupdate(NULL); /* update after a big change */ + + /* 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; +} + +/* + * Handle an ED style diff line. + * Return FAIL if the line does not contain diff info. + */ + static int +parse_diff_ed( + char_u *line, + linenr_T *lnum_orig, + long *count_orig, + linenr_T *lnum_new, + long *count_new) +{ + char_u *p; + long f1, l1, f2, l2; + int difftype; + + // The line must be one of three formats: + // change: {first}[,{last}]c{first}[,{last}] + // append: {first}a{first}[,{last}] + // delete: {first}[,{last}]d{first} + p = line; + f1 = getdigits(&p); + if (*p == ',') + { + ++p; + l1 = getdigits(&p); + } + else + l1 = f1; + if (*p != 'a' && *p != 'c' && *p != 'd') + return FAIL; // invalid diff format + difftype = *p++; + f2 = getdigits(&p); + if (*p == ',') + { + ++p; + l2 = getdigits(&p); + } + else + l2 = f2; + if (l1 < f1 || l2 < f2) + return FAIL; + + 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; + } + return OK; +} + +/* + * Parses unified diff with zero(!) context lines. + * Return FAIL if there is no diff information in "line". + */ + static int +parse_diff_unified( + char_u *line, + linenr_T *lnum_orig, + long *count_orig, + linenr_T *lnum_new, + long *count_new) +{ + char_u *p; + long oldline, oldcount, newline, newcount; + + // Parse unified diff hunk header: + // @@ -oldline,oldcount +newline,newcount @@ + p = line; + if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') + { + oldline = getdigits(&p); + if (*p == ',') + { + ++p; + oldcount = getdigits(&p); + } + else + oldcount = 1; + if (*p++ == ' ' && *p++ == '+') + { + newline = getdigits(&p); + if (*p == ',') + { + ++p; + newcount = getdigits(&p); + } + else + newcount = 1; + } + else + return FAIL; // invalid diff format + + if (oldcount == 0) + oldline += 1; + if (newcount == 0) + newline += 1; + if (newline == 0) + newline = 1; + + *lnum_orig = oldline; + *count_orig = oldcount; + *lnum_new = newline; + *count_new = newcount; + + return OK; + } + + return FAIL; +} + +/* + * Callback function for the xdl_diff() function. + * Stores the diff output in a grow array. + */ + static int +xdiff_out(void *priv, mmbuffer_t *mb, int nbuf) +{ + diffout_T *dout = (diffout_T *)priv; + char_u *p; + + // The header line always comes by itself, text lines in at least two + // parts. We drop the text part. + if (nbuf > 1) + return 0; + + // sanity check + if (STRNCMP(mb[0].ptr, "@@ ", 3) != 0) + return 0; + + if (ga_grow(&dout->dout_ga, 1) == FAIL) + return -1; + p = vim_strnsave((char_u *)mb[0].ptr, mb[0].size); + if (p == NULL) + return -1; + ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p; + return 0; +} + +#endif /* FEAT_DIFF */ diff --git a/src/digraph.c b/src/digraph.c new file mode 100644 index 0000000..d936136 --- /dev/null +++ b/src/digraph.c @@ -0,0 +1,2472 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * digraph.c: code for digraphs + */ + +#include "vim.h" + +#if defined(FEAT_DIGRAPHS) || defined(PROTO) + +typedef int result_T; + +typedef struct digraph +{ + char_u char1; + char_u char2; + result_T result; +} digr_T; + +static void printdigraph(digr_T *dp, result_T *previous); + +/* 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[] = + +#ifdef __MINT__ + /* + * ATARI digraphs + */ + {{'C', ',', 128}, /* ~@ XX */ + {'u', '"', 129}, /* */ + {'e', '\'', 130}, /* ‚ */ + {'a', '^', 131}, /* ƒ */ + {'a', '"', 132}, /* „ */ + {'a', '`', 133}, /* … */ + {'a', '@', 134}, /* † */ + {'c', ',', 135}, /* ~G XX */ + {'e', '^', 136}, /* ~H XX */ + {'e', '"', 137}, /* ‰ */ + {'e', '`', 138}, /* Š */ + {'i', '"', 139}, /* ‹ */ + {'i', '^', 140}, /* Œ */ + {'i', '`', 141}, /* */ + {'A', '"', 142}, /* Ž */ + {'A', '@', 143}, /* */ + {'E', '\'', 144}, /* */ + {'a', 'e', 145}, /* ‘ */ + {'A', 'E', 146}, /* ’ */ + {'o', '^', 147}, /* “ */ + {'o', '"', 148}, /* ” */ + {'o', '`', 149}, /* • */ + {'u', '^', 150}, /* – */ + {'u', '`', 151}, /* — */ + {'y', '"', 152}, /* ˜ */ + {'O', '"', 153}, /* ™ */ + {'U', '"', 154}, /* š */ + {'c', '|', 155}, /* › */ + {'$', '$', 156}, /* œ */ + {'Y', '-', 157}, /* ~] XX */ + {'s', 's', 158}, /* ž */ + {'f', 'f', 159}, /* Ÿ */ + {'a', '\'', 160}, /*   */ + {'i', '\'', 161}, /* ¡ */ + {'o', '\'', 162}, /* ¢ */ + {'u', '\'', 163}, /* £ */ + {'n', '~', 164}, /* ¤ */ + {'N', '~', 165}, /* ¥ */ + {'a', 'a', 166}, /* ¦ */ + {'o', 'o', 167}, /* § */ + {'~', '?', 168}, /* ¨ */ + {'-', 'a', 169}, /* © */ + {'a', '-', 170}, /* ª */ + {'1', '2', 171}, /* « */ + {'1', '4', 172}, /* ¬ */ + {'~', '!', 173}, /* ­ */ + {'<', '<', 174}, /* ® */ + {'>', '>', 175}, /* ¯ */ + {'j', 'u', 230}, /* æ */ + {'o', '/', 237}, /* í */ + {'+', '-', 241}, /* ñ */ + {'>', '=', 242}, /* ò */ + {'<', '=', 243}, /* ó */ + {':', '-', 246}, /* ö */ + {'~', '~', 247}, /* ÷ */ + {'~', 'o', 248}, /* ø */ + {'2', '2', 253}, /* ý */ + {NUL, NUL, NUL} + }; + +#else /* !__MINT__ */ +# ifdef HPUX_DIGRAPHS + + /* + * different HPUX digraphs + */ + {{'A', '`', 161}, /* ¡ */ + {'A', '^', 162}, /* ¢ */ + {'E', '`', 163}, /* £ */ + {'E', '^', 164}, /* ¤ */ + {'E', '"', 165}, /* ¥ */ + {'I', '^', 166}, /* ¦ */ + {'I', '"', 167}, /* § */ + {'\'', '\'', 168}, /* ¨ */ + {'`', '`', 169}, /* © */ + {'^', '^', 170}, /* ª */ + {'"', '"', 171}, /* « */ + {'~', '~', 172}, /* ¬ */ + {'U', '`', 173}, /* ­ */ + {'U', '^', 174}, /* ® */ + {'L', '=', 175}, /* ¯ */ + {'~', '_', 176}, /* ° */ + {'Y', '\'', 177}, /* ± */ + {'y', '\'', 178}, /* ² */ + {'~', 'o', 179}, /* ³ */ + {'C', ',', 180}, /* ´ */ + {'c', ',', 181}, /* µ */ + {'N', '~', 182}, /* ¶ */ + {'n', '~', 183}, /* · */ + {'~', '!', 184}, /* ¸ */ + {'~', '?', 185}, /* ¹ */ + {'o', 'x', 186}, /* º */ + {'L', '-', 187}, /* » */ + {'Y', '=', 188}, /* ¼ */ + {'p', 'p', 189}, /* ½ */ + {'f', 'l', 190}, /* ¾ */ + {'c', '|', 191}, /* ¿ */ + {'a', '^', 192}, /* À */ + {'e', '^', 193}, /* Á */ + {'o', '^', 194}, /*  */ + {'u', '^', 195}, /* à */ + {'a', '\'', 196}, /* Ä */ + {'e', '\'', 197}, /* Å */ + {'o', '\'', 198}, /* Æ */ + {'u', '\'', 199}, /* Ç */ + {'a', '`', 200}, /* È */ + {'e', '`', 201}, /* É */ + {'o', '`', 202}, /* Ê */ + {'u', '`', 203}, /* Ë */ + {'a', '"', 204}, /* Ì */ + {'e', '"', 205}, /* Í */ + {'o', '"', 206}, /* Î */ + {'u', '"', 207}, /* Ï */ + {'A', 'o', 208}, /* Ð */ + {'i', '^', 209}, /* Ñ */ + {'O', '/', 210}, /* Ò */ + {'A', 'E', 211}, /* Ó */ + {'a', 'o', 212}, /* Ô */ + {'i', '\'', 213}, /* Õ */ + {'o', '/', 214}, /* Ö */ + {'a', 'e', 215}, /* × */ + {'A', '"', 216}, /* Ø */ + {'i', '`', 217}, /* Ù */ + {'O', '"', 218}, /* Ú */ + {'U', '"', 219}, /* Û */ + {'E', '\'', 220}, /* Ü */ + {'i', '"', 221}, /* Ý */ + {'s', 's', 222}, /* Þ */ + {'O', '^', 223}, /* ß */ + {'A', '\'', 224}, /* à */ + {'A', '~', 225}, /* á */ + {'a', '~', 226}, /* â */ + {'D', '-', 227}, /* ã */ + {'d', '-', 228}, /* ä */ + {'I', '\'', 229}, /* å */ + {'I', '`', 230}, /* æ */ + {'O', '\'', 231}, /* ç */ + {'O', '`', 232}, /* è */ + {'O', '~', 233}, /* é */ + {'o', '~', 234}, /* ê */ + {'S', '~', 235}, /* ë */ + {'s', '~', 236}, /* ì */ + {'U', '\'', 237}, /* í */ + {'Y', '"', 238}, /* î */ + {'y', '"', 239}, /* ï */ + {'p', '-', 240}, /* ð */ + {'p', '~', 241}, /* ñ */ + {'~', '.', 242}, /* ò */ + {'j', 'u', 243}, /* ó */ + {'P', 'p', 244}, /* ô */ + {'3', '4', 245}, /* õ */ + {'-', '-', 246}, /* ö */ + {'1', '4', 247}, /* ÷ */ + {'1', '2', 248}, /* ø */ + {'a', '_', 249}, /* ù */ + {'o', '_', 250}, /* ú */ + {'<', '<', 251}, /* û */ + {'x', 'x', 252}, /* ü */ + {'>', '>', 253}, /* ý */ + {'+', '-', 254}, /* þ */ + {'n', 'u', 255}, /* x XX */ + {NUL, NUL, NUL} + }; + +# else /* !HPUX_DIGRAPHS */ + +# ifdef EBCDIC + + /* + * EBCDIC - ISO digraphs + * TODO: EBCDIC Table is Code-Page 1047 + */ + {{'a', '^', 66}, /* â */ + {'a', '"', 67}, /* ä */ + {'a', '`', 68}, /* à */ + {'a', '\'', 69}, /* á */ + {'a', '~', 70}, /* ã */ + {'a', '@', 71}, /* å */ + {'a', 'a', 71}, /* å */ + {'c', ',', 72}, /* ç */ + {'n', '~', 73}, /* ñ */ + {'c', '|', 74}, /* ¢ */ + {'e', '\'', 81}, /* é */ + {'e', '^', 82}, /* ê */ + {'e', '"', 83}, /* ë */ + {'e', '`', 84}, /* è */ + {'i', '\'', 85}, /* í */ + {'i', '^', 86}, /* î */ + {'i', '"', 87}, /* ï */ + {'i', '`', 88}, /* ì */ + {'s', 's', 89}, /* ß */ + {'A', '^', 98}, /*  */ + {'A', '"', 99}, /* Ä */ + {'A', '`', 100}, /* À */ + {'A', '\'', 101}, /* Á */ + {'A', '~', 102}, /* à */ + {'A', '@', 103}, /* Å */ + {'A', 'A', 103}, /* Å */ + {'C', ',', 104}, /* Ç */ + {'N', '~', 105}, /* Ñ */ + {'|', '|', 106}, /* ¦ */ + {'o', '/', 112}, /* ø */ + {'E', '\'', 113}, /* É */ + {'E', '^', 114}, /* Ê */ + {'E', '"', 115}, /* Ë */ + {'E', '`', 116}, /* È */ + {'I', '\'', 117}, /* Í */ + {'I', '^', 118}, /* Î */ + {'I', '"', 119}, /* Ï */ + {'I', '`', 120}, /* Ì */ + {'O', '/', 128}, /* 0/ XX */ + {'<', '<', 138}, /* « */ + {'>', '>', 139}, /* » */ + {'d', '-', 140}, /* ð */ + {'y', '\'', 141}, /* ý */ + {'i', 'p', 142}, /* þ */ + {'+', '-', 143}, /* ± */ + {'~', 'o', 144}, /* ° */ + {'a', '-', 154}, /* ª */ + {'o', '-', 155}, /* º */ + {'a', 'e', 156}, /* æ */ + {',', ',', 157}, /* , XX */ + {'A', 'E', 158}, /* Æ */ + {'o', 'x', 159}, /* ¤ - currency symbol in ISO 8859-1 */ + {'e', '=', 159}, /* ¤ - euro symbol in ISO 8859-15 */ + {'E', 'u', 159}, /* ¤ - euro symbol in ISO 8859-15 */ + {'j', 'u', 160}, /* µ */ + {'y', '"', 167}, /* x XX */ + {'~', '!', 170}, /* ¡ */ + {'~', '?', 171}, /* ¿ */ + {'D', '-', 172}, /* Ð */ + {'I', 'p', 174}, /* Þ */ + {'r', 'O', 175}, /* ® */ + {'-', ',', 176}, /* ¬ */ + {'$', '$', 177}, /* £ */ + {'Y', '-', 178}, /* ¥ */ + {'~', '.', 179}, /* · */ + {'c', 'O', 180}, /* © */ + {'p', 'a', 181}, /* § */ + {'p', 'p', 182}, /* ¶ */ + {'1', '4', 183}, /* ¼ */ + {'1', '2', 184}, /* ½ */ + {'3', '4', 185}, /* ¾ */ + {'Y', '\'', 186}, /* Ý */ + {'"', '"', 187}, /* ¨ */ + {'-', '=', 188}, /* ¯ */ + {'\'', '\'', 190}, /* ´ */ + {'O', 'E', 191}, /* × - OE in ISO 8859-15 */ + {'/', '\\', 191}, /* × - multiplication symbol in ISO 8859-1 */ + {'-', '-', 202}, /* ­ */ + {'o', '^', 203}, /* ô */ + {'o', '"', 204}, /* ö */ + {'o', '`', 205}, /* ò */ + {'o', '\'', 206}, /* ó */ + {'o', '~', 207}, /* õ */ + {'1', '1', 218}, /* ¹ */ + {'u', '^', 219}, /* û */ + {'u', '"', 220}, /* ü */ + {'u', '`', 221}, /* ù */ + {'u', '\'', 222}, /* ú */ + {':', '-', 225}, /* ÷ - division symbol in ISO 8859-1 */ + {'o', 'e', 225}, /* ÷ - oe in ISO 8859-15 */ + {'2', '2', 234}, /* ² */ + {'O', '^', 235}, /* Ô */ + {'O', '"', 236}, /* Ö */ + {'O', '`', 237}, /* Ò */ + {'O', '\'', 238}, /* Ó */ + {'O', '~', 239}, /* Õ */ + {'3', '3', 250}, /* ³ */ + {'U', '^', 251}, /* Û */ + {'U', '"', 252}, /* Ü */ + {'U', '`', 253}, /* Ù */ + {'U', '\'', 254}, /* Ú */ + {NUL, NUL, NUL} + }; + +# else +# ifdef OLD_DIGRAPHS + + /* + * digraphs compatible with Vim 5.x + */ + {{'~', '!', 161}, /* ¡ */ + {'c', '|', 162}, /* ¢ */ + {'$', '$', 163}, /* £ */ + {'o', 'x', 164}, /* ¤ - currency symbol in ISO 8859-1 */ + {'e', '=', 164}, /* ¤ - euro symbol in ISO 8859-15 */ + {'Y', '-', 165}, /* ¥ */ + {'|', '|', 166}, /* ¦ */ + {'p', 'a', 167}, /* § */ + {'"', '"', 168}, /* ¨ */ + {'c', 'O', 169}, /* © */ + {'a', '-', 170}, /* ª */ + {'<', '<', 171}, /* « */ + {'-', ',', 172}, /* ¬ */ + {'-', '-', 173}, /* ­ */ + {'r', 'O', 174}, /* ® */ + {'-', '=', 175}, /* ¯ */ + {'~', 'o', 176}, /* ° */ + {'+', '-', 177}, /* ± */ + {'2', '2', 178}, /* ² */ + {'3', '3', 179}, /* ³ */ + {'\'', '\'', 180}, /* ´ */ + {'j', 'u', 181}, /* µ */ + {'p', 'p', 182}, /* ¶ */ + {'~', '.', 183}, /* · */ + {',', ',', 184}, /* ¸ */ + {'1', '1', 185}, /* ¹ */ + {'o', '-', 186}, /* º */ + {'>', '>', 187}, /* » */ + {'1', '4', 188}, /* ¼ */ + {'1', '2', 189}, /* ½ */ + {'3', '4', 190}, /* ¾ */ + {'~', '?', 191}, /* ¿ */ + {'A', '`', 192}, /* À */ + {'A', '\'', 193}, /* Á */ + {'A', '^', 194}, /*  */ + {'A', '~', 195}, /* à */ + {'A', '"', 196}, /* Ä */ + {'A', '@', 197}, /* Å */ + {'A', 'A', 197}, /* Å */ + {'A', 'E', 198}, /* Æ */ + {'C', ',', 199}, /* Ç */ + {'E', '`', 200}, /* È */ + {'E', '\'', 201}, /* É */ + {'E', '^', 202}, /* Ê */ + {'E', '"', 203}, /* Ë */ + {'I', '`', 204}, /* Ì */ + {'I', '\'', 205}, /* Í */ + {'I', '^', 206}, /* Î */ + {'I', '"', 207}, /* Ï */ + {'D', '-', 208}, /* Ð */ + {'N', '~', 209}, /* Ñ */ + {'O', '`', 210}, /* Ò */ + {'O', '\'', 211}, /* Ó */ + {'O', '^', 212}, /* Ô */ + {'O', '~', 213}, /* Õ */ + {'O', '"', 214}, /* Ö */ + {'/', '\\', 215}, /* × - multiplication symbol in ISO 8859-1 */ + {'O', 'E', 215}, /* × - OE in ISO 8859-15 */ + {'O', '/', 216}, /* Ø */ + {'U', '`', 217}, /* Ù */ + {'U', '\'', 218}, /* Ú */ + {'U', '^', 219}, /* Û */ + {'U', '"', 220}, /* Ü */ + {'Y', '\'', 221}, /* Ý */ + {'I', 'p', 222}, /* Þ */ + {'s', 's', 223}, /* ß */ + {'a', '`', 224}, /* à */ + {'a', '\'', 225}, /* á */ + {'a', '^', 226}, /* â */ + {'a', '~', 227}, /* ã */ + {'a', '"', 228}, /* ä */ + {'a', '@', 229}, /* å */ + {'a', 'a', 229}, /* å */ + {'a', 'e', 230}, /* æ */ + {'c', ',', 231}, /* ç */ + {'e', '`', 232}, /* è */ + {'e', '\'', 233}, /* é */ + {'e', '^', 234}, /* ê */ + {'e', '"', 235}, /* ë */ + {'i', '`', 236}, /* ì */ + {'i', '\'', 237}, /* í */ + {'i', '^', 238}, /* î */ + {'i', '"', 239}, /* ï */ + {'d', '-', 240}, /* ð */ + {'n', '~', 241}, /* ñ */ + {'o', '`', 242}, /* ò */ + {'o', '\'', 243}, /* ó */ + {'o', '^', 244}, /* ô */ + {'o', '~', 245}, /* õ */ + {'o', '"', 246}, /* ö */ + {':', '-', 247}, /* ÷ - division symbol in ISO 8859-1 */ + {'o', 'e', 247}, /* ÷ - oe in ISO 8859-15 */ + {'o', '/', 248}, /* ø */ + {'u', '`', 249}, /* ù */ + {'u', '\'', 250}, /* ú */ + {'u', '^', 251}, /* û */ + {'u', '"', 252}, /* ü */ + {'y', '\'', 253}, /* ý */ + {'i', 'p', 254}, /* þ */ + {'y', '"', 255}, /* x XX */ + {NUL, NUL, NUL} + }; +# else /* OLD_DIGRAPHS */ + + /* + * 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}, +#define DG_START_LATIN 0xa1 + {'!', 'I', 0xa1}, + {'~', '!', 0xa1}, // ¡ Vim 5.x compatible + {'C', 't', 0xa2}, + {'c', '|', 0xa2}, // ¢ Vim 5.x compatible + {'P', 'd', 0xa3}, + {'$', '$', 0xa3}, // £ Vim 5.x compatible + {'C', 'u', 0xa4}, + {'o', 'x', 0xa4}, // ¤ Vim 5.x compatible + {'Y', 'e', 0xa5}, + {'Y', '-', 0xa5}, // ¥ Vim 5.x compatible + {'B', 'B', 0xa6}, + {'|', '|', 0xa6}, // ¦ Vim 5.x compatible + {'S', 'E', 0xa7}, + {'\'', ':', 0xa8}, + {'C', 'o', 0xa9}, + {'c', 'O', 0xa9}, // © Vim 5.x compatible + {'-', 'a', 0xaa}, + {'<', '<', 0xab}, + {'N', 'O', 0xac}, + {'-', ',', 0xac}, // ¬ Vim 5.x compatible + {'-', '-', 0xad}, + {'R', 'g', 0xae}, + {'\'', 'm', 0xaf}, + {'-', '=', 0xaf}, // ¯ Vim 5.x compatible + {'D', 'G', 0xb0}, + {'~', 'o', 0xb0}, // ° Vim 5.x compatible + {'+', '-', 0xb1}, + {'2', 'S', 0xb2}, + {'2', '2', 0xb2}, // ² Vim 5.x compatible + {'3', 'S', 0xb3}, + {'3', '3', 0xb3}, // ³ Vim 5.x compatible + {'\'', '\'', 0xb4}, + {'M', 'y', 0xb5}, + {'P', 'I', 0xb6}, + {'p', 'p', 0xb6}, // ¶ Vim 5.x compatible + {'.', 'M', 0xb7}, + {'~', '.', 0xb7}, // · Vim 5.x compatible + {'\'', ',', 0xb8}, + {'1', 'S', 0xb9}, + {'1', '1', 0xb9}, // ¹ Vim 5.x compatible + {'-', 'o', 0xba}, + {'>', '>', 0xbb}, + {'1', '4', 0xbc}, + {'1', '2', 0xbd}, + {'3', '4', 0xbe}, + {'?', 'I', 0xbf}, + {'~', '?', 0xbf}, // ¿ Vim 5.x compatible + {'A', '!', 0xc0}, + {'A', '`', 0xc0}, // À Vim 5.x compatible + {'A', '\'', 0xc1}, + {'A', '>', 0xc2}, + {'A', '^', 0xc2}, //  Vim 5.x compatible + {'A', '?', 0xc3}, + {'A', '~', 0xc3}, // à Vim 5.x compatible + {'A', ':', 0xc4}, + {'A', '"', 0xc4}, // Ä Vim 5.x compatible + {'A', 'A', 0xc5}, + {'A', '@', 0xc5}, // Å Vim 5.x compatible + {'A', 'E', 0xc6}, + {'C', ',', 0xc7}, + {'E', '!', 0xc8}, + {'E', '`', 0xc8}, // È Vim 5.x compatible + {'E', '\'', 0xc9}, + {'E', '>', 0xca}, + {'E', '^', 0xca}, // Ê Vim 5.x compatible + {'E', ':', 0xcb}, + {'E', '"', 0xcb}, // Ë Vim 5.x compatible + {'I', '!', 0xcc}, + {'I', '`', 0xcc}, // Ì Vim 5.x compatible + {'I', '\'', 0xcd}, + {'I', '>', 0xce}, + {'I', '^', 0xce}, // Î Vim 5.x compatible + {'I', ':', 0xcf}, + {'I', '"', 0xcf}, // Ï Vim 5.x compatible + {'D', '-', 0xd0}, + {'N', '?', 0xd1}, + {'N', '~', 0xd1}, // Ñ Vim 5.x compatible + {'O', '!', 0xd2}, + {'O', '`', 0xd2}, // Ò Vim 5.x compatible + {'O', '\'', 0xd3}, + {'O', '>', 0xd4}, + {'O', '^', 0xd4}, // Ô Vim 5.x compatible + {'O', '?', 0xd5}, + {'O', '~', 0xd5}, // Õ Vim 5.x compatible + {'O', ':', 0xd6}, + {'*', 'X', 0xd7}, + {'/', '\\', 0xd7}, // × Vim 5.x compatible + {'O', '/', 0xd8}, + {'U', '!', 0xd9}, + {'U', '`', 0xd9}, // Ù Vim 5.x compatible + {'U', '\'', 0xda}, + {'U', '>', 0xdb}, + {'U', '^', 0xdb}, // Û Vim 5.x compatible + {'U', ':', 0xdc}, + {'Y', '\'', 0xdd}, + {'T', 'H', 0xde}, + {'I', 'p', 0xde}, // Þ Vim 5.x compatible + {'s', 's', 0xdf}, + {'a', '!', 0xe0}, + {'a', '`', 0xe0}, // à Vim 5.x compatible + {'a', '\'', 0xe1}, + {'a', '>', 0xe2}, + {'a', '^', 0xe2}, // â Vim 5.x compatible + {'a', '?', 0xe3}, + {'a', '~', 0xe3}, // ã Vim 5.x compatible + {'a', ':', 0xe4}, + {'a', '"', 0xe4}, // ä Vim 5.x compatible + {'a', 'a', 0xe5}, + {'a', '@', 0xe5}, // å Vim 5.x compatible + {'a', 'e', 0xe6}, + {'c', ',', 0xe7}, + {'e', '!', 0xe8}, + {'e', '`', 0xe8}, // è Vim 5.x compatible + {'e', '\'', 0xe9}, + {'e', '>', 0xea}, + {'e', '^', 0xea}, // ê Vim 5.x compatible + {'e', ':', 0xeb}, + {'e', '"', 0xeb}, // ë Vim 5.x compatible + {'i', '!', 0xec}, + {'i', '`', 0xec}, // ì Vim 5.x compatible + {'i', '\'', 0xed}, + {'i', '>', 0xee}, + {'i', '^', 0xee}, // î Vim 5.x compatible + {'i', ':', 0xef}, + {'d', '-', 0xf0}, + {'n', '?', 0xf1}, + {'n', '~', 0xf1}, // ñ Vim 5.x compatible + {'o', '!', 0xf2}, + {'o', '`', 0xf2}, // ò Vim 5.x compatible + {'o', '\'', 0xf3}, + {'o', '>', 0xf4}, + {'o', '^', 0xf4}, // ô Vim 5.x compatible + {'o', '?', 0xf5}, + {'o', '~', 0xf5}, // õ Vim 5.x compatible + {'o', ':', 0xf6}, + {'-', ':', 0xf7}, + {'o', '/', 0xf8}, + {'u', '!', 0xf9}, + {'u', '`', 0xf9}, // ù Vim 5.x compatible + {'u', '\'', 0xfa}, + {'u', '>', 0xfb}, + {'u', '^', 0xfb}, // û Vim 5.x compatible + {'u', ':', 0xfc}, + {'y', '\'', 0xfd}, + {'t', 'h', 0xfe}, + {'y', ':', 0xff}, + {'y', '"', 0xff}, // x XX Vim 5.x compatible + +# define USE_UNICODE_DIGRAPHS + + {'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}, +#define DG_START_GREEK 0x0386 + {'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}, +#define DG_START_CYRILLIC 0x0401 + {'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}, +#define DG_START_HEBREW 0x05d0 + {'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}, +#define DG_START_ARABIC 0x060c + {',', '+', 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}, +#define DG_START_LATIN_EXTENDED 0x1e02 + {'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', '`', 0x1e80}, // extra alternative, easier to remember + {'w', '!', 0x1e81}, + {'w', '`', 0x1e81}, // extra alternative, easier to remember + {'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', '`', 0x1ef2}, // extra alternative, easier to remember + {'y', '!', 0x1ef3}, + {'y', '`', 0x1ef3}, // extra alternative, easier to remember + {'Y', '2', 0x1ef6}, + {'y', '2', 0x1ef7}, + {'Y', '?', 0x1ef8}, + {'y', '?', 0x1ef9}, +#define DG_START_GREEK_EXTENDED 0x1f00 + {';', '\'', 0x1f00}, + {',', '\'', 0x1f01}, + {';', '!', 0x1f02}, + {',', '!', 0x1f03}, + {'?', ';', 0x1f04}, + {'?', ',', 0x1f05}, + {'!', ':', 0x1f06}, + {'?', ':', 0x1f07}, +#define DG_START_PUNCTUATION 0x2002 + {'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}, + {',', '.', 0x2026}, + {'%', '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}, +#define DG_START_SUB_SUPER 0x2070 + {'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}, +#define DG_START_CURRENCY 0x20a4 + {'L', 'i', 0x20a4}, + {'P', 't', 0x20a7}, + {'W', '=', 0x20a9}, + {'=', 'e', 0x20ac}, /* euro */ + {'E', 'u', 0x20ac}, /* euro */ + {'=', 'R', 0x20bd}, /* rouble */ + {'=', 'P', 0x20bd}, /* rouble */ +#define DG_START_OTHER1 0x2103 + {'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}, +#define DG_START_ROMAN 0x2160 + {'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}, +#define DG_START_ARROWS 0x2190 + {'<', '-', 0x2190}, + {'-', '!', 0x2191}, + {'-', '>', 0x2192}, + {'-', 'v', 0x2193}, + {'<', '>', 0x2194}, + {'U', 'D', 0x2195}, + {'<', '=', 0x21d0}, + {'=', '>', 0x21d2}, + {'=', '=', 0x21d4}, +#define DG_START_MATH 0x2200 + {'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}, +#define DG_START_TECHNICAL 0x2302 + {'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}, +#define DG_START_OTHER2 0x2423 + {'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}, +#define DG_START_DRAWING 0x2500 + {'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}, +#define DG_START_BLOCK 0x2580 + {'T', 'B', 0x2580}, + {'L', 'B', 0x2584}, + {'F', 'B', 0x2588}, + {'l', 'B', 0x258c}, + {'R', 'B', 0x2590}, + {'.', 'S', 0x2591}, + {':', 'S', 0x2592}, + {'?', 'S', 0x2593}, +#define DG_START_SHAPES 0x25a0 + {'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}, +#define DG_START_SYMBOLS 0x2605 + {'*', '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}, +#define DG_START_DINGBATS 0x2713 + {'O', 'K', 0x2713}, + {'X', 'X', 0x2717}, + {'-', 'X', 0x2720}, +#define DG_START_CJK_SYMBOLS 0x3000 + {'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}, +#define DG_START_HIRAGANA 0x3041 + {'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}, +#define DG_START_KATAKANA 0x30a1 + {'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}, +#define DG_START_BOPOMOFO 0x3105 + {'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}, +#define DG_START_OTHER3 0x3220 + {'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}, + + {NUL, NUL, NUL} + }; + +# endif /* OLD_DIGRAPHS */ +# endif /* EBCDIC */ +# endif /* !HPUX_DIGRAPHS */ +#endif /* !__MINT__ */ + +/* + * handle digraphs after typing a character + */ + 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; +} + +/* + * Find a digraph for "val". If found return the string to display it. + * If not found return NULL. + */ + char_u * +get_digraph_for_char(int val_arg) +{ + int val = val_arg; + int i; + int use_defaults; + digr_T *dp; + static char_u r[3]; + +#if defined(USE_UNICODE_DIGRAPHS) + if (!enc_utf8) + { + char_u buf[6], *to; + vimconv_T vc; + + // convert the character from 'encoding' to Unicode + i = mb_char2bytes(val, buf); + vc.vc_type = CONV_NONE; + if (convert_setup(&vc, p_enc, (char_u *)"utf-8") == OK) + { + vc.vc_fail = TRUE; + to = string_convert(&vc, buf, &i); + if (to != NULL) + { + val = utf_ptr2char(to); + vim_free(to); + } + (void)convert_setup(&vc, NULL, NULL); + } + } +#endif + + for (use_defaults = 0; use_defaults <= 1; use_defaults++) + { + if (use_defaults == 0) + dp = (digr_T *)user_digraphs.ga_data; + else + dp = digraphdefault; + for (i = 0; use_defaults ? dp->char1 != NUL + : i < user_digraphs.ga_len; ++i) + { + if (dp->result == val) + { + r[0] = dp->char1; + r[1] = dp->char2; + r[2] = NUL; + return r; + } + ++dp; + } + } + return NULL; +} + +/* + * Get a digraph. Used after typing CTRL-K on the command line or in normal + * mode. + * Returns composed character, or NUL when ESC was used. + */ + int +get_digraph( + int cmdline) /* TRUE when called from the cmdline */ +{ + int c, cc; + + ++no_mapping; + ++allow_keys; + 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 +#if defined(FEAT_CRYPT) || defined(FEAT_EVAL) + && cmdline_star == 0 +#endif + ) + putcmdline(c, TRUE); + } +#ifdef FEAT_CMDL_INFO + else + add_to_showcmd(c); +#endif + ++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. + * 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 i; + int retval = 0; + digr_T *dp; + + if (IS_SPECIAL(char1) || IS_SPECIAL(char2)) + return char2; + + /* + * Search user digraphs first. + */ + 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) + { + 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; + } + } +#ifdef USE_UNICODE_DIGRAPHS + 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); + vim_free(to); + } + (void)convert_setup(&vc, NULL, NULL); + } + } +#endif + + /* 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 + */ + 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 ... + */ + 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) + { + if (ga_grow(&user_digraphs, 1) == OK) + { + dp = (digr_T *)user_digraphs.ga_data + user_digraphs.ga_len; + dp->char1 = char1; + dp->char2 = char2; + dp->result = n; + ++user_digraphs.ga_len; + } + } + } +} + +#if defined(USE_UNICODE_DIGRAPHS) + static void +digraph_header(char *msg) +{ + if (msg_col > 0) + msg_putchar('\n'); + msg_outtrans_attr((char_u *)msg, HL_ATTR(HLF_CM)); + msg_putchar('\n'); +} +#endif + + void +listdigraphs(int use_headers) +{ + int i; + digr_T *dp; + result_T previous = 0; + + msg_putchar('\n'); + + dp = digraphdefault; + for (i = 0; dp->char1 != NUL && !got_int; ++i) + { +#if defined(USE_UNICODE_DIGRAPHS) + 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, use_headers ? &previous : NULL); +#else + + if (getexactdigraph(dp->char1, dp->char2, FALSE) == dp->result + && (has_mbyte || dp->result <= 255)) + printdigraph(dp, use_headers ? &previous : NULL); +#endif + ++dp; + ui_breakcheck(); + } + + dp = (digr_T *)user_digraphs.ga_data; + for (i = 0; i < user_digraphs.ga_len && !got_int; ++i) + { +#if defined(USE_UNICODE_DIGRAPHS) + if (previous >= 0 && use_headers) + digraph_header(_("Custom")); + previous = -1; +#endif + printdigraph(dp, NULL); + ui_breakcheck(); + ++dp; + } + must_redraw = CLEAR; /* clear screen, because some digraphs may be + wrong, in which case we messed up ScreenLines */ +} + +struct dg_header_entry { + int dg_start; + char *dg_header; +} header_table[] = { + {DG_START_LATIN, N_("Latin supplement")}, + {DG_START_GREEK, N_("Greek and Coptic")}, + {DG_START_CYRILLIC, N_("Cyrillic")}, + {DG_START_HEBREW, N_("Hebrew")}, + {DG_START_ARABIC, N_("Arabic")}, + {DG_START_LATIN_EXTENDED, N_("Latin extended")}, + {DG_START_GREEK_EXTENDED, N_("Greek extended")}, + {DG_START_PUNCTUATION, N_("Punctuation")}, + {DG_START_SUB_SUPER, N_("Super- and subscripts")}, + {DG_START_CURRENCY, N_("Currency")}, + {DG_START_OTHER1, N_("Other")}, + {DG_START_ROMAN, N_("Roman numbers")}, + {DG_START_ARROWS, N_("Arrows")}, + {DG_START_MATH, N_("Mathematical operators")}, + {DG_START_TECHNICAL, N_("Technical")}, + {DG_START_OTHER2, N_("Other")}, + {DG_START_DRAWING, N_("Box drawing")}, + {DG_START_BLOCK, N_("Block elements")}, + {DG_START_SHAPES, N_("Geometric shapes")}, + {DG_START_SYMBOLS, N_("Symbols")}, + {DG_START_DINGBATS, N_("Dingbats")}, + {DG_START_CJK_SYMBOLS, N_("CJK symbols and punctuation")}, + {DG_START_HIRAGANA, N_("Hiragana")}, + {DG_START_KATAKANA, N_("Katakana")}, + {DG_START_BOPOMOFO, N_("Bopomofo")}, + {DG_START_OTHER3, N_("Other")}, + {0xfffffff, NULL}, +}; + + static void +printdigraph(digr_T *dp, result_T *previous) +{ + 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 defined(USE_UNICODE_DIGRAPHS) + if (previous != NULL) + { + int i; + + for (i = 0; header_table[i].dg_header != NULL; ++i) + if (*previous < header_table[i].dg_start + && dp->result >= header_table[i].dg_start + && dp->result < header_table[i + 1].dg_start) + { + digraph_header(_(header_table[i].dg_header)); + break; + } + *previous = dp->result; + } +#endif + 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++ = ' '; + *p = NUL; + msg_outtrans(buf); + p = buf; + 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; + *p = NUL; + msg_outtrans_attr(buf, HL_ATTR(HLF_8)); + p = buf; + if (char2cells(dp->result) == 1) + *p++ = ' '; + vim_snprintf((char *)p, sizeof(buf) - (p - buf), " %3d", dp->result); + msg_outtrans(buf); + } +} + +#endif /* FEAT_DIGRAPHS */ + +#if defined(FEAT_KEYMAP) || defined(PROTO) + +/* 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. + * Returns 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 * +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, 0) == FAIL) + { + /* try finding "keymap/'keymap'.vim" in 'runtimepath' */ + vim_snprintf((char *)buf, buflen, "keymap/%s.vim", + curbuf->b_p_keymap); + if (source_runtime(buf, 0) == FAIL) + { + vim_free(buf); + return N_("E544: Keymap file not found"); + } + } + vim_free(buf); + } + + return NULL; +} + +/* + * ":loadkeymap" command: load the following lines as the keymap. + */ + 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_init2(&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) == OK) + { + 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")); + vim_free(kp->from); + vim_free(kp->to); + } + else + ++curbuf->b_kmap_ga.ga_len; + } + vim_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); + } + keymap_clear(&curbuf->b_kmap_ga); + + p_cpo = save_cpo; + + ga_clear(&curbuf->b_kmap_ga); + curbuf->b_kmap_state &= ~KEYMAP_LOADED; + status_redraw_curbuf(); +} + + void +keymap_clear(garray_T *kmap) +{ + int i; + kmap_T *kp = (kmap_T *)kmap->ga_data; + + for (i = 0; i < kmap->ga_len; ++i) + { + vim_free(kp[i].from); + vim_free(kp[i].to); + } +} +#endif /* FEAT_KEYMAP */ diff --git a/src/dimm.idl b/src/dimm.idl new file mode 100644 index 0000000..ac44965 --- /dev/null +++ b/src/dimm.idl @@ -0,0 +1,544 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2000. +// +// File: dimm.idl +// +// Contents: ActiveIMM interface definitions +// +// +//-------------------------------------------------------------------------- + +cpp_quote("//=--------------------------------------------------------------------------=") +cpp_quote("// dimm.h") +cpp_quote("//=--------------------------------------------------------------------------=") +cpp_quote("// (C) Copyright 1995-1998 Microsoft Corporation. All Rights Reserved.") +cpp_quote("//") +cpp_quote("// THIS CODE AND INFORMATION IS PROVIDED \"AS IS\" WITHOUT WARRANTY OF") +cpp_quote("// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO") +cpp_quote("// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A") +cpp_quote("// PARTICULAR PURPOSE.") +cpp_quote("//=--------------------------------------------------------------------------=") +cpp_quote("") +cpp_quote("#pragma comment(lib,\"uuid.lib\")") +cpp_quote("") +cpp_quote("//--------------------------------------------------------------------------") +cpp_quote("// IActiveIMM Interfaces.") +cpp_quote("") + +/* +Disable a warning about lack of polymorphic type support for the following reasons +- the only reason to have library block in the file is to make midl accept coclass + statement and generate CLSID for CActiveIMM. +- the generated dimm_i.c has the clsid and that file is used to have clsid available +- the dimm.tlb is not used at all +- on top of it, there is no plans to port the app using dimm.idl to 64b platform. +*/ +/* + * midl_pragma is unsupported in midl version 3.01 shipped with VC5.0. + * It is supported in midl version 5.01 shipped with VC6.0 + * I could not produce message 2395. Is this needed? W.Briscoe 2001-08-14 + */ +#if (__midl >= 501) +midl_pragma warning( disable: 2395) // polymorphic types not supported in the TLB +#endif + +#ifndef DO_NO_IMPORTS +import "unknwn.idl"; +#endif + +[ + uuid(4955DD30-B159-11d0-8FCF-00AA006BCC59), + helpstring("ActiveIMM"), + lcid(0x0000), + version(0.1) +] +library ActiveIMM +{ + importlib("stdole2.tlb"); + + cpp_quote("#include ") + + cpp_quote("#if 0") + + typedef WORD LANGID; + + typedef struct + { + LPSTR lpReading; + LPSTR lpWord; + } REGISTERWORDA; + + typedef struct + { + LPWSTR lpReading; + LPWSTR lpWord; + } REGISTERWORDW; + + #define LF_FACESIZE 32 + + typedef struct + { + LONG lfHeight; + LONG lfWidth; + LONG lfEscapement; + LONG lfOrientation; + LONG lfWeight; + BYTE lfItalic; + BYTE lfUnderline; + BYTE lfStrikeOut; + BYTE lfCharSet; + BYTE lfOutPrecision; + BYTE lfClipPrecision; + BYTE lfQuality; + BYTE lfPitchAndFamily; + CHAR lfFaceName[LF_FACESIZE]; + } LOGFONTA; + + typedef struct + { + LONG lfHeight; + LONG lfWidth; + LONG lfEscapement; + LONG lfOrientation; + LONG lfWeight; + BYTE lfItalic; + BYTE lfUnderline; + BYTE lfStrikeOut; + BYTE lfCharSet; + BYTE lfOutPrecision; + BYTE lfClipPrecision; + BYTE lfQuality; + BYTE lfPitchAndFamily; + WCHAR lfFaceName[LF_FACESIZE]; + } LOGFONTW; + + typedef DWORD HIMC; + typedef DWORD HIMCC; + + typedef struct + { + DWORD dwIndex; + DWORD dwStyle; + POINT ptCurrentPos; + RECT rcArea; + } CANDIDATEFORM; + + typedef struct + { + DWORD dwStyle; + POINT ptCurrentPos; + RECT rcArea; + } COMPOSITIONFORM; + + typedef struct + { + DWORD dwSize; + DWORD dwStyle; + DWORD dwCount; + DWORD dwSelection; + DWORD dwPageStart; + DWORD dwPageSize; + DWORD dwOffset[1]; + } CANDIDATELIST; + + #define STYLE_DESCRIPTION_SIZE 32 + + typedef struct + { + DWORD dwStyle; + CHAR szDescription[STYLE_DESCRIPTION_SIZE]; + } STYLEBUFA; + + typedef struct + { + DWORD dwStyle; + WCHAR szDescription[STYLE_DESCRIPTION_SIZE]; + } STYLEBUFW; + + typedef WORD ATOM; + + cpp_quote("#endif") + + cpp_quote("#if (WINVER < 0x040A)") + + #define IMEMENUITEM_STRING_SIZE 80 + + typedef struct + { + UINT cbSize; + UINT fType; + UINT fState; + UINT wID; + HBITMAP hbmpChecked; + HBITMAP hbmpUnchecked; + DWORD dwItemData; + CHAR szString[IMEMENUITEM_STRING_SIZE]; + HBITMAP hbmpItem; + } IMEMENUITEMINFOA; + + typedef struct + { + UINT cbSize; + UINT fType; + UINT fState; + UINT wID; + HBITMAP hbmpChecked; + HBITMAP hbmpUnchecked; + DWORD dwItemData; + WCHAR szString[IMEMENUITEM_STRING_SIZE]; + HBITMAP hbmpItem; + } IMEMENUITEMINFOW; + + cpp_quote("#endif") + + cpp_quote("#ifndef _DDKIMM_H_") + + typedef struct + { + HWND hWnd; + BOOL fOpen; + POINT ptStatusWndPos; + POINT ptSoftKbdPos; + DWORD fdwConversion; + DWORD fdwSentence; + union + { + LOGFONTA A; + LOGFONTW W; + } lfFont; + COMPOSITIONFORM cfCompForm; + CANDIDATEFORM cfCandForm[4]; + HIMCC hCompStr; + HIMCC hCandInfo; + HIMCC hGuideLine; + HIMCC hPrivate; + DWORD dwNumMsgBuf; + HIMCC hMsgBuf; + DWORD fdwInit; + DWORD dwReserve[3]; + } INPUTCONTEXT; + + typedef struct + { + DWORD dwPrivateDataSize; + DWORD fdwProperty; + DWORD fdwConversionCaps; + DWORD fdwSentenceCaps; + DWORD fdwUICaps; + DWORD fdwSCSCaps; + DWORD fdwSelectCaps; + } IMEINFO; + + cpp_quote("#endif") + + [ + object, + uuid(08C03412-F96B-11d0-A475-00AA006BCC59), + pointer_default(unique) + ] + interface IEnumRegisterWordA : IUnknown + { + HRESULT Clone([out] IEnumRegisterWordA **ppEnum); + HRESULT Next([in] ULONG ulCount, [out] REGISTERWORDA *rgRegisterWord, [out] ULONG *pcFetched); + HRESULT Reset(); + HRESULT Skip([in] ULONG ulCount); + }; + + [ + object, + uuid(4955DD31-B159-11d0-8FCF-00AA006BCC59), + pointer_default(unique) + ] + interface IEnumRegisterWordW : IUnknown + { + HRESULT Clone([out] IEnumRegisterWordW **ppEnum); + HRESULT Next([in] ULONG ulCount, [out] REGISTERWORDW *rgRegisterWord, [out] ULONG *pcFetched); + HRESULT Reset(); + HRESULT Skip([in] ULONG ulCount); + }; + + + [ + object, + uuid(09b5eab0-f997-11d1-93d4-0060b067b86e), + pointer_default(unique) + ] + interface IEnumInputContext : IUnknown + { + HRESULT Clone([out] IEnumInputContext **ppEnum); + HRESULT Next([in] ULONG ulCount, [out] HIMC *rgInputContext, [out] ULONG *pcFetched); + HRESULT Reset(); + HRESULT Skip([in] ULONG ulCount); + }; + + + [ + object, + uuid(b3458082-bd00-11d1-939b-0060b067b86e), + pointer_default(unique) + ] + interface IActiveIMMRegistrar : IUnknown + { + HRESULT RegisterIME([in] REFCLSID rclsid, [in] LANGID lgid, [in] LPCWSTR pszIconFile, [in] LPCWSTR pszDesc); + HRESULT UnregisterIME([in] REFCLSID rclsid); + }; + + [ + object, + uuid(b5cf2cfa-8aeb-11d1-9364-0060b067b86e), + pointer_default(unique) + ] + interface IActiveIMMMessagePumpOwner : IUnknown + { + HRESULT Start(); + HRESULT End(); + HRESULT OnTranslateMessage([in] const MSG *pMsg); + HRESULT Pause([out] DWORD *pdwCookie); + HRESULT Resume([in] DWORD dwCookie); + } + + [ + object, + uuid(08c0e040-62d1-11d1-9326-0060b067b86e), + pointer_default(unique) + ] + interface IActiveIMMApp : IUnknown + { + HRESULT AssociateContext([in] HWND hWnd, [in] HIMC hIME, [out] HIMC *phPrev); + HRESULT ConfigureIMEA([in] HKL hKL, [in] HWND hWnd, [in] DWORD dwMode, [in] REGISTERWORDA *pData); + HRESULT ConfigureIMEW([in] HKL hKL, [in] HWND hWnd, [in] DWORD dwMode, [in] REGISTERWORDW *pData); + HRESULT CreateContext([out] HIMC *phIMC); + HRESULT DestroyContext([in] HIMC hIME); + HRESULT EnumRegisterWordA([in] HKL hKL, [in] LPSTR szReading, [in] DWORD dwStyle, [in] LPSTR szRegister, [in] LPVOID pData, [out] IEnumRegisterWordA **pEnum); + HRESULT EnumRegisterWordW([in] HKL hKL, [in] LPWSTR szReading, [in] DWORD dwStyle, [in] LPWSTR szRegister, [in] LPVOID pData, [out] IEnumRegisterWordW **pEnum); + HRESULT EscapeA([in] HKL hKL, [in] HIMC hIMC, [in] UINT uEscape, [in, out] LPVOID pData, [out] LRESULT *plResult); + HRESULT EscapeW([in] HKL hKL, [in] HIMC hIMC, [in] UINT uEscape, [in, out] LPVOID pData, [out] LRESULT *plResult); + HRESULT GetCandidateListA([in] HIMC hIMC, [in] DWORD dwIndex, [in] UINT uBufLen, [out] CANDIDATELIST *pCandList, [out] UINT *puCopied); + HRESULT GetCandidateListW([in] HIMC hIMC, [in] DWORD dwIndex, [in] UINT uBufLen, [out] CANDIDATELIST *pCandList, [out] UINT *puCopied); + HRESULT GetCandidateListCountA([in] HIMC hIMC, [out] DWORD *pdwListSize, [out] DWORD *pdwBufLen); + HRESULT GetCandidateListCountW([in] HIMC hIMC, [out] DWORD *pdwListSize, [out] DWORD *pdwBufLen); + HRESULT GetCandidateWindow([in] HIMC hIMC, [in] DWORD dwIndex, [out] CANDIDATEFORM *pCandidate); + HRESULT GetCompositionFontA([in] HIMC hIMC, [out] LOGFONTA *plf); + HRESULT GetCompositionFontW([in] HIMC hIMC, [out] LOGFONTW *plf); + HRESULT GetCompositionStringA([in] HIMC hIMC, [in] DWORD dwIndex, [in] DWORD dwBufLen, [out] LONG *plCopied, [out] LPVOID pBuf); + HRESULT GetCompositionStringW([in] HIMC hIMC, [in] DWORD dwIndex, [in] DWORD dwBufLen, [out] LONG *plCopied, [out] LPVOID pBuf); + HRESULT GetCompositionWindow([in] HIMC hIMC, [out] COMPOSITIONFORM *pCompForm); + HRESULT GetContext([in] HWND hWnd, [out] HIMC *phIMC); + HRESULT GetConversionListA([in] HKL hKL, [in] HIMC hIMC, [in] LPSTR pSrc, [in] UINT uBufLen, [in] UINT uFlag, [out] CANDIDATELIST *pDst, [out] UINT *puCopied); + HRESULT GetConversionListW([in] HKL hKL, [in] HIMC hIMC, [in] LPWSTR pSrc, [in] UINT uBufLen, [in] UINT uFlag, [out] CANDIDATELIST *pDst, [out] UINT *puCopied); + HRESULT GetConversionStatus([in] HIMC hIMC, [out] DWORD *pfdwConversion, [out] DWORD *pfdwSentence); + HRESULT GetDefaultIMEWnd([in] HWND hWnd, [out] HWND *phDefWnd); + HRESULT GetDescriptionA([in] HKL hKL, [in] UINT uBufLen, [out] LPSTR szDescription, [out] UINT *puCopied); + HRESULT GetDescriptionW([in] HKL hKL, [in] UINT uBufLen, [out] LPWSTR szDescription, [out] UINT *puCopied); + HRESULT GetGuideLineA([in] HIMC hIMC, [in] DWORD dwIndex, [in] DWORD dwBufLen, [out] LPSTR pBuf, [out] DWORD *pdwResult); + HRESULT GetGuideLineW([in] HIMC hIMC, [in] DWORD dwIndex, [in] DWORD dwBufLen, [out] LPWSTR pBuf, [out] DWORD *pdwResult); + HRESULT GetIMEFileNameA([in] HKL hKL, [in] UINT uBufLen, [out] LPSTR szFileName, [out] UINT *puCopied); + HRESULT GetIMEFileNameW([in] HKL hKL, [in] UINT uBufLen, [out] LPWSTR szFileName, [out] UINT *puCopied); + HRESULT GetOpenStatus([in] HIMC hIMC); + HRESULT GetProperty([in] HKL hKL, [in] DWORD fdwIndex, [out] DWORD *pdwProperty); + HRESULT GetRegisterWordStyleA([in] HKL hKL, [in] UINT nItem, [out] STYLEBUFA *pStyleBuf, [out] UINT *puCopied); + HRESULT GetRegisterWordStyleW([in] HKL hKL, [in] UINT nItem, [out] STYLEBUFW *pStyleBuf, [out] UINT *puCopied); + HRESULT GetStatusWindowPos([in] HIMC hIMC, [out] POINT *pptPos); + HRESULT GetVirtualKey([in] HWND hWnd, [out] UINT *puVirtualKey); + HRESULT InstallIMEA([in] LPSTR szIMEFileName, [in] LPSTR szLayoutText, [out] HKL *phKL); + HRESULT InstallIMEW([in] LPWSTR szIMEFileName, [in] LPWSTR szLayoutText, [out] HKL *phKL); + HRESULT IsIME([in] HKL hKL); + HRESULT IsUIMessageA([in] HWND hWndIME, [in] UINT msg, [in] WPARAM wParam, [in] LPARAM lParam); + HRESULT IsUIMessageW([in] HWND hWndIME, [in] UINT msg, [in] WPARAM wParam, [in] LPARAM lParam); + HRESULT NotifyIME([in] HIMC hIMC, [in] DWORD dwAction, [in] DWORD dwIndex, [in] DWORD dwValue); + HRESULT RegisterWordA([in] HKL hKL, [in] LPSTR szReading, [in] DWORD dwStyle, [in] LPSTR szRegister); + HRESULT RegisterWordW([in] HKL hKL, [in] LPWSTR szReading, [in] DWORD dwStyle, [in] LPWSTR szRegister); + HRESULT ReleaseContext([in] HWND hWnd, [in] HIMC hIMC); + HRESULT SetCandidateWindow([in] HIMC hIMC, [in] CANDIDATEFORM *pCandidate); + HRESULT SetCompositionFontA([in] HIMC hIMC, [in] LOGFONTA *plf); + HRESULT SetCompositionFontW([in] HIMC hIMC, [in] LOGFONTW *plf); + HRESULT SetCompositionStringA([in] HIMC hIMC, [in] DWORD dwIndex, [in] LPVOID pComp, [in] DWORD dwCompLen, [in] LPVOID pRead, [in] DWORD dwReadLen); + HRESULT SetCompositionStringW([in] HIMC hIMC, [in] DWORD dwIndex, [in] LPVOID pComp, [in] DWORD dwCompLen, [in] LPVOID pRead, [in] DWORD dwReadLen); + HRESULT SetCompositionWindow([in] HIMC hIMC, [in] COMPOSITIONFORM *pCompForm); + HRESULT SetConversionStatus([in] HIMC hIMC, [in] DWORD fdwConversion, [in] DWORD fdwSentence); + HRESULT SetOpenStatus([in] HIMC hIMC, [in] BOOL fOpen); + HRESULT SetStatusWindowPos([in] HIMC hIMC, [in] POINT *pptPos); + HRESULT SimulateHotKey([in] HWND hWnd, [in] DWORD dwHotKeyID); + HRESULT UnregisterWordA([in] HKL hKL, [in] LPSTR szReading, [in] DWORD dwStyle, [in] LPSTR szUnregister); + HRESULT UnregisterWordW([in] HKL hKL, [in] LPWSTR szReading, [in] DWORD dwStyle, [in] LPWSTR szUnregister); + + HRESULT Activate([in] BOOL fRestoreLayout); + HRESULT Deactivate(); + + HRESULT OnDefWindowProc([in] HWND hWnd, [in] UINT Msg, [in] WPARAM wParam, [in] LPARAM lParam, [out] LRESULT *plResult); + + HRESULT FilterClientWindows([in] ATOM *aaClassList, [in] UINT uSize); + + HRESULT GetCodePageA([in] HKL hKL, [out] UINT *uCodePage); + HRESULT GetLangId([in] HKL hKL, [out] LANGID *plid); + + // win98/nt5 apis + HRESULT AssociateContextEx([in] HWND hWnd, [in] HIMC hIMC, [in] DWORD dwFlags); + HRESULT DisableIME([in] DWORD idThread); + HRESULT GetImeMenuItemsA([in] HIMC hIMC, [in] DWORD dwFlags, [in] DWORD dwType, [in] IMEMENUITEMINFOA *pImeParentMenu, [out] IMEMENUITEMINFOA *pImeMenu, [in] DWORD dwSize, [out] DWORD *pdwResult); + HRESULT GetImeMenuItemsW([in] HIMC hIMC, [in] DWORD dwFlags, [in] DWORD dwType, [in] IMEMENUITEMINFOW *pImeParentMenu, [out] IMEMENUITEMINFOW *pImeMenu, [in] DWORD dwSize, [out] DWORD *pdwResult); + HRESULT EnumInputContext([in] DWORD idThread, [out] IEnumInputContext **ppEnum); + }; + + [ + object, + uuid(08C03411-F96B-11d0-A475-00AA006BCC59), + pointer_default(unique) + ] + interface IActiveIMMIME : IUnknown + { + HRESULT AssociateContext([in] HWND hWnd, [in] HIMC hIME, [out] HIMC *phPrev); + HRESULT ConfigureIMEA([in] HKL hKL, [in] HWND hWnd, [in] DWORD dwMode, [in] REGISTERWORDA *pData); + HRESULT ConfigureIMEW([in] HKL hKL, [in] HWND hWnd, [in] DWORD dwMode, [in] REGISTERWORDW *pData); + HRESULT CreateContext([out] HIMC *phIMC); + HRESULT DestroyContext([in] HIMC hIME); + HRESULT EnumRegisterWordA([in] HKL hKL, [in] LPSTR szReading, [in] DWORD dwStyle, [in] LPSTR szRegister, [in] LPVOID pData, [out] IEnumRegisterWordA **pEnum); + HRESULT EnumRegisterWordW([in] HKL hKL, [in] LPWSTR szReading, [in] DWORD dwStyle, [in] LPWSTR szRegister, [in] LPVOID pData, [out] IEnumRegisterWordW **pEnum); + HRESULT EscapeA([in] HKL hKL, [in] HIMC hIMC, [in] UINT uEscape, [in, out] LPVOID pData, [out] LRESULT *plResult); + HRESULT EscapeW([in] HKL hKL, [in] HIMC hIMC, [in] UINT uEscape, [in, out] LPVOID pData, [out] LRESULT *plResult); + HRESULT GetCandidateListA([in] HIMC hIMC, [in] DWORD dwIndex, [in] UINT uBufLen, [out] CANDIDATELIST *pCandList, [out] UINT *puCopied); + HRESULT GetCandidateListW([in] HIMC hIMC, [in] DWORD dwIndex, [in] UINT uBufLen, [out] CANDIDATELIST *pCandList, [out] UINT *puCopied); + HRESULT GetCandidateListCountA([in] HIMC hIMC, [out] DWORD *pdwListSize, [out] DWORD *pdwBufLen); + HRESULT GetCandidateListCountW([in] HIMC hIMC, [out] DWORD *pdwListSize, [out] DWORD *pdwBufLen); + HRESULT GetCandidateWindow([in] HIMC hIMC, [in] DWORD dwIndex, [out] CANDIDATEFORM *pCandidate); + HRESULT GetCompositionFontA([in] HIMC hIMC, [out] LOGFONTA *plf); + HRESULT GetCompositionFontW([in] HIMC hIMC, [out] LOGFONTW *plf); + HRESULT GetCompositionStringA([in] HIMC hIMC, [in] DWORD dwIndex, [in] DWORD dwBufLen, [out] LONG *plCopied, [out] LPVOID pBuf); + HRESULT GetCompositionStringW([in] HIMC hIMC, [in] DWORD dwIndex, [in] DWORD dwBufLen, [out] LONG *plCopied, [out] LPVOID pBuf); + HRESULT GetCompositionWindow([in] HIMC hIMC, [out] COMPOSITIONFORM *pCompForm); + HRESULT GetContext([in] HWND hWnd, [out] HIMC *phIMC); + HRESULT GetConversionListA([in] HKL hKL, [in] HIMC hIMC, [in] LPSTR pSrc, [in] UINT uBufLen, [in] UINT uFlag, [out] CANDIDATELIST *pDst, [out] UINT *puCopied); + HRESULT GetConversionListW([in] HKL hKL, [in] HIMC hIMC, [in] LPWSTR pSrc, [in] UINT uBufLen, [in] UINT uFlag, [out] CANDIDATELIST *pDst, [out] UINT *puCopied); + HRESULT GetConversionStatus([in] HIMC hIMC, [out] DWORD *pfdwConversion, [out] DWORD *pfdwSentence); + HRESULT GetDefaultIMEWnd([in] HWND hWnd, [out] HWND *phDefWnd); + HRESULT GetDescriptionA([in] HKL hKL, [in] UINT uBufLen, [out] LPSTR szDescription, [out] UINT *puCopied); + HRESULT GetDescriptionW([in] HKL hKL, [in] UINT uBufLen, [out] LPWSTR szDescription, [out] UINT *puCopied); + HRESULT GetGuideLineA([in] HIMC hIMC, [in] DWORD dwIndex, [in] DWORD dwBufLen, [out] LPSTR pBuf, [out] DWORD *pdwResult); + HRESULT GetGuideLineW([in] HIMC hIMC, [in] DWORD dwIndex, [in] DWORD dwBufLen, [out] LPWSTR pBuf, [out] DWORD *pdwResult); + HRESULT GetIMEFileNameA([in] HKL hKL, [in] UINT uBufLen, [out] LPSTR szFileName, [out] UINT *puCopied); + HRESULT GetIMEFileNameW([in] HKL hKL, [in] UINT uBufLen, [out] LPWSTR szFileName, [out] UINT *puCopied); + HRESULT GetOpenStatus([in] HIMC hIMC); + HRESULT GetProperty([in] HKL hKL, [in] DWORD fdwIndex, [out] DWORD *pdwProperty); + HRESULT GetRegisterWordStyleA([in] HKL hKL, [in] UINT nItem, [out] STYLEBUFA *pStyleBuf, [out] UINT *puCopied); + HRESULT GetRegisterWordStyleW([in] HKL hKL, [in] UINT nItem, [out] STYLEBUFW *pStyleBuf, [out] UINT *puCopied); + HRESULT GetStatusWindowPos([in] HIMC hIMC, [out] POINT *pptPos); + HRESULT GetVirtualKey([in] HWND hWnd, [out] UINT *puVirtualKey); + HRESULT InstallIMEA([in] LPSTR szIMEFileName, [in] LPSTR szLayoutText, [out] HKL *phKL); + HRESULT InstallIMEW([in] LPWSTR szIMEFileName, [in] LPWSTR szLayoutText, [out] HKL *phKL); + HRESULT IsIME([in] HKL hKL); + HRESULT IsUIMessageA([in] HWND hWndIME, [in] UINT msg, [in] WPARAM wParam, [in] LPARAM lParam); + HRESULT IsUIMessageW([in] HWND hWndIME, [in] UINT msg, [in] WPARAM wParam, [in] LPARAM lParam); + HRESULT NotifyIME([in] HIMC hIMC, [in] DWORD dwAction, [in] DWORD dwIndex, [in] DWORD dwValue); + HRESULT RegisterWordA([in] HKL hKL, [in] LPSTR szReading, [in] DWORD dwStyle, [in] LPSTR szRegister); + HRESULT RegisterWordW([in] HKL hKL, [in] LPWSTR szReading, [in] DWORD dwStyle, [in] LPWSTR szRegister); + HRESULT ReleaseContext([in] HWND hWnd, [in] HIMC hIMC); + HRESULT SetCandidateWindow([in] HIMC hIMC, [in] CANDIDATEFORM *pCandidate); + HRESULT SetCompositionFontA([in] HIMC hIMC, [in] LOGFONTA *plf); + HRESULT SetCompositionFontW([in] HIMC hIMC, [in] LOGFONTW *plf); + HRESULT SetCompositionStringA([in] HIMC hIMC, [in] DWORD dwIndex, [in] LPVOID pComp, [in] DWORD dwCompLen, [in] LPVOID pRead, [in] DWORD dwReadLen); + HRESULT SetCompositionStringW([in] HIMC hIMC, [in] DWORD dwIndex, [in] LPVOID pComp, [in] DWORD dwCompLen, [in] LPVOID pRead, [in] DWORD dwReadLen); + HRESULT SetCompositionWindow([in] HIMC hIMC, [in] COMPOSITIONFORM *pCompForm); + HRESULT SetConversionStatus([in] HIMC hIMC, [in] DWORD fdwConversion, [in] DWORD fdwSentence); + HRESULT SetOpenStatus([in] HIMC hIMC, [in] BOOL fOpen); + HRESULT SetStatusWindowPos([in] HIMC hIMC, [in] POINT *pptPos); + HRESULT SimulateHotKey([in] HWND hWnd, [in] DWORD dwHotKeyID); + HRESULT UnregisterWordA([in] HKL hKL, [in] LPSTR szReading, [in] DWORD dwStyle, [in] LPSTR szUnregister); + HRESULT UnregisterWordW([in] HKL hKL, [in] LPWSTR szReading, [in] DWORD dwStyle, [in] LPWSTR szUnregister); + + // ime helper methods + HRESULT GenerateMessage([in] HIMC hIMC); + + // HIMC and HIMCC management api's + HRESULT LockIMC([in] HIMC hIMC, [out] INPUTCONTEXT **ppIMC); + HRESULT UnlockIMC([in] HIMC hIMC); + HRESULT GetIMCLockCount([in] HIMC hIMC, [out] DWORD *pdwLockCount); + HRESULT CreateIMCC([in] DWORD dwSize, [out] HIMCC *phIMCC); + HRESULT DestroyIMCC([in] HIMCC hIMCC); + HRESULT LockIMCC([in] HIMCC hIMCC, [out] void **ppv); + HRESULT UnlockIMCC([in] HIMCC hIMCC); + HRESULT ReSizeIMCC([in] HIMCC hIMCC, [in] DWORD dwSize, [out] HIMCC *phIMCC); + HRESULT GetIMCCSize([in] HIMCC hIMCC, [out] DWORD *pdwSize); + HRESULT GetIMCCLockCount([in] HIMCC hIMCC, [out] DWORD *pdwLockCount); + + // hot key manipulation api's + HRESULT GetHotKey([in] DWORD dwHotKeyID, [out] UINT *puModifiers, [out] UINT *puVKey, [out] HKL *phKL); + HRESULT SetHotKey([in] DWORD dwHotKeyID, [in] UINT uModifiers, [in] UINT uVKey, [in] HKL hKL); + + // soft keyboard api's + HRESULT CreateSoftKeyboard([in] UINT uType, [in] HWND hOwner, [in] int x, [in] int y, [out] HWND *phSoftKbdWnd); + HRESULT DestroySoftKeyboard([in] HWND hSoftKbdWnd); + HRESULT ShowSoftKeyboard([in] HWND hSoftKbdWnd, [in] int nCmdShow); + + HRESULT GetCodePageA([in] HKL hKL, [out] UINT *uCodePage); + HRESULT GetLangId([in] HKL hKL, [out] LANGID *plid); + + HRESULT KeybdEvent([in] LANGID lgidIME, [in] BYTE bVk, [in] BYTE bScan, [in] DWORD dwFlags, [in] DWORD dwExtraInfo); + + HRESULT LockModal(); + HRESULT UnlockModal(); + + // win98/nt5 apis + HRESULT AssociateContextEx([in] HWND hWnd, [in] HIMC hIMC, [in] DWORD dwFlags); + HRESULT DisableIME([in] DWORD idThread); + HRESULT GetImeMenuItemsA([in] HIMC hIMC, [in] DWORD dwFlags, [in] DWORD dwType, [in] IMEMENUITEMINFOA *pImeParentMenu, [out] IMEMENUITEMINFOA *pImeMenu, [in] DWORD dwSize, [out] DWORD *pdwResult); + HRESULT GetImeMenuItemsW([in] HIMC hIMC, [in] DWORD dwFlags, [in] DWORD dwType, [in] IMEMENUITEMINFOW *pImeParentMenu, [out] IMEMENUITEMINFOW *pImeMenu, [in] DWORD dwSize, [out] DWORD *pdwResult); + HRESULT EnumInputContext([in] DWORD idThread, [out] IEnumInputContext **ppEnum); + HRESULT RequestMessageA([in] HIMC hIMC, [in] WPARAM wParam, [in] LPARAM lParam, [out] LRESULT *plResult); + HRESULT RequestMessageW([in] HIMC hIMC, [in] WPARAM wParam, [in] LPARAM lParam, [out] LRESULT *plResult); + + HRESULT SendIMCA([in] HWND hWnd, [in] UINT uMsg, [in] WPARAM wParam, [in] LPARAM lParam, [out] LRESULT *plResult); + HRESULT SendIMCW([in] HWND hWnd, [in] UINT uMsg, [in] WPARAM wParam, [in] LPARAM lParam, [out] LRESULT *plResult); + + HRESULT IsSleeping(); + }; + + [ + object, + uuid(6FE20962-D077-11d0-8FE7-00AA006BCC59), + pointer_default(unique) + ] + interface IActiveIME : IUnknown + { + HRESULT Inquire([in] DWORD dwSystemInfoFlags, [out] IMEINFO *pIMEInfo, [out] LPWSTR szWndClass, [out] DWORD *pdwPrivate); + HRESULT ConversionList([in] HIMC hIMC, [in] LPWSTR szSource, [in] UINT uFlag, [in] UINT uBufLen, [out] CANDIDATELIST *pDest, [out] UINT *puCopied); + HRESULT Configure([in] HKL hKL, [in] HWND hWnd, [in] DWORD dwMode, [in] REGISTERWORDW *pRegisterWord); + HRESULT Destroy([in] UINT uReserved); + HRESULT Escape([in] HIMC hIMC, [in] UINT uEscape, [in, out] void *pData, [out] LRESULT *plResult); + HRESULT SetActiveContext([in] HIMC hIMC, [in] BOOL fFlag); + HRESULT ProcessKey([in] HIMC hIMC, [in] UINT uVirKey, [in] DWORD lParam, [in] BYTE *pbKeyState); + HRESULT Notify([in] HIMC hIMC, [in] DWORD dwAction, [in] DWORD dwIndex, [in] DWORD dwValue); + HRESULT Select([in] HIMC hIMC, [in] BOOL fSelect); + HRESULT SetCompositionString([in] HIMC hIMC, [in] DWORD dwIndex, [in] void *pComp, [in] DWORD dwCompLen, [in] void *pRead, [in] DWORD dwReadLen); + HRESULT ToAsciiEx([in] UINT uVirKey, [in] UINT uScanCode, [in] BYTE *pbKeyState, [in] UINT fuState, [in] HIMC hIMC, [out] DWORD *pdwTransBuf, [out] UINT *puSize); + HRESULT RegisterWord([in] LPWSTR szReading, [in] DWORD dwStyle, [in] LPWSTR szString); + HRESULT UnregisterWord([in] LPWSTR szReading, [in] DWORD dwStyle, [in] LPWSTR szString); + HRESULT GetRegisterWordStyle([in] UINT nItem, [out] STYLEBUFW *pStyleBuf, [out] UINT *puBufSize); + HRESULT EnumRegisterWord([in] LPWSTR szReading, [in] DWORD dwStyle, [in] LPWSTR szRegister, [in] LPVOID pData, [out] IEnumRegisterWordW **ppEnum); + HRESULT GetCodePageA([out] UINT *uCodePage); + HRESULT GetLangId([out] LANGID *plid); + }; + + [ + object, + uuid(e1c4bf0e-2d53-11d2-93e1-0060b067b86e), + pointer_default(unique) + ] + interface IActiveIME2 : IActiveIME + { + HRESULT Sleep(); + HRESULT Unsleep([in] BOOL fDead); + }; + + [ + uuid(4955DD33-B159-11d0-8FCF-00AA006BCC59), + ] + coclass CActiveIMM + { + [default] interface IActiveIMMApp; + interface IActiveIMMIME; + interface IActiveIMMRegistrar; + interface IActiveIMMMessagePumpOwner; + }; +} diff --git a/src/dlldata.c b/src/dlldata.c new file mode 100644 index 0000000..d03124c --- /dev/null +++ b/src/dlldata.c @@ -0,0 +1,38 @@ +/********************************************************* + DllData file -- generated by MIDL compiler + + DO NOT ALTER THIS FILE + + This file is regenerated by MIDL on every IDL file compile. + + To completely reconstruct this file, delete it and rerun MIDL + on all the IDL files in this DLL, specifying this file for the + /dlldata command line option + +*********************************************************/ + +#define PROXY_DELEGATION + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +EXTERN_PROXY_FILE( if_ole ) + + +PROXYFILE_LIST_START +/* Start of list */ + REFERENCE_PROXY_FILE( if_ole ), +/* End of list */ +PROXYFILE_LIST_END + + +DLLDATA_ROUTINES( aProxyFileList, GET_DLL_CLSID ) + +#ifdef __cplusplus +} /*extern "C" */ +#endif + +/* end of generated dlldata file */ diff --git a/src/dosinst.c b/src/dosinst.c new file mode 100644 index 0000000..5b05598 --- /dev/null +++ b/src/dosinst.c @@ -0,0 +1,2845 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * dosinst.c: Install program for Vim on MS-DOS and MS-Windows + * + * Compile with Make_mvc.mak, Make_bc3.mak, Make_bc5.mak or Make_djg.mak. + */ + +/* + * Include common code for dosinst.c and uninstal.c. + */ +#define DOSINST +#include "dosinst.h" +#include + +#define GVIMEXT64_PATH "GvimExt64\\gvimext.dll" +#define GVIMEXT32_PATH "GvimExt32\\gvimext.dll" + +/* Macro to do an error check I was typing over and over */ +#define CHECK_REG_ERROR(code) \ + do { \ + if (code != ERROR_SUCCESS) \ + { \ + printf("%ld error number: %ld\n", (long)__LINE__, (long)code); \ + return 1; \ + } \ + } while (0) + +int has_vim = 0; /* installable vim.exe exists */ +int has_gvim = 0; /* installable gvim.exe exists */ + +char oldvimrc[BUFSIZE]; /* name of existing vimrc file */ +char vimrc[BUFSIZE]; /* name of vimrc file to create */ + +char *default_bat_dir = NULL; /* when not NULL, use this as the default + directory to write .bat files in */ +char *default_vim_dir = NULL; /* when not NULL, use this as the default + install dir for NSIS */ + +/* + * Structure used for each choice the user can make. + */ +struct choice +{ + int active; /* non-zero when choice is active */ + char *text; /* text displayed for this choice */ + void (*changefunc)(int idx); /* function to change this choice */ + int arg; /* argument for function */ + void (*installfunc)(int idx); /* function to install this choice */ +}; + +struct choice choices[30]; /* choices the user can make */ +int choice_count = 0; /* number of choices available */ + +#define TABLE_SIZE(s) (int)(sizeof(s) / sizeof(*s)) + +enum +{ + compat_vi = 1, + compat_vim, + compat_some_enhancements, + compat_all_enhancements +}; +char *(compat_choices[]) = +{ + "\nChoose the default way to run Vim:", + "Vi compatible", + "Vim default", + "with some Vim enhancements", + "with syntax highlighting and other features switched on", +}; +int compat_choice = (int)compat_all_enhancements; +char *compat_text = "- run Vim %s"; + +enum +{ + remap_no = 1, + remap_win +}; +char *(remap_choices[]) = +{ + "\nChoose:", + "Do not remap keys for Windows behavior", + "Remap a few keys for Windows behavior (CTRL-V, CTRL-C, CTRL-F, etc)", +}; +int remap_choice = (int)remap_no; +char *remap_text = "- %s"; + +enum +{ + mouse_xterm = 1, + mouse_mswin, + mouse_default +}; +char *(mouse_choices[]) = +{ + "\nChoose the way how Vim uses the mouse:", + "right button extends selection (the Unix way)", + "right button has a popup menu, left button starts select mode (the Windows way)", + "right button has a popup menu, left button starts visual mode", +}; +int mouse_choice = (int)mouse_default; +char *mouse_text = "- The mouse %s"; + +enum +{ + vimfiles_dir_none = 1, + vimfiles_dir_vim, + vimfiles_dir_home +}; +static char *(vimfiles_dir_choices[]) = +{ + "\nCreate plugin directories:", + "No", + "In the VIM directory", + "In your HOME directory", +}; + +/* non-zero when selected to install the popup menu entry. */ +static int install_popup = 0; + +/* non-zero when selected to install the "Open with" entry. */ +static int install_openwith = 0; + +/* non-zero when need to add an uninstall entry in the registry */ +static int need_uninstall_entry = 0; + +/* + * Definitions of the directory name (under $VIM) of the vimfiles directory + * and its subdirectories: + */ +static char *(vimfiles_subdirs[]) = +{ + "colors", + "compiler", + "doc", + "ftdetect", + "ftplugin", + "indent", + "keymap", + "plugin", + "syntax", +}; + +/* + * Obtain a choice from a table. + * First entry is a question, others are choices. + */ + static int +get_choice(char **table, int entries) +{ + int answer; + int idx; + char dummy[100]; + + do + { + for (idx = 0; idx < entries; ++idx) + { + if (idx) + printf("%2d ", idx); + puts(table[idx]); + } + printf("Choice: "); + if (scanf("%d", &answer) != 1) + { + scanf("%99s", dummy); + answer = 0; + } + } + while (answer < 1 || answer >= entries); + + return answer; +} + +/* + * Check if the user unpacked the archives properly. + * Sets "runtimeidx". + */ + static void +check_unpack(void) +{ + char buf[BUFSIZE]; + FILE *fd; + struct stat st; + + /* check for presence of the correct version number in installdir[] */ + runtimeidx = strlen(installdir) - strlen(VIM_VERSION_NODOT); + if (runtimeidx <= 0 + || stricmp(installdir + runtimeidx, VIM_VERSION_NODOT) != 0 + || (installdir[runtimeidx - 1] != '/' + && installdir[runtimeidx - 1] != '\\')) + { + printf("ERROR: Install program not in directory \"%s\"\n", + VIM_VERSION_NODOT); + printf("This program can only work when it is located in its original directory\n"); + myexit(1); + } + + /* check if filetype.vim is present, which means the runtime archive has + * been unpacked */ + sprintf(buf, "%s\\filetype.vim", installdir); + if (stat(buf, &st) < 0) + { + printf("ERROR: Cannot find filetype.vim in \"%s\"\n", installdir); + printf("It looks like you did not unpack the runtime archive.\n"); + printf("You must unpack the runtime archive \"vim%srt.zip\" before installing.\n", + VIM_VERSION_NODOT + 3); + myexit(1); + } + + /* Check if vim.exe or gvim.exe is in the current directory. */ + if ((fd = fopen("gvim.exe", "r")) != NULL) + { + fclose(fd); + has_gvim = 1; + } + if ((fd = fopen("vim.exe", "r")) != NULL) + { + fclose(fd); + has_vim = 1; + } + if (!has_gvim && !has_vim) + { + printf("ERROR: Cannot find any Vim executables in \"%s\"\n\n", + installdir); + myexit(1); + } +} + +/* + * Compare paths "p[plen]" to "q[qlen]". Return 0 if they match. + * Ignores case and differences between '/' and '\'. + * "plen" and "qlen" can be negative, strlen() is used then. + */ + static int +pathcmp(char *p, int plen, char *q, int qlen) +{ + int i; + + if (plen < 0) + plen = strlen(p); + if (qlen < 0) + qlen = strlen(q); + for (i = 0; ; ++i) + { + /* End of "p": check if "q" also ends or just has a slash. */ + if (i == plen) + { + if (i == qlen) /* match */ + return 0; + if (i == qlen - 1 && (q[i] == '\\' || q[i] == '/')) + return 0; /* match with trailing slash */ + return 1; /* no match */ + } + + /* End of "q": check if "p" also ends or just has a slash. */ + if (i == qlen) + { + if (i == plen) /* match */ + return 0; + if (i == plen - 1 && (p[i] == '\\' || p[i] == '/')) + return 0; /* match with trailing slash */ + return 1; /* no match */ + } + + if (!(mytoupper(p[i]) == mytoupper(q[i]) + || ((p[i] == '/' || p[i] == '\\') + && (q[i] == '/' || q[i] == '\\')))) + return 1; /* no match */ + } + /*NOTREACHED*/ +} + +/* + * If the executable "**destination" is in the install directory, find another + * one in $PATH. + * On input "**destination" is the path of an executable in allocated memory + * (or NULL). + * "*destination" is set to NULL or the location of the file. + */ + static void +findoldfile(char **destination) +{ + char *bp = *destination; + size_t indir_l = strlen(installdir); + char *cp = bp + indir_l; + char *tmpname; + char *farname; + + /* + * No action needed if exe not found or not in this directory. + */ + if (bp == NULL + || strnicmp(bp, installdir, indir_l) != 0 + || strchr("/\\", *cp++) == NULL + || strchr(cp, '\\') != NULL + || strchr(cp, '/') != NULL) + return; + + tmpname = alloc((int)strlen(cp) + 1); + strcpy(tmpname, cp); + tmpname[strlen(tmpname) - 1] = 'x'; /* .exe -> .exx */ + + if (access(tmpname, 0) == 0) + { + printf("\nERROR: %s and %s clash. Remove or rename %s.\n", + tmpname, cp, tmpname); + myexit(1); + } + + if (rename(cp, tmpname) != 0) + { + printf("\nERROR: failed to rename %s to %s: %s\n", + cp, tmpname, strerror(0)); + myexit(1); + } + + farname = searchpath_save(cp); + + if (rename(tmpname, cp) != 0) + { + printf("\nERROR: failed to rename %s back to %s: %s\n", + tmpname, cp, strerror(0)); + myexit(1); + } + + free(*destination); + free(tmpname); + *destination = farname; +} + +/* + * Check if there is a vim.[exe|bat|, gvim.[exe|bat|, etc. in the path. + * When "check_bat_only" is TRUE, only find "default_bat_dir". + */ + static void +find_bat_exe(int check_bat_only) +{ + int i; + + /* avoid looking in the "installdir" by chdir to system root */ + mch_chdir(sysdrive); + mch_chdir("\\"); + + for (i = 1; i < TARGET_COUNT; ++i) + { + targets[i].oldbat = searchpath_save(targets[i].batname); + if (!check_bat_only) + targets[i].oldexe = searchpath_save(targets[i].exename); + + if (default_bat_dir == NULL && targets[i].oldbat != NULL) + { + default_bat_dir = alloc(strlen(targets[i].oldbat) + 1); + strcpy(default_bat_dir, targets[i].oldbat); + remove_tail(default_bat_dir); + } + if (check_bat_only && targets[i].oldbat != NULL) + { + free(targets[i].oldbat); + targets[i].oldbat = NULL; + } + } + + mch_chdir(installdir); +} + +/* + * Get the value of $VIMRUNTIME or $VIM and write it in $TEMP/vimini.ini, so + * that NSIS can read it. + * When not set, use the directory of a previously installed Vim. + */ + static void +get_vim_env(void) +{ + char *vim; + char buf[BUFSIZE]; + FILE *fd; + char fname[BUFSIZE]; + + /* First get $VIMRUNTIME. If it's set, remove the tail. */ + vim = getenv("VIMRUNTIME"); + if (vim != NULL && *vim != 0 && strlen(vim) < BUFSIZE) + { + strcpy(buf, vim); + remove_tail(buf); + vim = buf; + } + else + { + vim = getenv("VIM"); + if (vim == NULL || *vim == 0) + { + /* Use the directory from an old uninstall entry. */ + if (default_vim_dir != NULL) + vim = default_vim_dir; + else + /* Let NSIS know there is no default, it should use + * $PROGRAMFILES. */ + vim = ""; + } + } + + /* NSIS also uses GetTempPath(), thus we should get the same directory + * name as where NSIS will look for vimini.ini. */ + GetTempPath(BUFSIZE, fname); + add_pathsep(fname); + strcat(fname, "vimini.ini"); + + fd = fopen(fname, "w"); + if (fd != NULL) + { + /* Make it look like an .ini file, so that NSIS can read it with a + * ReadINIStr command. */ + fprintf(fd, "[vimini]\n"); + fprintf(fd, "dir=\"%s\"\n", vim); + fclose(fd); + } + else + { + printf("Failed to open %s\n", fname); + sleep(2); + } +} + +static int num_windows; + +/* + * Callback used for EnumWindows(): + * Count the window if the title looks like it is for the uninstaller. + */ +/*ARGSUSED*/ + static BOOL CALLBACK +window_cb(HWND hwnd, LPARAM lparam) +{ + char title[256]; + + title[0] = 0; + GetWindowText(hwnd, title, 256); + if (strstr(title, "Vim ") != NULL && strstr(title, " Uninstall") != NULL) + ++num_windows; + return TRUE; +} + +/* + * Run the uninstaller silently. + */ + static int +run_silent_uninstall(char *uninst_exe) +{ + char vimrt_dir[BUFSIZE]; + char temp_uninst[BUFSIZE]; + char temp_dir[MAX_PATH]; + char buf[BUFSIZE * 2 + 10]; + int i; + DWORD tick; + + strcpy(vimrt_dir, uninst_exe); + remove_tail(vimrt_dir); + + if (!GetTempPath(sizeof(temp_dir), temp_dir)) + return FAIL; + + /* Copy the uninstaller to a temporary exe. */ + tick = GetTickCount(); + for (i = 0; ; i++) + { + sprintf(temp_uninst, "%s\\vimun%04X.exe", temp_dir, + (unsigned int)((i + tick) & 0xFFFF)); + if (CopyFile(uninst_exe, temp_uninst, TRUE)) + break; + if (GetLastError() != ERROR_FILE_EXISTS) + return FAIL; + if (i == 65535) + return FAIL; + } + + /* Run the copied uninstaller silently. */ + if (strchr(temp_uninst, ' ') != NULL) + sprintf(buf, "\"%s\" /S _?=%s", temp_uninst, vimrt_dir); + else + sprintf(buf, "%s /S _?=%s", temp_uninst, vimrt_dir); + run_command(buf); + + DeleteFile(temp_uninst); + return OK; +} + +/* + * Check for already installed Vims. + * Return non-zero when found one. + */ + static int +uninstall_check(int skip_question) +{ + HKEY key_handle; + HKEY uninstall_key_handle; + char *uninstall_key = "software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + char subkey_name_buff[BUFSIZE]; + char temp_string_buffer[BUFSIZE]; + DWORD local_bufsize = BUFSIZE; + FILETIME temp_pfiletime; + DWORD key_index; + char input; + long code; + DWORD value_type; + DWORD orig_num_keys; + DWORD new_num_keys; + DWORD allow_silent; + int foundone = 0; + + code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, uninstall_key, 0, + KEY_WOW64_64KEY | KEY_READ, &key_handle); + CHECK_REG_ERROR(code); + + for (key_index = 0; + RegEnumKeyEx(key_handle, key_index, subkey_name_buff, &local_bufsize, + NULL, NULL, NULL, &temp_pfiletime) != ERROR_NO_MORE_ITEMS; + key_index++) + { + local_bufsize = BUFSIZE; + if (strncmp("Vim", subkey_name_buff, 3) == 0) + { + /* Open the key named Vim* */ + code = RegOpenKeyEx(key_handle, subkey_name_buff, 0, + KEY_WOW64_64KEY | KEY_READ, &uninstall_key_handle); + CHECK_REG_ERROR(code); + + /* get the DisplayName out of it to show the user */ + code = RegQueryValueEx(uninstall_key_handle, "displayname", 0, + &value_type, (LPBYTE)temp_string_buffer, + &local_bufsize); + local_bufsize = BUFSIZE; + CHECK_REG_ERROR(code); + + allow_silent = 0; + if (skip_question) + { + DWORD varsize = sizeof(DWORD); + + RegQueryValueEx(uninstall_key_handle, "AllowSilent", 0, + &value_type, (LPBYTE)&allow_silent, + &varsize); + } + + foundone = 1; + printf("\n*********************************************************\n"); + printf("Vim Install found what looks like an existing Vim version.\n"); + printf("The name of the entry is:\n"); + printf("\n \"%s\"\n\n", temp_string_buffer); + + printf("Installing the new version will disable part of the existing version.\n"); + printf("(The batch files used in a console and the \"Edit with Vim\" entry in\n"); + printf("the popup menu will use the new version)\n"); + + if (skip_question) + printf("\nRunning uninstall program for \"%s\"\n", temp_string_buffer); + else + printf("\nDo you want to uninstall \"%s\" now?\n(y)es/(n)o) ", temp_string_buffer); + fflush(stdout); + + /* get the UninstallString */ + code = RegQueryValueEx(uninstall_key_handle, "uninstallstring", 0, + &value_type, (LPBYTE)temp_string_buffer, &local_bufsize); + local_bufsize = BUFSIZE; + CHECK_REG_ERROR(code); + + /* Remember the directory, it is used as the default for NSIS. */ + default_vim_dir = alloc(strlen(temp_string_buffer) + 1); + strcpy(default_vim_dir, temp_string_buffer); + remove_tail(default_vim_dir); + remove_tail(default_vim_dir); + + input = 'n'; + do + { + if (input != 'n') + printf("%c is an invalid reply. Please enter either 'y' or 'n'\n", input); + + if (skip_question) + input = 'y'; + else + { + rewind(stdin); + scanf("%c", &input); + } + switch (input) + { + case 'y': + case 'Y': + /* save the number of uninstall keys so we can know if + * it changed */ + RegQueryInfoKey(key_handle, NULL, NULL, NULL, + &orig_num_keys, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); + + /* Find existing .bat files before deleting them. */ + find_bat_exe(TRUE); + + if (allow_silent) + { + if (run_silent_uninstall(temp_string_buffer) + == FAIL) + allow_silent = 0; /* Retry with non silent. */ + } + if (!allow_silent) + { + /* Execute the uninstall program. Put it in double + * quotes if there is an embedded space. */ + { + char buf[BUFSIZE]; + + if (strchr(temp_string_buffer, ' ') != NULL) + sprintf(buf, "\"%s\"", temp_string_buffer); + else + strcpy(buf, temp_string_buffer); + run_command(buf); + } + + /* Count the number of windows with a title that match + * the installer, so that we can check when it's done. + * The uninstaller copies itself, executes the copy + * and exits, thus we can't wait for the process to + * finish. */ + sleep(1); /* wait for uninstaller to start up */ + num_windows = 0; + EnumWindows(window_cb, 0); + if (num_windows == 0) + { + /* Did not find the uninstaller, ask user to press + * Enter when done. Just in case. */ + printf("Press Enter when the uninstaller is finished\n"); + rewind(stdin); + (void)getchar(); + } + else + { + printf("Waiting for the uninstaller to finish (press CTRL-C to abort)."); + do + { + printf("."); + fflush(stdout); + sleep(1); /* wait for the uninstaller to finish */ + num_windows = 0; + EnumWindows(window_cb, 0); + } while (num_windows > 0); + } + } + printf("\nDone!\n"); + + /* Check if an uninstall reg key was deleted. + * if it was, we want to decrement key_index. + * if we don't do this, we will skip the key + * immediately after any key that we delete. */ + RegQueryInfoKey(key_handle, NULL, NULL, NULL, + &new_num_keys, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); + if (new_num_keys < orig_num_keys) + key_index--; + + input = 'y'; + break; + + case 'n': + case 'N': + /* Do not uninstall */ + input = 'n'; + break; + + default: /* just drop through and redo the loop */ + break; + } + + } while (input != 'n' && input != 'y'); + + RegCloseKey(uninstall_key_handle); + } + } + RegCloseKey(key_handle); + + return foundone; +} + +/* + * Find out information about the system. + */ + static void +inspect_system(void) +{ + char *p; + char buf[BUFSIZE]; + FILE *fd; + int i; + int foundone; + + /* This may take a little while, let the user know what we're doing. */ + printf("Inspecting system...\n"); + + /* + * If $VIM is set, check that it's pointing to our directory. + */ + p = getenv("VIM"); + if (p != NULL && pathcmp(p, -1, installdir, runtimeidx - 1) != 0) + { + printf("------------------------------------------------------\n"); + printf("$VIM is set to \"%s\".\n", p); + printf("This is different from where this version of Vim is:\n"); + strcpy(buf, installdir); + *(buf + runtimeidx - 1) = NUL; + printf("\"%s\"\n", buf); + printf("You must adjust or remove the setting of $VIM,\n"); + if (interactive) + { + printf("to be able to use this install program.\n"); + myexit(1); + } + printf("otherwise Vim WILL NOT WORK properly!\n"); + printf("------------------------------------------------------\n"); + } + + /* + * If $VIMRUNTIME is set, check that it's pointing to our runtime directory. + */ + p = getenv("VIMRUNTIME"); + if (p != NULL && pathcmp(p, -1, installdir, -1) != 0) + { + printf("------------------------------------------------------\n"); + printf("$VIMRUNTIME is set to \"%s\".\n", p); + printf("This is different from where this version of Vim is:\n"); + printf("\"%s\"\n", installdir); + printf("You must adjust or remove the setting of $VIMRUNTIME,\n"); + if (interactive) + { + printf("to be able to use this install program.\n"); + myexit(1); + } + printf("otherwise Vim WILL NOT WORK properly!\n"); + printf("------------------------------------------------------\n"); + } + + /* + * Check if there is a vim.[exe|bat|, gvim.[exe|bat|, etc. in the path. + */ + find_bat_exe(FALSE); + + /* + * A .exe in the install directory may be found anyway on Windows 2000. + * Check for this situation and find another executable if necessary. + * w.briscoe@ponl.com 2001-01-20 + */ + foundone = 0; + for (i = 1; i < TARGET_COUNT; ++i) + { + findoldfile(&(targets[i].oldexe)); + if (targets[i].oldexe != NULL) + foundone = 1; + } + + if (foundone) + { + printf("Warning: Found Vim executable(s) in your $PATH:\n"); + for (i = 1; i < TARGET_COUNT; ++i) + if (targets[i].oldexe != NULL) + printf("%s\n", targets[i].oldexe); + printf("It will be used instead of the version you are installing.\n"); + printf("Please delete or rename it, or adjust your $PATH setting.\n"); + } + + /* + * Check if there is an existing ../_vimrc or ../.vimrc file. + */ + strcpy(oldvimrc, installdir); + strcpy(oldvimrc + runtimeidx, "_vimrc"); + if ((fd = fopen(oldvimrc, "r")) == NULL) + { + strcpy(oldvimrc + runtimeidx, "vimrc~1"); /* short version of .vimrc */ + if ((fd = fopen(oldvimrc, "r")) == NULL) + { + strcpy(oldvimrc + runtimeidx, ".vimrc"); + fd = fopen(oldvimrc, "r"); + } + } + if (fd != NULL) + fclose(fd); + else + *oldvimrc = NUL; +} + +/* + * Add a dummy choice to avoid that the numbering changes depending on items + * in the environment. The user may type a number he remembered without + * looking. + */ + static void +add_dummy_choice(void) +{ + choices[choice_count].installfunc = NULL; + choices[choice_count].active = 0; + choices[choice_count].changefunc = NULL; + choices[choice_count].text = NULL; + choices[choice_count].arg = 0; + ++choice_count; +} + +/*********************************************** + * stuff for creating the batch files. + */ + +/* + * Install the vim.bat, gvim.bat, etc. files. + */ + static void +install_bat_choice(int idx) +{ + char *batpath = targets[choices[idx].arg].batpath; + char *oldname = targets[choices[idx].arg].oldbat; + char *exename = targets[choices[idx].arg].exenamearg; + char *vimarg = targets[choices[idx].arg].exearg; + FILE *fd; + + if (*batpath != NUL) + { + fd = fopen(batpath, "w"); + if (fd == NULL) + printf("\nERROR: Cannot open \"%s\" for writing.\n", batpath); + else + { + need_uninstall_entry = 1; + + fprintf(fd, "@echo off\n"); + fprintf(fd, "rem -- Run Vim --\n"); + fprintf(fd, "\n"); + fprintf(fd, "setlocal\n"); + + /* Don't use double quotes for the "set" argument, also when it + * contains a space. The quotes would be included in the value + * for MSDOS and NT. + * The order of preference is: + * 1. $VIMRUNTIME/vim.exe (user preference) + * 2. $VIM/vim70/vim.exe (hard coded version) + * 3. installdir/vim.exe (hard coded install directory) + */ + fprintf(fd, "set VIM_EXE_DIR=%s\n", installdir); + fprintf(fd, "if exist \"%%VIM%%\\%s\\%s\" set VIM_EXE_DIR=%%VIM%%\\%s\n", + VIM_VERSION_NODOT, exename, VIM_VERSION_NODOT); + fprintf(fd, "if exist \"%%VIMRUNTIME%%\\%s\" set VIM_EXE_DIR=%%VIMRUNTIME%%\n", exename); + fprintf(fd, "\n"); + + /* Give an error message when the executable could not be found. */ + fprintf(fd, "if exist \"%%VIM_EXE_DIR%%\\%s\" goto havevim\n", + exename); + fprintf(fd, "echo \"%%VIM_EXE_DIR%%\\%s\" not found\n", exename); + fprintf(fd, "goto eof\n"); + fprintf(fd, "\n"); + fprintf(fd, ":havevim\n"); + + fprintf(fd, "rem collect the arguments in VIMARGS for Win95\n"); + fprintf(fd, "set VIMARGS=\n"); + if (*exename == 'g') + fprintf(fd, "set VIMNOFORK=\n"); + fprintf(fd, ":loopstart\n"); + fprintf(fd, "if .%%1==. goto loopend\n"); + if (*exename == 'g') + { + fprintf(fd, "if NOT .%%1==.--nofork goto noforklongarg\n"); + fprintf(fd, "set VIMNOFORK=1\n"); + fprintf(fd, ":noforklongarg\n"); + fprintf(fd, "if NOT .%%1==.-f goto noforkarg\n"); + fprintf(fd, "set VIMNOFORK=1\n"); + fprintf(fd, ":noforkarg\n"); + } + fprintf(fd, "set VIMARGS=%%VIMARGS%% %%1\n"); + fprintf(fd, "shift\n"); + fprintf(fd, "goto loopstart\n"); + fprintf(fd, ":loopend\n"); + fprintf(fd, "\n"); + + fprintf(fd, "if .%%OS%%==.Windows_NT goto ntaction\n"); + fprintf(fd, "\n"); + + /* For gvim.exe use "start" to avoid that the console window stays + * open. */ + if (*exename == 'g') + { + fprintf(fd, "if .%%VIMNOFORK%%==.1 goto nofork\n"); + fprintf(fd, "start "); + } + + /* Always use quotes, $VIM or $VIMRUNTIME might have a space. */ + fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%VIMARGS%%\n", + exename, vimarg); + fprintf(fd, "goto eof\n"); + fprintf(fd, "\n"); + + if (*exename == 'g') + { + fprintf(fd, ":nofork\n"); + fprintf(fd, "start /w "); + /* Always use quotes, $VIM or $VIMRUNTIME might have a space. */ + fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%VIMARGS%%\n", + exename, vimarg); + fprintf(fd, "goto eof\n"); + fprintf(fd, "\n"); + } + + fprintf(fd, ":ntaction\n"); + fprintf(fd, "rem for WinNT we can use %%*\n"); + + /* For gvim.exe use "start /b" to avoid that the console window + * stays open. */ + if (*exename == 'g') + { + fprintf(fd, "if .%%VIMNOFORK%%==.1 goto noforknt\n"); + fprintf(fd, "start \"dummy\" /b "); + } + + /* Always use quotes, $VIM or $VIMRUNTIME might have a space. */ + fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%*\n", exename, vimarg); + fprintf(fd, "goto eof\n"); + fprintf(fd, "\n"); + + if (*exename == 'g') + { + fprintf(fd, ":noforknt\n"); + fprintf(fd, "start \"dummy\" /b /wait "); + /* Always use quotes, $VIM or $VIMRUNTIME might have a space. */ + fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%*\n", + exename, vimarg); + } + + fprintf(fd, "\n:eof\n"); + fprintf(fd, "set VIMARGS=\n"); + if (*exename == 'g') + fprintf(fd, "set VIMNOFORK=\n"); + + fclose(fd); + printf("%s has been %s\n", batpath, + oldname == NULL ? "created" : "overwritten"); + } + } +} + +/* + * Make the text string for choice "idx". + * The format "fmt" is must have one %s item, which "arg" is used for. + */ + static void +alloc_text(int idx, char *fmt, char *arg) +{ + if (choices[idx].text != NULL) + free(choices[idx].text); + + choices[idx].text = alloc((int)(strlen(fmt) + strlen(arg)) - 1); + sprintf(choices[idx].text, fmt, arg); +} + +/* + * Toggle the "Overwrite .../vim.bat" to "Don't overwrite". + */ + static void +toggle_bat_choice(int idx) +{ + char *batname = targets[choices[idx].arg].batpath; + char *oldname = targets[choices[idx].arg].oldbat; + + if (*batname == NUL) + { + alloc_text(idx, " Overwrite %s", oldname); + strcpy(batname, oldname); + } + else + { + alloc_text(idx, " Do NOT overwrite %s", oldname); + *batname = NUL; + } +} + +/* + * Do some work for a batch file entry: Append the batch file name to the path + * and set the text for the choice. + */ + static void +set_bat_text(int idx, char *batpath, char *name) +{ + strcat(batpath, name); + + alloc_text(idx, " Create %s", batpath); +} + +/* + * Select a directory to write the batch file line. + */ + static void +change_bat_choice(int idx) +{ + char *path; + char *batpath; + char *name; + int n; + char *s; + char *p; + int count; + char **names = NULL; + int i; + int target = choices[idx].arg; + + name = targets[target].batname; + batpath = targets[target].batpath; + + path = getenv("PATH"); + if (path == NULL) + { + printf("\nERROR: The variable $PATH is not set\n"); + return; + } + + /* + * first round: count number of names in path; + * second round: save names to names[]. + */ + for (;;) + { + count = 1; + for (p = path; *p; ) + { + s = strchr(p, ';'); + if (s == NULL) + s = p + strlen(p); + if (names != NULL) + { + names[count] = alloc((int)(s - p) + 1); + strncpy(names[count], p, s - p); + names[count][s - p] = NUL; + } + ++count; + p = s; + if (*p != NUL) + ++p; + } + if (names != NULL) + break; + names = alloc((int)(count + 1) * sizeof(char *)); + } + names[0] = alloc(50); + sprintf(names[0], "Select directory to create %s in:", name); + names[count] = alloc(50); + if (choices[idx].arg == 0) + sprintf(names[count], "Do not create any .bat file."); + else + sprintf(names[count], "Do not create a %s file.", name); + n = get_choice(names, count + 1); + + if (n == count) + { + /* Selected last item, don't create bat file. */ + *batpath = NUL; + if (choices[idx].arg != 0) + alloc_text(idx, " Do NOT create %s", name); + } + else + { + /* Selected one of the paths. For the first item only keep the path, + * for the others append the batch file name. */ + strcpy(batpath, names[n]); + add_pathsep(batpath); + if (choices[idx].arg != 0) + set_bat_text(idx, batpath, name); + } + + for (i = 0; i <= count; ++i) + free(names[i]); + free(names); +} + +char *bat_text_yes = "Install .bat files to use Vim at the command line:"; +char *bat_text_no = "do NOT install .bat files to use Vim at the command line"; + + static void +change_main_bat_choice(int idx) +{ + int i; + + /* let the user select a default directory or NONE */ + change_bat_choice(idx); + + if (targets[0].batpath[0] != NUL) + choices[idx].text = bat_text_yes; + else + choices[idx].text = bat_text_no; + + /* update the individual batch file selections */ + for (i = 1; i < TARGET_COUNT; ++i) + { + /* Only make it active when the first item has a path and the vim.exe + * or gvim.exe exists (there is a changefunc then). */ + if (targets[0].batpath[0] != NUL + && choices[idx + i].changefunc != NULL) + { + choices[idx + i].active = 1; + if (choices[idx + i].changefunc == change_bat_choice + && targets[i].batpath[0] != NUL) + { + strcpy(targets[i].batpath, targets[0].batpath); + set_bat_text(idx + i, targets[i].batpath, targets[i].batname); + } + } + else + choices[idx + i].active = 0; + } +} + +/* + * Initialize a choice for creating a batch file. + */ + static void +init_bat_choice(int target) +{ + char *batpath = targets[target].batpath; + char *oldbat = targets[target].oldbat; + char *p; + int i; + + choices[choice_count].arg = target; + choices[choice_count].installfunc = install_bat_choice; + choices[choice_count].active = 1; + choices[choice_count].text = NULL; /* will be set below */ + if (oldbat != NULL) + { + /* A [g]vim.bat exists: Only choice is to overwrite it or not. */ + choices[choice_count].changefunc = toggle_bat_choice; + *batpath = NUL; + toggle_bat_choice(choice_count); + } + else + { + if (default_bat_dir != NULL) + /* Prefer using the same path as an existing .bat file. */ + strcpy(batpath, default_bat_dir); + else + { + /* No [g]vim.bat exists: Write it to a directory in $PATH. Use + * $WINDIR by default, if it's empty the first item in $PATH. */ + p = getenv("WINDIR"); + if (p != NULL && *p != NUL) + strcpy(batpath, p); + else + { + p = getenv("PATH"); + if (p == NULL || *p == NUL) /* "cannot happen" */ + strcpy(batpath, "C:/Windows"); + else + { + i = 0; + while (*p != NUL && *p != ';') + batpath[i++] = *p++; + batpath[i] = NUL; + } + } + } + add_pathsep(batpath); + set_bat_text(choice_count, batpath, targets[target].batname); + + choices[choice_count].changefunc = change_bat_choice; + } + ++choice_count; +} + +/* + * Set up the choices for installing .bat files. + * For these items "arg" is the index in targets[]. + */ + static void +init_bat_choices(void) +{ + int i; + + /* The first item is used to switch installing batch files on/off and + * setting the default path. */ + choices[choice_count].text = bat_text_yes; + choices[choice_count].changefunc = change_main_bat_choice; + choices[choice_count].installfunc = NULL; + choices[choice_count].active = 1; + choices[choice_count].arg = 0; + ++choice_count; + + /* Add items for each batch file target. Only used when not disabled by + * the first item. When a .exe exists, don't offer to create a .bat. */ + for (i = 1; i < TARGET_COUNT; ++i) + if (targets[i].oldexe == NULL + && (targets[i].exenamearg[0] == 'g' ? has_gvim : has_vim)) + init_bat_choice(i); + else + add_dummy_choice(); +} + +/* + * Install the vimrc file. + */ + static void +install_vimrc(int idx) +{ + FILE *fd, *tfd; + char *fname; + + /* If an old vimrc file exists, overwrite it. + * Otherwise create a new one. */ + if (*oldvimrc != NUL) + fname = oldvimrc; + else + fname = vimrc; + + fd = fopen(fname, "w"); + if (fd == NULL) + { + printf("\nERROR: Cannot open \"%s\" for writing.\n", fname); + return; + } + switch (compat_choice) + { + case compat_vi: + fprintf(fd, "\" Vi compatible\n"); + fprintf(fd, "set compatible\n"); + break; + case compat_vim: + fprintf(fd, "\" Vim's default behavior\n"); + fprintf(fd, "if &compatible\n"); + fprintf(fd, " set nocompatible\n"); + fprintf(fd, "endif\n"); + break; + case compat_some_enhancements: + fprintf(fd, "\" Vim with some enhancements\n"); + fprintf(fd, "source $VIMRUNTIME/defaults.vim\n"); + break; + case compat_all_enhancements: + fprintf(fd, "\" Vim with all enhancements\n"); + fprintf(fd, "source $VIMRUNTIME/vimrc_example.vim\n"); + break; + } + switch (remap_choice) + { + case remap_no: + break; + case remap_win: + fprintf(fd, "\n"); + fprintf(fd, "\" Remap a few keys for Windows behavior\n"); + fprintf(fd, "source $VIMRUNTIME/mswin.vim\n"); + break; + } + switch (mouse_choice) + { + case mouse_xterm: + fprintf(fd, "\n"); + fprintf(fd, "\" Mouse behavior (the Unix way)\n"); + fprintf(fd, "behave xterm\n"); + break; + case mouse_mswin: + fprintf(fd, "\n"); + fprintf(fd, "\" Mouse behavior (the Windows way)\n"); + fprintf(fd, "behave mswin\n"); + break; + case mouse_default: + break; + } + if ((tfd = fopen("diff.exe", "r")) != NULL) + { + /* Use the diff.exe that comes with the self-extracting gvim.exe. */ + fclose(tfd); + fprintf(fd, "\n"); + fprintf(fd, "\" Use the internal diff if available.\n"); + fprintf(fd, "\" Otherwise use the special 'diffexpr' for Windows.\n"); + fprintf(fd, "if &diffopt !~# 'internal'\n"); + fprintf(fd, " set diffexpr=MyDiff()\n"); + fprintf(fd, "endif\n"); + fprintf(fd, "function MyDiff()\n"); + fprintf(fd, " let opt = '-a --binary '\n"); + fprintf(fd, " if &diffopt =~ 'icase' | let opt = opt . '-i ' | endif\n"); + fprintf(fd, " if &diffopt =~ 'iwhite' | let opt = opt . '-b ' | endif\n"); + /* Use quotes only when needed, they may cause trouble. + * Always escape "!". */ + fprintf(fd, " let arg1 = v:fname_in\n"); + fprintf(fd, " if arg1 =~ ' ' | let arg1 = '\"' . arg1 . '\"' | endif\n"); + fprintf(fd, " let arg1 = substitute(arg1, '!', '\\!', 'g')\n"); + fprintf(fd, " let arg2 = v:fname_new\n"); + fprintf(fd, " if arg2 =~ ' ' | let arg2 = '\"' . arg2 . '\"' | endif\n"); + fprintf(fd, " let arg2 = substitute(arg2, '!', '\\!', 'g')\n"); + fprintf(fd, " let arg3 = v:fname_out\n"); + fprintf(fd, " if arg3 =~ ' ' | let arg3 = '\"' . arg3 . '\"' | endif\n"); + fprintf(fd, " let arg3 = substitute(arg3, '!', '\\!', 'g')\n"); + + /* If the path has a space: When using cmd.exe (Win NT/2000/XP) put + * quotes around the diff command and rely on the default value of + * shellxquote to solve the quoting problem for the whole command. + * + * Otherwise put a double quote just before the space and at the + * end of the command. Putting quotes around the whole thing + * doesn't work on Win 95/98/ME. This is mostly guessed! */ + fprintf(fd, " if $VIMRUNTIME =~ ' '\n"); + fprintf(fd, " if &sh =~ '\\ ' . arg3\n"); + fprintf(fd, " if exists('l:shxq_sav')\n"); + fprintf(fd, " let &shellxquote=l:shxq_sav\n"); + fprintf(fd, " endif\n"); + fprintf(fd, "endfunction\n"); + fprintf(fd, "\n"); + } + fclose(fd); + printf("%s has been written\n", fname); +} + + static void +change_vimrc_choice(int idx) +{ + if (choices[idx].installfunc != NULL) + { + /* Switch to NOT change or create a vimrc file. */ + if (*oldvimrc != NUL) + alloc_text(idx, "Do NOT change startup file %s", oldvimrc); + else + alloc_text(idx, "Do NOT create startup file %s", vimrc); + choices[idx].installfunc = NULL; + choices[idx + 1].active = 0; + choices[idx + 2].active = 0; + choices[idx + 3].active = 0; + } + else + { + /* Switch to change or create a vimrc file. */ + if (*oldvimrc != NUL) + alloc_text(idx, "Overwrite startup file %s with:", oldvimrc); + else + alloc_text(idx, "Create startup file %s with:", vimrc); + choices[idx].installfunc = install_vimrc; + choices[idx + 1].active = 1; + choices[idx + 2].active = 1; + choices[idx + 3].active = 1; + } +} + +/* + * Change the choice how to run Vim. + */ + static void +change_run_choice(int idx) +{ + compat_choice = get_choice(compat_choices, TABLE_SIZE(compat_choices)); + alloc_text(idx, compat_text, compat_choices[compat_choice]); +} + +/* + * Change the choice if keys are to be remapped. + */ + static void +change_remap_choice(int idx) +{ + remap_choice = get_choice(remap_choices, TABLE_SIZE(remap_choices)); + alloc_text(idx, remap_text, remap_choices[remap_choice]); +} + +/* + * Change the choice how to select text. + */ + static void +change_mouse_choice(int idx) +{ + mouse_choice = get_choice(mouse_choices, TABLE_SIZE(mouse_choices)); + alloc_text(idx, mouse_text, mouse_choices[mouse_choice]); +} + + static void +init_vimrc_choices(void) +{ + /* set path for a new _vimrc file (also when not used) */ + strcpy(vimrc, installdir); + strcpy(vimrc + runtimeidx, "_vimrc"); + + /* Set opposite value and then toggle it by calling change_vimrc_choice() */ + if (*oldvimrc == NUL) + choices[choice_count].installfunc = NULL; + else + choices[choice_count].installfunc = install_vimrc; + choices[choice_count].text = NULL; + change_vimrc_choice(choice_count); + choices[choice_count].changefunc = change_vimrc_choice; + choices[choice_count].active = 1; + ++choice_count; + + /* default way to run Vim */ + alloc_text(choice_count, compat_text, compat_choices[compat_choice]); + choices[choice_count].changefunc = change_run_choice; + choices[choice_count].installfunc = NULL; + choices[choice_count].active = (*oldvimrc == NUL); + ++choice_count; + + /* Whether to remap keys */ + alloc_text(choice_count, remap_text , remap_choices[remap_choice]); + choices[choice_count].changefunc = change_remap_choice; + choices[choice_count].installfunc = NULL; + choices[choice_count].active = (*oldvimrc == NUL); + ++choice_count; + + /* default way to use the mouse */ + alloc_text(choice_count, mouse_text, mouse_choices[mouse_choice]); + choices[choice_count].changefunc = change_mouse_choice; + choices[choice_count].installfunc = NULL; + choices[choice_count].active = (*oldvimrc == NUL); + ++choice_count; +} + + static LONG +reg_create_key( + HKEY root, + const char *subkey, + PHKEY phKey, + DWORD flag) +{ + DWORD disp; + + *phKey = NULL; + return RegCreateKeyEx( + root, subkey, + 0, NULL, REG_OPTION_NON_VOLATILE, + flag | KEY_WRITE, + NULL, phKey, &disp); +} + + static LONG +reg_set_string_value( + HKEY hKey, + const char *value_name, + const char *data) +{ + return RegSetValueEx(hKey, value_name, 0, REG_SZ, + (LPBYTE)data, (DWORD)(1 + strlen(data))); +} + + static LONG +reg_create_key_and_value( + HKEY hRootKey, + const char *subkey, + const char *value_name, + const char *data, + DWORD flag) +{ + HKEY hKey; + LONG lRet = reg_create_key(hRootKey, subkey, &hKey, flag); + + if (ERROR_SUCCESS == lRet) + { + lRet = reg_set_string_value(hKey, value_name, data); + RegCloseKey(hKey); + } + return lRet; +} + + static LONG +register_inproc_server( + HKEY hRootKey, + const char *clsid, + const char *extname, + const char *module, + const char *threading_model, + DWORD flag) +{ + CHAR subkey[BUFSIZE]; + LONG lRet; + + sprintf(subkey, "CLSID\\%s", clsid); + lRet = reg_create_key_and_value(hRootKey, subkey, NULL, extname, flag); + if (ERROR_SUCCESS == lRet) + { + sprintf(subkey, "CLSID\\%s\\InProcServer32", clsid); + lRet = reg_create_key_and_value(hRootKey, subkey, NULL, module, flag); + if (ERROR_SUCCESS == lRet) + { + lRet = reg_create_key_and_value(hRootKey, subkey, + "ThreadingModel", threading_model, flag); + } + } + return lRet; +} + + static LONG +register_shellex( + HKEY hRootKey, + const char *clsid, + const char *name, + const char *exe_path, + DWORD flag) +{ + LONG lRet = reg_create_key_and_value( + hRootKey, + "*\\shellex\\ContextMenuHandlers\\gvim", + NULL, + clsid, + flag); + + if (ERROR_SUCCESS == lRet) + { + lRet = reg_create_key_and_value( + HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", + clsid, + name, + flag); + + if (ERROR_SUCCESS == lRet) + { + lRet = reg_create_key_and_value( + HKEY_LOCAL_MACHINE, + "Software\\Vim\\Gvim", + "path", + exe_path, + flag); + } + } + return lRet; +} + + static LONG +register_openwith( + HKEY hRootKey, + const char *exe_path, + DWORD flag) +{ + char exe_cmd[BUFSIZE]; + LONG lRet; + + sprintf(exe_cmd, "\"%s\" \"%%1\"", exe_path); + lRet = reg_create_key_and_value( + hRootKey, + "Applications\\gvim.exe\\shell\\edit\\command", + NULL, + exe_cmd, + flag); + + if (ERROR_SUCCESS == lRet) + { + int i; + static const char *openwith[] = { + ".htm\\OpenWithList\\gvim.exe", + ".vim\\OpenWithList\\gvim.exe", + "*\\OpenWithList\\gvim.exe", + }; + + for (i = 0; ERROR_SUCCESS == lRet + && i < sizeof(openwith) / sizeof(openwith[0]); i++) + { + lRet = reg_create_key_and_value(hRootKey, openwith[i], NULL, "", flag); + } + } + + return lRet; +} + + static LONG +register_uninstall( + HKEY hRootKey, + const char *appname, + const char *display_name, + const char *uninstall_string, + const char *display_icon, + const char *display_version, + const char *publisher) +{ + LONG lRet = reg_create_key_and_value(hRootKey, appname, + "DisplayName", display_name, KEY_WOW64_64KEY); + + if (ERROR_SUCCESS == lRet) + lRet = reg_create_key_and_value(hRootKey, appname, + "UninstallString", uninstall_string, KEY_WOW64_64KEY); + if (ERROR_SUCCESS == lRet) + lRet = reg_create_key_and_value(hRootKey, appname, + "DisplayIcon", display_icon, KEY_WOW64_64KEY); + if (ERROR_SUCCESS == lRet) + lRet = reg_create_key_and_value(hRootKey, appname, + "DisplayVersion", display_version, KEY_WOW64_64KEY); + if (ERROR_SUCCESS == lRet) + lRet = reg_create_key_and_value(hRootKey, appname, + "Publisher", publisher, KEY_WOW64_64KEY); + return lRet; +} + +/* + * Add some entries to the registry: + * - to add "Edit with Vim" to the context * menu + * - to add Vim to the "Open with..." list + * - to uninstall Vim + */ +/*ARGSUSED*/ + static int +install_registry(void) +{ + LONG lRet = ERROR_SUCCESS; + const char *vim_ext_ThreadingModel = "Apartment"; + const char *vim_ext_name = "Vim Shell Extension"; + const char *vim_ext_clsid = "{51EEE242-AD87-11d3-9C1E-0090278BBD99}"; + char vim_exe_path[BUFSIZE]; + char display_name[BUFSIZE]; + char uninstall_string[BUFSIZE]; + char icon_string[BUFSIZE]; + int i; + int loop_count = is_64bit_os() ? 2 : 1; + DWORD flag; + + sprintf(vim_exe_path, "%s\\gvim.exe", installdir); + + if (install_popup) + { + char bufg[BUFSIZE]; + + printf("Creating \"Edit with Vim\" popup menu entry\n"); + + for (i = 0; i < loop_count; i++) + { + if (i == 0) + { + sprintf(bufg, "%s\\" GVIMEXT32_PATH, installdir); + flag = KEY_WOW64_32KEY; + } + else + { + sprintf(bufg, "%s\\" GVIMEXT64_PATH, installdir); + flag = KEY_WOW64_64KEY; + } + + lRet = register_inproc_server( + HKEY_CLASSES_ROOT, vim_ext_clsid, vim_ext_name, + bufg, vim_ext_ThreadingModel, flag); + if (ERROR_SUCCESS != lRet) + return FAIL; + lRet = register_shellex( + HKEY_CLASSES_ROOT, vim_ext_clsid, vim_ext_name, + vim_exe_path, flag); + if (ERROR_SUCCESS != lRet) + return FAIL; + } + } + + if (install_openwith) + { + printf("Creating \"Open with ...\" list entry\n"); + + for (i = 0; i < loop_count; i++) + { + if (i == 0) + flag = KEY_WOW64_32KEY; + else + flag = KEY_WOW64_64KEY; + + lRet = register_openwith(HKEY_CLASSES_ROOT, vim_exe_path, flag); + if (ERROR_SUCCESS != lRet) + return FAIL; + } + } + + printf("Creating an uninstall entry\n"); + sprintf(display_name, "Vim " VIM_VERSION_SHORT +#ifdef _WIN64 + " (x64)" +#endif + ); + + /* For the NSIS installer use the generated uninstaller. */ + if (interactive) + sprintf(uninstall_string, "%s\\uninstal.exe", installdir); + else + sprintf(uninstall_string, "%s\\uninstall-gui.exe", installdir); + + sprintf(icon_string, "%s\\gvim.exe,0", installdir); + + lRet = register_uninstall( + HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Vim " VIM_VERSION_SHORT, + display_name, + uninstall_string, + icon_string, + VIM_VERSION_SHORT, + "Bram Moolenaar et al."); + if (ERROR_SUCCESS != lRet) + return FAIL; + + return OK; +} + + static void +change_popup_choice(int idx) +{ + if (install_popup == 0) + { + choices[idx].text = "Install an entry for Vim in the popup menu for the right\n mouse button so that you can edit any file with Vim"; + install_popup = 1; + } + else + { + choices[idx].text = "Do NOT install an entry for Vim in the popup menu for the\n right mouse button to edit any file with Vim"; + install_popup = 0; + } +} + +/* + * Only add the choice for the popup menu entry when gvim.exe was found and + * both gvimext.dll and regedit.exe exist. + */ + static void +init_popup_choice(void) +{ + struct stat st; + + if (has_gvim + && (stat(GVIMEXT32_PATH, &st) >= 0 + || stat(GVIMEXT64_PATH, &st) >= 0)) + { + choices[choice_count].changefunc = change_popup_choice; + choices[choice_count].installfunc = NULL; + choices[choice_count].active = 1; + change_popup_choice(choice_count); /* set the text */ + ++choice_count; + } + else + add_dummy_choice(); +} + + static void +change_openwith_choice(int idx) +{ + if (install_openwith == 0) + { + choices[idx].text = "Add Vim to the \"Open With...\" list in the popup menu for the right\n mouse button so that you can edit any file with Vim"; + install_openwith = 1; + } + else + { + choices[idx].text = "Do NOT add Vim to the \"Open With...\" list in the popup menu for the\n right mouse button to edit any file with Vim"; + install_openwith = 0; + } +} + +/* + * Only add the choice for the open-with menu entry when gvim.exe was found + * and regedit.exe exist. + */ + static void +init_openwith_choice(void) +{ + if (has_gvim) + { + choices[choice_count].changefunc = change_openwith_choice; + choices[choice_count].installfunc = NULL; + choices[choice_count].active = 1; + change_openwith_choice(choice_count); /* set the text */ + ++choice_count; + } + else + add_dummy_choice(); +} + +/* create_shortcut + * + * Create a shell link. + * + * returns 0 on failure, non-zero on successful completion. + * + * NOTE: Currently untested with mingw. + */ + int +create_shortcut( + const char *shortcut_name, + const char *iconfile_path, + int iconindex, + const char *shortcut_target, + const char *shortcut_args, + const char *workingdir + ) +{ + IShellLink *shelllink_ptr; + HRESULT hres; + IPersistFile *persistfile_ptr; + + /* Initialize COM library */ + hres = CoInitialize(NULL); + if (!SUCCEEDED(hres)) + { + printf("Error: Could not open the COM library. Not creating shortcut.\n"); + return FAIL; + } + + /* Instantiate a COM object for the ShellLink, store a pointer to it + * in shelllink_ptr. */ + hres = CoCreateInstance(&CLSID_ShellLink, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IShellLink, + (void **) &shelllink_ptr); + + if (SUCCEEDED(hres)) /* If the instantiation was successful... */ + { + /* ...Then build a PersistFile interface for the ShellLink so we can + * save it as a file after we build it. */ + hres = shelllink_ptr->lpVtbl->QueryInterface(shelllink_ptr, + &IID_IPersistFile, (void **) &persistfile_ptr); + + if (SUCCEEDED(hres)) + { + wchar_t wsz[BUFSIZE]; + + /* translate the (possibly) multibyte shortcut filename to windows + * Unicode so it can be used as a file name. + */ + MultiByteToWideChar(CP_ACP, 0, shortcut_name, -1, wsz, BUFSIZE); + + /* set the attributes */ + shelllink_ptr->lpVtbl->SetPath(shelllink_ptr, shortcut_target); + shelllink_ptr->lpVtbl->SetWorkingDirectory(shelllink_ptr, + workingdir); + shelllink_ptr->lpVtbl->SetIconLocation(shelllink_ptr, + iconfile_path, iconindex); + shelllink_ptr->lpVtbl->SetArguments(shelllink_ptr, shortcut_args); + + /* save the shortcut to a file and return the PersistFile object*/ + persistfile_ptr->lpVtbl->Save(persistfile_ptr, wsz, 1); + persistfile_ptr->lpVtbl->Release(persistfile_ptr); + } + else + { + printf("QueryInterface Error\n"); + return FAIL; + } + + /* Return the ShellLink object */ + shelllink_ptr->lpVtbl->Release(shelllink_ptr); + } + else + { + printf("CoCreateInstance Error - hres = %08x\n", (int)hres); + return FAIL; + } + + return OK; +} + +/* + * Build a path to where we will put a specified link. + * + * Return 0 on error, non-zero on success + */ + int +build_link_name( + char *link_path, + const char *link_name, + const char *shell_folder_name) +{ + char shell_folder_path[BUFSIZE]; + + if (get_shell_folder_path(shell_folder_path, shell_folder_name) == FAIL) + { + printf("An error occurred while attempting to find the path to %s.\n", + shell_folder_name); + return FAIL; + } + + /* Make sure the directory exists (create Start Menu\Programs\Vim). + * Ignore errors if it already exists. */ + vim_mkdir(shell_folder_path, 0755); + + /* build the path to the shortcut and the path to gvim.exe */ + sprintf(link_path, "%s\\%s.lnk", shell_folder_path, link_name); + + return OK; +} + + static int +build_shortcut( + const char *name, /* Name of the shortcut */ + const char *exename, /* Name of the executable (e.g., vim.exe) */ + const char *args, + const char *shell_folder, + const char *workingdir) +{ + char executable_path[BUFSIZE]; + char link_name[BUFSIZE]; + + sprintf(executable_path, "%s\\%s", installdir, exename); + + if (build_link_name(link_name, name, shell_folder) == FAIL) + { + printf("An error has occurred. A shortcut to %s will not be created %s.\n", + name, + *shell_folder == 'd' ? "on the desktop" : "in the Start menu"); + return FAIL; + } + + /* Create the shortcut: */ + return create_shortcut(link_name, executable_path, 0, + executable_path, args, workingdir); +} + +/* + * We used to use "homedir" as the working directory, but that is a bad choice + * on multi-user systems. However, not specifying a directory results in the + * current directory to be c:\Windows\system32 on Windows 7. Use environment + * variables instead. + */ +#define WORKDIR "%HOMEDRIVE%%HOMEPATH%" + +/* + * Create shortcut(s) in the Start Menu\Programs\Vim folder. + */ + static void +install_start_menu(int idx) +{ + need_uninstall_entry = 1; + printf("Creating start menu\n"); + if (has_vim) + { + if (build_shortcut("Vim", "vim.exe", "", + VIM_STARTMENU, WORKDIR) == FAIL) + return; + if (build_shortcut("Vim Read-only", "vim.exe", "-R", + VIM_STARTMENU, WORKDIR) == FAIL) + return; + if (build_shortcut("Vim Diff", "vim.exe", "-d", + VIM_STARTMENU, WORKDIR) == FAIL) + return; + } + if (has_gvim) + { + if (build_shortcut("gVim", "gvim.exe", "", + VIM_STARTMENU, WORKDIR) == FAIL) + return; + if (build_shortcut("gVim Easy", "gvim.exe", "-y", + VIM_STARTMENU, WORKDIR) == FAIL) + return; + if (build_shortcut("gVim Read-only", "gvim.exe", "-R", + VIM_STARTMENU, WORKDIR) == FAIL) + return; + if (build_shortcut("gVim Diff", "gvim.exe", "-d", + VIM_STARTMENU, WORKDIR) == FAIL) + return; + } + if (build_shortcut("Uninstall", + interactive ? "uninstal.exe" : "uninstall-gui.exe", "", + VIM_STARTMENU, installdir) == FAIL) + return; + /* For Windows NT the working dir of the vimtutor.bat must be right, + * otherwise gvim.exe won't be found and using gvimbat doesn't work. */ + if (build_shortcut("Vim tutor", "vimtutor.bat", "", + VIM_STARTMENU, installdir) == FAIL) + return; + if (build_shortcut("Help", has_gvim ? "gvim.exe" : "vim.exe", "-c h", + VIM_STARTMENU, WORKDIR) == FAIL) + return; + { + char shell_folder_path[BUFSIZE]; + + /* Creating the URL shortcut works a bit differently... */ + if (get_shell_folder_path(shell_folder_path, VIM_STARTMENU) == FAIL) + { + printf("Finding the path of the Start menu failed\n"); + return ; + } + add_pathsep(shell_folder_path); + strcat(shell_folder_path, "Vim Online.url"); + if (!WritePrivateProfileString("InternetShortcut", "URL", + "https://www.vim.org/", shell_folder_path)) + { + printf("Creating the Vim online URL failed\n"); + return; + } + } +} + + static void +toggle_startmenu_choice(int idx) +{ + if (choices[idx].installfunc == NULL) + { + choices[idx].installfunc = install_start_menu; + choices[idx].text = "Add Vim to the Start menu"; + } + else + { + choices[idx].installfunc = NULL; + choices[idx].text = "Do NOT add Vim to the Start menu"; + } +} + +/* + * Function to actually create the shortcuts + * + * Currently I am supplying no working directory to the shortcut. This + * means that the initial working dir will be: + * - the location of the shortcut if no file is supplied + * - the location of the file being edited if a file is supplied (ie via + * drag and drop onto the shortcut). + */ + void +install_shortcut_gvim(int idx) +{ + /* Create shortcut(s) on the desktop */ + if (choices[idx].arg) + { + (void)build_shortcut(icon_names[0], "gvim.exe", + "", "desktop", WORKDIR); + need_uninstall_entry = 1; + } +} + + void +install_shortcut_evim(int idx) +{ + if (choices[idx].arg) + { + (void)build_shortcut(icon_names[1], "gvim.exe", + "-y", "desktop", WORKDIR); + need_uninstall_entry = 1; + } +} + + void +install_shortcut_gview(int idx) +{ + if (choices[idx].arg) + { + (void)build_shortcut(icon_names[2], "gvim.exe", + "-R", "desktop", WORKDIR); + need_uninstall_entry = 1; + } +} + + void +toggle_shortcut_choice(int idx) +{ + char *arg; + + if (choices[idx].installfunc == install_shortcut_gvim) + arg = "gVim"; + else if (choices[idx].installfunc == install_shortcut_evim) + arg = "gVim Easy"; + else + arg = "gVim Read-only"; + if (choices[idx].arg) + { + choices[idx].arg = 0; + alloc_text(idx, "Do NOT create a desktop icon for %s", arg); + } + else + { + choices[idx].arg = 1; + alloc_text(idx, "Create a desktop icon for %s", arg); + } +} + + static void +init_startmenu_choice(void) +{ + /* Start menu */ + choices[choice_count].changefunc = toggle_startmenu_choice; + choices[choice_count].installfunc = NULL; + choices[choice_count].active = 1; + toggle_startmenu_choice(choice_count); /* set the text */ + ++choice_count; +} + +/* + * Add the choice for the desktop shortcuts. + */ + static void +init_shortcut_choices(void) +{ + /* Shortcut to gvim */ + choices[choice_count].text = NULL; + choices[choice_count].arg = 0; + choices[choice_count].active = has_gvim; + choices[choice_count].changefunc = toggle_shortcut_choice; + choices[choice_count].installfunc = install_shortcut_gvim; + toggle_shortcut_choice(choice_count); + ++choice_count; + + /* Shortcut to evim */ + choices[choice_count].text = NULL; + choices[choice_count].arg = 0; + choices[choice_count].active = has_gvim; + choices[choice_count].changefunc = toggle_shortcut_choice; + choices[choice_count].installfunc = install_shortcut_evim; + toggle_shortcut_choice(choice_count); + ++choice_count; + + /* Shortcut to gview */ + choices[choice_count].text = NULL; + choices[choice_count].arg = 0; + choices[choice_count].active = has_gvim; + choices[choice_count].changefunc = toggle_shortcut_choice; + choices[choice_count].installfunc = install_shortcut_gview; + toggle_shortcut_choice(choice_count); + ++choice_count; +} + +/* + * Attempt to register OLE for Vim. + */ + static void +install_OLE_register(void) +{ + char register_command_string[BUFSIZE + 30]; + + printf("\n--- Attempting to register Vim with OLE ---\n"); + printf("(There is no message whether this works or not.)\n"); + + sprintf(register_command_string, "\"%s\\gvim.exe\" -silent -register", installdir); + system(register_command_string); +} + +/* + * Remove the last part of directory "path[]" to get its parent, and put the + * result in "to[]". + */ + static void +dir_remove_last(const char *path, char to[BUFSIZE]) +{ + char c; + long last_char_to_copy; + long path_length = strlen(path); + + /* skip the last character just in case it is a '\\' */ + last_char_to_copy = path_length - 2; + c = path[last_char_to_copy]; + + while (c != '\\') + { + last_char_to_copy--; + c = path[last_char_to_copy]; + } + + strncpy(to, path, (size_t)last_char_to_copy); + to[last_char_to_copy] = NUL; +} + + static void +set_directories_text(int idx) +{ + int vimfiles_dir_choice = choices[idx].arg; + + if (vimfiles_dir_choice == (int)vimfiles_dir_none) + alloc_text(idx, "Do NOT create plugin directories%s", ""); + else + alloc_text(idx, "Create plugin directories: %s", + vimfiles_dir_choices[vimfiles_dir_choice]); +} + +/* + * To get the "real" home directory: + * - get value of $HOME + * - if not found, get value of $HOMEDRIVE$HOMEPATH + * - if not found, get value of $USERPROFILE + * + * This code is based on init_homedir() in misc1.c, keep in sync! + */ +static char *homedir = NULL; + + void +init_homedir(void) +{ + char *var; + char buf[MAX_PATH]; + + if (homedir != NULL) + { + free(homedir); + homedir = NULL; + } + + var = getenv("HOME"); + + /* + * Typically, $HOME is not defined on Windows, unless the user has + * specifically defined it for Vim's sake. However, on Windows NT + * platforms, $HOMEDRIVE and $HOMEPATH are automatically defined for + * each user. Try constructing $HOME from these. + */ + if (var == NULL || *var == NUL) + { + char *homedrive, *homepath; + + homedrive = getenv("HOMEDRIVE"); + homepath = getenv("HOMEPATH"); + if (homepath == NULL || *homepath == NUL) + homepath = "\\"; + if (homedrive != NULL + && strlen(homedrive) + strlen(homepath) < MAX_PATH) + { + sprintf(buf, "%s%s", homedrive, homepath); + if (buf[0] != NUL) + var = buf; + } + } + + if (var == NULL) + var = getenv("USERPROFILE"); + + /* + * Weird but true: $HOME may contain an indirect reference to another + * variable, esp. "%USERPROFILE%". Happens when $USERPROFILE isn't set + * when $HOME is being set. + */ + if (var != NULL && *var == '%') + { + char *p; + char *exp; + + p = strchr(var + 1, '%'); + if (p != NULL) + { + strncpy(buf, var + 1, p - (var + 1)); + buf[p - (var + 1)] = NUL; + exp = getenv(buf); + if (exp != NULL && *exp != NUL + && strlen(exp) + strlen(p) < MAX_PATH) + { + _snprintf(buf, MAX_PATH, "%s%s", exp, p + 1); + buf[MAX_PATH - 1] = NUL; + var = buf; + } + } + } + + if (var != NULL && *var == NUL) // empty is same as not set + var = NULL; + + if (var == NULL) + homedir = NULL; + else + homedir = _strdup(var); +} + +/* + * Change the directory that the vim plugin directories will be created in: + * $HOME, $VIM or nowhere. + */ + static void +change_directories_choice(int idx) +{ + int choice_count = TABLE_SIZE(vimfiles_dir_choices); + + /* Don't offer the $HOME choice if $HOME isn't set. */ + if (homedir == NULL) + --choice_count; + choices[idx].arg = get_choice(vimfiles_dir_choices, choice_count); + set_directories_text(idx); +} + +/* + * Create the plugin directories... + */ +/*ARGSUSED*/ + static void +install_vimfilesdir(int idx) +{ + int i; + int vimfiles_dir_choice = choices[idx].arg; + char *p; + char vimdir_path[BUFSIZE]; + char vimfiles_path[BUFSIZE]; + char tmp_dirname[BUFSIZE]; + + /* switch on the location that the user wants the plugin directories + * built in */ + switch (vimfiles_dir_choice) + { + case vimfiles_dir_vim: + { + /* Go to the %VIM% directory - check env first, then go one dir + * below installdir if there is no %VIM% environment variable. + * The accuracy of $VIM is checked in inspect_system(), so we + * can be sure it is ok to use here. */ + p = getenv("VIM"); + if (p == NULL) /* No $VIM in path */ + dir_remove_last(installdir, vimdir_path); + else + strcpy(vimdir_path, p); + break; + } + case vimfiles_dir_home: + { + // Find the $HOME directory. Its existence was already checked. + p = homedir; + if (p == NULL) + { + printf("Internal error: $HOME is NULL\n"); + p = "c:\\"; + } + strcpy(vimdir_path, p); + break; + } + case vimfiles_dir_none: + { + // Do not create vim plugin directory. + return; + } + } + + /* Now, just create the directory. If it already exists, it will fail + * silently. */ + sprintf(vimfiles_path, "%s\\vimfiles", vimdir_path); + vim_mkdir(vimfiles_path, 0755); + + printf("Creating the following directories in \"%s\":\n", vimfiles_path); + for (i = 0; i < TABLE_SIZE(vimfiles_subdirs); i++) + { + sprintf(tmp_dirname, "%s\\%s", vimfiles_path, vimfiles_subdirs[i]); + printf(" %s", vimfiles_subdirs[i]); + vim_mkdir(tmp_dirname, 0755); + } + printf("\n"); +} + +/* + * Add the creation of runtime files to the setup sequence. + */ + static void +init_directories_choice(void) +{ + struct stat st; + char tmp_dirname[BUFSIZE]; + char *p; + int vimfiles_dir_choice; + + choices[choice_count].text = alloc(150); + choices[choice_count].changefunc = change_directories_choice; + choices[choice_count].installfunc = install_vimfilesdir; + choices[choice_count].active = 1; + + // Check if the "compiler" directory already exists. That's a good + // indication that the plugin directories were already created. + if (getenv("HOME") != NULL) + { + vimfiles_dir_choice = (int)vimfiles_dir_home; + sprintf(tmp_dirname, "%s\\vimfiles\\compiler", getenv("HOME")); + if (stat(tmp_dirname, &st) == 0) + vimfiles_dir_choice = (int)vimfiles_dir_none; + } + else + { + vimfiles_dir_choice = (int)vimfiles_dir_vim; + p = getenv("VIM"); + if (p == NULL) // No $VIM in path, use the install dir. + dir_remove_last(installdir, tmp_dirname); + else + strcpy(tmp_dirname, p); + strcat(tmp_dirname, "\\vimfiles\\compiler"); + if (stat(tmp_dirname, &st) == 0) + vimfiles_dir_choice = (int)vimfiles_dir_none; + } + + choices[choice_count].arg = vimfiles_dir_choice; + set_directories_text(choice_count); + ++choice_count; +} + +/* + * Setup the choices and the default values. + */ + static void +setup_choices(void) +{ + /* install the batch files */ + init_bat_choices(); + + /* (over) write _vimrc file */ + init_vimrc_choices(); + + /* Whether to add Vim to the popup menu */ + init_popup_choice(); + + /* Whether to add Vim to the "Open With..." menu */ + init_openwith_choice(); + + /* Whether to add Vim to the Start Menu. */ + init_startmenu_choice(); + + /* Whether to add shortcuts to the Desktop. */ + init_shortcut_choices(); + + /* Whether to create the runtime directories. */ + init_directories_choice(); +} + + static void +print_cmd_line_help(void) +{ + printf("Vim installer non-interactive command line arguments:\n"); + printf("\n"); + printf("-create-batfiles [vim gvim evim view gview vimdiff gvimdiff]\n"); + printf(" Create .bat files for Vim variants in the Windows directory.\n"); + printf("-create-vimrc\n"); + printf(" Create a default _vimrc file if one does not already exist.\n"); + printf("-vimrc-remap [no|win]\n"); + printf(" Remap keys when creating a default _vimrc file.\n"); + printf("-vimrc-behave [unix|mswin|default]\n"); + printf(" Set mouse behavior when creating a default _vimrc file.\n"); + printf("-vimrc-compat [vi|vim|defaults|all]\n"); + printf(" Set Vi compatibility when creating a default _vimrc file.\n"); + printf("-install-popup\n"); + printf(" Install the Edit-with-Vim context menu entry\n"); + printf("-install-openwith\n"); + printf(" Add Vim to the \"Open With...\" context menu list\n"); + printf("-add-start-menu"); + printf(" Add Vim to the start menu\n"); + printf("-install-icons"); + printf(" Create icons for gVim executables on the desktop\n"); + printf("-create-directories [vim|home]\n"); + printf(" Create runtime directories to drop plugins into; in the $VIM\n"); + printf(" or $HOME directory\n"); + printf("-register-OLE"); + printf(" Ignored\n"); + printf("\n"); +} + +/* + * Setup installation choices based on command line switches + */ + static void +command_line_setup_choices(int argc, char **argv) +{ + int i, j; + + for (i = 1; i < argc; i++) + { + if (strcmp(argv[i], "-create-batfiles") == 0) + { + if (i + 1 == argc) + continue; + while (argv[i + 1][0] != '-' && i < argc) + { + i++; + for (j = 1; j < TARGET_COUNT; ++j) + if ((targets[j].exenamearg[0] == 'g' ? has_gvim : has_vim) + && strcmp(argv[i], targets[j].name) == 0) + { + init_bat_choice(j); + break; + } + if (j == TARGET_COUNT) + printf("%s is not a valid choice for -create-batfiles\n", + argv[i]); + + if (i + 1 == argc) + break; + } + } + else if (strcmp(argv[i], "-create-vimrc") == 0) + { + /* Setup default vimrc choices. If there is already a _vimrc file, + * it will NOT be overwritten. + */ + init_vimrc_choices(); + } + else if (strcmp(argv[i], "-vimrc-remap") == 0) + { + if (i + 1 == argc) + break; + i++; + if (strcmp(argv[i], "no") == 0) + remap_choice = remap_no; + else if (strcmp(argv[i], "win") == 0) + remap_choice = remap_win; + } + else if (strcmp(argv[i], "-vimrc-behave") == 0) + { + if (i + 1 == argc) + break; + i++; + if (strcmp(argv[i], "unix") == 0) + mouse_choice = mouse_xterm; + else if (strcmp(argv[i], "mswin") == 0) + mouse_choice = mouse_mswin; + else if (strcmp(argv[i], "default") == 0) + mouse_choice = mouse_default; + } + else if (strcmp(argv[i], "-vimrc-compat") == 0) + { + if (i + 1 == argc) + break; + i++; + if (strcmp(argv[i], "vi") == 0) + compat_choice = compat_vi; + else if (strcmp(argv[i], "vim") == 0) + compat_choice = compat_vim; + else if (strcmp(argv[i], "defaults") == 0) + compat_choice = compat_some_enhancements; + else if (strcmp(argv[i], "all") == 0) + compat_choice = compat_all_enhancements; + } + else if (strcmp(argv[i], "-install-popup") == 0) + { + init_popup_choice(); + } + else if (strcmp(argv[i], "-install-openwith") == 0) + { + init_openwith_choice(); + } + else if (strcmp(argv[i], "-add-start-menu") == 0) + { + init_startmenu_choice(); + } + else if (strcmp(argv[i], "-install-icons") == 0) + { + init_shortcut_choices(); + } + else if (strcmp(argv[i], "-create-directories") == 0) + { + int vimfiles_dir_choice = (int)vimfiles_dir_none; + + init_directories_choice(); + if (argv[i + 1][0] != '-') + { + i++; + if (strcmp(argv[i], "vim") == 0) + vimfiles_dir_choice = (int)vimfiles_dir_vim; + else if (strcmp(argv[i], "home") == 0) + { + if (homedir == NULL) // No $HOME in environment + vimfiles_dir_choice = (int)vimfiles_dir_none; + else + vimfiles_dir_choice = (int)vimfiles_dir_home; + } + else + { + printf("Unknown argument for -create-directories: %s\n", + argv[i]); + print_cmd_line_help(); + } + } + else /* No choice specified, default to vim directory */ + vimfiles_dir_choice = (int)vimfiles_dir_vim; + choices[choice_count - 1].arg = vimfiles_dir_choice; + } + else if (strcmp(argv[i], "-register-OLE") == 0) + { + /* This is always done when gvim is found */ + } + else /* Unknown switch */ + { + printf("Got unknown argument argv[%d] = %s\n", i, argv[i]); + print_cmd_line_help(); + } + } +} + + +/* + * Show a few screens full of helpful information. + */ + static void +show_help(void) +{ + static char *(items[]) = + { +"Installing .bat files\n" +"---------------------\n" +"The vim.bat file is written in one of the directories in $PATH.\n" +"This makes it possible to start Vim from the command line.\n" +"If vim.exe can be found in $PATH, the choice for vim.bat will not be\n" +"present. It is assumed you will use the existing vim.exe.\n" +"If vim.bat can already be found in $PATH this is probably for an old\n" +"version of Vim (but this is not checked!). You can overwrite it.\n" +"If no vim.bat already exists, you can select one of the directories in\n" +"$PATH for creating the batch file, or disable creating a vim.bat file.\n" +"\n" +"If you choose not to create the vim.bat file, Vim can still be executed\n" +"in other ways, but not from the command line.\n" +"\n" +"The same applies to choices for gvim, evim, (g)view, and (g)vimdiff.\n" +"The first item can be used to change the path for all of them.\n" +, +"Creating a _vimrc file\n" +"----------------------\n" +"The _vimrc file is used to set options for how Vim behaves.\n" +"The install program can create a _vimrc file with a few basic choices.\n" +"You can edit this file later to tune your preferences.\n" +"If you already have a _vimrc or .vimrc file it can be overwritten.\n" +"Don't do that if you have made changes to it.\n" +, +"Vim features\n" +"------------\n" +"(this choice is only available when creating a _vimrc file)\n" +"1. Vim can run in Vi-compatible mode. Many nice Vim features are then\n" +" disabled. Only choose Vi-compatible if you really need full Vi\n" +" compatibility.\n" +"2. Vim runs in not-Vi-compatible mode. Vim is still mostly Vi compatible,\n" +" but adds nice features like multi-level undo.\n" +"3. Running Vim with some enhancements is useful when you want some of\n" +" the nice Vim features, but have a slow computer and want to keep it\n" +" really fast.\n" +"4. Syntax highlighting shows many files in color. Not only does this look\n" +" nice, it also makes it easier to spot errors and you can work faster.\n" +" The other features include editing compressed files.\n" +, +"Windows key mapping\n" +"-------------------\n" +"(this choice is only available when creating a _vimrc file)\n" +"Under MS-Windows the CTRL-C key copies text to the clipboard and CTRL-V\n" +"pastes text from the clipboard. There are a few more keys like these.\n" +"Unfortunately, in Vim these keys normally have another meaning.\n" +"1. Choose to have the keys like they normally are in Vim (useful if you\n" +" also use Vim on other systems).\n" +"2. Choose to have the keys work like they are used on MS-Windows (useful\n" +" if you mostly work on MS-Windows).\n" +, +"Mouse use\n" +"---------\n" +"(this choice is only available when creating a _vimrc file)\n" +"The right mouse button can be used in two ways:\n" +"1. The Unix way is to extend an existing selection. The popup menu is\n" +" not available.\n" +"2. The MS-Windows way is to show a popup menu, which allows you to\n" +" copy/paste text, undo/redo, etc. Extending the selection can still be\n" +" done by keeping SHIFT pressed while using the left mouse button\n" +, +"Edit-with-Vim context menu entry\n" +"--------------------------------\n" +"(this choice is only available when gvim.exe and gvimext.dll are present)\n" +"You can associate different file types with Vim, so that you can (double)\n" +"click on a file to edit it with Vim. This means you have to individually\n" +"select each file type.\n" +"An alternative is the option offered here: Install an \"Edit with Vim\"\n" +"entry in the popup menu for the right mouse button. This means you can\n" +"edit any file with Vim.\n" +, +"\"Open With...\" context menu entry\n" +"--------------------------------\n" +"(this choice is only available when gvim.exe is present)\n" +"This option adds Vim to the \"Open With...\" entry in the popup menu for\n" +"the right mouse button. This also makes it possible to edit HTML files\n" +"directly from Internet Explorer.\n" +, +"Add Vim to the Start menu\n" +"-------------------------\n" +"In Windows 95 and later, Vim can be added to the Start menu. This will\n" +"create a submenu with an entry for vim, gvim, evim, vimdiff, etc..\n" +, +"Icons on the desktop\n" +"--------------------\n" +"(these choices are only available when installing gvim)\n" +"In Windows 95 and later, shortcuts (icons) can be created on the Desktop.\n" +, +"Create plugin directories\n" +"-------------------------\n" +"Plugin directories allow extending Vim by dropping a file into a directory.\n" +"This choice allows creating them in $HOME (if you have a home directory) or\n" +"$VIM (used for everybody on the system).\n" +, +NULL + }; + int i; + int c; + + rewind(stdin); + printf("\n"); + for (i = 0; items[i] != NULL; ++i) + { + puts(items[i]); + printf("Hit Enter to continue, b (back) or q (quit help): "); + c = getchar(); + rewind(stdin); + if (c == 'b' || c == 'B') + { + if (i == 0) + --i; + else + i -= 2; + } + if (c == 'q' || c == 'Q') + break; + printf("\n"); + } +} + +/* + * Install the choices. + */ + static void +install(void) +{ + int i; + + /* Install the selected choices. */ + for (i = 0; i < choice_count; ++i) + if (choices[i].installfunc != NULL && choices[i].active) + (choices[i].installfunc)(i); + + /* Add some entries to the registry, if needed. */ + if (install_popup + || install_openwith + || (need_uninstall_entry && interactive) + || !interactive) + install_registry(); + + /* Register gvim with OLE. */ + if (has_gvim) + install_OLE_register(); +} + +/* + * request_choice + */ + static void +request_choice(void) +{ + int i; + + printf("\n\nInstall will do for you:\n"); + for (i = 0; i < choice_count; ++i) + if (choices[i].active) + printf("%2d %s\n", i + 1, choices[i].text); + printf("To change an item, enter its number\n\n"); + printf("Enter item number, h (help), d (do it) or q (quit): "); +} + + int +main(int argc, char **argv) +{ + int i; + char buf[BUFSIZE]; + + /* + * Run interactively if there are no command line arguments. + */ + if (argc > 1) + interactive = 0; + else + interactive = 1; + + /* Initialize this program. */ + do_inits(argv); + init_homedir(); + + if (argc > 1 && strcmp(argv[1], "-uninstall-check") == 0) + { + /* Only check for already installed Vims. Used by NSIS installer. */ + i = uninstall_check(1); + + /* Find the value of $VIM, because NSIS isn't able to do this by + * itself. */ + get_vim_env(); + + /* When nothing found exit quietly. If something found wait for + * a little while, so that the user can read the messages. */ + if (i && _isatty(1)) + sleep(3); + exit(0); + } + + printf("This program sets up the installation of Vim " + VIM_VERSION_MEDIUM "\n\n"); + + /* Check if the user unpacked the archives properly. */ + check_unpack(); + + /* Check for already installed Vims. */ + if (interactive) + uninstall_check(0); + + /* Find out information about the system. */ + inspect_system(); + + if (interactive) + { + /* Setup all the choices. */ + setup_choices(); + + /* Let the user change choices and finally install (or quit). */ + for (;;) + { + request_choice(); + rewind(stdin); + if (scanf("%99s", buf) == 1) + { + if (isdigit(buf[0])) + { + /* Change a choice. */ + i = atoi(buf); + if (i > 0 && i <= choice_count && choices[i - 1].active) + (choices[i - 1].changefunc)(i - 1); + else + printf("\nIllegal choice\n"); + } + else if (buf[0] == 'h' || buf[0] == 'H') + { + /* Help */ + show_help(); + } + else if (buf[0] == 'd' || buf[0] == 'D') + { + /* Install! */ + install(); + printf("\nThat finishes the installation. Happy Vimming!\n"); + break; + } + else if (buf[0] == 'q' || buf[0] == 'Q') + { + /* Quit */ + printf("\nExiting without anything done\n"); + break; + } + else + printf("\nIllegal choice\n"); + } + } + printf("\n"); + myexit(0); + } + else + { + /* + * Run non-interactive - setup according to the command line switches + */ + command_line_setup_choices(argc, argv); + install(); + + /* Avoid that the user has to hit Enter, just wait a little bit to + * allow reading the messages. */ + sleep(2); + } + + return 0; +} diff --git a/src/dosinst.h b/src/dosinst.h new file mode 100644 index 0000000..59de932 --- /dev/null +++ b/src/dosinst.h @@ -0,0 +1,531 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ +/* + * dosinst.h: Common code for dosinst.c and uninstal.c + */ + +/* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */ +#if _MSC_VER >= 1400 +# define _CRT_SECURE_NO_DEPRECATE +# define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#include +#include +#include +#include +#include + +#ifndef UNIX_LINT +# include "vimio.h" +# include + +# include + +# include +# include +#endif + +#ifdef UNIX_LINT +/* Running lint on Unix: Some things are missing. */ +char *searchpath(char *name); +#endif + +#if defined(UNIX_LINT) +# include +# include +#endif + +#include "version.h" + +#if defined(UNIX_LINT) +# define vim_mkdir(x, y) mkdir((char *)(x), y) +#else +# ifndef __BORLANDC__ +# define vim_mkdir(x, y) _mkdir((char *)(x)) +# else +# define vim_mkdir(x, y) mkdir((char *)(x)) +# endif +#endif + +#define sleep(n) Sleep((n) * 1000) + +/* ---------------------------------------- */ + + +#define BUFSIZE 512 /* long enough to hold a file name path */ +#define NUL 0 + +#define FAIL 0 +#define OK 1 + +#ifndef FALSE +# define FALSE 0 +#endif +#ifndef TRUE +# define TRUE 1 +#endif + +/* + * Modern way of creating registry entries, also works on 64 bit windows when + * compiled as a 32 bit program. + */ +# ifndef KEY_WOW64_64KEY +# define KEY_WOW64_64KEY 0x0100 +# endif +# ifndef KEY_WOW64_32KEY +# define KEY_WOW64_32KEY 0x0200 +# endif + +#define VIM_STARTMENU "Programs\\Vim " VIM_VERSION_SHORT + +int interactive; /* non-zero when running interactively */ + +/* + * Call malloc() and exit when out of memory. + */ + static void * +alloc(int len) +{ + char *s; + + s = malloc(len); + if (s == NULL) + { + printf("ERROR: out of memory\n"); + exit(1); + } + return (void *)s; +} + +/* + * The toupper() in Bcc 5.5 doesn't work, use our own implementation. + */ + static int +mytoupper(int c) +{ + if (c >= 'a' && c <= 'z') + return c - 'a' + 'A'; + return c; +} + + static void +myexit(int n) +{ + if (!interactive) + { + /* Present a prompt, otherwise error messages can't be read. */ + printf("Press Enter to continue\n"); + rewind(stdin); + (void)getchar(); + } + exit(n); +} + + +typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); +/* + * Check if this is a 64-bit OS. + */ + static BOOL +is_64bit_os(void) +{ +#ifdef _WIN64 + return TRUE; +#else + BOOL bIsWow64 = FALSE; + LPFN_ISWOW64PROCESS pIsWow64Process; + + pIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( + GetModuleHandle("kernel32"), "IsWow64Process"); + if (pIsWow64Process != NULL) + pIsWow64Process(GetCurrentProcess(), &bIsWow64); + return bIsWow64; +#endif +} + +#ifdef __BORLANDC__ +/* Borland defines its own searchpath() in dir.h */ +# include +#else + static char * +searchpath(char *name) +{ + static char widename[2 * BUFSIZE]; + static char location[2 * BUFSIZE + 2]; + + /* There appears to be a bug in FindExecutableA() on Windows NT. + * Use FindExecutableW() instead... */ + MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1, + (LPWSTR)widename, BUFSIZE); + if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", + (LPWSTR)location) > (HINSTANCE)32) + { + WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, + (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); + return widename; + } + return NULL; +} +#endif + +/* + * Call searchpath() and save the result in allocated memory, or return NULL. + */ + static char * +searchpath_save(char *name) +{ + char *p; + char *s; + + p = searchpath(name); + if (p == NULL) + return NULL; + s = alloc(strlen(p) + 1); + strcpy(s, p); + return s; +} + + +#ifndef CSIDL_COMMON_PROGRAMS +# define CSIDL_COMMON_PROGRAMS 0x0017 +#endif +#ifndef CSIDL_COMMON_DESKTOPDIRECTORY +# define CSIDL_COMMON_DESKTOPDIRECTORY 0x0019 +#endif + +/* + * Get the path to a requested Windows shell folder. + * + * Return FAIL on error, OK on success + */ + int +get_shell_folder_path( + char *shell_folder_path, + const char *shell_folder_name) +{ + /* + * The following code was successfully built with make_mvc.mak. + * The resulting executable worked on Windows 95, Millennium Edition, and + * 2000 Professional. But it was changed after testing... + */ + LPITEMIDLIST pidl = 0; /* Pointer to an Item ID list allocated below */ + LPMALLOC pMalloc; /* Pointer to an IMalloc interface */ + int csidl; + int alt_csidl = -1; + static int desktop_csidl = -1; + static int programs_csidl = -1; + int *pcsidl; + int r; + + if (strcmp(shell_folder_name, "desktop") == 0) + { + pcsidl = &desktop_csidl; + csidl = CSIDL_COMMON_DESKTOPDIRECTORY; + alt_csidl = CSIDL_DESKTOP; + } + else if (strncmp(shell_folder_name, "Programs", 8) == 0) + { + pcsidl = &programs_csidl; + csidl = CSIDL_COMMON_PROGRAMS; + alt_csidl = CSIDL_PROGRAMS; + } + else + { + printf("\nERROR (internal) unrecognised shell_folder_name: \"%s\"\n\n", + shell_folder_name); + return FAIL; + } + + /* Did this stuff before, use the same ID again. */ + if (*pcsidl >= 0) + { + csidl = *pcsidl; + alt_csidl = -1; + } + +retry: + /* Initialize pointer to IMalloc interface */ + if (NOERROR != SHGetMalloc(&pMalloc)) + { + printf("\nERROR getting interface for shell_folder_name: \"%s\"\n\n", + shell_folder_name); + return FAIL; + } + + /* Get an ITEMIDLIST corresponding to the folder code */ + if (NOERROR != SHGetSpecialFolderLocation(0, csidl, &pidl)) + { + if (alt_csidl < 0 || NOERROR != SHGetSpecialFolderLocation(0, + alt_csidl, &pidl)) + { + printf("\nERROR getting ITEMIDLIST for shell_folder_name: \"%s\"\n\n", + shell_folder_name); + return FAIL; + } + csidl = alt_csidl; + alt_csidl = -1; + } + + /* Translate that ITEMIDLIST to a string */ + r = SHGetPathFromIDList(pidl, shell_folder_path); + + /* Free the data associated with pidl */ + pMalloc->lpVtbl->Free(pMalloc, pidl); + /* Release the IMalloc interface */ + pMalloc->lpVtbl->Release(pMalloc); + + if (!r) + { + if (alt_csidl >= 0) + { + /* We probably get here for Windows 95: the "all users" + * desktop/start menu entry doesn't exist. */ + csidl = alt_csidl; + alt_csidl = -1; + goto retry; + } + printf("\nERROR translating ITEMIDLIST for shell_folder_name: \"%s\"\n\n", + shell_folder_name); + return FAIL; + } + + /* If there is an alternative: verify we can write in this directory. + * This should cause a retry when the "all users" directory exists but we + * are a normal user and can't write there. */ + if (alt_csidl >= 0) + { + char tbuf[BUFSIZE]; + FILE *fd; + + strcpy(tbuf, shell_folder_path); + strcat(tbuf, "\\vim write test"); + fd = fopen(tbuf, "w"); + if (fd == NULL) + { + csidl = alt_csidl; + alt_csidl = -1; + goto retry; + } + fclose(fd); + unlink(tbuf); + } + + /* + * Keep the found csidl for next time, so that we don't have to do the + * write test every time. + */ + if (*pcsidl < 0) + *pcsidl = csidl; + + if (strncmp(shell_folder_name, "Programs\\", 9) == 0) + strcat(shell_folder_path, shell_folder_name + 8); + + return OK; +} + +/* + * List of targets. The first one (index zero) is used for the default path + * for the batch files. + */ +#define TARGET_COUNT 9 + +struct +{ + char *name; /* Vim exe name (without .exe) */ + char *batname; /* batch file name */ + char *lnkname; /* shortcut file name */ + char *exename; /* exe file name */ + char *exenamearg; /* exe file name when using exearg */ + char *exearg; /* argument for vim.exe or gvim.exe */ + char *oldbat; /* path to existing xxx.bat or NULL */ + char *oldexe; /* path to existing xxx.exe or NULL */ + char batpath[BUFSIZE]; /* path of batch file to create; not + created when it's empty */ +} targets[TARGET_COUNT] = +{ + {"all", "batch files"}, + {"vim", "vim.bat", "Vim.lnk", + "vim.exe", "vim.exe", ""}, + {"gvim", "gvim.bat", "gVim.lnk", + "gvim.exe", "gvim.exe", ""}, + {"evim", "evim.bat", "gVim Easy.lnk", + "evim.exe", "gvim.exe", "-y"}, + {"view", "view.bat", "Vim Read-only.lnk", + "view.exe", "vim.exe", "-R"}, + {"gview", "gview.bat", "gVim Read-only.lnk", + "gview.exe", "gvim.exe", "-R"}, + {"vimdiff", "vimdiff.bat", "Vim Diff.lnk", + "vimdiff.exe","vim.exe", "-d"}, + {"gvimdiff","gvimdiff.bat", "gVim Diff.lnk", + "gvimdiff.exe","gvim.exe", "-d"}, + {"vimtutor","vimtutor.bat", "Vim tutor.lnk", + "vimtutor.bat", "vimtutor.bat", ""}, +}; + +#define ICON_COUNT 3 +char *(icon_names[ICON_COUNT]) = + {"gVim " VIM_VERSION_SHORT, + "gVim Easy " VIM_VERSION_SHORT, + "gVim Read only " VIM_VERSION_SHORT}; +char *(icon_link_names[ICON_COUNT]) = + {"gVim " VIM_VERSION_SHORT ".lnk", + "gVim Easy " VIM_VERSION_SHORT ".lnk", + "gVim Read only " VIM_VERSION_SHORT ".lnk"}; + +/* This is only used for dosinst.c. */ +#if defined(DOSINST) +/* + * Run an external command and wait for it to finish. + */ + static void +run_command(char *cmd) +{ + char *cmd_path; + char cmd_buf[BUFSIZE]; + char *p; + + /* On WinNT, 'start' is a shell built-in for cmd.exe rather than an + * executable (start.exe) like in Win9x. */ + cmd_path = searchpath_save("cmd.exe"); + if (cmd_path != NULL) + { + /* There is a cmd.exe, so this might be Windows NT. If it is, + * we need to call cmd.exe explicitly. If it is a later OS, + * calling cmd.exe won't hurt if it is present. + * Also, "start" on NT expects a window title argument. + */ + /* Replace the slashes with backslashes. */ + while ((p = strchr(cmd_path, '/')) != NULL) + *p = '\\'; + sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd); + free(cmd_path); + } + else + { + /* No cmd.exe, just make the call and let the system handle it. */ + sprintf(cmd_buf, "start /w %s", cmd); + } + system(cmd_buf); +} +#endif + +/* + * Append a backslash to "name" if there isn't one yet. + */ + void +add_pathsep(char *name) +{ + int len = strlen(name); + + if (len > 0 && name[len - 1] != '\\' && name[len - 1] != '/') + strcat(name, "\\"); +} + +/* + * The normal chdir() does not change the default drive. This one does. + */ +/*ARGSUSED*/ + int +change_drive(int drive) +{ + char temp[3] = "-:"; + temp[0] = (char)(drive + 'A' - 1); + return !SetCurrentDirectory(temp); +} + +/* + * Change directory to "path". + * Return 0 for success, -1 for failure. + */ + int +mch_chdir(char *path) +{ + if (path[0] == NUL) /* just checking... */ + return 0; + if (path[1] == ':') /* has a drive name */ + { + if (change_drive(mytoupper(path[0]) - 'A' + 1)) + return -1; /* invalid drive name */ + path += 2; + } + if (*path == NUL) /* drive name only */ + return 0; + return chdir(path); /* let the normal chdir() do the rest */ +} + +/* + * Expand the executable name into a full path name. + */ +#if defined(__BORLANDC__) + +/* Only Borland C++ has this. */ +# define my_fullpath(b, n, l) _fullpath(b, n, l) + +#else + static char * +my_fullpath(char *buf, char *fname, int len) +{ + /* Only GetModuleFileName() will get the long file name path. + * GetFullPathName() may still use the short (FAT) name. */ + DWORD len_read = GetModuleFileName(NULL, buf, (size_t)len); + + return (len_read > 0 && len_read < (DWORD)len) ? buf : NULL; +} +#endif + +/* + * Remove the tail from a file or directory name. + * Puts a NUL on the last '/' or '\'. + */ + static void +remove_tail(char *path) +{ + int i; + + for (i = strlen(path) - 1; i > 0; --i) + if (path[i] == '/' || path[i] == '\\') + { + path[i] = NUL; + break; + } +} + + +char installdir[BUFSIZE]; /* top of the installation dir, where the + install.exe is located, E.g.: + "c:\vim\vim60" */ +int runtimeidx; /* index in installdir[] where "vim60" starts */ +char *sysdrive; /* system drive or "c:\" */ + +/* + * Setup for using this program. + * Sets "installdir[]". + */ + static void +do_inits(char **argv) +{ + /* Find out the full path of our executable. */ + if (my_fullpath(installdir, argv[0], BUFSIZE) == NULL) + { + printf("ERROR: Cannot get name of executable\n"); + myexit(1); + } + /* remove the tail, the executable name "install.exe" */ + remove_tail(installdir); + + /* change to the installdir */ + mch_chdir(installdir); + + /* Find the system drive. Only used for searching the Vim executable, not + * very important. */ + sysdrive = getenv("SYSTEMDRIVE"); + if (sysdrive == NULL || *sysdrive == NUL) + sysdrive = "C:\\"; +} diff --git a/src/edit.c b/src/edit.c new file mode 100644 index 0000000..eac4803 --- /dev/null +++ b/src/edit.c @@ -0,0 +1,10296 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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 "vim.h" + +#ifdef FEAT_INS_EXPAND +/* + * definitions used for CTRL-X submode + */ +# define CTRL_X_WANT_IDENT 0x100 + +# define CTRL_X_NORMAL 0 /* CTRL-N CTRL-P completion, default */ +# 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_EVAL 16 /* for builtin function complete() */ + +# define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] +# define CTRL_X_MODE_LINE_OR_EVAL(m) ((m) == CTRL_X_WHOLE_LINE || (m) == CTRL_X_EVAL) + +/* Message for CTRL-X mode, index is ctrl_x_mode. */ +static char *ctrl_x_msgs[] = +{ + N_(" Keyword completion (^N^P)"), /* CTRL_X_NORMAL, ^P/^N compl. */ + N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), + NULL, /* CTRL_X_SCROLL: depends on state */ + 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, /* CTRL_X_FINISHED */ + 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)"), + NULL, /* CTRL_X_EVAL doesn't use msg. */ +}; + +static char e_hitend[] = N_("Hit end of paragraph"); +# ifdef FEAT_COMPL_FUNC +static char e_complwin[] = N_("E839: Completion function changed window"); +static char e_compldel[] = N_("E840: Completion function deleted text"); +# endif + +/* + * 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; +static compl_T *compl_old_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_no_insert = FALSE; /* FALSE: select & insert + TRUE: noinsert */ +static int compl_no_select = FALSE; /* FALSE: select & insert + TRUE: noselect */ + +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; + +/* Which Ctrl-X mode are we in? */ +static int ctrl_x_mode = CTRL_X_NORMAL; + +/* 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 int compl_opt_suppress_empty = 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 void ins_compl_longest_match(compl_T *match); +static void ins_compl_del_pum(void); +static int pum_wanted(void); +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); +# if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) +static void ins_compl_add_list(list_T *list); +static void ins_compl_add_dict(dict_T *dict); +# endif +static void ins_compl_delete(void); +static void ins_compl_insert(int in_compl_func); +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_complete(int c, int enable_pum); +static void show_pum(int prev_w_wrow, int prev_w_leftcol); +static unsigned quote_meta(char_u *dest, char_u *str, int len); +#endif /* FEAT_INS_EXPAND */ + +#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); +#ifdef FEAT_JOB_CHANNEL +static void init_prompt(int cmdchar_todo); +#endif +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 start_arrow_common(pos_T *end_insert_pos, int change); +#ifdef FEAT_SPELL +static void check_spell_redraw(void); +static void spell_back_to_badword(void); +static int spell_bad_len = 0; /* length of located bad word */ +#endif +static void stop_insert(pos_T *end_insert_pos, int esc, int nomove); +static int echeck_abbr(int); +static void replace_join(int off); +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 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); +#ifdef FEAT_RIGHTLEFT +static void ins_ctrl_(void); +#endif +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); +#ifdef FEAT_MOUSE +static void ins_mouse(int c); +static void ins_mousescroll(int dir); +#endif +#if defined(FEAT_GUI_TABLINE) || defined(PROTO) +static void ins_tabline(int c); +#endif +static void ins_left(int end_change); +static void ins_home(int c); +static void ins_end(int c); +static void ins_s_left(void); +static void ins_right(int end_change); +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); +#ifdef FEAT_DND +static void ins_drop(void); +#endif +static int ins_tab(void); +static int ins_eol(int c); +#ifdef FEAT_DIGRAPHS +static int ins_digraph(void); +#endif +static int ins_ctrl_ey(int tc); +#ifdef FEAT_SMARTINDENT +static void ins_try_si(int c); +#endif +#if defined(FEAT_EVAL) +static char_u *do_insert_char_pre(int c); +#endif +static int ins_apply_autocmds(event_T event); + +static colnr_T Insstart_textlen; /* length of line when insert started */ +static colnr_T Insstart_blank_vcol; /* vcol for first inserted blank */ +static int 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() */ + +#ifdef FEAT_CINDENT +static int can_cindent; /* may do cindenting on this line */ +#endif + +static int old_indent = 0; /* for ^^D command in insert mode */ + +#ifdef FEAT_RIGHTLEFT +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 */ +#endif + +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 */ +static int dont_sync_undo = FALSE; /* CTRL-G U prevents syncing undo for + the next left/right cursor */ + +/* + * edit(): Start inserting text. + * + * "cmdchar" can be: + * 'i' normal insert command + * 'a' normal append command + * K_PS bracketed paste + * '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 = 0; + int mincol; + static linenr_T o_lnum = 0; + int i; + int did_backspace = TRUE; /* previous char was backspace */ +#ifdef FEAT_CINDENT + int line_is_white = FALSE; /* line is empty before insert */ +#endif + linenr_T old_topline = 0; /* topline before insertion */ +#ifdef FEAT_DIFF + int old_topfill = -1; +#endif + int inserted_space = FALSE; /* just inserted a space */ + int replaceState = REPLACE; + int nomove = FALSE; /* don't move cursor on return */ +#ifdef FEAT_JOB_CHANNEL + int cmdchar_todo = cmdchar; +#endif + + /* 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; + } + +#ifdef FEAT_INS_EXPAND + /* 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 */ +#endif + + /* + * Trigger InsertEnter autocommands. Do not do this for "r" or "grx". + */ + if (cmdchar != 'r' && cmdchar != 'v') + { + pos_T save_cursor = curwin->w_cursor; + +#ifdef FEAT_EVAL + 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 */ +#endif + ins_apply_autocmds(EVENT_INSERTENTER); + + /* 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 (!EQUAL_POS(curwin->w_cursor, save_cursor) +#ifdef FEAT_EVAL + && *get_vim_var_str(VV_CHAR) == NUL +#endif + && 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; + } + } + +#ifdef FEAT_CONCEAL + /* Check if the cursor line needs redrawing before changing State. If + * 'concealcursor' is "n" it needs to be redrawn without concealing. */ + conceal_check_cursor_line(); +#endif + +#ifdef FEAT_MOUSE + /* + * 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 +#endif + { + 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 + { + if (cmdchar == K_PS) + AppendCharToRedobuff('a'); + else + AppendCharToRedobuff(cmdchar); + if (cmdchar == 'g') /* "gI" command */ + AppendCharToRedobuff('I'); + else if (cmdchar == 'r') /* "r" command */ + count = 1; /* insert only one */ + } + } + + if (cmdchar == 'R') + { +#ifdef FEAT_FKMAP + if (p_fkmap && p_ri) + { + beep_flush(); + emsg(farsi_text_3); /* encoded in Farsi */ + State = INSERT; + } + else +#endif + 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 HAVE_INPUT_METHOD + im_set_active(curbuf->b_p_iminsert == B_IMODE_IM); +#endif + +#ifdef FEAT_MOUSE + setmouse(); +#endif +#ifdef FEAT_CMDL_INFO + clear_showcmd(); +#endif +#ifdef FEAT_RIGHTLEFT + /* 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; +#endif + if (!p_ek) + /* Disable bracketed paste mode, we won't recognize the escape + * sequences. */ + out_str(T_BD); + + /* + * Handle restarting Insert mode. + * Don't do this for "CTRL-O ." (repeat an insert): In that case we get + * here with something in the stuff buffer. + */ + if (restart_edit != 0 && stuff_empty()) + { +#ifdef FEAT_MOUSE + /* + * 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 +#endif + 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; + +#ifdef FEAT_MOUSE + where_paste_started.lnum = 0; +#endif +#ifdef FEAT_CINDENT + can_cindent = TRUE; +#endif +#ifdef FEAT_FOLDING + /* The cursor line is not in a closed fold, unless 'insertmode' is set or + * restarting. */ + if (!p_im && did_restart_edit == 0) + foldOpenCursor(); +#endif + + /* + * 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); + +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may show different cursor shape */ +#endif +#ifdef FEAT_DIGRAPHS + do_digraph(-1); /* clear digraphs */ +#endif + + /* + * 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); + vim_free(ptr); + } + + old_indent = 0; + + /* + * Main loop in Insert mode: repeat until Insert mode is left. + */ + for (;;) + { +#ifdef FEAT_RIGHTLEFT + if (!revins_legal) + revins_scol = -1; /* reset on illegal motions */ + else + revins_legal = 0; +#endif + if (arrow_used) /* don't repeat insert when arrow key used */ + count = 0; + + if (update_Insstart_orig) + Insstart_orig = Insstart; + + if (stop_insert_mode +#ifdef FEAT_INS_EXPAND + && !pum_visible() +#endif + ) + { + /* ":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; + +#ifdef FEAT_GUI + /* When 'mousefocus' is set a mouse movement may have taken us to + * another window. "need_mouse_correct" may then be set because of an + * autocommand. */ + if (need_mouse_correct) + gui_mouse_correct(); +#endif + +#ifdef FEAT_FOLDING + /* 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(); +#endif + +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf)) + { + init_prompt(cmdchar_todo); + cmdchar_todo = NUL; + } +#endif + + /* + * 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 +#ifdef FEAT_DIFF + && curwin->w_topfill == old_topfill +#endif + ) + { + mincol = curwin->w_wcol; + validate_cursor_col(); + + if ( +#ifdef FEAT_VARTABS + (int)curwin->w_wcol < mincol - tabstop_at( + get_nolist_virtcol(), curbuf->b_p_ts, + curbuf->b_p_vts_array) +#else + (int)curwin->w_wcol < mincol - curbuf->b_p_ts +#endif + && curwin->w_wrow == W_WINROW(curwin) + + curwin->w_height - 1 - get_scrolloff_value() + && (curwin->w_cursor.lnum != curwin->w_topline +#ifdef FEAT_DIFF + || curwin->w_topfill > 0 +#endif + )) + { +#ifdef FEAT_DIFF + if (curwin->w_topfill > 0) + --curwin->w_topfill; + else +#endif +#ifdef FEAT_FOLDING + if (hasFolding(curwin->w_topline, NULL, &old_topline)) + set_topline(curwin, old_topline + 1); + else +#endif + 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; +#ifdef FEAT_DIFF + old_topfill = curwin->w_topfill; +#endif + +#ifdef USE_ON_FLY_SCROLL + dont_scroll = FALSE; /* allow scrolling here */ +#endif + + /* + * Get a character for Insert mode. Ignore K_IGNORE and K_NOP. + */ + if (c != K_CURSORHOLD) + lastc = c; /* remember the previous char for CTRL-D */ + + /* After using CTRL-G U the next cursor key will not break undo. */ + if (dont_sync_undo == MAYBE) + dont_sync_undo = TRUE; + else + dont_sync_undo = FALSE; + if (cmdchar == K_PS) + /* Got here from normal mode when bracketed paste started. */ + c = K_PS; + else + do + { + c = safe_vgetc(); + + if (stop_insert_mode) + { + // Insert mode ended, possibly from a callback. + count = 0; + nomove = TRUE; + goto doESCkey; + } + } while (c == K_IGNORE || c == K_NOP); + + /* Don't want K_CURSORHOLD for the second key, e.g., after CTRL-V. */ + did_cursorhold = TRUE; + +#ifdef FEAT_RIGHTLEFT + if (p_hkmap && KeyTyped) + c = hkmap(c); /* Hebrew mode mapping */ +#endif +#ifdef FEAT_FKMAP + if (p_fkmap && KeyTyped) + c = fkmap(c); /* Farsi mode mapping */ +#endif + +#ifdef FEAT_INS_EXPAND + /* + * 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_LINE_OR_EVAL(ctrl_x_mode) + || (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)) + { +#if defined(FEAT_EVAL) + /* 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)); + vim_free(str); + } + else +#endif + 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))) + && stop_arrow() == OK) + { + ins_compl_delete(); + ins_compl_insert(FALSE); + } + } + } + + /* 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; +#endif + + /* 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; + } + } + +#ifdef FEAT_DIGRAPHS + c = do_digraph(c); +#endif + +#ifdef FEAT_INS_EXPAND + if ((c == Ctrl_V || c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) + goto docomplete; +#endif + if (c == Ctrl_V || c == Ctrl_Q) + { + ins_ctrl_v(); + c = Ctrl_V; /* pretend CTRL-V is last typed character */ + continue; + } + +#ifdef FEAT_CINDENT + if (cindent_on() +# ifdef FEAT_INS_EXPAND + && ctrl_x_mode == 0 +# endif + ) + { + /* 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(); + } +#endif + +#ifdef FEAT_RIGHTLEFT + 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; + } +#endif + + /* + * 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 */ +#ifdef FEAT_CMDWIN + 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; + } +#endif +#ifdef FEAT_JOB_CHANNEL + if (c == Ctrl_C && bt_prompt(curbuf)) + { + if (invoke_prompt_interrupt()) + { + if (!bt_prompt(curbuf)) + // buffer changed to a non-prompt buffer, get out of + // Insert mode + goto doESCkey; + break; + } + } +#endif + +#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(BO_IM); + 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)) + { + // When CTRL-C was typed got_int will be set, with the result + // that the autocommands won't be executed. When mapped got_int + // is not set, but let's keep the behavior the same. + if (cmdchar != 'r' && cmdchar != 'v' && c != Ctrl_C) + ins_apply_autocmds(EVENT_INSERTLEAVE); + 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 */ + do_cmdline_cmd((char_u *)"stop"); +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may need to update cursor shape */ +#endif + continue; + + case Ctrl_O: /* execute one command */ +#ifdef FEAT_COMPL_FUNC + if (ctrl_x_mode == CTRL_X_OMNI) + goto docomplete; +#endif + 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; + +#ifdef FEAT_NETBEANS_INTG + case K_F21: /* NetBeans command */ + ++no_mapping; /* don't map the next key hits */ + i = plain_vgetc(); + --no_mapping; + netbeans_keycommand(i); + break; +#endif + + 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; + +#ifdef FEAT_RIGHTLEFT + case Ctrl__: /* switch between languages */ + if (!p_ari) + goto normalchar; + ins_ctrl_(); + break; +#endif + + case Ctrl_D: /* Make indent one shiftwidth smaller. */ +#if defined(FEAT_INS_EXPAND) && defined(FEAT_FIND_ID) + if (ctrl_x_mode == CTRL_X_PATH_DEFINES) + goto docomplete; +#endif + /* FALLTHROUGH */ + + case Ctrl_T: /* Make indent one shiftwidth greater. */ +# ifdef FEAT_INS_EXPAND + if (c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) + { + if (has_compl_option(FALSE)) + goto docomplete; + break; + } +# endif + 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 */ +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf) && (mod_mask & MOD_MASK_SHIFT) == 0) + { + // In a prompt window CTRL-W is used for window commands. + // Use Shift-CTRL-W to delete a word. + stuffcharReadbuff(Ctrl_W); + restart_edit = 'A'; + nomove = TRUE; + count = 0; + goto doESCkey; + } +#endif + did_backspace = ins_bs(c, BACKSPACE_WORD, &inserted_space); + auto_format(FALSE, TRUE); + break; + + case Ctrl_U: /* delete all inserted text in current line */ +# ifdef FEAT_COMPL_FUNC + /* CTRL-X CTRL-U completes with 'completefunc'. */ + if (ctrl_x_mode == CTRL_X_FUNCTION) + goto docomplete; +# endif + did_backspace = ins_bs(c, BACKSPACE_LINE, &inserted_space); + auto_format(FALSE, TRUE); + inserted_space = FALSE; + break; + +#ifdef FEAT_MOUSE + case K_LEFTMOUSE: /* mouse keys */ + case K_LEFTMOUSE_NM: + case K_LEFTDRAG: + case K_LEFTRELEASE: + case K_LEFTRELEASE_NM: + case K_MOUSEMOVE: + 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; +#endif + case K_PS: + bracketed_paste(PASTE_INSERT, FALSE, NULL); + if (cmdchar == K_PS) + /* invoked from normal mode, bail out */ + goto doESCkey; + break; + case K_PE: + /* Got K_PE without K_PS, ignore. */ + break; + +#ifdef FEAT_GUI_TABLINE + case K_TABLINE: + case K_TABMENU: + ins_tabline(c); + break; +#endif + + case K_IGNORE: /* Something mapped to nothing */ + break; + + case K_CURSORHOLD: /* Didn't type something for a while. */ + ins_apply_autocmds(EVENT_CURSORHOLDI); + did_cursorhold = TRUE; + break; + +#ifdef FEAT_GUI_W32 + /* On Win32 ignore , we get it when closing the window was + * cancelled. */ + case K_F4: + if (mod_mask != MOD_MASK_ALT) + goto normalchar; + break; +#endif + +#ifdef FEAT_GUI + case K_VER_SCROLLBAR: + ins_scroll(); + break; + + case K_HOR_SCROLLBAR: + ins_horscroll(); + break; +#endif + + 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(dont_sync_undo == FALSE); + 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(dont_sync_undo == FALSE); + break; + + case K_S_RIGHT: /* */ + case K_C_RIGHT: + ins_s_right(); + break; + + case K_UP: /* */ +#ifdef FEAT_INS_EXPAND + if (pum_visible()) + goto docomplete; +#endif + if (mod_mask & MOD_MASK_SHIFT) + ins_pageup(); + else + ins_up(FALSE); + break; + + case K_S_UP: /* */ + case K_PAGEUP: + case K_KPAGEUP: +#ifdef FEAT_INS_EXPAND + if (pum_visible()) + goto docomplete; +#endif + ins_pageup(); + break; + + case K_DOWN: /* */ +#ifdef FEAT_INS_EXPAND + if (pum_visible()) + goto docomplete; +#endif + if (mod_mask & MOD_MASK_SHIFT) + ins_pagedown(); + else + ins_down(FALSE); + break; + + case K_S_DOWN: /* */ + case K_PAGEDOWN: + case K_KPAGEDOWN: +#ifdef FEAT_INS_EXPAND + if (pum_visible()) + goto docomplete; +#endif + ins_pagedown(); + break; + +#ifdef FEAT_DND + case K_DROP: /* drag-n-drop event */ + ins_drop(); + break; +#endif + + case K_S_TAB: /* When not mapped, use like a normal TAB */ + c = TAB; + /* FALLTHROUGH */ + + case TAB: /* TAB or Complete patterns along path */ +#if defined(FEAT_INS_EXPAND) && defined(FEAT_FIND_ID) + if (ctrl_x_mode == CTRL_X_PATH_PATTERNS) + goto docomplete; +#endif + 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: +#if defined(FEAT_QUICKFIX) + /* 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; + } +#endif +#ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + { + /* Execute the command in the cmdline window. */ + cmdwin_result = CAR; + goto doESCkey; + } +#endif +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf)) + { + invoke_prompt_callback(); + if (!bt_prompt(curbuf)) + // buffer changed to a non-prompt buffer, get out of + // Insert mode + goto doESCkey; + break; + } +#endif + if (ins_eol(c) == FAIL && !p_im) + goto doESCkey; /* out of memory */ + auto_format(FALSE, FALSE); + inserted_space = FALSE; + break; + +#if defined(FEAT_DIGRAPHS) || defined(FEAT_INS_EXPAND) + case Ctrl_K: /* digraph or keyword completion */ +# ifdef FEAT_INS_EXPAND + if (ctrl_x_mode == CTRL_X_DICTIONARY) + { + if (has_compl_option(TRUE)) + goto docomplete; + break; + } +# endif +# ifdef FEAT_DIGRAPHS + c = ins_digraph(); + if (c == NUL) + break; +# endif + goto normalchar; +#endif + +#ifdef FEAT_INS_EXPAND + 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; +#endif + + case Ctrl_L: /* Whole line completion after ^X */ +#ifdef FEAT_INS_EXPAND + if (ctrl_x_mode != CTRL_X_WHOLE_LINE) +#endif + { + /* CTRL-L with 'insertmode' set: Leave Insert mode */ + if (p_im) + { + if (echeck_abbr(Ctrl_L + ABBR_OFF)) + break; + goto doESCkey; + } + goto normalchar; + } +#ifdef FEAT_INS_EXPAND + /* 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 == CTRL_X_NORMAL + || ctrl_x_mode == CTRL_X_WHOLE_LINE) + && !(compl_cont_status & CONT_LOCAL)) + goto normalchar; + +docomplete: + compl_busy = TRUE; +#ifdef FEAT_FOLDING + disable_fold_update++; /* don't redraw folds here */ +#endif + if (ins_complete(c, TRUE) == FAIL) + compl_cont_status = 0; +#ifdef FEAT_FOLDING + disable_fold_update--; +#endif + compl_busy = FALSE; + break; +#endif /* FEAT_INS_EXPAND */ + + 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 defined(FEAT_EVAL) + 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); + } + vim_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; + } +#endif +#ifdef FEAT_SMARTINDENT + /* Try to perform smart-indenting. */ + ins_try_si(c); +#endif + + if (c == ' ') + { + inserted_space = TRUE; +#ifdef FEAT_CINDENT + if (inindent(0)) + can_cindent = FALSE; +#endif + 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); +#ifdef FEAT_RIGHTLEFT + revins_legal++; + revins_chars++; +#endif + } + + auto_format(FALSE, TRUE); + +#ifdef FEAT_FOLDING + /* When inserting a character the cursor line must never be in a + * closed fold. */ + foldOpenCursor(); +#endif + break; + } /* end of switch (c) */ + + /* If typed something may trigger CursorHoldI again. */ + if (c != K_CURSORHOLD +#ifdef FEAT_COMPL_FUNC + /* but not in CTRL-X mode, a script can't restore the state */ + && ctrl_x_mode == CTRL_X_NORMAL +#endif + ) + did_cursorhold = FALSE; + + /* If the cursor was moved we didn't just insert a space */ + if (arrow_used) + inserted_space = FALSE; + +#ifdef FEAT_CINDENT + if (can_cindent && cindent_on() +# ifdef FEAT_INS_EXPAND + && ctrl_x_mode == CTRL_X_NORMAL +# endif + ) + { +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(); + } + } +#endif /* FEAT_CINDENT */ + + } /* 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 UNUSED) /* not busy with something */ +{ +#ifdef FEAT_CONCEAL + linenr_T conceal_old_cursor_line = 0; + linenr_T conceal_new_cursor_line = 0; + int conceal_update_lines = FALSE; +#endif + + if (char_avail()) + return; + +#if defined(FEAT_CONCEAL) + /* Trigger CursorMoved if the cursor moved. Not when the popup menu is + * visible, the command might delete it. */ + if (ready && (has_cursormovedI() +# if defined(FEAT_CONCEAL) + || curwin->w_p_cole > 0 +# endif + ) + && !EQUAL_POS(last_cursormoved, curwin->w_cursor) +# ifdef FEAT_INS_EXPAND + && !pum_visible() +# endif + ) + { +# ifdef FEAT_SYN_HL + /* 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); +# endif + if (has_cursormovedI()) + { + /* Make sure curswant is correct, an autocommand may call + * getcurpos(). */ + update_curswant(); + ins_apply_autocmds(EVENT_CURSORMOVEDI); + } +# ifdef FEAT_CONCEAL + 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; + } +# endif + last_cursormoved = curwin->w_cursor; + } +#endif + + /* Trigger TextChangedI if b_changedtick differs. */ + if (ready && has_textchangedI() + && curbuf->b_last_changedtick != CHANGEDTICK(curbuf) +#ifdef FEAT_INS_EXPAND + && !pum_visible() +#endif + ) + { + aco_save_T aco; + varnumber_T tick = CHANGEDTICK(curbuf); + + // save and restore curwin and curbuf, in case the autocmd changes them + aucmd_prepbuf(&aco, curbuf); + apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, FALSE, curbuf); + aucmd_restbuf(&aco); + curbuf->b_last_changedtick = CHANGEDTICK(curbuf); + if (tick != CHANGEDTICK(curbuf)) // see ins_apply_autocmds() + u_save(curwin->w_cursor.lnum, + (linenr_T)(curwin->w_cursor.lnum + 1)); + } + +#ifdef FEAT_INS_EXPAND + /* Trigger TextChangedP if b_changedtick differs. When the popupmenu closes + * TextChangedI will need to trigger for backwards compatibility, thus use + * different b_last_changedtick* variables. */ + if (ready && has_textchangedP() + && curbuf->b_last_changedtick_pum != CHANGEDTICK(curbuf) + && pum_visible()) + { + aco_save_T aco; + varnumber_T tick = CHANGEDTICK(curbuf); + + // save and restore curwin and curbuf, in case the autocmd changes them + aucmd_prepbuf(&aco, curbuf); + apply_autocmds(EVENT_TEXTCHANGEDP, NULL, NULL, FALSE, curbuf); + aucmd_restbuf(&aco); + curbuf->b_last_changedtick_pum = CHANGEDTICK(curbuf); + if (tick != CHANGEDTICK(curbuf)) // see ins_apply_autocmds() + u_save(curwin->w_cursor.lnum, + (linenr_T)(curwin->w_cursor.lnum + 1)); + } +#endif + +#if defined(FEAT_CONCEAL) + 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) + redrawWinline(curwin, conceal_old_cursor_line); + redrawWinline(curwin, conceal_new_cursor_line == 0 + ? curwin->w_cursor.lnum : conceal_new_cursor_line); + curwin->w_valid &= ~VALID_CROW; + need_cursor_line_redraw = FALSE; + } +#endif + if (must_redraw) + update_screen(0); + else if (clear_cmdline || redraw_cmdline) + showmode(); /* clear cmdline and show mode */ + 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 */ + +#ifdef FEAT_CMDL_INFO + add_to_showcmd_c(Ctrl_V); +#endif + + 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(); +#ifdef FEAT_CMDL_INFO + clear_showcmd(); +#endif + insert_special(c, FALSE, TRUE); +#ifdef FEAT_RIGHTLEFT + revins_chars++; + revins_legal++; +#endif +} + +/* + * 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 = curwin->w_wincol; + pc_status = PC_STATUS_UNSET; +#ifdef FEAT_RIGHTLEFT + if (curwin->w_p_rl) + { + pc_col += curwin->w_width - 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 +#endif + { + 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); + } +} + +#if defined(FEAT_JOB_CHANNEL) || defined(PROTO) +/* + * Return the effective prompt for the current buffer. + */ + char_u * +prompt_text(void) +{ + if (curbuf->b_prompt_text == NULL) + return (char_u *)"% "; + return curbuf->b_prompt_text; +} + +/* + * Prepare for prompt mode: Make sure the last line has the prompt text. + * Move the cursor to this line. + */ + static void +init_prompt(int cmdchar_todo) +{ + char_u *prompt = prompt_text(); + char_u *text; + + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + text = ml_get_curline(); + if (STRNCMP(text, prompt, STRLEN(prompt)) != 0) + { + // prompt is missing, insert it or append a line with it + if (*text == NUL) + ml_replace(curbuf->b_ml.ml_line_count, prompt, TRUE); + else + ml_append(curbuf->b_ml.ml_line_count, prompt, 0, FALSE); + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + coladvance((colnr_T)MAXCOL); + changed_bytes(curbuf->b_ml.ml_line_count, 0); + } + + // Insert always starts after the prompt, allow editing text after it. + if (Insstart_orig.lnum != curwin->w_cursor.lnum + || Insstart_orig.col != (int)STRLEN(prompt)) + { + Insstart.lnum = curwin->w_cursor.lnum; + Insstart.col = (int)STRLEN(prompt); + Insstart_orig = Insstart; + Insstart_textlen = Insstart.col; + Insstart_blank_vcol = MAXCOL; + arrow_used = FALSE; + } + + if (cmdchar_todo == 'A') + coladvance((colnr_T)MAXCOL); + if (cmdchar_todo == 'I' || curwin->w_cursor.col <= (int)STRLEN(prompt)) + curwin->w_cursor.col = (int)STRLEN(prompt); + /* Make sure the cursor is in a valid position. */ + check_cursor(); +} + +/* + * Return TRUE if the cursor is in the editable position of the prompt line. + */ + int +prompt_curpos_editable() +{ + return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count + && curwin->w_cursor.col >= (int)STRLEN(prompt_text()); +} +#endif + +/* + * 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, curwin->w_cursor.lnum); + 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 < curwin->w_width) + { + 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, curwin->w_cursor.lnum); + } +} + +/* + * 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, 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 = alloc((unsigned)(i + 1)); + if (ptr != NULL) + { + new_cursor_col += i; + ptr[i] = NUL; + while (--i >= 0) + ptr[i] = ' '; + ins_str(ptr); + vim_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); + + vim_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; +} + +/* + * 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; + } +} + +/* + * 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 UNUSED) +{ + 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; +} + +#if defined(FEAT_INS_EXPAND) || defined(PROTO) +/* + * 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(); + } +} + +/* + * Whether other than default completion has been selected. + */ + int +ctrl_x_mode_not_default(void) +{ + return ctrl_x_mode != CTRL_X_NORMAL; +} + +/* + * Whether CTRL-X was typed without a following character. + */ + int +ctrl_x_mode_not_defined_yet(void) +{ + return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET; +} + +/* + * 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 +# ifdef FEAT_SPELL + && !curwin->w_p_spell +# endif + ) + : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL)) + { + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + msg_attr(dict_opt ? _("'dictionary' option is empty") + : _("'thesaurus' option is empty"), + HL_ATTR(HLF_E)); + if (emsg_silent == 0) + { + vim_beep(BO_COMPL); + setcursor(); + out_flush(); +#ifdef FEAT_EVAL + if (!get_vim_var_nr(VV_TESTING)) +#endif + 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 its 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); +#ifdef FEAT_FIND_ID + 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); +#endif + case CTRL_X_CMDLINE: + return (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N + || c == Ctrl_X); +#ifdef FEAT_COMPL_FUNC + 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); +#endif + case CTRL_X_SPELL: + return (c == Ctrl_S || c == Ctrl_P || c == Ctrl_N); + case CTRL_X_EVAL: + return (c == Ctrl_P || c == Ctrl_N); + } + internal_error("vim_is_ctrl_x_key()"); + 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 = (int *)alloc((unsigned)(actual_len * sizeof(int))); + if (wca != NULL) + { + 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 (MB_ISLOWER(c)) + { + has_lower = TRUE; + if (MB_ISUPPER(wca[i])) + { + /* Rule 1 is satisfied. */ + for (i = actual_compl_length; i < actual_len; ++i) + wca[i] = MB_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 && MB_ISUPPER(c) && MB_ISLOWER(wca[i])) + { + /* Rule 2 is satisfied. */ + for (i = actual_compl_length; i < actual_len; ++i) + wca[i] = MB_TOUPPER(wca[i]); + break; + } + was_letter = MB_ISLOWER(c) || MB_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 (MB_ISLOWER(c)) + wca[i] = MB_TOLOWER(wca[i]); + else if (MB_ISUPPER(c)) + wca[i] = MB_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; + + vim_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, + * maybe because alloc() returns NULL, 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 = (compl_T *)alloc_clear((unsigned)sizeof(compl_T)); + if (match == NULL) + return FAIL; + match->cp_number = -1; + if (flags & ORIGINAL_TEXT) + match->cp_number = 0; + if ((match->cp_str = vim_strnsave(str, len)) == NULL) + { + vim_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 ? (MB_TOLOWER(c1) != MB_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; +} + +/* + * Set variables that store noselect and noinsert behavior from the + * 'completeopt' value. + */ + void +completeopt_was_set(void) +{ + compl_no_insert = FALSE; + compl_no_select = FALSE; + if (strstr((char *)p_cot, "noselect") != NULL) + compl_no_select = TRUE; + if (strstr((char *)p_cot, "noinsert") != NULL) + compl_no_insert = TRUE; +} + +/* + * 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) +{ + int save_w_wrow = curwin->w_wrow; + int save_w_leftcol = curwin->w_leftcol; + + /* If already doing completions stop it. */ + if (ctrl_x_mode != CTRL_X_NORMAL) + ins_compl_prep(' '); + ins_compl_clear(); + ins_compl_free(); + + 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; + + ctrl_x_mode = CTRL_X_EVAL; + + 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; + if (compl_no_insert || compl_no_select) + { + ins_complete(K_DOWN, FALSE); + if (compl_no_select) + /* Down/Up has no real effect. */ + ins_complete(K_UP, FALSE); + } + else + ins_complete(Ctrl_N, FALSE); + compl_enter_selects = compl_no_insert; + + /* Lazily show the popup menu, unless we got interrupted. */ + if (!compl_interrupted) + show_pum(save_w_wrow, save_w_leftcol); + 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 the screen later, before drawing the popup menu over it. + pum_call_update_screen(); + 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(); + VIM_CLEAR(compl_match_array); + } +} + +/* + * 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 +#ifdef FEAT_GUI + && !gui.in_use +#endif + ) + 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; + +#if defined(FEAT_EVAL) + /* Dirty hard-coded hack: remove any matchparen highlighting. */ + do_cmdline_cmd((char_u *)"if exists('g:loaded_matchparen')|3match none|endif"); +#endif + + // Update the screen later, before drawing the popup menu over it. + pum_call_update_screen(); + + 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; + compl_match_array = (pumitem_T *)alloc_clear( + (unsigned)(sizeof(pumitem_T) + * compl_match_arraysize)); + if (compl_match_array != NULL) + { + /* 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) + { + /* In Replace mode when a $ is displayed at the end of the line only + * part of the screen would be updated. We do need to redraw here. */ + dollar_vcol = -1; + + /* 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) + { +#ifdef FEAT_SPELL + /* When 'dictionary' is empty and spell checking is enabled use + * "spell". */ + if (!thesaurus && curwin->w_p_spell) + dict = (char_u *)"spell"; + else +#endif + return; + } + + buf = alloc(LSIZE); + if (buf == NULL) + return; + 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_LINE_OR_EVAL(ctrl_x_mode)) + { + char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); + size_t len; + + if (pat_esc == NULL) + goto theend; + len = STRLEN(pat_esc) + 10; + ptr = alloc((unsigned)len); + if (ptr == NULL) + { + vim_free(pat_esc); + goto theend; + } + vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); + regmatch.regprog = vim_regcomp(ptr, RE_MAGIC); + vim_free(pat_esc); + vim_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, ","); +# ifdef FEAT_SPELL + if (!thesaurus && STRCMP(buf, "spell") == 0) + count = -1; + else +# endif + if (vim_strchr(buf, '`') != NULL + || expand_wildcards(1, &buf, &count, &files, + EW_FILE|EW_SILENT) != OK) + count = 0; + } + +# ifdef FEAT_SPELL + 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 +# endif + 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); + vim_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((char *)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_LINE_OR_EVAL(ctrl_x_mode)) + 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, FALSE); + } + 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; + + VIM_CLEAR(compl_pattern); + VIM_CLEAR(compl_leader); + + 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; + vim_free(match->cp_str); + /* several entries may use the same fname, free it just once. */ + if (match->cp_flags & FREE_FNAME) + vim_free(match->cp_fname); + for (i = 0; i < CPT_COUNT; ++i) + vim_free(match->cp_text[i]); + vim_free(match); + } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); + compl_first_match = compl_curr_match = NULL; + compl_shown_match = NULL; + compl_old_match = NULL; +} + + static void +ins_compl_clear(void) +{ + compl_cont_status = 0; + compl_started = FALSE; + compl_matches = 0; + VIM_CLEAR(compl_pattern); + VIM_CLEAR(compl_leader); + edit_submode_extra = NULL; + VIM_CLEAR(compl_orig_text); + compl_enter_selects = FALSE; + /* clear v:completed_item */ + set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED)); +} + +/* + * 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. + * Respect the 'backspace' option. */ + if ((int)(p - line) - (int)compl_col < 0 + || ((int)(p - line) - (int)compl_col == 0 + && ctrl_x_mode != CTRL_X_OMNI) || ctrl_x_mode == CTRL_X_EVAL + || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col + - compl_length < 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(); + + vim_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 + { +#ifdef FEAT_SPELL + spell_bad_len = 0; /* need to redetect bad word */ +#endif + /* + * Matches were cleared, need to search for them now. Befor drawing + * the popup menu display the changed text before the cursor. Set + * "compl_restarting" to avoid that the first match is inserted. + */ + pum_call_update_screen(); +#ifdef FEAT_GUI + if (gui.in_use) + { + /* Show the cursor after the match, not after the redrawn text. */ + setcursor(); + out_flush_cursor(FALSE, FALSE); + } +#endif + compl_restarting = TRUE; + if (ins_complete(Ctrl_N, TRUE) == 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 (stop_arrow() == FAIL) + return; + 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) + { + vim_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. + * The ORIGINAL_TEXT flag is either at the first item or might possibly be + * at the last item for backward completion */ + if (compl_first_match->cp_flags & ORIGINAL_TEXT) /* safety check */ + { + p = vim_strsave(str); + if (p != NULL) + { + vim_free(compl_first_match->cp_str); + compl_first_match->cp_str = p; + } + } + else if (compl_first_match->cp_prev != NULL + && (compl_first_match->cp_prev->cp_flags & ORIGINAL_TEXT)) + { + p = vim_strsave(str); + if (p != NULL) + { + vim_free(compl_first_match->cp_prev->cp_str); + compl_first_match->cp_prev->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 == CTRL_X_NORMAL && !compl_started)) + { + compl_get_longest = (strstr((char *)p_cot, "longest") != 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; +#ifdef FEAT_COMPL_FUNC + case Ctrl_U: + ctrl_x_mode = CTRL_X_FUNCTION; + break; + case Ctrl_O: + ctrl_x_mode = CTRL_X_OMNI; + break; +#endif + case 's': + case Ctrl_S: + ctrl_x_mode = CTRL_X_SPELL; +#ifdef FEAT_SPELL + ++emsg_off; /* Avoid getting the E756 error twice. */ + spell_back_to_badword(); + --emsg_off; +#endif + break; + case Ctrl_RSB: + ctrl_x_mode = CTRL_X_TAGS; + break; +#ifdef FEAT_FIND_ID + 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; +#endif + 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 = CTRL_X_NORMAL; + edit_submode = NULL; + showmode(); + break; + } + } + else if (ctrl_x_mode != CTRL_X_NORMAL) + { + /* 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 = CTRL_X_NORMAL; + 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 == CTRL_X_NORMAL && 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); + } + +#ifdef FEAT_CINDENT + want_cindent = (can_cindent && cindent_on()); +#endif + /* + * When completing whole lines: fix indent for 'cindent'. + * Otherwise, break line if it's too long. + */ + if (compl_cont_mode == CTRL_X_WHOLE_LINE) + { +#ifdef FEAT_CINDENT + /* re-indent the current line */ + if (want_cindent) + { + do_c_expr_indent(); + want_cindent = FALSE; /* don't do it again */ + } +#endif + } + else + { + int prev_col = curwin->w_cursor.col; + + /* put the cursor on the last char, for 'tw' formatting */ + if (prev_col > 0) + dec_cursor(); + /* only format when something was inserted */ + if (!arrow_used && !ins_need_undo && c != Ctrl_E) + 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. + * but only do this, if the Popup is still visible */ + 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; + if (!shortmess(SHM_COMPLETIONMENU)) + msg_clr_cmdline(); /* necessary for "noshowmode" */ + ctrl_x_mode = CTRL_X_NORMAL; + compl_enter_selects = FALSE; + if (edit_submode != NULL) + { + edit_submode = NULL; + showmode(); + } + +#ifdef FEAT_CMDWIN + if (c == Ctrl_C && cmdwin_type != 0) + /* Avoid the popup menu remains displayed when leaving the + * command line window. */ + update_screen(0); +#endif +#ifdef FEAT_CINDENT + /* + * Indent now if a key was typed that is in 'cinkeys'. + */ + if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) + do_c_expr_indent(); +#endif + /* Trigger the CompleteDone event to give scripts a chance to act + * upon the completion. */ + ins_apply_autocmds(EVENT_COMPLETEDONE); + } + } + 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. */ + ins_apply_autocmds(EVENT_COMPLETEDONE); + + /* 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; +} + +#ifdef FEAT_COMPL_FUNC +/* + * 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; + typval_T args[3]; + char_u *funcname; + pos_T pos; + win_T *curwin_save; + buf_T *curbuf_save; + typval_T rettv; + int save_State = State; + + 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].v_type = VAR_NUMBER; + args[0].vval.v_number = 0; + args[1].v_type = VAR_STRING; + args[1].vval.v_string = base != NULL ? base : (char_u *)""; + args[2].v_type = VAR_UNKNOWN; + + 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, &rettv) == OK) + { + switch (rettv.v_type) + { + case VAR_LIST: + matchlist = rettv.vval.v_list; + break; + case VAR_DICT: + matchdict = rettv.vval.v_dict; + break; + case VAR_SPECIAL: + if (rettv.vval.v_number == VVAL_NONE) + compl_opt_suppress_empty = TRUE; + // FALLTHROUGH + 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 */ + validate_cursor(); + if (!EQUAL_POS(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: + // Restore State, it might have been changed. + State = save_State; + + if (matchdict != NULL) + dict_unref(matchdict); + if (matchlist != NULL) + list_unref(matchlist); +} +#endif /* FEAT_COMPL_FUNC */ + +#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) || defined(PROTO) +/* + * 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, + * maybe because alloc() returns NULL, 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 = dict_get_string(tv->vval.v_dict, (char_u *)"word", FALSE); + cptext[CPT_ABBR] = dict_get_string(tv->vval.v_dict, + (char_u *)"abbr", FALSE); + cptext[CPT_MENU] = dict_get_string(tv->vval.v_dict, + (char_u *)"menu", FALSE); + cptext[CPT_KIND] = dict_get_string(tv->vval.v_dict, + (char_u *)"kind", FALSE); + cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict, + (char_u *)"info", FALSE); + cptext[CPT_USER_DATA] = dict_get_string(tv->vval.v_dict, + (char_u *)"user_data", FALSE); + if (dict_get_string(tv->vval.v_dict, (char_u *)"icase", FALSE) != NULL) + icase = dict_get_number(tv->vval.v_dict, (char_u *)"icase"); + if (dict_get_string(tv->vval.v_dict, (char_u *)"dup", FALSE) != NULL) + adup = dict_get_number(tv->vval.v_dict, (char_u *)"dup"); + if (dict_get_string(tv->vval.v_dict, (char_u *)"empty", FALSE) != NULL) + aempty = dict_get_number(tv->vval.v_dict, (char_u *)"empty"); + } + else + { + word = tv_get_string_chk(tv); + vim_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); +} +#endif + +/* + * 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; + int set_match_pos; + + if (!compl_started) + { + FOR_ALL_BUFFERS(ins_buf) + 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; + } + else if (ins_buf != curbuf && !buf_valid(ins_buf)) + ins_buf = curbuf; // In case the buffer was wiped out. + + compl_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 == CTRL_X_NORMAL + || CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) + && (!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; + /* Move the cursor back one character so that ^N can match the + * word immediately after the cursor. */ + if (ctrl_x_mode == CTRL_X_NORMAL && dec(&first_match_pos) < 0) + { + /* Move the cursor to after the last character in the + * buffer, so that word at start of buffer is found + * correctly. */ + first_match_pos.lnum = ins_buf->b_ml.ml_line_count; + first_match_pos.col = + (colnr_T)STRLEN(ml_get(first_match_pos.lnum)); + } + 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((char *)IObuff, TRUE, HL_ATTR(HLF_R)); + } + else if (*e_cpt == NUL) + break; + else + { + if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) + 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; + } + } +#ifdef FEAT_FIND_ID + else if (*e_cpt == 'i') + type = CTRL_X_PATH_PATTERNS; + else if (*e_cpt == 'd') + type = CTRL_X_PATH_DEFINES; +#endif + else if (*e_cpt == ']' || *e_cpt == 't') + { + type = CTRL_X_TAGS; + vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags.")); + (void)msg_trunc_attr((char *)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; + } + } + + /* If complete() was called then compl_pattern has been reset. The + * following won't work then, bail out. */ + if (compl_pattern == NULL) + break; + + switch (type) + { + case -1: + break; +#ifdef FEAT_FIND_ID + 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; +#endif + + 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 != CTRL_X_NORMAL ? 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; + +#ifdef FEAT_COMPL_FUNC + case CTRL_X_FUNCTION: + case CTRL_X_OMNI: + expand_by_function(type, compl_pattern); + break; +#endif + + case CTRL_X_SPELL: +#ifdef FEAT_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); +#endif + 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_LINE_OR_EVAL(ctrl_x_mode) + * || word-wise search that + * has added a word that was at the beginning of the line */ + if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode) + || (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, NULL, + compl_direction, + compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, + RE_LAST, (linenr_T)0, NULL, 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_LINE_OR_EVAL(ctrl_x_mode)) + { + 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 != compl_old_match) + found_new_match = OK; + + /* break the loop for specialized modes (use 'complete' just for the + * generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new + * match */ + if ((ctrl_x_mode != CTRL_X_NORMAL + && !CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) + || found_new_match != FAIL) + { + if (got_int) + break; + /* Fill the popup menu as soon as possible. */ + if (type != -1) + ins_compl_check_keys(0, FALSE); + + if ((ctrl_x_mode != CTRL_X_NORMAL + && !CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) + || 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 == CTRL_X_NORMAL || CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) + && *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 != CTRL_X_NORMAL + && !CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode))) + i = ins_compl_make_cyclic(); + + if (compl_old_match != NULL) + { + /* 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 ? compl_old_match->cp_next + : compl_old_match->cp_prev; + if (compl_curr_match == NULL) + compl_curr_match = compl_old_match; + } + return i; +} + +/* Delete the old text being completed. */ + static void +ins_compl_delete(void) +{ + int col; + + /* + * In insert mode: Delete the typed part. + * In replace mode: Put the old characters back, if any. + */ + col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); + if ((int)curwin->w_cursor.col > col) + { + if (stop_arrow() == FAIL) + return; + backspace_until_column(col); + } + + /* TODO: is this sufficient for redrawing? Redrawing everything causes + * flicker, thus we can't do that. */ + changed_cline_bef_curs(); + /* clear v:completed_item */ + set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED)); +} + +/* + * Insert the new text being completed. + * "in_compl_func" is TRUE when called from complete_check(). + */ + static void +ins_compl_insert(int in_compl_func) +{ + dict_T *dict; + + 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; + + /* Set completed item. */ + /* { word, abbr, menu, kind, info } */ + dict = dict_alloc_lock(VAR_FIXED); + if (dict != NULL) + { + dict_add_string(dict, "word", compl_shown_match->cp_str); + dict_add_string(dict, "abbr", compl_shown_match->cp_text[CPT_ABBR]); + dict_add_string(dict, "menu", compl_shown_match->cp_text[CPT_MENU]); + dict_add_string(dict, "kind", compl_shown_match->cp_text[CPT_KIND]); + dict_add_string(dict, "info", compl_shown_match->cp_text[CPT_INFO]); + dict_add_string(dict, "user_data", + compl_shown_match->cp_text[CPT_USER_DATA]); + } + set_vim_var_dict(VV_COMPLETED_ITEM, dict); + if (!in_compl_func) + compl_curr_match = compl_shown_match; +} + +/* + * 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 in_compl_func) /* called from complete_check() */ +{ + int num_matches = -1; + int todo = count; + compl_T *found_compl = NULL; + int found_end = FALSE; + int advance; + int started = compl_started; + + /* 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 (!compl_no_select && 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 (compl_no_insert && !started) + { + ins_bytes(compl_orig_text + ins_compl_len()); + compl_used_match = FALSE; + } + else if (insert_match) + { + if (!compl_get_longest || compl_used_match) + ins_compl_insert(in_compl_func); + 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 before showing the popup menu to show the user what was + // inserted. + pum_call_update_screen(); + + /* display the updated popup menu */ + ins_compl_show_pum(); +#ifdef FEAT_GUI + if (gui.in_use) + { + /* Show the cursor after the match, not after the redrawn text. */ + setcursor(); + out_flush_cursor(FALSE, FALSE); + } +#endif + + /* 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. */ + if (compl_no_insert && !started) + compl_enter_selects = TRUE; + else + 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) + { + char *lead = _("match in file"); + int space = sc_col - vim_strsize((char_u *)lead) - 2; + char_u *s; + char_u *e; + + if (space > 0) + { + /* We need the tail that fits. With double-byte encoding going + * back from the end is very slow, thus go from the start and keep + * the text that fits in "space" between "s" and "e". */ + for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) + { + space -= ptr2cells(e); + while (space < 0) + { + space += ptr2cells(s); + MB_PTR_ADV(s); + } + } + vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, + s > compl_shown_match->cp_fname ? "<" : "", s); + msg((char *)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. + * "in_compl_func" is TRUE when called from complete_check(), don't set + * compl_curr_match. + */ + void +ins_compl_check_keys(int frequency, int in_compl_func) +{ + static int count = 0; + int c; + + /* Don't check when reading keys from a script, :normal or feedkeys(). + * That would break the test scripts. But do check for keys when called + * from complete_check(). */ + if (!in_compl_func && (using_script() || ex_normal_busy)) + 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, in_compl_func); + } + 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 && !compl_no_insert) + { + int todo = compl_pending > 0 ? compl_pending : -compl_pending; + + compl_pending = 0; + (void)ins_compl_next(FALSE, todo, TRUE, in_compl_func); + } +} + +/* + * 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 + || 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, int enable_pum) +{ + char_u *line; + int startcol = 0; /* column where searched text starts */ + colnr_T curs_col; /* cursor column */ + int n; + int save_w_wrow; + int save_w_leftcol; + int insert_match; + int save_did_ai = did_ai; + + compl_direction = ins_compl_key2dir(c); + insert_match = ins_compl_use_match(c); + + if (!compl_started) + { + /* First time we hit ^N or ^P (in a row, I mean) */ + + did_ai = FALSE; +#ifdef FEAT_SMARTINDENT + did_si = FALSE; + can_si = FALSE; + can_si_back = FALSE; +#endif + 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 == CTRL_X_NORMAL + || 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)getwhitecols(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_LINE_OR_EVAL(ctrl_x_mode)) + 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 != CTRL_X_NORMAL) + /* Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL */ + 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 == CTRL_X_NORMAL || (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 = alloc(quote_meta(NULL, line + compl_col, + compl_length) + 2); + if (compl_pattern == NULL) + return FAIL; + 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, + * alloc(7) is enough -- Acevedo + */ + compl_pattern = alloc(7); + if (compl_pattern == NULL) + return FAIL; + STRCPY((char *)compl_pattern, "\\<"); + (void)quote_meta(compl_pattern + 2, line + compl_col, 1); + STRCAT((char *)compl_pattern, "\\k"); + } + else + { + compl_pattern = alloc(quote_meta(NULL, line + compl_col, + compl_length) + 2); + if (compl_pattern == NULL) + return FAIL; + STRCPY((char *)compl_pattern, "\\<"); + (void)quote_meta(compl_pattern + 2, line + compl_col, + compl_length); + } + } + } + else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) + { + compl_col = (colnr_T)getwhitecols(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, FALSE); + 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) + { +#ifdef FEAT_COMPL_FUNC + /* + * Call user defined function 'completefunc' with "a:findstart" + * set to 1 to obtain the length of text to use for completion. + */ + typval_T args[3]; + int col; + char_u *funcname; + pos_T pos; + win_T *curwin_save; + buf_T *curbuf_save; + int save_State = State; + + /* 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) + { + semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION + ? "completefunc" : "omnifunc"); + /* restore did_ai, so that adding comment leader works */ + did_ai = save_did_ai; + return FAIL; + } + + args[0].v_type = VAR_NUMBER; + args[0].vval.v_number = 1; + args[1].v_type = VAR_STRING; + args[1].vval.v_string = (char_u *)""; + args[2].v_type = VAR_UNKNOWN; + pos = curwin->w_cursor; + curwin_save = curwin; + curbuf_save = curbuf; + col = call_func_retnr(funcname, 2, args); + + State = save_State; + if (curwin_save != curwin || curbuf_save != curbuf) + { + emsg(_(e_complwin)); + return FAIL; + } + curwin->w_cursor = pos; /* restore the cursor position */ + validate_cursor(); + if (!EQUAL_POS(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 = CTRL_X_NORMAL; + edit_submode = NULL; + if (!shortmess(SHM_COMPLETIONMENU)) + msg_clr_cmdline(); + return FAIL; + } + + /* + * Reset extended parameters of completion, when start new + * completion. + */ + compl_opt_refresh_always = FALSE; + compl_opt_suppress_empty = 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) +#endif + return FAIL; + } + else if (ctrl_x_mode == CTRL_X_SPELL) + { +#ifdef FEAT_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) +#endif + return FAIL; + } + else + { + internal_error("ins_complete()"); + return FAIL; + } + + if (compl_cont_status & CONT_ADDING) + { + edit_submode_pre = (char_u *)_(" Adding"); + if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) + { + /* Insert a new line, keep indentation but ignore 'comments' */ +#ifdef FEAT_COMMENTS + char_u *old = curbuf->b_p_com; + + curbuf->b_p_com = (char_u *)""; +#endif + compl_startpos.lnum = curwin->w_cursor.lnum; + compl_startpos.col = compl_col; + ins_eol('\r'); +#ifdef FEAT_COMMENTS + curbuf->b_p_com = old; +#endif + 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. */ + vim_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) + { + VIM_CLEAR(compl_pattern); + VIM_CLEAR(compl_orig_text); + 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(); + } + else if (insert_match && stop_arrow() == FAIL) + return FAIL; + + compl_shown_match = compl_curr_match; + compl_shows_dir = compl_direction; + + /* + * Find next match (and following matches). + */ + save_w_wrow = curwin->w_wrow; + save_w_leftcol = curwin->w_leftcol; + n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE); + + /* 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 != CTRL_X_NORMAL + && 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. + if (!compl_opt_suppress_empty) + { + showmode(); + if (!shortmess(SHM_COMPLETIONMENU)) + { + if (edit_submode_extra != NULL) + { + if (!p_smd) + msg_attr((char *)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 (enable_pum && !compl_interrupted) + show_pum(save_w_wrow, save_w_leftcol); + + compl_was_interrupted = compl_interrupted; + compl_interrupted = FALSE; + + return OK; +} + + static void +show_pum(int prev_w_wrow, int prev_w_leftcol) +{ + /* RedrawingDisabled may be set when invoked through complete(). */ + int n = RedrawingDisabled; + + RedrawingDisabled = 0; + + /* If the cursor moved or the display scrolled we need to remove the pum + * first. */ + setcursor(); + if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol) + ins_compl_del_pum(); + + ins_compl_show_pum(); + setcursor(); + RedrawingDisabled = n; +} + +/* + * 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; + /* FALLTHROUGH */ + case '~': + if (!p_magic) /* quote these only if magic is set */ + break; + /* FALLTHROUGH */ + case '\\': + if (ctrl_x_mode == CTRL_X_DICTIONARY + || ctrl_x_mode == CTRL_X_THESAURUS) + break; + /* FALLTHROUGH */ + 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; +} +#endif /* FEAT_INS_EXPAND */ + +/* + * 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 FEAT_GUI + /* + * In GUI there is no point inserting the internal code for a special key. + * It is more useful to insert the string "" instead. This would + * probably be useful in a text window too, but it would not be + * vi-compatible (maybe there should be an option for it?) -- webb + */ + if (gui.in_use) + ++allow_keys; +#endif +#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(); +#ifdef FEAT_CMDL_INFO + if (!(State & CMDLINE) && MB_BYTE2LEN_CHECK(nc) == 1) + add_to_showcmd(nc); +#endif + 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; +#ifdef FEAT_GUI + if (gui.in_use) + --allow_keys; +#endif + 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. + */ +#ifdef MACOS_X + /* Command-key never produces a normal key */ + if (mod_mask & MOD_MASK_CMD) + allow_modmask = TRUE; +#endif + 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. + */ +#ifdef EBCDIC +# define ISSPECIAL(c) ((c) < ' ' || (c) == '0' || (c) == '^') +#else +# define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^') +#endif + +#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; +#ifdef FEAT_COMMENTS + char_u *p; +#endif + int fo_ins_blank; + int force_format = flags & INSCHAR_FORMAT; + + textwidth = comp_textwidth(force_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 + && (force_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. */ +#if defined(FEAT_EVAL) + int do_internal = TRUE; + colnr_T virtcol = get_nolist_virtcol() + + char2cells(c != NUL ? c : gchar_cursor()); + + if (*curbuf->b_p_fex != NUL && (flags & INSCHAR_NO_FEX) == 0 + && (force_format || virtcol > (colnr_T)textwidth)) + { + 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) +#endif + internal_format(textwidth, second_indent, flags, c == NUL, c); + } + + if (c == NUL) /* only formatting was wanted */ + return; + +#ifdef FEAT_COMMENTS + /* 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; +#endif + + did_ai = FALSE; +#ifdef FEAT_SMARTINDENT + did_si = FALSE; + can_si = FALSE; + can_si_back = FALSE; +#endif + + /* + * 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. + * Do the check for InsertCharPre before the call to vpeekc() because the + * InsertCharPre autocommand could change the input buffer. + */ +#ifdef USE_ON_FLY_SCROLL + dont_scroll = FALSE; /* allow scrolling here */ +#endif + + if ( !ISSPECIAL(c) + && (!has_mbyte || (*mb_char2len)(c) == 1) + && !has_insertcharpre() + && vpeekc() != NUL + && !(State & REPLACE_FLAG) +#ifdef FEAT_CINDENT + && !cindent_on() +#endif +#ifdef FEAT_RIGHTLEFT + && !p_ri +#endif + ) + { +#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 +# ifdef FEAT_FKMAP + && !(p_fkmap && KeyTyped) /* Farsi mode mapping moves cursor */ +# endif + && (textwidth == 0 + || (virtcol += byte2cells(buf[i - 1])) < (colnr_T)textwidth) + && !(!no_abbr && !vim_iswordc(c) && vim_iswordc(buf[i - 1]))) + { +#ifdef FEAT_RIGHTLEFT + c = vgetc(); + if (p_hkmap && KeyTyped) + c = hkmap(c); /* Hebrew mode mapping */ + buf[i++] = c; +#else + buf[i++] = vgetc(); +#endif + } + +#ifdef FEAT_DIGRAPHS + do_digraph(-1); /* clear digraphs */ + do_digraph(buf[i-1]); /* may be the start of a digraph */ +#endif + 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; +#ifdef FEAT_COMMENTS + colnr_T leader_len; + int no_leader = FALSE; + int do_comments = (flags & INSCHAR_DO_COM); +#endif +#ifdef FEAT_LINEBREAK + int has_lbr = curwin->w_p_lbr; + + /* make sure win_lbr_chartabsize() counts correctly */ + curwin->w_p_lbr = FALSE; +#endif + + /* + * 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; + int wcc; // counter for whitespace chars + + virtcol = get_nolist_virtcol() + + char2cells(c != NUL ? c : gchar_cursor()); + if (virtcol <= (colnr_T)textwidth) + break; + +#ifdef FEAT_COMMENTS + 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; +#endif + if (!(flags & INSCHAR_FORMAT) +#ifdef FEAT_COMMENTS + && leader_len == 0 +#endif + && !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 + wcc = 0; + while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) + { + dec_cursor(); + cc = gchar_cursor(); + + // Increment count of how many whitespace chars in this + // group; we only need to know if it's more than one. + if (wcc < 2) + wcc++; + } + if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) + break; /* only spaces in front of text */ + + // Don't break after a period when 'formatoptions' has 'p' and + // there are less than two spaces. + if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) + continue; + +#ifdef FEAT_COMMENTS + /* Don't break until after the comment leader */ + if (curwin->w_cursor.col < leader_len) + break; +#endif + 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 */ +#ifdef FEAT_COMMENTS + /* do not break "#a b" when 'tw' is 2 */ + if (curwin->w_cursor.col <= leader_len) + break; +#endif + 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) + { +#ifdef FEAT_COMMENTS + /* Don't break until after the comment leader */ + if (curwin->w_cursor.col < leader_len) + break; +#endif + 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 */ +#ifdef FEAT_COMMENTS + /* Don't break until after the comment leader */ + if (curwin->w_cursor.col < leader_len) + break; +#endif + + 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) +#ifdef FEAT_COMMENTS + + (do_comments ? OPENLINE_DO_COM : 0) + + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0) +#endif + , ((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 +#ifdef FEAT_COMMENTS + 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 *)" "); + } + else + { +#endif + (void)set_indent(second_indent, SIN_CHANGED); +#ifdef FEAT_COMMENTS + } +#endif + } + } + 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); + vim_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; +#ifdef FEAT_CINDENT + can_cindent = TRUE; +#endif + /* moved the cursor, don't autoindent or cindent now */ + did_ai = FALSE; +#ifdef FEAT_SMARTINDENT + did_si = FALSE; + can_si = FALSE; + can_si_back = FALSE; +#endif + line_breakcheck(); + } + + if (save_char != NUL) /* put back space after cursor */ + pchar_cursor(save_char); + +#ifdef FEAT_LINEBREAK + curwin->w_p_lbr = has_lbr; +#endif + 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; + } + +#ifdef FEAT_COMMENTS + /* 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; +#endif + + /* + * 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 curwin->w_width - '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 = curwin->w_width - curbuf->b_p_wm; +#ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + textwidth -= 1; +#endif +#ifdef FEAT_FOLDING + textwidth -= curwin->w_p_fdc; +#endif +#ifdef FEAT_SIGNS + if (signcolumn_on(curwin)) + textwidth -= 1; +#endif + if (curwin->w_p_nu || curwin->w_p_rnu) + textwidth -= 8; + } + if (textwidth < 0) + textwidth = 0; + if (ff && textwidth == 0) + { + textwidth = curwin->w_width - 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 */ +{ + start_arrow_common(end_insert_pos, TRUE); +} + +/* + * Like start_arrow() but with end_change argument. + * Will prepare for redo of CTRL-G U if "end_change" is FALSE. + */ + static void +start_arrow_with_change( + pos_T *end_insert_pos, /* can be NULL */ + int end_change) /* end undoable change */ +{ + start_arrow_common(end_insert_pos, end_change); + if (!end_change) + { + AppendCharToRedobuff(Ctrl_G); + AppendCharToRedobuff('U'); + } +} + + static void +start_arrow_common( + pos_T *end_insert_pos, /* can be NULL */ + int end_change) /* end undoable change */ +{ + if (!arrow_used && end_change) /* something has been inserted */ + { + AppendToRedobuff(ESC_STR); + stop_insert(end_insert_pos, FALSE, FALSE); + arrow_used = TRUE; /* this means we stopped the current insert */ + } +#ifdef FEAT_SPELL + check_spell_redraw(); +#endif +} + +#ifdef FEAT_SPELL +/* + * 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(curwin, lnum); + } +} + +/* + * 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); +} +#endif + +/* + * 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) + { + Insstart = curwin->w_cursor; /* new insertion starts here */ + if (Insstart.col > Insstart_orig.col && !ins_need_undo) + /* Don't update the original insert position when moved to the + * right, except when nothing was inserted yet. */ + update_Insstart_orig = FALSE; + Insstart_textlen = (colnr_T)linetabsize(ml_get_curline()); + + if (u_save_cursor() == OK) + { + arrow_used = FALSE; + ins_need_undo = FALSE; + } + + 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; + } + +#ifdef FEAT_FOLDING + /* Always open fold at the cursor line when inserting something. */ + foldOpenCursor(); +#endif + + 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)) + { + vim_free(last_insert); + last_insert = ptr; + last_insert_skip = new_insert_skip; + } + else + vim_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 + { + /* reset tpos, could have been invalidated in the loop above */ + tpos = curwin->w_cursor; + tpos.col++; + if (cc != NUL && gchar_pos(&tpos) == 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; +#ifdef FEAT_SMARTINDENT + did_si = FALSE; + can_si = FALSE; + can_si_back = FALSE; +#endif + + /* 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; + + vim_free(last_insert); + last_insert = alloc(MB_MAXBYTES * 3 + 5); + if (last_insert != NULL) + { + 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) +{ + VIM_CLEAR(last_insert); +# ifdef FEAT_INS_EXPAND + VIM_CLEAR(compl_orig_text); +# endif +} +#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; + } +#ifdef FEAT_GUI + else if (c == CSI) + { + *s++ = CSI; + *s++ = KS_EXTRA; + *s++ = (int)KE_CSI; + } +#endif + 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()) + { +#ifdef FEAT_LINEBREAK + int width; +#endif + int v = getviscol(); + + if (v == 0) + return FAIL; + +#ifdef FEAT_LINEBREAK + /* We might get stuck on 'showbreak', skip over it. */ + width = 1; + for (;;) + { + coladvance(v - width); + /* getviscol() is slow, skip it when 'showbreak' is empty, + * 'breakindent' is not set and there are no multi-byte + * characters */ + if ((*p_sbr == NUL && !curwin->w_p_bri + && !has_mbyte) || getviscol() < v) + break; + ++width; + } +#else + coladvance(v - 1); +#endif + + 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 +#ifdef FEAT_FOLDING + 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 +#endif + 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; +#ifdef FEAT_FOLDING + /* Move to last line of fold, will fail if it's the end-of-file. */ + (void)hasFolding(lnum, NULL, &lnum); +#endif + /* 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 +#ifdef FEAT_FOLDING + 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 +#endif + 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 long replace_stack_nr = 0; /* next entry in replace stack */ +static long replace_stack_len = 0; /* max. number of entries */ + + void +replace_push( + int c) /* character that is replaced (NUL is none) */ +{ + char_u *p; + + if (replace_stack_nr < replace_offset) /* nothing to do */ + return; + if (replace_stack_len <= replace_stack_nr) + { + replace_stack_len += 50; + p = lalloc(sizeof(char_u) * replace_stack_len, TRUE); + if (p == NULL) /* out of memory */ + { + replace_stack_len -= 50; + return; + } + if (replace_stack != NULL) + { + mch_memmove(p, replace_stack, + (size_t)(replace_stack_nr * sizeof(char_u))); + vim_free(replace_stack); + } + replace_stack = p; + } + p = replace_stack + replace_stack_nr - replace_offset; + if (replace_offset) + mch_memmove(p + 1, p, (size_t)(replace_offset * sizeof(char_u))); + *p = 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; + mch_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) +{ + VIM_CLEAR(replace_stack); + 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) + { +#ifdef FEAT_TEXT_PROP + size_t len_before = 0; // init to shut up GCC + + if (curbuf->b_has_textprop) + { + // Do not adjust text properties for individual delete and insert + // operations, do it afterwards on the resulting text. + len_before = STRLEN(ml_get_curline()); + ++text_prop_frozen; + } +#endif + 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); + +#ifdef FEAT_TEXT_PROP + if (curbuf->b_has_textprop) + { + size_t len_now = STRLEN(ml_get_curline()); + + --text_prop_frozen; + adjust_prop_columns(curwin->w_cursor.lnum, curwin->w_cursor.col, + (int)(len_now - len_before)); + } +#endif + } + else if (cc == 0) + (void)del_char_after_col(limit_col); +} + +#if defined(FEAT_RIGHTLEFT) || defined(PROTO) +/* + * 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 --> ' ' -- / -- */ +#ifdef EBCDIC + else if (islower(c)) +#else + /* 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') +#endif + 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"; + +#ifdef EBCDIC + /* see note about islower() above */ + if (!islower(c)) +#else + if (c < 'a' || c > 'z') +#endif + return c; + c = str[CharOrdLow(c)]; + break; + } + } + + return (int)(CharOrdLow(c) + p_aleph); + } +} +#endif + + 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); +#ifdef FEAT_CMDL_INFO + add_to_showcmd_c(Ctrl_R); +#endif + } + +#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; +#ifdef FEAT_CMDL_INFO + add_to_showcmd_c(literally); +#endif + regname = plain_vgetc(); + LANGMAP_ADJUST(regname, TRUE); + } + --no_mapping; + +#ifdef FEAT_EVAL + /* 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 == '=') + { + pos_T curpos = curwin->w_cursor; +# ifdef HAVE_INPUT_METHOD + 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(); + + // Cursor may be moved back a column. + curwin->w_cursor = curpos; + check_cursor(); +# ifdef HAVE_INPUT_METHOD + // Restore the Input Method. + if (im_on) + im_set_active(TRUE); +# endif + } + if (regname == NUL || !valid_yank_reg(regname, FALSE)) + { + vim_beep(BO_REG); + need_redraw = TRUE; /* remove the '"' */ + } + else + { +#endif + 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(BO_REG); + 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; + +#ifdef FEAT_EVAL + } + --no_u_sync; + if (u_sync_once == 1) + ins_need_undo = TRUE; + u_sync_once = 0; +#endif +#ifdef FEAT_CMDL_INFO + clear_showcmd(); +#endif + + /* 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; + +#ifdef FEAT_INS_EXPAND + /* Right after CTRL-X the cursor will be after the ruler. */ + setcursor(); +#endif + + /* + * 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; + + /* CTRL-G U: do not break undo with the next char */ + case 'U': + /* Allow one left/right cursor movement with the next char, + * without breaking undo. */ + dont_sync_undo = MAYBE; + break; + + /* Unknown CTRL-G command, reserved for future expansion. */ + default: vim_beep(BO_CTRLG); + } +} + +/* + * 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 HAVE_INPUT_METHOD + im_set_active(FALSE); +#endif + } + } +#ifdef HAVE_INPUT_METHOD + 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(); +#ifdef FEAT_GUI + /* may show different cursor shape or color */ + if (gui.in_use) + gui_update_cursor(TRUE, FALSE); +#endif +#if defined(FEAT_KEYMAP) + /* Show/unshow value of 'keymap' in status lines. */ + status_redraw_curbuf(); +#endif +} + +/* + * 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; + +#ifdef FEAT_SPELL + check_spell_redraw(); +#endif +#if defined(FEAT_HANGULIN) +# if defined(ESC_CHG_TO_ENG_MODE) + hangul_input_state_set(0); +# endif + if (composing_hangul) + { + push_raw_key(composing_hangul_buffer, 2); + composing_hangul = 0; + } +#endif + + 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') + stuffRedoReadbuff(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)) +#ifdef FEAT_RIGHTLEFT + && !revins_on +#endif + ) + { + 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 HAVE_INPUT_METHOD + /* 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(); + +#ifdef FEAT_MOUSE + setmouse(); +#endif +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may show different cursor shape */ +#endif + if (!p_ek) + /* Re-enable bracketed paste mode. */ + out_str(T_BE); + + /* + * When recording or for CTRL-O, need to display the new mode. + * Otherwise remove the mode message. + */ + if (reg_recording != 0 || restart_edit != NUL) + showmode(); + else if (p_smd && !skip_showmode()) + msg(""); + + return TRUE; /* exit Insert mode */ +} + +#ifdef FEAT_RIGHTLEFT +/* + * 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; +#ifdef FEAT_FKMAP + 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 +#endif + p_hkmap = curwin->w_p_rl ^ p_ri; /* be consistent! */ + showmode(); +} +#endif + +/* + * 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: +# ifdef MACOS_X + case K_LEFT: + case K_RIGHT: + case K_UP: + case K_DOWN: + case K_END: + case K_HOME: +# endif + 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) +{ +#ifdef FEAT_FKMAP + if (p_fkmap && p_ri) + { + beep_flush(); + emsg(farsi_text_3); /* encoded in Farsi */ + return; + } +#endif + +# ifdef FEAT_EVAL + set_vim_var_string(VV_INSERTMODE, + (char_u *)((State & REPLACE_FLAG) ? "i" + : replaceState == VREPLACE ? "v" + : "r"), 1); +# endif + ins_apply_autocmds(EVENT_INSERTCHANGE); + if (State & REPLACE_FLAG) + State = INSERT | (State & LANGMAP); + else + State = replaceState | (State & LANGMAP); + AppendCharToRedobuff(K_INS); + showmode(); +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may show different cursor shape */ +#endif +} + +/* + * 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; +#ifdef FEAT_SMARTINDENT + did_si = FALSE; + can_si = FALSE; + can_si_back = FALSE; +#endif +#ifdef FEAT_CINDENT + can_cindent = FALSE; /* no cindenting after ^D or ^T */ +#endif +} + + 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, FALSE) == FAIL) + vim_beep(BO_BS); + else + { + curwin->w_cursor.col = temp; + /* Adjust orig_line_count in case more lines have been deleted than + * have been added. That makes sure, that open_line() later + * can access all buffer lines correctly */ + if (State & VREPLACE_FLAG && + orig_line_count > curbuf->b_ml.ml_line_count) + orig_line_count = curbuf->b_ml.ml_line_count; + } + } + else if (del_char(FALSE) == FAIL) /* delete char under cursor */ + vim_beep(BO_BS); + did_ai = FALSE; +#ifdef FEAT_SMARTINDENT + did_si = FALSE; + can_si = FALSE; + can_si_back = FALSE; +#endif + AppendCharToRedobuff(K_DEL); +} + +/* + * 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() + || ( +#ifdef FEAT_RIGHTLEFT + !revins_on && +#endif + ((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(BO_BS); + return FALSE; + } + + if (stop_arrow() == FAIL) + return FALSE; + in_indent = inindent(0); +#ifdef FEAT_CINDENT + if (in_indent) + can_cindent = FALSE; +#endif +#ifdef FEAT_COMMENTS + end_comment_pending = NUL; /* After BS, don't auto-end comment */ +#endif +#ifdef FEAT_RIGHTLEFT + if (revins_on) /* put cursor after last inserted char */ + inc_cursor(); +#endif + + /* 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.lnum; + if (curwin->w_cursor.lnum == lnum +#ifdef FEAT_RIGHTLEFT + || revins_on +#endif + ) + { + if (u_save((linenr_T)(curwin->w_cursor.lnum - 2), + (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) + return FALSE; + --Insstart.lnum; + Insstart.col = (colnr_T)STRLEN(ml_get(Insstart.lnum)); + } + /* + * 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, 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. + */ +#ifdef FEAT_RIGHTLEFT + if (revins_on) /* put cursor on last inserted char */ + dec_cursor(); +#endif + mincol = 0; + /* keep indent */ + if (mode == BACKSPACE_LINE + && (curbuf->b_p_ai +#ifdef FEAT_CINDENT + || cindent_on() +#endif + ) +#ifdef FEAT_RIGHTLEFT + && !revins_on +#endif + ) + { + 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 +#ifdef FEAT_VARTABS + || tabstop_count(curbuf->b_p_vsts_array) +#endif + ) + && 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; + /* 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(); +#ifdef FEAT_VARTABS + if (p_sta && in_indent) + { + ts = (int)get_sw_value(curbuf); + want_vcol = (want_vcol / ts) * ts; + } + else + want_vcol = tabstop_start(want_vcol, get_sts_value(), + curbuf->b_p_vsts_array); +#else + if (p_sta && in_indent) + ts = (int)get_sw_value(curbuf); + else + ts = (int)get_sts_value(); + want_vcol = (want_vcol / ts) * ts; +#endif + + /* 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 + { + int cclass = 0, prev_cclass = 0; + + if (has_mbyte) + cclass = mb_get_class(ml_get_cursor()); + do + { +#ifdef FEAT_RIGHTLEFT + if (!revins_on) /* put cursor on char to be deleted */ +#endif + dec_cursor(); + + cc = gchar_cursor(); + /* look multi-byte character class */ + if (has_mbyte) + { + prev_cclass = cclass; + cclass = mb_get_class(ml_get_cursor()); + } + + /* start of word? */ + if (mode == BACKSPACE_WORD && !vim_isspace(cc)) + { + mode = BACKSPACE_WORD_NOT_SPACE; + temp = vim_iswordc(cc); + } + /* end of word? */ + else if (mode == BACKSPACE_WORD_NOT_SPACE + && ((vim_isspace(cc) || vim_iswordc(cc) != temp) + || prev_cclass != cclass)) + { +#ifdef FEAT_RIGHTLEFT + if (!revins_on) +#endif + inc_cursor(); +#ifdef FEAT_RIGHTLEFT + else if (State & REPLACE_FLAG) + dec_cursor(); +#endif + 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(); +#ifdef FEAT_RIGHTLEFT + if (revins_chars) + { + revins_chars--; + revins_legal++; + } + if (revins_on && gchar_cursor() == NUL) + break; +#endif + } + /* Just a single backspace?: */ + if (mode == BACKSPACE_CHAR) + break; + } while ( +#ifdef FEAT_RIGHTLEFT + revins_on || +#endif + (curwin->w_cursor.col > mincol + && (curwin->w_cursor.lnum != Insstart_orig.lnum + || curwin->w_cursor.col != Insstart_orig.col))); + } + did_backspace = TRUE; + } +#ifdef FEAT_SMARTINDENT + did_si = FALSE; + can_si = FALSE; + can_si_back = FALSE; +#endif + 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; + +#ifdef FEAT_FOLDING + /* 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(); +#endif + + return did_backspace; +} + +#ifdef FEAT_MOUSE + static void +ins_mouse(int c) +{ + pos_T tpos; + win_T *old_curwin = curwin; + +# ifdef FEAT_GUI + /* When GUI is active, also move/paste when 'mouse' is empty */ + if (!gui.in_use) +# endif + 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; +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf)) + // Restart Insert mode when re-entering the prompt buffer. + curbuf->b_prompt_insert = 'A'; +#endif + } + start_arrow(curwin == old_curwin ? &tpos : NULL); + if (curwin != new_curwin && win_valid(new_curwin)) + { + curwin = new_curwin; + curbuf = curwin->w_buffer; + } +# ifdef FEAT_CINDENT + can_cindent = TRUE; +# endif + } + + /* 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, *wp; +# ifdef FEAT_INS_EXPAND + int did_scroll = FALSE; +# endif + + 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 */ + wp = mouse_find_win(&row, &col); + if (wp == NULL) + return; + curwin = wp; + curbuf = curwin->w_buffer; + } + if (curwin == old_curwin) + undisplay_dollar(); + +# ifdef FEAT_INS_EXPAND + /* Don't scroll the window in which completion is being done. */ + if (!pum_visible() || curwin != old_curwin) +# endif + { + 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); + } +#ifdef FEAT_GUI + else + { + int val, step = 6; + + if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) + step = curwin->w_width; + val = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : step); + if (val < 0) + val = 0; + gui_do_horiz_scroll(val, TRUE); + } +#endif +# ifdef FEAT_INS_EXPAND + did_scroll = TRUE; +# endif + } + + curwin->w_redr_status = TRUE; + + curwin = old_curwin; + curbuf = curwin->w_buffer; + +# ifdef FEAT_INS_EXPAND + /* 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(); + } +# endif + + if (!EQUAL_POS(curwin->w_cursor, tpos)) + { + start_arrow(&tpos); +# ifdef FEAT_CINDENT + can_cindent = TRUE; +# endif + } +} +#endif + +/* + * Handle receiving P_PS: start paste mode. Inserts the following text up to + * P_PE literally. + * When "drop" is TRUE then consume the text and drop it. + */ + int +bracketed_paste(paste_mode_T mode, int drop, garray_T *gap) +{ + int c; + char_u buf[NUMBUFLEN + MB_MAXBYTES]; + int idx = 0; + char_u *end = find_termcode((char_u *)"PE"); + int ret_char = -1; + int save_allow_keys = allow_keys; + int save_paste = p_paste; + + /* If the end code is too long we can't detect it, read everything. */ + if (STRLEN(end) >= NUMBUFLEN) + end = NULL; + ++no_mapping; + allow_keys = 0; + if (!p_paste) + // Also have the side effects of setting 'paste' to make it work much + // faster. + set_option_value((char_u *)"paste", TRUE, NULL, 0); + + for (;;) + { + // When the end is not defined read everything there is. + if (end == NULL && vpeekc() == NUL) + break; + do + { + c = vgetc(); + } while (c == K_IGNORE || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR); + if (c == NUL || got_int) + // When CTRL-C was encountered the typeahead will be flushed and we + // won't get the end sequence. + break; + + if (has_mbyte) + idx += (*mb_char2bytes)(c, buf + idx); + else + buf[idx++] = c; + buf[idx] = NUL; + if (end != NULL && STRNCMP(buf, end, idx) == 0) + { + if (end[idx] == NUL) + break; /* Found the end of paste code. */ + continue; + } + if (!drop) + { + switch (mode) + { + case PASTE_CMDLINE: + put_on_cmdline(buf, idx, TRUE); + break; + + case PASTE_EX: + if (gap != NULL && ga_grow(gap, idx) == OK) + { + mch_memmove((char *)gap->ga_data + gap->ga_len, + buf, (size_t)idx); + gap->ga_len += idx; + } + break; + + case PASTE_INSERT: + if (stop_arrow() == OK) + { + c = buf[0]; + if (idx == 1 && (c == CAR || c == K_KENTER || c == NL)) + ins_eol(c); + else + { + ins_char_bytes(buf, idx); + AppendToRedobuffLit(buf, idx); + } + } + break; + + case PASTE_ONE_CHAR: + if (ret_char == -1) + { + if (has_mbyte) + ret_char = (*mb_ptr2char)(buf); + else + ret_char = buf[0]; + } + break; + } + } + idx = 0; + } + + --no_mapping; + allow_keys = save_allow_keys; + if (!save_paste) + set_option_value((char_u *)"paste", FALSE, NULL, 0); + + return ret_char; +} + +#if defined(FEAT_GUI_TABLINE) || defined(PROTO) + static void +ins_tabline(int c) +{ + /* We will be leaving the current window, unless closing another tab. */ + if (c != K_TABMENU || current_tabmenu != TABLINE_MENU_CLOSE + || (current_tab != 0 && current_tab != tabpage_index(curtab))) + { + undisplay_dollar(); + start_arrow(&curwin->w_cursor); +# ifdef FEAT_CINDENT + can_cindent = TRUE; +# endif + } + + if (c == K_TABLINE) + goto_tabpage(current_tab); + else + { + handle_tabmenu(); + redraw_statuslines(); /* will redraw the tabline when needed */ + } +} +#endif + +#if defined(FEAT_GUI) || defined(PROTO) + void +ins_scroll(void) +{ + pos_T tpos; + + undisplay_dollar(); + tpos = curwin->w_cursor; + if (gui_do_scroll()) + { + start_arrow(&tpos); +# ifdef FEAT_CINDENT + can_cindent = TRUE; +# endif + } +} + + void +ins_horscroll(void) +{ + pos_T tpos; + + undisplay_dollar(); + tpos = curwin->w_cursor; + if (gui_do_horiz_scroll(scrollbar_value, FALSE)) + { + start_arrow(&tpos); +# ifdef FEAT_CINDENT + can_cindent = TRUE; +# endif + } +} +#endif + + static void +ins_left( + int end_change) /* end undoable change */ +{ + pos_T tpos; + +#ifdef FEAT_FOLDING + if ((fdo_flags & FDO_HOR) && KeyTyped) + foldOpenCursor(); +#endif + undisplay_dollar(); + tpos = curwin->w_cursor; + if (oneleft() == OK) + { +#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) + /* Only call start_arrow() when not busy with preediting, it will + * break undo. K_LEFT is inserted in im_correct_cursor(). */ + if (p_imst == IM_OVER_THE_SPOT || !im_is_preediting()) +#endif + { + start_arrow_with_change(&tpos, end_change); + if (!end_change) + AppendCharToRedobuff(K_LEFT); + } +#ifdef FEAT_RIGHTLEFT + /* If exit reversed string, position is fixed */ + if (revins_scol != -1 && (int)curwin->w_cursor.col >= revins_scol) + revins_legal++; + revins_chars++; +#endif + } + + /* + * 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) + { + /* always break undo when moving upwards/downwards, else undo may break */ + 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(BO_CRSR); + dont_sync_undo = FALSE; +} + + static void +ins_home(int c) +{ + pos_T tpos; + +#ifdef FEAT_FOLDING + if ((fdo_flags & FDO_HOR) && KeyTyped) + foldOpenCursor(); +#endif + 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; + +#ifdef FEAT_FOLDING + if ((fdo_flags & FDO_HOR) && KeyTyped) + foldOpenCursor(); +#endif + 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) +{ +#ifdef FEAT_FOLDING + if ((fdo_flags & FDO_HOR) && KeyTyped) + foldOpenCursor(); +#endif + 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(BO_CRSR); +} + + static void +ins_right( + int end_change) /* end undoable change */ +{ +#ifdef FEAT_FOLDING + if ((fdo_flags & FDO_HOR) && KeyTyped) + foldOpenCursor(); +#endif + undisplay_dollar(); + if (gchar_cursor() != NUL || virtual_active()) + { + start_arrow_with_change(&curwin->w_cursor, end_change); + if (!end_change) + AppendCharToRedobuff(K_RIGHT); + 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; + } + +#ifdef FEAT_RIGHTLEFT + revins_legal++; + if (revins_chars) + revins_chars--; +#endif + } + /* 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(BO_CRSR); + dont_sync_undo = FALSE; +} + + static void +ins_s_right(void) +{ +#ifdef FEAT_FOLDING + if ((fdo_flags & FDO_HOR) && KeyTyped) + foldOpenCursor(); +#endif + 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(BO_CRSR); +} + + static void +ins_up( + int startcol) /* when TRUE move to Insstart.col */ +{ + pos_T tpos; + linenr_T old_topline = curwin->w_topline; +#ifdef FEAT_DIFF + int old_topfill = curwin->w_topfill; +#endif + + undisplay_dollar(); + tpos = curwin->w_cursor; + if (cursor_up(1L, TRUE) == OK) + { + if (startcol) + coladvance(getvcol_nolist(&Insstart)); + if (old_topline != curwin->w_topline +#ifdef FEAT_DIFF + || old_topfill != curwin->w_topfill +#endif + ) + redraw_later(VALID); + start_arrow(&tpos); +#ifdef FEAT_CINDENT + can_cindent = TRUE; +#endif + } + else + vim_beep(BO_CRSR); +} + + 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); +#ifdef FEAT_CINDENT + can_cindent = TRUE; +#endif + } + else + vim_beep(BO_CRSR); +} + + static void +ins_down( + int startcol) /* when TRUE move to Insstart.col */ +{ + pos_T tpos; + linenr_T old_topline = curwin->w_topline; +#ifdef FEAT_DIFF + int old_topfill = curwin->w_topfill; +#endif + + undisplay_dollar(); + tpos = curwin->w_cursor; + if (cursor_down(1L, TRUE) == OK) + { + if (startcol) + coladvance(getvcol_nolist(&Insstart)); + if (old_topline != curwin->w_topline +#ifdef FEAT_DIFF + || old_topfill != curwin->w_topfill +#endif + ) + redraw_later(VALID); + start_arrow(&tpos); +#ifdef FEAT_CINDENT + can_cindent = TRUE; +#endif + } + else + vim_beep(BO_CRSR); +} + + 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); +#ifdef FEAT_CINDENT + can_cindent = TRUE; +#endif + } + else + vim_beep(BO_CRSR); +} + +#ifdef FEAT_DND + static void +ins_drop(void) +{ + do_put('~', BACKWARD, 1L, PUT_CURSEND); +} +#endif + +/* + * 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); +#ifdef FEAT_CINDENT + if (ind) + can_cindent = FALSE; +#endif + + /* + * When nothing special, insert TAB like a normal character. + */ + if (!curbuf->b_p_et +#ifdef FEAT_VARTABS + && !(p_sta && ind + /* These five lines mean 'tabstop' != 'shiftwidth' */ + && ((tabstop_count(curbuf->b_p_vts_array) > 1) + || (tabstop_count(curbuf->b_p_vts_array) == 1 + && tabstop_first(curbuf->b_p_vts_array) + != get_sw_value(curbuf)) + || (tabstop_count(curbuf->b_p_vts_array) == 0 + && curbuf->b_p_ts != get_sw_value(curbuf)))) + && tabstop_count(curbuf->b_p_vsts_array) == 0 +#else + && !(p_sta && ind && curbuf->b_p_ts != get_sw_value(curbuf)) +#endif + && get_sts_value() == 0) + return TRUE; + + if (stop_arrow() == FAIL) + return TRUE; + + did_ai = FALSE; +#ifdef FEAT_SMARTINDENT + did_si = FALSE; + can_si = FALSE; + can_si_back = FALSE; +#endif + AppendToRedobuff((char_u *)"\t"); + +#ifdef FEAT_VARTABS + if (p_sta && ind) /* insert tab in indent, use 'shiftwidth' */ + { + temp = (int)get_sw_value(curbuf); + temp -= get_nolist_virtcol() % temp; + } + else if (tabstop_count(curbuf->b_p_vsts_array) > 0 || curbuf->b_p_sts != 0) + /* use 'softtabstop' when set */ + temp = tabstop_padding(get_nolist_virtcol(), get_sts_value(), + curbuf->b_p_vsts_array); + else /* otherwise use 'tabstop' */ + temp = tabstop_padding(get_nolist_virtcol(), curbuf->b_p_ts, + curbuf->b_p_vts_array); +#else + 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; +#endif + + /* + * 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. + */ +#ifdef FEAT_VARTABS + if (!curbuf->b_p_et && (tabstop_count(curbuf->b_p_vsts_array) > 0 + || get_sts_value() > 0 + || (p_sta && ind))) +#else + if (!curbuf->b_p_et && (get_sts_value() || (p_sta && ind))) +#endif + { + 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 'breakindent', 'showbreak' + * and 'linebreak' adding extra virtual columns. */ + while (VIM_ISWHITE(*ptr)) + { + i = lbr_chartabsize(NULL, (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; + char_u *line = ptr; + + /* Skip over the spaces we need. */ + while (vcol < want_vcol && *ptr == ' ') + { + vcol += lbr_chartabsize(line, 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); +#ifdef FEAT_TEXT_PROP + curbuf->b_ml.ml_line_len -= i; +#endif + } +#ifdef FEAT_NETBEANS_INTG + if (netbeans_active()) + { + netbeans_removed(curbuf, fpos.lnum, cursor->col, (long)(i + 1)); + netbeans_inserted(curbuf, fpos.lnum, cursor->col, + (char_u *)"\t", 1); + } +#endif + 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) + vim_free(saved_line); + curwin->w_p_list = save_list; + } + + return FALSE; +} + +/* + * Handle CR or NL in insert mode. + * Return FAIL when out of memory or can't undo. + */ + static int +ins_eol(int c) +{ + int i; + + if (echeck_abbr(c + ABBR_OFF)) + return OK; + if (stop_arrow() == FAIL) + return FAIL; + 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()); + +#ifdef FEAT_RIGHTLEFT +# ifdef FEAT_FKMAP + if (p_altkeymap && p_fkmap) + fkmap(NL); +# endif + /* 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()); +#endif + + AppendToRedobuff(NL_STR); + i = open_line(FORWARD, +#ifdef FEAT_COMMENTS + has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : +#endif + 0, old_indent); + old_indent = 0; +#ifdef FEAT_CINDENT + can_cindent = TRUE; +#endif +#ifdef FEAT_FOLDING + /* When inserting a line the cursor line must never be in a closed fold. */ + foldOpenCursor(); +#endif + + return i; +} + +#ifdef FEAT_DIGRAPHS +/* + * 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; +#ifdef FEAT_CMDL_INFO + add_to_showcmd_c(Ctrl_K); +#endif + } + +#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 */ + { +#ifdef FEAT_CMDL_INFO + clear_showcmd(); +#endif + 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; + } +#ifdef FEAT_CMDL_INFO + add_to_showcmd_c(c); +#endif + } + ++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); +#ifdef FEAT_CMDL_INFO + clear_showcmd(); +#endif + return c; + } + } +#ifdef FEAT_CMDL_INFO + clear_showcmd(); +#endif + return NUL; +} +#endif + +/* + * 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; + char_u *line; + + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) + { + vim_beep(BO_COPY); + return NUL; + } + + /* try to advance to the cursor column */ + temp = 0; + line = 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(line, &ptr, (colnr_T)temp); + } + if ((colnr_T)temp > curwin->w_virtcol) + ptr = prev_ptr; + + c = (*mb_ptr2char)(ptr); + if (c == NUL) + vim_beep(BO_COPY); + return c; +} + +/* + * CTRL-Y or CTRL-E typed in Insert mode. + */ + static int +ins_ctrl_ey(int tc) +{ + int c = tc; + +#ifdef FEAT_INS_EXPAND + if (ctrl_x_mode == CTRL_X_SCROLL) + { + if (c == Ctrl_Y) + scrolldown_clamp(); + else + scrollup_clamp(); + redraw_later(VALID); + } + else +#endif + { + 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; +#ifdef FEAT_RIGHTLEFT + revins_chars++; + revins_legal++; +#endif + c = Ctrl_V; /* pretend CTRL-V is last character */ + auto_format(FALSE, TRUE); + } + } + return c; +} + +#ifdef FEAT_SMARTINDENT +/* + * 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; +} +#endif + +/* + * Get the value that w_virtcol would have when 'list' is off. + * Unless 'cpo' contains the 'L' flag. + */ + colnr_T +get_nolist_virtcol(void) +{ + // check validity of cursor in current buffer + if (curwin->w_buffer == NULL + || curwin->w_buffer->b_ml.ml_mfp == NULL + || curwin->w_cursor.lnum > curwin->w_buffer->b_ml.ml_line_count) + return 0; + if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) + return getvcol_nolist(&curwin->w_cursor); + validate_virtcol(); + return curwin->w_virtcol; +} + +#if defined(FEAT_EVAL) +/* + * 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]; + int save_State = State; + + /* 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 (ins_apply_autocmds(EVENT_INSERTCHARPRE)) + { + /* 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; + + // Restore the State, it may have been changed. + State = save_State; + + return res; +} +#endif + +/* + * Trigger "event" and take care of fixing undo. + */ + static int +ins_apply_autocmds(event_T event) +{ + varnumber_T tick = CHANGEDTICK(curbuf); + int r; + + r = apply_autocmds(event, NULL, NULL, FALSE, curbuf); + + // If u_savesub() was called then we are not prepared to start + // a new line. Call u_save() with no contents to fix that. + if (tick != CHANGEDTICK(curbuf)) + u_save(curwin->w_cursor.lnum, (linenr_T)(curwin->w_cursor.lnum + 1)); + + return r; +} diff --git a/src/eval.c b/src/eval.c new file mode 100644 index 0000000..3f9db7d --- /dev/null +++ b/src/eval.c @@ -0,0 +1,10837 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ +#define USING_FLOAT_STUFF + +#include "vim.h" + +#if defined(FEAT_EVAL) || defined(PROTO) + +#ifdef VMS +# include +#endif + +#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ + +static char *e_letunexp = N_("E18: Unexpected characters in :let"); +static char *e_undefvar = N_("E121: Undefined variable: %s"); +static char *e_missbrac = N_("E111: Missing ']'"); +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_illvar = N_("E461: Illegal variable name: %s"); +#ifdef FEAT_FLOAT +static char *e_float_as_string = N_("E806: using Float as a String"); +#endif + +#define NAMESPACE_CHAR (char_u *)"abglstvw" + +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; + +/* + * 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" */ + +/* The names of packages that once were loaded are remembered. */ +static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, 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 */ + int fi_bi; /* index of blob */ + blob_T *fi_blob; /* blob being used */ +} forinfo_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. + */ + +/* 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}} + +static struct vimvar +{ + char *vv_name; /* name of variable, without v: */ + dictitem16_T vv_di; /* value and name for key (max 16 chars!) */ + 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_winid", 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_winid", 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("completed_item", VAR_DICT), VV_RO}, + {VV_NAME("option_new", VAR_STRING), VV_RO}, + {VV_NAME("option_old", VAR_STRING), VV_RO}, + {VV_NAME("option_type", VAR_STRING), VV_RO}, + {VV_NAME("errors", VAR_LIST), 0}, + {VV_NAME("false", VAR_SPECIAL), VV_RO}, + {VV_NAME("true", VAR_SPECIAL), VV_RO}, + {VV_NAME("null", VAR_SPECIAL), VV_RO}, + {VV_NAME("none", VAR_SPECIAL), VV_RO}, + {VV_NAME("vim_did_enter", VAR_NUMBER), VV_RO}, + {VV_NAME("testing", VAR_NUMBER), 0}, + {VV_NAME("t_number", VAR_NUMBER), VV_RO}, + {VV_NAME("t_string", VAR_NUMBER), VV_RO}, + {VV_NAME("t_func", VAR_NUMBER), VV_RO}, + {VV_NAME("t_list", VAR_NUMBER), VV_RO}, + {VV_NAME("t_dict", VAR_NUMBER), VV_RO}, + {VV_NAME("t_float", VAR_NUMBER), VV_RO}, + {VV_NAME("t_bool", VAR_NUMBER), VV_RO}, + {VV_NAME("t_none", VAR_NUMBER), VV_RO}, + {VV_NAME("t_job", VAR_NUMBER), VV_RO}, + {VV_NAME("t_channel", VAR_NUMBER), VV_RO}, + {VV_NAME("t_blob", VAR_NUMBER), VV_RO}, + {VV_NAME("termrfgresp", VAR_STRING), VV_RO}, + {VV_NAME("termrbgresp", VAR_STRING), VV_RO}, + {VV_NAME("termu7resp", VAR_STRING), VV_RO}, + {VV_NAME("termstyleresp", VAR_STRING), VV_RO}, + {VV_NAME("termblinkresp", VAR_STRING), VV_RO}, + {VV_NAME("event", VAR_DICT), VV_RO}, +}; + +/* 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_dict vv_di.di_tv.vval.v_dict +#define vv_blob vv_di.di_tv.vval.v_blob +#define vv_tv vv_di.di_tv + +static dictitem_T vimvars_var; /* variable used for v: */ +#define vimvarht vimvardict.dv_hashtab + +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_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 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 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 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 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 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 free_unref_items(int copyID); +static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); +static int get_env_len(char_u **arg); +static char_u * make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end); +static void check_vars(char_u *name, int len); +static typval_T *alloc_string_tv(char_u *string); +static void delete_var(hashtab_T *ht, hashitem_T *hi); +static void list_one_var(dictitem_T *v, char *prefix, int *first); +static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first); +static char_u *find_option_end(char_u **arg, int *opt_flags); + +/* for VIM_VERSION_ defines */ +#include "version.h" + + +#if defined(EBCDIC) || defined(PROTO) +/* + * Compare struct fst by function name. + */ + static int +compare_func_name(const void *s1, const void *s2) +{ + struct fst *p1 = (struct fst *)s1; + struct fst *p2 = (struct fst *)s2; + + return STRCMP(p1->f_name, p2->f_name); +} + +/* + * Sort the function table by function name. + * The sorting of the table above is ASCII dependent. + * On machines using EBCDIC we have to sort it. + */ + static void +sortFunctions(void) +{ + int funcCnt = (int)(sizeof(functions) / sizeof(struct fst)) - 1; + + qsort(functions, (size_t)funcCnt, sizeof(struct fst), compare_func_name); +} +#endif + + +/* + * 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); + func_init(); + + for (i = 0; i < VV_LEN; ++i) + { + p = &vimvars[i]; + if (STRLEN(p->vv_name) > 16) + { + iemsg("INTERNAL: name too long, increase size of dictitem16_T"); + getout(1); + } + 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); + } + vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; + + set_vim_var_nr(VV_SEARCHFORWARD, 1L); + set_vim_var_nr(VV_HLSEARCH, 1L); + set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED)); + set_vim_var_list(VV_ERRORS, list_alloc()); + set_vim_var_dict(VV_EVENT, dict_alloc_lock(VAR_FIXED)); + + set_vim_var_nr(VV_FALSE, VVAL_FALSE); + set_vim_var_nr(VV_TRUE, VVAL_TRUE); + set_vim_var_nr(VV_NONE, VVAL_NONE); + set_vim_var_nr(VV_NULL, VVAL_NULL); + + set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER); + set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING); + set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC); + set_vim_var_nr(VV_TYPE_LIST, VAR_TYPE_LIST); + set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT); + set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT); + set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL); + set_vim_var_nr(VV_TYPE_NONE, VAR_TYPE_NONE); + set_vim_var_nr(VV_TYPE_JOB, VAR_TYPE_JOB); + set_vim_var_nr(VV_TYPE_CHANNEL, VAR_TYPE_CHANNEL); + set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB); + + set_reg_var(0); /* default for v:register is not 0 but '"' */ + +#ifdef EBCDIC + /* + * Sort the function table, to enable binary search. + */ + sortFunctions(); +#endif +} + +#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) + VIM_CLEAR(p->vv_str); + 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(); +# if defined(FEAT_CMDL_COMPL) + free_locales(); +# endif + + /* 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) + vim_free(SCRIPT_SV(i)); + ga_clear(&ga_scripts); + + /* unreferenced lists and dicts */ + (void)garbage_collect(FALSE); + + /* functions */ + free_all_functions(); +} +#endif + + +/* + * 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; + typval_T *tvp; + + val = vim_strsave(value); + if (val != NULL) + { + tvp = alloc_string_tv(val); + if (tvp != NULL) + { + set_var(name, tvp, FALSE); + free_tv(tvp); + } + } +} + +static lval_T *redir_lval = NULL; +#define EVALCMD_BUSY (redir_lval == (lval_T *)&redir_lval) +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 + * When "append" is TRUE append to an existing variable. + * Returns OK if successfully completed the setup. FAIL otherwise. + */ + int +var_redir_start(char_u *name, int append) +{ + 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 = (lval_T *)alloc_clear((unsigned)sizeof(lval_T)); + if (redir_lval == NULL) + { + var_redir_stop(); + return FAIL; + } + + /* The output is stored in growarray "redir_ga" until redirection ends. */ + ga_init2(&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 */ + + if (ga_grow(&redir_ga, len) == OK) + { + mch_memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, len); + redir_ga.ga_len += len; + } + else + var_redir_stop(); +} + +/* + * Stop redirecting command output to a variable. + * Frees the allocated memory. + */ + void +var_redir_stop(void) +{ + typval_T tv; + + if (EVALCMD_BUSY) + { + redir_lval = NULL; + return; + } + + 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 */ + VIM_CLEAR(redir_ga.ga_data); + + VIM_CLEAR(redir_lval); + } + VIM_CLEAR(redir_varname); +} + + 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; +} + +# if defined(FEAT_POSTSCRIPT) || defined(PROTO) + 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) + { + mch_remove(fname); + return FAIL; + } + return OK; +} +# endif + +# if defined(FEAT_DIFF) || defined(PROTO) + 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); +} +# endif + +/* + * 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; + varnumber_T retval = FALSE; + + if (skip) + ++emsg_skip; + if (eval0(arg, &tv, nextcmd, !skip) == FAIL) + *error = TRUE; + else + { + *error = FALSE; + if (!skip) + { + retval = (tv_get_number_chk(&tv, error) != 0); + clear_tv(&tv); + } + } + if (skip) + --emsg_skip; + + return (int)retval; +} + +/* + * Call eval1() and give an error message if not done at a lower level. + */ + static int +eval1_emsg(char_u **arg, typval_T *rettv, int evaluate) +{ + char_u *start = *arg; + int ret; + int did_emsg_before = did_emsg; + int called_emsg_before = called_emsg; + + ret = eval1(arg, rettv, evaluate); + if (ret == FAIL) + { + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception, or we already gave a more specific error. + // Also check called_emsg for when using assert_fails(). + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) + semsg(_(e_invexpr2), start); + } + return ret; +} + + static int +eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv) +{ + char_u *s; + int dummy; + char_u buf[NUMBUFLEN]; + + if (expr->v_type == VAR_FUNC) + { + s = expr->vval.v_string; + if (s == NULL || *s == NUL) + return FAIL; + if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL, + 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL) + return FAIL; + } + else if (expr->v_type == VAR_PARTIAL) + { + partial_T *partial = expr->vval.v_partial; + + s = partial_name(partial); + if (s == NULL || *s == NUL) + return FAIL; + if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL, + 0L, 0L, &dummy, TRUE, partial, NULL) == FAIL) + return FAIL; + } + else + { + s = tv_get_string_buf_chk(expr, buf); + if (s == NULL) + return FAIL; + s = skipwhite(s); + if (eval1_emsg(&s, rettv, TRUE) == FAIL) + return FAIL; + if (*s != NUL) /* check for trailing chars after expr */ + { + clear_tv(rettv); + semsg(_(e_invexpr2), s); + return FAIL; + } + } + return OK; +} + +/* + * Like eval_to_bool() but using a typval_T instead of a string. + * Works for string, funcref and partial. + */ + int +eval_expr_to_bool(typval_T *expr, int *error) +{ + typval_T rettv; + int res; + + if (eval_expr_typval(expr, NULL, 0, &rettv) == FAIL) + { + *error = TRUE; + return FALSE; + } + res = (tv_get_number_chk(&rettv, error) != 0); + clear_tv(&rettv); + return res; +} + +/* + * 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(tv_get_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; +#ifdef FEAT_FLOAT + char_u numbuf[NUMBUFLEN]; +#endif + + if (eval0(arg, &tv, nextcmd, TRUE) == FAIL) + retval = NULL; + else + { + if (convert && tv.v_type == VAR_LIST) + { + ga_init2(&ga, (int)sizeof(char), 80); + if (tv.vval.v_list != NULL) + { + list_join(&ga, tv.vval.v_list, (char_u *)"\n", TRUE, FALSE, 0); + if (tv.vval.v_list->lv_len > 0) + ga_append(&ga, NL); + } + ga_append(&ga, NUL); + retval = (char_u *)ga.ga_data; + } +#ifdef FEAT_FLOAT + else if (convert && tv.v_type == VAR_FLOAT) + { + vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv.vval.v_float); + retval = vim_strsave(numbuf); + } +#endif + else + retval = vim_strsave(tv_get_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; + funccal_entry_T funccal_entry; + + save_funccal(&funccal_entry); + if (use_sandbox) + ++sandbox; + ++textlock; + retval = eval_to_string(arg, nextcmd, FALSE); + if (use_sandbox) + --sandbox; + --textlock; + restore_funccal(); + return retval; +} + +/* + * Top level evaluation function, returning a number. + * Evaluates "expr" silently. + * Returns -1 for an error. + */ + varnumber_T +eval_to_number(char_u *expr) +{ + typval_T rettv; + varnumber_T retval; + char_u *p = skipwhite(expr); + + ++emsg_off; + + if (eval1(&p, &rettv, TRUE) == FAIL) + retval = -1; + else + { + retval = tv_get_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)) + internal_error("restore_vimvar()"); + else + hash_remove(&vimvarht, hi); + } +} + +#if defined(FEAT_SPELL) || defined(PROTO) +/* + * 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 = tv_get_string(&li->li_tv); + + li = li->li_next; + if (li == NULL) + return -1; + return (int)tv_get_number(&li->li_tv); +} +#endif + +/* + * 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) + VIM_CLEAR(tv); + + return tv; +} + + +/* + * Call some Vim script function and return the result in "*rettv". + * Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc] + * should have type VAR_UNKNOWN. + * Returns OK or FAIL. + */ + int +call_vim_function( + char_u *func, + int argc, + typval_T *argv, + typval_T *rettv) +{ + int doesrange; + int ret; + + rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */ + ret = call_func(func, (int)STRLEN(func), rettv, argc, argv, NULL, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &doesrange, TRUE, NULL, NULL); + if (ret == FAIL) + clear_tv(rettv); + + return ret; +} + +/* + * Call Vim script function "func" and return the result as a number. + * Returns -1 when calling the function fails. + * Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc] should + * have type VAR_UNKNOWN. + */ + varnumber_T +call_func_retnr( + char_u *func, + int argc, + typval_T *argv) +{ + typval_T rettv; + varnumber_T retval; + + if (call_vim_function(func, argc, argv, &rettv) == FAIL) + return -1; + + retval = tv_get_number_chk(&rettv, NULL); + clear_tv(&rettv); + return retval; +} + +#if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) \ + || defined(FEAT_COMPL_FUNC) || defined(PROTO) + +# if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) || defined(PROTO) +/* + * Call Vim script function "func" and return the result as a string. + * Returns NULL when calling the function fails. + * Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc] should + * have type VAR_UNKNOWN. + */ + void * +call_func_retstr( + char_u *func, + int argc, + typval_T *argv) +{ + typval_T rettv; + char_u *retval; + + if (call_vim_function(func, argc, argv, &rettv) == FAIL) + return NULL; + + retval = vim_strsave(tv_get_string(&rettv)); + clear_tv(&rettv); + return retval; +} +# endif + +/* + * Call Vim script function "func" and return the result as a List. + * Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc] should + * have type VAR_UNKNOWN. + * Returns NULL when there is something wrong. + */ + void * +call_func_retlist( + char_u *func, + int argc, + typval_T *argv) +{ + typval_T rettv; + + if (call_vim_function(func, argc, argv, &rettv) == FAIL) + return NULL; + + if (rettv.v_type != VAR_LIST) + { + clear_tv(&rettv); + return NULL; + } + + return rettv.vval.v_list; +} +#endif + + +#ifdef FEAT_FOLDING +/* + * 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; + varnumber_T 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 (int)retval; +} +#endif + +/* + * ":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; /* +=, -= or .= */ + 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 != ']') + { + internal_error("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) + { + semsg(_(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 != ',') + { + semsg(_(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. + */ + void +list_hashtable_vars( + hashtab_T *ht, + char *prefix, + int empty, + int *first) +{ + hashitem_T *hi; + dictitem_T *di; + int todo; + char_u buf[IOSIZE]; + + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + di = HI2DI(hi); + + // apply :filter /pat/ to variable name + vim_strncpy((char_u *)buf, (char_u *)prefix, IOSIZE - 1); + vim_strcat((char_u *)buf, di->di_key, IOSIZE); + if (message_filtered(buf)) + continue; + + 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, "", TRUE, first); +} + +/* + * List buffer variables. + */ + static void +list_buf_vars(int *first) +{ + list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", TRUE, first); +} + +/* + * List window variables. + */ + static void +list_win_vars(int *first) +{ + list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", TRUE, first); +} + +/* + * List tab page variables. + */ + static void +list_tab_vars(int *first) +{ + list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", TRUE, first); +} + +/* + * List Vim variables. + */ + static void +list_vim_vars(int *first) +{ + list_hashtable_vars(&vimvarht, "v:", FALSE, first); +} + +/* + * List script-local variables, if there is a script. + */ + static void +list_script_vars(int *first) +{ + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) + list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), + "s:", 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; + semsg(_(e_invarg2), arg); + break; + } + error = TRUE; + } + else + { + if (tofree != NULL) + name = tofree; + if (get_var_tv(name, len, &tv, NULL, 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: + semsg(_("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("", + arg == arg_subsc ? name : name_start, + tv.v_type, + s == NULL ? (char_u *)"" : s, + first); + *arg = c; + vim_free(tf); + } + clear_tv(&tv); + } + } + } + + vim_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) + semsg(_(e_invarg2), name - 1); + else + { + if (op != NULL && (*op == '+' || *op == '-')) + semsg(_(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 = tv_get_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) + vim_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; + vim_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 = (long)tv_get_number(tv); + s = tv_get_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 != '.')) + { + semsg(_(e_letwrong), op); + s = NULL; /* don't set the value */ + } + 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); + vim_free(stringval); + stringval = s; + } + } + } + if (s != NULL) + { + set_option_value(arg, n, s, opt_flags); + arg_end = p; + } + *p = c1; + vim_free(stringval); + } + } + + /* + * ":let @r = expr": Set register contents. + */ + else if (*arg == '@') + { + ++arg; + if (op != NULL && (*op == '+' || *op == '-')) + semsg(_(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 = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') + { + s = get_reg_contents(*arg == '@' ? '"' : *arg, GREG_EXPR_SRC); + if (s != NULL) + { + p = ptofree = concat_str(s, p); + vim_free(s); + } + } + if (p != NULL) + { + write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE); + arg_end = arg + 1; + } + vim_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 + semsg(_(e_invarg2), arg); + + return arg_end; +} + +/* + * 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_READ_ONLY: will not change the variable + * 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"! + */ + 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". */ + vim_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; + semsg(_(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; + /* Only pass &ht when we would write to the variable, it prevents autoload + * as well. */ + v = find_var(lp->ll_name, (flags & GLV_READ_ONLY) ? NULL : &ht, + flags & GLV_NO_AUTOLOAD); + if (v == NULL && !quiet) + semsg(_(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; + var1.v_type = VAR_UNKNOWN; + var2.v_type = VAR_UNKNOWN; + 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) + && !(lp->ll_tv->v_type == VAR_BLOB + && lp->ll_tv->vval.v_blob != NULL)) + { + if (!quiet) + emsg(_("E689: Can only index a List, Dictionary or Blob")); + 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 (tv_get_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)); + clear_tv(&var1); + return NULL; + } + if (rettv != NULL + && !(rettv->v_type == VAR_LIST + && rettv->vval.v_list != NULL) + && !(rettv->v_type == VAR_BLOB + && rettv->vval.v_blob != NULL)) + { + if (!quiet) + emsg(_("E709: [:] requires a List or Blob value")); + 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! */ + { + clear_tv(&var1); + return NULL; + } + if (tv_get_string_chk(&var2) == NULL) + { + /* not a number or string */ + clear_tv(&var1); + clear_tv(&var2); + return NULL; + } + } + lp->ll_range = TRUE; + } + else + lp->ll_range = FALSE; + + if (*p != ']') + { + if (!quiet) + emsg(_(e_missbrac)); + clear_tv(&var1); + 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 = tv_get_string_chk(&var1); /* is number or string */ + if (key == NULL) + { + 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) + { + semsg(_(e_illvar), name); + return NULL; + } + + /* Key does not exist in dict: may need to add it. */ + if (*p == '[' || *p == '.' || unlet) + { + if (!quiet) + semsg(_(e_dictkey), key); + clear_tv(&var1); + return NULL; + } + if (len == -1) + lp->ll_newkey = vim_strsave(key); + else + lp->ll_newkey = vim_strnsave(key, len); + clear_tv(&var1); + if (lp->ll_newkey == NULL) + p = NULL; + break; + } + /* existing variable, need to check if it can be changed */ + else if ((flags & GLV_READ_ONLY) == 0 + && var_check_ro(lp->ll_di->di_flags, name, FALSE)) + { + clear_tv(&var1); + return NULL; + } + + clear_tv(&var1); + lp->ll_tv = &lp->ll_di->di_tv; + } + else if (lp->ll_tv->v_type == VAR_BLOB) + { + long bloblen = blob_len(lp->ll_tv->vval.v_blob); + + /* + * Get the number and item for the only or first index of the List. + */ + if (empty1) + lp->ll_n1 = 0; + else + // is number or string + lp->ll_n1 = (long)tv_get_number(&var1); + clear_tv(&var1); + + if (lp->ll_n1 < 0 + || lp->ll_n1 > bloblen + || (lp->ll_range && lp->ll_n1 == bloblen)) + { + if (!quiet) + semsg(_(e_blobidx), lp->ll_n1); + clear_tv(&var2); + return NULL; + } + if (lp->ll_range && !lp->ll_empty2) + { + lp->ll_n2 = (long)tv_get_number(&var2); + clear_tv(&var2); + if (lp->ll_n2 < 0 + || lp->ll_n2 >= bloblen + || lp->ll_n2 < lp->ll_n1) + { + if (!quiet) + semsg(_(e_blobidx), lp->ll_n2); + return NULL; + } + } + lp->ll_blob = lp->ll_tv->vval.v_blob; + lp->ll_tv = NULL; + } + else + { + /* + * Get the number and item for the only or first index of the List. + */ + if (empty1) + lp->ll_n1 = 0; + else + /* is number or string */ + lp->ll_n1 = (long)tv_get_number(&var1); + 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) + { + clear_tv(&var2); + if (!quiet) + semsg(_(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 = (long)tv_get_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) + semsg(_(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) + semsg(_(e_listidx), lp->ll_n2); + return NULL; + } + } + + lp->ll_tv = &lp->ll_li->li_tv; + } + } + + clear_tv(&var1); + return p; +} + +/* + * Clear lval "lp" that was filled by get_lval(). + */ + void +clear_lval(lval_T *lp) +{ + vim_free(lp->ll_exp_name); + vim_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) + { + cc = *endp; + *endp = NUL; + if (lp->ll_blob != NULL) + { + int error = FALSE, val; + + if (op != NULL && *op != '=') + { + semsg(_(e_letwrong), op); + return; + } + + if (lp->ll_range && rettv->v_type == VAR_BLOB) + { + int il, ir; + + if (lp->ll_empty2) + lp->ll_n2 = blob_len(lp->ll_blob) - 1; + + if (lp->ll_n2 - lp->ll_n1 + 1 != blob_len(rettv->vval.v_blob)) + { + emsg(_("E972: Blob value does not have the right number of bytes")); + return; + } + if (lp->ll_empty2) + lp->ll_n2 = blob_len(lp->ll_blob); + + ir = 0; + for (il = lp->ll_n1; il <= lp->ll_n2; il++) + blob_set(lp->ll_blob, il, + blob_get(rettv->vval.v_blob, ir++)); + } + else + { + val = (int)tv_get_number_chk(rettv, &error); + if (!error) + { + garray_T *gap = &lp->ll_blob->bv_ga; + + // Allow for appending a byte. Setting a byte beyond + // the end is an error otherwise. + if (lp->ll_n1 < gap->ga_len + || (lp->ll_n1 == gap->ga_len + && ga_grow(&lp->ll_blob->bv_ga, 1) == OK)) + { + blob_set(lp->ll_blob, lp->ll_n1, val); + if (lp->ll_n1 == gap->ga_len) + ++gap->ga_len; + } + // error for invalid range was already given in get_lval() + } + } + } + else if (op != NULL && *op != '=') + { + typval_T tv; + + /* handle +=, -= and .= */ + di = NULL; + if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), + &tv, &di, TRUE, FALSE) == OK) + { + if ((di == NULL + || (!var_check_ro(di->di_flags, lp->ll_name, FALSE) + && !tv_check_lock(di->di_tv.v_lock, lp->ll_name, + FALSE))) + && 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, FALSE)) + ; + else if (lp->ll_range) + { + listitem_T *ll_li = lp->ll_li; + int ll_n1 = lp->ll_n1; + + /* + * Check whether any of the list items is locked + */ + for (ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) + { + if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) + return; + ri = ri->li_next; + if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) + break; + ll_li = ll_li->li_next; + ++ll_n1; + } + + /* + * 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. */ + if (list_append_number(lp->ll_list, 0) == FAIL) + { + ri = NULL; + break; + } + } + 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 != '=') + { + semsg(_(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) + { + vim_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) +{ + varnumber_T n; + char_u numbuf[NUMBUFLEN]; + char_u *s; + + /* Can't do anything with a Funcref, Dict, v:true on the right. */ + if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT + && tv2->v_type != VAR_SPECIAL) + { + switch (tv1->v_type) + { + case VAR_UNKNOWN: + case VAR_DICT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: + break; + + case VAR_BLOB: + if (*op != '+' || tv2->v_type != VAR_BLOB) + break; + // BLOB += BLOB + if (tv1->vval.v_blob != NULL && tv2->vval.v_blob != NULL) + { + blob_T *b1 = tv1->vval.v_blob; + blob_T *b2 = tv2->vval.v_blob; + int i, len = blob_len(b2); + for (i = 0; i < len; i++) + ga_append(&b1->bv_ga, blob_get(b2, i)); + } + return OK; + + 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 = tv_get_number(tv1); +#ifdef FEAT_FLOAT + 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 +#endif + { + if (*op == '+') + n += tv_get_number(tv2); + else + n -= tv_get_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 = tv_get_string(tv1); + s = concat_str(s, tv_get_string_buf(tv2, numbuf)); + clear_tv(tv1); + tv1->v_type = VAR_STRING; + tv1->vval.v_string = s; + } + return OK; + + case VAR_FLOAT: +#ifdef FEAT_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 = tv_get_number(tv2); + if (*op == '+') + tv1->vval.v_float += f; + else + tv1->vval.v_float -= f; + } +#endif + return OK; + } + } + + semsg(_(e_letwrong), op); + return FAIL; +} + +/* + * 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; + char_u *expr; + typval_T tv; + list_T *l; + + *errp = TRUE; /* default: there is an error */ + + fi = (forinfo_T *)alloc_clear(sizeof(forinfo_T)); + if (fi == NULL) + return NULL; + + 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) + { + if (tv.v_type == VAR_LIST) + { + l = tv.vval.v_list; + if (l == NULL) + { + // a null list is like an empty list: do nothing + 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; + } + } + else if (tv.v_type == VAR_BLOB) + { + fi->fi_bi = 0; + if (tv.vval.v_blob != NULL) + { + typval_T btv; + + // Make a copy, so that the iteration still works when the + // blob is changed. + blob_copy(&tv, &btv); + fi->fi_blob = btv.vval.v_blob; + } + clear_tv(&tv); + } + else + { + emsg(_(e_listreq)); + clear_tv(&tv); + } + } + } + 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; + + if (fi->fi_blob != NULL) + { + typval_T tv; + + if (fi->fi_bi >= blob_len(fi->fi_blob)) + return FALSE; + tv.v_type = VAR_NUMBER; + tv.v_lock = VAR_FIXED; + tv.vval.v_number = blob_get(fi->fi_blob, fi->fi_bi); + ++fi->fi_bi; + return ex_let_vars(arg, &tv, TRUE, + fi->fi_semicolon, fi->fi_varcount, NULL) == OK; + } + + 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); + } + if (fi != NULL && fi->fi_blob != NULL) + blob_unref(fi->fi_blob); + vim_free(fi); +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + + 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_EXPRESSION) + { + /* Autoload function/variable contains '#'. */ + break; + } + else if ((c == '<' || c == '#') + && xp->xp_context == EXPAND_FUNCTIONS + && vim_strchr(xp->xp_pattern, '(') == NULL) + { + /* Function name can start with "" and contain '#'. */ + 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; +} + +#endif /* FEAT_CMDL_COMPL */ + +/* + * ":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 + { + if (*arg == '$') + { + char_u *name = ++arg; + + if (get_env_len(&arg) == 0) + { + semsg(_(e_invarg2), name - 1); + return; + } + vim_unsetenv(name); + arg = skipwhite(arg); + continue; + } + + /* 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 (do_unlet(lp->ll_name, forceit) == FAIL) + ret = FAIL; + *name_end = cc; + } + else if ((lp->ll_list != NULL + && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE)) + || (lp->ll_dict != NULL + && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE))) + return FAIL; + else if (lp->ll_range) + { + listitem_T *li; + listitem_T *ll_li = lp->ll_li; + int ll_n1 = lp->ll_n1; + + while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) + { + li = ll_li->li_next; + if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) + return FAIL; + ll_li = li; + ++ll_n1; + } + + /* 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; + dict_T *d; + dictitem_T *di; + + ht = find_var_ht(name, &varname); + if (ht != NULL && *varname != NUL) + { + d = get_current_funccal_dict(ht); + if (d == NULL) + { + if (ht == &globvarht) + d = &globvardict; + else if (ht == &compat_hashtab) + d = &vimvardict; + else + { + di = find_var_in_ht(ht, *name, (char_u *)"", FALSE); + d = di == NULL ? NULL : di->di_tv.vval.v_dict; + } + if (d == NULL) + { + internal_error("do_unlet()"); + return FAIL; + } + } + hi = hash_find(ht, varname); + if (HASHITEM_EMPTY(hi)) + hi = find_hi_in_scoped_ht(name, &ht); + if (hi != NULL && !HASHITEM_EMPTY(hi)) + { + di = HI2DI(hi); + if (var_check_fixed(di->di_flags, name, FALSE) + || var_check_ro(di->di_flags, name, FALSE) + || tv_check_lock(d->dv_lock, name, FALSE)) + return FAIL; + + delete_var(ht, hi); + return OK; + } + } + if (forceit) + return OK; + semsg(_("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. */ + di = find_var(lp->ll_name, NULL, TRUE); + if (di == NULL) + ret = FAIL; + else if ((di->di_flags & DI_FLAGS_FIX) + && di->di_tv.v_type != VAR_DICT + && di->di_tv.v_type != VAR_LIST) + /* For historic reasons this error is not given for a list or dict. + * E.g., the b: dict could be locked/unlocked. */ + semsg(_("E940: Cannot lock or unlock variable %s"), lp->ll_name); + 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; + blob_T *b; + 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_UNKNOWN: + case VAR_NUMBER: + case VAR_STRING: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_FLOAT: + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: + break; + + case VAR_BLOB: + if ((b = tv->vval.v_blob) != NULL) + { + if (lock) + b->bv_lock |= VAR_LOCKED; + else + b->bv_lock &= ~VAR_LOCKED; + } + break; + 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; +} + +#if (defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)) || defined(PROTO) +/* + * 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); +} +#endif + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + +/* + * 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 *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) + { + vim_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); + } + + /* 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); + + VIM_CLEAR(varnamebuf); + varnamebuflen = 0; + return NULL; +} + +#endif /* FEAT_CMDL_COMPL */ + +/* + * Return TRUE if "pat" matches "text". + * Does not use 'cpo' and always uses 'magic'. + */ + static int +pattern_match(char_u *pat, char_u *text, int ic) +{ + int matches = FALSE; + char_u *save_cpo; + regmatch_T regmatch; + + /* avoid 'l' flag in 'cpoptions' */ + save_cpo = p_cpo; + p_cpo = (char_u *)""; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) + { + regmatch.rm_ic = ic; + matches = vim_regexec_nl(®match, text, (colnr_T)0); + vim_regfree(regmatch.regprog); + } + p_cpo = save_cpo; + return matches; +} + +/* + * 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. + */ + int +eval0( + char_u *arg, + typval_T *rettv, + char_u **nextcmd, + int evaluate) +{ + int ret; + char_u *p; + int did_emsg_before = did_emsg; + int called_emsg_before = called_emsg; + + 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, or we already gave a more specific error. + * Also check called_emsg for when using assert_fails(). + */ + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) + semsg(_(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. + */ + 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 (tv_get_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 (tv_get_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 (tv_get_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 (tv_get_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 (tv_get_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; + int ic; + + /* + * 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; + i = p[len]; + if (!isalnum(i) && i != '_') + { + 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) + { + int ret = typval_compare(rettv, &var2, type, type_is, ic); + + clear_tv(&var2); + return ret; + } + } + + 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; + varnumber_T n1, n2; +#ifdef FEAT_FLOAT + float_T f1 = 0, f2 = 0; +#endif + 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 + && rettv->v_type != VAR_BLOB)) +#ifdef FEAT_FLOAT + && (op == '.' || rettv->v_type != VAR_FLOAT) +#endif + ) + { + /* 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 && tv_get_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 = tv_get_string_buf(rettv, buf1); /* already checked */ + s2 = tv_get_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_BLOB + && var2.v_type == VAR_BLOB) + { + blob_T *b1 = rettv->vval.v_blob; + blob_T *b2 = var2.vval.v_blob; + blob_T *b = blob_alloc(); + int i; + + if (b != NULL) + { + for (i = 0; i < blob_len(b1); i++) + ga_append(&b->bv_ga, blob_get(b1, i)); + for (i = 0; i < blob_len(b2); i++) + ga_append(&b->bv_ga, blob_get(b2, i)); + + clear_tv(rettv); + rettv_blob_set(rettv, b); + } + } + 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; + +#ifdef FEAT_FLOAT + if (rettv->v_type == VAR_FLOAT) + { + f1 = rettv->vval.v_float; + n1 = 0; + } + else +#endif + { + n1 = tv_get_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; + } +#ifdef FEAT_FLOAT + if (var2.v_type == VAR_FLOAT) + f1 = n1; +#endif + } +#ifdef FEAT_FLOAT + if (var2.v_type == VAR_FLOAT) + { + f2 = var2.vval.v_float; + n2 = 0; + } + else +#endif + { + n2 = tv_get_number_chk(&var2, &error); + if (error) + { + clear_tv(rettv); + clear_tv(&var2); + return FAIL; + } +#ifdef FEAT_FLOAT + if (rettv->v_type == VAR_FLOAT) + f2 = n2; +#endif + } + clear_tv(rettv); + +#ifdef FEAT_FLOAT + /* 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 +#endif + { + 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; + varnumber_T n1, n2; +#ifdef FEAT_FLOAT + int use_float = FALSE; + float_T f1 = 0, f2; +#endif + 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) + { +#ifdef FEAT_FLOAT + if (rettv->v_type == VAR_FLOAT) + { + f1 = rettv->vval.v_float; + use_float = TRUE; + n1 = 0; + } + else +#endif + n1 = tv_get_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) + { +#ifdef FEAT_FLOAT + if (var2.v_type == VAR_FLOAT) + { + if (!use_float) + { + f1 = n1; + use_float = TRUE; + } + f2 = var2.vval.v_float; + n2 = 0; + } + else +#endif + { + n2 = tv_get_number_chk(&var2, &error); + clear_tv(&var2); + if (error) + return FAIL; +#ifdef FEAT_FLOAT + if (use_float) + f2 = n2; +#endif + } + + /* + * Compute the result. + * When either side is a float the result is a float. + */ +#ifdef FEAT_FLOAT + if (use_float) + { + if (op == '*') + f1 = f1 * f2; + else if (op == '/') + { +# ifdef VMS + /* VMS crashes on divide by zero, work around it */ + if (f2 == 0.0) + { + if (f1 == 0) + f1 = -1 * __F_FLT_MAX - 1L; /* similar to NaN */ + else if (f1 < 0) + f1 = -1 * __F_FLT_MAX; + else + f1 = __F_FLT_MAX; + } + else + f1 = f1 / f2; +# else + /* We rely on the floating point library to handle divide + * by zero to result in "inf" and not a crash. */ + f1 = f1 / f2; +# endif + } + else + { + emsg(_("E804: Cannot use '%' with Float")); + return FAIL; + } + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = f1; + } + else +#endif + { + if (op == '*') + n1 = n1 * n2; + else if (op == '/') + { + if (n2 == 0) /* give an error message? */ + { + if (n1 == 0) + n1 = VARNUM_MIN; /* similar to NaN */ + else if (n1 < 0) + n1 = -VARNUM_MAX; + else + n1 = VARNUM_MAX; + } + 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 + * 0zFFFFFFFF Blob 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 UNUSED) /* after "." operator */ +{ + varnumber_T 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': + { +#ifdef FEAT_FLOAT + 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 +#endif + if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z')) + { + char_u *bp; + blob_T *blob = NULL; // init for gcc + + // Blob constant: 0z0123456789abcdef + if (evaluate) + blob = blob_alloc(); + for (bp = *arg + 2; vim_isxdigit(bp[0]); bp += 2) + { + if (!vim_isxdigit(bp[1])) + { + if (blob != NULL) + { + emsg(_("E973: Blob literal should have an even number of hex characters")); + ga_clear(&blob->bv_ga); + VIM_CLEAR(blob); + } + ret = FAIL; + break; + } + if (blob != NULL) + ga_append(&blob->bv_ga, + (hex2nr(*bp) << 4) + hex2nr(*(bp+1))); + if (bp[2] == '.' && vim_isxdigit(bp[3])) + ++bp; + } + if (blob != NULL) + rettv_blob_set(rettv, blob); + *arg = bp; + } + else + { + // decimal, hex or octal number + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0); + *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; + + /* + * Lambda: {arg, arg -> expr} + * Dictionary: {key: val, key: val} + */ + case '{': ret = get_lambda_tv(arg, rettv, evaluate); + if (ret == NOTDONE) + ret = dict_get_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, + GREG_EXPR_SRC); + } + 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! */ + { + partial_T *partial; + + if (!evaluate) + check_vars(s, len); + + /* If "s" is the name of a variable of type VAR_FUNC + * use its contents. */ + s = deref_func_name(s, &len, &partial, !evaluate); + + /* Need to make a copy, in case evaluating the arguments makes + * the name invalid. */ + s = vim_strsave(s); + if (s == NULL) + ret = FAIL; + else + /* Invoke the function. */ + ret = get_func_tv(s, len, rettv, arg, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &len, evaluate, partial, NULL); + vim_free(s); + + /* 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 = NULL; + 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, NULL, TRUE, FALSE); + else + { + check_vars(s, len); + ret = OK; + } + } + vim_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; + varnumber_T val = 0; +#ifdef FEAT_FLOAT + float_T f = 0.0; + + if (rettv->v_type == VAR_FLOAT) + f = rettv->vval.v_float; + else +#endif + val = tv_get_number_chk(rettv, &error); + if (error) + { + clear_tv(rettv); + ret = FAIL; + } + else + { + while (end_leader > start_leader) + { + --end_leader; + if (*end_leader == '!') + { +#ifdef FEAT_FLOAT + if (rettv->v_type == VAR_FLOAT) + f = !f; + else +#endif + val = !val; + } + else if (*end_leader == '-') + { +#ifdef FEAT_FLOAT + if (rettv->v_type == VAR_FLOAT) + f = -f; + else +#endif + val = -val; + } + } +#ifdef FEAT_FLOAT + if (rettv->v_type == VAR_FLOAT) + { + clear_tv(rettv); + rettv->vval.v_float = f; + } + else +#endif + { + 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 i; + long n1, n2 = 0; + long len = -1; + int range = FALSE; + char_u *s; + char_u *key = NULL; + + switch (rettv->v_type) + { + case VAR_FUNC: + case VAR_PARTIAL: + if (verbose) + emsg(_("E695: Cannot index a Funcref")); + return FAIL; + case VAR_FLOAT: +#ifdef FEAT_FLOAT + if (verbose) + emsg(_(e_float_as_string)); + return FAIL; +#endif + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: + if (verbose) + emsg(_("E909: Cannot index a special variable")); + return FAIL; + case VAR_UNKNOWN: + if (evaluate) + return FAIL; + /* FALLTHROUGH */ + + case VAR_STRING: + case VAR_NUMBER: + case VAR_LIST: + case VAR_DICT: + case VAR_BLOB: + break; + } + + init_tv(&var1); + init_tv(&var2); + 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 && tv_get_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 && tv_get_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 = tv_get_number(&var1); + clear_tv(&var1); + } + if (range) + { + if (empty2) + n2 = -1; + else + { + n2 = tv_get_number(&var2); + clear_tv(&var2); + } + } + + switch (rettv->v_type) + { + case VAR_UNKNOWN: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_FLOAT: + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: + break; /* not evaluating, skipping over subscript */ + + case VAR_NUMBER: + case VAR_STRING: + s = tv_get_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_BLOB: + len = blob_len(rettv->vval.v_blob); + if (range) + { + // The resulting variable is a sub-blob. 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 - 1; + if (n1 >= len || n2 < 0 || n1 > n2) + { + clear_tv(rettv); + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; + } + else + { + blob_T *blob = blob_alloc(); + + if (blob != NULL) + { + if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL) + { + blob_free(blob); + return FAIL; + } + blob->bv_ga.ga_len = n2 - n1 + 1; + for (i = n1; i <= n2; i++) + blob_set(blob, i - n1, + blob_get(rettv->vval.v_blob, i)); + + clear_tv(rettv); + rettv_blob_set(rettv, blob); + } + } + } + else + { + // The resulting variable is a byte value. + // If the index is too big or negative that is an error. + if (n1 < 0) + n1 = len + n1; + if (n1 < len && n1 >= 0) + { + int v = blob_get(rettv->vval.v_blob, n1); + + clear_tv(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = v; + } + else + semsg(_(e_blobidx), n1); + } + 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) + semsg(_(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; + for (item = list_find(rettv->vval.v_list, n1); + n1 <= n2; ++n1) + { + if (list_append_tv(l, &item->li_tv) == FAIL) + { + list_free(l); + return FAIL; + } + item = item->li_next; + } + clear_tv(rettv); + rettv_list_set(rettv, l); + } + 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 = tv_get_string_chk(&var1); + if (key == NULL) + { + clear_tv(&var1); + return FAIL; + } + } + + item = dict_find(rettv->vval.v_dict, key, (int)len); + + if (item == NULL && verbose) + semsg(_(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. + */ + 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) + semsg(_("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) + semsg(_("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 != '"') + { + semsg(_("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 if (*p == 'u') + n = 4; + else + n = 8; + 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, TRUE); + if (extra != 0) + { + name += extra; + break; + } + /* FALLTHROUGH */ + + default: MB_COPY_CHAR(p, name); + break; + } + } + else + MB_COPY_CHAR(p, name); + + } + *name = NUL; + if (*p != NUL) /* just in case */ + ++p; + *arg = p; + + 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 != '\'') + { + semsg(_("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; +} + +/* + * Return the function name of the partial. + */ + char_u * +partial_name(partial_T *pt) +{ + if (pt->pt_name != NULL) + return pt->pt_name; + return pt->pt_func->uf_name; +} + + static void +partial_free(partial_T *pt) +{ + int i; + + for (i = 0; i < pt->pt_argc; ++i) + clear_tv(&pt->pt_argv[i]); + vim_free(pt->pt_argv); + dict_unref(pt->pt_dict); + if (pt->pt_name != NULL) + { + func_unref(pt->pt_name); + vim_free(pt->pt_name); + } + else + func_ptr_unref(pt->pt_func); + vim_free(pt); +} + +/* + * Unreference a closure: decrement the reference count and free it when it + * becomes zero. + */ + void +partial_unref(partial_T *pt) +{ + if (pt != NULL && --pt->pt_refcount <= 0) + partial_free(pt); +} + +static int tv_equal_recurse_limit; + + static int +func_equal( + typval_T *tv1, + typval_T *tv2, + int ic) /* ignore case */ +{ + char_u *s1, *s2; + dict_T *d1, *d2; + int a1, a2; + int i; + + /* empty and NULL function name considered the same */ + s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string + : partial_name(tv1->vval.v_partial); + if (s1 != NULL && *s1 == NUL) + s1 = NULL; + s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string + : partial_name(tv2->vval.v_partial); + if (s2 != NULL && *s2 == NUL) + s2 = NULL; + if (s1 == NULL || s2 == NULL) + { + if (s1 != s2) + return FALSE; + } + else if (STRCMP(s1, s2) != 0) + return FALSE; + + /* empty dict and NULL dict is different */ + d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict; + d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict; + if (d1 == NULL || d2 == NULL) + { + if (d1 != d2) + return FALSE; + } + else if (!dict_equal(d1, d2, ic, TRUE)) + return FALSE; + + /* empty list and no list considered the same */ + a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc; + a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc; + if (a1 != a2) + return FALSE; + for (i = 0; i < a1; ++i) + if (!tv_equal(tv1->vval.v_partial->pt_argv + i, + tv2->vval.v_partial->pt_argv + i, ic, TRUE)) + return FALSE; + + return TRUE; +} + +/* + * 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. + */ + 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; + + /* 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; + } + + /* For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and + * arguments. */ + if ((tv1->v_type == VAR_FUNC + || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL)) + && (tv2->v_type == VAR_FUNC + || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL))) + { + ++recursive_cnt; + r = func_equal(tv1, tv2, ic); + --recursive_cnt; + return r; + } + + if (tv1->v_type != tv2->v_type) + return FALSE; + + 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_BLOB: + return blob_equal(tv1->vval.v_blob, tv2->vval.v_blob); + + case VAR_NUMBER: + return tv1->vval.v_number == tv2->vval.v_number; + + case VAR_STRING: + s1 = tv_get_string_buf(tv1, buf1); + s2 = tv_get_string_buf(tv2, buf2); + return ((ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) == 0); + + case VAR_SPECIAL: + return tv1->vval.v_number == tv2->vval.v_number; + + case VAR_FLOAT: +#ifdef FEAT_FLOAT + return tv1->vval.v_float == tv2->vval.v_float; +#endif + case VAR_JOB: +#ifdef FEAT_JOB_CHANNEL + return tv1->vval.v_job == tv2->vval.v_job; +#endif + case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL + return tv1->vval.v_channel == tv2->vval.v_channel; +#endif + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_UNKNOWN: + break; + } + + /* VAR_UNKNOWN can be the result of a invalid expression, let's say it + * does not equal anything, not even itself. */ + return FALSE; +} + +/* + * Return the next (unique) copy ID. + * Used for serializing nested structures. + */ + int +get_copyID(void) +{ + current_copyID += COPYID_INC; + return current_copyID; +} + +/* + * 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. + * When "testing" is TRUE this is called from test_garbagecollect_now(). + * Return TRUE if some memory was freed. + */ + int +garbage_collect(int testing) +{ + int copyID; + int abort = FALSE; + buf_T *buf; + win_T *wp; + int i; + int did_free = FALSE; + tabpage_T *tp; + + if (!testing) + { + /* 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. */ + copyID = get_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. */ + abort = abort || set_ref_in_previous_funccal(copyID); + + /* script-local variables */ + for (i = 1; i <= ga_scripts.ga_len; ++i) + abort = abort || set_ref_in_ht(&SCRIPT_VARS(i), copyID, NULL); + + /* buffer-local variables */ + FOR_ALL_BUFFERS(buf) + abort = abort || set_ref_in_item(&buf->b_bufvar.di_tv, copyID, + NULL, NULL); + + /* window-local variables */ + FOR_ALL_TAB_WINDOWS(tp, wp) + abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID, + NULL, NULL); + if (aucmd_win != NULL) + abort = abort || set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID, + NULL, NULL); + + /* tabpage-local variables */ + FOR_ALL_TABPAGES(tp) + abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID, + NULL, NULL); + /* global variables */ + abort = abort || set_ref_in_ht(&globvarht, copyID, NULL); + + /* function-local variables */ + abort = abort || set_ref_in_call_stack(copyID); + + /* named functions (matters for closures) */ + abort = abort || set_ref_in_functions(copyID); + + /* function call arguments, if v:testing is set. */ + abort = abort || set_ref_in_func_args(copyID); + + /* v: vars */ + abort = abort || set_ref_in_ht(&vimvarht, copyID, NULL); + +#ifdef FEAT_LUA + abort = abort || set_ref_in_lua(copyID); +#endif + +#ifdef FEAT_PYTHON + abort = abort || set_ref_in_python(copyID); +#endif + +#ifdef FEAT_PYTHON3 + abort = abort || set_ref_in_python3(copyID); +#endif + +#ifdef FEAT_JOB_CHANNEL + abort = abort || set_ref_in_channel(copyID); + abort = abort || set_ref_in_job(copyID); +#endif +#ifdef FEAT_NETBEANS_INTG + abort = abort || set_ref_in_nb_channel(copyID); +#endif + +#ifdef FEAT_TIMERS + abort = abort || set_ref_in_timer(copyID); +#endif + +#ifdef FEAT_QUICKFIX + abort = abort || set_ref_in_quickfix(copyID); +#endif + +#ifdef FEAT_TERMINAL + abort = abort || set_ref_in_term(copyID); +#endif + + if (!abort) + { + /* + * 2. Free lists and dictionaries that are not referenced. + */ + did_free = free_unref_items(copyID); + + /* + * 3. Check if any funccal can be freed now. + * This may call us back recursively. + */ + free_unref_funccal(copyID, testing); + } + else if (p_verbose > 0) + { + verb_msg(_("Not enough memory to set references, garbage collection aborted!")); + } + + return did_free; +} + +/* + * Free lists, dictionaries, channels and jobs that are no longer referenced. + */ + static int +free_unref_items(int copyID) +{ + int did_free = FALSE; + + /* Let all "free" functions know that we are here. This means no + * dictionaries, lists, channels or jobs are to be freed, because we will + * do that here. */ + in_free_unref_items = TRUE; + + /* + * PASS 1: free the contents of the items. We don't free the items + * themselves yet, so that it is possible to decrement refcount counters + */ + + /* Go through the list of dicts and free items without the copyID. */ + did_free |= dict_free_nonref(copyID); + + /* Go through the list of lists and free items without the copyID. */ + did_free |= list_free_nonref(copyID); + +#ifdef FEAT_JOB_CHANNEL + /* Go through the list of jobs and free items without the copyID. This + * must happen before doing channels, because jobs refer to channels, but + * the reference from the channel to the job isn't tracked. */ + did_free |= free_unused_jobs_contents(copyID, COPYID_MASK); + + /* Go through the list of channels and free items without the copyID. */ + did_free |= free_unused_channels_contents(copyID, COPYID_MASK); +#endif + + /* + * PASS 2: free the items themselves. + */ + dict_free_items(copyID); + list_free_items(copyID); + +#ifdef FEAT_JOB_CHANNEL + /* Go through the list of jobs and free items without the copyID. This + * must happen before doing channels, because jobs refer to channels, but + * the reference from the channel to the job isn't tracked. */ + free_unused_jobs(copyID, COPYID_MASK); + + /* Go through the list of channels and free items without the copyID. */ + free_unused_channels(copyID, COPYID_MASK); +#endif + + in_free_unref_items = FALSE; + + return did_free; +} + +/* + * Mark all lists and dicts referenced through hashtab "ht" with "copyID". + * "list_stack" is used to add lists to be marked. Can be NULL. + * + * Returns TRUE if setting references failed somehow. + */ + int +set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack) +{ + int todo; + int abort = FALSE; + hashitem_T *hi; + hashtab_T *cur_ht; + ht_stack_T *ht_stack = NULL; + ht_stack_T *tempitem; + + cur_ht = ht; + for (;;) + { + if (!abort) + { + /* Mark each item in the hashtab. If the item contains a hashtab + * it is added to ht_stack, if it contains a list it is added to + * list_stack. */ + todo = (int)cur_ht->ht_used; + for (hi = cur_ht->ht_array; todo > 0; ++hi) + if (!HASHITEM_EMPTY(hi)) + { + --todo; + abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID, + &ht_stack, list_stack); + } + } + + if (ht_stack == NULL) + break; + + /* take an item from the stack */ + cur_ht = ht_stack->ht; + tempitem = ht_stack; + ht_stack = ht_stack->prev; + free(tempitem); + } + + return abort; +} + +/* + * Mark all lists and dicts referenced through list "l" with "copyID". + * "ht_stack" is used to add hashtabs to be marked. Can be NULL. + * + * Returns TRUE if setting references failed somehow. + */ + int +set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack) +{ + listitem_T *li; + int abort = FALSE; + list_T *cur_l; + list_stack_T *list_stack = NULL; + list_stack_T *tempitem; + + cur_l = l; + for (;;) + { + if (!abort) + /* Mark each item in the list. If the item contains a hashtab + * it is added to ht_stack, if it contains a list it is added to + * list_stack. */ + for (li = cur_l->lv_first; !abort && li != NULL; li = li->li_next) + abort = abort || set_ref_in_item(&li->li_tv, copyID, + ht_stack, &list_stack); + if (list_stack == NULL) + break; + + /* take an item from the stack */ + cur_l = list_stack->list; + tempitem = list_stack; + list_stack = list_stack->prev; + free(tempitem); + } + + return abort; +} + +/* + * Mark all lists and dicts referenced through typval "tv" with "copyID". + * "list_stack" is used to add lists to be marked. Can be NULL. + * "ht_stack" is used to add hashtabs to be marked. Can be NULL. + * + * Returns TRUE if setting references failed somehow. + */ + int +set_ref_in_item( + typval_T *tv, + int copyID, + ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + int abort = FALSE; + + if (tv->v_type == VAR_DICT) + { + dict_T *dd = tv->vval.v_dict; + + if (dd != NULL && dd->dv_copyID != copyID) + { + /* Didn't see this dict yet. */ + dd->dv_copyID = copyID; + if (ht_stack == NULL) + { + abort = set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack); + } + else + { + ht_stack_T *newitem = (ht_stack_T*)malloc(sizeof(ht_stack_T)); + if (newitem == NULL) + abort = TRUE; + else + { + newitem->ht = &dd->dv_hashtab; + newitem->prev = *ht_stack; + *ht_stack = newitem; + } + } + } + } + else if (tv->v_type == VAR_LIST) + { + list_T *ll = tv->vval.v_list; + + if (ll != NULL && ll->lv_copyID != copyID) + { + /* Didn't see this list yet. */ + ll->lv_copyID = copyID; + if (list_stack == NULL) + { + abort = set_ref_in_list(ll, copyID, ht_stack); + } + else + { + list_stack_T *newitem = (list_stack_T*)malloc( + sizeof(list_stack_T)); + if (newitem == NULL) + abort = TRUE; + else + { + newitem->list = ll; + newitem->prev = *list_stack; + *list_stack = newitem; + } + } + } + } + else if (tv->v_type == VAR_FUNC) + { + abort = set_ref_in_func(tv->vval.v_string, NULL, copyID); + } + else if (tv->v_type == VAR_PARTIAL) + { + partial_T *pt = tv->vval.v_partial; + int i; + + /* A partial does not have a copyID, because it cannot contain itself. + */ + if (pt != NULL) + { + abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); + + if (pt->pt_dict != NULL) + { + typval_T dtv; + + dtv.v_type = VAR_DICT; + dtv.vval.v_dict = pt->pt_dict; + set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + + for (i = 0; i < pt->pt_argc; ++i) + abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID, + ht_stack, list_stack); + } + } +#ifdef FEAT_JOB_CHANNEL + else if (tv->v_type == VAR_JOB) + { + job_T *job = tv->vval.v_job; + typval_T dtv; + + if (job != NULL && job->jv_copyID != copyID) + { + job->jv_copyID = copyID; + if (job->jv_channel != NULL) + { + dtv.v_type = VAR_CHANNEL; + dtv.vval.v_channel = job->jv_channel; + set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + if (job->jv_exit_partial != NULL) + { + dtv.v_type = VAR_PARTIAL; + dtv.vval.v_partial = job->jv_exit_partial; + set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + } + } + else if (tv->v_type == VAR_CHANNEL) + { + channel_T *ch =tv->vval.v_channel; + ch_part_T part; + typval_T dtv; + jsonq_T *jq; + cbq_T *cq; + + if (ch != NULL && ch->ch_copyID != copyID) + { + ch->ch_copyID = copyID; + for (part = PART_SOCK; part < PART_COUNT; ++part) + { + for (jq = ch->ch_part[part].ch_json_head.jq_next; jq != NULL; + jq = jq->jq_next) + set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack); + for (cq = ch->ch_part[part].ch_cb_head.cq_next; cq != NULL; + cq = cq->cq_next) + if (cq->cq_partial != NULL) + { + dtv.v_type = VAR_PARTIAL; + dtv.vval.v_partial = cq->cq_partial; + set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + if (ch->ch_part[part].ch_partial != NULL) + { + dtv.v_type = VAR_PARTIAL; + dtv.vval.v_partial = ch->ch_part[part].ch_partial; + set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + } + if (ch->ch_partial != NULL) + { + dtv.v_type = VAR_PARTIAL; + dtv.vval.v_partial = ch->ch_partial; + set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + if (ch->ch_close_partial != NULL) + { + dtv.v_type = VAR_PARTIAL; + dtv.vval.v_partial = ch->ch_close_partial; + set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + } + } +#endif + return abort; +} + + static char * +get_var_special_name(int nr) +{ + switch (nr) + { + case VVAL_FALSE: return "v:false"; + case VVAL_TRUE: return "v:true"; + case VVAL_NONE: return "v:none"; + case VVAL_NULL: return "v:null"; + } + internal_error("get_var_special_name()"); + return "42"; +} + +/* + * 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. + * When "copyID" is not NULL replace recursive lists and dicts with "...". + * When both "echo_style" and "composite_val" are FALSE, put quotes around + * stings as "string()", otherwise does not put quotes around strings, as + * ":echo" displays values. + * When "restore_copyID" is FALSE, repeated items in dictionaries and lists + * are replaced with "...". + * May return NULL. + */ + char_u * +echo_string_core( + typval_T *tv, + char_u **tofree, + char_u *numbuf, + int copyID, + int echo_style, + int restore_copyID, + int composite_val) +{ + static int recurse = 0; + char_u *r = NULL; + + if (recurse >= DICT_MAXNEST) + { + if (!did_echo_string_emsg) + { + /* Only give this message once for a recursive call to avoid + * flooding the user with errors. And stop iterating over lists + * and dicts. */ + did_echo_string_emsg = TRUE; + emsg(_("E724: variable nested too deep for displaying")); + } + *tofree = NULL; + return (char_u *)"{E724}"; + } + ++recurse; + + switch (tv->v_type) + { + case VAR_STRING: + if (echo_style && !composite_val) + { + *tofree = NULL; + r = tv->vval.v_string; + if (r == NULL) + r = (char_u *)""; + } + else + { + *tofree = string_quote(tv->vval.v_string, FALSE); + r = *tofree; + } + break; + + case VAR_FUNC: + if (echo_style) + { + *tofree = NULL; + r = tv->vval.v_string; + } + else + { + *tofree = string_quote(tv->vval.v_string, TRUE); + r = *tofree; + } + break; + + case VAR_PARTIAL: + { + partial_T *pt = tv->vval.v_partial; + char_u *fname = string_quote(pt == NULL ? NULL + : partial_name(pt), FALSE); + garray_T ga; + int i; + char_u *tf; + + ga_init2(&ga, 1, 100); + ga_concat(&ga, (char_u *)"function("); + if (fname != NULL) + { + ga_concat(&ga, fname); + vim_free(fname); + } + if (pt != NULL && pt->pt_argc > 0) + { + ga_concat(&ga, (char_u *)", ["); + for (i = 0; i < pt->pt_argc; ++i) + { + if (i > 0) + ga_concat(&ga, (char_u *)", "); + ga_concat(&ga, + tv2string(&pt->pt_argv[i], &tf, numbuf, copyID)); + vim_free(tf); + } + ga_concat(&ga, (char_u *)"]"); + } + if (pt != NULL && pt->pt_dict != NULL) + { + typval_T dtv; + + ga_concat(&ga, (char_u *)", "); + dtv.v_type = VAR_DICT; + dtv.vval.v_dict = pt->pt_dict; + ga_concat(&ga, tv2string(&dtv, &tf, numbuf, copyID)); + vim_free(tf); + } + ga_concat(&ga, (char_u *)")"); + + *tofree = ga.ga_data; + r = *tofree; + break; + } + + case VAR_BLOB: + r = blob2string(tv->vval.v_blob, tofree, numbuf); + 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 + && tv->vval.v_list->lv_len > 0) + { + *tofree = NULL; + r = (char_u *)"[...]"; + } + else + { + int old_copyID = tv->vval.v_list->lv_copyID; + + tv->vval.v_list->lv_copyID = copyID; + *tofree = list2string(tv, copyID, restore_copyID); + if (restore_copyID) + tv->vval.v_list->lv_copyID = old_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 + && tv->vval.v_dict->dv_hashtab.ht_used != 0) + { + *tofree = NULL; + r = (char_u *)"{...}"; + } + else + { + int old_copyID = tv->vval.v_dict->dv_copyID; + tv->vval.v_dict->dv_copyID = copyID; + *tofree = dict2string(tv, copyID, restore_copyID); + if (restore_copyID) + tv->vval.v_dict->dv_copyID = old_copyID; + r = *tofree; + } + break; + + case VAR_NUMBER: + case VAR_UNKNOWN: + *tofree = NULL; + r = tv_get_string_buf(tv, numbuf); + break; + + case VAR_JOB: + case VAR_CHANNEL: + *tofree = NULL; + r = tv_get_string_buf(tv, numbuf); + if (composite_val) + { + *tofree = string_quote(r, FALSE); + r = *tofree; + } + break; + + case VAR_FLOAT: +#ifdef FEAT_FLOAT + *tofree = NULL; + vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float); + r = numbuf; + break; +#endif + + case VAR_SPECIAL: + *tofree = NULL; + r = (char_u *)get_var_special_name(tv->vval.v_number); + break; + } + + if (--recurse == 0) + did_echo_string_emsg = FALSE; + 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. + * Does not put quotes around strings, as ":echo" displays values. + * When "copyID" is not NULL replace recursive lists and dicts with "...". + * May return NULL. + */ + char_u * +echo_string( + typval_T *tv, + char_u **tofree, + char_u *numbuf, + int copyID) +{ + return echo_string_core(tv, tofree, numbuf, copyID, TRUE, FALSE, FALSE); +} + +/* + * 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. + */ + char_u * +tv2string( + typval_T *tv, + char_u **tofree, + char_u *numbuf, + int copyID) +{ + return echo_string_core(tv, tofree, numbuf, copyID, FALSE, TRUE, FALSE); +} + +/* + * Return string "str" in ' quotes, doubling ' characters. + * If "str" is NULL an empty string is assumed. + * If "function" is TRUE make it function('string'). + */ + 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; +} + +#if defined(FEAT_FLOAT) || defined(PROTO) +/* + * 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. + */ + int +string2float( + char_u *text, + float_T *value) /* result stored here */ +{ + char *s = (char *)text; + float_T f; + + /* MS-Windows does not deal with "inf" and "nan" properly. */ + if (STRNICMP(text, "inf", 3) == 0) + { + *value = INFINITY; + return 3; + } + if (STRNICMP(text, "-inf", 3) == 0) + { + *value = -INFINITY; + return 4; + } + if (STRNICMP(text, "nan", 3) == 0) + { + *value = NAN; + return 3; + } + f = strtod(s, &s); + *value = f; + return (int)((char_u *)s - text); +} +#endif + +/* + * Get the value of an environment variable. + * "arg" is pointing to the '$'. It is advanced to after the name. + * If the environment variable was not set, silently assume it is empty. + * Return FAIL if the name is invalid. + */ + static int +get_env_tv(char_u **arg, typval_T *rettv, int evaluate) +{ + char_u *string = NULL; + int len; + int cc; + char_u *name; + int mustfree = FALSE; + + ++*arg; + name = *arg; + len = get_env_len(arg); + if (evaluate) + { + if (len == 0) + return FAIL; /* invalid empty name */ + + 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) + vim_free(string); + + /* next try expanding things like $VIM and ${HOME} */ + string = expand_env_save(name - 1); + if (string != NULL && *string == '$') + VIM_CLEAR(string); + } + name[len] = cc; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = string; + } + + return OK; +} + + + +/* + * Translate a String variable into a position. + * Returns NULL when there is an error. + */ + 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 = tv_get_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(); + /* In silent Ex mode topline is zero, but that's not a valid line + * number; use one instead. */ + pos.lnum = curwin->w_topline > 0 ? curwin->w_topline : 1; + return &pos; + } + else if (name[1] == '$') /* "w$": last visible line */ + { + validate_botline(); + /* In silent Ex mode botline is zero, return zero then. */ + pos.lnum = curwin->w_botline > 0 ? curwin->w_botline - 1 : 0; + 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. + */ + int +list2fpos( + typval_T *arg, + pos_T *posp, + int *fnump, + colnr_T *curswantp) +{ + list_T *l = arg->vval.v_list; + long i = 0; + long n; + + /* List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only + * there when "fnump" isn't NULL; "coladd" and "curswant" are optional. */ + if (arg->v_type != VAR_LIST + || l == NULL + || l->lv_len < (fnump == NULL ? 2 : 3) + || l->lv_len > (fnump == NULL ? 4 : 5)) + 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); /* off */ + if (n < 0) + posp->coladd = 0; + else + posp->coladd = n; + + if (curswantp != NULL) + *curswantp = list_find_nr(l, i + 1, NULL); /* curswant */ + + 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. + */ + 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 == ':') + { + /* "s:" is start of "s:var", but "n:" is not and can be used in + * slice "[n:]". Also "xx:" is not a namespace. */ + len = (int)(p - *arg); + if ((len == 1 && vim_strchr(NAMESPACE_CHAR, **arg) == NULL) + || len > 1) + break; + } + } + 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. + */ + 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); + // Only give an error when there is something, otherwise it will be + // reported at a higher level. + if (len == 0 && verbose && **arg != NUL) + semsg(_(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. + */ + 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; + int len; + + 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; + } + else if (br_nest == 0 && mb_nest == 0 && *p == ':') + { + /* "s:" is start of "s:var", but "n:" is not and can be used in + * slice "[n:]". Also "xx:" is not a namespace. But {ns}: is. */ + len = (int)(p - arg); + if ((len == 1 && vim_strchr(NAMESPACE_CHAR, *arg) == NULL) + || (len > 1 && p[-1] != '}')) + 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); + } + } + vim_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); + vim_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. + */ + 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 '}'). + */ + int +eval_isnamec1(int c) +{ + return (ASCII_ISALPHA(c) || c == '_'); +} + +/* + * Set number v: variable to "val". + */ + void +set_vim_var_nr(int idx, varnumber_T val) +{ + vimvars[idx].vv_nr = val; +} + +/* + * Get number v: variable value. + */ + varnumber_T +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. + * If the String variable has never been set, return an empty string. + * Never returns NULL; + */ + char_u * +get_vim_var_str(int idx) +{ + return tv_get_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; +} + +/* + * Get Dict v: variable value. Caller must take care of reference count when + * needed. + */ + dict_T * +get_vim_var_dict(int idx) +{ + return vimvars[idx].vv_dict; +} + +/* + * 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; +} + +/* + * Save variables that might be changed as a side effect. Used when executing + * a timer callback. + */ + void +save_vimvars(vimvars_save_T *vvsave) +{ + vvsave->vv_prevcount = vimvars[VV_PREVCOUNT].vv_nr; + vvsave->vv_count = vimvars[VV_COUNT].vv_nr; + vvsave->vv_count1 = vimvars[VV_COUNT1].vv_nr; +} + +/* + * Restore variables saved by save_vimvars(). + */ + void +restore_vimvars(vimvars_save_T *vvsave) +{ + vimvars[VV_PREVCOUNT].vv_nr = vvsave->vv_prevcount; + vimvars[VV_COUNT].vv_nr = vvsave->vv_count; + vimvars[VV_COUNT1].vv_nr = vvsave->vv_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) */ +{ + clear_tv(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_STRING; + 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) +{ + clear_tv(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_LIST; + vimvars[idx].vv_list = val; + if (val != NULL) + ++val->lv_refcount; +} + +/* + * Set Dictionary v: variable to "val". + */ + void +set_vim_var_dict(int idx, dict_T *val) +{ + clear_tv(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_DICT; + vimvars[idx].vv_dict = val; + if (val != NULL) + { + ++val->dv_refcount; + dict_set_items_ro(val); + } +} + +/* + * 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) + { + vim_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 += 10; /* " ++ff=unix" */ + 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->force_ff == 'u' ? "unix" + : eap->force_ff == 'd' ? "dos" + : "mac"); + 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. If OK is returned "rettv" must be cleared. + */ + int +get_var_tv( + char_u *name, + int len, /* length of "name" */ + typval_T *rettv, /* NULL when only checking existence */ + dictitem_T **dip, /* non-NULL when typval's dict item is needed */ + int verbose, /* may give error message */ + int no_autoload) /* do not use script autoloading */ +{ + int ret = OK; + typval_T *tv = NULL; + dictitem_T *v; + int cc; + + /* truncate the name, so that we can use strcmp() */ + cc = name[len]; + name[len] = NUL; + + /* + * Check for user-defined variables. + */ + v = find_var(name, NULL, no_autoload); + if (v != NULL) + { + tv = &v->di_tv; + if (dip != NULL) + *dip = v; + } + + if (tv == NULL) + { + if (rettv != NULL && verbose) + semsg(_(e_undefvar), name); + ret = FAIL; + } + else if (rettv != NULL) + copy_tv(tv, rettv); + + name[len] = cc; + + return ret; +} + +/* + * Check if variable "name[len]" is a local variable or an argument. + * If so, "*eval_lavars_used" is set to TRUE. + */ + static void +check_vars(char_u *name, int len) +{ + int cc; + char_u *varname; + hashtab_T *ht; + + if (eval_lavars_used == NULL) + return; + + /* truncate the name, so that we can use strcmp() */ + cc = name[len]; + name[len] = NUL; + + ht = find_var_ht(name, &varname); + if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht()) + { + if (find_var(name, NULL, TRUE) != NULL) + *eval_lavars_used = TRUE; + } + + name[len] = cc; +} + +/* + * 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) + */ + 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 + || rettv->v_type == VAR_PARTIAL))) + && !VIM_ISWHITE(*(*arg - 1))) + { + if (**arg == '(') + { + partial_T *pt = NULL; + + /* need to copy the funcref so that we can clear rettv */ + if (evaluate) + { + functv = *rettv; + rettv->v_type = VAR_UNKNOWN; + + /* Invoke the function. Recursive! */ + if (functv.v_type == VAR_PARTIAL) + { + pt = functv.vval.v_partial; + s = partial_name(pt); + } + else + 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, pt, 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; + } + } + } + + /* Turn "dict.Func" into a partial for "Func" bound to "dict". + * Don't do this when "Func" is already a partial that was bound + * explicitly (pt_auto is FALSE). */ + if (selfdict != NULL + && (rettv->v_type == VAR_FUNC + || (rettv->v_type == VAR_PARTIAL + && (rettv->vval.v_partial->pt_auto + || rettv->vval.v_partial->pt_dict == NULL)))) + selfdict = make_partial(selfdict, rettv); + + dict_unref(selfdict); + return ret; +} + +/* + * Allocate memory for a variable type-value, and make it empty (0 or NULL + * value). + */ + typval_T * +alloc_tv(void) +{ + return (typval_T *)alloc_clear((unsigned)sizeof(typval_T)); +} + +/* + * Allocate memory for a variable type-value, and assign a string to it. + * The string "s" must have been allocated, it is consumed. + * Return NULL for out of memory, the variable otherwise. + */ + static typval_T * +alloc_string_tv(char_u *s) +{ + typval_T *rettv; + + rettv = alloc_tv(); + if (rettv != NULL) + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = s; + } + else + vim_free(s); + return rettv; +} + +/* + * 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: + vim_free(varp->vval.v_string); + break; + case VAR_PARTIAL: + partial_unref(varp->vval.v_partial); + break; + case VAR_BLOB: + blob_unref(varp->vval.v_blob); + break; + case VAR_LIST: + list_unref(varp->vval.v_list); + break; + case VAR_DICT: + dict_unref(varp->vval.v_dict); + break; + case VAR_JOB: +#ifdef FEAT_JOB_CHANNEL + job_unref(varp->vval.v_job); + break; +#endif + case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL + channel_unref(varp->vval.v_channel); + break; +#endif + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_UNKNOWN: + case VAR_SPECIAL: + break; + } + vim_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: + VIM_CLEAR(varp->vval.v_string); + break; + case VAR_PARTIAL: + partial_unref(varp->vval.v_partial); + varp->vval.v_partial = NULL; + break; + case VAR_BLOB: + blob_unref(varp->vval.v_blob); + varp->vval.v_blob = 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: + case VAR_SPECIAL: + varp->vval.v_number = 0; + break; + case VAR_FLOAT: +#ifdef FEAT_FLOAT + varp->vval.v_float = 0.0; + break; +#endif + case VAR_JOB: +#ifdef FEAT_JOB_CHANNEL + job_unref(varp->vval.v_job); + varp->vval.v_job = NULL; +#endif + break; + case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL + channel_unref(varp->vval.v_channel); + varp->vval.v_channel = NULL; +#endif + case VAR_UNKNOWN: + break; + } + varp->v_lock = 0; + } +} + +/* + * Set the value of a variable to NULL without freeing items. + */ + void +init_tv(typval_T *varp) +{ + if (varp != NULL) + vim_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. + * tv_get_number_chk() is similar to tv_get_number(), but informs the + * caller of incompatible types: it sets *denote to TRUE if "denote" + * is not NULL or returns -1 otherwise. + */ + varnumber_T +tv_get_number(typval_T *varp) +{ + int error = FALSE; + + return tv_get_number_chk(varp, &error); /* return 0L on error */ +} + + varnumber_T +tv_get_number_chk(typval_T *varp, int *denote) +{ + varnumber_T n = 0L; + + switch (varp->v_type) + { + case VAR_NUMBER: + return varp->vval.v_number; + case VAR_FLOAT: +#ifdef FEAT_FLOAT + emsg(_("E805: Using a Float as a Number")); + break; +#endif + case VAR_FUNC: + case VAR_PARTIAL: + 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, + STR2NR_ALL, &n, NULL, 0); + 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; + case VAR_SPECIAL: + return varp->vval.v_number == VVAL_TRUE ? 1 : 0; + break; + case VAR_JOB: +#ifdef FEAT_JOB_CHANNEL + emsg(_("E910: Using a Job as a Number")); + break; +#endif + case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL + emsg(_("E913: Using a Channel as a Number")); + break; +#endif + case VAR_BLOB: + emsg(_("E974: Using a Blob as a Number")); + break; + case VAR_UNKNOWN: + internal_error("tv_get_number(UNKNOWN)"); + break; + } + if (denote == NULL) /* useful for values that must be unsigned */ + n = -1; + else + *denote = TRUE; + return n; +} + +#ifdef FEAT_FLOAT + float_T +tv_get_float(typval_T *varp) +{ + switch (varp->v_type) + { + case VAR_NUMBER: + return (float_T)(varp->vval.v_number); + case VAR_FLOAT: + return varp->vval.v_float; + case VAR_FUNC: + case VAR_PARTIAL: + emsg(_("E891: Using a Funcref as a Float")); + break; + case VAR_STRING: + emsg(_("E892: Using a String as a Float")); + break; + case VAR_LIST: + emsg(_("E893: Using a List as a Float")); + break; + case VAR_DICT: + emsg(_("E894: Using a Dictionary as a Float")); + break; + case VAR_SPECIAL: + emsg(_("E907: Using a special value as a Float")); + break; + case VAR_JOB: +# ifdef FEAT_JOB_CHANNEL + emsg(_("E911: Using a Job as a Float")); + break; +# endif + case VAR_CHANNEL: +# ifdef FEAT_JOB_CHANNEL + emsg(_("E914: Using a Channel as a Float")); + break; +# endif + case VAR_BLOB: + emsg(_("E975: Using a Blob as a Float")); + break; + case VAR_UNKNOWN: + internal_error("tv_get_float(UNKNOWN)"); + break; + } + return 0; +} +#endif + +/* + * Get the string value of a variable. + * If it is a Number variable, the number is converted into a string. + * tv_get_string() uses a single, static buffer. YOU CAN ONLY USE IT ONCE! + * tv_get_string_buf() uses a given buffer. + * If the String variable has never been set, return an empty string. + * Never returns NULL; + * tv_get_string_chk() and tv_get_string_buf_chk() are similar, but return + * NULL on error. + */ + char_u * +tv_get_string(typval_T *varp) +{ + static char_u mybuf[NUMBUFLEN]; + + return tv_get_string_buf(varp, mybuf); +} + + char_u * +tv_get_string_buf(typval_T *varp, char_u *buf) +{ + char_u *res = tv_get_string_buf_chk(varp, buf); + + return res != NULL ? res : (char_u *)""; +} + +/* + * Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! + */ + char_u * +tv_get_string_chk(typval_T *varp) +{ + static char_u mybuf[NUMBUFLEN]; + + return tv_get_string_buf_chk(varp, mybuf); +} + + char_u * +tv_get_string_buf_chk(typval_T *varp, char_u *buf) +{ + switch (varp->v_type) + { + case VAR_NUMBER: + vim_snprintf((char *)buf, NUMBUFLEN, "%lld", + (long_long_T)varp->vval.v_number); + return buf; + case VAR_FUNC: + case VAR_PARTIAL: + 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: +#ifdef FEAT_FLOAT + emsg(_(e_float_as_string)); + break; +#endif + case VAR_STRING: + if (varp->vval.v_string != NULL) + return varp->vval.v_string; + return (char_u *)""; + case VAR_SPECIAL: + STRCPY(buf, get_var_special_name(varp->vval.v_number)); + return buf; + case VAR_BLOB: + emsg(_("E976: using Blob as a String")); + break; + case VAR_JOB: +#ifdef FEAT_JOB_CHANNEL + { + job_T *job = varp->vval.v_job; + char *status; + + if (job == NULL) + return (char_u *)"no process"; + status = job->jv_status == JOB_FAILED ? "fail" + : job->jv_status >= JOB_ENDED ? "dead" + : "run"; +# ifdef UNIX + vim_snprintf((char *)buf, NUMBUFLEN, + "process %ld %s", (long)job->jv_pid, status); +# elif defined(WIN32) + vim_snprintf((char *)buf, NUMBUFLEN, + "process %ld %s", + (long)job->jv_proc_info.dwProcessId, + status); +# else + /* fall-back */ + vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status); +# endif + return buf; + } +#endif + break; + case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL + { + channel_T *channel = varp->vval.v_channel; + char *status = channel_status(channel, -1); + + if (channel == NULL) + vim_snprintf((char *)buf, NUMBUFLEN, "channel %s", status); + else + vim_snprintf((char *)buf, NUMBUFLEN, + "channel %d %s", channel->ch_id, status); + return buf; + } +#endif + break; + case VAR_UNKNOWN: + emsg(_("E908: using an invalid value as a String")); + break; + } + return NULL; +} + +/* + * Turn a typeval into a string. Similar to tv_get_string_buf() but uses + * string() on Dict, List, etc. + */ + char_u * +tv_stringify(typval_T *varp, char_u *buf) +{ + if (varp->v_type == VAR_LIST + || varp->v_type == VAR_DICT + || varp->v_type == VAR_FUNC + || varp->v_type == VAR_PARTIAL + || varp->v_type == VAR_FLOAT) + { + typval_T tmp; + + f_string(varp, &tmp); + tv_get_string_buf(&tmp, buf); + clear_tv(varp); + *varp = tmp; + return tmp.vval.v_string; + } + return tv_get_string_buf(varp, buf); +} + +/* + * 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. + */ + dictitem_T * +find_var(char_u *name, hashtab_T **htp, int no_autoload) +{ + char_u *varname; + hashtab_T *ht; + dictitem_T *ret = NULL; + + ht = find_var_ht(name, &varname); + if (htp != NULL) + *htp = ht; + if (ht == NULL) + return NULL; + ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL); + if (ret != NULL) + return ret; + + /* Search in parent scope for lambda */ + return find_var_in_scoped_ht(name, no_autoload || htp != NULL); +} + +/* + * Find variable "varname" in hashtab "ht" with name "htname". + * Returns NULL if not found. + */ + 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_sctx.sc_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 get_funccal_local_var(); + case 'a': return get_funccal_args_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. + * Return NULL if the name is not valid. + * Set "varname" to the start of name without ':'. + */ + hashtab_T * +find_var_ht(char_u *name, char_u **varname) +{ + hashitem_T *hi; + hashtab_T *ht; + + if (name[0] == NUL) + return NULL; + 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; + + ht = get_funccal_local_ht(); + if (ht == NULL) + return &globvarht; /* global variable */ + return ht; /* local 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') /* a: function argument */ + return get_funccal_args_ht(); + if (*name == 'l') /* l: local function variable */ + return get_funccal_local_ht(); + if (*name == 's' /* script variable */ + && current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) + return &SCRIPT_VARS(current_sctx.sc_sid); + return NULL; +} + +/* + * Get the string value of a (global/local) variable. + * Note: see tv_get_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 tv_get_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; + + if (ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len)) == OK) + { + /* 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) = + (scriptvar_T *)alloc_clear(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. + */ + 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_ALLOC) + vim_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); + vim_free(di); +} + +/* + * List the value of one internal variable. + */ + static void +list_one_var(dictitem_T *v, char *prefix, int *first) +{ + char_u *tofree; + char_u *s; + char_u numbuf[NUMBUFLEN]; + + s = echo_string(&v->di_tv, &tofree, numbuf, get_copyID()); + list_one_var_a(prefix, v->di_key, v->di_tv.v_type, + s == NULL ? (char_u *)"" : s, first); + vim_free(tofree); +} + + static void +list_one_var_a( + char *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((char *)name); + msg_putchar(' '); + msg_advance(22); + if (type == VAR_NUMBER) + msg_putchar('#'); + else if (type == VAR_FUNC || type == VAR_PARTIAL) + 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 || type == VAR_PARTIAL) + msg_puts("()"); + 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. + */ + 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) + { + semsg(_(e_illvar), name); + return; + } + v = find_var_in_ht(ht, 0, varname, TRUE); + + /* Search in parent scope which is possible to reference from lambda */ + if (v == NULL) + v = find_var_in_scoped_ht(name, TRUE); + + if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) + && 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, FALSE) + || tv_check_lock(v->di_tv.v_lock, name, FALSE)) + return; + + /* + * Handle setting internal v: variables separately where needed to + * prevent changing the type. + */ + if (ht == &vimvarht) + { + if (v->di_tv.v_type == VAR_STRING) + { + VIM_CLEAR(v->di_tv.vval.v_string); + if (copy || tv->v_type != VAR_STRING) + { + char_u *val = tv_get_string(tv); + + // Careful: when assigning to v:errmsg and tv_get_string() + // causes an error message the variable will alrady be set. + if (v->di_tv.vval.v_string == NULL) + v->di_tv.vval.v_string = vim_strsave(val); + } + 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; + } + return; + } + else if (v->di_tv.v_type == VAR_NUMBER) + { + v->di_tv.vval.v_number = tv_get_number(tv); + if (STRCMP(varname, "searchforward") == 0) + set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); +#ifdef FEAT_SEARCH_EXTRA + else if (STRCMP(varname, "hlsearch") == 0) + { + no_hlsearch = !v->di_tv.vval.v_number; + redraw_all_later(SOME_VALID); + } +#endif + return; + } + else if (v->di_tv.v_type != tv->v_type) + { + semsg(_("E963: setting %s to value with wrong type"), name); + return; + } + } + + clear_tv(&v->di_tv); + } + else /* add a new variable */ + { + /* Can't add "v:" variable. */ + if (ht == &vimvarht) + { + semsg(_(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) + { + vim_free(v); + return; + } + v->di_flags = DI_FLAGS_ALLOC; + } + + 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. + */ + int +var_check_ro(int flags, char_u *name, int use_gettext) +{ + if (flags & DI_FLAGS_RO) + { + semsg(_(e_readonlyvar), use_gettext ? (char_u *)_(name) : name); + return TRUE; + } + if ((flags & DI_FLAGS_RO_SBX) && sandbox) + { + semsg(_(e_readonlysbx), use_gettext ? (char_u *)_(name) : name); + return TRUE; + } + return FALSE; +} + +/* + * Return TRUE if di_flags "flags" indicates variable "name" is fixed. + * Also give an error message. + */ + int +var_check_fixed(int flags, char_u *name, int use_gettext) +{ + if (flags & DI_FLAGS_FIX) + { + semsg(_("E795: Cannot delete variable %s"), + use_gettext ? (char_u *)_(name) : name); + return TRUE; + } + return FALSE; +} + +/* + * Check if a funcref is assigned to a valid variable name. + * Return TRUE and give an error if not. + */ + int +var_check_func_name( + char_u *name, /* points to start of variable name */ + int new_var) /* TRUE when creating the variable */ +{ + /* Allow for w: b: s: and t:. */ + if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':') + && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') + ? name[2] : name[0])) + { + semsg(_("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, FALSE)) + { + semsg(_("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. + */ + 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) + { + semsg(_(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" or _("name") when use_gettext is + * TRUE. + */ + int +tv_check_lock(int lock, char_u *name, int use_gettext) +{ + if (lock & VAR_LOCKED) + { + semsg(_("E741: Value is locked: %s"), + name == NULL ? (char_u *)_("Unknown") + : use_gettext ? (char_u *)_(name) + : name); + return TRUE; + } + if (lock & VAR_FIXED) + { + semsg(_("E742: Cannot change value of %s"), + name == NULL ? (char_u *)_("Unknown") + : use_gettext ? (char_u *)_(name) + : 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, blob 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: + case VAR_SPECIAL: + to->vval.v_number = from->vval.v_number; + break; + case VAR_FLOAT: +#ifdef FEAT_FLOAT + to->vval.v_float = from->vval.v_float; + break; +#endif + case VAR_JOB: +#ifdef FEAT_JOB_CHANNEL + to->vval.v_job = from->vval.v_job; + if (to->vval.v_job != NULL) + ++to->vval.v_job->jv_refcount; + break; +#endif + case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL + to->vval.v_channel = from->vval.v_channel; + if (to->vval.v_channel != NULL) + ++to->vval.v_channel->ch_refcount; + break; +#endif + 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_PARTIAL: + if (from->vval.v_partial == NULL) + to->vval.v_partial = NULL; + else + { + to->vval.v_partial = from->vval.v_partial; + ++to->vval.v_partial->pt_refcount; + } + break; + case VAR_BLOB: + if (from->vval.v_blob == NULL) + to->vval.v_blob = NULL; + else + { + to->vval.v_blob = from->vval.v_blob; + ++to->vval.v_blob->bv_refcount; + } + 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; + case VAR_UNKNOWN: + internal_error("copy_tv(UNKNOWN)"); + 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. + */ + 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: + case VAR_PARTIAL: + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: + 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_BLOB: + ret = blob_copy(from, to); + 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; + case VAR_UNKNOWN: + internal_error("item_copy(UNKNOWN)"); + ret = FAIL; + } + --recurse; + return ret; +} + +/* + * 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. + */ + void +get_user_input( + typval_T *argvars, + typval_T *rettv, + int inputdialog, + int secret) +{ + char_u *prompt = tv_get_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. When running tests + * with --not-a-term we assume feedkeys() will be used. */ + if (no_console_input() && !is_not_a_term()) + 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((char *)prompt, echo_attr); + msg_didout = FALSE; + msg_starthere(); + *p = c; + } + cmdline_row = msg_row; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + defstr = tv_get_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 = tv_get_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(secret ? 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(tv_get_string_buf( + &argvars[2], buf)); + + vim_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; +} + +/* + * ":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]; + int did_emsg_before = did_emsg; + int called_emsg_before = called_emsg; + + 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() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) + semsg(_(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(" ", echo_attr); + p = echo_string(&rettv, &tofree, numbuf, get_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); + } + } + vim_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) +{ + echo_attr = syn_name2attr(eap->arg); +} + +/* + * ":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 = did_emsg; + + ga_init2(&ga, 1, 80); + + if (eap->skip) + ++emsg_skip; + while (*arg != NUL && *arg != '|' && *arg != '\n') + { + p = arg; + ret = eval1_emsg(&arg, &rettv, !eap->skip); + if (ret == FAIL) + break; + + if (!eap->skip) + { + char_u buf[NUMBUFLEN]; + + if (eap->cmdidx == CMD_execute) + p = tv_get_string_buf(&rettv, buf); + else + p = tv_stringify(&rettv, buf); + len = (int)STRLEN(p); + if (ga_grow(&ga, len + 2) == FAIL) + { + clear_tv(&rettv); + ret = FAIL; + break; + } + 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 || eap->cmdidx == CMD_echoerr) + { + /* Mark the already 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(); + } + + 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(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); +} + +/* + * Find window specified by "vp" in tabpage "tp". + */ + win_T * +find_win_by_nr( + typval_T *vp, + tabpage_T *tp) /* NULL for current tab page */ +{ + win_T *wp; + int nr = (int)tv_get_number_chk(vp, NULL); + + if (nr < 0) + return NULL; + if (nr == 0) + return curwin; + + FOR_ALL_WINDOWS_IN_TAB(tp, wp) + { + if (nr >= LOWEST_WIN_ID) + { + if (wp->w_id == nr) + return wp; + } + else if (--nr <= 0) + break; + } + if (nr >= LOWEST_WIN_ID) + return NULL; + return wp; +} + +/* + * Find a window: When using a Window ID in any tab page, when using a number + * in the current tab page. + */ + win_T * +find_win_by_nr_or_id(typval_T *vp) +{ + int nr = (int)tv_get_number_chk(vp, NULL); + + if (nr >= LOWEST_WIN_ID) + return win_id2wp(vp); + return find_win_by_nr(vp, NULL); +} + +/* + * Find window specified by "wvp" in tabpage "tvp". + */ + win_T * +find_tabwin( + typval_T *wvp, /* VAR_UNKNOWN for current window */ + typval_T *tvp) /* VAR_UNKNOWN for current tab page */ +{ + win_T *wp = NULL; + tabpage_T *tp = NULL; + long n; + + if (wvp->v_type != VAR_UNKNOWN) + { + if (tvp->v_type != VAR_UNKNOWN) + { + n = (long)tv_get_number(tvp); + if (n >= 0) + tp = find_tabpage(n); + } + else + tp = curtab; + + if (tp != NULL) + wp = find_win_by_nr(wvp, tp); + } + else + wp = curwin; + + return wp; +} + +/* + * getwinvar() and gettabwinvar() + */ + void +getwinvar( + typval_T *argvars, + typval_T *rettv, + int off) /* 1 for gettabwinvar() */ +{ + win_T *win; + char_u *varname; + dictitem_T *v; + tabpage_T *tp = NULL; + int done = FALSE; + win_T *oldcurwin; + tabpage_T *oldtabpage; + int need_switch_win; + + if (off == 1) + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + else + tp = curtab; + win = find_win_by_nr(&argvars[off], tp); + varname = tv_get_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. Only do this when needed, + * autocommands get blocked. */ + need_switch_win = !(tp == curtab && win == curwin); + if (!need_switch_win + || switch_win(&oldcurwin, &oldtabpage, win, tp, TRUE) == OK) + { + if (*varname == '&') + { + if (varname[1] == NUL) + { + /* get all window-local options in a dict */ + dict_T *opts = get_winbuf_options(FALSE); + + if (opts != NULL) + { + rettv_dict_set(rettv, opts); + done = TRUE; + } + } + else if (get_option_tv(&varname, rettv, 1) == OK) + /* window-local-option */ + 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; + } + } + } + + if (need_switch_win) + /* 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; +} + +/* + * "setwinvar()" and "settabwinvar()" functions + */ + void +setwinvar(typval_T *argvars, typval_T *rettv UNUSED, int off) +{ + win_T *win; + win_T *save_curwin; + tabpage_T *save_curtab; + int need_switch_win; + 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)tv_get_number_chk(&argvars[0], NULL)); + else + tp = curtab; + win = find_win_by_nr(&argvars[off], tp); + varname = tv_get_string_chk(&argvars[off + 1]); + varp = &argvars[off + 2]; + + if (win != NULL && varname != NULL && varp != NULL) + { + need_switch_win = !(tp == curtab && win == curwin); + if (!need_switch_win + || switch_win(&save_curwin, &save_curtab, win, tp, TRUE) == OK) + { + if (*varname == '&') + { + long numval; + char_u *strval; + int error = FALSE; + + ++varname; + numval = (long)tv_get_number_chk(varp, &error); + strval = tv_get_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); + vim_free(winvarname); + } + } + } + if (need_switch_win) + restore_win(save_curwin, save_curtab, TRUE); + } +} + +/* + * 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; +} + +/* + * Return the autoload script name for a function or variable name. + * Returns NULL when out of memory. + */ + 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; +} + +/* + * If "name" has a package name try autoloading the script for it. + * Return TRUE if a package was loaded. + */ + 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) == OK) + { + ((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, 0) == OK) + ret = TRUE; + } + + vim_free(tofree); + return ret; +} + +#if defined(FEAT_VIMINFO) || defined(FEAT_SESSION) +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) +{ + 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; +} +#endif + +#if defined(FEAT_VIMINFO) || defined(PROTO) +/* + * 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; + funccal_entry_T funccal_entry; + + if (!writing && (find_viminfo_parameter('!') != NULL)) + { + tab = vim_strchr(virp->vir_line + 1, '\t'); + if (tab != NULL) + { + *tab++ = '\0'; /* isolate the variable name */ + switch (*tab) + { + case 'S': type = VAR_STRING; break; +#ifdef FEAT_FLOAT + case 'F': type = VAR_FLOAT; break; +#endif + case 'D': type = VAR_DICT; break; + case 'L': type = VAR_LIST; break; + case 'B': type = VAR_BLOB; break; + case 'X': type = VAR_SPECIAL; break; + } + + tab = vim_strchr(tab, '\t'); + if (tab != NULL) + { + tv.v_type = type; + if (type == VAR_STRING || type == VAR_DICT + || type == VAR_LIST || type == VAR_BLOB) + tv.vval.v_string = viminfo_readstring(virp, + (int)(tab - virp->vir_line + 1), TRUE); +#ifdef FEAT_FLOAT + else if (type == VAR_FLOAT) + (void)string2float(tab + 1, &tv.vval.v_float); +#endif + 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 + { + vim_free(tv.vval.v_string); + tv = *etv; + vim_free(etv); + } + } + else if (type == VAR_BLOB) + { + blob_T *blob = string2blob(tv.vval.v_string); + + if (blob == NULL) + // Failed to parse back the blob, use it as a string. + tv.v_type = VAR_STRING; + else + { + vim_free(tv.vval.v_string); + tv.v_type = VAR_BLOB; + tv.vval.v_blob = blob; + } + } + + /* when in a function use global variables */ + save_funccal(&funccal_entry); + set_var(virp->vir_line + 1, &tv, FALSE); + restore_funccal(); + + if (tv.v_type == VAR_STRING) + vim_free(tv.vval.v_string); + else if (tv.v_type == VAR_DICT || tv.v_type == VAR_LIST || + tv.v_type == VAR_BLOB) + 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; + case VAR_BLOB: s = "BLO"; break; + case VAR_SPECIAL: s = "XPL"; break; + + case VAR_UNKNOWN: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_JOB: + case VAR_CHANNEL: + continue; + } + fprintf(fp, "!%s\t%s\t", this_var->di_key, s); + if (this_var->di_tv.v_type == VAR_SPECIAL) + { + sprintf((char *)numbuf, "%ld", + (long)this_var->di_tv.vval.v_number); + p = numbuf; + tofree = NULL; + } + else + p = echo_string(&this_var->di_tv, &tofree, numbuf, 0); + if (p != NULL) + viminfo_writestring(fp, p); + vim_free(tofree); + } + } + } +} +#endif + +#if defined(FEAT_SESSION) || defined(PROTO) + 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(tv_get_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) + { + vim_free(p); + return FAIL; + } + vim_free(p); + } +#ifdef FEAT_FLOAT + 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; + } +#endif + } + } + return OK; +} +#endif + +/* + * Display script name where an item was last set. + * Should only be invoked when 'verbose' is non-zero. + */ + void +last_set_msg(sctx_T script_ctx) +{ + char_u *p; + + if (script_ctx.sc_sid != 0) + { + p = home_replace_save(NULL, get_scriptname(script_ctx.sc_sid)); + if (p != NULL) + { + verbose_enter(); + msg_puts(_("\n\tLast set from ")); + msg_puts((char *)p); + if (script_ctx.sc_lnum > 0) + { + msg_puts(_(" line ")); + msg_outnum((long)script_ctx.sc_lnum); + } + verbose_leave(); + vim_free(p); + } + } +} + +/* reset v:option_new, v:option_old and v:option_type */ + void +reset_v_option_vars(void) +{ + set_vim_var_string(VV_OPTION_NEW, NULL, -1); + set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_TYPE, NULL, -1); +} + +/* + * Prepare "gap" for an assert error and add the sourcing position. + */ + void +prepare_assert_error(garray_T *gap) +{ + char buf[NUMBUFLEN]; + + ga_init2(gap, 1, 100); + if (sourcing_name != NULL) + { + ga_concat(gap, sourcing_name); + if (sourcing_lnum > 0) + ga_concat(gap, (char_u *)" "); + } + if (sourcing_lnum > 0) + { + sprintf(buf, "line %ld", (long)sourcing_lnum); + ga_concat(gap, (char_u *)buf); + } + if (sourcing_name != NULL || sourcing_lnum > 0) + ga_concat(gap, (char_u *)": "); +} + +/* + * Add an assert error to v:errors. + */ + void +assert_error(garray_T *gap) +{ + struct vimvar *vp = &vimvars[VV_ERRORS]; + + if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) + /* Make sure v:errors is a list. */ + set_vim_var_list(VV_ERRORS, list_alloc()); + list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, gap->ga_len); +} + + int +assert_equal_common(typval_T *argvars, assert_type_T atype) +{ + garray_T ga; + + if (tv_equal(&argvars[0], &argvars[1], FALSE, FALSE) + != (atype == ASSERT_EQUAL)) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], + atype); + assert_error(&ga); + ga_clear(&ga); + return 1; + } + return 0; +} + + int +assert_equalfile(typval_T *argvars) +{ + char_u buf1[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + char_u *fname1 = tv_get_string_buf_chk(&argvars[0], buf1); + char_u *fname2 = tv_get_string_buf_chk(&argvars[1], buf2); + garray_T ga; + FILE *fd1; + FILE *fd2; + + if (fname1 == NULL || fname2 == NULL) + return 0; + + IObuff[0] = NUL; + fd1 = mch_fopen((char *)fname1, READBIN); + if (fd1 == NULL) + { + vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1); + } + else + { + fd2 = mch_fopen((char *)fname2, READBIN); + if (fd2 == NULL) + { + fclose(fd1); + vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2); + } + else + { + int c1, c2; + long count = 0; + + for (;;) + { + c1 = fgetc(fd1); + c2 = fgetc(fd2); + if (c1 == EOF) + { + if (c2 != EOF) + STRCPY(IObuff, "first file is shorter"); + break; + } + else if (c2 == EOF) + { + STRCPY(IObuff, "second file is shorter"); + break; + } + else if (c1 != c2) + { + vim_snprintf((char *)IObuff, IOSIZE, + "difference at byte %ld", count); + break; + } + ++count; + } + fclose(fd1); + fclose(fd2); + } + } + if (IObuff[0] != NUL) + { + prepare_assert_error(&ga); + ga_concat(&ga, IObuff); + assert_error(&ga); + ga_clear(&ga); + return 1; + } + return 0; +} + + int +assert_match_common(typval_T *argvars, assert_type_T atype) +{ + garray_T ga; + char_u buf1[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + char_u *pat = tv_get_string_buf_chk(&argvars[0], buf1); + char_u *text = tv_get_string_buf_chk(&argvars[1], buf2); + + if (pat == NULL || text == NULL) + emsg(_(e_invarg)); + else if (pattern_match(pat, text, FALSE) != (atype == ASSERT_MATCH)) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], + atype); + assert_error(&ga); + ga_clear(&ga); + return 1; + } + return 0; +} + + int +assert_inrange(typval_T *argvars) +{ + garray_T ga; + int error = FALSE; + varnumber_T lower = tv_get_number_chk(&argvars[0], &error); + varnumber_T upper = tv_get_number_chk(&argvars[1], &error); + varnumber_T actual = tv_get_number_chk(&argvars[2], &error); + char_u *tofree; + char msg[200]; + char_u numbuf[NUMBUFLEN]; + + if (error) + return 0; + if (actual < lower || actual > upper) + { + prepare_assert_error(&ga); + if (argvars[3].v_type != VAR_UNKNOWN) + { + ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0)); + vim_free(tofree); + } + else + { + vim_snprintf(msg, 200, "Expected range %ld - %ld, but got %ld", + (long)lower, (long)upper, (long)actual); + ga_concat(&ga, (char_u *)msg); + } + assert_error(&ga); + ga_clear(&ga); + return 1; + } + return 0; +} + +/* + * Common for assert_true() and assert_false(). + * Return non-zero for failure. + */ + int +assert_bool(typval_T *argvars, int isTrue) +{ + int error = FALSE; + garray_T ga; + + if (argvars[0].v_type == VAR_SPECIAL + && argvars[0].vval.v_number == (isTrue ? VVAL_TRUE : VVAL_FALSE)) + return 0; + if (argvars[0].v_type != VAR_NUMBER + || (tv_get_number_chk(&argvars[0], &error) == 0) == isTrue + || error) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[1], + (char_u *)(isTrue ? "True" : "False"), + NULL, &argvars[0], ASSERT_OTHER); + assert_error(&ga); + ga_clear(&ga); + return 1; + } + return 0; +} + + int +assert_report(typval_T *argvars) +{ + garray_T ga; + + prepare_assert_error(&ga); + ga_concat(&ga, tv_get_string(&argvars[0])); + assert_error(&ga); + ga_clear(&ga); + return 1; +} + + int +assert_exception(typval_T *argvars) +{ + garray_T ga; + char_u *error = tv_get_string_chk(&argvars[0]); + + if (vimvars[VV_EXCEPTION].vv_str == NULL) + { + prepare_assert_error(&ga); + ga_concat(&ga, (char_u *)"v:exception is not set"); + assert_error(&ga); + ga_clear(&ga); + return 1; + } + else if (error != NULL + && strstr((char *)vimvars[VV_EXCEPTION].vv_str, (char *)error) == NULL) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], + &vimvars[VV_EXCEPTION].vv_tv, ASSERT_OTHER); + assert_error(&ga); + ga_clear(&ga); + return 1; + } + return 0; +} + + int +assert_beeps(typval_T *argvars) +{ + char_u *cmd = tv_get_string_chk(&argvars[0]); + garray_T ga; + int ret = 0; + + called_vim_beep = FALSE; + suppress_errthrow = TRUE; + emsg_silent = FALSE; + do_cmdline_cmd(cmd); + if (!called_vim_beep) + { + prepare_assert_error(&ga); + ga_concat(&ga, (char_u *)"command did not beep: "); + ga_concat(&ga, cmd); + assert_error(&ga); + ga_clear(&ga); + ret = 1; + } + + suppress_errthrow = FALSE; + emsg_on_display = FALSE; + return ret; +} + + int +assert_fails(typval_T *argvars) +{ + char_u *cmd = tv_get_string_chk(&argvars[0]); + garray_T ga; + int ret = 0; + char_u numbuf[NUMBUFLEN]; + char_u *tofree; + + called_emsg = FALSE; + suppress_errthrow = TRUE; + emsg_silent = TRUE; + do_cmdline_cmd(cmd); + if (!called_emsg) + { + prepare_assert_error(&ga); + ga_concat(&ga, (char_u *)"command did not fail: "); + if (argvars[1].v_type != VAR_UNKNOWN + && argvars[2].v_type != VAR_UNKNOWN) + { + ga_concat(&ga, echo_string(&argvars[2], &tofree, numbuf, 0)); + vim_free(tofree); + } + else + ga_concat(&ga, cmd); + assert_error(&ga); + ga_clear(&ga); + ret = 1; + } + else if (argvars[1].v_type != VAR_UNKNOWN) + { + char_u buf[NUMBUFLEN]; + char *error = (char *)tv_get_string_buf_chk(&argvars[1], buf); + + if (error == NULL + || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[2], NULL, &argvars[1], + &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER); + assert_error(&ga); + ga_clear(&ga); + ret = 1; + } + } + + called_emsg = FALSE; + suppress_errthrow = FALSE; + emsg_silent = FALSE; + emsg_on_display = FALSE; + set_vim_var_string(VV_ERRMSG, NULL, 0); + return ret; +} + +/* + * Append "p[clen]" to "gap", escaping unprintable characters. + * Changes NL to \n, CR to \r, etc. + */ + static void +ga_concat_esc(garray_T *gap, char_u *p, int clen) +{ + char_u buf[NUMBUFLEN]; + + if (clen > 1) + { + mch_memmove(buf, p, clen); + buf[clen] = NUL; + ga_concat(gap, buf); + } + else switch (*p) + { + case BS: ga_concat(gap, (char_u *)"\\b"); break; + case ESC: ga_concat(gap, (char_u *)"\\e"); break; + case FF: ga_concat(gap, (char_u *)"\\f"); break; + case NL: ga_concat(gap, (char_u *)"\\n"); break; + case TAB: ga_concat(gap, (char_u *)"\\t"); break; + case CAR: ga_concat(gap, (char_u *)"\\r"); break; + case '\\': ga_concat(gap, (char_u *)"\\\\"); break; + default: + if (*p < ' ') + { + vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p); + ga_concat(gap, buf); + } + else + ga_append(gap, *p); + break; + } +} + +/* + * Append "str" to "gap", escaping unprintable characters. + * Changes NL to \n, CR to \r, etc. + */ + static void +ga_concat_shorten_esc(garray_T *gap, char_u *str) +{ + char_u *p; + char_u *s; + int c; + int clen; + char_u buf[NUMBUFLEN]; + int same_len; + + if (str == NULL) + { + ga_concat(gap, (char_u *)"NULL"); + return; + } + + for (p = str; *p != NUL; ++p) + { + same_len = 1; + s = p; + c = mb_ptr2char_adv(&s); + clen = s - p; + while (*s != NUL && c == mb_ptr2char(s)) + { + ++same_len; + s += clen; + } + if (same_len > 20) + { + ga_concat(gap, (char_u *)"\\["); + ga_concat_esc(gap, p, clen); + ga_concat(gap, (char_u *)" occurs "); + vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len); + ga_concat(gap, buf); + ga_concat(gap, (char_u *)" times]"); + p = s - 1; + } + else + ga_concat_esc(gap, p, clen); + } +} + +/* + * Fill "gap" with information about an assert error. + */ + void +fill_assert_error( + garray_T *gap, + typval_T *opt_msg_tv, + char_u *exp_str, + typval_T *exp_tv, + typval_T *got_tv, + assert_type_T atype) +{ + char_u numbuf[NUMBUFLEN]; + char_u *tofree; + + if (opt_msg_tv->v_type != VAR_UNKNOWN) + { + ga_concat(gap, echo_string(opt_msg_tv, &tofree, numbuf, 0)); + vim_free(tofree); + ga_concat(gap, (char_u *)": "); + } + + if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) + ga_concat(gap, (char_u *)"Pattern "); + else if (atype == ASSERT_NOTEQUAL) + ga_concat(gap, (char_u *)"Expected not equal to "); + else + ga_concat(gap, (char_u *)"Expected "); + if (exp_str == NULL) + { + ga_concat_shorten_esc(gap, tv2string(exp_tv, &tofree, numbuf, 0)); + vim_free(tofree); + } + else + ga_concat_shorten_esc(gap, exp_str); + if (atype != ASSERT_NOTEQUAL) + { + if (atype == ASSERT_MATCH) + ga_concat(gap, (char_u *)" does not match "); + else if (atype == ASSERT_NOTMATCH) + ga_concat(gap, (char_u *)" does match "); + else + ga_concat(gap, (char_u *)" but got "); + ga_concat_shorten_esc(gap, tv2string(got_tv, &tofree, numbuf, 0)); + vim_free(tofree); + } +} + +/* + * Compare "typ1" and "typ2". Put the result in "typ1". + */ + int +typval_compare( + typval_T *typ1, /* first operand */ + typval_T *typ2, /* second operand */ + exptype_T type, /* operator */ + int type_is, /* TRUE for "is" and "isnot" */ + int ic) /* ignore case */ +{ + int i; + varnumber_T n1, n2; + char_u *s1, *s2; + char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; + + if (type_is && typ1->v_type != typ2->v_type) + { + /* For "is" a different type always means FALSE, for "notis" + * it means TRUE. */ + n1 = (type == TYPE_NEQUAL); + } + else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB) + { + if (type_is) + { + n1 = (typ1->v_type == typ2->v_type + && typ1->vval.v_blob == typ2->vval.v_blob); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + else if (typ1->v_type != typ2->v_type + || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) + { + if (typ1->v_type != typ2->v_type) + emsg(_("E977: Can only compare Blob with Blob")); + else + emsg(_(e_invalblob)); + clear_tv(typ1); + return FAIL; + } + else + { + // Compare two Blobs for being equal or unequal. + n1 = blob_equal(typ1->vval.v_blob, typ2->vval.v_blob); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + } + else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) + { + if (type_is) + { + n1 = (typ1->v_type == typ2->v_type + && typ1->vval.v_list == typ2->vval.v_list); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + else if (typ1->v_type != typ2->v_type + || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) + { + if (typ1->v_type != typ2->v_type) + emsg(_("E691: Can only compare List with List")); + else + emsg(_("E692: Invalid operation for List")); + clear_tv(typ1); + return FAIL; + } + else + { + /* Compare two Lists for being equal or unequal. */ + n1 = list_equal(typ1->vval.v_list, typ2->vval.v_list, + ic, FALSE); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + } + + else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT) + { + if (type_is) + { + n1 = (typ1->v_type == typ2->v_type + && typ1->vval.v_dict == typ2->vval.v_dict); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + else if (typ1->v_type != typ2->v_type + || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) + { + if (typ1->v_type != typ2->v_type) + emsg(_("E735: Can only compare Dictionary with Dictionary")); + else + emsg(_("E736: Invalid operation for Dictionary")); + clear_tv(typ1); + return FAIL; + } + else + { + /* Compare two Dictionaries for being equal or unequal. */ + n1 = dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, + ic, FALSE); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + } + + else if (typ1->v_type == VAR_FUNC || typ2->v_type == VAR_FUNC + || typ1->v_type == VAR_PARTIAL || typ2->v_type == VAR_PARTIAL) + { + if (type != TYPE_EQUAL && type != TYPE_NEQUAL) + { + emsg(_("E694: Invalid operation for Funcrefs")); + clear_tv(typ1); + return FAIL; + } + if ((typ1->v_type == VAR_PARTIAL + && typ1->vval.v_partial == NULL) + || (typ2->v_type == VAR_PARTIAL + && typ2->vval.v_partial == NULL)) + /* when a partial is NULL assume not equal */ + n1 = FALSE; + else if (type_is) + { + if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) + /* strings are considered the same if their value is + * the same */ + n1 = tv_equal(typ1, typ2, ic, FALSE); + else if (typ1->v_type == VAR_PARTIAL + && typ2->v_type == VAR_PARTIAL) + n1 = (typ1->vval.v_partial == typ2->vval.v_partial); + else + n1 = FALSE; + } + else + n1 = tv_equal(typ1, typ2, ic, FALSE); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + +#ifdef FEAT_FLOAT + /* + * If one of the two variables is a float, compare as a float. + * When using "=~" or "!~", always compare as string. + */ + else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT) + && type != TYPE_MATCH && type != TYPE_NOMATCH) + { + float_T f1, f2; + + if (typ1->v_type == VAR_FLOAT) + f1 = typ1->vval.v_float; + else + f1 = tv_get_number(typ1); + if (typ2->v_type == VAR_FLOAT) + f2 = typ2->vval.v_float; + else + f2 = tv_get_number(typ2); + 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 */ + } + } +#endif + + /* + * If one of the two variables is a number, compare as a number. + * When using "=~" or "!~", always compare as string. + */ + else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER) + && type != TYPE_MATCH && type != TYPE_NOMATCH) + { + n1 = tv_get_number(typ1); + n2 = tv_get_number(typ2); + 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 = tv_get_string_buf(typ1, buf1); + s2 = tv_get_string_buf(typ2, 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: + n1 = pattern_match(s2, s1, ic); + if (type == TYPE_NOMATCH) + n1 = !n1; + break; + + case TYPE_UNKNOWN: break; /* avoid gcc warning */ + } + } + clear_tv(typ1); + typ1->v_type = VAR_NUMBER; + typ1->vval.v_number = n1; + + return OK; +} + + char_u * +typval_tostring(typval_T *arg) +{ + char_u *tofree; + char_u numbuf[NUMBUFLEN]; + char_u *ret = NULL; + + if (arg == NULL) + return vim_strsave((char_u *)"(does not exist)"); + ret = tv2string(arg, &tofree, numbuf, 0); + /* Make a copy if we have a value but it's not in allocated memory. */ + if (ret != NULL && tofree == NULL) + ret = vim_strsave(ret); + return ret; +} + + int +var_exists(char_u *var) +{ + char_u *name; + char_u *tofree; + typval_T tv; + int len = 0; + int n = FALSE; + + /* get_name_len() takes care of expanding curly braces */ + name = var; + len = get_name_len(&var, &tofree, TRUE, FALSE); + if (len > 0) + { + if (tofree != NULL) + name = tofree; + n = (get_var_tv(name, len, &tv, NULL, FALSE, TRUE) == OK); + if (n) + { + /* handle d.key, l[idx], f(expr) */ + n = (handle_subscript(&var, &tv, TRUE, FALSE) == OK); + if (n) + clear_tv(&tv); + } + } + if (*var != NUL) + n = FALSE; + + vim_free(tofree); + return n; +} + +#endif /* FEAT_EVAL */ + + +#if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) || defined(PROTO) + +#ifdef WIN3264 +/* + * Functions for ":8" filename modifier: get 8.3 version of a filename. + */ + +/* + * Get the short path (8.3) for the filename in "fnamep". + * Only works for a valid file name. + * When the path gets longer "fnamep" is changed and the allocated buffer + * is put in "bufp". + * *fnamelen is the length of "fnamep" and set to 0 for a nonexistent path. + * Returns OK on success, FAIL on failure. + */ + static int +get_short_pathname(char_u **fnamep, char_u **bufp, int *fnamelen) +{ + int l, len; + char_u *newbuf; + + len = *fnamelen; + l = GetShortPathName((LPSTR)*fnamep, (LPSTR)*fnamep, len); + if (l > len - 1) + { + /* If that doesn't work (not enough space), then save the string + * and try again with a new buffer big enough. */ + newbuf = vim_strnsave(*fnamep, l); + if (newbuf == NULL) + return FAIL; + + vim_free(*bufp); + *fnamep = *bufp = newbuf; + + /* Really should always succeed, as the buffer is big enough. */ + l = GetShortPathName((LPSTR)*fnamep, (LPSTR)*fnamep, l+1); + } + + *fnamelen = l; + return OK; +} + +/* + * Get the short path (8.3) for the filename in "fname". The converted + * path is returned in "bufp". + * + * Some of the directories specified in "fname" may not exist. This function + * will shorten the existing directories at the beginning of the path and then + * append the remaining non-existing path. + * + * fname - Pointer to the filename to shorten. On return, contains the + * pointer to the shortened pathname + * bufp - Pointer to an allocated buffer for the filename. + * fnamelen - Length of the filename pointed to by fname + * + * Returns OK on success (or nothing done) and FAIL on failure (out of memory). + */ + static int +shortpath_for_invalid_fname( + char_u **fname, + char_u **bufp, + int *fnamelen) +{ + char_u *short_fname, *save_fname, *pbuf_unused; + char_u *endp, *save_endp; + char_u ch; + int old_len, len; + int new_len, sfx_len; + int retval = OK; + + /* Make a copy */ + old_len = *fnamelen; + save_fname = vim_strnsave(*fname, old_len); + pbuf_unused = NULL; + short_fname = NULL; + + endp = save_fname + old_len - 1; /* Find the end of the copy */ + save_endp = endp; + + /* + * Try shortening the supplied path till it succeeds by removing one + * directory at a time from the tail of the path. + */ + len = 0; + for (;;) + { + /* go back one path-separator */ + while (endp > save_fname && !after_pathsep(save_fname, endp + 1)) + --endp; + if (endp <= save_fname) + break; /* processed the complete path */ + + /* + * Replace the path separator with a NUL and try to shorten the + * resulting path. + */ + ch = *endp; + *endp = 0; + short_fname = save_fname; + len = (int)STRLEN(short_fname) + 1; + if (get_short_pathname(&short_fname, &pbuf_unused, &len) == FAIL) + { + retval = FAIL; + goto theend; + } + *endp = ch; /* preserve the string */ + + if (len > 0) + break; /* successfully shortened the path */ + + /* failed to shorten the path. Skip the path separator */ + --endp; + } + + if (len > 0) + { + /* + * Succeeded in shortening the path. Now concatenate the shortened + * path with the remaining path at the tail. + */ + + /* Compute the length of the new path. */ + sfx_len = (int)(save_endp - endp) + 1; + new_len = len + sfx_len; + + *fnamelen = new_len; + vim_free(*bufp); + if (new_len > old_len) + { + /* There is not enough space in the currently allocated string, + * copy it to a buffer big enough. */ + *fname = *bufp = vim_strnsave(short_fname, new_len); + if (*fname == NULL) + { + retval = FAIL; + goto theend; + } + } + else + { + /* Transfer short_fname to the main buffer (it's big enough), + * unless get_short_pathname() did its work in-place. */ + *fname = *bufp = save_fname; + if (short_fname != save_fname) + vim_strncpy(save_fname, short_fname, len); + save_fname = NULL; + } + + /* concat the not-shortened part of the path */ + vim_strncpy(*fname + len, endp, sfx_len); + (*fname)[new_len] = NUL; + } + +theend: + vim_free(pbuf_unused); + vim_free(save_fname); + + return retval; +} + +/* + * Get a pathname for a partial path. + * Returns OK for success, FAIL for failure. + */ + static int +shortpath_for_partial( + char_u **fnamep, + char_u **bufp, + int *fnamelen) +{ + int sepcount, len, tflen; + char_u *p; + char_u *pbuf, *tfname; + int hasTilde; + + /* Count up the path separators from the RHS.. so we know which part + * of the path to return. */ + sepcount = 0; + for (p = *fnamep; p < *fnamep + *fnamelen; MB_PTR_ADV(p)) + if (vim_ispathsep(*p)) + ++sepcount; + + /* Need full path first (use expand_env() to remove a "~/") */ + hasTilde = (**fnamep == '~'); + if (hasTilde) + pbuf = tfname = expand_env_save(*fnamep); + else + pbuf = tfname = FullName_save(*fnamep, FALSE); + + len = tflen = (int)STRLEN(tfname); + + if (get_short_pathname(&tfname, &pbuf, &len) == FAIL) + return FAIL; + + if (len == 0) + { + /* Don't have a valid filename, so shorten the rest of the + * path if we can. This CAN give us invalid 8.3 filenames, but + * there's not a lot of point in guessing what it might be. + */ + len = tflen; + if (shortpath_for_invalid_fname(&tfname, &pbuf, &len) == FAIL) + return FAIL; + } + + /* Count the paths backward to find the beginning of the desired string. */ + for (p = tfname + len - 1; p >= tfname; --p) + { + if (has_mbyte) + p -= mb_head_off(tfname, p); + if (vim_ispathsep(*p)) + { + if (sepcount == 0 || (hasTilde && sepcount == 1)) + break; + else + sepcount --; + } + } + if (hasTilde) + { + --p; + if (p >= tfname) + *p = '~'; + else + return FAIL; + } + else + ++p; + + /* Copy in the string - p indexes into tfname - allocated at pbuf */ + vim_free(*bufp); + *fnamelen = (int)STRLEN(p); + *bufp = pbuf; + *fnamep = p; + + return OK; +} +#endif /* WIN3264 */ + +/* + * 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 tilde_file, // "~" is a file name, not $HOME + 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; +#ifdef WIN3264 + char_u *fname_start = *fnamep; + int has_shortname = 0; +#endif + +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 and VMS */ + if ((*fnamep)[0] == '~' +#if !defined(UNIX) && !(defined(VMS) && defined(USER_HOME)) + && ((*fnamep)[1] == '/' +# ifdef BACKSLASH_IN_FILENAME + || (*fnamep)[1] == '\\' +# endif + || (*fnamep)[1] == NUL) +#endif + && !(tilde_file && (*fnamep)[1] == NUL) + ) + { + *fnamep = expand_env_save(*fnamep); + vim_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); + vim_free(*bufp); /* free any allocated file name */ + *bufp = *fnamep; + if (*fnamep == NULL) + return -1; + } + +#ifdef WIN3264 +# if _WIN32_WINNT >= 0x0500 + if (vim_strchr(*fnamep, '~') != NULL) + { + /* Expand 8.3 filename to full path. Needed to make sure the same + * file does not have two different names. + * Note: problem does not occur if _WIN32_WINNT < 0x0500. */ + p = alloc(_MAX_PATH + 1); + if (p != NULL) + { + if (GetLongPathName((LPSTR)*fnamep, (LPSTR)p, _MAX_PATH)) + { + vim_free(*bufp); + *bufp = *fnamep = p; + } + else + vim_free(p); + } + } +# endif +#endif + /* Append a path separator to a directory. */ + if (mch_isdir(*fnamep)) + { + /* Make room for one or two extra characters. */ + *fnamep = vim_strnsave(*fnamep, (int)STRLEN(*fnamep) + 2); + vim_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') + { +#ifdef WIN3264 + has_shortname = 1; /* Postpone this. */ +#endif + 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 == '.') + { + mch_dirname(dirname, MAXPATHL); + s = shorten_fname(p, dirname); + if (s != NULL) + { + *fnamep = s; + if (pbuf != NULL) + { + vim_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; + vim_free(*bufp); + *bufp = s; + } + } + } + vim_free(pbuf); + } + } + + tail = gettail(*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); +#ifdef VMS + if (*fnamelen > 0) + *fnamelen += 1; /* the path separator is part of the path */ +#endif + if (*fnamelen == 0) + { + /* Result is empty. Turn it into "." to make ":cd %:h" work. */ + p = vim_strsave((char_u *)"."); + if (p == NULL) + return -1; + vim_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; +#ifdef WIN3264 + has_shortname = 1; +#endif + } + +#ifdef WIN3264 + /* + * Handle ":8" after we have done 'heads' and before we do 'tails'. + */ + if (has_shortname) + { + /* Copy the string if it is shortened by :h and when it wasn't copied + * yet, because we are going to change it in place. Avoids changing + * the buffer name for "%:8". */ + if (*fnamelen < (int)STRLEN(*fnamep) || *fnamep == fname_start) + { + p = vim_strnsave(*fnamep, *fnamelen); + if (p == NULL) + return -1; + vim_free(*bufp); + *bufp = *fnamep = p; + } + + /* Split into two implementations - makes it easier. First is where + * there isn't a full name already, second is where there is. */ + if (!has_fullname && !vim_isAbsName(*fnamep)) + { + if (shortpath_for_partial(fnamep, bufp, fnamelen) == FAIL) + return -1; + } + else + { + int l = *fnamelen; + + /* Simple case, already have the full-name. + * Nearly always shorter, so try first time. */ + if (get_short_pathname(fnamep, bufp, &l) == FAIL) + return -1; + + if (l == 0) + { + /* Couldn't find the filename, search the paths. */ + l = *fnamelen; + if (shortpath_for_invalid_fname(fnamep, bufp, &l) == FAIL) + return -1; + } + *fnamelen = l; + } + } +#endif /* WIN3264 */ + + /* ":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; +#ifdef VMS + /* cut version from the extension */ + s = *fnamep + *fnamelen - 1; + for ( ; s > *fnamep; --s) + if (s[0] == ';') + break; + if (s > *fnamep) + *fnamelen = s - *fnamep; +#endif + } + 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, NULL, flags); + if (s != NULL) + { + *fnamep = s; + *fnamelen = (int)STRLEN(s); + vim_free(*bufp); + *bufp = s; + didit = TRUE; + } + } + vim_free(sub); + vim_free(str); + } + vim_free(pat); + } + } + /* after using ":s", repeat all the modifiers */ + if (didit) + goto repeat; + } + } + + if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') + { + /* vim_strsave_shellescape() needs a NUL terminated string. */ + c = (*fnamep)[*fnamelen]; + if (c != NUL) + (*fnamep)[*fnamelen] = NUL; + p = vim_strsave_shellescape(*fnamep, FALSE, FALSE); + if (c != NUL) + (*fnamep)[*fnamelen] = c; + if (p == NULL) + return -1; + vim_free(*bufp); + *bufp = *fnamep = p; + *fnamelen = (int)STRLEN(p); + *usedlen += 2; + } + + return valid; +} + +/* + * Perform a substitution on "str" with pattern "pat" and substitute "sub". + * When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL. + * "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, + typval_T *expr, + char_u *flags) +{ + int sublen; + regmatch_T regmatch; + int i; + int do_all; + char_u *tail; + char_u *end; + 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_init2(&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; + end = str + STRLEN(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 */ + i = MB_PTR2LEN(tail); + mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, + (size_t)i); + ga.ga_len += i; + tail += i; + 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, expr, tail, FALSE, TRUE, FALSE); + if (ga_grow(&ga, (int)((end - tail) + sublen - + (regmatch.endp[0] - regmatch.startp[0]))) == FAIL) + { + ga_clear(&ga); + break; + } + + /* copy the text up to where the match is */ + i = (int)(regmatch.startp[0] - tail); + mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); + /* add the substituted text */ + (void)vim_regsub(®match, sub, expr, (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 or {expr} changed the value. */ + free_string_option(save_cpo); + + return ret; +} + + static int +filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) +{ + typval_T rettv; + typval_T argv[3]; + int retval = FAIL; + + copy_tv(tv, &vimvars[VV_VAL].vv_tv); + argv[0] = vimvars[VV_KEY].vv_tv; + argv[1] = vimvars[VV_VAL].vv_tv; + if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL) + 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 = (tv_get_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 tv_get_number_chk(). */ + if (error) + goto theend; + } + retval = OK; +theend: + clear_tv(&vimvars[VV_VAL].vv_tv); + return retval; +} + + +/* + * Implementation of map() and filter(). + */ + void +filter_map(typval_T *argvars, typval_T *rettv, int map) +{ + typval_T *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; + blob_T *b = NULL; + int rem; + int todo; + char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); + char_u *arg_errmsg = (char_u *)(map ? N_("map() argument") + : N_("filter() argument")); + int save_did_emsg; + int idx = 0; + + if (argvars[0].v_type == VAR_BLOB) + { + if ((b = argvars[0].vval.v_blob) == NULL) + return; + } + else if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) == NULL + || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TRUE))) + return; + } + else if (argvars[0].v_type == VAR_DICT) + { + if ((d = argvars[0].vval.v_dict) == NULL + || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TRUE))) + return; + } + else + { + semsg(_(e_listdictarg), ermsg); + return; + } + + expr = &argvars[1]; + /* 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->v_type != VAR_UNKNOWN) + { + prepare_vimvar(VV_VAL, &save_val); + + /* 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)) + { + int r; + + --todo; + di = HI2DI(hi); + if (map && + (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TRUE) + || var_check_ro(di->di_flags, arg_errmsg, TRUE))) + break; + vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); + r = filter_map_one(&di->di_tv, expr, map, &rem); + clear_tv(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) + break; + if (!map && rem) + { + if (var_check_fixed(di->di_flags, arg_errmsg, TRUE) + || var_check_ro(di->di_flags, arg_errmsg, TRUE)) + break; + dictitem_remove(d, di); + } + } + } + hash_unlock(ht); + } + else if (argvars[0].v_type == VAR_BLOB) + { + int i; + typval_T tv; + + vimvars[VV_KEY].vv_type = VAR_NUMBER; + for (i = 0; i < b->bv_ga.ga_len; i++) + { + tv.v_type = VAR_NUMBER; + tv.vval.v_number = blob_get(b, i); + vimvars[VV_KEY].vv_nr = idx; + if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) + break; + if (tv.v_type != VAR_NUMBER) + { + emsg(_(e_invalblob)); + return; + } + tv.v_type = VAR_NUMBER; + blob_set(b, i, tv.vval.v_number); + if (!map && rem) + { + char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; + + mch_memmove(p + idx, p + i + 1, + (size_t)b->bv_ga.ga_len - i - 1); + --b->bv_ga.ga_len; + --i; + } + } + } + else + { + // argvars[0].v_type == VAR_LIST + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + for (li = l->lv_first; li != NULL; li = nli) + { + if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) + 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); +} + +#endif /* defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) */ diff --git a/src/evalfunc.c b/src/evalfunc.c new file mode 100644 index 0000000..fa7ed9b --- /dev/null +++ b/src/evalfunc.c @@ -0,0 +1,14809 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * evalfunc.c: Builtin functions + */ +#define USING_FLOAT_STUFF + +#include "vim.h" + +#if defined(FEAT_EVAL) || defined(PROTO) + +#ifdef AMIGA +# include /* for strftime() */ +#endif + +#ifdef VMS +# include +#endif + +#ifdef MACOS_X +# include /* for time_t */ +#endif + +static char *e_listarg = N_("E686: Argument of %s must be a List"); +static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob"); +static char *e_stringreq = N_("E928: String required"); + +#ifdef FEAT_FLOAT +static void f_abs(typval_T *argvars, typval_T *rettv); +static void f_acos(typval_T *argvars, typval_T *rettv); +#endif +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_appendbufline(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_arglistid(typval_T *argvars, typval_T *rettv); +static void f_argv(typval_T *argvars, typval_T *rettv); +static void f_assert_beeps(typval_T *argvars, typval_T *rettv); +static void f_assert_equal(typval_T *argvars, typval_T *rettv); +static void f_assert_equalfile(typval_T *argvars, typval_T *rettv); +static void f_assert_exception(typval_T *argvars, typval_T *rettv); +static void f_assert_fails(typval_T *argvars, typval_T *rettv); +static void f_assert_false(typval_T *argvars, typval_T *rettv); +static void f_assert_inrange(typval_T *argvars, typval_T *rettv); +static void f_assert_match(typval_T *argvars, typval_T *rettv); +static void f_assert_notequal(typval_T *argvars, typval_T *rettv); +static void f_assert_notmatch(typval_T *argvars, typval_T *rettv); +static void f_assert_report(typval_T *argvars, typval_T *rettv); +static void f_assert_true(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +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); +#endif +#ifdef FEAT_BEVAL +static void f_balloon_show(typval_T *argvars, typval_T *rettv); +# if defined(FEAT_BEVAL_TERM) +static void f_balloon_split(typval_T *argvars, typval_T *rettv); +# endif +#endif +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_bufwinid(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); +#ifdef FEAT_FLOAT +static void f_ceil(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_JOB_CHANNEL +static void f_ch_canread(typval_T *argvars, typval_T *rettv); +static void f_ch_close(typval_T *argvars, typval_T *rettv); +static void f_ch_close_in(typval_T *argvars, typval_T *rettv); +static void f_ch_evalexpr(typval_T *argvars, typval_T *rettv); +static void f_ch_evalraw(typval_T *argvars, typval_T *rettv); +static void f_ch_getbufnr(typval_T *argvars, typval_T *rettv); +static void f_ch_getjob(typval_T *argvars, typval_T *rettv); +static void f_ch_info(typval_T *argvars, typval_T *rettv); +static void f_ch_log(typval_T *argvars, typval_T *rettv); +static void f_ch_logfile(typval_T *argvars, typval_T *rettv); +static void f_ch_open(typval_T *argvars, typval_T *rettv); +static void f_ch_read(typval_T *argvars, typval_T *rettv); +static void f_ch_readblob(typval_T *argvars, typval_T *rettv); +static void f_ch_readraw(typval_T *argvars, typval_T *rettv); +static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv); +static void f_ch_sendraw(typval_T *argvars, typval_T *rettv); +static void f_ch_setoptions(typval_T *argvars, typval_T *rettv); +static void f_ch_status(typval_T *argvars, typval_T *rettv); +#endif +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); +#if defined(FEAT_INS_EXPAND) +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); +#endif +static void f_confirm(typval_T *argvars, typval_T *rettv); +static void f_copy(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_cos(typval_T *argvars, typval_T *rettv); +static void f_cosh(typval_T *argvars, typval_T *rettv); +#endif +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); +#ifdef WIN3264 +static void f_debugbreak(typval_T *argvars, typval_T *rettv); +#endif +static void f_deepcopy(typval_T *argvars, typval_T *rettv); +static void f_delete(typval_T *argvars, typval_T *rettv); +static void f_deletebufline(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_execute(typval_T *argvars, typval_T *rettv); +static void f_exepath(typval_T *argvars, typval_T *rettv); +static void f_exists(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_exp(typval_T *argvars, typval_T *rettv); +#endif +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); +#ifdef FEAT_FLOAT +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); +#endif +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_funcref(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_getbufinfo(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_getchangelist(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_getcharsearch(typval_T *argvars, typval_T *rettv); +static void f_getcmdline(typval_T *argvars, typval_T *rettv); +#if defined(FEAT_CMDL_COMPL) +static void f_getcompletion(typval_T *argvars, typval_T *rettv); +#endif +static void f_getcmdpos(typval_T *argvars, typval_T *rettv); +static void f_getcmdtype(typval_T *argvars, typval_T *rettv); +static void f_getcmdwintype(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_getjumplist(typval_T *argvars, typval_T *rettv); +static void f_getline(typval_T *argvars, typval_T *rettv); +static void f_getloclist(typval_T *argvars UNUSED, typval_T *rettv UNUSED); +static void f_getmatches(typval_T *argvars, typval_T *rettv); +static void f_getpid(typval_T *argvars, typval_T *rettv); +static void f_getcurpos(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_gettabinfo(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_gettagstack(typval_T *argvars, typval_T *rettv); +static void f_getwininfo(typval_T *argvars, typval_T *rettv); +static void f_getwinpos(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_glob2regpat(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); +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) +static void f_isnan(typval_T *argvars, typval_T *rettv); +#endif +static void f_items(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_JOB_CHANNEL +static void f_job_getchannel(typval_T *argvars, typval_T *rettv); +static void f_job_info(typval_T *argvars, typval_T *rettv); +static void f_job_setoptions(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_status(typval_T *argvars, typval_T *rettv); +#endif +static void f_join(typval_T *argvars, typval_T *rettv); +static void f_js_decode(typval_T *argvars, typval_T *rettv); +static void f_js_encode(typval_T *argvars, typval_T *rettv); +static void f_json_decode(typval_T *argvars, typval_T *rettv); +static void f_json_encode(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); +#ifdef FEAT_FLOAT +static void f_log(typval_T *argvars, typval_T *rettv); +static void f_log10(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_LUA +static void f_luaeval(typval_T *argvars, typval_T *rettv); +#endif +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_matchaddpos(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_matchstrpos(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); +#ifdef vim_mkdir +static void f_mkdir(typval_T *argvars, typval_T *rettv); +#endif +static void f_mode(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_MZSCHEME +static void f_mzeval(typval_T *argvars, typval_T *rettv); +#endif +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); +#ifdef FEAT_PERL +static void f_perleval(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_FLOAT +static void f_pow(typval_T *argvars, typval_T *rettv); +#endif +static void f_prevnonblank(typval_T *argvars, typval_T *rettv); +static void f_printf(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_JOB_CHANNEL +static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv); +static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv); +static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv); +#endif +static void f_pumvisible(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_PYTHON3 +static void f_py3eval(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_PYTHON +static void f_pyeval(typval_T *argvars, typval_T *rettv); +#endif +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) +static void f_pyxeval(typval_T *argvars, typval_T *rettv); +#endif +static void f_range(typval_T *argvars, typval_T *rettv); +static void f_readfile(typval_T *argvars, typval_T *rettv); +static void f_reg_executing(typval_T *argvars, typval_T *rettv); +static void f_reg_recording(typval_T *argvars, typval_T *rettv); +static void f_reltime(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_reltimefloat(typval_T *argvars, typval_T *rettv); +#endif +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_remote_startserver(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); +#ifdef FEAT_FLOAT +static void f_round(typval_T *argvars, typval_T *rettv); +#endif +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_setbufline(typval_T *argvars, typval_T *rettv); +static void f_setbufvar(typval_T *argvars, typval_T *rettv); +static void f_setcharsearch(typval_T *argvars, typval_T *rettv); +static void f_setcmdpos(typval_T *argvars, typval_T *rettv); +static void f_setfperm(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_settagstack(typval_T *argvars, typval_T *rettv); +static void f_setwinvar(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_CRYPT +static void f_sha256(typval_T *argvars, typval_T *rettv); +#endif /* FEAT_CRYPT */ +static void f_shellescape(typval_T *argvars, typval_T *rettv); +static void f_shiftwidth(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_SIGNS +static void f_sign_define(typval_T *argvars, typval_T *rettv); +static void f_sign_getdefined(typval_T *argvars, typval_T *rettv); +static void f_sign_getplaced(typval_T *argvars, typval_T *rettv); +static void f_sign_jump(typval_T *argvars, typval_T *rettv); +static void f_sign_place(typval_T *argvars, typval_T *rettv); +static void f_sign_undefine(typval_T *argvars, typval_T *rettv); +static void f_sign_unplace(typval_T *argvars, typval_T *rettv); +#endif +static void f_simplify(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_sin(typval_T *argvars, typval_T *rettv); +static void f_sinh(typval_T *argvars, typval_T *rettv); +#endif +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); +#ifdef FEAT_FLOAT +static void f_sqrt(typval_T *argvars, typval_T *rettv); +static void f_str2float(typval_T *argvars, typval_T *rettv); +#endif +static void f_str2nr(typval_T *argvars, typval_T *rettv); +static void f_strchars(typval_T *argvars, typval_T *rettv); +#ifdef HAVE_STRFTIME +static void f_strftime(typval_T *argvars, typval_T *rettv); +#endif +static void f_strgetchar(typval_T *argvars, typval_T *rettv); +static void f_stridx(typval_T *argvars, typval_T *rettv); +static void f_strlen(typval_T *argvars, typval_T *rettv); +static void f_strcharpart(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_swapinfo(typval_T *argvars, typval_T *rettv); +static void f_swapname(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_systemlist(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_alloc_fail(typval_T *argvars, typval_T *rettv); +static void f_test_autochdir(typval_T *argvars, typval_T *rettv); +static void f_test_feedinput(typval_T *argvars, typval_T *rettv); +static void f_test_option_not_set(typval_T *argvars, typval_T *rettv); +static void f_test_override(typval_T *argvars, typval_T *rettv); +static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv); +static void f_test_ignore_error(typval_T *argvars, typval_T *rettv); +static void f_test_null_blob(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_JOB_CHANNEL +static void f_test_null_channel(typval_T *argvars, typval_T *rettv); +#endif +static void f_test_null_dict(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_JOB_CHANNEL +static void f_test_null_job(typval_T *argvars, typval_T *rettv); +#endif +static void f_test_null_list(typval_T *argvars, typval_T *rettv); +static void f_test_null_partial(typval_T *argvars, typval_T *rettv); +static void f_test_null_string(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_GUI +static void f_test_scrollbar(typval_T *argvars, typval_T *rettv); +#endif +static void f_test_settime(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_tan(typval_T *argvars, typval_T *rettv); +static void f_tanh(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_TIMERS +static void f_timer_info(typval_T *argvars, typval_T *rettv); +static void f_timer_pause(typval_T *argvars, typval_T *rettv); +static void f_timer_start(typval_T *argvars, typval_T *rettv); +static void f_timer_stop(typval_T *argvars, typval_T *rettv); +static void f_timer_stopall(typval_T *argvars, typval_T *rettv); +#endif +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_trim(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_trunc(typval_T *argvars, typval_T *rettv); +#endif +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_win_findbuf(typval_T *argvars, typval_T *rettv); +static void f_win_getid(typval_T *argvars, typval_T *rettv); +static void f_win_gotoid(typval_T *argvars, typval_T *rettv); +static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv); +static void f_win_id2win(typval_T *argvars, typval_T *rettv); +static void f_win_screenpos(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_winlayout(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_wordcount(typval_T *argvars, typval_T *rettv); +static void f_xor(typval_T *argvars, typval_T *rettv); + +/* + * 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[] = +{ +#ifdef FEAT_FLOAT + {"abs", 1, 1, f_abs}, + {"acos", 1, 1, f_acos}, /* WJMc */ +#endif + {"add", 2, 2, f_add}, + {"and", 2, 2, f_and}, + {"append", 2, 2, f_append}, + {"appendbufline", 3, 3, f_appendbufline}, + {"argc", 0, 1, f_argc}, + {"argidx", 0, 0, f_argidx}, + {"arglistid", 0, 2, f_arglistid}, + {"argv", 0, 2, f_argv}, +#ifdef FEAT_FLOAT + {"asin", 1, 1, f_asin}, /* WJMc */ +#endif + {"assert_beeps", 1, 2, f_assert_beeps}, + {"assert_equal", 2, 3, f_assert_equal}, + {"assert_equalfile", 2, 2, f_assert_equalfile}, + {"assert_exception", 1, 2, f_assert_exception}, + {"assert_fails", 1, 3, f_assert_fails}, + {"assert_false", 1, 2, f_assert_false}, + {"assert_inrange", 3, 4, f_assert_inrange}, + {"assert_match", 2, 3, f_assert_match}, + {"assert_notequal", 2, 3, f_assert_notequal}, + {"assert_notmatch", 2, 3, f_assert_notmatch}, + {"assert_report", 1, 1, f_assert_report}, + {"assert_true", 1, 2, f_assert_true}, +#ifdef FEAT_FLOAT + {"atan", 1, 1, f_atan}, + {"atan2", 2, 2, f_atan2}, +#endif +#ifdef FEAT_BEVAL + {"balloon_show", 1, 1, f_balloon_show}, +# if defined(FEAT_BEVAL_TERM) + {"balloon_split", 1, 1, f_balloon_split}, +# endif +#endif + {"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}, + {"bufwinid", 1, 1, f_bufwinid}, + {"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}, +#ifdef FEAT_FLOAT + {"ceil", 1, 1, f_ceil}, +#endif +#ifdef FEAT_JOB_CHANNEL + {"ch_canread", 1, 1, f_ch_canread}, + {"ch_close", 1, 1, f_ch_close}, + {"ch_close_in", 1, 1, f_ch_close_in}, + {"ch_evalexpr", 2, 3, f_ch_evalexpr}, + {"ch_evalraw", 2, 3, f_ch_evalraw}, + {"ch_getbufnr", 2, 2, f_ch_getbufnr}, + {"ch_getjob", 1, 1, f_ch_getjob}, + {"ch_info", 1, 1, f_ch_info}, + {"ch_log", 1, 2, f_ch_log}, + {"ch_logfile", 1, 2, f_ch_logfile}, + {"ch_open", 1, 2, f_ch_open}, + {"ch_read", 1, 2, f_ch_read}, + {"ch_readblob", 1, 2, f_ch_readblob}, + {"ch_readraw", 1, 2, f_ch_readraw}, + {"ch_sendexpr", 2, 3, f_ch_sendexpr}, + {"ch_sendraw", 2, 3, f_ch_sendraw}, + {"ch_setoptions", 2, 2, f_ch_setoptions}, + {"ch_status", 1, 2, f_ch_status}, +#endif + {"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}, +#if defined(FEAT_INS_EXPAND) + {"complete", 2, 2, f_complete}, + {"complete_add", 1, 1, f_complete_add}, + {"complete_check", 0, 0, f_complete_check}, +#endif + {"confirm", 1, 4, f_confirm}, + {"copy", 1, 1, f_copy}, +#ifdef FEAT_FLOAT + {"cos", 1, 1, f_cos}, + {"cosh", 1, 1, f_cosh}, +#endif + {"count", 2, 4, f_count}, + {"cscope_connection",0,3, f_cscope_connection}, + {"cursor", 1, 3, f_cursor}, +#ifdef WIN3264 + {"debugbreak", 1, 1, f_debugbreak}, +#endif + {"deepcopy", 1, 2, f_deepcopy}, + {"delete", 1, 2, f_delete}, + {"deletebufline", 2, 3, f_deletebufline}, + {"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}, + {"execute", 1, 2, f_execute}, + {"exepath", 1, 1, f_exepath}, + {"exists", 1, 1, f_exists}, +#ifdef FEAT_FLOAT + {"exp", 1, 1, f_exp}, +#endif + {"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}, +#ifdef FEAT_FLOAT + {"float2nr", 1, 1, f_float2nr}, + {"floor", 1, 1, f_floor}, + {"fmod", 2, 2, f_fmod}, +#endif + {"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}, + {"funcref", 1, 3, f_funcref}, + {"function", 1, 3, f_function}, + {"garbagecollect", 0, 1, f_garbagecollect}, + {"get", 2, 3, f_get}, + {"getbufinfo", 0, 1, f_getbufinfo}, + {"getbufline", 2, 3, f_getbufline}, + {"getbufvar", 2, 3, f_getbufvar}, + {"getchangelist", 1, 1, f_getchangelist}, + {"getchar", 0, 1, f_getchar}, + {"getcharmod", 0, 0, f_getcharmod}, + {"getcharsearch", 0, 0, f_getcharsearch}, + {"getcmdline", 0, 0, f_getcmdline}, + {"getcmdpos", 0, 0, f_getcmdpos}, + {"getcmdtype", 0, 0, f_getcmdtype}, + {"getcmdwintype", 0, 0, f_getcmdwintype}, +#if defined(FEAT_CMDL_COMPL) + {"getcompletion", 2, 3, f_getcompletion}, +#endif + {"getcurpos", 0, 0, f_getcurpos}, + {"getcwd", 0, 2, 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}, + {"getjumplist", 0, 2, f_getjumplist}, + {"getline", 1, 2, f_getline}, + {"getloclist", 1, 2, f_getloclist}, + {"getmatches", 0, 0, f_getmatches}, + {"getpid", 0, 0, f_getpid}, + {"getpos", 1, 1, f_getpos}, + {"getqflist", 0, 1, f_getqflist}, + {"getreg", 0, 3, f_getreg}, + {"getregtype", 0, 1, f_getregtype}, + {"gettabinfo", 0, 1, f_gettabinfo}, + {"gettabvar", 2, 3, f_gettabvar}, + {"gettabwinvar", 3, 4, f_gettabwinvar}, + {"gettagstack", 0, 1, f_gettagstack}, + {"getwininfo", 0, 1, f_getwininfo}, + {"getwinpos", 0, 1, f_getwinpos}, + {"getwinposx", 0, 0, f_getwinposx}, + {"getwinposy", 0, 0, f_getwinposy}, + {"getwinvar", 2, 3, f_getwinvar}, + {"glob", 1, 4, f_glob}, + {"glob2regpat", 1, 1, f_glob2regpat}, + {"globpath", 2, 5, f_globpath}, + {"has", 1, 1, f_has}, + {"has_key", 2, 2, f_has_key}, + {"haslocaldir", 0, 2, 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}, +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) + {"isnan", 1, 1, f_isnan}, +#endif + {"items", 1, 1, f_items}, +#ifdef FEAT_JOB_CHANNEL + {"job_getchannel", 1, 1, f_job_getchannel}, + {"job_info", 0, 1, f_job_info}, + {"job_setoptions", 2, 2, f_job_setoptions}, + {"job_start", 1, 2, f_job_start}, + {"job_status", 1, 1, f_job_status}, + {"job_stop", 1, 2, f_job_stop}, +#endif + {"join", 1, 2, f_join}, + {"js_decode", 1, 1, f_js_decode}, + {"js_encode", 1, 1, f_js_encode}, + {"json_decode", 1, 1, f_json_decode}, + {"json_encode", 1, 1, f_json_encode}, + {"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}, +#ifdef FEAT_FLOAT + {"log", 1, 1, f_log}, + {"log10", 1, 1, f_log10}, +#endif +#ifdef FEAT_LUA + {"luaeval", 1, 2, f_luaeval}, +#endif + {"map", 2, 2, f_map}, + {"maparg", 1, 4, f_maparg}, + {"mapcheck", 1, 3, f_mapcheck}, + {"match", 2, 4, f_match}, + {"matchadd", 2, 5, f_matchadd}, + {"matchaddpos", 2, 5, f_matchaddpos}, + {"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}, + {"matchstrpos", 2, 4, f_matchstrpos}, + {"max", 1, 1, f_max}, + {"min", 1, 1, f_min}, +#ifdef vim_mkdir + {"mkdir", 1, 3, f_mkdir}, +#endif + {"mode", 0, 1, f_mode}, +#ifdef FEAT_MZSCHEME + {"mzeval", 1, 1, f_mzeval}, +#endif + {"nextnonblank", 1, 1, f_nextnonblank}, + {"nr2char", 1, 2, f_nr2char}, + {"or", 2, 2, f_or}, + {"pathshorten", 1, 1, f_pathshorten}, +#ifdef FEAT_PERL + {"perleval", 1, 1, f_perleval}, +#endif +#ifdef FEAT_FLOAT + {"pow", 2, 2, f_pow}, +#endif + {"prevnonblank", 1, 1, f_prevnonblank}, + {"printf", 1, 19, f_printf}, +#ifdef FEAT_JOB_CHANNEL + {"prompt_setcallback", 2, 2, f_prompt_setcallback}, + {"prompt_setinterrupt", 2, 2, f_prompt_setinterrupt}, + {"prompt_setprompt", 2, 2, f_prompt_setprompt}, +#endif +#ifdef FEAT_TEXT_PROP + {"prop_add", 3, 3, f_prop_add}, + {"prop_clear", 1, 3, f_prop_clear}, + {"prop_list", 1, 2, f_prop_list}, + {"prop_remove", 2, 3, f_prop_remove}, + {"prop_type_add", 2, 2, f_prop_type_add}, + {"prop_type_change", 2, 2, f_prop_type_change}, + {"prop_type_delete", 1, 2, f_prop_type_delete}, + {"prop_type_get", 1, 2, f_prop_type_get}, + {"prop_type_list", 0, 1, f_prop_type_list}, +#endif + {"pumvisible", 0, 0, f_pumvisible}, +#ifdef FEAT_PYTHON3 + {"py3eval", 1, 1, f_py3eval}, +#endif +#ifdef FEAT_PYTHON + {"pyeval", 1, 1, f_pyeval}, +#endif +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) + {"pyxeval", 1, 1, f_pyxeval}, +#endif + {"range", 1, 3, f_range}, + {"readfile", 1, 3, f_readfile}, + {"reg_executing", 0, 0, f_reg_executing}, + {"reg_recording", 0, 0, f_reg_recording}, + {"reltime", 0, 2, f_reltime}, +#ifdef FEAT_FLOAT + {"reltimefloat", 1, 1, f_reltimefloat}, +#endif + {"reltimestr", 1, 1, f_reltimestr}, + {"remote_expr", 2, 4, f_remote_expr}, + {"remote_foreground", 1, 1, f_remote_foreground}, + {"remote_peek", 1, 2, f_remote_peek}, + {"remote_read", 1, 2, f_remote_read}, + {"remote_send", 2, 3, f_remote_send}, + {"remote_startserver", 1, 1, f_remote_startserver}, + {"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}, +#ifdef FEAT_FLOAT + {"round", 1, 1, f_round}, +#endif + {"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}, + {"setbufline", 3, 3, f_setbufline}, + {"setbufvar", 3, 3, f_setbufvar}, + {"setcharsearch", 1, 1, f_setcharsearch}, + {"setcmdpos", 1, 1, f_setcmdpos}, + {"setfperm", 2, 2, f_setfperm}, + {"setline", 2, 2, f_setline}, + {"setloclist", 2, 4, f_setloclist}, + {"setmatches", 1, 1, f_setmatches}, + {"setpos", 2, 2, f_setpos}, + {"setqflist", 1, 3, f_setqflist}, + {"setreg", 2, 3, f_setreg}, + {"settabvar", 3, 3, f_settabvar}, + {"settabwinvar", 4, 4, f_settabwinvar}, + {"settagstack", 2, 3, f_settagstack}, + {"setwinvar", 3, 3, f_setwinvar}, +#ifdef FEAT_CRYPT + {"sha256", 1, 1, f_sha256}, +#endif + {"shellescape", 1, 2, f_shellescape}, + {"shiftwidth", 0, 1, f_shiftwidth}, +#ifdef FEAT_SIGNS + {"sign_define", 1, 2, f_sign_define}, + {"sign_getdefined", 0, 1, f_sign_getdefined}, + {"sign_getplaced", 0, 2, f_sign_getplaced}, + {"sign_jump", 3, 3, f_sign_jump}, + {"sign_place", 4, 5, f_sign_place}, + {"sign_undefine", 0, 1, f_sign_undefine}, + {"sign_unplace", 1, 2, f_sign_unplace}, +#endif + {"simplify", 1, 1, f_simplify}, +#ifdef FEAT_FLOAT + {"sin", 1, 1, f_sin}, + {"sinh", 1, 1, f_sinh}, +#endif + {"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}, +#ifdef FEAT_FLOAT + {"sqrt", 1, 1, f_sqrt}, + {"str2float", 1, 1, f_str2float}, +#endif + {"str2nr", 1, 2, f_str2nr}, + {"strcharpart", 2, 3, f_strcharpart}, + {"strchars", 1, 2, f_strchars}, + {"strdisplaywidth", 1, 2, f_strdisplaywidth}, +#ifdef HAVE_STRFTIME + {"strftime", 1, 2, f_strftime}, +#endif + {"strgetchar", 2, 2, f_strgetchar}, + {"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}, + {"swapinfo", 1, 1, f_swapinfo}, + {"swapname", 1, 1, f_swapname}, + {"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}, + {"systemlist", 1, 2, f_systemlist}, + {"tabpagebuflist", 0, 1, f_tabpagebuflist}, + {"tabpagenr", 0, 1, f_tabpagenr}, + {"tabpagewinnr", 1, 2, f_tabpagewinnr}, + {"tagfiles", 0, 0, f_tagfiles}, + {"taglist", 1, 2, f_taglist}, +#ifdef FEAT_FLOAT + {"tan", 1, 1, f_tan}, + {"tanh", 1, 1, f_tanh}, +#endif + {"tempname", 0, 0, f_tempname}, +#ifdef FEAT_TERMINAL + {"term_dumpdiff", 2, 3, f_term_dumpdiff}, + {"term_dumpload", 1, 2, f_term_dumpload}, + {"term_dumpwrite", 2, 3, f_term_dumpwrite}, + {"term_getaltscreen", 1, 1, f_term_getaltscreen}, +# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + {"term_getansicolors", 1, 1, f_term_getansicolors}, +# endif + {"term_getattr", 2, 2, f_term_getattr}, + {"term_getcursor", 1, 1, f_term_getcursor}, + {"term_getjob", 1, 1, f_term_getjob}, + {"term_getline", 2, 2, f_term_getline}, + {"term_getscrolled", 1, 1, f_term_getscrolled}, + {"term_getsize", 1, 1, f_term_getsize}, + {"term_getstatus", 1, 1, f_term_getstatus}, + {"term_gettitle", 1, 1, f_term_gettitle}, + {"term_gettty", 1, 2, f_term_gettty}, + {"term_list", 0, 0, f_term_list}, + {"term_scrape", 2, 2, f_term_scrape}, + {"term_sendkeys", 2, 2, f_term_sendkeys}, +# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + {"term_setansicolors", 2, 2, f_term_setansicolors}, +# endif + {"term_setkill", 2, 2, f_term_setkill}, + {"term_setrestore", 2, 2, f_term_setrestore}, + {"term_setsize", 3, 3, f_term_setsize}, + {"term_start", 1, 2, f_term_start}, + {"term_wait", 1, 2, f_term_wait}, +#endif + {"test_alloc_fail", 3, 3, f_test_alloc_fail}, + {"test_autochdir", 0, 0, f_test_autochdir}, + {"test_feedinput", 1, 1, f_test_feedinput}, + {"test_garbagecollect_now", 0, 0, f_test_garbagecollect_now}, + {"test_ignore_error", 1, 1, f_test_ignore_error}, + {"test_null_blob", 0, 0, f_test_null_blob}, +#ifdef FEAT_JOB_CHANNEL + {"test_null_channel", 0, 0, f_test_null_channel}, +#endif + {"test_null_dict", 0, 0, f_test_null_dict}, +#ifdef FEAT_JOB_CHANNEL + {"test_null_job", 0, 0, f_test_null_job}, +#endif + {"test_null_list", 0, 0, f_test_null_list}, + {"test_null_partial", 0, 0, f_test_null_partial}, + {"test_null_string", 0, 0, f_test_null_string}, + {"test_option_not_set", 1, 1, f_test_option_not_set}, + {"test_override", 2, 2, f_test_override}, +#ifdef FEAT_GUI + {"test_scrollbar", 3, 3, f_test_scrollbar}, +#endif + {"test_settime", 1, 1, f_test_settime}, +#ifdef FEAT_TIMERS + {"timer_info", 0, 1, f_timer_info}, + {"timer_pause", 2, 2, f_timer_pause}, + {"timer_start", 2, 3, f_timer_start}, + {"timer_stop", 1, 1, f_timer_stop}, + {"timer_stopall", 0, 0, f_timer_stopall}, +#endif + {"tolower", 1, 1, f_tolower}, + {"toupper", 1, 1, f_toupper}, + {"tr", 3, 3, f_tr}, + {"trim", 1, 2, f_trim}, +#ifdef FEAT_FLOAT + {"trunc", 1, 1, f_trunc}, +#endif + {"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}, + {"win_findbuf", 1, 1, f_win_findbuf}, + {"win_getid", 0, 2, f_win_getid}, + {"win_gotoid", 1, 1, f_win_gotoid}, + {"win_id2tabwin", 1, 1, f_win_id2tabwin}, + {"win_id2win", 1, 1, f_win_id2win}, + {"win_screenpos", 1, 1, f_win_screenpos}, + {"winbufnr", 1, 1, f_winbufnr}, + {"wincol", 0, 0, f_wincol}, + {"winheight", 1, 1, f_winheight}, + {"winlayout", 0, 1, f_winlayout}, + {"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}, + {"wordcount", 0, 0, f_wordcount}, + {"writefile", 2, 3, f_writefile}, + {"xor", 2, 2, f_xor}, +}; + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + +/* + * 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); +} + +#endif /* FEAT_CMDL_COMPL */ + +/* + * Find internal function in table above. + * Return index, or -1 if not found + */ + int +find_internal_func( + char_u *name) /* name of the function */ +{ + int first = 0; + int last = (int)(sizeof(functions) / sizeof(struct fst)) - 1; + int cmp; + int x; + + /* + * Find the function name in the table. Binary search. + */ + while (first <= last) + { + x = first + ((unsigned)(last - first) >> 1); + 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; +} + + int +call_internal_func( + char_u *name, + int argcount, + typval_T *argvars, + typval_T *rettv) +{ + int i; + + i = find_internal_func(name); + if (i < 0) + return ERROR_UNKNOWN; + if (argcount < functions[i].f_min_argc) + return ERROR_TOOFEW; + if (argcount > functions[i].f_max_argc) + return ERROR_TOOMANY; + argvars[argcount].v_type = VAR_UNKNOWN; + functions[i].f_func(argvars, rettv); + return ERROR_NONE; +} + +/* + * 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_SPECIAL + && argvars[0].vval.v_number == VVAL_TRUE) + || (argvars[0].v_type == VAR_STRING + && argvars[0].vval.v_string != NULL + && *argvars[0].vval.v_string != NUL)); +} + +/* + * 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 +tv_get_lnum(typval_T *argvars) +{ + typval_T rettv; + linenr_T lnum; + + lnum = (linenr_T)tv_get_number_chk(&argvars[0], NULL); + if (lnum == 0) /* no valid number, try using line() */ + { + rettv.v_type = VAR_NUMBER; + f_line(argvars, &rettv); + lnum = (linenr_T)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 +tv_get_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 (linenr_T)tv_get_number_chk(&argvars[0], NULL); +} + +#ifdef FEAT_FLOAT +/* + * 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 = tv_get_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 = 0.0; + + 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; +} +#endif + +/* + * "add(list, item)" function + */ + static void +f_add(typval_T *argvars, typval_T *rettv) +{ + list_T *l; + blob_T *b; + + 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 *)N_("add() argument"), TRUE) + && list_append_tv(l, &argvars[1]) == OK) + copy_tv(&argvars[0], rettv); + } + else if (argvars[0].v_type == VAR_BLOB) + { + if ((b = argvars[0].vval.v_blob) != NULL + && !tv_check_lock(b->bv_lock, + (char_u *)N_("add() argument"), TRUE)) + { + int error = FALSE; + varnumber_T n = tv_get_number_chk(&argvars[1], &error); + + if (!error) + { + ga_append(&b->bv_ga, (int)n); + copy_tv(&argvars[0], rettv); + } + } + } + else + emsg(_(e_listblobreq)); +} + +/* + * "and(expr, expr)" function + */ + static void +f_and(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + & tv_get_number_chk(&argvars[1], NULL); +} + +/* + * If there is a window for "curbuf", make it the current window. + */ + static void +find_win_for_curbuf(void) +{ + wininfo_T *wip; + + for (wip = curbuf->b_wininfo; wip != NULL; wip = wip->wi_next) + { + if (wip->wi_win != NULL) + { + curwin = wip->wi_win; + break; + } + } +} + +/* + * Set line or list of lines in buffer "buf". + */ + static void +set_buffer_lines( + buf_T *buf, + linenr_T lnum_arg, + int append, + typval_T *lines, + typval_T *rettv) +{ + linenr_T lnum = lnum_arg + (append ? 1 : 0); + char_u *line = NULL; + list_T *l = NULL; + listitem_T *li = NULL; + long added = 0; + linenr_T append_lnum; + buf_T *curbuf_save = NULL; + win_T *curwin_save = NULL; + int is_curbuf = buf == curbuf; + + /* When using the current buffer ml_mfp will be set if needed. Useful when + * setline() is used on startup. For other buffers the buffer must be + * loaded. */ + if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1) + { + rettv->vval.v_number = 1; /* FAIL */ + return; + } + + if (!is_curbuf) + { + curbuf_save = curbuf; + curwin_save = curwin; + curbuf = buf; + find_win_for_curbuf(); + } + + if (append) + // appendbufline() uses the line number below which we insert + append_lnum = lnum - 1; + else + // setbufline() uses the line number above which we insert, we only + // append if it's below the last line + append_lnum = curbuf->b_ml.ml_line_count; + + if (lines->v_type == VAR_LIST) + { + l = lines->vval.v_list; + li = l->lv_first; + } + else + line = tv_get_string_chk(lines); + + /* default result is zero == OK */ + for (;;) + { + if (l != NULL) + { + /* list argument, get next string */ + if (li == NULL) + break; + line = tv_get_string_chk(&li->li_tv); + li = li->li_next; + } + + rettv->vval.v_number = 1; /* FAIL */ + if (line == NULL || 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 (!append && lnum <= curbuf->b_ml.ml_line_count) + { + // Existing line, replace it. + // Removes any existing text properties. + if (u_savesub(lnum) == OK && ml_replace_len( + lnum, line, (colnr_T)STRLEN(line) + 1, TRUE, TRUE) == OK) + { + changed_bytes(lnum, 0); + if (is_curbuf && lnum == curwin->w_cursor.lnum) + check_cursor_col(); + rettv->vval.v_number = 0; /* OK */ + } + } + else if (added > 0 || u_save(lnum - 1, lnum) == OK) + { + /* 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) + { + win_T *wp; + tabpage_T *tp; + + appended_lines_mark(append_lnum, added); + FOR_ALL_TAB_WINDOWS(tp, wp) + if (wp->w_buffer == buf && wp->w_cursor.lnum > append_lnum) + wp->w_cursor.lnum += added; + check_cursor_col(); + +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf) && (State & INSERT)) + // show the line with the prompt + update_topline(); +#endif + } + + if (!is_curbuf) + { + curbuf = curbuf_save; + curwin = curwin_save; + } +} + +/* + * "append(lnum, string/list)" function + */ + static void +f_append(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum = tv_get_lnum(&argvars[0]); + + set_buffer_lines(curbuf, lnum, TRUE, &argvars[1], rettv); +} + +/* + * "appendbufline(buf, lnum, string/list)" function + */ + static void +f_appendbufline(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + buf_T *buf; + + buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL) + rettv->vval.v_number = 1; /* FAIL */ + else + { + lnum = tv_get_lnum_buf(&argvars[1], buf); + set_buffer_lines(buf, lnum, TRUE, &argvars[2], rettv); + } +} + +/* + * "argc([window id])" function + */ + static void +f_argc(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + + if (argvars[0].v_type == VAR_UNKNOWN) + // use the current window + rettv->vval.v_number = ARGCOUNT; + else if (argvars[0].v_type == VAR_NUMBER + && tv_get_number(&argvars[0]) == -1) + // use the global argument list + rettv->vval.v_number = GARGCOUNT; + else + { + // use the argument list of the specified window + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp != NULL) + rettv->vval.v_number = WARGCOUNT(wp); + else + rettv->vval.v_number = -1; + } +} + +/* + * "argidx()" function + */ + static void +f_argidx(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = curwin->w_arg_idx; +} + +/* + * "arglistid()" function + */ + static void +f_arglistid(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + + rettv->vval.v_number = -1; + wp = find_tabwin(&argvars[0], &argvars[1]); + if (wp != NULL) + rettv->vval.v_number = wp->w_alist->id; +} + +/* + * Get the argument list for a given window + */ + static void +get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv) +{ + int idx; + + if (rettv_list_alloc(rettv) == OK && arglist != NULL) + for (idx = 0; idx < argcount; ++idx) + list_append_string(rettv->vval.v_list, + alist_name(&arglist[idx]), -1); +} + +/* + * "argv(nr)" function + */ + static void +f_argv(typval_T *argvars, typval_T *rettv) +{ + int idx; + aentry_T *arglist = NULL; + int argcount = -1; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type == VAR_UNKNOWN) + { + arglist = ARGLIST; + argcount = ARGCOUNT; + } + else if (argvars[1].v_type == VAR_NUMBER + && tv_get_number(&argvars[1]) == -1) + { + arglist = GARGLIST; + argcount = GARGCOUNT; + } + else + { + win_T *wp = find_win_by_nr_or_id(&argvars[1]); + + if (wp != NULL) + { + /* Use the argument list of the specified window */ + arglist = WARGLIST(wp); + argcount = WARGCOUNT(wp); + } + } + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + idx = tv_get_number_chk(&argvars[0], NULL); + if (arglist != NULL && idx >= 0 && idx < argcount) + rettv->vval.v_string = vim_strsave(alist_name(&arglist[idx])); + else if (idx == -1) + get_arglist_as_rettv(arglist, argcount, rettv); + } + else + get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv); +} + +/* + * "assert_beeps(cmd [, error])" function + */ + static void +f_assert_beeps(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_beeps(argvars); +} + +/* + * "assert_equal(expected, actual[, msg])" function + */ + static void +f_assert_equal(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL); +} + +/* + * "assert_equalfile(fname-one, fname-two)" function + */ + static void +f_assert_equalfile(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_equalfile(argvars); +} + +/* + * "assert_notequal(expected, actual[, msg])" function + */ + static void +f_assert_notequal(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL); +} + +/* + * "assert_exception(string[, msg])" function + */ + static void +f_assert_exception(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_exception(argvars); +} + +/* + * "assert_fails(cmd [, error[, msg]])" function + */ + static void +f_assert_fails(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_fails(argvars); +} + +/* + * "assert_false(actual[, msg])" function + */ + static void +f_assert_false(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_bool(argvars, FALSE); +} + +/* + * "assert_inrange(lower, upper[, msg])" function + */ + static void +f_assert_inrange(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_inrange(argvars); +} + +/* + * "assert_match(pattern, actual[, msg])" function + */ + static void +f_assert_match(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH); +} + +/* + * "assert_notmatch(pattern, actual[, msg])" function + */ + static void +f_assert_notmatch(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH); +} + +/* + * "assert_report(msg)" function + */ + static void +f_assert_report(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_report(argvars); +} + +/* + * "assert_true(actual[, msg])" function + */ + static void +f_assert_true(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = assert_bool(argvars, TRUE); +} + +#ifdef FEAT_FLOAT +/* + * "asin()" function + */ + static void +f_asin(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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 = 0.0; + + 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 = 0.0, fy = 0.0; + + 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; +} +#endif + +/* + * "balloon_show()" function + */ +#ifdef FEAT_BEVAL + static void +f_balloon_show(typval_T *argvars, typval_T *rettv UNUSED) +{ + if (balloonEval != NULL) + { + if (argvars[0].v_type == VAR_LIST +# ifdef FEAT_GUI + && !gui.in_use +# endif + ) + post_balloon(balloonEval, NULL, argvars[0].vval.v_list); + else + post_balloon(balloonEval, tv_get_string_chk(&argvars[0]), NULL); + } +} + +# if defined(FEAT_BEVAL_TERM) + static void +f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED) +{ + if (rettv_list_alloc(rettv) == OK) + { + char_u *msg = tv_get_string_chk(&argvars[0]); + + if (msg != NULL) + { + pumitem_T *array; + int size = split_message(msg, &array); + int i; + + /* Skip the first and last item, they are always empty. */ + for (i = 1; i < size - 1; ++i) + list_append_string(rettv->vval.v_list, array[i].pum_text, -1); + while (size > 0) + vim_free(array[--size].pum_text); + vim_free(array); + } + } +} +# endif +#endif + +/* + * "browse(save, title, initdir, default)" function + */ + static void +f_browse(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_BROWSE + int save; + char_u *title; + char_u *initdir; + char_u *defname; + char_u buf[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + int error = FALSE; + + save = (int)tv_get_number_chk(&argvars[0], &error); + title = tv_get_string_chk(&argvars[1]); + initdir = tv_get_string_buf_chk(&argvars[2], buf); + defname = tv_get_string_buf_chk(&argvars[3], buf2); + + if (error || title == NULL || initdir == NULL || defname == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = + do_browse(save ? BROWSE_SAVE : 0, + title, defname, NULL, initdir, NULL, curbuf); +#else + rettv->vval.v_string = NULL; +#endif + rettv->v_type = VAR_STRING; +} + +/* + * "browsedir(title, initdir)" function + */ + static void +f_browsedir(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_BROWSE + char_u *title; + char_u *initdir; + char_u buf[NUMBUFLEN]; + + title = tv_get_string_chk(&argvars[0]); + initdir = tv_get_string_buf_chk(&argvars[1], buf); + + if (title == NULL || initdir == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = do_browse(BROWSE_DIR, + title, NULL, NULL, initdir, NULL, curbuf); +#else + rettv->vval.v_string = NULL; +#endif + rettv->v_type = VAR_STRING; +} + +/* + * 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_ALL_BUFFERS(buf) + if (buf->b_fname != NULL + && (path_with_url(buf->b_fname) +#ifdef FEAT_QUICKFIX + || bt_nofile(buf) +#endif + ) + && 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); +} + + buf_T * +buflist_find_by_name(char_u *name, int curtab_only) +{ + int save_magic; + char_u *save_cpo; + buf_T *buf; + + /* 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; + return buf; +} + +/* + * Get buffer by number or pattern. + */ + buf_T * +tv_get_buf(typval_T *tv, int curtab_only) +{ + char_u *name = tv->vval.v_string; + 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; + + buf = buflist_find_by_name(name, curtab_only); + + /* If not found, try expanding the name, like done for bufexists(). */ + if (buf == NULL) + buf = find_buffer(tv); + + return buf; +} + +#ifdef FEAT_SIGNS +/* + * Get the buffer from "arg" and give an error and return NULL if it is not + * valid. + */ + static buf_T * +get_buf_arg(typval_T *arg) +{ + buf_T *buf; + + ++emsg_off; + buf = tv_get_buf(arg, FALSE); + --emsg_off; + if (buf == NULL) + semsg(_("E158: Invalid buffer name: %s"), tv_get_string(arg)); + return buf; +} +#endif + +/* + * "bufname(expr)" function + */ + static void +f_bufname(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + + (void)tv_get_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = tv_get_buf(&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)tv_get_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = tv_get_buf(&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 + && tv_get_number_chk(&argvars[1], &error) != 0 + && !error + && (name = tv_get_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; +} + + static void +buf_win_common(typval_T *argvars, typval_T *rettv, int get_nr) +{ + win_T *wp; + int winnr = 0; + buf_T *buf; + + (void)tv_get_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = tv_get_buf(&argvars[0], TRUE); + FOR_ALL_WINDOWS(wp) + { + ++winnr; + if (wp->w_buffer == buf) + break; + } + rettv->vval.v_number = (wp != NULL ? (get_nr ? winnr : wp->w_id) : -1); + --emsg_off; +} + +/* + * "bufwinid(nr)" function + */ + static void +f_bufwinid(typval_T *argvars, typval_T *rettv) +{ + buf_win_common(argvars, rettv, FALSE); +} + +/* + * "bufwinnr(nr)" function + */ + static void +f_bufwinnr(typval_T *argvars, typval_T *rettv) +{ + buf_win_common(argvars, rettv, TRUE); +} + +/* + * "byte2line(byte)" function + */ + static void +f_byte2line(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifndef FEAT_BYTEOFF + rettv->vval.v_number = -1; +#else + long boff = 0; + + boff = tv_get_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); +#endif +} + + static void +byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED) +{ + char_u *t; + char_u *str; + varnumber_T idx; + + str = tv_get_string_chk(&argvars[0]); + idx = tv_get_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); +} + +/* + * "call(func, arglist [, dict])" function + */ + static void +f_call(typval_T *argvars, typval_T *rettv) +{ + char_u *func; + partial_T *partial = NULL; + 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 if (argvars[0].v_type == VAR_PARTIAL) + { + partial = argvars[0].vval.v_partial; + func = partial_name(partial); + } + else + func = tv_get_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], partial, selfdict, rettv); +} + +#ifdef FEAT_FLOAT +/* + * "ceil({float})" function + */ + static void +f_ceil(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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; +} +#endif + +#ifdef FEAT_JOB_CHANNEL +/* + * "ch_canread()" function + */ + static void +f_ch_canread(typval_T *argvars, typval_T *rettv) +{ + channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + + rettv->vval.v_number = 0; + if (channel != NULL) + rettv->vval.v_number = channel_has_readahead(channel, PART_SOCK) + || channel_has_readahead(channel, PART_OUT) + || channel_has_readahead(channel, PART_ERR); +} + +/* + * "ch_close()" function + */ + static void +f_ch_close(typval_T *argvars, typval_T *rettv UNUSED) +{ + channel_T *channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0); + + if (channel != NULL) + { + channel_close(channel, FALSE); + channel_clear(channel); + } +} + +/* + * "ch_close()" function + */ + static void +f_ch_close_in(typval_T *argvars, typval_T *rettv UNUSED) +{ + channel_T *channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0); + + if (channel != NULL) + channel_close_in(channel); +} + +/* + * "ch_getbufnr()" function + */ + static void +f_ch_getbufnr(typval_T *argvars, typval_T *rettv) +{ + channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + + rettv->vval.v_number = -1; + if (channel != NULL) + { + char_u *what = tv_get_string(&argvars[1]); + int part; + + if (STRCMP(what, "err") == 0) + part = PART_ERR; + else if (STRCMP(what, "out") == 0) + part = PART_OUT; + else if (STRCMP(what, "in") == 0) + part = PART_IN; + else + part = PART_SOCK; + if (channel->ch_part[part].ch_bufref.br_buf != NULL) + rettv->vval.v_number = + channel->ch_part[part].ch_bufref.br_buf->b_fnum; + } +} + +/* + * "ch_getjob()" function + */ + static void +f_ch_getjob(typval_T *argvars, typval_T *rettv) +{ + channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + + if (channel != NULL) + { + rettv->v_type = VAR_JOB; + rettv->vval.v_job = channel->ch_job; + if (channel->ch_job != NULL) + ++channel->ch_job->jv_refcount; + } +} + +/* + * "ch_info()" function + */ + static void +f_ch_info(typval_T *argvars, typval_T *rettv UNUSED) +{ + channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + + if (channel != NULL && rettv_dict_alloc(rettv) != FAIL) + channel_info(channel, rettv->vval.v_dict); +} + +/* + * "ch_log()" function + */ + static void +f_ch_log(typval_T *argvars, typval_T *rettv UNUSED) +{ + char_u *msg = tv_get_string(&argvars[0]); + channel_T *channel = NULL; + + if (argvars[1].v_type != VAR_UNKNOWN) + channel = get_channel_arg(&argvars[1], FALSE, FALSE, 0); + + ch_log(channel, "%s", msg); +} + +/* + * "ch_logfile()" function + */ + static void +f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED) +{ + char_u *fname; + char_u *opt = (char_u *)""; + char_u buf[NUMBUFLEN]; + + /* Don't open a file in restricted mode. */ + if (check_restricted() || check_secure()) + return; + fname = tv_get_string(&argvars[0]); + if (argvars[1].v_type == VAR_STRING) + opt = tv_get_string_buf(&argvars[1], buf); + ch_logfile(fname, opt); +} + +/* + * "ch_open()" function + */ + static void +f_ch_open(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_CHANNEL; + if (check_restricted() || check_secure()) + return; + rettv->vval.v_channel = channel_open_func(argvars); +} + +/* + * "ch_read()" function + */ + static void +f_ch_read(typval_T *argvars, typval_T *rettv) +{ + common_channel_read(argvars, rettv, FALSE, FALSE); +} + +/* + * "ch_readblob()" function + */ + static void +f_ch_readblob(typval_T *argvars, typval_T *rettv) +{ + common_channel_read(argvars, rettv, TRUE, TRUE); +} + +/* + * "ch_readraw()" function + */ + static void +f_ch_readraw(typval_T *argvars, typval_T *rettv) +{ + common_channel_read(argvars, rettv, TRUE, FALSE); +} + +/* + * "ch_evalexpr()" function + */ + static void +f_ch_evalexpr(typval_T *argvars, typval_T *rettv) +{ + ch_expr_common(argvars, rettv, TRUE); +} + +/* + * "ch_sendexpr()" function + */ + static void +f_ch_sendexpr(typval_T *argvars, typval_T *rettv) +{ + ch_expr_common(argvars, rettv, FALSE); +} + +/* + * "ch_evalraw()" function + */ + static void +f_ch_evalraw(typval_T *argvars, typval_T *rettv) +{ + ch_raw_common(argvars, rettv, TRUE); +} + +/* + * "ch_sendraw()" function + */ + static void +f_ch_sendraw(typval_T *argvars, typval_T *rettv) +{ + ch_raw_common(argvars, rettv, FALSE); +} + +/* + * "ch_setoptions()" function + */ + static void +f_ch_setoptions(typval_T *argvars, typval_T *rettv UNUSED) +{ + channel_T *channel; + jobopt_T opt; + + channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + if (channel == NULL) + return; + clear_job_options(&opt); + if (get_job_options(&argvars[1], &opt, + JO_CB_ALL + JO_TIMEOUT_ALL + JO_MODE_ALL, 0) == OK) + channel_set_options(channel, &opt); + free_job_options(&opt); +} + +/* + * "ch_status()" function + */ + static void +f_ch_status(typval_T *argvars, typval_T *rettv) +{ + channel_T *channel; + jobopt_T opt; + int part = -1; + + /* return an empty string by default */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + + if (argvars[1].v_type != VAR_UNKNOWN) + { + clear_job_options(&opt); + if (get_job_options(&argvars[1], &opt, JO_PART, 0) == OK + && (opt.jo_set & JO_PART)) + part = opt.jo_part; + } + + rettv->vval.v_string = vim_strsave((char_u *)channel_status(channel, part)); +} +#endif + +/* + * "changenr()" function + */ + static void +f_changenr(typval_T *argvars UNUSED, 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 = (int)tv_get_number_chk(&argvars[1], NULL); + + if (utf8) + rettv->vval.v_number = (*utf_ptr2char)(tv_get_string(&argvars[0])); + else + rettv->vval.v_number = (*mb_ptr2char)(tv_get_string(&argvars[0])); + } + else + rettv->vval.v_number = tv_get_string(&argvars[0])[0]; +} + +/* + * "cindent(lnum)" function + */ + static void +f_cindent(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CINDENT + pos_T pos; + linenr_T lnum; + + pos = curwin->w_cursor; + lnum = tv_get_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 +#endif + rettv->vval.v_number = -1; +} + +/* + * "clearmatches()" function + */ + static void +f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + clear_matches(curwin); +#endif +} + +/* + * "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; +} + +#if defined(FEAT_INS_EXPAND) +/* + * "complete()" function + */ + static void +f_complete(typval_T *argvars, typval_T *rettv UNUSED) +{ + 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 = (int)tv_get_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 UNUSED, typval_T *rettv) +{ + int saved = RedrawingDisabled; + + RedrawingDisabled = 0; + ins_compl_check_keys(0, TRUE); + rettv->vval.v_number = compl_interrupted; + RedrawingDisabled = saved; +} +#endif + +/* + * "confirm(message, buttons[, default [, type]])" function + */ + static void +f_confirm(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + 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 = tv_get_string_chk(&argvars[0]); + if (message == NULL) + error = TRUE; + if (argvars[1].v_type != VAR_UNKNOWN) + { + buttons = tv_get_string_buf_chk(&argvars[1], buf); + if (buttons == NULL) + error = TRUE; + if (argvars[2].v_type != VAR_UNKNOWN) + { + def = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + typestr = tv_get_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); +#endif +} + +/* + * "copy()" function + */ + static void +f_copy(typval_T *argvars, typval_T *rettv) +{ + item_copy(&argvars[0], rettv, FALSE, 0); +} + +#ifdef FEAT_FLOAT +/* + * "cos()" function + */ + static void +f_cos(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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 = 0.0; + + 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; +} +#endif + +/* + * "count()" function + */ + static void +f_count(typval_T *argvars, typval_T *rettv) +{ + long n = 0; + int ic = FALSE; + int error = FALSE; + + if (argvars[2].v_type != VAR_UNKNOWN) + ic = (int)tv_get_number_chk(&argvars[2], &error); + + if (argvars[0].v_type == VAR_STRING) + { + char_u *expr = tv_get_string_chk(&argvars[1]); + char_u *p = argvars[0].vval.v_string; + char_u *next; + + if (!error && expr != NULL && *expr != NUL && p != NULL) + { + if (ic) + { + size_t len = STRLEN(expr); + + while (*p != NUL) + { + if (MB_STRNICMP(p, expr, len) == 0) + { + ++n; + p += len; + } + else + MB_PTR_ADV(p); + } + } + else + while ((next = (char_u *)strstr((char *)p, (char *)expr)) + != NULL) + { + ++n; + p = next + STRLEN(expr); + } + } + + } + else 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) + { + if (argvars[3].v_type != VAR_UNKNOWN) + { + idx = (long)tv_get_number_chk(&argvars[3], &error); + if (!error) + { + li = list_find(l, idx); + if (li == NULL) + semsg(_(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) + { + if (argvars[2].v_type != VAR_UNKNOWN) + { + 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 + semsg(_(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 UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_CSCOPE + 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)tv_get_number(&argvars[0]); + dbpath = tv_get_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) + prepend = tv_get_string_buf(&argvars[2], buf); + } + + rettv->vval.v_number = cs_connection(num, dbpath, prepend); +#endif +} + +/* + * "cursor(lnum, col)" function, or + * "cursor(list)" + * + * 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; + int set_curswant = TRUE; + + rettv->vval.v_number = -1; + if (argvars[1].v_type == VAR_UNKNOWN) + { + pos_T pos; + colnr_T curswant = -1; + + if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) + { + emsg(_(e_invarg)); + return; + } + line = pos.lnum; + col = pos.col; + coladd = pos.coladd; + if (curswant >= 0) + { + curwin->w_curswant = curswant - 1; + set_curswant = FALSE; + } + } + else + { + line = tv_get_lnum(argvars); + col = (long)tv_get_number_chk(&argvars[1], NULL); + if (argvars[2].v_type != VAR_UNKNOWN) + coladd = (long)tv_get_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 = set_curswant; + rettv->vval.v_number = 0; +} + +#ifdef WIN3264 +/* + * "debugbreak()" function + */ + static void +f_debugbreak(typval_T *argvars, typval_T *rettv) +{ + int pid; + + rettv->vval.v_number = FAIL; + pid = (int)tv_get_number(&argvars[0]); + if (pid == 0) + emsg(_(e_invarg)); + else + { + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); + + if (hProcess != NULL) + { + DebugBreakProcess(hProcess); + CloseHandle(hProcess); + rettv->vval.v_number = OK; + } + } +} +#endif + +/* + * "deepcopy()" function + */ + static void +f_deepcopy(typval_T *argvars, typval_T *rettv) +{ + int noref = 0; + int copyID; + + if (argvars[1].v_type != VAR_UNKNOWN) + noref = (int)tv_get_number_chk(&argvars[1], NULL); + if (noref < 0 || noref > 1) + emsg(_(e_invarg)); + else + { + copyID = get_copyID(); + item_copy(&argvars[0], rettv, TRUE, noref == 0 ? copyID : 0); + } +} + +/* + * "delete()" function + */ + static void +f_delete(typval_T *argvars, typval_T *rettv) +{ + char_u nbuf[NUMBUFLEN]; + char_u *name; + char_u *flags; + + rettv->vval.v_number = -1; + if (check_restricted() || check_secure()) + return; + + name = tv_get_string(&argvars[0]); + if (name == NULL || *name == NUL) + { + emsg(_(e_invarg)); + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) + flags = tv_get_string_buf(&argvars[1], nbuf); + else + flags = (char_u *)""; + + if (*flags == NUL) + /* delete a file */ + rettv->vval.v_number = mch_remove(name) == 0 ? 0 : -1; + else if (STRCMP(flags, "d") == 0) + /* delete an empty directory */ + rettv->vval.v_number = mch_rmdir(name) == 0 ? 0 : -1; + else if (STRCMP(flags, "rf") == 0) + /* delete a directory recursively */ + rettv->vval.v_number = delete_recursive(name); + else + semsg(_(e_invexpr2), flags); +} + +/* + * "deletebufline()" function + */ + static void +f_deletebufline(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + linenr_T first, last; + linenr_T lnum; + long count; + int is_curbuf; + buf_T *curbuf_save = NULL; + win_T *curwin_save = NULL; + tabpage_T *tp; + win_T *wp; + + buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL) + { + rettv->vval.v_number = 1; /* FAIL */ + return; + } + is_curbuf = buf == curbuf; + + first = tv_get_lnum_buf(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) + last = tv_get_lnum_buf(&argvars[2], buf); + else + last = first; + + if (buf->b_ml.ml_mfp == NULL || first < 1 + || first > buf->b_ml.ml_line_count || last < first) + { + rettv->vval.v_number = 1; /* FAIL */ + return; + } + + if (!is_curbuf) + { + curbuf_save = curbuf; + curwin_save = curwin; + curbuf = buf; + find_win_for_curbuf(); + } + if (last > curbuf->b_ml.ml_line_count) + last = curbuf->b_ml.ml_line_count; + count = last - first + 1; + + // 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 (u_save(first - 1, last + 1) == FAIL) + { + rettv->vval.v_number = 1; /* FAIL */ + return; + } + + for (lnum = first; lnum <= last; ++lnum) + ml_delete(first, TRUE); + + FOR_ALL_TAB_WINDOWS(tp, wp) + if (wp->w_buffer == buf) + { + if (wp->w_cursor.lnum > last) + wp->w_cursor.lnum -= count; + else if (wp->w_cursor.lnum> first) + wp->w_cursor.lnum = first; + if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) + wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count; + } + check_cursor_col(); + deleted_lines_mark(first, count); + + if (!is_curbuf) + { + curbuf = curbuf_save; + curwin = curwin_save; + } +} + +/* + * "did_filetype()" function + */ + static void +f_did_filetype(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + rettv->vval.v_number = did_filetype; +} + +/* + * "diff_filler()" function + */ + static void +f_diff_filler(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_DIFF + rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars)); +#endif +} + +/* + * "diff_hlID()" function + */ + static void +f_diff_hlID(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_DIFF + linenr_T lnum = tv_get_lnum(argvars); + static linenr_T prev_lnum = 0; + static varnumber_T 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 != CHANGEDTICK(curbuf) + || 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 = CHANGEDTICK(curbuf); + fnum = curbuf->b_fnum; + } + + if (hlID == HLF_CHD || hlID == HLF_TXD) + { + col = tv_get_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; +#endif +} + +/* + * "empty({expr})" function + */ + static void +f_empty(typval_T *argvars, typval_T *rettv) +{ + int n = FALSE; + + 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_PARTIAL: + n = FALSE; + break; + case VAR_NUMBER: + n = argvars[0].vval.v_number == 0; + break; + case VAR_FLOAT: +#ifdef FEAT_FLOAT + n = argvars[0].vval.v_float == 0.0; + break; +#endif + 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; + case VAR_SPECIAL: + n = argvars[0].vval.v_number != VVAL_TRUE; + break; + + case VAR_BLOB: + n = argvars[0].vval.v_blob == NULL + || argvars[0].vval.v_blob->bv_ga.ga_len == 0; + break; + + case VAR_JOB: +#ifdef FEAT_JOB_CHANNEL + n = argvars[0].vval.v_job == NULL + || argvars[0].vval.v_job->jv_status != JOB_STARTED; + break; +#endif + case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL + n = argvars[0].vval.v_channel == NULL + || !channel_is_open(argvars[0].vval.v_channel); + break; +#endif + case VAR_UNKNOWN: + internal_error("f_empty(UNKNOWN)"); + n = TRUE; + break; + } + + 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(tv_get_string(&argvars[0]), + tv_get_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, *p; + + s = tv_get_string_chk(&argvars[0]); + if (s != NULL) + s = skipwhite(s); + + p = s; + if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) + { + if (p != NULL && !aborting()) + semsg(_(e_invexpr2), p); + need_clr_eos = FALSE; + 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 UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = vgetc_busy; +} + +/* + * "executable()" function + */ + static void +f_executable(typval_T *argvars, typval_T *rettv) +{ + char_u *name = tv_get_string(&argvars[0]); + + /* Check in $PATH and also check directly if there is a directory name. */ + rettv->vval.v_number = mch_can_exe(name, NULL, TRUE) + || (gettail(name) != name && mch_can_exe(name, NULL, FALSE)); +} + +static garray_T redir_execute_ga; + +/* + * Append "value[value_len]" to the execute() output. + */ + void +execute_redir_str(char_u *value, int value_len) +{ + int len; + + if (value_len == -1) + len = (int)STRLEN(value); /* Append the entire string */ + else + len = value_len; /* Append only "value_len" characters */ + if (ga_grow(&redir_execute_ga, len) == OK) + { + mch_memmove((char *)redir_execute_ga.ga_data + + redir_execute_ga.ga_len, value, len); + redir_execute_ga.ga_len += len; + } +} + +/* + * Get next line from a list. + * Called by do_cmdline() to get the next line. + * Returns allocated string, or NULL for end of function. + */ + + static char_u * +get_list_line( + int c UNUSED, + void *cookie, + int indent UNUSED) +{ + listitem_T **p = (listitem_T **)cookie; + listitem_T *item = *p; + char_u buf[NUMBUFLEN]; + char_u *s; + + if (item == NULL) + return NULL; + s = tv_get_string_buf_chk(&item->li_tv, buf); + *p = item->li_next; + return s == NULL ? NULL : vim_strsave(s); +} + +/* + * "execute()" function + */ + static void +f_execute(typval_T *argvars, typval_T *rettv) +{ + char_u *cmd = NULL; + list_T *list = NULL; + int save_msg_silent = msg_silent; + int save_emsg_silent = emsg_silent; + int save_emsg_noredir = emsg_noredir; + int save_redir_execute = redir_execute; + int save_redir_off = redir_off; + garray_T save_ga; + int save_msg_col = msg_col; + int echo_output = FALSE; + + rettv->vval.v_string = NULL; + rettv->v_type = VAR_STRING; + + if (argvars[0].v_type == VAR_LIST) + { + list = argvars[0].vval.v_list; + if (list == NULL || list->lv_first == NULL) + /* empty list, no commands, empty output */ + return; + ++list->lv_refcount; + } + else + { + cmd = tv_get_string_chk(&argvars[0]); + if (cmd == NULL) + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) + { + char_u buf[NUMBUFLEN]; + char_u *s = tv_get_string_buf_chk(&argvars[1], buf); + + if (s == NULL) + return; + if (*s == NUL) + echo_output = TRUE; + if (STRNCMP(s, "silent", 6) == 0) + ++msg_silent; + if (STRCMP(s, "silent!") == 0) + { + emsg_silent = TRUE; + emsg_noredir = TRUE; + } + } + else + ++msg_silent; + + if (redir_execute) + save_ga = redir_execute_ga; + ga_init2(&redir_execute_ga, (int)sizeof(char), 500); + redir_execute = TRUE; + redir_off = FALSE; + if (!echo_output) + msg_col = 0; // prevent leading spaces + + if (cmd != NULL) + do_cmdline_cmd(cmd); + else + { + listitem_T *item = list->lv_first; + + do_cmdline(NULL, get_list_line, (void *)&item, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); + --list->lv_refcount; + } + + /* Need to append a NUL to the result. */ + if (ga_grow(&redir_execute_ga, 1) == OK) + { + ((char *)redir_execute_ga.ga_data)[redir_execute_ga.ga_len] = NUL; + rettv->vval.v_string = redir_execute_ga.ga_data; + } + else + { + ga_clear(&redir_execute_ga); + rettv->vval.v_string = NULL; + } + msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + emsg_noredir = save_emsg_noredir; + + redir_execute = save_redir_execute; + if (redir_execute) + redir_execute_ga = save_ga; + redir_off = save_redir_off; + + // "silent reg" or "silent echo x" leaves msg_col somewhere in the line. + if (echo_output) + // When not working silently: put it in column zero. A following + // "echon" will overwrite the message, unavoidably. + msg_col = 0; + else + // When working silently: Put it back where it was, since nothing + // should have been written. + msg_col = save_msg_col; +} + +/* + * "exepath()" function + */ + static void +f_exepath(typval_T *argvars, typval_T *rettv) +{ + char_u *p = NULL; + + (void)mch_can_exe(tv_get_string(&argvars[0]), &p, TRUE); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = p; +} + +/* + * "exists()" function + */ + static void +f_exists(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + int n = FALSE; + + p = tv_get_string(&argvars[0]); + if (*p == '$') /* environment variable */ + { + /* first try "normal" environment variables (fast) */ + if (mch_getenv(p + 1) != NULL) + n = TRUE; + else + { + /* try expanding things like $VIM and ${HOME} */ + p = expand_env_save(p); + if (p != NULL && *p != '$') + n = TRUE; + vim_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, FALSE); + } + 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 */ + { + n = var_exists(p); + } + + rettv->vval.v_number = n; +} + +#ifdef FEAT_FLOAT +/* + * "exp()" function + */ + static void +f_exp(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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; +} +#endif + +/* + * "expand()" function + */ + static void +f_expand(typval_T *argvars, typval_T *rettv) +{ + char_u *s; + int len; + char *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 + && tv_get_number_chk(&argvars[2], &error) + && !error) + { + rettv_list_set(rettv, NULL); + } + + s = tv_get_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) + { + if (rettv_list_alloc(rettv) != FAIL && result != NULL) + list_append_string(rettv->vval.v_list, result, -1); + else + vim_free(result); + } + 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 + && tv_get_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 if (rettv_list_alloc(rettv) != FAIL) + { + int i; + + ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); + for (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; + } +} + +/* + * "extend(list, list [, idx])" function + * "extend(dict, dict [, action])" function + */ + static void +f_extend(typval_T *argvars, typval_T *rettv) +{ + char_u *arg_errmsg = (char_u *)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, arg_errmsg, TRUE) + && l2 != NULL) + { + if (argvars[2].v_type != VAR_UNKNOWN) + { + before = (long)tv_get_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) + { + semsg(_(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, arg_errmsg, TRUE) + && d2 != NULL) + { + /* Check the third argument. */ + if (argvars[2].v_type != VAR_UNKNOWN) + { + static char *(av[]) = {"keep", "force", "error"}; + + action = tv_get_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) + { + semsg(_(e_invarg2), action); + return; + } + } + else + action = (char_u *)"force"; + + dict_extend(d1, d2, action); + + copy_tv(&argvars[0], rettv); + } + } + else + semsg(_(e_listdictarg), "extend()"); +} + +/* + * "feedkeys()" function + */ + static void +f_feedkeys(typval_T *argvars, typval_T *rettv UNUSED) +{ + int remap = TRUE; + int insert = FALSE; + char_u *keys, *flags; + char_u nbuf[NUMBUFLEN]; + int typed = FALSE; + int execute = FALSE; + int dangerous = FALSE; + int lowlevel = 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 = tv_get_string(&argvars[0]); + + if (argvars[1].v_type != VAR_UNKNOWN) + { + flags = tv_get_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; + case 'i': insert = TRUE; break; + case 'x': execute = TRUE; break; + case '!': dangerous = TRUE; break; + case 'L': lowlevel = TRUE; break; + } + } + } + + if (*keys != NUL || execute) + { + /* 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) + { + if (lowlevel) + { +#ifdef USE_INPUT_BUF + add_to_input_buf(keys, (int)STRLEN(keys)); +#else + emsg(_("E980: lowlevel input not supported")); +#endif + } + else + { + ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), + insert ? 0 : typebuf.tb_len, !typed, FALSE); + if (vgetc_busy +#ifdef FEAT_TIMERS + || timer_busy +#endif + ) + typebuf_was_filled = TRUE; + } + vim_free(keys_esc); + + if (execute) + { + int save_msg_scroll = msg_scroll; + + /* Avoid a 1 second delay when the keys start Insert mode. */ + msg_scroll = FALSE; + + if (!dangerous) + ++ex_normal_busy; + exec_normal(TRUE, FALSE, TRUE); + if (!dangerous) + --ex_normal_busy; + + msg_scroll |= save_msg_scroll; + } + } + } +} + +/* + * "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 = tv_get_string(&argvars[0]); + if (*p && !mch_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) +{ + rettv->vval.v_number = filewritable(tv_get_string(&argvars[0])); +} + + static void +findfilendir( + typval_T *argvars UNUSED, + typval_T *rettv, + int find_what UNUSED) +{ +#ifdef FEAT_SEARCHPATH + 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; +#endif + + rettv->vval.v_string = NULL; + rettv->v_type = VAR_STRING; + +#ifdef FEAT_SEARCHPATH + fname = tv_get_string(&argvars[0]); + + if (argvars[1].v_type != VAR_UNKNOWN) + { + p = tv_get_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 = (int)tv_get_number_chk(&argvars[2], &error); + } + } + + if (count < 0 && rettv_list_alloc(rettv) == FAIL) + error = TRUE; + + if (*fname != NUL && !error) + { + do + { + if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) + vim_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; +#endif +} + +/* + * "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); +} + +#ifdef FEAT_FLOAT +/* + * "float2nr({float})" function + */ + static void +f_float2nr(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + if (get_float_arg(argvars, &f) == OK) + { + if (f <= -VARNUM_MAX + DBL_EPSILON) + rettv->vval.v_number = -VARNUM_MAX; + else if (f >= VARNUM_MAX - DBL_EPSILON) + rettv->vval.v_number = VARNUM_MAX; + else + rettv->vval.v_number = (varnumber_T)f; + } +} + +/* + * "floor({float})" function + */ + static void +f_floor(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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 = 0.0, fy = 0.0; + + 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; +} +#endif + +/* + * "fnameescape({string})" function + */ + static void +f_fnameescape(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_string = vim_strsave_fnameescape( + tv_get_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 = tv_get_string_chk(&argvars[0]); + mods = tv_get_string_buf_chk(&argvars[1], buf); + if (fname == NULL || mods == NULL) + fname = NULL; + else + { + len = (int)STRLEN(fname); + (void)modify_fname(mods, FALSE, &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); + vim_free(fbuf); +} + +/* + * "foldclosed()" function + */ + static void +foldclosed_both( + typval_T *argvars UNUSED, + typval_T *rettv, + int end UNUSED) +{ +#ifdef FEAT_FOLDING + linenr_T lnum; + linenr_T first, last; + + lnum = tv_get_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; + } + } +#endif + 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 UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_FOLDING + linenr_T lnum; + + lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + rettv->vval.v_number = foldLevel(lnum); +#endif +} + +/* + * "foldtext()" function + */ + static void +f_foldtext(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_FOLDING + linenr_T foldstart; + linenr_T foldend; + char_u *dashes; + linenr_T lnum; + char_u *s; + char_u *r; + int len; + char *txt; + long count; +#endif + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_FOLDING + foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART); + foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND); + dashes = get_vim_var_str(VV_FOLDDASHES); + if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count + && dashes != NULL) + { + /* Find first non-empty line in the fold. */ + for (lnum = foldstart; lnum < foldend; ++lnum) + if (!linewhite(lnum)) + break; + + /* 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)get_vim_var_nr(VV_FOLDEND)) + { + s = skipwhite(ml_get(lnum + 1)); + if (*s == '*') + s = skipwhite(s + 1); + } + } + count = (long)(foldend - foldstart + 1); + txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count); + r = alloc((unsigned)(STRLEN(txt) + + STRLEN(dashes) /* for %s */ + + 20 /* for %3ld */ + + STRLEN(s))); /* concatenated */ + if (r != NULL) + { + sprintf((char *)r, txt, dashes, count); + len = (int)STRLEN(r); + STRCAT(r, s); + /* remove 'foldmarker' and 'commentstring' */ + foldtext_cleanup(r + len); + rettv->vval.v_string = r; + } + } +#endif +} + +/* + * "foldtextresult(lnum)" function + */ + static void +f_foldtextresult(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_FOLDING + linenr_T lnum; + char_u *text; + char_u buf[FOLD_TEXT_LEN]; + foldinfo_T foldinfo; + int fold_count; + static int entered = FALSE; +#endif + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_FOLDING + if (entered) + return; /* reject recursive use */ + entered = TRUE; + + lnum = tv_get_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; + } + + entered = FALSE; +#endif +} + +/* + * "foreground()" function + */ + static void +f_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_GUI + if (gui.in_use) + gui_mch_set_foreground(); +#else +# ifdef WIN32 + win32_set_foreground(); +# endif +#endif +} + + static void +common_function(typval_T *argvars, typval_T *rettv, int is_funcref) +{ + char_u *s; + char_u *name; + int use_string = FALSE; + partial_T *arg_pt = NULL; + char_u *trans_name = NULL; + + if (argvars[0].v_type == VAR_FUNC) + { + /* function(MyFunc, [arg], dict) */ + s = argvars[0].vval.v_string; + } + else if (argvars[0].v_type == VAR_PARTIAL + && argvars[0].vval.v_partial != NULL) + { + /* function(dict.MyFunc, [arg]) */ + arg_pt = argvars[0].vval.v_partial; + s = partial_name(arg_pt); + } + else + { + /* function('MyFunc', [arg], dict) */ + s = tv_get_string(&argvars[0]); + use_string = TRUE; + } + + if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) + { + name = s; + trans_name = trans_function_name(&name, FALSE, + TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL); + if (*name != NUL) + s = NULL; + } + + if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s)) + || (is_funcref && trans_name == NULL)) + semsg(_(e_invarg2), use_string ? tv_get_string(&argvars[0]) : s); + /* Don't check an autoload name for existence here. */ + else if (trans_name != NULL && (is_funcref + ? find_func(trans_name) == NULL + : !translated_function_exists(trans_name))) + semsg(_("E700: Unknown function: %s"), s); + else + { + int dict_idx = 0; + int arg_idx = 0; + list_T *list = NULL; + + 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, "%ld_", (long)current_sctx.sc_sid); + name = alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1)); + if (name != NULL) + { + STRCPY(name, sid_buf); + STRCAT(name, s + off); + } + } + else + name = vim_strsave(s); + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[2].v_type != VAR_UNKNOWN) + { + /* function(name, [args], dict) */ + arg_idx = 1; + dict_idx = 2; + } + else if (argvars[1].v_type == VAR_DICT) + /* function(name, dict) */ + dict_idx = 1; + else + /* function(name, [args]) */ + arg_idx = 1; + if (dict_idx > 0) + { + if (argvars[dict_idx].v_type != VAR_DICT) + { + emsg(_("E922: expected a dict")); + vim_free(name); + goto theend; + } + if (argvars[dict_idx].vval.v_dict == NULL) + dict_idx = 0; + } + if (arg_idx > 0) + { + if (argvars[arg_idx].v_type != VAR_LIST) + { + emsg(_("E923: Second argument of function() must be a list or a dict")); + vim_free(name); + goto theend; + } + list = argvars[arg_idx].vval.v_list; + if (list == NULL || list->lv_len == 0) + arg_idx = 0; + } + } + if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) + { + partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T)); + + /* result is a VAR_PARTIAL */ + if (pt == NULL) + vim_free(name); + else + { + if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) + { + listitem_T *li; + int i = 0; + int arg_len = 0; + int lv_len = 0; + + if (arg_pt != NULL) + arg_len = arg_pt->pt_argc; + if (list != NULL) + lv_len = list->lv_len; + pt->pt_argc = arg_len + lv_len; + pt->pt_argv = (typval_T *)alloc( + sizeof(typval_T) * pt->pt_argc); + if (pt->pt_argv == NULL) + { + vim_free(pt); + vim_free(name); + goto theend; + } + for (i = 0; i < arg_len; i++) + copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + if (lv_len > 0) + for (li = list->lv_first; li != NULL; + li = li->li_next) + copy_tv(&li->li_tv, &pt->pt_argv[i++]); + } + + /* For "function(dict.func, [], dict)" and "func" is a partial + * use "dict". That is backwards compatible. */ + if (dict_idx > 0) + { + /* The dict is bound explicitly, pt_auto is FALSE. */ + pt->pt_dict = argvars[dict_idx].vval.v_dict; + ++pt->pt_dict->dv_refcount; + } + else if (arg_pt != NULL) + { + /* If the dict was bound automatically the result is also + * bound automatically. */ + pt->pt_dict = arg_pt->pt_dict; + pt->pt_auto = arg_pt->pt_auto; + if (pt->pt_dict != NULL) + ++pt->pt_dict->dv_refcount; + } + + pt->pt_refcount = 1; + if (arg_pt != NULL && arg_pt->pt_func != NULL) + { + pt->pt_func = arg_pt->pt_func; + func_ptr_ref(pt->pt_func); + vim_free(name); + } + else if (is_funcref) + { + pt->pt_func = find_func(trans_name); + func_ptr_ref(pt->pt_func); + vim_free(name); + } + else + { + pt->pt_name = name; + func_ref(name); + } + } + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + } + else + { + /* result is a VAR_FUNC */ + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = name; + func_ref(name); + } + } +theend: + vim_free(trans_name); +} + +/* + * "funcref()" function + */ + static void +f_funcref(typval_T *argvars, typval_T *rettv) +{ + common_function(argvars, rettv, TRUE); +} + +/* + * "function()" function + */ + static void +f_function(typval_T *argvars, typval_T *rettv) +{ + common_function(argvars, rettv, FALSE); +} + +/* + * "garbagecollect()" function + */ + static void +f_garbagecollect(typval_T *argvars, typval_T *rettv UNUSED) +{ + /* 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 && tv_get_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_BLOB) + { + int error = FALSE; + int idx = tv_get_number_chk(&argvars[1], &error); + + if (!error) + { + rettv->v_type = VAR_NUMBER; + if (idx < 0) + idx = blob_len(argvars[0].vval.v_blob) + idx; + if (idx < 0 || idx >= blob_len(argvars[0].vval.v_blob)) + rettv->vval.v_number = -1; + else + { + rettv->vval.v_number = blob_get(argvars[0].vval.v_blob, idx); + tv = rettv; + } + } + } + else if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) != NULL) + { + int error = FALSE; + + li = list_find(l, (long)tv_get_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, tv_get_string(&argvars[1]), -1); + if (di != NULL) + tv = &di->di_tv; + } + } + else if (argvars[0].v_type == VAR_PARTIAL || argvars[0].v_type == VAR_FUNC) + { + partial_T *pt; + partial_T fref_pt; + + if (argvars[0].v_type == VAR_PARTIAL) + pt = argvars[0].vval.v_partial; + else + { + vim_memset(&fref_pt, 0, sizeof(fref_pt)); + fref_pt.pt_name = argvars[0].vval.v_string; + pt = &fref_pt; + } + + if (pt != NULL) + { + char_u *what = tv_get_string(&argvars[1]); + char_u *n; + + if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) + { + rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); + n = partial_name(pt); + if (n == NULL) + rettv->vval.v_string = NULL; + else + { + rettv->vval.v_string = vim_strsave(n); + if (rettv->v_type == VAR_FUNC) + func_ref(rettv->vval.v_string); + } + } + else if (STRCMP(what, "dict") == 0) + rettv_dict_set(rettv, pt->pt_dict); + else if (STRCMP(what, "args") == 0) + { + rettv->v_type = VAR_LIST; + if (rettv_list_alloc(rettv) == OK) + { + int i; + + for (i = 0; i < pt->pt_argc; ++i) + list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); + } + } + else + semsg(_(e_invarg2), what); + return; + } + } + else + semsg(_(e_listdictblobarg), "get()"); + + if (tv == NULL) + { + if (argvars[2].v_type != VAR_UNKNOWN) + copy_tv(&argvars[2], rettv); + } + else + copy_tv(tv, rettv); +} + +/* + * Returns buffer options, variables and other attributes in a dictionary. + */ + static dict_T * +get_buffer_info(buf_T *buf) +{ + dict_T *dict; + tabpage_T *tp; + win_T *wp; + list_T *windows; + + dict = dict_alloc(); + if (dict == NULL) + return NULL; + + dict_add_number(dict, "bufnr", buf->b_fnum); + dict_add_string(dict, "name", buf->b_ffname); + dict_add_number(dict, "lnum", buf == curbuf ? curwin->w_cursor.lnum + : buflist_findlnum(buf)); + dict_add_number(dict, "loaded", buf->b_ml.ml_mfp != NULL); + dict_add_number(dict, "listed", buf->b_p_bl); + dict_add_number(dict, "changed", bufIsChanged(buf)); + dict_add_number(dict, "changedtick", CHANGEDTICK(buf)); + dict_add_number(dict, "hidden", + buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); + + /* Get a reference to buffer variables */ + dict_add_dict(dict, "variables", buf->b_vars); + + /* List of windows displaying this buffer */ + windows = list_alloc(); + if (windows != NULL) + { + FOR_ALL_TAB_WINDOWS(tp, wp) + if (wp->w_buffer == buf) + list_append_number(windows, (varnumber_T)wp->w_id); + dict_add_list(dict, "windows", windows); + } + +#ifdef FEAT_SIGNS + if (buf->b_signlist != NULL) + { + /* List of signs placed in this buffer */ + list_T *signs = list_alloc(); + if (signs != NULL) + { + get_buffer_signs(buf, signs); + dict_add_list(dict, "signs", signs); + } + } +#endif + + return dict; +} + +/* + * "getbufinfo()" function + */ + static void +f_getbufinfo(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = NULL; + buf_T *argbuf = NULL; + dict_T *d; + int filtered = FALSE; + int sel_buflisted = FALSE; + int sel_bufloaded = FALSE; + int sel_bufmodified = FALSE; + + if (rettv_list_alloc(rettv) != OK) + return; + + /* List of all the buffers or selected buffers */ + if (argvars[0].v_type == VAR_DICT) + { + dict_T *sel_d = argvars[0].vval.v_dict; + + if (sel_d != NULL) + { + dictitem_T *di; + + filtered = TRUE; + + di = dict_find(sel_d, (char_u *)"buflisted", -1); + if (di != NULL && tv_get_number(&di->di_tv)) + sel_buflisted = TRUE; + + di = dict_find(sel_d, (char_u *)"bufloaded", -1); + if (di != NULL && tv_get_number(&di->di_tv)) + sel_bufloaded = TRUE; + + di = dict_find(sel_d, (char_u *)"bufmodified", -1); + if (di != NULL && tv_get_number(&di->di_tv)) + sel_bufmodified = TRUE; + } + } + else if (argvars[0].v_type != VAR_UNKNOWN) + { + /* Information about one buffer. Argument specifies the buffer */ + (void)tv_get_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + argbuf = tv_get_buf(&argvars[0], FALSE); + --emsg_off; + if (argbuf == NULL) + return; + } + + /* Return information about all the buffers or a specified buffer */ + FOR_ALL_BUFFERS(buf) + { + if (argbuf != NULL && argbuf != buf) + continue; + if (filtered && ((sel_bufloaded && buf->b_ml.ml_mfp == NULL) + || (sel_buflisted && !buf->b_p_bl) + || (sel_bufmodified && !buf->b_changed))) + continue; + + d = get_buffer_info(buf); + if (d != NULL) + list_append_dict(rettv->vval.v_list, d); + if (argbuf != NULL) + return; + } +} + +/* + * 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) == FAIL) + return; + + 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) + if (list_append_string(rettv->vval.v_list, + ml_get_buf(buf, start++, FALSE), -1) == FAIL) + break; + } +} + +/* + * "getbufline()" function + */ + static void +f_getbufline(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + linenr_T end; + buf_T *buf; + + (void)tv_get_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = tv_get_buf(&argvars[0], FALSE); + --emsg_off; + + lnum = tv_get_lnum_buf(&argvars[1], buf); + if (argvars[2].v_type == VAR_UNKNOWN) + end = lnum; + else + end = tv_get_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)tv_get_number(&argvars[0]); /* issue errmsg if type error */ + varname = tv_get_string_chk(&argvars[1]); + ++emsg_off; + buf = tv_get_buf(&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 == '&') + { + if (varname[1] == NUL) + { + /* get all buffer-local options in a dict */ + dict_T *opts = get_winbuf_options(TRUE); + + if (opts != NULL) + { + rettv_dict_set(rettv, opts); + done = TRUE; + } + } + else if (get_option_tv(&varname, rettv, TRUE) == OK) + /* buffer-local-option */ + 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; +} + +/* + * "getchangelist()" function + */ + static void +f_getchangelist(typval_T *argvars, typval_T *rettv) +{ +#ifdef FEAT_JUMPLIST + buf_T *buf; + int i; + list_T *l; + dict_T *d; +#endif + + if (rettv_list_alloc(rettv) != OK) + return; + +#ifdef FEAT_JUMPLIST + (void)tv_get_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = tv_get_buf(&argvars[0], FALSE); + --emsg_off; + if (buf == NULL) + return; + + l = list_alloc(); + if (l == NULL) + return; + + if (list_append_list(rettv->vval.v_list, l) == FAIL) + return; + /* + * The current window change list index tracks only the position in the + * current buffer change list. For other buffers, use the change list + * length as the current index. + */ + list_append_number(rettv->vval.v_list, + (varnumber_T)((buf == curwin->w_buffer) + ? curwin->w_changelistidx : buf->b_changelistlen)); + + for (i = 0; i < buf->b_changelistlen; ++i) + { + if (buf->b_changelist[i].lnum == 0) + continue; + if ((d = dict_alloc()) == NULL) + return; + if (list_append_dict(l, d) == FAIL) + return; + dict_add_number(d, "lnum", (long)buf->b_changelist[i].lnum); + dict_add_number(d, "col", (long)buf->b_changelist[i].col); + dict_add_number(d, "coladd", (long)buf->b_changelist[i].coladd); + } +#endif +} +/* + * "getchar()" function + */ + static void +f_getchar(typval_T *argvars, typval_T *rettv) +{ + varnumber_T n; + int error = FALSE; + +#ifdef MESSAGE_QUEUE + // vpeekc() used to check for messages, but that caused problems, invoking + // a callback where it was not expected. Some plugins use getchar(1) in a + // loop to await a message, therefore make sure we check for messages here. + parse_queued_messages(); +#endif + + /* 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 = plain_vgetc(); + else if (tv_get_number_chk(&argvars[0], &error) == 1) + /* getchar(1): only check if char avail */ + n = vpeekc_any(); + else if (error || vpeekc_any() == NUL) + /* illegal argument or getchar(0) and no char avail: return zero */ + n = 0; + else + /* getchar(0) and char avail: return char */ + n = plain_vgetc(); + + if (n == K_IGNORE) + continue; + break; + } + --no_mapping; + --allow_keys; + + set_vim_var_nr(VV_MOUSE_WIN, 0); + set_vim_var_nr(VV_MOUSE_WINID, 0); + set_vim_var_nr(VV_MOUSE_LNUM, 0); + set_vim_var_nr(VV_MOUSE_COL, 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); + +#ifdef FEAT_MOUSE + 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); + if (win == NULL) + return; + (void)mouse_comp_pos(win, &row, &col, &lnum); + for (wp = firstwin; wp != win; wp = wp->w_next) + ++winnr; + set_vim_var_nr(VV_MOUSE_WIN, winnr); + set_vim_var_nr(VV_MOUSE_WINID, win->w_id); + set_vim_var_nr(VV_MOUSE_LNUM, lnum); + set_vim_var_nr(VV_MOUSE_COL, col + 1); + } + } +#endif + } +} + +/* + * "getcharmod()" function + */ + static void +f_getcharmod(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = mod_mask; +} + +/* + * "getcharsearch()" function + */ + static void +f_getcharsearch(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_dict_alloc(rettv) != FAIL) + { + dict_T *dict = rettv->vval.v_dict; + + dict_add_string(dict, "char", last_csearch()); + dict_add_number(dict, "forward", last_csearch_forward()); + dict_add_number(dict, "until", last_csearch_until()); + } +} + +/* + * "getcmdline()" function + */ + static void +f_getcmdline(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = get_cmdline_str(); +} + +/* + * "getcmdpos()" function + */ + static void +f_getcmdpos(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = get_cmdline_pos() + 1; +} + +/* + * "getcmdtype()" function + */ + static void +f_getcmdtype(typval_T *argvars UNUSED, 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; + } +} + +/* + * "getcmdwintype()" function + */ + static void +f_getcmdwintype(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_CMDWIN + rettv->vval.v_string = alloc(2); + if (rettv->vval.v_string != NULL) + { + rettv->vval.v_string[0] = cmdwin_type; + rettv->vval.v_string[1] = NUL; + } +#endif +} + +#if defined(FEAT_CMDL_COMPL) +/* + * "getcompletion()" function + */ + static void +f_getcompletion(typval_T *argvars, typval_T *rettv) +{ + char_u *pat; + expand_T xpc; + int filtered = FALSE; + int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH + | WILD_NO_BEEP; + + if (argvars[2].v_type != VAR_UNKNOWN) + filtered = tv_get_number_chk(&argvars[2], NULL); + + if (p_wic) + options |= WILD_ICASE; + + /* For filtered results, 'wildignore' is used */ + if (!filtered) + options |= WILD_KEEP_ALL; + + ExpandInit(&xpc); + xpc.xp_pattern = tv_get_string(&argvars[0]); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); + xpc.xp_context = cmdcomplete_str_to_type(tv_get_string(&argvars[1])); + if (xpc.xp_context == EXPAND_NOTHING) + { + if (argvars[1].v_type == VAR_STRING) + semsg(_(e_invarg2), argvars[1].vval.v_string); + else + emsg(_(e_invarg)); + return; + } + +# if defined(FEAT_MENU) + if (xpc.xp_context == EXPAND_MENUS) + { + set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, FALSE); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); + } +# endif +#ifdef FEAT_CSCOPE + if (xpc.xp_context == EXPAND_CSCOPE) + { + set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); + } +#endif +#ifdef FEAT_SIGNS + if (xpc.xp_context == EXPAND_SIGN) + { + set_context_in_sign_cmd(&xpc, xpc.xp_pattern); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); + } +#endif + + pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); + if ((rettv_list_alloc(rettv) != FAIL) && (pat != NULL)) + { + int i; + + ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); + + for (i = 0; i < xpc.xp_numfiles; i++) + list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + } + vim_free(pat); + ExpandCleanup(&xpc); +} +#endif + +/* + * "getcwd()" function + */ + static void +f_getcwd(typval_T *argvars, typval_T *rettv) +{ + win_T *wp = NULL; + char_u *cwd; + int global = FALSE; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number == -1) + global = TRUE; + else + wp = find_tabwin(&argvars[0], &argvars[1]); + + if (wp != NULL && wp->w_localdir != NULL) + rettv->vval.v_string = vim_strsave(wp->w_localdir); + else if (wp != NULL || global) + { + if (globaldir != NULL) + rettv->vval.v_string = vim_strsave(globaldir); + else + { + cwd = alloc(MAXPATHL); + if (cwd != NULL) + { + if (mch_dirname(cwd, MAXPATHL) != FAIL) + rettv->vval.v_string = vim_strsave(cwd); + vim_free(cwd); + } + } + } +#ifdef BACKSLASH_IN_FILENAME + if (rettv->vval.v_string != NULL) + slash_adjust(rettv->vval.v_string); +#endif +} + +/* + * "getfontname()" function + */ + static void +f_getfontname(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_GUI + if (gui.in_use) + { + GuiFont font; + char_u *name = NULL; + + if (argvars[0].v_type == VAR_UNKNOWN) + { + /* Get the "Normal" font. Either the name saved by + * hl_set_font_name() or from the font ID. */ + font = gui.norm_font; + name = hl_get_font_name(); + } + else + { + name = tv_get_string(&argvars[0]); + if (STRCMP(name, "*") == 0) /* don't use font dialog */ + return; + font = gui_mch_get_font(name, FALSE); + if (font == NOFONT) + return; /* Invalid font name, return empty string. */ + } + rettv->vval.v_string = gui_mch_get_fontname(font, name); + if (argvars[0].v_type != VAR_UNKNOWN) + gui_mch_free_font(font); + } +#endif +} + +/* + * "getfperm({fname})" function + */ + static void +f_getfperm(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + stat_T st; + char_u *perm = NULL; + char_u flags[] = "rwx"; + int i; + + fname = tv_get_string(&argvars[0]); + + rettv->v_type = VAR_STRING; + if (mch_stat((char *)fname, &st) >= 0) + { + perm = vim_strsave((char_u *)"---------"); + if (perm != NULL) + { + for (i = 0; i < 9; i++) + { + if (st.st_mode & (1 << (8 - i))) + perm[i] = flags[i % 3]; + } + } + } + rettv->vval.v_string = perm; +} + +/* + * "getfsize({fname})" function + */ + static void +f_getfsize(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + stat_T st; + + fname = tv_get_string(&argvars[0]); + + rettv->v_type = VAR_NUMBER; + + if (mch_stat((char *)fname, &st) >= 0) + { + if (mch_isdir(fname)) + rettv->vval.v_number = 0; + else + { + rettv->vval.v_number = (varnumber_T)st.st_size; + + /* non-perfect check for overflow */ + if ((off_T)rettv->vval.v_number != (off_T)st.st_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_u *fname; + stat_T st; + + fname = tv_get_string(&argvars[0]); + + if (mch_stat((char *)fname, &st) >= 0) + rettv->vval.v_number = (varnumber_T)st.st_mtime; + else + rettv->vval.v_number = -1; +} + +/* + * "getftype({fname})" function + */ + static void +f_getftype(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + stat_T st; + char_u *type = NULL; + char *t; + + fname = tv_get_string(&argvars[0]); + + rettv->v_type = VAR_STRING; + if (mch_lstat((char *)fname, &st) >= 0) + { + if (S_ISREG(st.st_mode)) + t = "file"; + else if (S_ISDIR(st.st_mode)) + t = "dir"; + else if (S_ISLNK(st.st_mode)) + t = "link"; + else if (S_ISBLK(st.st_mode)) + t = "bdev"; + else if (S_ISCHR(st.st_mode)) + t = "cdev"; + else if (S_ISFIFO(st.st_mode)) + t = "fifo"; + else if (S_ISSOCK(st.st_mode)) + t = "socket"; + else + t = "other"; + type = vim_strsave((char_u *)t); + } + rettv->vval.v_string = type; +} + +/* + * "getjumplist()" function + */ + static void +f_getjumplist(typval_T *argvars, typval_T *rettv) +{ +#ifdef FEAT_JUMPLIST + win_T *wp; + int i; + list_T *l; + dict_T *d; +#endif + + if (rettv_list_alloc(rettv) != OK) + return; + +#ifdef FEAT_JUMPLIST + wp = find_tabwin(&argvars[0], &argvars[1]); + if (wp == NULL) + return; + + l = list_alloc(); + if (l == NULL) + return; + + if (list_append_list(rettv->vval.v_list, l) == FAIL) + return; + list_append_number(rettv->vval.v_list, (varnumber_T)wp->w_jumplistidx); + + cleanup_jumplist(wp, TRUE); + + for (i = 0; i < wp->w_jumplistlen; ++i) + { + if (wp->w_jumplist[i].fmark.mark.lnum == 0) + continue; + if ((d = dict_alloc()) == NULL) + return; + if (list_append_dict(l, d) == FAIL) + return; + dict_add_number(d, "lnum", (long)wp->w_jumplist[i].fmark.mark.lnum); + dict_add_number(d, "col", (long)wp->w_jumplist[i].fmark.mark.col); + dict_add_number(d, "coladd", (long)wp->w_jumplist[i].fmark.mark.coladd); + dict_add_number(d, "bufnr", (long)wp->w_jumplist[i].fmark.fnum); + if (wp->w_jumplist[i].fname != NULL) + dict_add_string(d, "filename", wp->w_jumplist[i].fname); + } +#endif +} + +/* + * "getline(lnum, [end])" function + */ + static void +f_getline(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + linenr_T end; + int retlist; + + lnum = tv_get_lnum(argvars); + if (argvars[1].v_type == VAR_UNKNOWN) + { + end = 0; + retlist = FALSE; + } + else + { + end = tv_get_lnum(&argvars[1]); + retlist = TRUE; + } + + get_buffer_lines(curbuf, lnum, end, retlist, rettv); +} + +#ifdef FEAT_QUICKFIX + static void +get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) +{ + if (what_arg->v_type == VAR_UNKNOWN) + { + if (rettv_list_alloc(rettv) == OK) + if (is_qf || wp != NULL) + (void)get_errorlist(NULL, wp, -1, rettv->vval.v_list); + } + else + { + if (rettv_dict_alloc(rettv) == OK) + if (is_qf || (wp != NULL)) + { + if (what_arg->v_type == VAR_DICT) + { + dict_T *d = what_arg->vval.v_dict; + + if (d != NULL) + qf_get_properties(wp, d, rettv->vval.v_dict); + } + else + emsg(_(e_dictreq)); + } + } +} +#endif + +/* + * "getloclist()" function + */ + static void +f_getloclist(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_QUICKFIX + win_T *wp; + + wp = find_win_by_nr_or_id(&argvars[0]); + get_qf_loc_list(FALSE, wp, &argvars[1], rettv); +#endif +} + +/* + * "getmatches()" function + */ + static void +f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + dict_T *dict; + matchitem_T *cur = curwin->w_match_head; + int i; + + if (rettv_list_alloc(rettv) == OK) + { + while (cur != NULL) + { + dict = dict_alloc(); + if (dict == NULL) + return; + if (cur->match.regprog == NULL) + { + /* match added with matchaddpos() */ + for (i = 0; i < MAXPOSMATCH; ++i) + { + llpos_T *llpos; + char buf[6]; + list_T *l; + + llpos = &cur->pos.pos[i]; + if (llpos->lnum == 0) + break; + l = list_alloc(); + if (l == NULL) + break; + list_append_number(l, (varnumber_T)llpos->lnum); + if (llpos->col > 0) + { + list_append_number(l, (varnumber_T)llpos->col); + list_append_number(l, (varnumber_T)llpos->len); + } + sprintf(buf, "pos%d", i + 1); + dict_add_list(dict, buf, l); + } + } + else + { + dict_add_string(dict, "pattern", cur->pattern); + } + dict_add_string(dict, "group", syn_id2name(cur->hlg_id)); + dict_add_number(dict, "priority", (long)cur->priority); + dict_add_number(dict, "id", (long)cur->id); +# if defined(FEAT_CONCEAL) + if (cur->conceal_char) + { + char_u buf[MB_MAXBYTES + 1]; + + buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; + dict_add_string(dict, "conceal", (char_u *)&buf); + } +# endif + list_append_dict(rettv->vval.v_list, dict); + cur = cur->next; + } + } +#endif +} + +/* + * "getpid()" function + */ + static void +f_getpid(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = mch_get_pid(); +} + + static void +getpos_both( + typval_T *argvars, + typval_T *rettv, + int getcurpos) +{ + pos_T *fp; + list_T *l; + int fnum = -1; + + if (rettv_list_alloc(rettv) == OK) + { + l = rettv->vval.v_list; + if (getcurpos) + fp = &curwin->w_cursor; + else + fp = var2fpos(&argvars[0], TRUE, &fnum); + if (fnum != -1) + list_append_number(l, (varnumber_T)fnum); + else + list_append_number(l, (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); + if (getcurpos) + { + update_curswant(); + list_append_number(l, curwin->w_curswant == MAXCOL ? + (varnumber_T)MAXCOL : (varnumber_T)curwin->w_curswant + 1); + } + } + else + rettv->vval.v_number = FALSE; +} + +/* + * "getcurpos()" function + */ + static void +f_getcurpos(typval_T *argvars, typval_T *rettv) +{ + getpos_both(argvars, rettv, TRUE); +} + +/* + * "getpos(string)" function + */ + static void +f_getpos(typval_T *argvars, typval_T *rettv) +{ + getpos_both(argvars, rettv, FALSE); +} + +/* + * "getqflist()" function + */ + static void +f_getqflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_QUICKFIX + get_qf_loc_list(TRUE, NULL, &argvars[0], rettv); +#endif +} + +/* + * "getreg()" function + */ + static void +f_getreg(typval_T *argvars, typval_T *rettv) +{ + char_u *strregname; + int regname; + int arg2 = FALSE; + int return_list = FALSE; + int error = FALSE; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + strregname = tv_get_string_chk(&argvars[0]); + error = strregname == NULL; + if (argvars[1].v_type != VAR_UNKNOWN) + { + arg2 = (int)tv_get_number_chk(&argvars[1], &error); + if (!error && argvars[2].v_type != VAR_UNKNOWN) + return_list = (int)tv_get_number_chk(&argvars[2], &error); + } + } + else + strregname = get_vim_var_str(VV_REG); + + if (error) + return; + + regname = (strregname == NULL ? '"' : *strregname); + if (regname == 0) + regname = '"'; + + if (return_list) + { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = (list_T *)get_reg_contents(regname, + (arg2 ? GREG_EXPR_SRC : 0) | GREG_LIST); + if (rettv->vval.v_list == NULL) + (void)rettv_list_alloc(rettv); + else + ++rettv->vval.v_list->lv_refcount; + } + else + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = get_reg_contents(regname, + arg2 ? GREG_EXPR_SRC : 0); + } +} + +/* + * "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 = tv_get_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 = get_vim_var_str(VV_REG); + + 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, "%ld", reglen + 1); + break; + } + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(buf); +} + +/* + * Returns information (variables, options, etc.) about a tab page + * as a dictionary. + */ + static dict_T * +get_tabpage_info(tabpage_T *tp, int tp_idx) +{ + win_T *wp; + dict_T *dict; + list_T *l; + + dict = dict_alloc(); + if (dict == NULL) + return NULL; + + dict_add_number(dict, "tabnr", tp_idx); + + l = list_alloc(); + if (l != NULL) + { + for (wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + wp; wp = wp->w_next) + list_append_number(l, (varnumber_T)wp->w_id); + dict_add_list(dict, "windows", l); + } + + /* Make a reference to tabpage variables */ + dict_add_dict(dict, "variables", tp->tp_vars); + + return dict; +} + +/* + * "gettabinfo()" function + */ + static void +f_gettabinfo(typval_T *argvars, typval_T *rettv) +{ + tabpage_T *tp, *tparg = NULL; + dict_T *d; + int tpnr = 0; + + if (rettv_list_alloc(rettv) != OK) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + /* Information about one tab page */ + tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + if (tparg == NULL) + return; + } + + /* Get information about a specific tab page or all tab pages */ + FOR_ALL_TABPAGES(tp) + { + tpnr++; + if (tparg != NULL && tp != tparg) + continue; + d = get_tabpage_info(tp, tpnr); + if (d != NULL) + list_append_dict(rettv->vval.v_list, d); + if (tparg != NULL) + return; + } +} + +/* + * "gettabvar()" function + */ + static void +f_gettabvar(typval_T *argvars, typval_T *rettv) +{ + win_T *oldcurwin; + tabpage_T *tp, *oldtabpage; + dictitem_T *v; + char_u *varname; + int done = FALSE; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + varname = tv_get_string_chk(&argvars[1]); + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + if (tp != NULL && varname != NULL) + { + /* Set tp to be our tabpage, temporarily. Also set the window to the + * first window in the tabpage, otherwise the window is not valid. */ + if (switch_win(&oldcurwin, &oldtabpage, + tp == curtab || tp->tp_firstwin == NULL ? firstwin + : tp->tp_firstwin, tp, TRUE) == OK) + { + /* look up the variable */ + /* Let gettabvar({nr}, "") return the "t:" dictionary. */ + v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', 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[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); +} + +/* + * "gettagstack()" function + */ + static void +f_gettagstack(typval_T *argvars, typval_T *rettv) +{ + win_T *wp = curwin; // default is current window + + if (rettv_dict_alloc(rettv) != OK) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) + return; + } + + get_tagstack(wp, rettv->vval.v_dict); +} + +/* + * Returns information about a window as a dictionary. + */ + static dict_T * +get_win_info(win_T *wp, short tpnr, short winnr) +{ + dict_T *dict; + + dict = dict_alloc(); + if (dict == NULL) + return NULL; + + dict_add_number(dict, "tabnr", tpnr); + dict_add_number(dict, "winnr", winnr); + dict_add_number(dict, "winid", wp->w_id); + dict_add_number(dict, "height", wp->w_height); + dict_add_number(dict, "winrow", wp->w_winrow + 1); +#ifdef FEAT_MENU + dict_add_number(dict, "winbar", wp->w_winbar_height); +#endif + dict_add_number(dict, "width", wp->w_width); + dict_add_number(dict, "wincol", wp->w_wincol + 1); + dict_add_number(dict, "bufnr", wp->w_buffer->b_fnum); + +#ifdef FEAT_TERMINAL + dict_add_number(dict, "terminal", bt_terminal(wp->w_buffer)); +#endif +#ifdef FEAT_QUICKFIX + dict_add_number(dict, "quickfix", bt_quickfix(wp->w_buffer)); + dict_add_number(dict, "loclist", + (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)); +#endif + + /* Add a reference to window variables */ + dict_add_dict(dict, "variables", wp->w_vars); + + return dict; +} + +/* + * "getwininfo()" function + */ + static void +f_getwininfo(typval_T *argvars, typval_T *rettv) +{ + tabpage_T *tp; + win_T *wp = NULL, *wparg = NULL; + dict_T *d; + short tabnr = 0, winnr; + + if (rettv_list_alloc(rettv) != OK) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + wparg = win_id2wp(argvars); + if (wparg == NULL) + return; + } + + /* Collect information about either all the windows across all the tab + * pages or one particular window. + */ + FOR_ALL_TABPAGES(tp) + { + tabnr++; + winnr = 0; + FOR_ALL_WINDOWS_IN_TAB(tp, wp) + { + winnr++; + if (wparg != NULL && wp != wparg) + continue; + d = get_win_info(wp, tabnr, winnr); + if (d != NULL) + list_append_dict(rettv->vval.v_list, d); + if (wparg != NULL) + /* found information about a specific window */ + return; + } + } +} + +/* + * "win_findbuf()" function + */ + static void +f_win_findbuf(typval_T *argvars, typval_T *rettv) +{ + if (rettv_list_alloc(rettv) != FAIL) + win_findbuf(argvars, rettv->vval.v_list); +} + +/* + * "win_getid()" function + */ + static void +f_win_getid(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = win_getid(argvars); +} + +/* + * "win_gotoid()" function + */ + static void +f_win_gotoid(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = win_gotoid(argvars); +} + +/* + * "win_id2tabwin()" function + */ + static void +f_win_id2tabwin(typval_T *argvars, typval_T *rettv) +{ + if (rettv_list_alloc(rettv) != FAIL) + win_id2tabwin(argvars, rettv->vval.v_list); +} + +/* + * "win_id2win()" function + */ + static void +f_win_id2win(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = win_id2win(argvars); +} + +/* + * "win_screenpos()" function + */ + static void +f_win_screenpos(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + wp = find_win_by_nr_or_id(&argvars[0]); + list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1); + list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1); +} + +/* + * "getwinpos({timeout})" function + */ + static void +f_getwinpos(typval_T *argvars UNUSED, typval_T *rettv) +{ + int x = -1; + int y = -1; + + if (rettv_list_alloc(rettv) == FAIL) + return; +#ifdef FEAT_GUI + if (gui.in_use) + (void)gui_mch_get_winpos(&x, &y); +# if defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE) + else +# endif +#endif +#if defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE) + { + varnumber_T timeout = 100; + + if (argvars[0].v_type != VAR_UNKNOWN) + timeout = tv_get_number(&argvars[0]); + term_get_winpos(&x, &y, timeout); + } +#endif + list_append_number(rettv->vval.v_list, (varnumber_T)x); + list_append_number(rettv->vval.v_list, (varnumber_T)y); +} + + +/* + * "getwinposx()" function + */ + static void +f_getwinposx(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = -1; +#ifdef FEAT_GUI + if (gui.in_use) + { + int x, y; + + if (gui_mch_get_winpos(&x, &y) == OK) + rettv->vval.v_number = x; + return; + } +#endif +#if defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE) + { + int x, y; + + if (term_get_winpos(&x, &y, (varnumber_T)100) == OK) + rettv->vval.v_number = x; + } +#endif +} + +/* + * "getwinposy()" function + */ + static void +f_getwinposy(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = -1; +#ifdef FEAT_GUI + if (gui.in_use) + { + int x, y; + + if (gui_mch_get_winpos(&x, &y) == OK) + rettv->vval.v_number = y; + return; + } +#endif +#if defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE) + { + int x, y; + + if (term_get_winpos(&x, &y, (varnumber_T)100) == OK) + rettv->vval.v_number = y; + } +#endif +} + +/* + * "getwinvar()" function + */ + static void +f_getwinvar(typval_T *argvars, typval_T *rettv) +{ + getwinvar(argvars, rettv, 0); +} + +/* + * "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 (tv_get_number_chk(&argvars[1], &error)) + options |= WILD_KEEP_ALL; + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (tv_get_number_chk(&argvars[2], &error)) + { + rettv_list_set(rettv, NULL); + } + if (argvars[3].v_type != VAR_UNKNOWN + && tv_get_number_chk(&argvars[3], &error)) + options |= WILD_ALLLINKS; + } + } + 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, tv_get_string(&argvars[0]), + NULL, options, WILD_ALL); + else if (rettv_list_alloc(rettv) != FAIL) + { + int i; + + ExpandOne(&xpc, tv_get_string(&argvars[0]), + NULL, options, WILD_ALL_KEEP); + for (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 = tv_get_string_buf_chk(&argvars[1], buf1); + int error = FALSE; + garray_T ga; + int i; + + /* 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[2].v_type != VAR_UNKNOWN) + { + if (tv_get_number_chk(&argvars[2], &error)) + flags |= WILD_KEEP_ALL; + if (argvars[3].v_type != VAR_UNKNOWN) + { + if (tv_get_number_chk(&argvars[3], &error)) + { + rettv_list_set(rettv, NULL); + } + if (argvars[4].v_type != VAR_UNKNOWN + && tv_get_number_chk(&argvars[4], &error)) + flags |= WILD_ALLLINKS; + } + } + if (file != NULL && !error) + { + ga_init2(&ga, (int)sizeof(char_u *), 10); + globpath(tv_get_string(&argvars[0]), file, &ga, flags); + if (rettv->v_type == VAR_STRING) + rettv->vval.v_string = ga_concat_strings(&ga, "\n"); + else if (rettv_list_alloc(rettv) != FAIL) + for (i = 0; i < ga.ga_len; ++i) + list_append_string(rettv->vval.v_list, + ((char_u **)(ga.ga_data))[i], -1); + ga_clear_strings(&ga); + } + else + rettv->vval.v_string = NULL; +} + +/* + * "glob2regpat()" function + */ + static void +f_glob2regpat(typval_T *argvars, typval_T *rettv) +{ + char_u *pat = tv_get_string_chk(&argvars[0]); + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (pat == NULL) + ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, FALSE); +} + +/* for VIM_VERSION_ defines */ +#include "version.h" + +/* + * "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 AMIGA + "amiga", +# ifdef FEAT_ARP + "arp", +# endif +#endif +#ifdef __BEOS__ + "beos", +#endif +#if defined(BSD) && !defined(MACOS_X) + "bsd", +#endif +#ifdef hpux + "hpux", +#endif +#ifdef __linux__ + "linux", +#endif +#ifdef MACOS_X + "mac", /* Mac OS X (and, once, Mac OS Classic) */ + "osx", /* Mac OS X */ +# ifdef MACOS_X_DARWIN + "macunix", /* Mac OS X, with the darwin feature */ + "osxdarwin", /* synonym for macunix */ +# endif +#endif +#ifdef __QNX__ + "qnx", +#endif +#ifdef SUN_SYSTEM + "sun", +#else + "moon", +#endif +#ifdef UNIX + "unix", +#endif +#ifdef VMS + "vms", +#endif +#ifdef WIN32 + "win32", +#endif +#if defined(UNIX) && (defined(__CYGWIN32__) || defined(__CYGWIN__)) + "win32unix", +#endif +#if defined(WIN64) || defined(_WIN64) + "win64", +#endif +#ifdef EBCDIC + "ebcdic", +#endif +#ifndef CASE_INSENSITIVE_FILENAME + "fname_case", +#endif +#ifdef HAVE_ACL + "acl", +#endif +#ifdef FEAT_ARABIC + "arabic", +#endif + "autocmd", +#ifdef FEAT_AUTOCHDIR + "autochdir", +#endif +#ifdef FEAT_AUTOSERVERNAME + "autoservername", +#endif +#ifdef FEAT_BEVAL_GUI + "balloon_eval", +# ifndef FEAT_GUI_W32 /* other GUIs always have multiline balloons */ + "balloon_multiline", +# endif +#endif +#ifdef FEAT_BEVAL_TERM + "balloon_eval_term", +#endif +#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 +#ifdef FEAT_BYTEOFF + "byte_offset", +#endif +#ifdef FEAT_JOB_CHANNEL + "channel", +#endif +#ifdef FEAT_CINDENT + "cindent", +#endif +#ifdef FEAT_CLIENTSERVER + "clientserver", +#endif +#ifdef FEAT_CLIPBOARD + "clipboard", +#endif +#ifdef FEAT_CMDL_COMPL + "cmdline_compl", +#endif +#ifdef FEAT_CMDHIST + "cmdline_hist", +#endif +#ifdef FEAT_COMMENTS + "comments", +#endif +#ifdef FEAT_CONCEAL + "conceal", +#endif +#ifdef FEAT_CRYPT + "cryptv", + "crypt-blowfish", + "crypt-blowfish2", +#endif +#ifdef FEAT_CSCOPE + "cscope", +#endif + "cursorbind", +#ifdef CURSOR_SHAPE + "cursorshape", +#endif +#ifdef DEBUG + "debug", +#endif +#ifdef FEAT_CON_DIALOG + "dialog_con", +#endif +#ifdef FEAT_GUI_DIALOG + "dialog_gui", +#endif +#ifdef FEAT_DIFF + "diff", +#endif +#ifdef FEAT_DIGRAPHS + "digraphs", +#endif +#ifdef FEAT_DIRECTX + "directx", +#endif +#ifdef FEAT_DND + "dnd", +#endif +#ifdef FEAT_EMACS_TAGS + "emacs_tags", +#endif + "eval", /* always present, of course! */ + "ex_extra", /* graduated feature */ +#ifdef FEAT_SEARCH_EXTRA + "extra_search", +#endif +#ifdef FEAT_FKMAP + "farsi", +#endif +#ifdef FEAT_SEARCHPATH + "file_in_path", +#endif +#ifdef FEAT_FILTERPIPE + "filterpipe", +#endif +#ifdef FEAT_FIND_ID + "find_in_path", +#endif +#ifdef FEAT_FLOAT + "float", +#endif +#ifdef FEAT_FOLDING + "folding", +#endif +#ifdef FEAT_FOOTER + "footer", +#endif +#if !defined(USE_SYSTEM) && defined(UNIX) + "fork", +#endif +#ifdef FEAT_GETTEXT + "gettext", +#endif +#ifdef FEAT_GUI + "gui", +#endif +#ifdef FEAT_GUI_ATHENA +# ifdef FEAT_GUI_NEXTAW + "gui_neXtaw", +# else + "gui_athena", +# endif +#endif +#ifdef FEAT_GUI_GTK + "gui_gtk", +# ifdef USE_GTK3 + "gui_gtk3", +# else + "gui_gtk2", +# endif +#endif +#ifdef FEAT_GUI_GNOME + "gui_gnome", +#endif +#ifdef FEAT_GUI_MAC + "gui_mac", +#endif +#ifdef FEAT_GUI_MOTIF + "gui_motif", +#endif +#ifdef FEAT_GUI_PHOTON + "gui_photon", +#endif +#ifdef FEAT_GUI_W32 + "gui_win32", +#endif +#ifdef FEAT_HANGULIN + "hangul_input", +#endif +#if defined(HAVE_ICONV_H) && defined(USE_ICONV) + "iconv", +#endif +#ifdef FEAT_INS_EXPAND + "insert_expand", +#endif +#ifdef FEAT_JOB_CHANNEL + "job", +#endif +#ifdef FEAT_JUMPLIST + "jumplist", +#endif +#ifdef FEAT_KEYMAP + "keymap", +#endif + "lambda", /* always with FEAT_EVAL, since 7.4.2120 with closure */ +#ifdef FEAT_LANGMAP + "langmap", +#endif +#ifdef FEAT_LIBCALL + "libcall", +#endif +#ifdef FEAT_LINEBREAK + "linebreak", +#endif +#ifdef FEAT_LISP + "lispindent", +#endif + "listcmds", +#ifdef FEAT_LOCALMAP + "localmap", +#endif +#ifdef FEAT_LUA +# ifndef DYNAMIC_LUA + "lua", +# endif +#endif +#ifdef FEAT_MENU + "menu", +#endif +#ifdef FEAT_SESSION + "mksession", +#endif +#ifdef FEAT_MODIFY_FNAME + "modify_fname", +#endif +#ifdef FEAT_MOUSE + "mouse", +#endif +#ifdef FEAT_MOUSESHAPE + "mouseshape", +#endif +#if defined(UNIX) || defined(VMS) +# ifdef FEAT_MOUSE_DEC + "mouse_dec", +# endif +# ifdef FEAT_MOUSE_GPM + "mouse_gpm", +# endif +# ifdef FEAT_MOUSE_JSB + "mouse_jsbterm", +# endif +# ifdef FEAT_MOUSE_NET + "mouse_netterm", +# endif +# ifdef FEAT_MOUSE_PTERM + "mouse_pterm", +# endif +# ifdef FEAT_MOUSE_SGR + "mouse_sgr", +# endif +# ifdef FEAT_SYSMOUSE + "mouse_sysmouse", +# endif +# ifdef FEAT_MOUSE_URXVT + "mouse_urxvt", +# endif +# ifdef FEAT_MOUSE_XTERM + "mouse_xterm", +# endif +#endif + "multi_byte", +#ifdef FEAT_MBYTE_IME + "multi_byte_ime", +#endif +#ifdef FEAT_MULTI_LANG + "multi_lang", +#endif +#ifdef FEAT_MZSCHEME +#ifndef DYNAMIC_MZSCHEME + "mzscheme", +#endif +#endif +#ifdef FEAT_NUM64 + "num64", +#endif +#ifdef FEAT_OLE + "ole", +#endif +#ifdef FEAT_EVAL + "packages", +#endif +#ifdef FEAT_PATH_EXTRA + "path_extra", +#endif +#ifdef FEAT_PERL +#ifndef DYNAMIC_PERL + "perl", +#endif +#endif +#ifdef FEAT_PERSISTENT_UNDO + "persistent_undo", +#endif +#if defined(FEAT_PYTHON) + "python_compiled", +# if defined(DYNAMIC_PYTHON) + "python_dynamic", +# else + "python", + "pythonx", +# endif +#endif +#if defined(FEAT_PYTHON3) + "python3_compiled", +# if defined(DYNAMIC_PYTHON3) + "python3_dynamic", +# else + "python3", + "pythonx", +# endif +#endif +#ifdef FEAT_POSTSCRIPT + "postscript", +#endif +#ifdef FEAT_PRINTER + "printer", +#endif +#ifdef FEAT_PROFILE + "profile", +#endif +#ifdef FEAT_RELTIME + "reltime", +#endif +#ifdef FEAT_QUICKFIX + "quickfix", +#endif +#ifdef FEAT_RIGHTLEFT + "rightleft", +#endif +#if defined(FEAT_RUBY) && !defined(DYNAMIC_RUBY) + "ruby", +#endif + "scrollbind", +#ifdef FEAT_CMDL_INFO + "showcmd", + "cmdline_info", +#endif +#ifdef FEAT_SIGNS + "signs", +#endif +#ifdef FEAT_SMARTINDENT + "smartindent", +#endif +#ifdef STARTUPTIME + "startuptime", +#endif +#ifdef FEAT_STL_OPT + "statusline", +#endif +#ifdef FEAT_NETBEANS_INTG + "netbeans_intg", +#endif +#ifdef FEAT_SPELL + "spell", +#endif +#ifdef FEAT_SYN_HL + "syntax", +#endif +#if defined(USE_SYSTEM) || !defined(UNIX) + "system", +#endif +#ifdef FEAT_TAG_BINS + "tag_binary", +#endif +#ifdef FEAT_TAG_OLDSTATIC + "tag_old_static", +#endif +#ifdef FEAT_TAG_ANYWHITE + "tag_any_white", +#endif +#ifdef FEAT_TCL +# ifndef DYNAMIC_TCL + "tcl", +# endif +#endif +#ifdef FEAT_TERMGUICOLORS + "termguicolors", +#endif +#if defined(FEAT_TERMINAL) && !defined(WIN3264) + "terminal", +#endif +#ifdef TERMINFO + "terminfo", +#endif +#ifdef FEAT_TERMRESPONSE + "termresponse", +#endif +#ifdef FEAT_TEXTOBJ + "textobjects", +#endif +#ifdef FEAT_TEXT_PROP + "textprop", +#endif +#ifdef HAVE_TGETENT + "tgetent", +#endif +#ifdef FEAT_TIMERS + "timers", +#endif +#ifdef FEAT_TITLE + "title", +#endif +#ifdef FEAT_TOOLBAR + "toolbar", +#endif +#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11) + "unnamedplus", +#endif +#ifdef FEAT_USR_CMDS + "user-commands", /* was accidentally included in 5.4 */ + "user_commands", +#endif +#ifdef FEAT_VARTABS + "vartabs", +#endif +#ifdef FEAT_VIMINFO + "viminfo", +#endif + "vertsplit", + "virtualedit", + "visual", + "visualextra", + "vreplace", +#ifdef FEAT_VTP + "vtp", +#endif +#ifdef FEAT_WILDIGN + "wildignore", +#endif +#ifdef FEAT_WILDMENU + "wildmenu", +#endif + "windows", +#ifdef FEAT_WAK + "winaltkeys", +#endif +#ifdef FEAT_WRITEBACKUP + "writebackup", +#endif +#ifdef FEAT_XIM + "xim", +#endif +#ifdef FEAT_XFONTSET + "xfontset", +#endif +#ifdef FEAT_XPM_W32 + "xpm", + "xpm_w32", /* for backward compatibility */ +#else +# if defined(HAVE_XPM) + "xpm", +# endif +#endif +#ifdef USE_XSMP + "xsmp", +#endif +#ifdef USE_XSMP_INTERACT + "xsmp_interact", +#endif +#ifdef FEAT_XCLIPBOARD + "xterm_clipboard", +#endif +#ifdef FEAT_XTERM_SAVE + "xterm_save", +#endif +#if defined(UNIX) && defined(FEAT_X11) + "X11", +#endif + NULL + }; + + name = tv_get_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, "ttyin") == 0) + n = mch_input_isatty(); + else if (STRICMP(name, "ttyout") == 0) + n = stdout_isatty; + else if (STRICMP(name, "multi_byte_encoding") == 0) + n = has_mbyte; +#if defined(FEAT_BEVAL) && defined(FEAT_GUI_W32) + else if (STRICMP(name, "balloon_multiline") == 0) + n = multiline_balloon_available(); +#endif +#ifdef DYNAMIC_TCL + else if (STRICMP(name, "tcl") == 0) + n = tcl_enabled(FALSE); +#endif +#if defined(USE_ICONV) && defined(DYNAMIC_ICONV) + else if (STRICMP(name, "iconv") == 0) + n = iconv_enabled(FALSE); +#endif +#ifdef DYNAMIC_LUA + else if (STRICMP(name, "lua") == 0) + n = lua_enabled(FALSE); +#endif +#ifdef DYNAMIC_MZSCHEME + else if (STRICMP(name, "mzscheme") == 0) + n = mzscheme_enabled(FALSE); +#endif +#ifdef DYNAMIC_RUBY + else if (STRICMP(name, "ruby") == 0) + n = ruby_enabled(FALSE); +#endif +#ifdef DYNAMIC_PYTHON + else if (STRICMP(name, "python") == 0) + n = python_enabled(FALSE); +#endif +#ifdef DYNAMIC_PYTHON3 + else if (STRICMP(name, "python3") == 0) + n = python3_enabled(FALSE); +#endif +#if defined(DYNAMIC_PYTHON) || defined(DYNAMIC_PYTHON3) + else if (STRICMP(name, "pythonx") == 0) + { +# if defined(DYNAMIC_PYTHON) && defined(DYNAMIC_PYTHON3) + if (p_pyx == 0) + n = python3_enabled(FALSE) || python_enabled(FALSE); + else if (p_pyx == 3) + n = python3_enabled(FALSE); + else if (p_pyx == 2) + n = python_enabled(FALSE); +# elif defined(DYNAMIC_PYTHON) + n = python_enabled(FALSE); +# elif defined(DYNAMIC_PYTHON3) + n = python3_enabled(FALSE); +# endif + } +#endif +#ifdef DYNAMIC_PERL + else if (STRICMP(name, "perl") == 0) + n = perl_enabled(FALSE); +#endif +#ifdef FEAT_GUI + else if (STRICMP(name, "gui_running") == 0) + n = (gui.in_use || gui.starting); +# ifdef FEAT_BROWSE + else if (STRICMP(name, "browse") == 0) + n = gui.in_use; /* gui_mch_browse() works when GUI is running */ +# endif +#endif +#ifdef FEAT_SYN_HL + else if (STRICMP(name, "syntax_items") == 0) + n = syntax_present(curwin); +#endif +#ifdef FEAT_VTP + else if (STRICMP(name, "vcon") == 0) + n = is_term_win32() && has_vtp_working(); +#endif +#ifdef FEAT_NETBEANS_INTG + else if (STRICMP(name, "netbeans_enabled") == 0) + n = netbeans_active(); +#endif +#if defined(FEAT_TERMINAL) && defined(WIN3264) + else if (STRICMP(name, "terminal") == 0) + n = terminal_enabled(); +#endif +#if defined(FEAT_TERMINAL) && defined(WIN3264) + else if (STRICMP(name, "conpty") == 0) + n = use_conpty(); +#endif + } + + 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, + tv_get_string(&argvars[1]), -1) != NULL; +} + +/* + * "haslocaldir()" function + */ + static void +f_haslocaldir(typval_T *argvars, typval_T *rettv) +{ + win_T *wp = NULL; + + wp = find_tabwin(&argvars[0], &argvars[1]); + rettv->vval.v_number = (wp != NULL && wp->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 = tv_get_string(&argvars[0]); + if (argvars[1].v_type == VAR_UNKNOWN) + mode = (char_u *)"nvo"; + else + { + mode = tv_get_string_buf(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) + abbr = (int)tv_get_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 UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CMDHIST + int histype; + char_u *str; + char_u buf[NUMBUFLEN]; +#endif + + rettv->vval.v_number = FALSE; + if (check_restricted() || check_secure()) + return; +#ifdef FEAT_CMDHIST + str = tv_get_string_chk(&argvars[0]); /* NULL on type error */ + histype = str != NULL ? get_histtype(str) : -1; + if (histype >= 0) + { + str = tv_get_string_buf(&argvars[1], buf); + if (*str != NUL) + { + init_history(); + add_to_history(histype, str, FALSE, NUL); + rettv->vval.v_number = TRUE; + return; + } + } +#endif +} + +/* + * "histdel()" function + */ + static void +f_histdel(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_CMDHIST + int n; + char_u buf[NUMBUFLEN]; + char_u *str; + + str = tv_get_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)tv_get_number(&argvars[1])); + else + /* string given: remove all matching entries */ + n = del_history_entry(get_histtype(str), + tv_get_string_buf(&argvars[1], buf)); + rettv->vval.v_number = n; +#endif +} + +/* + * "histget()" function + */ + static void +f_histget(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CMDHIST + int type; + int idx; + char_u *str; + + str = tv_get_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)tv_get_number_chk(&argvars[1], NULL); + /* -1 on type error */ + rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); + } +#else + rettv->vval.v_string = NULL; +#endif + rettv->v_type = VAR_STRING; +} + +/* + * "histnr()" function + */ + static void +f_histnr(typval_T *argvars UNUSED, typval_T *rettv) +{ + int i; + +#ifdef FEAT_CMDHIST + char_u *history = tv_get_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 +#endif + 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(tv_get_string(&argvars[0])); +} + +/* + * "highlight_exists()" function + */ + static void +f_hlexists(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0])); +} + +/* + * "hostname()" function + */ + static void +f_hostname(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u hostname[256]; + + mch_get_host_name(hostname, 256); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(hostname); +} + +/* + * iconv() function + */ + static void +f_iconv(typval_T *argvars UNUSED, 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 = tv_get_string(&argvars[0]); + from = enc_canonize(enc_skip(tv_get_string_buf(&argvars[1], buf1))); + to = enc_canonize(enc_skip(tv_get_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); + vim_free(from); + vim_free(to); +} + +/* + * "indent()" function + */ + static void +f_indent(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + + lnum = tv_get_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; + blob_T *b; + long idx = 0; + int ic = FALSE; + int error = FALSE; + + rettv->vval.v_number = -1; + if (argvars[0].v_type == VAR_BLOB) + { + typval_T tv; + int start = 0; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + start = tv_get_number_chk(&argvars[2], &error); + if (error) + return; + } + b = argvars[0].vval.v_blob; + if (b == NULL) + return; + if (start < 0) + { + start = blob_len(b) + start; + if (start < 0) + start = 0; + } + + for (idx = start; idx < blob_len(b); ++idx) + { + tv.v_type = VAR_NUMBER; + tv.vval.v_number = blob_get(b, idx); + if (tv_equal(&tv, &argvars[1], ic, FALSE)) + { + rettv->vval.v_number = idx; + return; + } + } + return; + } + else if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_listblobreq)); + return; + } + + l = argvars[0].vval.v_list; + if (l != NULL) + { + item = l->lv_first; + if (argvars[2].v_type != VAR_UNKNOWN) + { + /* Start at specified item. Use the cached index that list_find() + * sets, so that a negative number also works. */ + item = list_find(l, (long)tv_get_number_chk(&argvars[2], &error)); + idx = l->lv_idx; + if (argvars[3].v_type != VAR_UNKNOWN) + ic = (int)tv_get_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; + +/* + * "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, inputsecret_flag); +} + +/* + * "inputdialog()" function + */ + static void +f_inputdialog(typval_T *argvars, typval_T *rettv) +{ +#if defined(FEAT_GUI_TEXTDIALOG) + /* Use a GUI dialog if the GUI is running and 'c' is not in 'guioptions' */ + if (gui.in_use && vim_strchr(p_go, GO_CONDIALOG) == NULL) + { + char_u *message; + char_u buf[NUMBUFLEN]; + char_u *defstr = (char_u *)""; + + message = tv_get_string_chk(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN + && (defstr = tv_get_string_buf_chk(&argvars[1], buf)) != NULL) + vim_strncpy(IObuff, defstr, IOSIZE - 1); + else + IObuff[0] = NUL; + if (message != NULL && defstr != NULL + && do_dialog(VIM_QUESTION, NULL, message, + (char_u *)_("&OK\n&Cancel"), 1, IObuff, FALSE) == 1) + rettv->vval.v_string = vim_strsave(IObuff); + else + { + if (message != NULL && defstr != NULL + && argvars[1].v_type != VAR_UNKNOWN + && argvars[2].v_type != VAR_UNKNOWN) + rettv->vval.v_string = vim_strsave( + tv_get_string_buf(&argvars[2], buf)); + else + rettv->vval.v_string = NULL; + } + rettv->v_type = VAR_STRING; + } + else +#endif + get_user_input(argvars, rettv, TRUE, inputsecret_flag); +} + +/* + * "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. When running tests + * with --not-a-term we assume feedkeys() will be used. */ + if (no_console_input() && !is_not_a_term()) + return; +#endif + if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) + { + semsg(_(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((char *)tv_get_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 UNUSED, 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(_("called inputrestore() more often than inputsave()")); + rettv->vval.v_number = 1; /* Failed */ + } +} + +/* + * "inputsave()" function + */ + static void +f_inputsave(typval_T *argvars UNUSED, typval_T *rettv) +{ + /* Add an entry to the stack of typeahead storage. */ + if (ga_grow(&ga_userinput, 1) == OK) + { + save_typeahead((tasave_T *)(ga_userinput.ga_data) + + ga_userinput.ga_len); + ++ga_userinput.ga_len; + /* default return is zero == OK */ + } + else + rettv->vval.v_number = 1; /* Failed */ +} + +/* + * "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_BLOB) + { + int val, len; + char_u *p; + + len = blob_len(argvars[0].vval.v_blob); + if (argvars[2].v_type != VAR_UNKNOWN) + { + before = (long)tv_get_number_chk(&argvars[2], &error); + if (error) + return; // type error; errmsg already given + if (before < 0 || before > len) + { + semsg(_(e_invarg2), tv_get_string(&argvars[2])); + return; + } + } + val = tv_get_number_chk(&argvars[1], &error); + if (error) + return; + if (val < 0 || val > 255) + { + semsg(_(e_invarg2), tv_get_string(&argvars[1])); + return; + } + + if (ga_grow(&argvars[0].vval.v_blob->bv_ga, 1) == FAIL) + return; + p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; + mch_memmove(p + before + 1, p + before, (size_t)len - before); + *(p + before) = val; + ++argvars[0].vval.v_blob->bv_ga.ga_len; + + copy_tv(&argvars[0], rettv); + } + else if (argvars[0].v_type != VAR_LIST) + semsg(_(e_listblobarg), "insert()"); + else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, + (char_u *)N_("insert() argument"), TRUE)) + { + if (argvars[2].v_type != VAR_UNKNOWN) + before = (long)tv_get_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) + { + semsg(_(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 = ~tv_get_number_chk(&argvars[0], NULL); +} + +/* + * "isdirectory()" function + */ + static void +f_isdirectory(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = mch_isdir(tv_get_string(&argvars[0])); +} + +/* + * 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)); +} + +/* + * "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(tv_get_string(&argvars[0]), NULL, &lv, FALSE, FALSE, + GLV_NO_AUTOLOAD | GLV_READ_ONLY, FNE_CHECK_START); + if (end != NULL && lv.ll_name != NULL) + { + if (*end != NUL) + emsg(_(e_trailing)); + else + { + if (lv.ll_tv == NULL) + { + 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) + semsg(_(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); +} + +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) +/* + * "isnan()" function + */ + static void +f_isnan(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT + && isnan(argvars[0].vval.v_float); +} +#endif + +/* + * "items(dict)" function + */ + static void +f_items(typval_T *argvars, typval_T *rettv) +{ + dict_list(argvars, rettv, 2); +} + +#if defined(FEAT_JOB_CHANNEL) || defined(PROTO) +/* + * Get the job from the argument. + * Returns NULL if the job is invalid. + */ + static job_T * +get_job_arg(typval_T *tv) +{ + job_T *job; + + if (tv->v_type != VAR_JOB) + { + semsg(_(e_invarg2), tv_get_string(tv)); + return NULL; + } + job = tv->vval.v_job; + + if (job == NULL) + emsg(_("E916: not a valid job")); + return job; +} + +/* + * "job_getchannel()" function + */ + static void +f_job_getchannel(typval_T *argvars, typval_T *rettv) +{ + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL) + { + rettv->v_type = VAR_CHANNEL; + rettv->vval.v_channel = job->jv_channel; + if (job->jv_channel != NULL) + ++job->jv_channel->ch_refcount; + } +} + +/* + * "job_info()" function + */ + static void +f_job_info(typval_T *argvars, typval_T *rettv) +{ + if (argvars[0].v_type != VAR_UNKNOWN) + { + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL && rettv_dict_alloc(rettv) != FAIL) + job_info(job, rettv->vval.v_dict); + } + else if (rettv_list_alloc(rettv) == OK) + job_info_all(rettv->vval.v_list); +} + +/* + * "job_setoptions()" function + */ + static void +f_job_setoptions(typval_T *argvars, typval_T *rettv UNUSED) +{ + job_T *job = get_job_arg(&argvars[0]); + jobopt_T opt; + + if (job == NULL) + return; + clear_job_options(&opt); + if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT + JO_EXIT_CB, 0) == OK) + job_set_options(job, &opt); + free_job_options(&opt); +} + +/* + * "job_start()" function + */ + static void +f_job_start(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_JOB; + if (check_restricted() || check_secure()) + return; + rettv->vval.v_job = job_start(argvars, NULL, NULL, FALSE); +} + +/* + * "job_status()" function + */ + static void +f_job_status(typval_T *argvars, typval_T *rettv) +{ + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL) + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave((char_u *)job_status(job)); + } +} + +/* + * "job_stop()" function + */ + static void +f_job_stop(typval_T *argvars, typval_T *rettv) +{ + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL) + rettv->vval.v_number = job_stop(job, argvars, NULL); +} +#endif + +/* + * "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 = tv_get_string_chk(&argvars[1]); + + rettv->v_type = VAR_STRING; + + if (sep != NULL) + { + ga_init2(&ga, (int)sizeof(char), 80); + list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); + ga_append(&ga, NUL); + rettv->vval.v_string = (char_u *)ga.ga_data; + } + else + rettv->vval.v_string = NULL; +} + +/* + * "js_decode()" function + */ + static void +f_js_decode(typval_T *argvars, typval_T *rettv) +{ + js_read_T reader; + + reader.js_buf = tv_get_string(&argvars[0]); + reader.js_fill = NULL; + reader.js_used = 0; + if (json_decode_all(&reader, rettv, JSON_JS) != OK) + emsg(_(e_invarg)); +} + +/* + * "js_encode()" function + */ + static void +f_js_encode(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = json_encode(&argvars[0], JSON_JS); +} + +/* + * "json_decode()" function + */ + static void +f_json_decode(typval_T *argvars, typval_T *rettv) +{ + js_read_T reader; + + reader.js_buf = tv_get_string(&argvars[0]); + reader.js_fill = NULL; + reader.js_used = 0; + json_decode_all(&reader, rettv, 0); +} + +/* + * "json_encode()" function + */ + static void +f_json_encode(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = json_encode(&argvars[0], 0); +} + +/* + * "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 UNUSED, typval_T *rettv) +{ + int n = 0; + buf_T *buf; + + FOR_ALL_BUFFERS(buf) + 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( + tv_get_string(&argvars[0])); + break; + case VAR_BLOB: + rettv->vval.v_number = blob_len(argvars[0].vval.v_blob); + 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; + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_JOB: + case VAR_CHANNEL: + emsg(_("E701: Invalid type for len()")); + break; + } +} + + static void +libcall_common(typval_T *argvars UNUSED, 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 it's 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 UNUSED, typval_T *rettv) +{ +#ifndef FEAT_BYTEOFF + rettv->vval.v_number = -1; +#else + linenr_T lnum; + + lnum = tv_get_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; +#endif +} + +/* + * "lispindent(lnum)" function + */ + static void +f_lispindent(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_LISP + pos_T pos; + linenr_T lnum; + + pos = curwin->w_cursor; + lnum = tv_get_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 +#endif + rettv->vval.v_number = -1; +} + +/* + * "localtime()" function + */ + static void +f_localtime(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = (varnumber_T)time(NULL); +} + + 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 = tv_get_string(&argvars[0]); + if (*keys == NUL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + which = tv_get_string_buf_chk(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) + { + abbr = (int)tv_get_number(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) + get_dict = (int)tv_get_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); + vim_free(keys_buf); + + if (!get_dict) + { + /* Return a string. */ + if (rhs != NULL) + { + if (*rhs == NUL) + rettv->vval.v_string = vim_strsave((char_u *)""); + else + 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_string(dict, "lhs", lhs); + dict_add_string(dict, "rhs", mp->m_orig_str); + dict_add_number(dict, "noremap", mp->m_noremap ? 1L : 0L); + dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L); + dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L); + dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid); + dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum); + dict_add_number(dict, "buffer", (long)buffer_local); + dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L); + dict_add_string(dict, "mode", mapmode); + + vim_free(lhs); + vim_free(mapmode); + } +} + +#ifdef FEAT_FLOAT +/* + * "log()" function + */ + static void +f_log(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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 = 0.0; + + 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; +} +#endif + +#ifdef FEAT_LUA +/* + * "luaeval()" function + */ + static void +f_luaeval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + str = tv_get_string_buf(&argvars[0], buf); + do_luaeval(str, argvars + 1, rettv); +} +#endif + +/* + * "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); +} + +typedef enum +{ + MATCH_END, /* matchend() */ + MATCH_MATCH, /* match() */ + MATCH_STR, /* matchstr() */ + MATCH_LIST, /* matchlist() */ + MATCH_POS /* matchstrpos() */ +} matchtype_T; + + static void +find_some_match(typval_T *argvars, typval_T *rettv, matchtype_T 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 == MATCH_LIST || type == MATCH_POS) + { + /* type MATCH_LIST: return empty list when there are no matches. + * type MATCH_POS: return ["", -1, -1, -1] */ + if (rettv_list_alloc(rettv) == FAIL) + goto theend; + if (type == MATCH_POS + && (list_append_string(rettv->vval.v_list, + (char_u *)"", 0) == FAIL + || list_append_number(rettv->vval.v_list, + (varnumber_T)-1) == FAIL + || list_append_number(rettv->vval.v_list, + (varnumber_T)-1) == FAIL + || list_append_number(rettv->vval.v_list, + (varnumber_T)-1) == FAIL)) + { + list_free(rettv->vval.v_list); + rettv->vval.v_list = NULL; + goto theend; + } + } + else if (type == MATCH_STR) + { + 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 = tv_get_string(&argvars[0]); + len = (long)STRLEN(str); + } + + pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) + goto theend; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + int error = FALSE; + + start = (long)tv_get_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 = (long)tv_get_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; + } + vim_free(tofree); + expr = 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 == MATCH_POS) + { + listitem_T *li1 = rettv->vval.v_list->lv_first; + listitem_T *li2 = li1->li_next; + listitem_T *li3 = li2->li_next; + listitem_T *li4 = li3->li_next; + + vim_free(li1->li_tv.vval.v_string); + li1->li_tv.vval.v_string = vim_strnsave(regmatch.startp[0], + (int)(regmatch.endp[0] - regmatch.startp[0])); + li3->li_tv.vval.v_number = + (varnumber_T)(regmatch.startp[0] - expr); + li4->li_tv.vval.v_number = + (varnumber_T)(regmatch.endp[0] - expr); + if (l != NULL) + li2->li_tv.vval.v_number = (varnumber_T)idx; + } + else if (type == MATCH_LIST) + { + int i; + + /* return list with matched string and submatches */ + for (i = 0; i < NSUBEXP; ++i) + { + if (regmatch.endp[i] == NULL) + { + if (list_append_string(rettv->vval.v_list, + (char_u *)"", 0) == FAIL) + break; + } + else if (list_append_string(rettv->vval.v_list, + regmatch.startp[i], + (int)(regmatch.endp[i] - regmatch.startp[i])) + == FAIL) + break; + } + } + else if (type == MATCH_STR) + { + /* 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 != MATCH_END) + 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: + if (type == MATCH_POS && l == NULL && rettv->vval.v_list != NULL) + /* matchstrpos() without a list: drop the second item. */ + listitem_remove(rettv->vval.v_list, + rettv->vval.v_list->lv_first->li_next); + vim_free(tofree); + p_cpo = save_cpo; +} + +/* + * "match()" function + */ + static void +f_match(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_MATCH); +} + +#ifdef FEAT_SEARCH_EXTRA + static int +matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win) +{ + dictitem_T *di; + + if (tv->v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return FAIL; + } + + if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL) + *conceal_char = dict_get_string(tv->vval.v_dict, + (char_u *)"conceal", FALSE); + + if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL) + { + *win = find_win_by_nr_or_id(&di->di_tv); + if (*win == NULL) + { + emsg(_("E957: Invalid window number")); + return FAIL; + } + } + + return OK; +} +#endif + +/* + * "matchadd()" function + */ + static void +f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + char_u buf[NUMBUFLEN]; + char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); /* group */ + char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); /* pattern */ + int prio = 10; /* default priority */ + int id = -1; + int error = FALSE; + char_u *conceal_char = NULL; + win_T *win = curwin; + + rettv->vval.v_number = -1; + + if (grp == NULL || pat == NULL) + return; + if (argvars[2].v_type != VAR_UNKNOWN) + { + prio = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + id = (int)tv_get_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) + return; + } + } + if (error == TRUE) + return; + if (id >= 1 && id <= 3) + { + semsg(_("E798: ID is reserved for \":match\": %d"), id); + return; + } + + rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, + conceal_char); +#endif +} + +/* + * "matchaddpos()" function + */ + static void +f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + char_u buf[NUMBUFLEN]; + char_u *group; + int prio = 10; + int id = -1; + int error = FALSE; + list_T *l; + char_u *conceal_char = NULL; + win_T *win = curwin; + + rettv->vval.v_number = -1; + + group = tv_get_string_buf_chk(&argvars[0], buf); + if (group == NULL) + return; + + if (argvars[1].v_type != VAR_LIST) + { + semsg(_(e_listarg), "matchaddpos()"); + return; + } + l = argvars[1].vval.v_list; + if (l == NULL) + return; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + prio = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + id = (int)tv_get_number_chk(&argvars[3], &error); + + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) + return; + } + } + if (error == TRUE) + return; + + /* id == 3 is ok because matchaddpos() is supposed to substitute :3match */ + if (id == 1 || id == 2) + { + semsg(_("E798: ID is reserved for \":match\": %d"), id); + return; + } + + rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, + conceal_char); +#endif +} + +/* + * "matcharg()" function + */ + static void +f_matcharg(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_list_alloc(rettv) == OK) + { +#ifdef FEAT_SEARCH_EXTRA + int id = (int)tv_get_number(&argvars[0]); + matchitem_T *m; + + if (id >= 1 && id <= 3) + { + 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); + } + } +#endif + } +} + +/* + * "matchdelete()" function + */ + static void +f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + rettv->vval.v_number = match_delete(curwin, + (int)tv_get_number(&argvars[0]), TRUE); +#endif +} + +/* + * "matchend()" function + */ + static void +f_matchend(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_END); +} + +/* + * "matchlist()" function + */ + static void +f_matchlist(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_LIST); +} + +/* + * "matchstr()" function + */ + static void +f_matchstr(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_STR); +} + +/* + * "matchstrpos()" function + */ + static void +f_matchstrpos(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_POS); +} + + static void +max_min(typval_T *argvars, typval_T *rettv, int domax) +{ + varnumber_T n = 0; + varnumber_T 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 = tv_get_number_chk(&li->li_tv, &error); + for (;;) + { + li = li->li_next; + if (li == NULL) + break; + i = tv_get_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 = tv_get_number_chk(&HI2DI(hi)->di_tv, &error); + if (first) + { + n = i; + first = FALSE; + } + else if (domax ? i > n : i < n) + n = i; + } + } + } + } + else + semsg(_(e_listdictarg), domax ? "max()" : "min()"); + 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); +} + +/* + * Create the directory in which "dir" is located, and higher levels when + * needed. + * Return OK or FAIL. + */ + 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 = gettail_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 (mch_isdir(updir)) + r = OK; + else if (mkdir_recurse(updir, prot) == OK) + r = vim_mkdir_emsg(updir, prot); + vim_free(updir); + return r; +} + +#ifdef vim_mkdir +/* + * "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 = tv_get_string_buf(&argvars[0], buf); + if (*dir == NUL) + return; + + if (*gettail(dir) == NUL) + /* remove trailing slashes */ + *gettail_sep(dir) = NUL; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[2].v_type != VAR_UNKNOWN) + { + prot = (int)tv_get_number_chk(&argvars[2], NULL); + if (prot == -1) + return; + } + if (STRCMP(tv_get_string(&argvars[1]), "p") == 0) + { + if (mch_isdir(dir)) + { + /* With the "p" flag it's OK if the dir already exists. */ + rettv->vval.v_number = OK; + return; + } + mkdir_recurse(dir, prot); + } + } + rettv->vval.v_number = vim_mkdir_emsg(dir, prot); +} +#endif + +/* + * "mode()" function + */ + static void +f_mode(typval_T *argvars, typval_T *rettv) +{ + char_u buf[4]; + + vim_memset(buf, 0, sizeof(buf)); + + if (time_for_testing == 93784) + { + /* Testing the two-character code. */ + buf[0] = 'x'; + buf[1] = '!'; + } +#ifdef FEAT_TERMINAL + else if (term_use_loop()) + buf[0] = 't'; +#endif + else 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'; +#ifdef FEAT_INS_EXPAND + if (ins_compl_active()) + buf[1] = 'c'; + else if (ctrl_x_mode_not_defined_yet()) + buf[1] = 'x'; +#endif + } + } + else if ((State & CMDLINE) || exmode_active) + { + buf[0] = 'c'; + if (exmode_active == EXMODE_VIM) + buf[1] = 'v'; + else if (exmode_active == EXMODE_NORMAL) + buf[1] = 'e'; + } + else + { + buf[0] = 'n'; + if (finish_op) + { + buf[1] = 'o'; + // to be able to detect force-linewise/blockwise/characterwise operations + buf[2] = motion_force; + } + else if (restart_edit == 'I' || restart_edit == 'R' + || restart_edit == 'V') + { + buf[1] = 'i'; + buf[2] = restart_edit; + } + } + + /* 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; +} + +#if defined(FEAT_MZSCHEME) || defined(PROTO) +/* + * "mzeval()" function + */ + static void +f_mzeval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + str = tv_get_string_buf(&argvars[0], buf); + do_mzeval(str, rettv); +} + + void +mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv) +{ + typval_T argvars[3]; + + argvars[0].v_type = VAR_STRING; + argvars[0].vval.v_string = name; + copy_tv(args, &argvars[1]); + argvars[2].v_type = VAR_UNKNOWN; + f_call(argvars, rettv); + clear_tv(&argvars[1]); +} +#endif + +/* + * "nextnonblank()" function + */ + static void +f_nextnonblank(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + + for (lnum = tv_get_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 = (int)tv_get_number_chk(&argvars[1], NULL); + if (utf8) + buf[(*utf_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + else + buf[(*mb_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } + else + { + buf[0] = (char_u)tv_get_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 = tv_get_number_chk(&argvars[0], NULL) + | tv_get_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 = tv_get_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); + } +} + +#ifdef FEAT_PERL +/* + * "perleval()" function + */ + static void +f_perleval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + str = tv_get_string_buf(&argvars[0], buf); + do_perleval(str, rettv); +} +#endif + +#ifdef FEAT_FLOAT +/* + * "pow()" function + */ + static void +f_pow(typval_T *argvars, typval_T *rettv) +{ + float_T fx = 0.0, fy = 0.0; + + 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; +} +#endif + +/* + * "prevnonblank()" function + */ + static void +f_prevnonblank(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + + lnum = tv_get_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) +{ + char_u buf[NUMBUFLEN]; + int len; + char_u *s; + int saved_did_emsg = did_emsg; + char *fmt; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + /* Get the required length, allocate the buffer and do it for real. */ + did_emsg = FALSE; + fmt = (char *)tv_get_string_buf(&argvars[0], buf); + len = vim_vsnprintf_typval(NULL, 0, fmt, ap, argvars + 1); + if (!did_emsg) + { + s = alloc(len + 1); + if (s != NULL) + { + rettv->vval.v_string = s; + (void)vim_vsnprintf_typval((char *)s, len + 1, fmt, + ap, argvars + 1); + } + } + did_emsg |= saved_did_emsg; +} + +#ifdef FEAT_JOB_CHANNEL +/* + * "prompt_setcallback({buffer}, {callback})" function + */ + static void +f_prompt_setcallback(typval_T *argvars, typval_T *rettv UNUSED) +{ + buf_T *buf; + char_u *callback; + partial_T *partial; + + if (check_secure()) + return; + buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL) + return; + + callback = get_callback(&argvars[1], &partial); + if (callback == NULL) + return; + + free_callback(buf->b_prompt_callback, buf->b_prompt_partial); + if (partial == NULL) + buf->b_prompt_callback = vim_strsave(callback); + else + /* pointer into the partial */ + buf->b_prompt_callback = callback; + buf->b_prompt_partial = partial; +} + +/* + * "prompt_setinterrupt({buffer}, {callback})" function + */ + static void +f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv UNUSED) +{ + buf_T *buf; + char_u *callback; + partial_T *partial; + + if (check_secure()) + return; + buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL) + return; + + callback = get_callback(&argvars[1], &partial); + if (callback == NULL) + return; + + free_callback(buf->b_prompt_interrupt, buf->b_prompt_int_partial); + if (partial == NULL) + buf->b_prompt_interrupt = vim_strsave(callback); + else + /* pointer into the partial */ + buf->b_prompt_interrupt = callback; + buf->b_prompt_int_partial = partial; +} + +/* + * "prompt_setprompt({buffer}, {text})" function + */ + static void +f_prompt_setprompt(typval_T *argvars, typval_T *rettv UNUSED) +{ + buf_T *buf; + char_u *text; + + if (check_secure()) + return; + buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL) + return; + + text = tv_get_string(&argvars[1]); + vim_free(buf->b_prompt_text); + buf->b_prompt_text = vim_strsave(text); +} +#endif + +/* + * "pumvisible()" function + */ + static void +f_pumvisible(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_INS_EXPAND + if (pum_visible()) + rettv->vval.v_number = 1; +#endif +} + +#ifdef FEAT_PYTHON3 +/* + * "py3eval()" function + */ + static void +f_py3eval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + if (p_pyx == 0) + p_pyx = 3; + + str = tv_get_string_buf(&argvars[0], buf); + do_py3eval(str, rettv); +} +#endif + +#ifdef FEAT_PYTHON +/* + * "pyeval()" function + */ + static void +f_pyeval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + if (p_pyx == 0) + p_pyx = 2; + + str = tv_get_string_buf(&argvars[0], buf); + do_pyeval(str, rettv); +} +#endif + +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) +/* + * "pyxeval()" function + */ + static void +f_pyxeval(typval_T *argvars, typval_T *rettv) +{ +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + init_pyxversion(); + if (p_pyx == 2) + f_pyeval(argvars, rettv); + else + f_py3eval(argvars, rettv); +# elif defined(FEAT_PYTHON) + f_pyeval(argvars, rettv); +# elif defined(FEAT_PYTHON3) + f_py3eval(argvars, rettv); +# endif +} +#endif + +/* + * "range()" function + */ + static void +f_range(typval_T *argvars, typval_T *rettv) +{ + varnumber_T start; + varnumber_T end; + varnumber_T stride = 1; + varnumber_T i; + int error = FALSE; + + start = tv_get_number_chk(&argvars[0], &error); + if (argvars[1].v_type == VAR_UNKNOWN) + { + end = start - 1; + start = 0; + } + else + { + end = tv_get_number_chk(&argvars[1], &error); + if (argvars[2].v_type != VAR_UNKNOWN) + stride = tv_get_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 + { + if (rettv_list_alloc(rettv) == OK) + for (i = start; stride > 0 ? i <= end : i >= end; i += stride) + if (list_append_number(rettv->vval.v_list, + (varnumber_T)i) == FAIL) + break; + } +} + +/* + * "readfile()" function + */ + static void +f_readfile(typval_T *argvars, typval_T *rettv) +{ + int binary = FALSE; + int blob = 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(tv_get_string(&argvars[1]), "b") == 0) + binary = TRUE; + if (STRCMP(tv_get_string(&argvars[1]), "B") == 0) + blob = TRUE; + + if (argvars[2].v_type != VAR_UNKNOWN) + maxline = (long)tv_get_number(&argvars[2]); + } + + if (blob) + { + if (rettv_blob_alloc(rettv) == FAIL) + return; + } + else + { + if (rettv_list_alloc(rettv) == FAIL) + return; + } + + /* Always open the file in binary mode, library functions have a mind of + * their own about CR-LF conversion. */ + fname = tv_get_string(&argvars[0]); + if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) + { + semsg(_(e_notopen), *fname == NUL ? (char_u *)_("") : fname); + return; + } + + if (blob) + { + if (read_blob(fd, rettv->vval.v_blob) == FAIL) + { + emsg("cannot read file"); + blob_free(rettv->vval.v_blob); + } + fclose(fd); + 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 = vim_realloc(prev, prevlen + len + 1)) != NULL) + { + mch_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) + { + vim_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) + mch_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) + : vim_realloc(prev, prevsize); + if (newprev == NULL) + { + do_outofmem_msg((long_u)prevsize); + failed = TRUE; + break; + } + prev = newprev; + } + /* Add the line part to end of "prev". */ + mch_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); + /* readfile doc says an empty list is returned on error */ + rettv->vval.v_list = list_alloc(); + } + + vim_free(prev); + fclose(fd); +} + + static void +return_register(int regname, typval_T *rettv) +{ + char_u buf[2] = {0, 0}; + + buf[0] = (char_u)regname; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(buf); +} + +/* + * "reg_executing()" function + */ + static void +f_reg_executing(typval_T *argvars UNUSED, typval_T *rettv) +{ + return_register(reg_executing, rettv); +} + +/* + * "reg_recording()" function + */ + static void +f_reg_recording(typval_T *argvars UNUSED, typval_T *rettv) +{ + return_register(reg_recording, rettv); +} + +#if defined(FEAT_RELTIME) +/* + * Convert a List to proftime_T. + * Return FAIL when there is something wrong. + */ + static int +list2proftime(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); +# ifdef WIN3264 + tm->HighPart = n1; + tm->LowPart = n2; +# else + tm->tv_sec = n1; + tm->tv_usec = n2; +# endif + return error ? FAIL : OK; +} +#endif /* FEAT_RELTIME */ + +/* + * "reltime()" function + */ + static void +f_reltime(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_RELTIME + 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); + } + + if (rettv_list_alloc(rettv) == OK) + { + long n1, n2; + +# ifdef WIN3264 + n1 = res.HighPart; + n2 = res.LowPart; +# else + n1 = res.tv_sec; + n2 = res.tv_usec; +# endif + list_append_number(rettv->vval.v_list, (varnumber_T)n1); + list_append_number(rettv->vval.v_list, (varnumber_T)n2); + } +#endif +} + +#ifdef FEAT_FLOAT +/* + * "reltimefloat()" function + */ + static void +f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv) +{ +# ifdef FEAT_RELTIME + proftime_T tm; +# endif + + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = 0; +# ifdef FEAT_RELTIME + if (list2proftime(&argvars[0], &tm) == OK) + rettv->vval.v_float = profile_float(&tm); +# endif +} +#endif + +/* + * "reltimestr()" function + */ + static void +f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_RELTIME + proftime_T tm; +#endif + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_RELTIME + if (list2proftime(&argvars[0], &tm) == OK) + rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm)); +#endif +} + +#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) + static void +make_connection(void) +{ + if (X_DISPLAY == NULL +# ifdef FEAT_GUI + && !gui.in_use +# endif + ) + { + x_force_connect = TRUE; + setup_term_clip(); + x_force_connect = FALSE; + } +} + + static int +check_connection(void) +{ + make_connection(); + if (X_DISPLAY == NULL) + { + emsg(_("E240: No connection to the X server")); + return FAIL; + } + return OK; +} +#endif + +#ifdef FEAT_CLIENTSERVER + static void +remote_common(typval_T *argvars, typval_T *rettv, int expr) +{ + char_u *server_name; + char_u *keys; + char_u *r = NULL; + char_u buf[NUMBUFLEN]; + int timeout = 0; +# ifdef WIN32 + HWND w; +# else + Window w; +# endif + + if (check_restricted() || check_secure()) + return; + +# ifdef FEAT_X11 + if (check_connection() == FAIL) + return; +# endif + if (argvars[2].v_type != VAR_UNKNOWN + && argvars[3].v_type != VAR_UNKNOWN) + timeout = tv_get_number(&argvars[3]); + + server_name = tv_get_string_chk(&argvars[0]); + if (server_name == NULL) + return; /* type error; errmsg already given */ + keys = tv_get_string_buf(&argvars[1], buf); +# ifdef WIN32 + if (serverSendToVim(server_name, keys, &r, &w, expr, timeout, TRUE) < 0) +# else + if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout, + 0, TRUE) < 0) +# endif + { + if (r != NULL) + { + emsg((char *)r); // sending worked but evaluation failed + vim_free(r); + } + else + semsg(_("E241: Unable to send to %s"), server_name); + return; + } + + rettv->vval.v_string = r; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + dictitem_T v; + char_u str[30]; + char_u *idvar; + + idvar = tv_get_string_chk(&argvars[2]); + if (idvar != NULL && *idvar != NUL) + { + sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w); + v.di_tv.v_type = VAR_STRING; + v.di_tv.vval.v_string = vim_strsave(str); + set_var(idvar, &v.di_tv, FALSE); + vim_free(v.di_tv.vval.v_string); + } + } +} +#endif + +/* + * "remote_expr()" function + */ + static void +f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_CLIENTSERVER + remote_common(argvars, rettv, TRUE); +#endif +} + +/* + * "remote_foreground()" function + */ + static void +f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_CLIENTSERVER +# ifdef WIN32 + /* On Win32 it's done in this application. */ + { + char_u *server_name = tv_get_string_chk(&argvars[0]); + + if (server_name != NULL) + serverForeground(server_name); + } +# else + /* Send a foreground() expression to the server. */ + argvars[1].v_type = VAR_STRING; + argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()"); + argvars[2].v_type = VAR_UNKNOWN; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + remote_common(argvars, rettv, TRUE); + vim_free(argvars[1].vval.v_string); +# endif +#endif +} + + static void +f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CLIENTSERVER + dictitem_T v; + char_u *s = NULL; +# ifdef WIN32 + long_u n = 0; +# endif + char_u *serverid; + + if (check_restricted() || check_secure()) + { + rettv->vval.v_number = -1; + return; + } + serverid = tv_get_string_chk(&argvars[0]); + if (serverid == NULL) + { + rettv->vval.v_number = -1; + return; /* type error; errmsg already given */ + } +# ifdef WIN32 + sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n); + if (n == 0) + rettv->vval.v_number = -1; + else + { + s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0); + rettv->vval.v_number = (s != NULL); + } +# else + if (check_connection() == FAIL) + return; + + rettv->vval.v_number = serverPeekReply(X_DISPLAY, + serverStrToWin(serverid), &s); +# endif + + if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0) + { + char_u *retvar; + + v.di_tv.v_type = VAR_STRING; + v.di_tv.vval.v_string = vim_strsave(s); + retvar = tv_get_string_chk(&argvars[1]); + if (retvar != NULL) + set_var(retvar, &v.di_tv, FALSE); + vim_free(v.di_tv.vval.v_string); + } +#else + rettv->vval.v_number = -1; +#endif +} + + static void +f_remote_read(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *r = NULL; + +#ifdef FEAT_CLIENTSERVER + char_u *serverid = tv_get_string_chk(&argvars[0]); + + if (serverid != NULL && !check_restricted() && !check_secure()) + { + int timeout = 0; +# ifdef WIN32 + /* The server's HWND is encoded in the 'id' parameter */ + long_u n = 0; +# endif + + if (argvars[1].v_type != VAR_UNKNOWN) + timeout = tv_get_number(&argvars[1]); + +# ifdef WIN32 + sscanf((char *)serverid, SCANF_HEX_LONG_U, &n); + if (n != 0) + r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout); + if (r == NULL) +# else + if (check_connection() == FAIL + || serverReadReply(X_DISPLAY, serverStrToWin(serverid), + &r, FALSE, timeout) < 0) +# endif + emsg(_("E277: Unable to read a server reply")); + } +#endif + rettv->v_type = VAR_STRING; + rettv->vval.v_string = r; +} + +/* + * "remote_send()" function + */ + static void +f_remote_send(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_CLIENTSERVER + remote_common(argvars, rettv, FALSE); +#endif +} + +/* + * "remote_startserver()" function + */ + static void +f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_CLIENTSERVER + char_u *server = tv_get_string_chk(&argvars[0]); + + if (server == NULL) + return; /* type error; errmsg already given */ + if (serverName != NULL) + emsg(_("E941: already started a server")); + else + { +# ifdef FEAT_X11 + if (check_connection() == OK) + serverRegisterName(X_DISPLAY, server); +# else + serverSetName(server); +# endif + } +#else + emsg(_("E942: +clientserver feature not available")); +#endif +} + +/* + * "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_u *arg_errmsg = (char_u *)N_("remove() argument"); + int error = FALSE; + + if (argvars[0].v_type == VAR_DICT) + { + if (argvars[2].v_type != VAR_UNKNOWN) + semsg(_(e_toomanyarg), "remove()"); + else if ((d = argvars[0].vval.v_dict) != NULL + && !tv_check_lock(d->dv_lock, arg_errmsg, TRUE)) + { + key = tv_get_string_chk(&argvars[1]); + if (key != NULL) + { + di = dict_find(d, key, -1); + if (di == NULL) + semsg(_(e_dictkey), key); + else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE) + && !var_check_ro(di->di_flags, arg_errmsg, TRUE)) + { + *rettv = di->di_tv; + init_tv(&di->di_tv); + dictitem_remove(d, di); + } + } + } + } + else if (argvars[0].v_type == VAR_BLOB) + { + idx = (long)tv_get_number_chk(&argvars[1], &error); + if (!error) + { + blob_T *b = argvars[0].vval.v_blob; + int len = blob_len(b); + char_u *p; + + if (idx < 0) + // count from the end + idx = len + idx; + if (idx < 0 || idx >= len) + { + semsg(_(e_blobidx), idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) + { + // Remove one item, return its value. + p = (char_u *)b->bv_ga.ga_data; + rettv->vval.v_number = (varnumber_T) *(p + idx); + mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); + --b->bv_ga.ga_len; + } + else + { + blob_T *blob; + + // Remove range of items, return list with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) + return; + if (end < 0) + // count from the end + end = len + end; + if (end >= len || idx > end) + { + semsg(_(e_blobidx), end); + return; + } + blob = blob_alloc(); + if (blob == NULL) + return; + blob->bv_ga.ga_len = end - idx + 1; + if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL) + { + vim_free(blob); + return; + } + p = (char_u *)b->bv_ga.ga_data; + mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx, + (size_t)(end - idx + 1)); + ++blob->bv_refcount; + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = blob; + + mch_memmove(p + idx, p + end + 1, (size_t)(len - end)); + b->bv_ga.ga_len -= end - idx + 1; + } + } + } + else if (argvars[0].v_type != VAR_LIST) + semsg(_(e_listdictblobarg), "remove()"); + else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, arg_errmsg, TRUE)) + { + idx = (long)tv_get_number_chk(&argvars[1], &error); + if (error) + ; // type error: do nothing, errmsg already given + else if ((item = list_find(l, idx)) == NULL) + semsg(_(e_listidx), idx); + else + { + if (argvars[2].v_type == VAR_UNKNOWN) + { + /* Remove one item, return its value. */ + vimlist_remove(l, item, item); + *rettv = item->li_tv; + vim_free(item); + } + else + { + // Remove range of items, return list with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) + ; // type error: do nothing + else if ((item2 = list_find(l, end)) == NULL) + semsg(_(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 + { + vimlist_remove(l, item, item2); + if (rettv_list_alloc(rettv) == OK) + { + 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(tv_get_string(&argvars[0]), + tv_get_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 = (int)tv_get_number(&argvars[1]); + if (argvars[0].v_type == VAR_LIST) + { + if (rettv_list_alloc(rettv) == OK && 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 = tv_get_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++) + mch_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 = tv_get_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 = getnextcomp(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) + { + vim_free(p); + vim_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 = getnextcomp(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) + { + vim_free(remain); + remain = cpy; + } + } + q[-1] = NUL; + } + + q = gettail(p); + if (q > p && *q == NUL) + { + /* Ignore trailing path separator. */ + q[-1] = NUL; + q = gettail(p); + } + if (q > p && !mch_isFullName(buf)) + { + /* symlink is relative to directory of argument */ + cpy = alloc((unsigned)(STRLEN(p) + STRLEN(buf) + 1)); + if (cpy != NULL) + { + STRCPY(cpy, p); + STRCPY(gettail(cpy), buf); + vim_free(p); + p = cpy; + } + } + else + { + vim_free(p); + p = vim_strsave(buf); + } + } + + if (remain == NULL) + break; + + /* Append the first path component of "remain" to "p". */ + q = getnextcomp(remain + 1); + len = q - remain - (*q != NUL); + cpy = vim_strnsave(p, STRLEN(p) + len); + if (cpy != NULL) + { + STRNCAT(cpy, remain, len); + vim_free(p); + p = cpy; + } + /* Shorten "remain". */ + if (*q != NUL) + STRMOVE(remain, q - 1); + else + VIM_CLEAR(remain); + } + + /* 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) + { + vim_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)) + *gettail_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: + vim_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_BLOB) + { + blob_T *b = argvars[0].vval.v_blob; + int i, len = blob_len(b); + + for (i = 0; i < len / 2; i++) + { + int tmp = blob_get(b, i); + + blob_set(b, i, blob_get(b, len - i - 1)); + blob_set(b, len - i - 1, tmp); + } + rettv_blob_set(rettv, b); + return; + } + + if (argvars[0].v_type != VAR_LIST) + semsg(_(e_listblobarg), "reverse()"); + else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, + (char_u *)N_("reverse() argument"), TRUE)) + { + 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_list_set(rettv, l); + 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 */ +#define SP_COLUMN 0x80 /* start at cursor column */ + +/* + * 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 = tv_get_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; + case 'z': mask = SP_COLUMN; break; + } + if (mask == 0) + { + semsg(_(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; +#ifdef FEAT_RELTIME + long time_limit = 0; +#endif + int options = SEARCH_KEEP; + int subpatnum; + + pat = tv_get_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; + if (flags & SP_COLUMN) + options |= SEARCH_COL; + + /* Optional arguments: line number to stop searching and timeout. */ + if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) + { + lnum_stop = (long)tv_get_number_chk(&argvars[2], NULL); + if (lnum_stop < 0) + goto theend; +#ifdef FEAT_RELTIME + if (argvars[3].v_type != VAR_UNKNOWN) + { + time_limit = (long)tv_get_number_chk(&argvars[3], NULL); + if (time_limit < 0) + goto theend; + } +#endif + } + +#ifdef FEAT_RELTIME + /* Set the time limit, if there is one. */ + profile_setlimit(time_limit, &tm); +#endif + + /* + * 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))) + { + semsg(_(e_invarg2), tv_get_string(&argvars[1])); + goto theend; + } + + pos = save_cursor = curwin->w_cursor; + subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L, + options, RE_SEARCH, (linenr_T)lnum_stop, &tm, NULL); + 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; +} + +#ifdef FEAT_FLOAT + +/* + * 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 = 0.0; + + 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; +} +#endif + +/* + * "screenattr()" function + */ + static void +f_screenattr(typval_T *argvars, typval_T *rettv) +{ + int row; + int col; + int c; + + row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + col = (int)tv_get_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 = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + col = (int)tv_get_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 UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = screen_screencol() + 1; +} + +/* + * "screenrow()" function + */ + static void +f_screenrow(typval_T *argvars UNUSED, 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 = tv_get_string_chk(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) + { + locally = (int)tv_get_number_chk(&argvars[1], &error) == 0; + if (!error && argvars[2].v_type != VAR_UNKNOWN) + thisblock = (int)tv_get_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; + typval_T *skip; + int save_p_ws = p_ws; + int dir; + int flags = 0; + char_u nbuf1[NUMBUFLEN]; + char_u nbuf2[NUMBUFLEN]; + int retval = 0; /* default: FAIL */ + long lnum_stop = 0; + long time_limit = 0; + + /* Get the three pattern arguments: start, middle, end. Will result in an + * error if not a valid argument. */ + spat = tv_get_string_chk(&argvars[0]); + mpat = tv_get_string_buf_chk(&argvars[1], nbuf1); + epat = tv_get_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))) + { + semsg(_(e_invarg2), tv_get_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 = NULL; + else + { + skip = &argvars[4]; + if (skip->v_type != VAR_FUNC && skip->v_type != VAR_PARTIAL + && skip->v_type != VAR_STRING) + { + /* Type error */ + semsg(_(e_invarg2), tv_get_string(&argvars[4])); + goto theend; + } + if (argvars[5].v_type != VAR_UNKNOWN) + { + lnum_stop = (long)tv_get_number_chk(&argvars[5], NULL); + if (lnum_stop < 0) + { + semsg(_(e_invarg2), tv_get_string(&argvars[5])); + goto theend; + } +#ifdef FEAT_RELTIME + if (argvars[6].v_type != VAR_UNKNOWN) + { + time_limit = (long)tv_get_number_chk(&argvars[6], NULL); + if (time_limit < 0) + { + semsg(_(e_invarg2), tv_get_string(&argvars[6])); + goto theend; + } + } +#endif + } + } + + 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; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + 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 */ + typval_T *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 UNUSED) /* 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 use_skip = FALSE; + 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; + +#ifdef FEAT_RELTIME + /* Set the time limit, if there is one. */ + profile_setlimit(time_limit, &tm); +#endif + + /* 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) + 17)); + pat3 = alloc((unsigned)(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 25)); + if (pat2 == NULL || pat3 == NULL) + goto theend; + sprintf((char *)pat2, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); + if (*mpat == NUL) + STRCPY(pat3, pat2); + else + sprintf((char *)pat3, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", + spat, epat, mpat); + if (flags & SP_START) + options |= SEARCH_START; + + if (skip != NULL) + { + /* Empty string means to not use the skip expression. */ + if (skip->v_type == VAR_STRING || skip->v_type == VAR_FUNC) + use_skip = skip->vval.v_string != NULL + && *skip->vval.v_string != NUL; + } + + save_cursor = curwin->w_cursor; + pos = curwin->w_cursor; + CLEAR_POS(&firstpos); + CLEAR_POS(&foundpos); + pat = pat3; + for (;;) + { + n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L, + options, RE_SEARCH, lnum_stop, &tm, NULL); + if (n == FAIL || (firstpos.lnum != 0 && EQUAL_POS(pos, firstpos))) + /* didn't find it or found the first match again: FAIL */ + break; + + if (firstpos.lnum == 0) + firstpos = pos; + if (EQUAL_POS(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 (use_skip) + { + save_pos = curwin->w_cursor; + curwin->w_cursor = pos; + err = FALSE; + r = eval_expr_to_bool(skip, &err); + 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: + vim_free(pat2); + vim_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; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + 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 UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CLIENTSERVER + char_u buf[NUMBUFLEN]; + char_u *server = tv_get_string_chk(&argvars[0]); + char_u *reply = tv_get_string_buf_chk(&argvars[1], buf); + + rettv->vval.v_number = -1; + if (server == NULL || reply == NULL) + return; + if (check_restricted() || check_secure()) + return; +# ifdef FEAT_X11 + if (check_connection() == FAIL) + return; +# endif + + if (serverSendReply(server, reply) < 0) + { + emsg(_("E258: Unable to send to client")); + return; + } + rettv->vval.v_number = 0; +#else + rettv->vval.v_number = -1; +#endif +} + + static void +f_serverlist(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *r = NULL; + +#ifdef FEAT_CLIENTSERVER +# ifdef WIN32 + r = serverGetVimNames(); +# else + make_connection(); + if (X_DISPLAY != NULL) + r = serverGetVimNames(X_DISPLAY); +# endif +#endif + rettv->v_type = VAR_STRING; + rettv->vval.v_string = r; +} + +/* + * "setbufline()" function + */ + static void +f_setbufline(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + buf_T *buf; + + buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL) + rettv->vval.v_number = 1; /* FAIL */ + else + { + lnum = tv_get_lnum_buf(&argvars[1], buf); + set_buffer_lines(buf, lnum, FALSE, &argvars[2], rettv); + } +} + +/* + * "setbufvar()" function + */ + static void +f_setbufvar(typval_T *argvars, typval_T *rettv UNUSED) +{ + buf_T *buf; + char_u *varname, *bufvarname; + typval_T *varp; + char_u nbuf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + return; + (void)tv_get_number(&argvars[0]); /* issue errmsg if type error */ + varname = tv_get_string_chk(&argvars[1]); + buf = tv_get_buf(&argvars[0], FALSE); + varp = &argvars[2]; + + if (buf != NULL && varname != NULL && varp != NULL) + { + if (*varname == '&') + { + long numval; + char_u *strval; + int error = FALSE; + aco_save_T aco; + + /* set curbuf to be our buf, temporarily */ + aucmd_prepbuf(&aco, buf); + + ++varname; + numval = (long)tv_get_number_chk(varp, &error); + strval = tv_get_string_buf_chk(varp, nbuf); + if (!error && strval != NULL) + set_option_value(varname, numval, strval, OPT_LOCAL); + + /* reset notion of buffer */ + aucmd_restbuf(&aco); + } + else + { + buf_T *save_curbuf = curbuf; + + bufvarname = alloc((unsigned)STRLEN(varname) + 3); + if (bufvarname != NULL) + { + curbuf = buf; + STRCPY(bufvarname, "b:"); + STRCPY(bufvarname + 2, varname); + set_var(bufvarname, varp, TRUE); + vim_free(bufvarname); + curbuf = save_curbuf; + } + } + } +} + + static void +f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED) +{ + dict_T *d; + dictitem_T *di; + char_u *csearch; + + if (argvars[0].v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return; + } + + if ((d = argvars[0].vval.v_dict) != NULL) + { + csearch = dict_get_string(d, (char_u *)"char", FALSE); + if (csearch != NULL) + { + if (enc_utf8) + { + int pcc[MAX_MCO]; + int c = utfc_ptr2char(csearch, pcc); + + set_last_csearch(c, csearch, utfc_ptr2len(csearch)); + } + else + set_last_csearch(PTR2CHAR(csearch), + csearch, MB_PTR2LEN(csearch)); + } + + di = dict_find(d, (char_u *)"forward", -1); + if (di != NULL) + set_csearch_direction((int)tv_get_number(&di->di_tv) + ? FORWARD : BACKWARD); + + di = dict_find(d, (char_u *)"until", -1); + if (di != NULL) + set_csearch_until(!!tv_get_number(&di->di_tv)); + } +} + +/* + * "setcmdpos()" function + */ + static void +f_setcmdpos(typval_T *argvars, typval_T *rettv) +{ + int pos = (int)tv_get_number(&argvars[0]) - 1; + + if (pos >= 0) + rettv->vval.v_number = set_cmdline_pos(pos); +} + +/* + * "setfperm({fname}, {mode})" function + */ + static void +f_setfperm(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + char_u modebuf[NUMBUFLEN]; + char_u *mode_str; + int i; + int mask; + int mode = 0; + + rettv->vval.v_number = 0; + fname = tv_get_string_chk(&argvars[0]); + if (fname == NULL) + return; + mode_str = tv_get_string_buf_chk(&argvars[1], modebuf); + if (mode_str == NULL) + return; + if (STRLEN(mode_str) != 9) + { + semsg(_(e_invarg2), mode_str); + return; + } + + mask = 1; + for (i = 8; i >= 0; --i) + { + if (mode_str[i] != '-') + mode |= mask; + mask = mask << 1; + } + rettv->vval.v_number = mch_setperm(fname, mode) == OK; +} + +/* + * "setline()" function + */ + static void +f_setline(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum = tv_get_lnum(&argvars[0]); + + set_buffer_lines(curbuf, lnum, FALSE, &argvars[1], rettv); +} + +/* + * Used by "setqflist()" and "setloclist()" functions + */ + static void +set_qf_ll_list( + win_T *wp UNUSED, + typval_T *list_arg UNUSED, + typval_T *action_arg UNUSED, + typval_T *what_arg UNUSED, + typval_T *rettv) +{ +#ifdef FEAT_QUICKFIX + static char *e_invact = N_("E927: Invalid action: '%s'"); + char_u *act; + int action = 0; + static int recursive = 0; +#endif + + rettv->vval.v_number = -1; + +#ifdef FEAT_QUICKFIX + if (list_arg->v_type != VAR_LIST) + emsg(_(e_listreq)); + else if (recursive != 0) + emsg(_(e_au_recursive)); + else + { + list_T *l = list_arg->vval.v_list; + dict_T *d = NULL; + int valid_dict = TRUE; + + if (action_arg->v_type == VAR_STRING) + { + act = tv_get_string_chk(action_arg); + if (act == NULL) + return; /* type error; errmsg already given */ + if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f') && + act[1] == NUL) + action = *act; + else + semsg(_(e_invact), act); + } + else if (action_arg->v_type == VAR_UNKNOWN) + action = ' '; + else + emsg(_(e_stringreq)); + + if (action_arg->v_type != VAR_UNKNOWN + && what_arg->v_type != VAR_UNKNOWN) + { + if (what_arg->v_type == VAR_DICT) + d = what_arg->vval.v_dict; + else + { + emsg(_(e_dictreq)); + valid_dict = FALSE; + } + } + + ++recursive; + if (l != NULL && action && valid_dict && set_errorlist(wp, l, action, + (char_u *)(wp == NULL ? ":setqflist()" : ":setloclist()"), + d) == OK) + rettv->vval.v_number = 0; + --recursive; + } +#endif +} + +/* + * "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_or_id(&argvars[0]); + if (win != NULL) + set_qf_ll_list(win, &argvars[1], &argvars[2], &argvars[3], rettv); +} + +/* + * "setmatches()" function + */ + static void +f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + list_T *l; + listitem_T *li; + dict_T *d; + list_T *s = NULL; + + 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 *)"pos1", -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) + { + int i = 0; + char_u buf[5]; + dictitem_T *di; + char_u *group; + int priority; + int id; + char_u *conceal; + + d = li->li_tv.vval.v_dict; + if (dict_find(d, (char_u *)"pattern", -1) == NULL) + { + if (s == NULL) + { + s = list_alloc(); + if (s == NULL) + return; + } + + /* match from matchaddpos() */ + for (i = 1; i < 9; i++) + { + sprintf((char *)buf, (char *)"pos%d", i); + if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) + { + if (di->di_tv.v_type != VAR_LIST) + return; + + list_append_tv(s, &di->di_tv); + s->lv_refcount++; + } + else + break; + } + } + + group = dict_get_string(d, (char_u *)"group", TRUE); + priority = (int)dict_get_number(d, (char_u *)"priority"); + id = (int)dict_get_number(d, (char_u *)"id"); + conceal = dict_find(d, (char_u *)"conceal", -1) != NULL + ? dict_get_string(d, (char_u *)"conceal", TRUE) + : NULL; + if (i == 0) + { + match_add(curwin, group, + dict_get_string(d, (char_u *)"pattern", FALSE), + priority, id, NULL, conceal); + } + else + { + match_add(curwin, group, NULL, priority, id, s, conceal); + list_unref(s); + s = NULL; + } + vim_free(group); + vim_free(conceal); + + li = li->li_next; + } + rettv->vval.v_number = 0; + } +#endif +} + +/* + * "setpos()" function + */ + static void +f_setpos(typval_T *argvars, typval_T *rettv) +{ + pos_T pos; + int fnum; + char_u *name; + colnr_T curswant = -1; + + rettv->vval.v_number = -1; + name = tv_get_string_chk(argvars); + if (name != NULL) + { + if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) + { + if (--pos.col < 0) + pos.col = 0; + if (name[0] == '.' && name[1] == NUL) + { + /* set cursor; "fnum" is ignored */ + curwin->w_cursor = pos; + if (curswant >= 0) + { + curwin->w_curswant = curswant - 1; + curwin->w_set_curswant = FALSE; + } + check_cursor(); + rettv->vval.v_number = 0; + } + 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], &argvars[2], 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 = tv_get_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 = '"'; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + stropt = tv_get_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; + } + } + + if (argvars[1].v_type == VAR_LIST) + { + char_u **lstval; + char_u **allocval; + char_u buf[NUMBUFLEN]; + char_u **curval; + char_u **curallocval; + list_T *ll = argvars[1].vval.v_list; + listitem_T *li; + int len; + + /* If the list is NULL handle like an empty list. */ + len = ll == NULL ? 0 : ll->lv_len; + + /* First half: use for pointers to result lines; second half: use for + * pointers to allocated copies. */ + lstval = (char_u **)alloc(sizeof(char_u *) * ((len + 1) * 2)); + if (lstval == NULL) + return; + curval = lstval; + allocval = lstval + len + 2; + curallocval = allocval; + + for (li = ll == NULL ? NULL : ll->lv_first; li != NULL; + li = li->li_next) + { + strval = tv_get_string_buf_chk(&li->li_tv, buf); + if (strval == NULL) + goto free_lstval; + if (strval == buf) + { + /* Need to make a copy, next tv_get_string_buf_chk() will + * overwrite the string. */ + strval = vim_strsave(buf); + if (strval == NULL) + goto free_lstval; + *curallocval++ = strval; + } + *curval++ = strval; + } + *curval++ = NULL; + + write_reg_contents_lst(regname, lstval, -1, + append, yank_type, block_len); +free_lstval: + while (curallocval > allocval) + vim_free(*--curallocval); + vim_free(lstval); + } + else + { + strval = tv_get_string_chk(&argvars[1]); + if (strval == NULL) + return; + 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)tv_get_number_chk(&argvars[0], NULL)); + varname = tv_get_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); + vim_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); +} + +/* + * "settagstack()" function + */ + static void +f_settagstack(typval_T *argvars, typval_T *rettv) +{ + static char *e_invact2 = N_("E962: Invalid action: '%s'"); + win_T *wp; + dict_T *d; + int action = 'r'; + + rettv->vval.v_number = -1; + + // first argument: window number or id + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) + return; + + // second argument: dict with items to set in the tag stack + if (argvars[1].v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return; + } + d = argvars[1].vval.v_dict; + if (d == NULL) + return; + + // third argument: action - 'a' for append and 'r' for replace. + // default is to replace the stack. + if (argvars[2].v_type == VAR_UNKNOWN) + action = 'r'; + else if (argvars[2].v_type == VAR_STRING) + { + char_u *actstr; + actstr = tv_get_string_chk(&argvars[2]); + if (actstr == NULL) + return; + if ((*actstr == 'r' || *actstr == 'a') && actstr[1] == NUL) + action = *actstr; + else + { + semsg(_(e_invact2), actstr); + return; + } + } + else + { + emsg(_(e_stringreq)); + return; + } + + if (set_tagstack(wp, d, action) == OK) + rettv->vval.v_number = 0; +} + +/* + * "setwinvar()" function + */ + static void +f_setwinvar(typval_T *argvars, typval_T *rettv) +{ + setwinvar(argvars, rettv, 0); +} + +#ifdef FEAT_CRYPT +/* + * "sha256({string})" function + */ + static void +f_sha256(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + + p = tv_get_string(&argvars[0]); + rettv->vval.v_string = vim_strsave( + sha256_bytes(p, (int)STRLEN(p), NULL, 0)); + rettv->v_type = VAR_STRING; +} +#endif /* FEAT_CRYPT */ + +/* + * "shellescape({string})" function + */ + static void +f_shellescape(typval_T *argvars, typval_T *rettv) +{ + int do_special = non_zero_arg(&argvars[1]); + + rettv->vval.v_string = vim_strsave_shellescape( + tv_get_string(&argvars[0]), do_special, do_special); + rettv->v_type = VAR_STRING; +} + +/* + * shiftwidth() function + */ + static void +f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = 0; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + long col; + + col = (long)tv_get_number_chk(argvars, NULL); + if (col < 0) + return; // type error; errmsg already given +#ifdef FEAT_VARTABS + rettv->vval.v_number = get_sw_value_col(curbuf, col); + return; +#endif + } + + rettv->vval.v_number = get_sw_value(curbuf); +} + +#ifdef FEAT_SIGNS +/* + * "sign_define()" function + */ + static void +f_sign_define(typval_T *argvars, typval_T *rettv) +{ + char_u *name; + dict_T *dict; + char_u *icon = NULL; + char_u *linehl = NULL; + char_u *text = NULL; + char_u *texthl = NULL; + + rettv->vval.v_number = -1; + + name = tv_get_string_chk(&argvars[0]); + if (name == NULL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return; + } + + // sign attributes + dict = argvars[1].vval.v_dict; + if (dict_find(dict, (char_u *)"icon", -1) != NULL) + icon = dict_get_string(dict, (char_u *)"icon", TRUE); + if (dict_find(dict, (char_u *)"linehl", -1) != NULL) + linehl = dict_get_string(dict, (char_u *)"linehl", TRUE); + if (dict_find(dict, (char_u *)"text", -1) != NULL) + text = dict_get_string(dict, (char_u *)"text", TRUE); + if (dict_find(dict, (char_u *)"texthl", -1) != NULL) + texthl = dict_get_string(dict, (char_u *)"texthl", TRUE); + } + + if (sign_define_by_name(name, icon, linehl, text, texthl) == OK) + rettv->vval.v_number = 0; + + vim_free(icon); + vim_free(linehl); + vim_free(text); + vim_free(texthl); +} + +/* + * "sign_getdefined()" function + */ + static void +f_sign_getdefined(typval_T *argvars, typval_T *rettv) +{ + char_u *name = NULL; + + if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + name = tv_get_string(&argvars[0]); + + sign_getlist(name, rettv->vval.v_list); +} + +/* + * "sign_getplaced()" function + */ + static void +f_sign_getplaced(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = NULL; + dict_T *dict; + dictitem_T *di; + linenr_T lnum = 0; + int sign_id = 0; + char_u *group = NULL; + int notanum = FALSE; + + if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + // get signs placed in the specified buffer + buf = get_buf_arg(&argvars[0]); + if (buf == NULL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type != VAR_DICT || + ((dict = argvars[1].vval.v_dict) == NULL)) + { + emsg(_(e_dictreq)); + return; + } + if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL) + { + // get signs placed at this line + (void)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) + return; + lnum = tv_get_lnum(&di->di_tv); + } + if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL) + { + // get sign placed with this identifier + sign_id = (int)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) + return; + } + if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL) + { + group = tv_get_string_chk(&di->di_tv); + if (group == NULL) + return; + if (*group == '\0') // empty string means global group + group = NULL; + } + } + } + + sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list); +} + +/* + * "sign_jump()" function + */ + static void +f_sign_jump(typval_T *argvars, typval_T *rettv) +{ + int sign_id; + char_u *sign_group = NULL; + buf_T *buf; + int notanum = FALSE; + + rettv->vval.v_number = -1; + + // Sign identifer + sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); + if (notanum) + return; + if (sign_id <= 0) + { + emsg(_(e_invarg)); + return; + } + + // Sign group + sign_group = tv_get_string_chk(&argvars[1]); + if (sign_group == NULL) + return; + if (sign_group[0] == '\0') + sign_group = NULL; // global sign group + else + { + sign_group = vim_strsave(sign_group); + if (sign_group == NULL) + return; + } + + // Buffer to place the sign + buf = get_buf_arg(&argvars[2]); + if (buf == NULL) + goto cleanup; + + rettv->vval.v_number = sign_jump(sign_id, sign_group, buf); + +cleanup: + vim_free(sign_group); +} + +/* + * "sign_place()" function + */ + static void +f_sign_place(typval_T *argvars, typval_T *rettv) +{ + int sign_id; + char_u *group = NULL; + char_u *sign_name; + buf_T *buf; + dict_T *dict; + dictitem_T *di; + linenr_T lnum = 0; + int prio = SIGN_DEF_PRIO; + int notanum = FALSE; + + rettv->vval.v_number = -1; + + // Sign identifer + sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); + if (notanum) + return; + if (sign_id < 0) + { + emsg(_(e_invarg)); + return; + } + + // Sign group + group = tv_get_string_chk(&argvars[1]); + if (group == NULL) + return; + if (group[0] == '\0') + group = NULL; // global sign group + else + { + group = vim_strsave(group); + if (group == NULL) + return; + } + + // Sign name + sign_name = tv_get_string_chk(&argvars[2]); + if (sign_name == NULL) + goto cleanup; + + // Buffer to place the sign + buf = get_buf_arg(&argvars[3]); + if (buf == NULL) + goto cleanup; + + if (argvars[4].v_type != VAR_UNKNOWN) + { + if (argvars[4].v_type != VAR_DICT || + ((dict = argvars[4].vval.v_dict) == NULL)) + { + emsg(_(e_dictreq)); + goto cleanup; + } + + // Line number where the sign is to be placed + if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL) + { + (void)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) + goto cleanup; + lnum = tv_get_lnum(&di->di_tv); + } + if ((di = dict_find(dict, (char_u *)"priority", -1)) != NULL) + { + // Sign priority + prio = (int)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) + goto cleanup; + } + } + + if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK) + rettv->vval.v_number = sign_id; + +cleanup: + vim_free(group); +} + +/* + * "sign_undefine()" function + */ + static void +f_sign_undefine(typval_T *argvars, typval_T *rettv) +{ + char_u *name; + + rettv->vval.v_number = -1; + + if (argvars[0].v_type == VAR_UNKNOWN) + { + // Free all the signs + free_signs(); + rettv->vval.v_number = 0; + } + else + { + // Free only the specified sign + name = tv_get_string_chk(&argvars[0]); + if (name == NULL) + return; + + if (sign_undefine_by_name(name) == OK) + rettv->vval.v_number = 0; + } +} + +/* + * "sign_unplace()" function + */ + static void +f_sign_unplace(typval_T *argvars, typval_T *rettv) +{ + dict_T *dict; + dictitem_T *di; + int sign_id = 0; + buf_T *buf = NULL; + char_u *group = NULL; + + rettv->vval.v_number = -1; + + if (argvars[0].v_type != VAR_STRING) + { + emsg(_(e_invarg)); + return; + } + + group = tv_get_string(&argvars[0]); + if (group[0] == '\0') + group = NULL; // global sign group + else + { + group = vim_strsave(group); + if (group == NULL) + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + goto cleanup; + } + dict = argvars[1].vval.v_dict; + + if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL) + { + buf = get_buf_arg(&di->di_tv); + if (buf == NULL) + goto cleanup; + } + if (dict_find(dict, (char_u *)"id", -1) != NULL) + sign_id = dict_get_number(dict, (char_u *)"id"); + } + + if (buf == NULL) + { + // Delete the sign in all the buffers + FOR_ALL_BUFFERS(buf) + if (sign_unplace(sign_id, group, buf, 0) == OK) + rettv->vval.v_number = 0; + } + else + { + if (sign_unplace(sign_id, group, buf, 0) == OK) + rettv->vval.v_number = 0; + } + +cleanup: + vim_free(group); +} +#endif + +/* + * "simplify()" function + */ + static void +f_simplify(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + + p = tv_get_string(&argvars[0]); + rettv->vval.v_string = vim_strsave(p); + simplify_filename(rettv->vval.v_string); /* simplify in place */ + rettv->v_type = VAR_STRING; +} + +#ifdef FEAT_FLOAT +/* + * "sin()" function + */ + static void +f_sin(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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 = 0.0; + + 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; +} +#endif + +static int +#ifdef __BORLANDC__ + _RTLENTRYF +#endif + item_compare(const void *s1, const void *s2); +static int +#ifdef __BORLANDC__ + _RTLENTRYF +#endif + item_compare2(const void *s1, const void *s2); + +/* struct used in the array that's given to qsort() */ +typedef struct +{ + listitem_T *item; + int idx; +} sortItem_T; + +/* struct storing information about current sort */ +typedef struct +{ + int item_compare_ic; + int item_compare_numeric; + int item_compare_numbers; +#ifdef FEAT_FLOAT + int item_compare_float; +#endif + char_u *item_compare_func; + partial_T *item_compare_partial; + dict_T *item_compare_selfdict; + int item_compare_func_err; + int item_compare_keep_zero; +} sortinfo_T; +static sortinfo_T *sortinfo = NULL; +#define ITEM_COMPARE_FAIL 999 + +/* + * Compare functions for f_sort() and f_uniq() below. + */ + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +item_compare(const void *s1, const void *s2) +{ + sortItem_T *si1, *si2; + typval_T *tv1, *tv2; + char_u *p1, *p2; + char_u *tofree1 = NULL, *tofree2 = NULL; + int res; + char_u numbuf1[NUMBUFLEN]; + char_u numbuf2[NUMBUFLEN]; + + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + tv1 = &si1->item->li_tv; + tv2 = &si2->item->li_tv; + + if (sortinfo->item_compare_numbers) + { + varnumber_T v1 = tv_get_number(tv1); + varnumber_T v2 = tv_get_number(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + +#ifdef FEAT_FLOAT + if (sortinfo->item_compare_float) + { + float_T v1 = tv_get_float(tv1); + float_T v2 = tv_get_float(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } +#endif + + /* tv2string() puts quotes around a string and allocates memory. Don't do + * that for string variables. Use a single quote when comparing with a + * non-string to do what the docs promise. */ + if (tv1->v_type == VAR_STRING) + { + if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) + p1 = (char_u *)"'"; + else + p1 = tv1->vval.v_string; + } + else + p1 = tv2string(tv1, &tofree1, numbuf1, 0); + if (tv2->v_type == VAR_STRING) + { + if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) + p2 = (char_u *)"'"; + else + p2 = tv2->vval.v_string; + } + else + p2 = tv2string(tv2, &tofree2, numbuf2, 0); + if (p1 == NULL) + p1 = (char_u *)""; + if (p2 == NULL) + p2 = (char_u *)""; + if (!sortinfo->item_compare_numeric) + { + if (sortinfo->item_compare_ic) + res = STRICMP(p1, p2); + else + res = STRCMP(p1, p2); + } + else + { + double n1, n2; + n1 = strtod((char *)p1, (char **)&p1); + n2 = strtod((char *)p2, (char **)&p2); + res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; + } + + /* When the result would be zero, compare the item indexes. Makes the + * sort stable. */ + if (res == 0 && !sortinfo->item_compare_keep_zero) + res = si1->idx > si2->idx ? 1 : -1; + + vim_free(tofree1); + vim_free(tofree2); + return res; +} + + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +item_compare2(const void *s1, const void *s2) +{ + sortItem_T *si1, *si2; + int res; + typval_T rettv; + typval_T argv[3]; + int dummy; + char_u *func_name; + partial_T *partial = sortinfo->item_compare_partial; + + /* shortcut after failure in previous call; compare all items equal */ + if (sortinfo->item_compare_func_err) + return 0; + + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + + if (partial == NULL) + func_name = sortinfo->item_compare_func; + else + func_name = partial_name(partial); + + /* 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(&si1->item->li_tv, &argv[0]); + copy_tv(&si2->item->li_tv, &argv[1]); + + rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ + res = call_func(func_name, (int)STRLEN(func_name), + &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, + partial, sortinfo->item_compare_selfdict); + clear_tv(&argv[0]); + clear_tv(&argv[1]); + + if (res == FAIL) + res = ITEM_COMPARE_FAIL; + else + res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); + if (sortinfo->item_compare_func_err) + res = ITEM_COMPARE_FAIL; /* return value has wrong type */ + clear_tv(&rettv); + + /* When the result would be zero, compare the pointers themselves. Makes + * the sort stable. */ + if (res == 0 && !sortinfo->item_compare_keep_zero) + res = si1->idx > si2->idx ? 1 : -1; + + return res; +} + +/* + * "sort({list})" function + */ + static void +do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) +{ + list_T *l; + listitem_T *li; + sortItem_T *ptrs; + sortinfo_T *old_sortinfo; + sortinfo_T info; + long len; + long i; + + /* Pointer to current info struct used in compare function. Save and + * restore the current one for nested calls. */ + old_sortinfo = sortinfo; + sortinfo = &info; + + if (argvars[0].v_type != VAR_LIST) + semsg(_(e_listarg), sort ? "sort()" : "uniq()"); + else + { + l = argvars[0].vval.v_list; + if (l == NULL || tv_check_lock(l->lv_lock, + (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), + TRUE)) + goto theend; + rettv_list_set(rettv, l); + + len = list_len(l); + if (len <= 1) + goto theend; /* short list sorts pretty quickly */ + + info.item_compare_ic = FALSE; + info.item_compare_numeric = FALSE; + info.item_compare_numbers = FALSE; +#ifdef FEAT_FLOAT + info.item_compare_float = FALSE; +#endif + info.item_compare_func = NULL; + info.item_compare_partial = NULL; + info.item_compare_selfdict = NULL; + if (argvars[1].v_type != VAR_UNKNOWN) + { + /* optional second argument: {func} */ + if (argvars[1].v_type == VAR_FUNC) + info.item_compare_func = argvars[1].vval.v_string; + else if (argvars[1].v_type == VAR_PARTIAL) + info.item_compare_partial = argvars[1].vval.v_partial; + else + { + int error = FALSE; + + i = (long)tv_get_number_chk(&argvars[1], &error); + if (error) + goto theend; /* type error; errmsg already given */ + if (i == 1) + info.item_compare_ic = TRUE; + else if (argvars[1].v_type != VAR_NUMBER) + info.item_compare_func = tv_get_string(&argvars[1]); + else if (i != 0) + { + emsg(_(e_invarg)); + goto theend; + } + if (info.item_compare_func != NULL) + { + if (*info.item_compare_func == NUL) + { + /* empty string means default sort */ + info.item_compare_func = NULL; + } + else if (STRCMP(info.item_compare_func, "n") == 0) + { + info.item_compare_func = NULL; + info.item_compare_numeric = TRUE; + } + else if (STRCMP(info.item_compare_func, "N") == 0) + { + info.item_compare_func = NULL; + info.item_compare_numbers = TRUE; + } +#ifdef FEAT_FLOAT + else if (STRCMP(info.item_compare_func, "f") == 0) + { + info.item_compare_func = NULL; + info.item_compare_float = TRUE; + } +#endif + else if (STRCMP(info.item_compare_func, "i") == 0) + { + info.item_compare_func = NULL; + info.item_compare_ic = TRUE; + } + } + } + + if (argvars[2].v_type != VAR_UNKNOWN) + { + /* optional third argument: {dict} */ + if (argvars[2].v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + goto theend; + } + info.item_compare_selfdict = argvars[2].vval.v_dict; + } + } + + /* Make an array with each entry pointing to an item in the List. */ + ptrs = (sortItem_T *)alloc((int)(len * sizeof(sortItem_T))); + if (ptrs == NULL) + goto theend; + + 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].item = li; + ptrs[i].idx = i; + ++i; + } + + info.item_compare_func_err = FALSE; + info.item_compare_keep_zero = FALSE; + /* test the compare function */ + if ((info.item_compare_func != NULL + || info.item_compare_partial != NULL) + && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) + == ITEM_COMPARE_FAIL) + emsg(_("E702: Sort compare function failed")); + else + { + /* Sort the array with item pointers. */ + qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), + info.item_compare_func == NULL + && info.item_compare_partial == NULL + ? item_compare : item_compare2); + + if (!info.item_compare_func_err) + { + /* Clear the List and append the items in sorted order. */ + l->lv_first = l->lv_last = l->lv_idx_item = NULL; + l->lv_len = 0; + for (i = 0; i < len; ++i) + list_append(l, ptrs[i].item); + } + } + } + else + { + int (*item_compare_func_ptr)(const void *, const void *); + + /* f_uniq(): ptrs will be a stack of items to remove */ + info.item_compare_func_err = FALSE; + info.item_compare_keep_zero = TRUE; + item_compare_func_ptr = info.item_compare_func != NULL + || info.item_compare_partial != NULL + ? item_compare2 : item_compare; + + for (li = l->lv_first; li != NULL && li->li_next != NULL; + li = li->li_next) + { + if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) + == 0) + ptrs[i++].item = li; + if (info.item_compare_func_err) + { + emsg(_("E882: Uniq compare function failed")); + break; + } + } + + if (!info.item_compare_func_err) + { + while (--i >= 0) + { + li = ptrs[i].item->li_next; + ptrs[i].item->li_next = li->li_next; + if (li->li_next != NULL) + li->li_next->li_prev = ptrs[i].item; + else + l->lv_last = ptrs[i].item; + list_fix_watch(l, li); + listitem_free(li); + l->lv_len--; + } + } + } + + vim_free(ptrs); + } +theend: + sortinfo = old_sortinfo; +} + +/* + * "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 = tv_get_string(&argvars[0]); +#ifdef FEAT_SPELL + rettv->vval.v_string = eval_soundfold(s); +#else + rettv->vval.v_string = vim_strsave(s); +#endif +} + +/* + * "spellbadword()" function + */ + static void +f_spellbadword(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *word = (char_u *)""; + hlf_T attr = HLF_COUNT; + int len = 0; + + if (rettv_list_alloc(rettv) == FAIL) + return; + +#ifdef FEAT_SPELL + 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(); + curwin->w_set_curswant = TRUE; + } + } + else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) + { + char_u *str = tv_get_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; + capcol -= len; + } + } + } +#endif + + 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 UNUSED, typval_T *rettv) +{ +#ifdef FEAT_SPELL + char_u *str; + int typeerr = FALSE; + int maxcount; + garray_T ga; + int i; + listitem_T *li; + int need_capital = FALSE; +#endif + + if (rettv_list_alloc(rettv) == FAIL) + return; + +#ifdef FEAT_SPELL + if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) + { + str = tv_get_string(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) + { + maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr); + if (maxcount <= 0) + return; + if (argvars[2].v_type != VAR_UNKNOWN) + { + need_capital = (int)tv_get_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) + vim_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); + } +#endif +} + + 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 = tv_get_string(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) + { + pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) + typeerr = TRUE; + if (argvars[2].v_type != VAR_UNKNOWN) + keepempty = (int)tv_get_number_chk(&argvars[2], &typeerr); + } + if (pat == NULL || *pat == NUL) + pat = (char_u *)"[\\x01- ]\\+"; + + if (rettv_list_alloc(rettv) == FAIL) + return; + 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])) + { + if (list_append_string(rettv->vval.v_list, str, + (int)(end - str)) == FAIL) + break; + } + 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; +} + +#ifdef FEAT_FLOAT +/* + * "sqrt()" function + */ + static void +f_sqrt(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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(tv_get_string(&argvars[0])); + int isneg = (*p == '-'); + + if (*p == '+' || *p == '-') + p = skipwhite(p + 1); + (void)string2float(p, &rettv->vval.v_float); + if (isneg) + rettv->vval.v_float *= -1; + rettv->v_type = VAR_FLOAT; +} +#endif + +/* + * "str2nr()" function + */ + static void +f_str2nr(typval_T *argvars, typval_T *rettv) +{ + int base = 10; + char_u *p; + varnumber_T n; + int what; + int isneg; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + base = (int)tv_get_number(&argvars[1]); + if (base != 2 && base != 8 && base != 10 && base != 16) + { + emsg(_(e_invarg)); + return; + } + } + + p = skipwhite(tv_get_string(&argvars[0])); + isneg = (*p == '-'); + if (*p == '+' || *p == '-') + p = skipwhite(p + 1); + switch (base) + { + case 2: what = STR2NR_BIN + STR2NR_FORCE; break; + case 8: what = STR2NR_OCT + STR2NR_FORCE; break; + case 16: what = STR2NR_HEX + STR2NR_FORCE; break; + default: what = 0; + } + vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); + if (isneg) + rettv->vval.v_number = -n; + else + rettv->vval.v_number = n; + +} + +#ifdef HAVE_STRFTIME +/* + * "strftime({format}[, {time}])" function + */ + static void +f_strftime(typval_T *argvars, typval_T *rettv) +{ + char_u result_buf[256]; + struct tm *curtime; + time_t seconds; + char_u *p; + + rettv->v_type = VAR_STRING; + + p = tv_get_string(&argvars[0]); + if (argvars[1].v_type == VAR_UNKNOWN) + seconds = time(NULL); + else + seconds = (time_t)tv_get_number(&argvars[1]); + curtime = localtime(&seconds); + /* MSVC returns NULL for an invalid value of seconds. */ + if (curtime == 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); + else + result_buf[0] = NUL; + + if (conv.vc_type != CONV_NONE) + vim_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); + vim_free(enc); + } +} +#endif + +/* + * "strgetchar()" function + */ + static void +f_strgetchar(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + int len; + int error = FALSE; + int charidx; + int byteidx = 0; + + rettv->vval.v_number = -1; + str = tv_get_string_chk(&argvars[0]); + if (str == NULL) + return; + len = (int)STRLEN(str); + charidx = (int)tv_get_number_chk(&argvars[1], &error); + if (error) + return; + + while (charidx >= 0 && byteidx < len) + { + if (charidx == 0) + { + rettv->vval.v_number = mb_ptr2char(str + byteidx); + break; + } + --charidx; + byteidx += MB_CPTR2LEN(str + byteidx); + } +} + +/* + * "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 = tv_get_string_chk(&argvars[1]); + save_haystack = haystack = tv_get_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 = (int)tv_get_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 + */ + 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, + get_copyID()); + /* 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( + tv_get_string(&argvars[0]))); +} + +/* + * "strchars()" function + */ + static void +f_strchars(typval_T *argvars, typval_T *rettv) +{ + char_u *s = tv_get_string(&argvars[0]); + int skipcc = 0; + varnumber_T len = 0; + int (*func_mb_ptr2char_adv)(char_u **pp); + + if (argvars[1].v_type != VAR_UNKNOWN) + skipcc = (int)tv_get_number_chk(&argvars[1], NULL); + if (skipcc < 0 || skipcc > 1) + emsg(_(e_invarg)); + else + { + func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; + while (*s != NUL) + { + func_mb_ptr2char_adv(&s); + ++len; + } + rettv->vval.v_number = len; + } +} + +/* + * "strdisplaywidth()" function + */ + static void +f_strdisplaywidth(typval_T *argvars, typval_T *rettv) +{ + char_u *s = tv_get_string(&argvars[0]); + int col = 0; + + if (argvars[1].v_type != VAR_UNKNOWN) + col = (int)tv_get_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 = tv_get_string(&argvars[0]); + + rettv->vval.v_number = (varnumber_T)(mb_string2cells(s, -1)); +} + +/* + * "strcharpart()" function + */ + static void +f_strcharpart(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + int nchar; + int nbyte = 0; + int charlen; + int len = 0; + int slen; + int error = FALSE; + + p = tv_get_string(&argvars[0]); + slen = (int)STRLEN(p); + + nchar = (int)tv_get_number_chk(&argvars[1], &error); + if (!error) + { + if (nchar > 0) + while (nchar > 0 && nbyte < slen) + { + nbyte += MB_CPTR2LEN(p + nbyte); + --nchar; + } + else + nbyte = nchar; + if (argvars[2].v_type != VAR_UNKNOWN) + { + charlen = (int)tv_get_number(&argvars[2]); + while (charlen > 0 && nbyte + len < slen) + { + int off = nbyte + len; + + if (off < 0) + len += 1; + else + len += MB_CPTR2LEN(p + off); + --charlen; + } + } + else + len = slen - nbyte; /* default: all bytes that are available. */ + } + + /* + * Only return the overlap between the specified part and the actual + * string. + */ + if (nbyte < 0) + { + len += nbyte; + nbyte = 0; + } + else if (nbyte > slen) + nbyte = slen; + if (len < 0) + len = 0; + else if (nbyte + len > slen) + len = slen - nbyte; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strnsave(p + nbyte, len); +} + +/* + * "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 = tv_get_string(&argvars[0]); + slen = (int)STRLEN(p); + + n = (int)tv_get_number_chk(&argvars[1], &error); + if (error) + len = 0; + else if (argvars[2].v_type != VAR_UNKNOWN) + len = (int)tv_get_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 = tv_get_string_chk(&argvars[1]); + haystack = tv_get_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 = (int)tv_get_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 != '\0'; ++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(tv_get_string(&argvars[0])); +} + +/* + * "submatch()" function + */ + static void +f_submatch(typval_T *argvars, typval_T *rettv) +{ + int error = FALSE; + int no; + int retList = 0; + + no = (int)tv_get_number_chk(&argvars[0], &error); + if (error) + return; + if (no < 0 || no >= NSUBEXP) + { + semsg(_("E935: invalid submatch number: %d"), no); + return; + } + if (argvars[1].v_type != VAR_UNKNOWN) + retList = (int)tv_get_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 = tv_get_string_chk(&argvars[0]); + char_u *pat = tv_get_string_buf_chk(&argvars[1], patbuf); + char_u *sub = NULL; + typval_T *expr = NULL; + char_u *flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); + + if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) + expr = &argvars[2]; + else + sub = tv_get_string_buf_chk(&argvars[2], subbuf); + + rettv->v_type = VAR_STRING; + if (str == NULL || pat == NULL || (sub == NULL && expr == NULL) + || flg == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg); +} + +/* + * "swapinfo(swap_filename)" function + */ + static void +f_swapinfo(typval_T *argvars, typval_T *rettv) +{ + if (rettv_dict_alloc(rettv) == OK) + get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict); +} + +/* + * "swapname(expr)" function + */ + static void +f_swapname(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + + rettv->v_type = VAR_STRING; + buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL || buf->b_ml.ml_mfp == NULL + || buf->b_ml.ml_mfp->mf_fname == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname); +} + +/* + * "synID(lnum, col, trans)" function + */ + static void +f_synID(typval_T *argvars UNUSED, typval_T *rettv) +{ + int id = 0; +#ifdef FEAT_SYN_HL + linenr_T lnum; + colnr_T col; + int trans; + int transerr = FALSE; + + lnum = tv_get_lnum(argvars); /* -1 on type error */ + col = (linenr_T)tv_get_number(&argvars[1]) - 1; /* -1 on type error */ + trans = (int)tv_get_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); +#endif + + rettv->vval.v_number = id; +} + +/* + * "synIDattr(id, what [, mode])" function + */ + static void +f_synIDattr(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *p = NULL; +#ifdef FEAT_SYN_HL + int id; + char_u *what; + char_u *mode; + char_u modebuf[NUMBUFLEN]; + int modec; + + id = (int)tv_get_number(&argvars[0]); + what = tv_get_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) + { + mode = tv_get_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 defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + if (USE_24BIT) + modec = 'g'; + else +#endif + 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_ext(NULL, id - 1, FALSE); + 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); + /* strikeout */ + else if (TOLOWER_ASC(what[1]) == 't' && + TOLOWER_ASC(what[2]) == 'r') + p = highlight_has_attr(id, HL_STRIKETHROUGH, 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); +#endif + rettv->v_type = VAR_STRING; + rettv->vval.v_string = p; +} + +/* + * "synIDtrans(id)" function + */ + static void +f_synIDtrans(typval_T *argvars UNUSED, typval_T *rettv) +{ + int id; + +#ifdef FEAT_SYN_HL + id = (int)tv_get_number(&argvars[0]); + + if (id > 0) + id = syn_get_final_id(id); + else +#endif + id = 0; + + rettv->vval.v_number = id; +} + +/* + * "synconcealed(lnum, col)" function + */ + static void +f_synconcealed(typval_T *argvars UNUSED, typval_T *rettv) +{ +#if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL) + linenr_T lnum; + colnr_T col; + int syntax_flags = 0; + int cchar; + int matchid = 0; + char_u str[NUMBUFLEN]; +#endif + + rettv_list_set(rettv, NULL); + +#if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL) + lnum = tv_get_lnum(argvars); /* -1 on type error */ + col = (colnr_T)tv_get_number(&argvars[1]) - 1; /* -1 on type error */ + + vim_memset(str, NUL, sizeof(str)); + + if (rettv_list_alloc(rettv) != FAIL) + { + 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) + cchar = (lcs_conceal == NUL) ? ' ' : 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); + } +#endif +} + +/* + * "synstack(lnum, col)" function + */ + static void +f_synstack(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_SYN_HL + linenr_T lnum; + colnr_T col; + int i; + int id; +#endif + + rettv_list_set(rettv, NULL); + +#ifdef FEAT_SYN_HL + lnum = tv_get_lnum(argvars); /* -1 on type error */ + col = (colnr_T)tv_get_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) != FAIL) + { + (void)syn_get_id(curwin, lnum, (colnr_T)col, FALSE, NULL, TRUE); + for (i = 0; ; ++i) + { + id = syn_get_stack_item(i); + if (id < 0) + break; + if (list_append_number(rettv->vval.v_list, id) == FAIL) + break; + } + } +#endif +} + + static void +get_cmd_output_as_rettv( + typval_T *argvars, + typval_T *rettv, + int retlist) +{ + char_u *res = NULL; + char_u *p; + char_u *infile = NULL; + int err = FALSE; + FILE *fd; + list_T *list = NULL; + int flags = SHELL_SILENT; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (check_restricted() || check_secure()) + goto errret; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + /* + * Write the text to a temp file, to be used for input of the shell + * command. + */ + if ((infile = vim_tempname('i', TRUE)) == NULL) + { + emsg(_(e_notmp)); + goto errret; + } + + fd = mch_fopen((char *)infile, WRITEBIN); + if (fd == NULL) + { + semsg(_(e_notopen), infile); + goto errret; + } + if (argvars[1].v_type == VAR_NUMBER) + { + linenr_T lnum; + buf_T *buf; + + buf = buflist_findnr(argvars[1].vval.v_number); + if (buf == NULL) + { + semsg(_(e_nobufnr), argvars[1].vval.v_number); + fclose(fd); + goto errret; + } + + for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) + { + for (p = ml_get_buf(buf, lnum, FALSE); *p != NUL; ++p) + if (putc(*p == '\n' ? NUL : *p, fd) == EOF) + { + err = TRUE; + break; + } + if (putc(NL, fd) == EOF) + { + err = TRUE; + break; + } + } + } + else if (argvars[1].v_type == VAR_LIST) + { + if (write_list(fd, argvars[1].vval.v_list, TRUE) == FAIL) + err = TRUE; + } + else + { + size_t len; + char_u buf[NUMBUFLEN]; + + p = tv_get_string_buf_chk(&argvars[1], buf); + if (p == NULL) + { + fclose(fd); + goto errret; /* type error; errmsg already given */ + } + len = STRLEN(p); + if (len > 0 && fwrite(p, len, 1, fd) != 1) + err = TRUE; + } + if (fclose(fd) != 0) + err = TRUE; + if (err) + { + emsg(_("E677: Error writing temp file")); + goto errret; + } + } + + /* Omit SHELL_COOKED when invoked with ":silent". Avoids that the shell + * echoes typeahead, that messes up the display. */ + if (!msg_silent) + flags += SHELL_COOKED; + + if (retlist) + { + int len; + listitem_T *li; + char_u *s = NULL; + char_u *start; + char_u *end; + int i; + + res = get_cmd_output(tv_get_string(&argvars[0]), infile, flags, &len); + if (res == NULL) + goto errret; + + list = list_alloc(); + if (list == NULL) + goto errret; + + for (i = 0; i < len; ++i) + { + start = res + i; + while (i < len && res[i] != NL) + ++i; + end = res + i; + + s = alloc((unsigned)(end - start + 1)); + if (s == NULL) + goto errret; + + for (p = s; start < end; ++p, ++start) + *p = *start == NUL ? NL : *start; + *p = NUL; + + li = listitem_alloc(); + if (li == NULL) + { + vim_free(s); + goto errret; + } + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_string = s; + list_append(list, li); + } + + rettv_list_set(rettv, list); + list = NULL; + } + else + { + res = get_cmd_output(tv_get_string(&argvars[0]), infile, flags, NULL); +#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 + rettv->vval.v_string = res; + res = NULL; + } + +errret: + if (infile != NULL) + { + mch_remove(infile); + vim_free(infile); + } + if (res != NULL) + vim_free(res); + if (list != NULL) + list_free(list); +} + +/* + * "system()" function + */ + static void +f_system(typval_T *argvars, typval_T *rettv) +{ + get_cmd_output_as_rettv(argvars, rettv, FALSE); +} + +/* + * "systemlist()" function + */ + static void +f_systemlist(typval_T *argvars, typval_T *rettv) +{ + get_cmd_output_as_rettv(argvars, rettv, TRUE); +} + +/* + * "tabpagebuflist()" function + */ + static void +f_tabpagebuflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + tabpage_T *tp; + win_T *wp = NULL; + + if (argvars[0].v_type == VAR_UNKNOWN) + wp = firstwin; + else + { + tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp != NULL) + wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + } + if (wp != NULL && rettv_list_alloc(rettv) != FAIL) + { + for (; wp != NULL; wp = wp->w_next) + if (list_append_number(rettv->vval.v_list, + wp->w_buffer->b_fnum) == FAIL) + break; + } +} + +/* + * "tabpagenr()" function + */ + static void +f_tabpagenr(typval_T *argvars UNUSED, typval_T *rettv) +{ + int nr = 1; + char_u *arg; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + arg = tv_get_string_chk(&argvars[0]); + nr = 0; + if (arg != NULL) + { + if (STRCMP(arg, "$") == 0) + nr = tabpage_index(NULL) - 1; + else + semsg(_(e_invexpr2), arg); + } + } + else + nr = tabpage_index(curtab); + rettv->vval.v_number = nr; +} + + +/* + * 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 = tv_get_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 + { + semsg(_(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 UNUSED, typval_T *rettv) +{ + int nr = 1; + tabpage_T *tp; + + tp = find_tabpage((int)tv_get_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 UNUSED, typval_T *rettv) +{ + char_u *fname; + tagname_T tn; + int first; + + if (rettv_list_alloc(rettv) == FAIL) + return; + fname = alloc(MAXPATHL); + if (fname == NULL) + return; + + for (first = TRUE; ; first = FALSE) + if (get_tagfname(&tn, first, fname) == FAIL + || list_append_string(rettv->vval.v_list, fname, -1) == FAIL) + break; + tagname_free(&tn); + vim_free(fname); +} + +/* + * "taglist()" function + */ + static void +f_taglist(typval_T *argvars, typval_T *rettv) +{ + char_u *fname = NULL; + char_u *tag_pattern; + + tag_pattern = tv_get_string(&argvars[0]); + + rettv->vval.v_number = FALSE; + if (*tag_pattern == NUL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + fname = tv_get_string(&argvars[1]); + if (rettv_list_alloc(rettv) == OK) + (void)get_tags(rettv->vval.v_list, tag_pattern, fname); +} + +/* + * "tempname()" function + */ + static void +f_tempname(typval_T *argvars UNUSED, typval_T *rettv) +{ + static int x = 'A'; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_tempname(x, FALSE); + + /* 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 + { +#ifdef EBCDIC + if (x == 'I') + x = 'J'; + else if (x == 'R') + x = 'S'; + else +#endif + ++x; + } + } while (x == 'I' || x == 'O'); +} + +#ifdef FEAT_FLOAT +/* + * "tan()" function + */ + static void +f_tan(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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 = 0.0; + + 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; +} +#endif + +/* + * "test_alloc_fail(id, countdown, repeat)" function + */ + static void +f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED) +{ + if (argvars[0].v_type != VAR_NUMBER + || argvars[0].vval.v_number <= 0 + || argvars[1].v_type != VAR_NUMBER + || argvars[1].vval.v_number < 0 + || argvars[2].v_type != VAR_NUMBER) + emsg(_(e_invarg)); + else + { + alloc_fail_id = argvars[0].vval.v_number; + if (alloc_fail_id >= aid_last) + emsg(_(e_invarg)); + alloc_fail_countdown = argvars[1].vval.v_number; + alloc_fail_repeat = argvars[2].vval.v_number; + did_outofmem_msg = FALSE; + } +} + +/* + * "test_autochdir()" + */ + static void +f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#if defined(FEAT_AUTOCHDIR) + test_autochdir = TRUE; +#endif +} + +/* + * "test_feedinput()" + */ + static void +f_test_feedinput(typval_T *argvars, typval_T *rettv UNUSED) +{ +#ifdef USE_INPUT_BUF + char_u *val = tv_get_string_chk(&argvars[0]); + + if (val != NULL) + { + trash_input_buf(); + add_to_input_buf_csi(val, (int)STRLEN(val)); + } +#endif +} + +/* + * "test_option_not_set({name})" function + */ + static void +f_test_option_not_set(typval_T *argvars, typval_T *rettv UNUSED) +{ + char_u *name = (char_u *)""; + + if (argvars[0].v_type != VAR_STRING) + emsg(_(e_invarg)); + else + { + name = tv_get_string(&argvars[0]); + if (reset_option_was_set(name) == FAIL) + semsg(_(e_invarg2), name); + } +} + +/* + * "test_override({name}, {val})" function + */ + static void +f_test_override(typval_T *argvars, typval_T *rettv UNUSED) +{ + char_u *name = (char_u *)""; + int val; + static int save_starting = -1; + + if (argvars[0].v_type != VAR_STRING + || (argvars[1].v_type) != VAR_NUMBER) + emsg(_(e_invarg)); + else + { + name = tv_get_string(&argvars[0]); + val = (int)tv_get_number(&argvars[1]); + + if (STRCMP(name, (char_u *)"redraw") == 0) + disable_redraw_for_testing = val; + else if (STRCMP(name, (char_u *)"redraw_flag") == 0) + ignore_redraw_flag_for_testing = val; + else if (STRCMP(name, (char_u *)"char_avail") == 0) + disable_char_avail_for_testing = val; + else if (STRCMP(name, (char_u *)"starting") == 0) + { + if (val) + { + if (save_starting < 0) + save_starting = starting; + starting = 0; + } + else + { + starting = save_starting; + save_starting = -1; + } + } + else if (STRCMP(name, (char_u *)"nfa_fail") == 0) + nfa_fail_for_testing = val; + else if (STRCMP(name, (char_u *)"ALL") == 0) + { + disable_char_avail_for_testing = FALSE; + disable_redraw_for_testing = FALSE; + ignore_redraw_flag_for_testing = FALSE; + nfa_fail_for_testing = FALSE; + if (save_starting >= 0) + { + starting = save_starting; + save_starting = -1; + } + } + else + semsg(_(e_invarg2), name); + } +} + +/* + * "test_garbagecollect_now()" function + */ + static void +f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + /* This is dangerous, any Lists and Dicts used internally may be freed + * while still in use. */ + garbage_collect(TRUE); +} + +/* + * "test_ignore_error()" function + */ + static void +f_test_ignore_error(typval_T *argvars, typval_T *rettv UNUSED) +{ + ignore_error_for_testing(tv_get_string(&argvars[0])); +} + + static void +f_test_null_blob(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; +} + +#ifdef FEAT_JOB_CHANNEL + static void +f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_CHANNEL; + rettv->vval.v_channel = NULL; +} +#endif + + static void +f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv_dict_set(rettv, NULL); +} + +#ifdef FEAT_JOB_CHANNEL + static void +f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_JOB; + rettv->vval.v_job = NULL; +} +#endif + + static void +f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv_list_set(rettv, NULL); +} + + static void +f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = NULL; +} + + static void +f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +} + +#ifdef FEAT_GUI + static void +f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED) +{ + char_u *which; + long value; + int dragging; + scrollbar_T *sb = NULL; + + if (argvars[0].v_type != VAR_STRING + || (argvars[1].v_type) != VAR_NUMBER + || (argvars[2].v_type) != VAR_NUMBER) + { + emsg(_(e_invarg)); + return; + } + which = tv_get_string(&argvars[0]); + value = tv_get_number(&argvars[1]); + dragging = tv_get_number(&argvars[2]); + + if (STRCMP(which, "left") == 0) + sb = &curwin->w_scrollbars[SBAR_LEFT]; + else if (STRCMP(which, "right") == 0) + sb = &curwin->w_scrollbars[SBAR_RIGHT]; + else if (STRCMP(which, "hor") == 0) + sb = &gui.bottom_sbar; + if (sb == NULL) + { + semsg(_(e_invarg2), which); + return; + } + gui_drag_scrollbar(sb, value, dragging); +# ifndef USE_ON_FLY_SCROLL + // need to loop through normal_cmd() to handle the scroll events + exec_normal(FALSE, TRUE, FALSE); +# endif +} +#endif + + static void +f_test_settime(typval_T *argvars, typval_T *rettv UNUSED) +{ + time_for_testing = (time_t)tv_get_number(&argvars[0]); +} + +#if defined(FEAT_JOB_CHANNEL) || defined(FEAT_TIMERS) || defined(PROTO) +/* + * Get a callback from "arg". It can be a Funcref or a function name. + * When "arg" is zero return an empty string. + * Return NULL for an invalid argument. + */ + char_u * +get_callback(typval_T *arg, partial_T **pp) +{ + if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) + { + *pp = arg->vval.v_partial; + ++(*pp)->pt_refcount; + return partial_name(*pp); + } + *pp = NULL; + if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) + { + func_ref(arg->vval.v_string); + return arg->vval.v_string; + } + if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) + return (char_u *)""; + emsg(_("E921: Invalid callback argument")); + return NULL; +} + +/* + * Unref/free "callback" and "partial" returned by get_callback(). + */ + void +free_callback(char_u *callback, partial_T *partial) +{ + if (partial != NULL) + partial_unref(partial); + else if (callback != NULL) + { + func_unref(callback); + vim_free(callback); + } +} +#endif + +#ifdef FEAT_TIMERS +/* + * "timer_info([timer])" function + */ + static void +f_timer_info(typval_T *argvars, typval_T *rettv) +{ + timer_T *timer = NULL; + + if (rettv_list_alloc(rettv) != OK) + return; + if (argvars[0].v_type != VAR_UNKNOWN) + { + if (argvars[0].v_type != VAR_NUMBER) + emsg(_(e_number_exp)); + else + { + timer = find_timer((int)tv_get_number(&argvars[0])); + if (timer != NULL) + add_timer_info(rettv, timer); + } + } + else + add_timer_info_all(rettv); +} + +/* + * "timer_pause(timer, paused)" function + */ + static void +f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED) +{ + timer_T *timer = NULL; + int paused = (int)tv_get_number(&argvars[1]); + + if (argvars[0].v_type != VAR_NUMBER) + emsg(_(e_number_exp)); + else + { + timer = find_timer((int)tv_get_number(&argvars[0])); + if (timer != NULL) + timer->tr_paused = paused; + } +} + +/* + * "timer_start(time, callback [, options])" function + */ + static void +f_timer_start(typval_T *argvars, typval_T *rettv) +{ + long msec = (long)tv_get_number(&argvars[0]); + timer_T *timer; + int repeat = 0; + char_u *callback; + dict_T *dict; + partial_T *partial; + + rettv->vval.v_number = -1; + if (check_secure()) + return; + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (argvars[2].v_type != VAR_DICT + || (dict = argvars[2].vval.v_dict) == NULL) + { + semsg(_(e_invarg2), tv_get_string(&argvars[2])); + return; + } + if (dict_find(dict, (char_u *)"repeat", -1) != NULL) + repeat = dict_get_number(dict, (char_u *)"repeat"); + } + + callback = get_callback(&argvars[1], &partial); + if (callback == NULL) + return; + + timer = create_timer(msec, repeat); + if (timer == NULL) + free_callback(callback, partial); + else + { + if (partial == NULL) + timer->tr_callback = vim_strsave(callback); + else + /* pointer into the partial */ + timer->tr_callback = callback; + timer->tr_partial = partial; + rettv->vval.v_number = (varnumber_T)timer->tr_id; + } +} + +/* + * "timer_stop(timer)" function + */ + static void +f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED) +{ + timer_T *timer; + + if (argvars[0].v_type != VAR_NUMBER) + { + emsg(_(e_number_exp)); + return; + } + timer = find_timer((int)tv_get_number(&argvars[0])); + if (timer != NULL) + stop_timer(timer); +} + +/* + * "timer_stopall()" function + */ + static void +f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + stop_all_timers(); +} +#endif + +/* + * "tolower(string)" function + */ + static void +f_tolower(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = strlow_save(tv_get_string(&argvars[0])); +} + +/* + * "toupper(string)" function + */ + static void +f_toupper(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = strup_save(tv_get_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 = tv_get_string(&argvars[0]); + fromstr = tv_get_string_buf_chk(&argvars[1], buf); + tostr = tv_get_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_init2(&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: + semsg(_(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; + } + + (void)ga_grow(&ga, cplen); + mch_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 */ + (void)ga_grow(&ga, 1); + ga_append(&ga, NUL); + + rettv->vval.v_string = ga.ga_data; +} + +/* + * "trim({expr})" function + */ + static void +f_trim(typval_T *argvars, typval_T *rettv) +{ + char_u buf1[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + char_u *head = tv_get_string_buf_chk(&argvars[0], buf1); + char_u *mask = NULL; + char_u *tail; + char_u *prev; + char_u *p; + int c1; + + rettv->v_type = VAR_STRING; + if (head == NULL) + { + rettv->vval.v_string = NULL; + return; + } + + if (argvars[1].v_type == VAR_STRING) + mask = tv_get_string_buf_chk(&argvars[1], buf2); + + while (*head != NUL) + { + c1 = PTR2CHAR(head); + if (mask == NULL) + { + if (c1 > ' ' && c1 != 0xa0) + break; + } + else + { + for (p = mask; *p != NUL; MB_PTR_ADV(p)) + if (c1 == PTR2CHAR(p)) + break; + if (*p == NUL) + break; + } + MB_PTR_ADV(head); + } + + for (tail = head + STRLEN(head); tail > head; tail = prev) + { + prev = tail; + MB_PTR_BACK(head, prev); + c1 = PTR2CHAR(prev); + if (mask == NULL) + { + if (c1 > ' ' && c1 != 0xa0) + break; + } + else + { + for (p = mask; *p != NUL; MB_PTR_ADV(p)) + if (c1 == PTR2CHAR(p)) + break; + if (*p == NUL) + break; + } + } + rettv->vval.v_string = vim_strnsave(head, (int)(tail - head)); +} + +#ifdef FEAT_FLOAT +/* + * "trunc({float})" function + */ + static void +f_trunc(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + 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; +} +#endif + +/* + * "type(expr)" function + */ + static void +f_type(typval_T *argvars, typval_T *rettv) +{ + int n = -1; + + switch (argvars[0].v_type) + { + case VAR_NUMBER: n = VAR_TYPE_NUMBER; break; + case VAR_STRING: n = VAR_TYPE_STRING; break; + case VAR_PARTIAL: + case VAR_FUNC: n = VAR_TYPE_FUNC; break; + case VAR_LIST: n = VAR_TYPE_LIST; break; + case VAR_DICT: n = VAR_TYPE_DICT; break; + case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; + case VAR_SPECIAL: + if (argvars[0].vval.v_number == VVAL_FALSE + || argvars[0].vval.v_number == VVAL_TRUE) + n = VAR_TYPE_BOOL; + else + n = VAR_TYPE_NONE; + break; + case VAR_JOB: n = VAR_TYPE_JOB; break; + case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break; + case VAR_BLOB: n = VAR_TYPE_BLOB; break; + case VAR_UNKNOWN: + internal_error("f_type(UNKNOWN)"); + n = -1; + break; + } + rettv->vval.v_number = n; +} + +/* + * "undofile(name)" function + */ + static void +f_undofile(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; +#ifdef FEAT_PERSISTENT_UNDO + { + char_u *fname = tv_get_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); + vim_free(ffname); + } + } +#else + rettv->vval.v_string = NULL; +#endif +} + +/* + * "undotree()" function + */ + static void +f_undotree(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_dict_alloc(rettv) == OK) + { + dict_T *dict = rettv->vval.v_dict; + list_T *list; + + dict_add_number(dict, "synced", (long)curbuf->b_u_synced); + dict_add_number(dict, "seq_last", curbuf->b_u_seq_last); + dict_add_number(dict, "save_last", (long)curbuf->b_u_save_nr_last); + dict_add_number(dict, "seq_cur", curbuf->b_u_seq_cur); + dict_add_number(dict, "time_cur", (long)curbuf->b_u_time_cur); + dict_add_number(dict, "save_cur", (long)curbuf->b_u_save_nr_cur); + + 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 UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_WILDMENU + if (wild_menu_showing) + rettv->vval.v_number = 1; +#endif +} + +/* + * "winbufnr(nr)" function + */ + static void +f_winbufnr(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + + wp = find_win_by_nr_or_id(&argvars[0]); + 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 UNUSED, 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_or_id(&argvars[0]); + if (wp == NULL) + rettv->vval.v_number = -1; + else + rettv->vval.v_number = wp->w_height; +} + +/* + * "winlayout()" function + */ + static void +f_winlayout(typval_T *argvars, typval_T *rettv) +{ + tabpage_T *tp; + + if (rettv_list_alloc(rettv) != OK) + return; + + if (argvars[0].v_type == VAR_UNKNOWN) + tp = curtab; + else + { + tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp == NULL) + return; + } + + get_framelayout(tp->tp_topframe, rettv->vval.v_list, TRUE); +} + +/* + * "winline()" function + */ + static void +f_winline(typval_T *argvars UNUSED, typval_T *rettv) +{ + validate_cursor(); + rettv->vval.v_number = curwin->w_wrow + 1; +} + +/* + * "winnr()" function + */ + static void +f_winnr(typval_T *argvars UNUSED, 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 UNUSED, typval_T *rettv) +{ + win_T *wp; + int winnr = 1; + garray_T ga; + char_u buf[50]; + + ga_init2(&ga, (int)sizeof(char), 70); + FOR_ALL_WINDOWS(wp) + { + 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 UNUSED) +{ + dict_T *dict; + + if (argvars[0].v_type != VAR_DICT + || (dict = argvars[0].vval.v_dict) == NULL) + emsg(_(e_invarg)); + else + { + if (dict_find(dict, (char_u *)"lnum", -1) != NULL) + curwin->w_cursor.lnum = (linenr_T)dict_get_number(dict, (char_u *)"lnum"); + if (dict_find(dict, (char_u *)"col", -1) != NULL) + curwin->w_cursor.col = (colnr_T)dict_get_number(dict, (char_u *)"col"); + if (dict_find(dict, (char_u *)"coladd", -1) != NULL) + curwin->w_cursor.coladd = (colnr_T)dict_get_number(dict, (char_u *)"coladd"); + if (dict_find(dict, (char_u *)"curswant", -1) != NULL) + { + curwin->w_curswant = (colnr_T)dict_get_number(dict, (char_u *)"curswant"); + curwin->w_set_curswant = FALSE; + } + + if (dict_find(dict, (char_u *)"topline", -1) != NULL) + set_topline(curwin, (linenr_T)dict_get_number(dict, (char_u *)"topline")); +#ifdef FEAT_DIFF + if (dict_find(dict, (char_u *)"topfill", -1) != NULL) + curwin->w_topfill = (int)dict_get_number(dict, (char_u *)"topfill"); +#endif + if (dict_find(dict, (char_u *)"leftcol", -1) != NULL) + curwin->w_leftcol = (colnr_T)dict_get_number(dict, (char_u *)"leftcol"); + if (dict_find(dict, (char_u *)"skipcol", -1) != NULL) + curwin->w_skipcol = (colnr_T)dict_get_number(dict, (char_u *)"skipcol"); + + check_cursor(); + win_new_height(curwin, curwin->w_height); + win_new_width(curwin, curwin->w_width); + 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; +#ifdef FEAT_DIFF + check_topfill(curwin, TRUE); +#endif + } +} + +/* + * "winsaveview()" function + */ + static void +f_winsaveview(typval_T *argvars UNUSED, typval_T *rettv) +{ + dict_T *dict; + + if (rettv_dict_alloc(rettv) == FAIL) + return; + dict = rettv->vval.v_dict; + + dict_add_number(dict, "lnum", (long)curwin->w_cursor.lnum); + dict_add_number(dict, "col", (long)curwin->w_cursor.col); + dict_add_number(dict, "coladd", (long)curwin->w_cursor.coladd); + update_curswant(); + dict_add_number(dict, "curswant", (long)curwin->w_curswant); + + dict_add_number(dict, "topline", (long)curwin->w_topline); +#ifdef FEAT_DIFF + dict_add_number(dict, "topfill", (long)curwin->w_topfill); +#endif + dict_add_number(dict, "leftcol", (long)curwin->w_leftcol); + dict_add_number(dict, "skipcol", (long)curwin->w_skipcol); +} + +/* + * "winwidth(nr)" function + */ + static void +f_winwidth(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) + rettv->vval.v_number = -1; + else + rettv->vval.v_number = wp->w_width; +} + +/* + * "wordcount()" function + */ + static void +f_wordcount(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_dict_alloc(rettv) == FAIL) + return; + cursor_pos_info(rettv->vval.v_dict); +} + +/* + * "writefile()" function + */ + static void +f_writefile(typval_T *argvars, typval_T *rettv) +{ + int binary = FALSE; + int append = FALSE; +#ifdef HAVE_FSYNC + int do_fsync = p_fs; +#endif + char_u *fname; + FILE *fd; + int ret = 0; + listitem_T *li; + list_T *list = NULL; + blob_T *blob = NULL; + + rettv->vval.v_number = -1; + if (check_restricted() || check_secure()) + return; + + if (argvars[0].v_type == VAR_LIST) + { + list = argvars[0].vval.v_list; + if (list == NULL) + return; + for (li = list->lv_first; li != NULL; li = li->li_next) + if (tv_get_string_chk(&li->li_tv) == NULL) + return; + } + else if (argvars[0].v_type == VAR_BLOB) + { + blob = argvars[0].vval.v_blob; + if (blob == NULL) + return; + } + else + { + semsg(_(e_invarg2), "writefile()"); + return; + } + + if (argvars[2].v_type != VAR_UNKNOWN) + { + char_u *arg2 = tv_get_string_chk(&argvars[2]); + + if (arg2 == NULL) + return; + if (vim_strchr(arg2, 'b') != NULL) + binary = TRUE; + if (vim_strchr(arg2, 'a') != NULL) + append = TRUE; +#ifdef HAVE_FSYNC + if (vim_strchr(arg2, 's') != NULL) + do_fsync = TRUE; + else if (vim_strchr(arg2, 'S') != NULL) + do_fsync = FALSE; +#endif + } + + fname = tv_get_string_chk(&argvars[1]); + if (fname == NULL) + return; + + /* Always open the file in binary mode, library functions have a mind of + * their own about CR-LF conversion. */ + if (*fname == NUL || (fd = mch_fopen((char *)fname, + append ? APPENDBIN : WRITEBIN)) == NULL) + { + semsg(_(e_notcreate), *fname == NUL ? (char_u *)_("") : fname); + ret = -1; + } + else if (blob) + { + if (write_blob(fd, blob) == FAIL) + ret = -1; +#ifdef HAVE_FSYNC + else if (do_fsync) + // Ignore the error, the user wouldn't know what to do about it. + // May happen for a device. + vim_ignored = fsync(fileno(fd)); +#endif + fclose(fd); + } + else + { + if (write_list(fd, list, binary) == FAIL) + ret = -1; +#ifdef HAVE_FSYNC + else if (do_fsync) + /* Ignore the error, the user wouldn't know what to do about it. + * May happen for a device. */ + vim_ignored = fsync(fileno(fd)); +#endif + 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 = tv_get_number_chk(&argvars[0], NULL) + ^ tv_get_number_chk(&argvars[1], NULL); +} + +#endif /* FEAT_EVAL */ diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h new file mode 100644 index 0000000..867047f --- /dev/null +++ b/src/ex_cmdidxs.h @@ -0,0 +1,72 @@ +/* Automatically generated code by create_cmdidxs.vim + * + * Table giving the index of the first command in cmdnames[] to lookup + * based on the first letter of a command. + */ +static const unsigned short cmdidxs1[26] = +{ + /* a */ 0, + /* b */ 19, + /* c */ 42, + /* d */ 103, + /* e */ 125, + /* f */ 145, + /* g */ 161, + /* h */ 167, + /* i */ 176, + /* j */ 194, + /* k */ 196, + /* l */ 201, + /* m */ 259, + /* n */ 277, + /* o */ 297, + /* p */ 309, + /* q */ 348, + /* r */ 351, + /* s */ 371, + /* t */ 438, + /* u */ 481, + /* v */ 492, + /* w */ 510, + /* x */ 524, + /* y */ 533, + /* z */ 534 +}; + +/* + * Table giving the index of the first command in cmdnames[] to lookup + * based on the first 2 letters of a command. + * Values in cmdidxs2[c1][c2] are relative to cmdidxs1[c1] so that they + * fit in a byte. + */ +static const unsigned char cmdidxs2[26][26] = +{ /* a b c d e f g h i j k l m n o p q r s t u v w x y z */ + /* a */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 0, 0, 0, 7, 15, 0, 16, 0, 0, 0, 0, 0 }, + /* b */ { 2, 0, 0, 4, 5, 7, 0, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, 13, 0, 0, 0, 0, 22, 0, 0, 0 }, + /* c */ { 3, 10, 12, 14, 16, 18, 21, 0, 0, 0, 0, 29, 33, 36, 42, 51, 53, 54, 55, 0, 57, 0, 60, 0, 0, 0 }, + /* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 6, 15, 0, 16, 0, 0, 17, 0, 0, 19, 20, 0, 0, 0, 0, 0, 0, 0 }, + /* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0 }, + /* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0 }, + /* g */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 4, 5, 0, 0, 0, 0 }, + /* h */ { 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* i */ { 1, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 5, 6, 0, 0, 0, 0, 0, 13, 0, 15, 0, 0, 0, 0, 0 }, + /* j */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + /* k */ { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* l */ { 3, 9, 11, 15, 16, 20, 23, 28, 0, 0, 0, 30, 33, 36, 40, 46, 0, 48, 57, 49, 50, 54, 56, 0, 0, 0 }, + /* m */ { 1, 0, 0, 0, 7, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 }, + /* n */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 10, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0 }, + /* o */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 5, 0, 0, 0, 0, 0, 0, 9, 0, 11, 0, 0, 0 }, + /* p */ { 1, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 0, 0, 16, 17, 26, 0, 27, 0, 28, 0 }, + /* q */ { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* r */ { 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 19, 0, 0, 0, 0 }, + /* s */ { 2, 6, 15, 0, 18, 22, 0, 24, 25, 0, 0, 28, 30, 34, 38, 40, 0, 48, 0, 49, 0, 61, 62, 0, 63, 0 }, + /* t */ { 2, 0, 19, 0, 22, 24, 0, 25, 0, 26, 0, 27, 31, 34, 36, 37, 0, 38, 40, 0, 41, 0, 0, 0, 0, 0 }, + /* u */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* v */ { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 12, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0 }, + /* w */ { 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, 8, 0, 9, 10, 0, 0, 0, 12, 13, 0, 0, 0, 0 }, + /* x */ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0 }, + /* y */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +static const int command_count = 547; diff --git a/src/ex_cmds.c b/src/ex_cmds.c new file mode 100644 index 0000000..a3974c1 --- /dev/null +++ b/src/ex_cmds.c @@ -0,0 +1,7754 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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 "vim.h" +#include "version.h" + +#ifdef FEAT_FLOAT +# include +#endif + +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); +#ifdef FEAT_VIMINFO +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); +#endif + +static int check_readonly(int *forceit, buf_T *buf); +static void delbuf_msg(char_u *name); +static int +#ifdef __BORLANDC__ + _RTLENTRYF +#endif + help_compare(const void *s1, const void *s2); +static void prepare_help_buffer(void); + +/* + * ":ascii" and "ga". + */ + void +do_ascii(exarg_T *eap UNUSED) +{ + int c; + int cval; + char buf1[20]; + char buf2[20]; + char_u buf3[7]; +#ifdef FEAT_DIGRAPHS + char_u *dig; +#endif + 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 < ' ' +#ifndef EBCDIC + || c > '~' +#endif + )) + { + transchar_nonprint(buf3, c); + vim_snprintf(buf1, sizeof(buf1), " <%s>", (char *)buf3); + } + else + buf1[0] = NUL; +#ifndef EBCDIC + if (c >= 0x80) + vim_snprintf(buf2, sizeof(buf2), " ", + (char *)transchar(c & 0x7f)); + else +#endif + buf2[0] = NUL; +#ifdef FEAT_DIGRAPHS + dig = get_digraph_for_char(cval); + if (dig != NULL) + vim_snprintf((char *)IObuff, IOSIZE, + _("<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"), + transchar(c), buf1, buf2, cval, cval, cval, dig); + else +#endif + 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); +#ifdef FEAT_DIGRAPHS + dig = get_digraph_for_char(c); + if (dig != NULL) + vim_snprintf((char *)IObuff + len, IOSIZE - len, + c < 0x10000 ? _("> %d, Hex %04x, Oct %o, Digr %s") + : _("> %d, Hex %08x, Oct %o, Digr %s"), + c, c, c, dig); + else +#endif + 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((char *)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; + +#ifdef FEAT_RIGHTLEFT + 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; + } +#endif + + 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 = curwin->w_width - 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_strchr(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 */ +#ifdef FEAT_FLOAT +static int sort_flt; /* sort on floating number */ +#endif + +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 */ + union { + struct + { + varnumber_T start_col_nr; /* starting column number */ + varnumber_T end_col_nr; /* ending column number */ + } line; + varnumber_T value; /* value if sorting by integer */ +#ifdef FEAT_FLOAT + float_T value_flt; /* value if sorting by float */ +#endif + } st_u; +} sorti_T; + +static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +sort_compare(const void *s1, const void *s2); + + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +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.st_u.value == l2.st_u.value ? 0 + : l1.st_u.value > l2.st_u.value ? 1 : -1; +#ifdef FEAT_FLOAT + else if (sort_flt) + result = l1.st_u.value_flt == l2.st_u.value_flt ? 0 + : l1.st_u.value_flt > l2.st_u.value_flt ? 1 : -1; +#endif + 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.st_u.line.start_col_nr, + l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1); + sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = 0; + STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr, + l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1); + sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.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; + sorti_T *nrs; + 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_what = 0; + int format_found = 0; + int change_occurred = FALSE; // Buffer contents changed. + + /* 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; + nrs = (sorti_T *)lalloc((long_u)(count * sizeof(sorti_T)), TRUE); + if (nrs == NULL) + goto sortend; + + sort_abort = sort_ic = sort_rx = sort_nr = 0; +#ifdef FEAT_FLOAT + sort_flt = 0; +#endif + + 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 = 1; + ++format_found; + } +#ifdef FEAT_FLOAT + else if (*p == 'f') + { + sort_flt = 1; + ++format_found; + } +#endif + else if (*p == 'b') + { + sort_what = STR2NR_BIN + STR2NR_FORCE; + ++format_found; + } + else if (*p == 'o') + { + sort_what = STR2NR_OCT + STR2NR_FORCE; + ++format_found; + } + else if (*p == 'x') + { + sort_what = STR2NR_HEX + STR2NR_FORCE; + ++format_found; + } + 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 + { + semsg(_(e_invarg2), p); + goto sortend; + } + } + + /* Can only have one of 'n', 'b', 'o' and 'x'. */ + if (format_found > 1) + { + emsg(_(e_invarg)); + goto sortend; + } + + /* From here on "sort_nr" is used as a flag for any integer number + * sorting. */ + sort_nr += sort_what; + + /* + * 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 +#ifdef FEAT_FLOAT + || sort_flt +#endif + ) + { + /* 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_nr) + { + if (sort_what & STR2NR_HEX) + s = skiptohex(p); + else if (sort_what & STR2NR_BIN) + s = skiptobin(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].st_u.value = -MAXLNUM; + else + vim_str2nr(s, NULL, NULL, sort_what, + &nrs[lnum - eap->line1].st_u.value, NULL, 0); + } +#ifdef FEAT_FLOAT + else + { + s = skipwhite(p); + if (*s == '+') + s = skipwhite(s + 1); + + if (*s == NUL) + /* empty line should sort before any number */ + nrs[lnum - eap->line1].st_u.value_flt = -DBL_MAX; + else + nrs[lnum - eap->line1].st_u.value_flt = + strtod((char *)s, NULL); + } +#endif + *s2 = c; + } + else + { + /* Store the column to sort at. */ + nrs[lnum - eap->line1].st_u.line.start_col_nr = start_col; + nrs[lnum - eap->line1].st_u.line.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 = alloc((unsigned)maxlen + 1); + if (sortbuf1 == NULL) + goto sortend; + sortbuf2 = alloc((unsigned)maxlen + 1); + if (sortbuf2 == NULL) + goto sortend; + + /* 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) + { + linenr_T get_lnum = nrs[eap->forceit ? count - i - 1 : i].lnum; + + // If the original line number of the line being placed is not the same + // as "lnum" (accounting for offset), we know that the buffer changed. + if (get_lnum + ((linenr_T)count - 1) != lnum) + change_occurred = TRUE; + + s = ml_get(get_lnum); + if (!unique || i == 0 + || (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) + { + // Copy the line into a buffer, it may become invalid in + // ml_append(). And it's needed for "unique". + STRCPY(sortbuf1, s); + if (ml_append(lnum++, sortbuf1, (colnr_T)0, FALSE) == FAIL) + break; + } + 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); + msgmore(-deleted); + } + else if (deleted < 0) + mark_adjust(eap->line2, MAXLNUM, -deleted, 0L); + + if (change_occurred || deleted != 0) + changed_lines(eap->line1, 0, eap->line2 + 1, -deleted); + + curwin->w_cursor.lnum = eap->line1; + beginline(BL_WHITE | BL_FIX); + +sortend: + vim_free(nrs); + vim_free(sortbuf1); + vim_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 */ + 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 */ +#ifdef FEAT_VARTABS + int *new_vts_array = NULL; + char_u *new_ts_str; /* string value of tab argument */ +#else + int temp; + int new_ts; +#endif + 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 */ + +#ifdef FEAT_VARTABS + new_ts_str = eap->arg; + if (!tabstop_set(eap->arg, &new_vts_array)) + return; + while (vim_isdigit(*(eap->arg)) || *(eap->arg) == ',') + ++(eap->arg); + + // This ensures that either new_vts_array and new_ts_str are freshly + // allocated, or new_vts_array points to an existing array and new_ts_str + // is null. + if (new_vts_array == NULL) + { + new_vts_array = curbuf->b_p_vts_array; + new_ts_str = NULL; + } + else + new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str); +#else + new_ts = getdigits(&(eap->arg)); + if (new_ts < 0) + { + emsg(_(e_positive)); + return; + } + if (new_ts == 0) + new_ts = curbuf->b_p_ts; +#endif + 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) + { +#ifdef FEAT_VARTABS + int t, s; + + tabstop_fromto(start_vcol, vcol, + curbuf->b_p_ts, new_vts_array, &t, &s); + num_tabs = t; + num_spaces = s; +#else + 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; +#endif + } + 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 = lalloc(old_len - col + start_col + len + 1, + TRUE); + if (new_line == NULL) + break; + if (start_col > 0) + mch_memmove(new_line, ptr, (size_t)start_col); + mch_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)); + +#ifdef FEAT_VARTABS + // If a single value was given then it can be considered equal to + // either the value of 'tabstop' or the value of 'vartabstop'. + if (tabstop_count(curbuf->b_p_vts_array) == 0 + && tabstop_count(new_vts_array) == 1 + && curbuf->b_p_ts == tabstop_first(new_vts_array)) + ; /* not changed */ + else if (tabstop_count(curbuf->b_p_vts_array) > 0 + && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) + ; /* not changed */ + else + redraw_curbuf_later(NOT_VALID); +#else + if (curbuf->b_p_ts != new_ts) + redraw_curbuf_later(NOT_VALID); +#endif + if (first_line != 0) + changed_lines(first_line, 0, last_line + 1, 0L); + + curwin->w_p_list = save_list; /* restore 'list' */ + +#ifdef FEAT_VARTABS + if (new_ts_str != NULL) /* set the new tabstop */ + { + // If 'vartabstop' is in use or if the value given to retab has more + // than one tabstop then update 'vartabstop'. + int *old_vts_ary = curbuf->b_p_vts_array; + + if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) + { + set_string_option_direct((char_u *)"vts", -1, new_ts_str, + OPT_FREE|OPT_LOCAL, 0); + curbuf->b_p_vts_array = new_vts_array; + vim_free(old_vts_ary); + } + else + { + // 'vartabstop' wasn't in use and a single value was given to + // retab then update 'tabstop'. + curbuf->b_p_ts = tabstop_first(new_vts_array); + vim_free(new_vts_array); + } + vim_free(new_ts_str); + } +#else + curbuf->b_p_ts = new_ts; +#endif + 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 +#ifdef FEAT_FOLDING + win_T *win; + tabpage_T *tp; +#endif + + if (dest >= line1 && dest < line2) + { + emsg(_("E134: Cannot move a range of lines into itself")); + return FAIL; + } + + // Do nothing if we are not actually moving any lines. This will prevent + // the 'modified' flag from being set without cause. + if (dest == line1 - 1 || dest == line2) + { + // Move the cursor as if lines were moved (see below) to be backwards + // compatible. + if (dest >= line1) + curwin->w_cursor.lnum = dest; + else + curwin->w_cursor.lnum = dest + (line2 - line1) + 1; + + return OK; + } + + 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); + vim_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_nofold(line1, line2, last_line - line2, 0L); + if (dest >= line2) + { + mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L); +#ifdef FEAT_FOLDING + FOR_ALL_TAB_WINDOWS(tp, win) { + if (win->w_buffer == curbuf) + foldMoveRange(&win->w_folds, line1, line2, dest); + } +#endif + curbuf->b_op_start.lnum = dest - num_lines + 1; + curbuf->b_op_end.lnum = dest; + } + else + { + mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L); +#ifdef FEAT_FOLDING + FOR_ALL_TAB_WINDOWS(tp, win) { + if (win->w_buffer == curbuf) + foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2); + } +#endif + 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_nofold(last_line - num_lines + 1, last_line, + -(last_line - dest - extra), 0L); + + /* + * 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) + smsg(NGETTEXT("%ld line moved", "%ld lines moved", num_lines), + (long)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); + vim_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) +{ + vim_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)); + vim_free(newcmd); + return; + } + len += (int)STRLEN(prevcmd); + } + if ((t = alloc((unsigned)len)) == NULL) + { + vim_free(newcmd); + return; + } + *t = NUL; + if (newcmd != NULL) + STRCAT(t, newcmd); + if (ins_prevcmd) + STRCAT(t, prevcmd); + p = t + STRLEN(t); + STRCAT(t, trailarg); + vim_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); + + vim_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 *)"%#"); + + if (cmd != NULL) + { + AppendToRedobuffLit(cmd, -1); + vim_free(cmd); + } + else + AppendToRedobuffLit(prevcmd, -1); + AppendToRedobuff((char_u *)"\n"); + bangredo = FALSE; + } + /* + * Add quotes around the command, for shells that need them. + */ + if (*p_shq != NUL) + { + newcmd = alloc((unsigned)(STRLEN(prevcmd) + 2 * STRLEN(p_shq) + 1)); + if (newcmd == NULL) + return; + 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) + vim_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 |= SHELL_DOOUT; + +#ifdef FEAT_FILTERPIPE + if (!do_in && do_out && !p_stmp) + { + /* Use a pipe to fetch stdout of the command, do not use a temp file. */ + shell_flags |= SHELL_READ; + 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 |= SHELL_WRITE; + 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 |= SHELL_READ|SHELL_WRITE; + curbuf->b_op_start.lnum = line1; + curbuf->b_op_end.lnum = line2; + curwin->w_cursor.lnum = line2; + } + else +#endif + if ((do_in && (itmp = vim_tempname('i', FALSE)) == NULL) + || (do_out && (otmp = vim_tempname('o', FALSE)) == 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 defined(FEAT_EVAL) + if (!aborting()) +#endif + (void)semsg(_(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); + if (cmd_buf == NULL) + goto filterend; + + 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) + { + vim_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 SHELL_DOOUT flag when the output is being redirected. + */ + if (call_shell(cmd_buf, SHELL_FILTER | SHELL_COOKED | shell_flags)) + { + redraw_later_clear(); + wait_return(FALSE); + } + vim_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) != OK) + { +#if defined(FEAT_EVAL) + if (!aborting()) +#endif + { + msg_putchar('\n'); + semsg(_(e_notread), otmp); + } + goto error; + } + if (curbuf != old_curbuf) + goto filterend; + } + + read_linecount = curbuf->b_ml.ml_line_count - read_linecount; + + if (shell_flags & SHELL_READ) + { + 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 */ +#ifdef FEAT_FOLDING + foldUpdate(curwin, curbuf->b_op_start.lnum, curbuf->b_op_end.lnum); +#endif + } + 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(msg_buf, sizeof(msg_buf), + _("%ld lines filtered"), (long)linecount); + if (msg(msg_buf) && !msg_scroll) + /* save message to display it after redraw */ + set_keep_msg((char_u *)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) + mch_remove(itmp); + if (otmp != NULL) + mch_remove(otmp); + vim_free(itmp); + vim_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; +#ifndef FEAT_GUI_MSWIN + int save_nwr; +#endif +#ifdef MSWIN + int winstart = FALSE; +#endif + + /* + * 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; + } + +#ifdef MSWIN + /* + * Check if ":!start" is used. + */ + if (cmd != NULL) + winstart = (STRNICMP(cmd, "start ", 6) == 0); +#endif + + /* + * 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) + { +#ifdef MSWIN + if (!winstart) +#endif + stoptermcap(); + } +#ifdef MSWIN + if (!winstart) +#endif + msg_putchar('\n'); /* may shift screen one line up */ + + /* warning message before calling the shell */ + if (p_warn && !autocmd_busy && msg_silent == 0) + FOR_ALL_BUFFERS(buf) + if (bufIsChangedNotTerm(buf)) + { +#ifdef FEAT_GUI_MSWIN + if (!winstart) + starttermcap(); /* don't want a message box here */ +#endif + msg_puts(_("[No write since last change]\n")); +#ifdef FEAT_GUI_MSWIN + if (!winstart) + stoptermcap(); +#endif + 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, SHELL_COOKED | flags); + 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(). + */ +#ifndef FEAT_GUI_MSWIN + if (cmd == NULL +# ifdef WIN3264 + || (winstart && !need_wait_return) +# endif + ) + { + 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; +# ifdef AMIGA + wait_return(term_console ? -1 : msg_silent == 0); /* see below */ +# else + wait_return(msg_silent == 0); +# endif + no_wait_return = save_nwr; + } +#endif /* FEAT_GUI_W32 */ + +#ifdef MSWIN + if (!winstart) /* if winstart==TRUE, never stopped termcap! */ +#endif + 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. + */ +#ifdef AMIGA + if (skip_redraw) /* ':' hit in wait_return() */ + { + if (msg_silent == 0) + redraw_later_clear(); + } + else if (term_console) + { + OUT_STR(IF_EB("\033[0 q", ESC_STR "[0 q")); /* get window size */ + if (got_int && msg_silent == 0) + redraw_later_clear(); /* if got_int is TRUE, redraw needed */ + else + must_redraw = 0; /* no extra redraw needed */ + } +#endif + } + + /* display any error messages now */ + display_errors(); + + apply_autocmds(EVENT_SHELLCMDPOST, NULL, NULL, FALSE, curbuf); +} + +#if !defined(UNIX) + static char_u * +find_pipe(char_u *cmd) +{ + char_u *p; + int inquote = FALSE; + + for (p = cmd; *p != NUL; ++p) + { + if (!inquote && *p == '|') + return p; + if (*p == '"') + inquote = !inquote; + else if (rem_backslash(p)) + ++p; + } + return NULL; +} +#endif + +/* + * Create a shell command from a command string, input redirection file and + * output redirection file. + * Returns an allocated string with the shell command, or NULL for failure. + */ + 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 */ +{ + char_u *buf; + long_u len; + +#if defined(UNIX) + int is_fish_shell; + char_u *shell_name = get_isolated_shell_name(); + + /* Account for fish's different syntax for subshells */ + is_fish_shell = (fnamecmp(shell_name, "fish") == 0); + vim_free(shell_name); + if (is_fish_shell) + len = (long_u)STRLEN(cmd) + 13; /* "begin; " + "; end" + NUL */ + else +#endif + len = (long_u)STRLEN(cmd) + 3; /* "()" + NUL */ + if (itmp != NULL) + len += (long_u)STRLEN(itmp) + 9; /* " { < " + " } " */ + if (otmp != NULL) + len += (long_u)STRLEN(otmp) + (long_u)STRLEN(p_srr) + 2; /* " " */ + buf = lalloc(len, TRUE); + if (buf == NULL) + return NULL; + +#if defined(UNIX) + /* + * Put braces around the command (for concatenated commands) when + * redirecting input and/or output. + */ + if (itmp != NULL || otmp != NULL) + { + if (is_fish_shell) + vim_snprintf((char *)buf, len, "begin; %s; end", (char *)cmd); + else + 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 = find_pipe(buf); + if (p != NULL) + *p = NUL; + } + STRCAT(buf, " <"); /* " < " causes problems on Amiga */ + STRCAT(buf, itmp); + if (*p_shq == NUL) + { + p = find_pipe(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)), +#ifdef FEAT_QUICKFIX + " %s %s", +#else + " %s%s", /* " > %s" causes problems on Amiga */ +#endif + (char *)opt, (char *)fname); +} + +#if defined(FEAT_VIMINFO) || defined(PROTO) + +static int read_viminfo_barline(vir_T *virp, int got_encoding, int force, int writing); +static void write_viminfo_version(FILE *fp_out); +static void write_viminfo_barlines(vir_T *virp, FILE *fp_out); +static int viminfo_errcnt; + + static int +no_viminfo(void) +{ + /* "vim -i NONE" does not read or write a viminfo file */ + return STRCMP(p_viminfofile, "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((char *)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(_("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(); + } + + vim_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 */ + stat_T st_new; /* mch_stat() of potential new file */ +#if defined(UNIX) || defined(VMS) + mode_t umask_save; +#endif +#ifdef UNIX + int shortname = FALSE; /* use 8.3 file name */ + stat_T st_old; /* mch_stat() of existing viminfo file */ +#endif +#ifdef WIN3264 + int hidden = FALSE; +#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) + { + int fd; + + /* if it does exist, but we can't read it, don't try writing */ + if (mch_stat((char *)fname, &st_new) == 0) + goto end; + + /* Create the new .viminfo non-accessible for others, because it may + * contain text from non-accessible documents. It is up to the user to + * widen access (e.g. to a group). This may also fail if there is a + * race condition, then just give up. */ + fd = mch_open((char *)fname, + O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600); + if (fd < 0) + goto end; + fp_out = fdopen(fd, WRITEBIN); + } + 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 once all writing is + * successful. + */ +#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. + */ + st_old.st_dev = (dev_t)0; + st_old.st_ino = 0; + st_old.st_mode = 0600; + if (mch_stat((char *)fname, &st_old) == 0 + && getuid() != ROOT_UID + && !(st_old.st_uid == getuid() + ? (st_old.st_mode & 0200) + : (st_old.st_gid == getgid() + ? (st_old.st_mode & 0020) + : (st_old.st_mode & 0002)))) + { + int tt = msg_didany; + + /* avoid a wait_return for this message, it's annoying */ + semsg(_("E137: Viminfo file is not writable: %s"), fname); + msg_didany = tt; + fclose(fp_in); + goto end; + } +#endif +#ifdef WIN3264 + /* Get the file attributes of the existing viminfo file. */ + hidden = mch_ishidden(fname); +#endif + + /* + * Make tempname, find one that does not exist yet. + * Beware of a race condition: If someone logs out and all Vim + * instances exit at the same time a temp file might be created between + * stat() and open(). Use mch_open() with O_EXCL to avoid that. + * May try twice: Once normal and once with shortname set, just in + * case somebody puts his viminfo file in an 8.3 filesystem. + */ + for (;;) + { + int next_char = 'z'; + char_u *wp; + + tempname = buf_modname( +#ifdef UNIX + shortname, +#else + FALSE, +#endif + fname, +#ifdef VMS + (char_u *)"-tmp", +#else + (char_u *)".tmp", +#endif + FALSE); + if (tempname == NULL) /* out of memory */ + break; + + /* + * Try a series of names. Change one character, just before + * the extension. This should also work for an 8.3 + * file name, when after adding the extension it still is + * the same file as the original. + */ + wp = tempname + STRLEN(tempname) - 5; + if (wp < gettail(tempname)) /* empty file name? */ + wp = gettail(tempname); + for (;;) + { + /* + * Check if tempfile already exists. Never overwrite an + * existing file! + */ + if (mch_stat((char *)tempname, &st_new) == 0) + { +#ifdef UNIX + /* + * Check if tempfile is same as original file. May happen + * when modname() gave the same file back. E.g. silly + * link, or file name-length reached. Try again with + * shortname set. + */ + if (!shortname && st_new.st_dev == st_old.st_dev + && st_new.st_ino == st_old.st_ino) + { + VIM_CLEAR(tempname); + shortname = TRUE; + break; + } +#endif + } + else + { + /* Try creating the file exclusively. This may fail if + * another Vim tries to do it at the same time. */ +#ifdef VMS + /* fdopen() fails for some reason */ + umask_save = umask(077); + fp_out = mch_fopen((char *)tempname, WRITEBIN); + (void)umask(umask_save); +#else + 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_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, + (int)((st_old.st_mode & 0777) | 0600)); + (void)umask(umask_save); +# else + fd = mch_open((char *)tempname, + O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600); +# endif + if (fd < 0) + { + fp_out = NULL; +# ifdef EEXIST + /* Avoid trying lots of names while the problem is lack + * of premission, only retry if the file already + * exists. */ + if (errno != EEXIST) + break; +# endif + } + else + fp_out = fdopen(fd, WRITEBIN); +#endif /* VMS */ + if (fp_out != NULL) + break; + } + + /* Assume file exists, try again with another name. */ + if (next_char == 'a' - 1) + { + /* They all exist? Must be something wrong! Don't write + * the viminfo file then. */ + semsg(_("E929: Too many viminfo temp files, like %s!"), + tempname); + break; + } + *wp = next_char; + --next_char; + } + + if (tempname != NULL) + break; + /* continue if shortname was set */ + } + +#if defined(UNIX) && defined(HAVE_FCHOWN) + if (tempname != NULL && fp_out != NULL) + { + stat_T tmp_st; + + /* + * Make sure the original owner can read/write the tempfile and + * otherwise preserve permissions, making sure the group matches. + */ + if (mch_stat((char *)tempname, &tmp_st) >= 0) + { + if (st_old.st_uid != tmp_st.st_uid) + /* Changing the owner might fail, in which case the + * file will now owned by the current user, oh well. */ + vim_ignored = fchown(fileno(fp_out), st_old.st_uid, -1); + if (st_old.st_gid != tmp_st.st_gid + && fchown(fileno(fp_out), -1, st_old.st_gid) == -1) + /* can't set the group to what it should be, remove + * group permissions */ + (void)mch_setperm(tempname, 0600); + } + else + /* can't stat the file, set conservative permissions */ + (void)mch_setperm(tempname, 0600); + } +#endif + } + + /* + * Check if the new viminfo file can be written to. + */ + if (fp_out == NULL) + { + semsg(_("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(_("Writing viminfo file \"%s\""), fname); + verbose_leave(); + } + + viminfo_errcnt = 0; + do_viminfo(fp_in, fp_out, forceit ? 0 : (VIF_WANT_INFO | VIF_WANT_MARKS)); + + if (fclose(fp_out) == EOF) + ++viminfo_errcnt; + + if (fp_in != NULL) + { + fclose(fp_in); + + /* In case of an error keep the original viminfo file. Otherwise + * rename the newly written file. Give an error if that fails. */ + if (viminfo_errcnt == 0) + { + if (vim_rename(tempname, fname) == -1) + { + ++viminfo_errcnt; + semsg(_("E886: Can't rename viminfo file to %s!"), fname); + } +# ifdef WIN3264 + /* If the viminfo file was hidden then also hide the new file. */ + else if (hidden) + mch_hide(fname); +# endif + } + if (viminfo_errcnt > 0) + mch_remove(tempname); + } + +end: + vim_free(fname); + vim_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 (*p_viminfofile != NUL) + file = p_viminfofile; + else if ((file = find_viminfo_parameter('n')) == NULL || *file == NUL) + { +#ifdef VIMINFO_FILE2 +# ifdef VMS + if (mch_getenv((char_u *)"SYS$LOGIN") == NULL) +# else +# ifdef MSWIN + /* Use $VIM only if $HOME is the default "C:/". */ + if (STRCMP(vim_getenv((char_u *)"HOME", NULL), "C:/") == 0 + && mch_getenv((char_u *)"HOME") == NULL) +# else + if (mch_getenv((char_u *)"HOME") == NULL) +# endif +# endif + { + /* 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 eof = FALSE; + vir_T vir; + int merge = FALSE; + int do_copy_marks = FALSE; + garray_T buflist; + + if ((vir.vir_line = alloc(LSIZE)) == NULL) + return; + vir.vir_fd = fp_in; + vir.vir_conv.vc_type = CONV_NONE; + ga_init2(&vir.vir_barlines, (int)sizeof(char_u *), 100); + vir.vir_version = -1; + + if (fp_in != NULL) + { + if (flags & VIF_WANT_INFO) + { + if (fp_out != NULL) + { + /* Registers and marks are read and kept separate from what + * this Vim is using. They are merged when writing. */ + prepare_viminfo_registers(); + prepare_viminfo_marks(); + } + + 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] != '>') + ; + + do_copy_marks = (flags & + (VIF_WANT_MARKS | VIF_GET_OLDFILES | VIF_FORCEIT)); + } + + 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); + write_viminfo_version(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); +#ifdef FEAT_CMDHIST + write_viminfo_history(fp_out, merge); +#endif + write_viminfo_registers(fp_out); + finish_viminfo_registers(); +#ifdef FEAT_EVAL + write_viminfo_varlist(fp_out); +#endif + write_viminfo_filemarks(fp_out); + finish_viminfo_marks(); + write_viminfo_bufferlist(fp_out); + write_viminfo_barlines(&vir, fp_out); + + if (do_copy_marks) + ga_init2(&buflist, sizeof(buf_T *), 50); + write_viminfo_marks(fp_out, do_copy_marks ? &buflist : NULL); + } + + if (do_copy_marks) + { + copy_viminfo_marks(&vir, fp_out, &buflist, eof, flags); + if (fp_out != NULL) + ga_clear(&buflist); + } + + vim_free(vir.vir_line); + if (vir.vir_conv.vc_type != CONV_NONE) + convert_setup(&vir.vir_conv, NULL, NULL); + ga_clear_strings(&vir.vir_barlines); +} + +/* + * 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; + int got_encoding = FALSE; + +#ifdef FEAT_CMDHIST + prepare_viminfo_history(forceit ? 9999 : 0, writing); +#endif + + 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 '<': /* long line - ignored */ + /* A comment or empty line. */ + case NUL: + case '\r': + case '\n': + case '#': + eof = viminfo_readline(virp); + break; + case '|': + eof = read_viminfo_barline(virp, got_encoding, + forceit, writing); + break; + case '*': /* "*encoding=value" */ + got_encoding = TRUE; + eof = viminfo_encoding(virp); + break; + case '!': /* global variable */ +#ifdef FEAT_EVAL + eof = read_viminfo_varlist(virp, writing); +#else + eof = viminfo_readline(virp); +#endif + break; + case '%': /* entry for buffer list */ + eof = read_viminfo_bufferlist(virp, writing); + break; + case '"': + /* When registers are in bar lines skip the old style register + * lines. */ + if (virp->vir_version < VIMINFO_VERSION_WITH_REGISTERS) + eof = read_viminfo_register(virp, forceit); + else + do { + eof = viminfo_readline(virp); + } while (!eof && (virp->vir_line[0] == TAB + || virp->vir_line[0] == '<')); + 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 '@': +#ifdef FEAT_CMDHIST + /* When history is in bar lines skip the old style history + * lines. */ + if (virp->vir_version < VIMINFO_VERSION_WITH_HISTORY) + eof = read_viminfo_history(virp, writing); + else +#endif + eof = viminfo_readline(virp); + break; + case '-': + case '\'': + /* When file marks are in bar lines skip the old style lines. */ + if (virp->vir_version < VIMINFO_VERSION_WITH_MARKS) + eof = read_viminfo_filemark(virp, forceit); + else + eof = viminfo_readline(virp); + break; + default: + if (viminfo_error("E575: ", _("Illegal starting char"), + virp->vir_line)) + eof = TRUE; + else + eof = viminfo_readline(virp); + break; + } + } + +#ifdef FEAT_CMDHIST + /* Finish reading history items. */ + if (!writing) + finish_viminfo_history(virp); +#endif + + /* Change file names to buffer numbers for fmarks. */ + FOR_ALL_BUFFERS(buf) + 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 UNUSED) /* convert the string */ +{ + char_u *retval; + char_u *s, *d; + long len; + + if (virp->vir_line[off] == Ctrl_V && vim_isdigit(virp->vir_line[off + 1])) + { + len = atol((char *)virp->vir_line + off + 1); + retval = lalloc(len, TRUE); + if (retval == NULL) + { + /* Line too long? File messed up? Skip next line. */ + (void)vim_fgets(virp->vir_line, 10, virp->vir_fd); + return NULL; + } + (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) + { + vim_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); +} + +/* + * Write a string in quotes that barline_parse() can read back. + * Breaks the line in less than LSIZE pieces when needed. + * Returns remaining characters in the line. + */ + int +barline_writestring(FILE *fd, char_u *s, int remaining_start) +{ + char_u *p; + int remaining = remaining_start; + int len = 2; + + /* Count the number of characters produced, including quotes. */ + for (p = s; *p != NUL; ++p) + { + if (*p == NL) + len += 2; + else if (*p == '"' || *p == '\\') + len += 2; + else + ++len; + } + if (len > remaining - 2) + { + fprintf(fd, ">%d\n|<", len); + remaining = LSIZE - 20; + } + + putc('"', fd); + for (p = s; *p != NUL; ++p) + { + if (*p == NL) + { + putc('\\', fd); + putc('n', fd); + --remaining; + } + else if (*p == '"' || *p == '\\') + { + putc('\\', fd); + putc(*p, fd); + --remaining; + } + else + putc(*p, fd); + --remaining; + + if (remaining < 3) + { + putc('\n', fd); + putc('|', fd); + putc('<', fd); + /* Leave enough space for another continuation. */ + remaining = LSIZE - 20; + } + } + putc('"', fd); + return remaining - 2; +} + +/* + * Parse a viminfo line starting with '|'. + * Add each decoded value to "values". + * Returns TRUE if the next line is to be read after using the parsed values. + */ + static int +barline_parse(vir_T *virp, char_u *text, garray_T *values) +{ + char_u *p = text; + char_u *nextp = NULL; + char_u *buf = NULL; + bval_T *value; + int i; + int allocated = FALSE; + int eof; + char_u *sconv; + int converted; + + while (*p == ',') + { + ++p; + if (ga_grow(values, 1) == FAIL) + break; + value = (bval_T *)(values->ga_data) + values->ga_len; + + if (*p == '>') + { + /* Need to read a continuation line. Put strings in allocated + * memory, because virp->vir_line is overwritten. */ + if (!allocated) + { + for (i = 0; i < values->ga_len; ++i) + { + bval_T *vp = (bval_T *)(values->ga_data) + i; + + if (vp->bv_type == BVAL_STRING && !vp->bv_allocated) + { + vp->bv_string = vim_strnsave(vp->bv_string, vp->bv_len); + vp->bv_allocated = TRUE; + } + } + allocated = TRUE; + } + + if (vim_isdigit(p[1])) + { + size_t len; + size_t todo; + size_t n; + + /* String value was split into lines that are each shorter + * than LSIZE: + * |{bartype},>{length of "{text}{text2}"} + * |<"{text1} + * |<{text2}",{value} + * Length includes the quotes. + */ + ++p; + len = getdigits(&p); + buf = alloc((int)(len + 1)); + if (buf == NULL) + return TRUE; + p = buf; + for (todo = len; todo > 0; todo -= n) + { + eof = viminfo_readline(virp); + if (eof || virp->vir_line[0] != '|' + || virp->vir_line[1] != '<') + { + /* File was truncated or garbled. Read another line if + * this one starts with '|'. */ + vim_free(buf); + return eof || virp->vir_line[0] == '|'; + } + /* Get length of text, excluding |< and NL chars. */ + n = STRLEN(virp->vir_line); + while (n > 0 && (virp->vir_line[n - 1] == NL + || virp->vir_line[n - 1] == CAR)) + --n; + n -= 2; + if (n > todo) + { + /* more values follow after the string */ + nextp = virp->vir_line + 2 + todo; + n = todo; + } + mch_memmove(p, virp->vir_line + 2, n); + p += n; + } + *p = NUL; + p = buf; + } + else + { + /* Line ending in ">" continues in the next line: + * |{bartype},{lots of values},> + * |<{value},{value} + */ + eof = viminfo_readline(virp); + if (eof || virp->vir_line[0] != '|' + || virp->vir_line[1] != '<') + /* File was truncated or garbled. Read another line if + * this one starts with '|'. */ + return eof || virp->vir_line[0] == '|'; + p = virp->vir_line + 2; + } + } + + if (isdigit(*p)) + { + value->bv_type = BVAL_NR; + value->bv_nr = getdigits(&p); + ++values->ga_len; + } + else if (*p == '"') + { + int len = 0; + char_u *s = p; + + /* Unescape special characters in-place. */ + ++p; + while (*p != '"') + { + if (*p == NL || *p == NUL) + return TRUE; /* syntax error, drop the value */ + if (*p == '\\') + { + ++p; + if (*p == 'n') + s[len++] = '\n'; + else + s[len++] = *p; + ++p; + } + else + s[len++] = *p++; + } + ++p; + s[len] = NUL; + + converted = FALSE; + if (virp->vir_conv.vc_type != CONV_NONE && *s != NUL) + { + sconv = string_convert(&virp->vir_conv, s, NULL); + if (sconv != NULL) + { + if (s == buf) + vim_free(s); + s = sconv; + buf = s; + converted = TRUE; + } + } + + /* Need to copy in allocated memory if the string wasn't allocated + * above and we did allocate before, thus vir_line may change. */ + if (s != buf && allocated) + s = vim_strsave(s); + value->bv_string = s; + value->bv_type = BVAL_STRING; + value->bv_len = len; + value->bv_allocated = allocated || converted; + ++values->ga_len; + if (nextp != NULL) + { + /* values following a long string */ + p = nextp; + nextp = NULL; + } + } + else if (*p == ',') + { + value->bv_type = BVAL_EMPTY; + ++values->ga_len; + } + else + break; + } + return TRUE; +} + + static int +read_viminfo_barline(vir_T *virp, int got_encoding, int force, int writing) +{ + char_u *p = virp->vir_line + 1; + int bartype; + garray_T values; + bval_T *vp; + int i; + int read_next = TRUE; + + /* The format is: |{bartype},{value},... + * For a very long string: + * |{bartype},>{length of "{text}{text2}"} + * |<{text1} + * |<{text2},{value} + * For a long line not using a string + * |{bartype},{lots of values},> + * |<{value},{value} + */ + if (*p == '<') + { + /* Continuation line of an unrecognized item. */ + if (writing) + ga_add_string(&virp->vir_barlines, virp->vir_line); + } + else + { + ga_init2(&values, sizeof(bval_T), 20); + bartype = getdigits(&p); + switch (bartype) + { + case BARTYPE_VERSION: + /* Only use the version when it comes before the encoding. + * If it comes later it was copied by a Vim version that + * doesn't understand the version. */ + if (!got_encoding) + { + read_next = barline_parse(virp, p, &values); + vp = (bval_T *)values.ga_data; + if (values.ga_len > 0 && vp->bv_type == BVAL_NR) + virp->vir_version = vp->bv_nr; + } + break; + + case BARTYPE_HISTORY: + read_next = barline_parse(virp, p, &values); + handle_viminfo_history(&values, writing); + break; + + case BARTYPE_REGISTER: + read_next = barline_parse(virp, p, &values); + handle_viminfo_register(&values, force); + break; + + case BARTYPE_MARK: + read_next = barline_parse(virp, p, &values); + handle_viminfo_mark(&values, force); + break; + + default: + /* copy unrecognized line (for future use) */ + if (writing) + ga_add_string(&virp->vir_barlines, virp->vir_line); + } + for (i = 0; i < values.ga_len; ++i) + { + vp = (bval_T *)values.ga_data + i; + if (vp->bv_type == BVAL_STRING && vp->bv_allocated) + vim_free(vp->bv_string); + } + ga_clear(&values); + } + + if (read_next) + return viminfo_readline(virp); + return FALSE; +} + + static void +write_viminfo_version(FILE *fp_out) +{ + fprintf(fp_out, "# Viminfo version\n|%d,%d\n\n", + BARTYPE_VERSION, VIMINFO_VERSION); +} + + static void +write_viminfo_barlines(vir_T *virp, FILE *fp_out) +{ + int i; + garray_T *gap = &virp->vir_barlines; + int seen_useful = FALSE; + char *line; + + if (gap->ga_len > 0) + { + fputs(_("\n# Bar lines, copied verbatim:\n"), fp_out); + + /* Skip over continuation lines until seeing a useful line. */ + for (i = 0; i < gap->ga_len; ++i) + { + line = ((char **)(gap->ga_data))[i]; + if (seen_useful || line[1] != '<') + { + fputs(line, fp_out); + seen_useful = TRUE; + } + } + } +} +#endif /* FEAT_VIMINFO */ + +/* + * Return the current time in seconds. Calls time(), unless test_settime() + * was used. + */ + time_T +vim_time(void) +{ +# ifdef FEAT_EVAL + return time_for_testing == 0 ? time(NULL) : time_for_testing; +# else + return time(NULL); +# endif +} + +/* + * Implementation of ":fixdel", also used by get_stty(). + * resulting + * ^? ^H + * not ^? ^? + */ + void +do_fixdel(exarg_T *eap UNUSED) +{ + 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 numbuf[30]; + + if (curwin->w_p_nu || use_number) + { + vim_snprintf(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; + + /* apply :filter /pat/ */ + if (message_filtered(ml_get(lnum))) + return; + + 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; +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return FAIL; +#endif + /* + * 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; + } + vim_free(fname); + vim_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; + redraw_tabline = TRUE; + } + + // print file name if no argument or 'F' is not in 'shortmess' + if (*eap->arg == NUL || !shortmess(SHM_FILEINFO)) + 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; +#ifdef FEAT_BROWSE + char_u *browse_file = NULL; +#endif + buf_T *alt_buf = NULL; + int name_was_missing; + + if (not_writing()) /* check 'write' option */ + return FAIL; + + ffname = eap->arg; +#ifdef FEAT_BROWSE + if (cmdmod.browse) + { + browse_file = do_browse(BROWSE_SAVE, (char_u *)_("Save As"), ffname, + NULL, NULL, NULL, curbuf); + if (browse_file == NULL) + goto theend; + ffname = browse_file; + } +#endif + 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 && ( +#ifdef FEAT_QUICKFIX + bt_dontwrite_msg(curbuf) || +#endif + 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 defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + 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 +#endif + { + 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); +#ifdef FEAT_EVAL + if (curbuf != was_curbuf || aborting()) +#else + if (curbuf != was_curbuf) +#endif + { + /* 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); + } +#ifdef FEAT_EVAL + if (curbuf != was_curbuf || aborting()) +#else + if (curbuf != was_curbuf) +#endif + { + /* 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, NULL); + do_modelines(0); + } + + /* Autocommands may have changed buffer names, esp. when + * 'autochdir' is set. */ + fname = curbuf->b_sfname; + } + + name_was_missing = curbuf->b_ffname == NULL; + + 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 and the file name + * got changed or set. */ + if (eap->cmdidx == CMD_saveas || name_was_missing) + { + DO_AUTOCHDIR; + } + } + +theend: +#ifdef FEAT_BROWSE + vim_free(browse_file); +#endif + vim_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 +#ifdef FEAT_QUICKFIX + && !bt_nofile(buf) +#endif + && vim_fexists(ffname)) + { + if (!eap->forceit && !eap->append) + { +#ifdef UNIX + /* with UNIX it is possible to open a directory */ + if (mch_isdir(ffname)) + { + semsg(_(e_isadir2), ffname); + return FAIL; + } +#endif +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + 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 +#endif + { + 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; + int r; + 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 = alloc(5); + if (dir == NULL) + return FAIL; + STRCPY(dir, "."); + } + else + { + dir = alloc(MAXPATHL); + if (dir == NULL) + return FAIL; + p = p_dir; + copy_option_part(&p, dir, MAXPATHL, ","); + } + swapname = makeswapname(fname, ffname, curbuf, dir); + vim_free(dir); + r = vim_fexists(swapname); + if (r) + { +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + 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) + { + vim_free(swapname); + return FAIL; + } + eap->forceit = TRUE; + } + else +#endif + { + semsg(_("E768: Swap file exists: %s (:silent! overrides)"), + swapname); + vim_free(swapname); + return FAIL; + } + } + vim_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_ALL_BUFFERS(buf) + { +#ifdef FEAT_TERMINAL + if (exiting && term_job_running(buf->b_term)) + { + no_write_message_nobang(buf); + ++error; + } + else +#endif + if (bufIsChanged(buf) && !bt_dontwrite(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; + } +#ifdef FEAT_BROWSE + /* ":browse wall": ask for file name if there isn't one */ + if (buf->b_ffname == NULL && cmdmod.browse) + browse_save_fname(buf); +#endif + if (buf->b_ffname == NULL) + { + semsg(_("E141: No file name for buffer %ld"), (long)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 + { + bufref_T bufref; + + set_bufref(&bufref, buf); + if (buf_write_all(buf, eap->forceit) == FAIL) + ++error; + /* an autocommand may have deleted the buffer */ + if (!bufref_valid(&bufref)) + 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) +{ + stat_T st; + + /* Handle a file being readonly when the 'readonly' option is set or when + * the file exists and permissions are read-only. + * We will send 0777 to check_file_readonly(), as the "perm" variable is + * important for device checks but not here. */ + if (!*forceit && (buf->b_p_ro + || (mch_stat((char *)buf->b_ffname, &st) >= 0 + && check_file_readonly(buf->b_ffname, 0777)))) + { +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + 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 +#endif + if (buf->b_p_ro) + emsg(_(e_readonly)); + else + semsg(_("E505: \"%s\" is read-only (add ! to override)"), + buf->b_fname); + return TRUE; + } + + return FALSE; +} + +/* + * Try to abandon the current file and edit a new or existing file. + * "fnum" is the number of the file, if zero use "ffname_arg"/"sfname_arg". + * "lnum" is the line number for the cursor in the new file (if non-zero). + * + * Return: + * GETFILE_ERROR for "normal" error, + * GETFILE_NOT_WRITTEN for "not written" error, + * GETFILE_SAME_FILE for success + * GETFILE_OPEN_OTHER for successfully opening another file. + */ + int +getfile( + int fnum, + char_u *ffname_arg, + char_u *sfname_arg, + int setpm, + linenr_T lnum, + int forceit) +{ + char_u *ffname = ffname_arg; + char_u *sfname = sfname_arg; + int other; + int retval; + char_u *free_me = NULL; + + if (text_locked()) + return GETFILE_ERROR; + if (curbuf_locked()) + return GETFILE_ERROR; + + 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 && !buf_hide(curbuf) + && curbufIsChanged() && autowrite(curbuf, forceit) == FAIL) + { +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + if (p_confirm && p_write) + dialog_changed(curbuf, FALSE); + if (curbufIsChanged()) +#endif + { + if (other) + --no_wait_return; + no_write_message(); + retval = GETFILE_NOT_WRITTEN; /* 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 = GETFILE_SAME_FILE; /* it's in the same file */ + } + else if (do_ecmd(fnum, ffname, sfname, NULL, lnum, + (buf_hide(curbuf) ? ECMD_HIDE : 0) + (forceit ? ECMD_FORCEIT : 0), + curwin) == OK) + retval = GETFILE_OPEN_OTHER; /* opened another file */ + else + retval = GETFILE_ERROR; /* error encountered */ + +theend: + vim_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; +#if defined(FEAT_EVAL) + int did_set_swapcommand = FALSE; +#endif + buf_T *buf; + bufref_T bufref; + bufref_T old_curbuf; + char_u *free_fname = NULL; +#ifdef FEAT_BROWSE + char_u *browse_file = NULL; +#endif + int retval = FAIL; + long n; + pos_T orig_pos; + linenr_T topline = 0; + int newcol = -1; + int solcol = -1; + pos_T *pos; + char_u *command = NULL; +#ifdef FEAT_SPELL + int did_get_winopts = FALSE; +#endif + int readfile_flags = 0; + int did_inc_redrawing_disabled = FALSE; + long *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so; + + if (eap != NULL) + command = eap->do_ecmd_cmd; + set_bufref(&old_curbuf, curbuf); + + if (fnum != 0) + { + if (fnum == curbuf->b_fnum) /* file is already being edited */ + return OK; /* nothing to do */ + other_file = TRUE; + } + else + { +#ifdef FEAT_BROWSE + if (cmdmod.browse) + { + if ( +# ifdef FEAT_GUI + !gui.in_use && +# endif + au_has_group((char_u *)"FileExplorer")) + { + /* No browsing supported but we do have the file explorer: + * Edit the directory. */ + if (ffname == NULL || !mch_isdir(ffname)) + ffname = (char_u *)"."; + } + else + { + browse_file = do_browse(0, (char_u *)_("Edit File"), ffname, + NULL, NULL, NULL, curbuf); + if (browse_file == NULL) + goto theend; + ffname = browse_file; + } + } +#endif + /* if no short name given, use ffname for short name */ + if (sfname == NULL) + sfname = ffname; +#ifdef USE_FNAME_CASE +# ifdef USE_LONG_FNAME + if (USE_LONG_FNAME) +# endif + 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 defined(FEAT_EVAL) + if ((command != NULL || newlnum > (linenr_T)0) + && *get_vim_var_str(VV_SWAPCOMMAND) == NUL) + { + int len; + char_u *p; + + /* Set v:swapcommand for the SwapExists autocommands. */ + if (command != NULL) + len = (int)STRLEN(command) + 3; + else + len = 30; + p = alloc((unsigned)len); + if (p != NULL) + { + if (command != NULL) + vim_snprintf((char *)p, len, ":%s\r", command); + else + vim_snprintf((char *)p, len, "%ldG", (long)newlnum); + set_vim_var_string(VV_SWAPCOMMAND, p, -1); + did_set_swapcommand = TRUE; + vim_free(p); + } + } +#endif + + /* + * 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)); + + /* autocommands may change curwin and curbuf */ + if (oldwin != NULL) + oldwin = curwin; + set_bufref(&old_curbuf, curbuf); + } + if (buf == NULL) + goto theend; + if (buf->b_ml.ml_mfp == NULL) /* no memfile yet */ + { + oldbuf = FALSE; + } + else /* existing memfile */ + { + oldbuf = TRUE; + set_bufref(&bufref, buf); + (void)buf_check_timestamp(buf, FALSE); + /* Check if autocommands made the buffer invalid or changed the + * current buffer. */ + if (!bufref_valid(&bufref) || curbuf != old_curbuf.br_buf) + goto theend; +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + goto theend; +#endif + } + + /* 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(), nothing to do here. + */ + 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); + set_bufref(&au_new_curbuf, buf); + apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); + if (!bufref_valid(&au_new_curbuf)) + { + /* new buffer has been deleted */ + delbuf_msg(new_name); /* frees new_name */ + goto theend; + } +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + { + vim_free(new_name); + goto theend; + } +#endif + if (buf == curbuf) /* already in new buffer */ + auto_buf = TRUE; + else + { + win_T *the_curwin = curwin; + + /* Set the w_closing flag to avoid that autocommands close the + * window. And set b_locked for the same reason. */ + the_curwin->w_closing = TRUE; + ++buf->b_locked; + + if (curbuf == old_curbuf.br_buf) + buf_copy_options(buf, BCO_ENTER); + + /* Close the link to the current buffer. This will set + * oldwin->w_buffer to NULL. */ + u_sync(FALSE); + close_buffer(oldwin, curbuf, + (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE); + + the_curwin->w_closing = FALSE; + --buf->b_locked; + +#ifdef FEAT_EVAL + /* autocmds may abort script processing */ + if (aborting() && curwin->w_buffer != NULL) + { + vim_free(new_name); + goto theend; + } +#endif + /* Be careful again, like above. */ + if (!bufref_valid(&au_new_curbuf)) + { + /* 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 + { +#ifdef FEAT_SYN_HL + /* + * 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); +#endif + 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); +#ifdef FEAT_SPELL + did_get_winopts = TRUE; +#endif + } + vim_free(new_name); + au_new_curbuf.br_buf = NULL; + au_new_curbuf.br_buf_free_count = 0; + } + + 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); + } + + /* Don't redraw until the cursor is in the right line, otherwise + * autocommands may cause ml_get errors. */ + ++RedrawingDisabled; + did_inc_redrawing_disabled = TRUE; + + buf = curbuf; + if ((flags & ECMD_SET_HELP) || keep_help_flag) + { + prepare_help_buffer(); + } + else + { + /* 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; +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + goto theend; +#endif + + /* 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; + set_bufref(&bufref, buf); + + 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) + { + vim_free(new_name); + 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 (!bufref_valid(&bufref)) + { + delbuf_msg(new_name); /* frees new_name */ + goto theend; + } + vim_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; +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + goto theend; +#endif + 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 + */ + /* Assume success now */ + retval = OK; + + /* + * 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(); + +#ifdef FEAT_FOLDING + /* 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); + } +#endif + + /* Change directories when the 'acd' option is set. */ + DO_AUTOCHDIR; + + /* + * Careful: open_buffer() and apply_autocmds() may change the current + * buffer and window. + */ + orig_pos = curwin->w_cursor; + 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 defined(FEAT_EVAL) + if (should_abort(open_buffer(FALSE, eap, readfile_flags))) + retval = FAIL; +#else + (void)open_buffer(FALSE, eap, readfile_flags); +#endif + +#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. Also when it moves within a line. But not when it moves + * to the first non-blank. */ + if (!EQUAL_POS(curwin->w_cursor, orig_pos)) + { + char_u *text = ml_get_curline(); + + if (curwin->w_cursor.lnum != orig_pos.lnum + || curwin->w_cursor.col != (int)(skipwhite(text) - text)) + { + 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(); + +#ifdef FEAT_TITLE + maketitle(); +#endif + } + +#ifdef FEAT_DIFF + /* 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); + } +#endif + +#ifdef FEAT_SPELL + /* 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); +#endif + + 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; + + if (!shortmess(SHM_FILEINFO)) + fileinfo(FALSE, TRUE, FALSE); + + msg_scrolled_ign = FALSE; + } + +#ifdef FEAT_VIMINFO + curbuf->b_last_used = vim_time(); +#endif + + if (command != NULL) + do_cmdline(command, NULL, NULL, DOCMD_VERBOSE); + +#ifdef FEAT_KEYMAP + if (curbuf->b_kmap_state & KEYMAP_INIT) + (void)keymap_init(); +#endif + + --RedrawingDisabled; + did_inc_redrawing_disabled = FALSE; + if (!skip_redraw) + { + n = *so_ptr; + if (topline == 0 && command == NULL) + *so_ptr = 9999; // force cursor halfway the window + update_topline(); + curwin->w_scbind_pos = curwin->w_topline; + *so_ptr = n; + redraw_curbuf_later(NOT_VALID); /* redraw this buffer later */ + } + + if (p_im) + need_start_insertmode = TRUE; + +#ifdef FEAT_AUTOCHDIR + /* Change directories when the 'acd' option is set and we aren't already in + * that directory (should already be done above). Expect getcwd() to be + * faster than calling shorten_fnames() unnecessarily. */ + if (p_acd && curbuf->b_ffname != NULL) + { + char_u curdir[MAXPATHL]; + char_u filedir[MAXPATHL]; + + vim_strncpy(filedir, curbuf->b_ffname, MAXPATHL - 1); + *gettail_sep(filedir) = NUL; + if (mch_dirname(curdir, MAXPATHL) != FAIL + && vim_fnamecmp(curdir, filedir) != 0) + do_autochdir(); + } +#endif + +#if defined(FEAT_NETBEANS_INTG) + if (curbuf->b_ffname != NULL) + { +# ifdef FEAT_NETBEANS_INTG + if ((flags & ECMD_SET_HELP) != ECMD_SET_HELP) + netbeans_file_opened(curbuf); +# endif + } +#endif + +theend: + if (did_inc_redrawing_disabled) + --RedrawingDisabled; +#if defined(FEAT_EVAL) + if (did_set_swapcommand) + set_vim_var_string(VV_SWAPCOMMAND, NULL, -1); +#endif +#ifdef FEAT_BROWSE + vim_free(browse_file); +#endif + vim_free(free_fname); + return retval; +} + + static void +delbuf_msg(char_u *name) +{ + semsg(_("E143: Autocommands unexpectedly deleted new buffer %s"), + name == NULL ? (char_u *)"" : name); + vim_free(name); + au_new_curbuf.br_buf = NULL; + au_new_curbuf.br_buf_free_count = 0; +} + +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 need to 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 + { + int save_State = State; + + /* Set State to avoid the cursor shape to be set to INSERT mode + * when getline() returns. */ + State = CMDLINE; + theline = eap->getline( +#ifdef FEAT_EVAL + eap->cstack->cs_looplevel > 0 ? -1 : +#endif + NUL, eap->cookie, indent); + State = save_State; + } + 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)) + { + vim_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 + (empty ? 1 : 0), 1L); + + vim_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; + long 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 (!ONE_WINDOW) + bigness = curwin->w_height - 3; + else + bigness = curwin->w_p_scr * 2; + 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 = atol((char *)x); + + /* bigness could be < 0 if atol(x) overflows. */ + if (bigness > 2 * curbuf->b_ml.ml_line_count || bigness < 0) + bigness = 2 * curbuf->b_ml.ml_line_count; + + 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; + else if (curs < 1) + curs = 1; + + 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('-'); + } + } + + if (curwin->w_cursor.lnum != curs) + { + curwin->w_cursor.lnum = curs; + curwin->w_cursor.col = 0; + } + 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" */ + +/* + * Flags that are kept between calls to :substitute. + */ +typedef struct { + int do_all; /* do multiple substitutions per line */ + int do_ask; /* ask for confirmation */ + int do_count; /* count only */ + int do_error; /* if false, ignore errors */ + int do_print; /* print last line with subs. */ + int do_list; /* list last line with subs. */ + int do_number; /* list last line with line nr*/ + int do_ic; /* ignore case flag */ +} subflags_T; + +/* 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 subflags_T subflags = {FALSE, FALSE, FALSE, TRUE, FALSE, + FALSE, FALSE, 0}; +#ifdef FEAT_EVAL + subflags_T subflags_save; +#endif + int save_do_all; /* remember user specified 'g' flag */ + int save_do_ask; /* remember user specified 'c' 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; +#ifdef FEAT_EVAL + int save_ma = 0; +#endif + + 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 */ + { +#ifdef FEAT_FKMAP /* reverse the flow of the Farsi characters */ + if (p_altkeymap && curwin->w_p_rl) + lrF_sub(cmd); +#endif + 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 + { + vim_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(pat, "\\n") == 0 + && *sub == NUL + && (*cmd == NUL || (cmd[1] == NUL && (*cmd == 'g' || *cmd == 'l' + || *cmd == 'p' || *cmd == '#')))) + { + linenr_T joined_lines_count; + + 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; + + /* The number of lines joined is the number of lines in the range plus + * one. One less when the last line is included. */ + joined_lines_count = eap->line2 - eap->line1 + 1; + if (eap->line2 < curbuf->b_ml.ml_line_count) + ++joined_lines_count; + if (joined_lines_count > 1) + { + (void)do_join(joined_lines_count, FALSE, TRUE, FALSE, TRUE); + sub_nsubs = joined_lines_count - 1; + sub_nlines = 1; + (void)do_sub_msg(FALSE); + ex_may_print(eap); + } + + if (!cmdmod.keeppatterns) + save_re_pat(RE_SUBST, pat, p_magic); +#ifdef FEAT_CMDHIST + /* put pattern in history */ + add_to_history(HIST_SEARCH, pat, TRUE, NUL); +#endif + + return; + } + + /* + * Find trailing options. When '&' is used, keep old options. + */ + if (*cmd == '&') + ++cmd; + else + { + if (!p_ed) + { + if (p_gd) /* default is global on */ + subflags.do_all = TRUE; + else + subflags.do_all = FALSE; + subflags.do_ask = FALSE; + } + subflags.do_error = TRUE; + subflags.do_print = FALSE; + subflags.do_count = FALSE; + subflags.do_number = FALSE; + subflags.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') + subflags.do_all = !subflags.do_all; + else if (*cmd == 'c') + subflags.do_ask = !subflags.do_ask; + else if (*cmd == 'n') + subflags.do_count = TRUE; + else if (*cmd == 'e') + subflags.do_error = !subflags.do_error; + else if (*cmd == 'r') /* use last used regexp */ + which_pat = RE_LAST; + else if (*cmd == 'p') + subflags.do_print = TRUE; + else if (*cmd == '#') + { + subflags.do_print = TRUE; + subflags.do_number = TRUE; + } + else if (*cmd == 'l') + { + subflags.do_print = TRUE; + subflags.do_list = TRUE; + } + else if (*cmd == 'i') /* ignore case */ + subflags.do_ic = 'i'; + else if (*cmd == 'I') /* don't ignore case */ + subflags.do_ic = 'I'; + else + break; + ++cmd; + } + if (subflags.do_count) + subflags.do_ask = FALSE; + + save_do_all = subflags.do_all; + save_do_ask = subflags.do_ask; + + /* + * check for a trailing count + */ + cmd = skipwhite(cmd); + if (VIM_ISDIGIT(*cmd)) + { + i = getdigits(&cmd); + if (i <= 0 && !eap->skip && subflags.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 (!subflags.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 (subflags.do_error) + emsg(_(e_invcmd)); + return; + } + + /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */ + if (subflags.do_ic == 'i') + regmatch.rmm_ic = TRUE; + else if (subflags.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 +#if defined(FEAT_EVAL) + || aborting() +#endif + ); ++lnum) + { + nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, + (colnr_T)0, NULL, 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 alloc()/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; + VIM_CLEAR(sub_firstline); + } + + if (sub_firstline == NULL) + { + sub_firstline = vim_strsave(ml_get(sub_firstlnum)); + if (sub_firstline == NULL) + { + vim_free(new_start); + goto outofmem; + } + } + + /* 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 (subflags.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; +#ifdef FEAT_EVAL + /* Skip the substitution, unless an expression is used, + * then it is evaluated in the sandbox. */ + if (!(sub[0] == '\\' && sub[1] == '=')) +#endif + goto skip; + } + + if (subflags.do_ask) + { + int typed = 0; + + /* change State to CONFIRM, so that the mouse works + * properly */ + save_State = State; + State = CONFIRM; +#ifdef FEAT_MOUSE + setmouse(); /* disable mouse in xterm */ +#endif + curwin->w_cursor.col = regmatch.startpos[0].col; + if (curwin->w_p_crb) + do_check_cursorbind(); + + /* 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 (subflags.do_ask) + { + if (exmode_active) + { + char_u *resp; + colnr_T sc, ec; + + print_line_no_prefix(lnum, + subflags.do_number, subflags.do_list); + + getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL); + curwin->w_cursor.col = regmatch.endpos[0].col - 1; + if (curwin->w_cursor.col < 0) + curwin->w_cursor.col = 0; + getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec); + if (subflags.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; + vim_free(resp); + } + } + else + { + char_u *orig_line = NULL; + int len_change = 0; +#ifdef FEAT_FOLDING + int save_p_fen = curwin->w_p_fen; + + curwin->w_p_fen = FALSE; +#endif + /* 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); + + if (new_line == NULL) + VIM_CLEAR(orig_line); + else + { + /* 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); + +#ifdef FEAT_FOLDING + curwin->w_p_fen = save_p_fen; +#endif + 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), + _("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 */ + subflags.do_all = FALSE; + line2 = lnum; + break; + } + if (typed == 'a') + { + subflags.do_ask = FALSE; + break; + } +#ifdef FEAT_INS_EXPAND + if (typed == Ctrl_E) + scrollup_clamp(); + else if (typed == Ctrl_Y) + scrolldown_clamp(); +#endif + } + State = save_State; +#ifdef FEAT_MOUSE + setmouse(); +#endif + 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. + */ +#ifdef FEAT_EVAL + if (subflags.do_count) + { + /* prevent accidentally changing the buffer by a function */ + save_ma = curbuf->b_p_ma; + curbuf->b_p_ma = FALSE; + sandbox++; + } + /* Save flags for recursion. They can change for e.g. + * :s/^/\=execute("s#^##gn") */ + subflags_save = subflags; +#endif + /* get length of substitution part */ + sublen = vim_regsub_multi(®match, + sub_firstlnum - regmatch.startpos[0].lnum, + sub, sub_firstline, FALSE, p_magic, TRUE); +#ifdef FEAT_EVAL + /* Don't keep flags set by a recursive call. */ + subflags = subflags_save; + if (subflags.do_count) + { + curbuf->b_p_ma = save_ma; + if (sandbox > 0) + sandbox--; + goto skip; + } +#endif + + /* 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 + * Adjust text properties here, since we have all information + * needed. + */ + if (nmatch == 1) + { + p1 = sub_firstline; +#ifdef FEAT_TEXT_PROP + if (curbuf->b_has_textprop) + adjust_prop_columns(lnum, regmatch.startpos[0].col, + sublen - 1 - (regmatch.endpos[0].col + - regmatch.startpos[0].col)); +#endif + } + 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 alloc()/free()). + */ + new_start_len = needed_len + 50; + if ((new_start = alloc_check(new_start_len)) == NULL) + goto outofmem; + *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 alloc()/free()). + */ + len = (unsigned)STRLEN(new_start); + needed_len += len; + if (needed_len > (int)new_start_len) + { + new_start_len = needed_len + 50; + if ((p1 = alloc_check(new_start_len)) == NULL) + { + vim_free(new_start); + goto outofmem; + } + mch_memmove(p1, new_start, (size_t)(len + 1)); + vim_free(new_start); + new_start = p1; + } + new_end = new_start + len; + } + + /* + * copy the text up to the part that matched + */ + mch_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; + vim_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 + subflags.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. */ + vim_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 + { + colnr_T plen = (colnr_T)(p1 - new_start + 1); + + *p1 = NUL; // truncate up to the CR + ml_append(lnum - 1, new_start, plen, FALSE); + mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); + if (subflags.do_ask) + appended_lines(lnum - 1, 1L); + else + { + if (first_line == 0) + first_line = lnum; + last_line = lnum + 1; + } +#ifdef FEAT_TEXT_PROP + adjust_props_for_split(lnum, plen, 1); +#endif + // 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 + || !(subflags.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, 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 (subflags.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 (subflags.do_ask) + changed_bytes(lnum, 0); + else + { + if (first_line == 0) + first_line = lnum; + last_line = lnum + 1; + } + + sub_firstlnum = lnum; + vim_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, 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; + vim_free(new_start); /* for when substitute was cancelled */ + VIM_CLEAR(sub_firstline); /* free the copy of the original line */ + } + + 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); + } + +outofmem: + vim_free(sub_firstline); /* may have to free allocated copy of the line */ + + /* ":s/pat//n" doesn't move the cursor */ + if (subflags.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) + { + /* when interactive leave cursor on the match */ + if (!subflags.do_ask) + { + if (endcolumn) + coladvance((colnr_T)MAXCOL); + else + beginline(BL_WHITE | BL_FIX); + } + if (!do_sub_msg(subflags.do_count) && subflags.do_ask) + msg(""); + } + else + global_need_beginline = TRUE; + if (subflags.do_print) + print_line(curwin->w_cursor.lnum, + subflags.do_number, subflags.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 (subflags.do_error) /* nothing found */ + semsg(_(e_patnotf2), get_search_pat()); + } + +#ifdef FEAT_FOLDING + if (subflags.do_ask && hasAnyFolding(curwin)) + /* Cursor position may require updating */ + changed_window_setting(); +#endif + + vim_regfree(regmatch.regprog); + + /* Restore the flag values, they can be used for ":&&". */ + subflags.do_all = save_do_all; + subflags.do_ask = save_do_ask; +} + +/* + * 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()) + { + char *msg_single; + char *msg_plural; + + if (got_int) + STRCPY(msg_buf, _("(Interrupted) ")); + else + *msg_buf = NUL; + + msg_single = count_only + ? NGETTEXT("%ld match on %ld line", + "%ld matches on %ld line", sub_nsubs) + : NGETTEXT("%ld substitution on %ld line", + "%ld substitutions on %ld line", sub_nsubs); + msg_plural = count_only + ? NGETTEXT("%ld match on %ld lines", + "%ld matches on %ld lines", sub_nsubs) + : NGETTEXT("%ld substitution on %ld lines", + "%ld substitutions on %ld lines", sub_nsubs); + + vim_snprintf_add(msg_buf, sizeof(msg_buf), + NGETTEXT(msg_single, msg_plural, sub_nlines), + sub_nsubs, (long)sub_nlines); + + if (msg(msg_buf)) + /* save message to display it after redraw */ + set_keep_msg((char_u *)msg_buf, 0); + return TRUE; + } + if (got_int) + { + emsg(_(e_interr)); + return TRUE; + } + return FALSE; +} + + static void +global_exe_one(char_u *cmd, linenr_T lnum) +{ + 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); +} + +/* + * 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; + + /* When nesting the command works on one line. This allows for + * ":g/found/v/notfound/command". */ + if (global_busy && (eap->line1 != 1 + || eap->line2 != curbuf->b_ml.ml_line_count)) + { + /* will increment global_busy to break out of the loop */ + emsg(_("E147: Cannot do :global recursive with a range")); + 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 */ + } + +#ifdef FEAT_FKMAP /* when in Farsi mode, reverse the character flow */ + if (p_altkeymap && curwin->w_p_rl) + lrFswap(pat,0); +#endif + + if (search_regcomp(pat, RE_BOTH, which_pat, SEARCH_HIS, ®match) == FAIL) + { + emsg(_(e_invcmd)); + return; + } + + if (global_busy) + { + lnum = curwin->w_cursor.lnum; + match = vim_regexec_multi(®match, curwin, curbuf, lnum, + (colnr_T)0, NULL, NULL); + if ((type == 'g' && match) || (type == 'v' && !match)) + global_exe_one(cmd, lnum); + } + else + { + /* + * 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, 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(_("Pattern found in every line: %s"), pat); + else + smsg(_("Pattern not found: %s"), pat); + } + else + { +#ifdef FEAT_CLIPBOARD + start_global_changes(); +#endif + global_exe(cmd); +#ifdef FEAT_CLIPBOARD + end_global_changes(); +#endif + } + + 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) + { + global_exe_one(cmd, lnum); + 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); +} + +#ifdef FEAT_VIMINFO + int +read_viminfo_sub_string(vir_T *virp, int force) +{ + if (force) + vim_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); + } +} +#endif /* FEAT_VIMINFO */ + +#if defined(EXITFREE) || defined(PROTO) + void +free_old_sub(void) +{ + vim_free(old_sub); +} +#endif + +#if defined(FEAT_QUICKFIX) || defined(PROTO) +/* + * 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; + +# ifdef FEAT_GUI + need_mouse_correct = TRUE; +# endif + + /* + * If there is already a preview window open, use that one. + */ + if (!curwin->w_p_pvw) + { + FOR_ALL_WINDOWS(wp) + 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' */ +# ifdef FEAT_DIFF + curwin->w_p_diff = FALSE; /* no 'diff' */ +# endif +# ifdef FEAT_FOLDING + curwin->w_p_fdc = 0; /* no 'foldcolumn' */ +# endif + return TRUE; + } + } + return FALSE; +} + +#endif + + +/* + * ":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; +#ifdef FEAT_MULTI_LANG + int len; + char_u *lang; +#endif +#ifdef FEAT_FOLDING + int old_KeyTyped = KeyTyped; +#endif + + 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; + +#ifdef FEAT_MULTI_LANG + /* Check for a specified language */ + lang = check_help_lang(arg); +#endif + + /* 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; +#ifdef FEAT_MULTI_LANG + 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; + } +#endif + if (i >= num_matches || n == FAIL) + { +#ifdef FEAT_MULTI_LANG + if (lang != NULL) + semsg(_("E661: Sorry, no '%s' help for %s"), lang, arg); + else +#endif + semsg(_("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); + +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + + /* + * Re-use an existing help window or open a new one. + * Always open a new one for ":tab help". + */ + if (!bt_help(curwin->w_buffer) || cmdmod.tab != 0) + { + if (cmdmod.tab != 0) + wp = NULL; + else + FOR_ALL_WINDOWS(wp) + if (bt_help(wp->w_buffer)) + 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(_("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 */ + +#ifdef FEAT_FOLDING + /* Restore KeyTyped, setting 'filetype=help' may reset it. + * It is needed for do_tag top open folds under the cursor. */ + KeyTyped = old_KeyTyped; +#endif + + 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: + vim_free(tag); +} + +/* + * ":helpclose": Close one help window + */ + void +ex_helpclose(exarg_T *eap UNUSED) +{ + win_T *win; + + FOR_ALL_WINDOWS(win) + { + if (bt_help(win->w_buffer)) + { + win_close(win, FALSE); + return; + } + } +} + +#if defined(FEAT_MULTI_LANG) || defined(PROTO) +/* + * 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; +} +#endif + +/* + * 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 +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +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??", + "-?", "q?", "v_g?", + "/\\?", "/\\z(\\)", "\\=", ":s\\=", + "[count]", "[quotex]", + "[range]", ":[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??", + "-?", "q?", "v_g?", + "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", + "\\[count]", "\\[quotex]", + "\\[range]", ":\\[range]", + "\\[pattern]", "\\\\bar", "/\\\\%\\$", + "s/\\\\\\~", "s/\\\\U", "s/\\\\L", + "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"}; + static char *(expr_table[]) = {"!=?", "!~?", "<=?", "=?", ">?", "is?", "isnot?"}; + int flags; + + d = IObuff; /* assume IObuff is long enough! */ + + if (STRNICMP(arg, "expr-", 5) == 0) + { + // When the string starting with "expr-" and containing '?' and matches + // the table, it is taken literally. Otherwise '?' is recognized as a + // wildcard. + for (i = (int)(sizeof(expr_table) / sizeof(char *)); --i >= 0; ) + if (STRCMP(arg + 5, expr_table[i]) == 0) + { + STRCPY(d, arg); + break; + } + } + else + { + // 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++ = '\\'; + + /* + * If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'. + */ + if (*arg == '(' && arg[1] == '\'') + arg++; + 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 < ' ') + { +#ifdef EBCDIC + *d++ = CtrlChar(*s); +#else + *d++ = *s + '@'; +#endif + 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 contains "({" or "([", tag terminates at the "(". + * This is for help on functions, e.g.: abs({expr}). + */ + if (*s == '(' && (s[1] == '{' || s[1] =='[')) + break; + + /* + * If tag starts with ', toss everything after a second '. Fixes + * CTRL-] on 'option'. (would include the trailing '.'). + */ + if (*s == '\'' && s > arg && *arg == '\'') + break; + /* Also '{' and '}'. */ + if (*s == '}' && s > arg && *arg == '{') + break; + } + *d = NUL; + + if (*IObuff == '`') + { + if (d > IObuff + 2 && d[-1] == '`') + { + /* remove the backticks from `command` */ + mch_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`, */ + mch_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`\. */ + mch_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) + vim_free((*matches)[--*num_matches]); + } + return OK; +} + +/* + * Called when starting to edit a buffer for a help file. + */ + static void +prepare_help_buffer(void) +{ + char_u *p; + + curbuf->b_help = TRUE; +#ifdef FEAT_QUICKFIX + set_string_option_direct((char_u *)"buftype", -1, + (char_u *)"help", OPT_FREE|OPT_LOCAL, 0); +#endif + + /* + * 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 = +#ifdef EBCDIC + (char_u *)"65-255,^*,^|,^\""; +#else + (char_u *)"!-~,^*,^|,^\",192-255"; +#endif + 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); + } + +#ifdef FEAT_FOLDING + /* Don't use the global foldmethod.*/ + set_string_option_direct((char_u *)"fdm", -1, (char_u *)"manual", + OPT_FREE|OPT_LOCAL, 0); +#endif + + 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 */ +#ifdef FEAT_ARABIC + curwin->w_p_arab = FALSE; /* no arabic mode */ +#endif +#ifdef FEAT_RIGHTLEFT + curwin->w_p_rl = FALSE; /* help window is left-to-right */ +#endif +#ifdef FEAT_FOLDING + curwin->w_p_fen = FALSE; /* No folding in the help window */ +#endif +#ifdef FEAT_DIFF + curwin->w_p_diff = FALSE; /* No 'diff' */ +#endif +#ifdef FEAT_SPELL + curwin->w_p_spell = FALSE; /* No spell checking */ +#endif + + set_buflisted(FALSE); +} + +/* + * 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" if still needed. */ + if (STRCMP(curbuf->b_p_ft, "help") != 0) + { + ++curbuf_lock; + set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL); + --curbuf_lock; + } + +#ifdef FEAT_SYN_HL + if (!syntax_present(curwin)) +#endif + { + 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 = gettail(curbuf->b_fname); + if (fnamecmp(fname, "help.txt") == 0 +#ifdef FEAT_MULTI_LANG + || (fnamencmp(fname, "help.", 5) == 0 + && ASCII_ISALPHA(fname[5]) + && ASCII_ISALPHA(fname[6]) + && TOLOWER_ASC(fname[7]) == 'x' + && fname[8] == NUL) +#endif + ) + { + 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 (rt != NULL && fullpathcmp(rt, NameBuff, FALSE) != FPC_SAME) + { + 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); +#ifdef FEAT_MULTI_LANG + STRCAT(NameBuff, "doc/*.??[tx]"); +#else + STRCAT(NameBuff, "doc/*.txt"); +#endif + if (gen_expand_wildcards(1, &NameBuff, &fcount, + &fnames, EW_FILE|EW_SILENT) == OK + && fcount > 0) + { +#ifdef FEAT_MULTI_LANG + int i1, i2; + char_u *f1, *f2; + char_u *t1, *t2; + char_u *e1, *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 = gettail(f1); + t2 = gettail(f2); + e1 = vim_strrchr(t1, '.'); + e2 = vim_strrchr(t2, '.'); + if (e1 == NULL || e2 == NULL) + continue; + if (fnamecmp(e1, ".txt") != 0 + && fnamecmp(e1, fname + 4) != 0) + { + /* Not .txt and not .abx, remove it. */ + VIM_CLEAR(fnames[i1]); + continue; + } + if (e1 - f1 != e2 - f2 + || fnamencmp(f1, f2, e1 - f1) != 0) + continue; + if (fnamecmp(e1, ".txt") == 0 + && fnamecmp(e2, fname + 4) == 0) + /* use .abx instead of .txt */ + VIM_CLEAR(fnames[i1]); + } + } +#endif + 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) + vim_free(cp); + ++lnum; + } + fclose(fd); + } + } + FreeWild(fcount, fnames); + } + } + if (mustfree) + vim_free(rt); + } + break; + } + } +} + +/* + * ":exusage" + */ + void +ex_exusage(exarg_T *eap UNUSED) +{ + do_cmdline_cmd((char_u *)"help ex-cmd-index"); +} + +/* + * ":viusage" + */ + void +ex_viusage(exarg_T *eap UNUSED) +{ + do_cmdline_cmd((char_u *)"help normal-index"); +} + +/* + * Generate tags in one help directory. + */ + 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) + semsg(_("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) + { + semsg(_("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_init2(&ga, (int)sizeof(char_u *), 100); + if (add_help_tags || fullpathcmp((char_u *)"$VIMRUNTIME/doc", + dir, FALSE) == FPC_SAME) + { + if (ga_grow(&ga, 1) == FAIL) + got_int = TRUE; + else + { + s = alloc(18 + (unsigned)STRLEN(tagfname)); + if (s == NULL) + got_int = TRUE; + else + { + 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) + { + semsg(_("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) + { + semsg(_("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; + if (ga_grow(&ga, 1) == FAIL) + { + got_int = TRUE; + break; + } + s = alloc((unsigned)(p2 - p1 + STRLEN(fname) + 2)); + if (s == NULL) + { + got_int = TRUE; + break; + } + ((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. + */ + if (ga.ga_data != NULL) + 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((char *)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) + vim_free(((char_u **)ga.ga_data)[i]); + ga_clear(&ga); + fclose(fd_tags); /* there is no check for an error... */ +} + +/* + * Generate tags in one help directory, taking care of translations. + */ + static void +do_helptags(char_u *dirname, int add_help_tags) +{ +#ifdef FEAT_MULTI_LANG + int len; + int i, j; + garray_T ga; + char_u lang[2]; + char_u ext[5]; + char_u fname[8]; + int filecount; + char_u **files; + + /* 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) + { + semsg(_("E151: No match: %s"), NameBuff); + return; + } + + /* Go over all files in the directory to find out what languages are + * present. */ + ga_init2(&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. */ + if (ga_grow(&ga, 2) == FAIL) + break; + ((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); + +#else + /* No language support, just use "*.txt" and "tags". */ + helptags_one(dirname, (char_u *)".txt", (char_u *)"tags", add_help_tags); +#endif +} + + static void +helptags_cb(char_u *fname, void *cookie) +{ + do_helptags(fname, *(int *)cookie); +} + +/* + * ":helptags" + */ + void +ex_helptags(exarg_T *eap) +{ + expand_T xpc; + char_u *dirname; + 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); + } + + if (STRCMP(eap->arg, "ALL") == 0) + { + do_in_path(p_rtp, (char_u *)"doc", DIP_ALL + DIP_DIR, + helptags_cb, &add_help_tags); + } + else + { + ExpandInit(&xpc); + xpc.xp_context = EXPAND_DIRECTORIES; + dirname = ExpandOne(&xpc, eap->arg, NULL, + WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); + if (dirname == NULL || !mch_isdir(dirname)) + semsg(_("E150: Not a directory: %s"), eap->arg); + else + do_helptags(dirname, add_help_tags); + vim_free(dirname); + } +} + +/* + * Make the user happy. + */ + void +ex_smile(exarg_T *eap UNUSED) +{ + static char *code[] = { + "\34 \4o\14$\4ox\30 \2o\30$\1ox\25 \2o\36$\1o\11 \1o\1$\3 \2$\1 \1o\1$x\5 \1o\1 \1$\1 \2o\10 \1o\44$\1o\7 \2$\1 \2$\1 \2$\1o\1$x\2 \2o\1 \1$\1 \1$\1 \1\"\1$\6 \1o\11$\4 \15$\4 \11$\1o\7 \3$\1o\2$\1o\1$x\2 \1\"\6$\1o\1$\5 \1o\11$\6 \13$\6 \12$\1o\4 \10$x\4 \7$\4 \13$\6 \13$\6 \27$x\4 \27$\4 \15$\4 \16$\2 \3\"\3$x\5 \1\"\3$\4\"\61$\5 \1\"\3$x\6 \3$\3 \1o\62$\5 \1\"\3$\1ox\5 \1o\2$\1\"\3 \63$\7 \3$\1ox\5 \3$\4 \55$\1\"\1 \1\"\6$", + "\5o\4$\1ox\4 \1o\3$\4o\5$\2 \45$\3 \1o\21$x\4 \10$\1\"\4$\3 \42$\5 \4$\10\"x\3 \4\"\7 \4$\4 \1\"\34$\1\"\6 \1o\3$x\16 \1\"\3$\1o\5 \3\"\22$\1\"\2$\1\"\11 \3$x\20 \3$\1o\12 \1\"\2$\2\"\6$\4\"\13 \1o\3$x\21 \4$\1o\40 \1o\3$\1\"x\22 \1\"\4$\1o\6 \1o\6$\1o\1\"\4$\1o\10 \1o\4$x\24 \1\"\5$\2o\5 \2\"\4$\1o\5$\1o\3 \1o\4$\2\"x\27 \2\"\5$\4o\2 \1\"\3$\1o\11$\3\"x\32 \2\"\7$\2o\1 \12$x\42 \4\"\13$x\46 \14$x\47 \12$\1\"x\50 \1\"\3$\4\"x" + }; + char *p; + int n; + int i; + + msg_start(); + msg_putchar('\n'); + for (i = 0; i < 2; ++i) + for (p = code[i]; *p != NUL; ++p) + if (*p == 'x') + msg_putchar('\n'); + else + for (n = *p++; n > 0; --n) + if (*p == 'o' || *p == '$') + msg_putchar_attr(*p, HL_ATTR(HLF_L)); + else + msg_putchar(*p); + msg_clr_eos(); +} + +/* + * ":drop" + * Opens the first argument in a window. When there are two or more arguments + * the argument list is redefined. + */ + void +ex_drop(exarg_T *eap) +{ + int split = FALSE; + win_T *wp; + buf_T *buf; + tabpage_T *tp; + + /* + * Check if the first argument is already being edited in a window. If + * so, jump to that window. + * We would actually need to check all arguments, but that's complicated + * and mostly only one file is dropped. + * This also ignores wildcards, since it is very unlikely the user is + * editing a file name with a wildcard character. + */ + set_arglist(eap->arg); + + /* + * Expanding wildcards may result in an empty argument list. E.g. when + * editing "foo.pyc" and ".pyc" is in 'wildignore'. Assume that we + * already did an error message for this. + */ + if (ARGCOUNT == 0) + return; + + if (cmdmod.tab) + { + /* ":tab drop file ...": open a tab for each argument that isn't + * edited in a window yet. It's like ":tab all" but without closing + * windows or tabs. */ + ex_all(eap); + } + else + { + /* ":drop file ...": Edit the first argument. Jump to an existing + * window if possible, edit in current window if the current buffer + * can be abandoned, otherwise open a new window. */ + buf = buflist_findnr(ARGLIST[0].ae_fnum); + + FOR_ALL_TAB_WINDOWS(tp, wp) + { + if (wp->w_buffer == buf) + { + goto_tabpage_win(tp, wp); + curwin->w_arg_idx = 0; + return; + } + } + + /* + * Check whether the current buffer is changed. If so, we will need + * to split the current window or data could be lost. + * Skip the check if the 'hidden' option is set, as in this case the + * buffer won't be lost. + */ + if (!buf_hide(curbuf)) + { + ++emsg_off; + split = check_changed(curbuf, CCGD_AW | CCGD_EXCMD); + --emsg_off; + } + + /* Fake a ":sfirst" or ":first" command edit the first argument. */ + if (split) + { + eap->cmdidx = CMD_sfirst; + eap->cmd[0] = 's'; + } + else + eap->cmdidx = CMD_first; + ex_rewind(eap); + } +} + +/* + * Skip over the pattern argument of ":vimgrep /pat/[g][j]". + * Put the start of the pattern in "*s", unless "s" is NULL. + * If "flags" is not NULL put the flags in it: VGR_GLOBAL, VGR_NOJUMP. + * If "s" is not NULL terminate the pattern with a NUL. + * Return a pointer to the char just past the pattern plus flags. + */ + char_u * +skip_vimgrep_pat(char_u *p, char_u **s, int *flags) +{ + int c; + + if (vim_isIDc(*p)) + { + /* ":vimgrep pattern fname" */ + if (s != NULL) + *s = p; + p = skiptowhite(p); + if (s != NULL && *p != NUL) + *p++ = NUL; + } + else + { + /* ":vimgrep /pattern/[g][j] fname" */ + if (s != NULL) + *s = p + 1; + c = *p; + p = skip_regexp(p + 1, c, TRUE, NULL); + if (*p != c) + return NULL; + + /* Truncate the pattern. */ + if (s != NULL) + *p = NUL; + ++p; + + /* Find the flags */ + while (*p == 'g' || *p == 'j') + { + if (flags != NULL) + { + if (*p == 'g') + *flags |= VGR_GLOBAL; + else + *flags |= VGR_NOJUMP; + } + ++p; + } + } + return p; +} + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * List v:oldfiles in a nice way. + */ + void +ex_oldfiles(exarg_T *eap UNUSED) +{ + list_T *l = get_vim_var_list(VV_OLDFILES); + listitem_T *li; + int nr = 0; + char_u *fname; + + if (l == NULL) + msg(_("No old files")); + else + { + msg_start(); + msg_scroll = TRUE; + for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) + { + ++nr; + fname = tv_get_string(&li->li_tv); + if (!message_filtered(fname)) + { + msg_outnum((long)nr); + msg_puts(": "); + msg_outtrans(fname); + msg_clr_eos(); + 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; + +# ifdef FEAT_BROWSE_CMD + if (cmdmod.browse) + { + quit_more = FALSE; + nr = prompt_for_number(FALSE); + msg_starthere(); + if (nr > 0) + { + char_u *p = list_find_str(get_vim_var_list(VV_OLDFILES), + (long)nr); + + if (p != NULL) + { + p = expand_env_save(p); + eap->arg = p; + eap->cmdidx = CMD_edit; + cmdmod.browse = FALSE; + do_exedit(eap, NULL); + vim_free(p); + } + } + } +# endif + } +} +#endif diff --git a/src/ex_cmds.h b/src/ex_cmds.h new file mode 100644 index 0000000..07afb00 --- /dev/null +++ b/src/ex_cmds.h @@ -0,0 +1,1819 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * 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. Run "make cmdidxs" to re-generate ex_cmdidxs.h. + * 3. Add a "case: CMD_xxx" in the big switch in ex_docmd.c. + * 4. Add an entry in the index for Ex commands at ":help ex-cmd-index". + * 5. Add documentation in ../doc/xxx.txt. Add a tag for both the short and + * long name of the command. + */ + +#ifdef RANGE +# undef RANGE /* SASC on Amiga defines it */ +#endif + +#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; when missing + * disallows editing another buffer when + * curbuf_lock is set */ +#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 */ + +/* values for cmd_addr_type */ +#define ADDR_LINES 0 // buffer line numbers +#define ADDR_WINDOWS 1 // window number +#define ADDR_ARGUMENTS 2 // argument number +#define ADDR_LOADED_BUFFERS 3 // buffer number of loaded buffer +#define ADDR_BUFFERS 4 // buffer number +#define ADDR_TABS 5 // tab page number +#define ADDR_TABS_RELATIVE 6 // Tab page that only relative +#define ADDR_QUICKFIX 7 // quickfix list entry number +#define ADDR_OTHER 99 // something else + +#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, e) {(char_u *)b, c, (long_u)(d), e} + +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 */ + int cmd_addr_type; /* flag for address type */ +} cmdnames[] = +#else +# define EX(a, b, c, d, e) a +enum CMD_index +#endif +{ +EX(CMD_append, "append", ex_append, + BANG|RANGE|ZEROR|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_abbreviate, "abbreviate", ex_abbreviate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_abclear, "abclear", ex_abclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_aboveleft, "aboveleft", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_all, "all", ex_all, + BANG|RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_amenu, "amenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_anoremenu, "anoremenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_args, "args", ex_args, + BANG|FILES|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_argadd, "argadd", ex_argadd, + BANG|RANGE|NOTADR|ZEROR|FILES|TRLBAR, + ADDR_ARGUMENTS), +EX(CMD_argdelete, "argdelete", ex_argdelete, + BANG|RANGE|NOTADR|FILES|TRLBAR, + ADDR_ARGUMENTS), +EX(CMD_argdo, "argdo", ex_listdo, + BANG|NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, + ADDR_ARGUMENTS), +EX(CMD_argedit, "argedit", ex_argedit, + BANG|NEEDARG|RANGE|NOTADR|ZEROR|FILES|EDITCMD|ARGOPT|TRLBAR, + ADDR_ARGUMENTS), +EX(CMD_argglobal, "argglobal", ex_args, + BANG|FILES|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_arglocal, "arglocal", ex_args, + BANG|FILES|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_argument, "argument", ex_argument, + BANG|RANGE|NOTADR|COUNT|EXTRA|EDITCMD|ARGOPT|TRLBAR, + ADDR_ARGUMENTS), +EX(CMD_ascii, "ascii", do_ascii, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_autocmd, "autocmd", ex_autocmd, + BANG|EXTRA|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_augroup, "augroup", ex_autocmd, + BANG|WORD1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_aunmenu, "aunmenu", ex_menu, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_buffer, "buffer", ex_buffer, + BANG|RANGE|NOTADR|BUFNAME|BUFUNL|COUNT|EXTRA|EDITCMD|TRLBAR, + ADDR_BUFFERS), +EX(CMD_bNext, "bNext", ex_bprevious, + BANG|RANGE|NOTADR|COUNT|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_ball, "ball", ex_buffer_all, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_badd, "badd", ex_edit, + NEEDARG|FILE1|EDITCMD|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_bdelete, "bdelete", ex_bunload, + BANG|RANGE|NOTADR|BUFNAME|COUNT|EXTRA|TRLBAR, + ADDR_BUFFERS), +EX(CMD_behave, "behave", ex_behave, + BANG|NEEDARG|WORD1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_belowright, "belowright", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_bfirst, "bfirst", ex_brewind, + BANG|RANGE|NOTADR|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_blast, "blast", ex_blast, + BANG|RANGE|NOTADR|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_bmodified, "bmodified", ex_bmodified, + BANG|RANGE|NOTADR|COUNT|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_bnext, "bnext", ex_bnext, + BANG|RANGE|NOTADR|COUNT|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_botright, "botright", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_bprevious, "bprevious", ex_bprevious, + BANG|RANGE|NOTADR|COUNT|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_brewind, "brewind", ex_brewind, + BANG|RANGE|NOTADR|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_break, "break", ex_break, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_breakadd, "breakadd", ex_breakadd, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_breakdel, "breakdel", ex_breakdel, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_breaklist, "breaklist", ex_breaklist, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_browse, "browse", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM|CMDWIN, + ADDR_LINES), +EX(CMD_buffers, "buffers", buflist_list, + BANG|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_bufdo, "bufdo", ex_listdo, + BANG|NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, + ADDR_BUFFERS), +EX(CMD_bunload, "bunload", ex_bunload, + BANG|RANGE|NOTADR|BUFNAME|COUNT|EXTRA|TRLBAR, + ADDR_LOADED_BUFFERS), +EX(CMD_bwipeout, "bwipeout", ex_bunload, + BANG|RANGE|NOTADR|BUFNAME|BUFUNL|COUNT|EXTRA|TRLBAR, + ADDR_BUFFERS), +EX(CMD_change, "change", ex_change, + BANG|WHOLEFOLD|RANGE|COUNT|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_cNext, "cNext", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cNfile, "cNfile", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cabbrev, "cabbrev", ex_abbreviate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_cabclear, "cabclear", ex_abclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_caddbuffer, "caddbuffer", ex_cbuffer, + RANGE|NOTADR|WORD1|TRLBAR, + ADDR_LINES), +EX(CMD_caddexpr, "caddexpr", ex_cexpr, + NEEDARG|WORD1|NOTRLCOM|TRLBAR, + ADDR_LINES), +EX(CMD_caddfile, "caddfile", ex_cfile, + TRLBAR|FILE1, + ADDR_LINES), +EX(CMD_call, "call", ex_call, + RANGE|NEEDARG|EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_catch, "catch", ex_catch, + EXTRA|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_cbuffer, "cbuffer", ex_cbuffer, + BANG|RANGE|NOTADR|WORD1|TRLBAR, + ADDR_LINES), +EX(CMD_cbottom, "cbottom", ex_cbottom, + TRLBAR, + ADDR_LINES), +EX(CMD_cc, "cc", ex_cc, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cclose, "cclose", ex_cclose, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_cd, "cd", ex_cd, + BANG|FILE1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_cdo, "cdo", ex_listdo, + BANG|NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, + ADDR_QUICKFIX), +EX(CMD_center, "center", ex_align, + TRLBAR|RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_cexpr, "cexpr", ex_cexpr, + NEEDARG|WORD1|NOTRLCOM|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cfile, "cfile", ex_cfile, + TRLBAR|FILE1|BANG, + ADDR_LINES), +EX(CMD_cfdo, "cfdo", ex_listdo, + BANG|NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, + ADDR_QUICKFIX), +EX(CMD_cfirst, "cfirst", ex_cc, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cgetfile, "cgetfile", ex_cfile, + TRLBAR|FILE1, + ADDR_LINES), +EX(CMD_cgetbuffer, "cgetbuffer", ex_cbuffer, + RANGE|NOTADR|WORD1|TRLBAR, + ADDR_LINES), +EX(CMD_cgetexpr, "cgetexpr", ex_cexpr, + NEEDARG|WORD1|NOTRLCOM|TRLBAR, + ADDR_LINES), +EX(CMD_chdir, "chdir", ex_cd, + BANG|FILE1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_changes, "changes", ex_changes, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_checkpath, "checkpath", ex_checkpath, + TRLBAR|BANG|CMDWIN, + ADDR_LINES), +EX(CMD_checktime, "checktime", ex_checktime, + RANGE|NOTADR|BUFNAME|COUNT|EXTRA|TRLBAR, + ADDR_LINES), +EX(CMD_chistory, "chistory", qf_history, + TRLBAR, + ADDR_LINES), +EX(CMD_clist, "clist", qf_list, + BANG|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_clast, "clast", ex_cc, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_close, "close", ex_close, + BANG|RANGE|NOTADR|COUNT|TRLBAR|CMDWIN, + ADDR_WINDOWS), +EX(CMD_clearjumps, "clearjumps", ex_clearjumps, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_cmap, "cmap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_cmapclear, "cmapclear", ex_mapclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_cmenu, "cmenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_cnext, "cnext", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cnewer, "cnewer", qf_age, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_cnfile, "cnfile", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cnoremap, "cnoremap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_cnoreabbrev, "cnoreabbrev", ex_abbreviate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_cnoremenu, "cnoremenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_copy, "copy", ex_copymove, + RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_colder, "colder", qf_age, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_colorscheme, "colorscheme", ex_colorscheme, + WORD1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_command, "command", ex_command, + EXTRA|BANG|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_comclear, "comclear", ex_comclear, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_compiler, "compiler", ex_compiler, + BANG|TRLBAR|WORD1|CMDWIN, + ADDR_LINES), +EX(CMD_continue, "continue", ex_continue, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_confirm, "confirm", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM|CMDWIN, + ADDR_LINES), +EX(CMD_copen, "copen", ex_copen, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_cprevious, "cprevious", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cpfile, "cpfile", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cquit, "cquit", ex_cquit, + TRLBAR|BANG, + ADDR_LINES), +EX(CMD_crewind, "crewind", ex_cc, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_cscope, "cscope", ex_cscope, + EXTRA|NOTRLCOM|XFILE, + ADDR_LINES), +EX(CMD_cstag, "cstag", ex_cstag, + BANG|TRLBAR|WORD1, + ADDR_LINES), +EX(CMD_cunmap, "cunmap", ex_unmap, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_cunabbrev, "cunabbrev", ex_abbreviate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_cunmenu, "cunmenu", ex_menu, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_cwindow, "cwindow", ex_cwindow, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_delete, "delete", ex_operators, + RANGE|WHOLEFOLD|REGSTR|COUNT|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_delmarks, "delmarks", ex_delmarks, + BANG|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_debug, "debug", ex_debug, + NEEDARG|EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_debuggreedy, "debuggreedy", ex_debuggreedy, + RANGE|NOTADR|ZEROR|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_delcommand, "delcommand", ex_delcommand, + NEEDARG|WORD1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_delfunction, "delfunction", ex_delfunction, + BANG|NEEDARG|WORD1|CMDWIN, + ADDR_LINES), +EX(CMD_display, "display", ex_display, + EXTRA|NOTRLCOM|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_diffupdate, "diffupdate", ex_diffupdate, + BANG|TRLBAR, + ADDR_LINES), +EX(CMD_diffget, "diffget", ex_diffgetput, + RANGE|EXTRA|TRLBAR|MODIFY, + ADDR_LINES), +EX(CMD_diffoff, "diffoff", ex_diffoff, + BANG|TRLBAR, + ADDR_LINES), +EX(CMD_diffpatch, "diffpatch", ex_diffpatch, + EXTRA|FILE1|TRLBAR|MODIFY, + ADDR_LINES), +EX(CMD_diffput, "diffput", ex_diffgetput, + RANGE|EXTRA|TRLBAR, + ADDR_LINES), +EX(CMD_diffsplit, "diffsplit", ex_diffsplit, + EXTRA|FILE1|TRLBAR, + ADDR_LINES), +EX(CMD_diffthis, "diffthis", ex_diffthis, + TRLBAR, + ADDR_LINES), +EX(CMD_digraphs, "digraphs", ex_digraphs, + BANG|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_djump, "djump", ex_findpat, + BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA, + ADDR_LINES), +EX(CMD_dlist, "dlist", ex_findpat, + BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA|CMDWIN, + ADDR_LINES), +EX(CMD_doautocmd, "doautocmd", ex_doautocmd, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_doautoall, "doautoall", ex_doautoall, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_drop, "drop", ex_drop, + FILES|EDITCMD|NEEDARG|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_dsearch, "dsearch", ex_findpat, + BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA|CMDWIN, + ADDR_LINES), +EX(CMD_dsplit, "dsplit", ex_findpat, + BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA, + ADDR_LINES), +EX(CMD_edit, "edit", ex_edit, + BANG|FILE1|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_earlier, "earlier", ex_later, + TRLBAR|EXTRA|NOSPC|CMDWIN, + ADDR_LINES), +EX(CMD_echo, "echo", ex_echo, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_echoerr, "echoerr", ex_execute, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_echohl, "echohl", ex_echohl, + EXTRA|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_echomsg, "echomsg", ex_execute, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_echon, "echon", ex_echo, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_else, "else", ex_else, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_elseif, "elseif", ex_else, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_emenu, "emenu", ex_emenu, + NEEDARG|EXTRA|TRLBAR|NOTRLCOM|RANGE|NOTADR|CMDWIN, + ADDR_LINES), +EX(CMD_endif, "endif", ex_endif, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_endfunction, "endfunction", ex_endfunction, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_endfor, "endfor", ex_endwhile, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_endtry, "endtry", ex_endtry, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_endwhile, "endwhile", ex_endwhile, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_enew, "enew", ex_edit, + BANG|TRLBAR, + ADDR_LINES), +EX(CMD_ex, "ex", ex_edit, + BANG|FILE1|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_execute, "execute", ex_execute, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_exit, "exit", ex_exit, + RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_exusage, "exusage", ex_exusage, + TRLBAR, + ADDR_LINES), +EX(CMD_file, "file", ex_file, + RANGE|NOTADR|ZEROR|BANG|FILE1|TRLBAR, + ADDR_LINES), +EX(CMD_files, "files", buflist_list, + BANG|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_filetype, "filetype", ex_filetype, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_filter, "filter", ex_wrongmodifier, + BANG|NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_find, "find", ex_find, + RANGE|NOTADR|BANG|FILE1|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_finally, "finally", ex_finally, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_finish, "finish", ex_finish, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_first, "first", ex_rewind, + EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_fixdel, "fixdel", do_fixdel, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_fold, "fold", ex_fold, + RANGE|WHOLEFOLD|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_foldclose, "foldclose", ex_foldopen, + RANGE|BANG|WHOLEFOLD|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_folddoopen, "folddoopen", ex_folddo, + RANGE|DFLALL|NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_folddoclosed, "folddoclosed", ex_folddo, + RANGE|DFLALL|NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_foldopen, "foldopen", ex_foldopen, + RANGE|BANG|WHOLEFOLD|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_for, "for", ex_while, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_function, "function", ex_function, + EXTRA|BANG|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_global, "global", ex_global, + RANGE|WHOLEFOLD|BANG|EXTRA|DFLALL|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_goto, "goto", ex_goto, + RANGE|NOTADR|COUNT|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_grep, "grep", ex_make, + RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_grepadd, "grepadd", ex_make, + RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_gui, "gui", ex_gui, + BANG|FILES|EDITCMD|ARGOPT|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_gvim, "gvim", ex_gui, + BANG|FILES|EDITCMD|ARGOPT|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_help, "help", ex_help, + BANG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_helpclose, "helpclose", ex_helpclose, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_helpfind, "helpfind", ex_helpfind, + EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_helpgrep, "helpgrep", ex_helpgrep, + EXTRA|NOTRLCOM|NEEDARG, + ADDR_LINES), +EX(CMD_helptags, "helptags", ex_helptags, + NEEDARG|FILES|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_hardcopy, "hardcopy", ex_hardcopy, + RANGE|COUNT|EXTRA|TRLBAR|DFLALL|BANG, + ADDR_LINES), +EX(CMD_highlight, "highlight", ex_highlight, + BANG|EXTRA|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_hide, "hide", ex_hide, + BANG|RANGE|NOTADR|COUNT|EXTRA|TRLBAR, + ADDR_WINDOWS), +EX(CMD_history, "history", ex_history, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_insert, "insert", ex_append, + BANG|RANGE|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_iabbrev, "iabbrev", ex_abbreviate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_iabclear, "iabclear", ex_abclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_if, "if", ex_if, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_ijump, "ijump", ex_findpat, + BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA, + ADDR_LINES), +EX(CMD_ilist, "ilist", ex_findpat, + BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA|CMDWIN, + ADDR_LINES), +EX(CMD_imap, "imap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_imapclear, "imapclear", ex_mapclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_imenu, "imenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_inoremap, "inoremap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_inoreabbrev, "inoreabbrev", ex_abbreviate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_inoremenu, "inoremenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_intro, "intro", ex_intro, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_isearch, "isearch", ex_findpat, + BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA|CMDWIN, + ADDR_LINES), +EX(CMD_isplit, "isplit", ex_findpat, + BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA, + ADDR_LINES), +EX(CMD_iunmap, "iunmap", ex_unmap, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_iunabbrev, "iunabbrev", ex_abbreviate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_iunmenu, "iunmenu", ex_menu, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_join, "join", ex_join, + BANG|RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_jumps, "jumps", ex_jumps, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_k, "k", ex_mark, + RANGE|WORD1|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_keepmarks, "keepmarks", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_keepjumps, "keepjumps", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_keeppatterns, "keeppatterns", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_keepalt, "keepalt", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_list, "list", ex_print, + RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_lNext, "lNext", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_lNfile, "lNfile", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_last, "last", ex_last, + EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_language, "language", ex_language, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_laddexpr, "laddexpr", ex_cexpr, + NEEDARG|WORD1|NOTRLCOM|TRLBAR, + ADDR_LINES), +EX(CMD_laddbuffer, "laddbuffer", ex_cbuffer, + RANGE|NOTADR|WORD1|TRLBAR, + ADDR_LINES), +EX(CMD_laddfile, "laddfile", ex_cfile, + TRLBAR|FILE1, + ADDR_LINES), +EX(CMD_later, "later", ex_later, + TRLBAR|EXTRA|NOSPC|CMDWIN, + ADDR_LINES), +EX(CMD_lbuffer, "lbuffer", ex_cbuffer, + BANG|RANGE|NOTADR|WORD1|TRLBAR, + ADDR_LINES), +EX(CMD_lbottom, "lbottom", ex_cbottom, + TRLBAR, + ADDR_LINES), +EX(CMD_lcd, "lcd", ex_cd, + BANG|FILE1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_lchdir, "lchdir", ex_cd, + BANG|FILE1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_lclose, "lclose", ex_cclose, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_lcscope, "lcscope", ex_cscope, + EXTRA|NOTRLCOM|XFILE, + ADDR_LINES), +EX(CMD_ldo, "ldo", ex_listdo, + BANG|NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, + ADDR_QUICKFIX), +EX(CMD_left, "left", ex_align, + TRLBAR|RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_leftabove, "leftabove", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_let, "let", ex_let, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_lexpr, "lexpr", ex_cexpr, + NEEDARG|WORD1|NOTRLCOM|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_lfile, "lfile", ex_cfile, + TRLBAR|FILE1|BANG, + ADDR_LINES), +EX(CMD_lfdo, "lfdo", ex_listdo, + BANG|NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, + ADDR_QUICKFIX), +EX(CMD_lfirst, "lfirst", ex_cc, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_lgetfile, "lgetfile", ex_cfile, + TRLBAR|FILE1, + ADDR_LINES), +EX(CMD_lgetbuffer, "lgetbuffer", ex_cbuffer, + RANGE|NOTADR|WORD1|TRLBAR, + ADDR_LINES), +EX(CMD_lgetexpr, "lgetexpr", ex_cexpr, + NEEDARG|WORD1|NOTRLCOM|TRLBAR, + ADDR_LINES), +EX(CMD_lgrep, "lgrep", ex_make, + RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_lgrepadd, "lgrepadd", ex_make, + RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_lhelpgrep, "lhelpgrep", ex_helpgrep, + EXTRA|NOTRLCOM|NEEDARG, + ADDR_LINES), +EX(CMD_lhistory, "lhistory", qf_history, + TRLBAR, + ADDR_LINES), +EX(CMD_ll, "ll", ex_cc, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_llast, "llast", ex_cc, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_llist, "llist", qf_list, + BANG|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_lmap, "lmap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_lmapclear, "lmapclear", ex_mapclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_lmake, "lmake", ex_make, + BANG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_lnoremap, "lnoremap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_lnext, "lnext", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_lnewer, "lnewer", qf_age, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_lnfile, "lnfile", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_loadview, "loadview", ex_loadview, + FILE1|TRLBAR, + ADDR_LINES), +EX(CMD_loadkeymap, "loadkeymap", ex_loadkeymap, + CMDWIN, + ADDR_LINES), +EX(CMD_lockmarks, "lockmarks", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_lockvar, "lockvar", ex_lockvar, + BANG|EXTRA|NEEDARG|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_lolder, "lolder", qf_age, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_lopen, "lopen", ex_copen, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_lprevious, "lprevious", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_lpfile, "lpfile", ex_cnext, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_lrewind, "lrewind", ex_cc, + RANGE|NOTADR|COUNT|TRLBAR|BANG, + ADDR_LINES), +EX(CMD_ltag, "ltag", ex_tag, + NOTADR|TRLBAR|BANG|WORD1, + ADDR_LINES), +EX(CMD_lunmap, "lunmap", ex_unmap, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_lua, "lua", ex_lua, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_luado, "luado", ex_luado, + RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_luafile, "luafile", ex_luafile, + RANGE|FILE1|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_lvimgrep, "lvimgrep", ex_vimgrep, + RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_lvimgrepadd, "lvimgrepadd", ex_vimgrep, + RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_lwindow, "lwindow", ex_cwindow, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_ls, "ls", buflist_list, + BANG|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_move, "move", ex_copymove, + RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_mark, "mark", ex_mark, + RANGE|WORD1|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_make, "make", ex_make, + BANG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_map, "map", ex_map, + BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_mapclear, "mapclear", ex_mapclear, + EXTRA|BANG|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_marks, "marks", do_marks, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_match, "match", ex_match, + RANGE|NOTADR|EXTRA|CMDWIN, + ADDR_LINES), +EX(CMD_menu, "menu", ex_menu, + RANGE|NOTADR|ZEROR|BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_menutranslate, "menutranslate", ex_menutranslate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_messages, "messages", ex_messages, + EXTRA|TRLBAR|RANGE|CMDWIN, + ADDR_OTHER), +EX(CMD_mkexrc, "mkexrc", ex_mkrc, + BANG|FILE1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_mksession, "mksession", ex_mkrc, + BANG|FILE1|TRLBAR, + ADDR_LINES), +EX(CMD_mkspell, "mkspell", ex_mkspell, + BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_mkvimrc, "mkvimrc", ex_mkrc, + BANG|FILE1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_mkview, "mkview", ex_mkrc, + BANG|FILE1|TRLBAR, + ADDR_LINES), +EX(CMD_mode, "mode", ex_mode, + WORD1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_mzscheme, "mzscheme", ex_mzscheme, + RANGE|EXTRA|DFLALL|NEEDARG|CMDWIN|SBOXOK, + ADDR_LINES), +EX(CMD_mzfile, "mzfile", ex_mzfile, + RANGE|FILE1|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_next, "next", ex_next, + RANGE|NOTADR|BANG|FILES|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_nbkey, "nbkey", ex_nbkey, + EXTRA|NOTADR|NEEDARG, + ADDR_LINES), +EX(CMD_nbclose, "nbclose", ex_nbclose, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_nbstart, "nbstart", ex_nbstart, + WORD1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_new, "new", ex_splitview, + BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_nmap, "nmap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_nmapclear, "nmapclear", ex_mapclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_nmenu, "nmenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_nnoremap, "nnoremap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_nnoremenu, "nnoremenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_noremap, "noremap", ex_map, + BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_noautocmd, "noautocmd", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_nohlsearch, "nohlsearch", ex_nohlsearch, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_noreabbrev, "noreabbrev", ex_abbreviate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_noremenu, "noremenu", ex_menu, + RANGE|NOTADR|ZEROR|BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_noswapfile, "noswapfile", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_normal, "normal", ex_normal, + RANGE|BANG|EXTRA|NEEDARG|NOTRLCOM|USECTRLV|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_number, "number", ex_print, + RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_nunmap, "nunmap", ex_unmap, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_nunmenu, "nunmenu", ex_menu, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_open, "open", ex_open, + RANGE|BANG|EXTRA, + ADDR_LINES), +EX(CMD_oldfiles, "oldfiles", ex_oldfiles, + BANG|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_omap, "omap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_omapclear, "omapclear", ex_mapclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_omenu, "omenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_only, "only", ex_only, + BANG|NOTADR|RANGE|COUNT|TRLBAR, + ADDR_WINDOWS), +EX(CMD_onoremap, "onoremap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_onoremenu, "onoremenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_options, "options", ex_options, + TRLBAR, + ADDR_LINES), +EX(CMD_ounmap, "ounmap", ex_unmap, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_ounmenu, "ounmenu", ex_menu, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_ownsyntax, "ownsyntax", ex_ownsyntax, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_print, "print", ex_print, + RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN|SBOXOK, + ADDR_LINES), +EX(CMD_packadd, "packadd", ex_packadd, + BANG|FILE1|NEEDARG|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_packloadall, "packloadall", ex_packloadall, + BANG|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_pclose, "pclose", ex_pclose, + BANG|TRLBAR, + ADDR_LINES), +EX(CMD_perl, "perl", ex_perl, + RANGE|EXTRA|DFLALL|NEEDARG|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_perldo, "perldo", ex_perldo, + RANGE|EXTRA|DFLALL|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pedit, "pedit", ex_pedit, + BANG|FILE1|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_pop, "pop", ex_tag, + RANGE|NOTADR|BANG|COUNT|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_popup, "popup", ex_popup, + NEEDARG|EXTRA|BANG|TRLBAR|NOTRLCOM|CMDWIN, + ADDR_LINES), +EX(CMD_ppop, "ppop", ex_ptag, + RANGE|NOTADR|BANG|COUNT|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_preserve, "preserve", ex_preserve, + TRLBAR, + ADDR_LINES), +EX(CMD_previous, "previous", ex_previous, + EXTRA|RANGE|NOTADR|COUNT|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_promptfind, "promptfind", gui_mch_find_dialog, + EXTRA|NOTRLCOM|CMDWIN, + ADDR_LINES), +EX(CMD_promptrepl, "promptrepl", gui_mch_replace_dialog, + EXTRA|NOTRLCOM|CMDWIN, + ADDR_LINES), +EX(CMD_profile, "profile", ex_profile, + BANG|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_profdel, "profdel", ex_breakdel, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_psearch, "psearch", ex_psearch, + BANG|RANGE|WHOLEFOLD|DFLALL|EXTRA, + ADDR_LINES), +EX(CMD_ptag, "ptag", ex_ptag, + RANGE|NOTADR|BANG|WORD1|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_ptNext, "ptNext", ex_ptag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_ptfirst, "ptfirst", ex_ptag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_ptjump, "ptjump", ex_ptag, + BANG|TRLBAR|WORD1, + ADDR_LINES), +EX(CMD_ptlast, "ptlast", ex_ptag, + BANG|TRLBAR, + ADDR_LINES), +EX(CMD_ptnext, "ptnext", ex_ptag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_ptprevious, "ptprevious", ex_ptag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_ptrewind, "ptrewind", ex_ptag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_ptselect, "ptselect", ex_ptag, + BANG|TRLBAR|WORD1, + ADDR_LINES), +EX(CMD_put, "put", ex_put, + RANGE|WHOLEFOLD|BANG|REGSTR|TRLBAR|ZEROR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_pwd, "pwd", ex_pwd, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_python, "python", ex_python, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pydo, "pydo", ex_pydo, + RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pyfile, "pyfile", ex_pyfile, + RANGE|FILE1|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_py3, "py3", ex_py3, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_py3do, "py3do", ex_py3do, + RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_python3, "python3", ex_py3, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_py3file, "py3file", ex_py3file, + RANGE|FILE1|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pyx, "pyx", ex_pyx, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pyxdo, "pyxdo", ex_pyxdo, + RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pythonx, "pythonx", ex_pyx, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pyxfile, "pyxfile", ex_pyxfile, + RANGE|FILE1|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_quit, "quit", ex_quit, + BANG|RANGE|COUNT|NOTADR|TRLBAR|CMDWIN, + ADDR_WINDOWS), +EX(CMD_quitall, "quitall", ex_quit_all, + BANG|TRLBAR, + ADDR_LINES), +EX(CMD_qall, "qall", ex_quit_all, + BANG|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_read, "read", ex_read, + BANG|RANGE|WHOLEFOLD|FILE1|ARGOPT|TRLBAR|ZEROR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_recover, "recover", ex_recover, + BANG|FILE1|TRLBAR, + ADDR_LINES), +EX(CMD_redo, "redo", ex_redo, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_redir, "redir", ex_redir, + BANG|FILES|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_redraw, "redraw", ex_redraw, + BANG|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_redrawstatus, "redrawstatus", ex_redrawstatus, + BANG|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_redrawtabline, "redrawtabline", ex_redrawtabline, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_registers, "registers", ex_display, + EXTRA|NOTRLCOM|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_resize, "resize", ex_resize, + RANGE|NOTADR|TRLBAR|WORD1|CMDWIN, + ADDR_LINES), +EX(CMD_retab, "retab", ex_retab, + TRLBAR|RANGE|WHOLEFOLD|DFLALL|BANG|WORD1|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_return, "return", ex_return, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_rewind, "rewind", ex_rewind, + EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_right, "right", ex_align, + TRLBAR|RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_rightbelow, "rightbelow", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_runtime, "runtime", ex_runtime, + BANG|NEEDARG|FILES|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_ruby, "ruby", ex_ruby, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_rubydo, "rubydo", ex_rubydo, + RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_rubyfile, "rubyfile", ex_rubyfile, + RANGE|FILE1|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_rundo, "rundo", ex_rundo, + NEEDARG|FILE1, + ADDR_LINES), +EX(CMD_rviminfo, "rviminfo", ex_viminfo, + BANG|FILE1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_substitute, "substitute", do_sub, + RANGE|WHOLEFOLD|EXTRA|CMDWIN, + ADDR_LINES), +EX(CMD_sNext, "sNext", ex_previous, + EXTRA|RANGE|NOTADR|COUNT|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_sargument, "sargument", ex_argument, + BANG|RANGE|NOTADR|COUNT|EXTRA|EDITCMD|ARGOPT|TRLBAR, + ADDR_ARGUMENTS), +EX(CMD_sall, "sall", ex_all, + BANG|RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_sandbox, "sandbox", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_saveas, "saveas", ex_write, + BANG|DFLALL|FILE1|ARGOPT|CMDWIN|TRLBAR, + ADDR_LINES), +EX(CMD_sbuffer, "sbuffer", ex_buffer, + BANG|RANGE|NOTADR|BUFNAME|BUFUNL|COUNT|EXTRA|EDITCMD|TRLBAR, + ADDR_BUFFERS), +EX(CMD_sbNext, "sbNext", ex_bprevious, + RANGE|NOTADR|COUNT|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_sball, "sball", ex_buffer_all, + RANGE|NOTADR|COUNT|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_sbfirst, "sbfirst", ex_brewind, + EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_sblast, "sblast", ex_blast, + EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_sbmodified, "sbmodified", ex_bmodified, + RANGE|NOTADR|COUNT|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_sbnext, "sbnext", ex_bnext, + RANGE|NOTADR|COUNT|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_sbprevious, "sbprevious", ex_bprevious, + RANGE|NOTADR|COUNT|EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_sbrewind, "sbrewind", ex_brewind, + EDITCMD|TRLBAR, + ADDR_LINES), +EX(CMD_scriptnames, "scriptnames", ex_scriptnames, + BANG|RANGE|NOTADR|COUNT|TRLBAR|CMDWIN, + ADDR_OTHER), +EX(CMD_scriptencoding, "scriptencoding", ex_scriptencoding, + WORD1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_scscope, "scscope", ex_scscope, + EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_set, "set", ex_set, + TRLBAR|EXTRA|CMDWIN|SBOXOK, + ADDR_LINES), +EX(CMD_setfiletype, "setfiletype", ex_setfiletype, + TRLBAR|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_setglobal, "setglobal", ex_set, + TRLBAR|EXTRA|CMDWIN|SBOXOK, + ADDR_LINES), +EX(CMD_setlocal, "setlocal", ex_set, + TRLBAR|EXTRA|CMDWIN|SBOXOK, + ADDR_LINES), +EX(CMD_sfind, "sfind", ex_splitview, + BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_sfirst, "sfirst", ex_rewind, + EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_shell, "shell", ex_shell, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_simalt, "simalt", ex_simalt, + NEEDARG|WORD1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_sign, "sign", ex_sign, + NEEDARG|RANGE|NOTADR|EXTRA|CMDWIN, + ADDR_LINES), +EX(CMD_silent, "silent", ex_wrongmodifier, + NEEDARG|EXTRA|BANG|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_sleep, "sleep", ex_sleep, + RANGE|NOTADR|COUNT|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_slast, "slast", ex_last, + EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_smagic, "smagic", ex_submagic, + RANGE|WHOLEFOLD|EXTRA|CMDWIN, + ADDR_LINES), +EX(CMD_smap, "smap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_smapclear, "smapclear", ex_mapclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_smenu, "smenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_snext, "snext", ex_next, + RANGE|NOTADR|BANG|FILES|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_snomagic, "snomagic", ex_submagic, + RANGE|WHOLEFOLD|EXTRA|CMDWIN, + ADDR_LINES), +EX(CMD_snoremap, "snoremap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_snoremenu, "snoremenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_source, "source", ex_source, + BANG|FILE1|TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_sort, "sort", ex_sort, + RANGE|DFLALL|WHOLEFOLD|BANG|EXTRA|NOTRLCOM|MODIFY, + ADDR_LINES), +EX(CMD_split, "split", ex_splitview, + BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_spellgood, "spellgood", ex_spell, + BANG|RANGE|NOTADR|NEEDARG|EXTRA|TRLBAR, + ADDR_LINES), +EX(CMD_spelldump, "spelldump", ex_spelldump, + BANG|TRLBAR, + ADDR_LINES), +EX(CMD_spellinfo, "spellinfo", ex_spellinfo, + TRLBAR, + ADDR_LINES), +EX(CMD_spellrepall, "spellrepall", ex_spellrepall, + TRLBAR, + ADDR_LINES), +EX(CMD_spellundo, "spellundo", ex_spell, + BANG|RANGE|NOTADR|NEEDARG|EXTRA|TRLBAR, + ADDR_LINES), +EX(CMD_spellwrong, "spellwrong", ex_spell, + BANG|RANGE|NOTADR|NEEDARG|EXTRA|TRLBAR, + ADDR_LINES), +EX(CMD_sprevious, "sprevious", ex_previous, + EXTRA|RANGE|NOTADR|COUNT|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_srewind, "srewind", ex_rewind, + EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_stop, "stop", ex_stop, + TRLBAR|BANG|CMDWIN, + ADDR_LINES), +EX(CMD_stag, "stag", ex_stag, + RANGE|NOTADR|BANG|WORD1|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_startinsert, "startinsert", ex_startinsert, + BANG|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_startgreplace, "startgreplace", ex_startinsert, + BANG|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_startreplace, "startreplace", ex_startinsert, + BANG|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_stopinsert, "stopinsert", ex_stopinsert, + BANG|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_stjump, "stjump", ex_stag, + BANG|TRLBAR|WORD1, + ADDR_LINES), +EX(CMD_stselect, "stselect", ex_stag, + BANG|TRLBAR|WORD1, + ADDR_LINES), +EX(CMD_sunhide, "sunhide", ex_buffer_all, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_sunmap, "sunmap", ex_unmap, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_sunmenu, "sunmenu", ex_menu, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_suspend, "suspend", ex_stop, + TRLBAR|BANG|CMDWIN, + ADDR_LINES), +EX(CMD_sview, "sview", ex_splitview, + BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_swapname, "swapname", ex_swapname, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_syntax, "syntax", ex_syntax, + EXTRA|NOTRLCOM|CMDWIN, + ADDR_LINES), +EX(CMD_syntime, "syntime", ex_syntime, + NEEDARG|WORD1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_syncbind, "syncbind", ex_syncbind, + TRLBAR, + ADDR_LINES), +EX(CMD_smile, "smile", ex_smile, + TRLBAR|CMDWIN|SBOXOK, + ADDR_LINES), +EX(CMD_t, "t", ex_copymove, + RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_tNext, "tNext", ex_tag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_tag, "tag", ex_tag, + RANGE|NOTADR|BANG|WORD1|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_tags, "tags", do_tags, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_tab, "tab", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_TABS), +EX(CMD_tabclose, "tabclose", ex_tabclose, + BANG|RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR|CMDWIN, + ADDR_TABS), +EX(CMD_tabdo, "tabdo", ex_listdo, + NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, + ADDR_TABS), +EX(CMD_tabedit, "tabedit", ex_splitview, + BANG|FILE1|RANGE|NOTADR|ZEROR|EDITCMD|ARGOPT|TRLBAR, + ADDR_TABS), +EX(CMD_tabfind, "tabfind", ex_splitview, + BANG|FILE1|RANGE|NOTADR|ZEROR|EDITCMD|ARGOPT|NEEDARG|TRLBAR, + ADDR_TABS), +EX(CMD_tabfirst, "tabfirst", ex_tabnext, + TRLBAR, + ADDR_TABS), +EX(CMD_tabmove, "tabmove", ex_tabmove, + RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR, + ADDR_TABS), +EX(CMD_tablast, "tablast", ex_tabnext, + TRLBAR, + ADDR_TABS), +EX(CMD_tabnext, "tabnext", ex_tabnext, + RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR, + ADDR_TABS), +EX(CMD_tabnew, "tabnew", ex_splitview, + BANG|FILE1|RANGE|NOTADR|ZEROR|EDITCMD|ARGOPT|TRLBAR, + ADDR_TABS), +EX(CMD_tabonly, "tabonly", ex_tabonly, + BANG|RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR|CMDWIN, + ADDR_TABS), +EX(CMD_tabprevious, "tabprevious", ex_tabnext, + RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR, + ADDR_TABS_RELATIVE), +EX(CMD_tabNext, "tabNext", ex_tabnext, + RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR, + ADDR_TABS_RELATIVE), +EX(CMD_tabrewind, "tabrewind", ex_tabnext, + TRLBAR, + ADDR_TABS), +EX(CMD_tabs, "tabs", ex_tabs, + TRLBAR|CMDWIN, + ADDR_TABS), +EX(CMD_tcl, "tcl", ex_tcl, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_tcldo, "tcldo", ex_tcldo, + RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_tclfile, "tclfile", ex_tclfile, + RANGE|FILE1|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_tearoff, "tearoff", ex_tearoff, + NEEDARG|EXTRA|TRLBAR|NOTRLCOM|CMDWIN, + ADDR_LINES), +EX(CMD_terminal, "terminal", ex_terminal, + RANGE|BANG|FILES|CMDWIN, + ADDR_LINES), +EX(CMD_tfirst, "tfirst", ex_tag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_throw, "throw", ex_throw, + EXTRA|NEEDARG|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_tjump, "tjump", ex_tag, + BANG|TRLBAR|WORD1, + ADDR_LINES), +EX(CMD_tlast, "tlast", ex_tag, + BANG|TRLBAR, + ADDR_LINES), +EX(CMD_tlmenu, "tlmenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_tlnoremenu, "tlnoremenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_tlunmenu, "tlunmenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_tmenu, "tmenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_tmap, "tmap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_tmapclear, "tmapclear", ex_mapclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_tnext, "tnext", ex_tag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_tnoremap, "tnoremap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_topleft, "topleft", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_tprevious, "tprevious", ex_tag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_trewind, "trewind", ex_tag, + RANGE|NOTADR|BANG|TRLBAR|ZEROR, + ADDR_LINES), +EX(CMD_try, "try", ex_try, + TRLBAR|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_tselect, "tselect", ex_tag, + BANG|TRLBAR|WORD1, + ADDR_LINES), +EX(CMD_tunmenu, "tunmenu", ex_menu, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_tunmap, "tunmap", ex_unmap, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_undo, "undo", ex_undo, + RANGE|NOTADR|COUNT|ZEROR|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_undojoin, "undojoin", ex_undojoin, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_undolist, "undolist", ex_undolist, + TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_unabbreviate, "unabbreviate", ex_abbreviate, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_unhide, "unhide", ex_buffer_all, + RANGE|NOTADR|COUNT|TRLBAR, + ADDR_LINES), +EX(CMD_unlet, "unlet", ex_unlet, + BANG|EXTRA|NEEDARG|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_unlockvar, "unlockvar", ex_lockvar, + BANG|EXTRA|NEEDARG|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_unmap, "unmap", ex_unmap, + BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_unmenu, "unmenu", ex_menu, + BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_unsilent, "unsilent", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_update, "update", ex_update, + RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR, + ADDR_LINES), +EX(CMD_vglobal, "vglobal", ex_global, + RANGE|WHOLEFOLD|EXTRA|DFLALL|CMDWIN, + ADDR_LINES), +EX(CMD_version, "version", ex_version, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_verbose, "verbose", ex_wrongmodifier, + NEEDARG|RANGE|NOTADR|EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_vertical, "vertical", ex_wrongmodifier, + NEEDARG|EXTRA|NOTRLCOM, + ADDR_LINES), +EX(CMD_visual, "visual", ex_edit, + BANG|FILE1|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_view, "view", ex_edit, + BANG|FILE1|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_vimgrep, "vimgrep", ex_vimgrep, + RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_vimgrepadd, "vimgrepadd", ex_vimgrep, + RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE, + ADDR_LINES), +EX(CMD_viusage, "viusage", ex_viusage, + TRLBAR, + ADDR_LINES), +EX(CMD_vmap, "vmap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_vmapclear, "vmapclear", ex_mapclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_vmenu, "vmenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_vnoremap, "vnoremap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_vnew, "vnew", ex_splitview, + BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_vnoremenu, "vnoremenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_vsplit, "vsplit", ex_splitview, + BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_vunmap, "vunmap", ex_unmap, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_vunmenu, "vunmenu", ex_menu, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_write, "write", ex_write, + RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_wNext, "wNext", ex_wnext, + RANGE|WHOLEFOLD|NOTADR|BANG|FILE1|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_wall, "wall", do_wqall, + BANG|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_while, "while", ex_while, + EXTRA|NOTRLCOM|SBOXOK|CMDWIN, + ADDR_LINES), +EX(CMD_winsize, "winsize", ex_winsize, + EXTRA|NEEDARG|TRLBAR, + ADDR_LINES), +EX(CMD_wincmd, "wincmd", ex_wincmd, + NEEDARG|WORD1|RANGE|NOTADR|CMDWIN, + ADDR_WINDOWS), +EX(CMD_windo, "windo", ex_listdo, + NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, + ADDR_WINDOWS), +EX(CMD_winpos, "winpos", ex_winpos, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_wnext, "wnext", ex_wnext, + RANGE|NOTADR|BANG|FILE1|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_wprevious, "wprevious", ex_wnext, + RANGE|NOTADR|BANG|FILE1|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_wq, "wq", ex_exit, + RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR, + ADDR_LINES), +EX(CMD_wqall, "wqall", do_wqall, + BANG|FILE1|ARGOPT|DFLALL|TRLBAR, + ADDR_LINES), +EX(CMD_wundo, "wundo", ex_wundo, + BANG|NEEDARG|FILE1, + ADDR_LINES), +EX(CMD_wviminfo, "wviminfo", ex_viminfo, + BANG|FILE1|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_xit, "xit", ex_exit, + RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_xall, "xall", do_wqall, + BANG|TRLBAR, + ADDR_LINES), +EX(CMD_xmap, "xmap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_xmapclear, "xmapclear", ex_mapclear, + EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_xmenu, "xmenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_xnoremap, "xnoremap", ex_map, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_xnoremenu, "xnoremenu", ex_menu, + RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_xunmap, "xunmap", ex_unmap, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_xunmenu, "xunmenu", ex_menu, + EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN, + ADDR_LINES), +EX(CMD_yank, "yank", ex_operators, + RANGE|WHOLEFOLD|REGSTR|COUNT|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_z, "z", ex_z, + RANGE|WHOLEFOLD|EXTRA|EXFLAGS|TRLBAR|CMDWIN, + ADDR_LINES), + +/* commands that don't start with a lowercase letter */ +EX(CMD_bang, "!", ex_bang, + RANGE|WHOLEFOLD|BANG|FILES|CMDWIN, + ADDR_LINES), +EX(CMD_pound, "#", ex_print, + RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_and, "&", do_sub, + RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_star, "*", ex_at, + RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_lshift, "<", ex_operators, + RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_equal, "=", ex_equal, + RANGE|TRLBAR|DFLALL|EXFLAGS|CMDWIN, + ADDR_LINES), +EX(CMD_rshift, ">", ex_operators, + RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN|MODIFY, + ADDR_LINES), +EX(CMD_at, "@", ex_at, + RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_Next, "Next", ex_previous, + EXTRA|RANGE|NOTADR|COUNT|BANG|EDITCMD|ARGOPT|TRLBAR, + ADDR_LINES), +EX(CMD_Print, "Print", ex_print, + RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN, + ADDR_LINES), +EX(CMD_X, "X", ex_X, + TRLBAR, + ADDR_LINES), +EX(CMD_tilde, "~", do_sub, + RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY, + ADDR_LINES), + +#ifndef DO_DECLARE_EXCMD +#ifdef FEAT_USR_CMDS + CMD_SIZE, /* MUST be after all real commands! */ + CMD_USER = -1, /* User-defined command */ + CMD_USER_BUF = -2 /* User-defined command local to buffer */ +#else + CMD_SIZE /* MUST be the last one! */ +#endif +#endif +}; + +#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 addr_type; /* type of the count/range */ + 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 (first char of argument) */ + int force_enc; /* ++enc= argument (index in cmd[]) */ + int bad_char; /* BAD_KEEP, BAD_DROP or replacement byte */ +#ifdef FEAT_USR_CMDS + int useridx; /* user command index */ +#endif + char *errmsg; /* returned error message */ + char_u *(*getline)(int, void *, int); + void *cookie; /* argument for getline() */ +#ifdef FEAT_EVAL + struct condstack *cstack; /* condition stack for ":if" etc. */ +#endif + long verbose_save; // saved value of p_verbose + int save_msg_silent; // saved value of msg_silent + int did_esilent; // how many times emsg_silent was incremented +#ifdef HAVE_SANDBOX + int did_sandbox; // when TRUE did ++sandbox +#endif +}; + +#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 */ + +#endif diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c new file mode 100644 index 0000000..30efcdf --- /dev/null +++ b/src/ex_cmds2.c @@ -0,0 +1,5729 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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 "vim.h" +#include "version.h" + +static void cmd_source(char_u *fname, exarg_T *eap); + +#ifdef FEAT_EVAL +/* Growarray to store info about already sourced scripts. + * For Unix 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; +# ifdef UNIX + int sn_dev_valid; + dev_t sn_dev; + ino_t sn_ino; +# endif +# ifdef FEAT_PROFILE + 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 */ +# endif +} 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]) + +# ifdef FEAT_PROFILE +/* 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)]) +# endif +#endif + +#if defined(FEAT_EVAL) || defined(PROTO) +static int debug_greedy = FALSE; /* batch mode debugging: don't save + and restore typeahead. */ +static void do_setdebugtracelevel(char_u *arg); +static void do_checkbacktracelevel(void); +static void do_showbacktrace(char_u *cmd); + +static char_u *debug_oldval = NULL; /* old and newval for debug expressions */ +static char_u *debug_newval = NULL; +static int debug_expr = 0; /* use debug_expr */ + + int +has_watchexpr(void) +{ + return debug_expr; +} + +/* + * 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 +#define CMD_BACKTRACE 7 +#define CMD_FRAME 8 +#define CMD_UP 9 +#define CMD_DOWN 10 + +#ifdef ALWAYS_USE_GUI + /* Can't do this when there is no terminal for input/output. */ + if (!gui.in_use) + { + /* Break as soon as possible. */ + debug_break_level = 9999; + return; + } +#endif + + /* 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; + debug_mode = TRUE; + + if (!debug_did_msg) + msg(_("Entering Debug mode. Type \"cont\" to continue.")); + if (debug_oldval != NULL) + { + smsg(_("Oldval = \"%s\""), debug_oldval); + vim_free(debug_oldval); + debug_oldval = NULL; + } + if (debug_newval != NULL) + { + smsg(_("Newval = \"%s\""), debug_newval); + vim_free(debug_newval); + debug_newval = NULL; + } + if (sourcing_name != NULL) + msg((char *)sourcing_name); + if (sourcing_lnum != 0) + smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd); + else + smsg(_("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; + } + + vim_free(cmdline); + 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; + msg_starthere(); + 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 = 0; + if (p[1] == 'r') + { + last_cmd = CMD_FRAME; + tail = "rame"; + } + else + { + 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; + case 'b': last_cmd = CMD_BACKTRACE; + if (p[1] == 't') + tail = "t"; + else + tail = "acktrace"; + break; + case 'w': last_cmd = CMD_BACKTRACE; + tail = "here"; + break; + case 'u': last_cmd = CMD_UP; + tail = "p"; + break; + case 'd': last_cmd = CMD_DOWN; + tail = "own"; + 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 != CMD_FRAME) + 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; + case CMD_BACKTRACE: + do_showbacktrace(cmd); + continue; + case CMD_FRAME: + if (*p == NUL) + { + do_showbacktrace(cmd); + } + else + { + p = skipwhite(p); + do_setdebugtracelevel(p); + } + continue; + case CMD_UP: + debug_backtrace_level++; + do_checkbacktracelevel(); + continue; + case CMD_DOWN: + debug_backtrace_level--; + do_checkbacktracelevel(); + continue; + } + /* Going out reset backtrace_level */ + debug_backtrace_level = 0; + 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; + } + lines_left = Rows - 1; + } + vim_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; + debug_mode = FALSE; + 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; +} + + static int +get_maxbacktrace_level(void) +{ + char *p, *q; + int maxbacktrace = 0; + + if (sourcing_name != NULL) + { + p = (char *)sourcing_name; + while ((q = strstr(p, "..")) != NULL) + { + p = q + 2; + maxbacktrace++; + } + } + return maxbacktrace; +} + + static void +do_setdebugtracelevel(char_u *arg) +{ + int level; + + level = atoi((char *)arg); + if (*arg == '+' || level < 0) + debug_backtrace_level += level; + else + debug_backtrace_level = level; + + do_checkbacktracelevel(); +} + + static void +do_checkbacktracelevel(void) +{ + if (debug_backtrace_level < 0) + { + debug_backtrace_level = 0; + msg(_("frame is zero")); + } + else + { + int max = get_maxbacktrace_level(); + + if (debug_backtrace_level > max) + { + debug_backtrace_level = max; + smsg(_("frame at highest level: %d"), max); + } + } +} + + static void +do_showbacktrace(char_u *cmd) +{ + char *cur; + char *next; + int i = 0; + int max = get_maxbacktrace_level(); + + if (sourcing_name != NULL) + { + cur = (char *)sourcing_name; + while (!got_int) + { + next = strstr(cur, ".."); + if (next != NULL) + *next = NUL; + if (i == max - debug_backtrace_level) + smsg("->%d %s", max - i, cur); + else + smsg(" %d %s", max - i, cur); + ++i; + if (next == NULL) + break; + *next = '.'; + cur = next + 2; + } + } + if (sourcing_lnum != 0) + smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd); + else + smsg(_("cmd: %s"), cmd); +} + +/* + * ":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(_("Breakpoint in \"%s%s\" line %ld"), + p, + debug_breakpoint_name + (*p == NUL ? 0 : 3), + (long)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, DBG_FILE or DBG_EXPR */ + char_u *dbg_name; /* function, expression or file name */ + regprog_T *dbg_prog; /* regexp program */ + linenr_T dbg_lnum; /* line number in function or file */ + int dbg_forceit; /* ! used */ +#ifdef FEAT_EVAL + typval_T *dbg_val; /* last result of watchexpression */ +#endif + int dbg_level; /* stored nested level for expr */ +}; + +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 */ + +#ifdef FEAT_PROFILE +/* Profiling uses file and func names similar to breakpoints. */ +static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL}; +#endif +#define DBG_FUNC 1 +#define DBG_FILE 2 +#define DBG_EXPR 3 + +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; + + if (ga_grow(gap, 1) == FAIL) + return FAIL; + 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 ( +#ifdef FEAT_PROFILE + gap != &prof_ga && +#endif + STRNCMP(p, "here", 4) == 0) + { + if (curbuf->b_ffname == NULL) + { + emsg(_(e_noname)); + return FAIL; + } + bp->dbg_type = DBG_FILE; + here = TRUE; + } + else if ( +#ifdef FEAT_PROFILE + gap != &prof_ga && +#endif + STRNCMP(p, "expr", 4) == 0) + bp->dbg_type = DBG_EXPR; + else + { + semsg(_(e_invarg2), p); + return FAIL; + } + p = skipwhite(p + 4); + + /* Find optional line number. */ + if (here) + bp->dbg_lnum = curwin->w_cursor.lnum; + else if ( +#ifdef FEAT_PROFILE + gap != &prof_ga && +#endif + 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)) + { + semsg(_(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 if (bp->dbg_type == DBG_EXPR) + { + bp->dbg_name = vim_strsave(p); + if (bp->dbg_name != NULL) + bp->dbg_val = eval_expr(bp->dbg_name, NULL); + } + 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); + vim_free(q); + if (p == NULL) + return FAIL; + if (*p != '*') + { + bp->dbg_name = fix_fname(p); + vim_free(p); + } + else + bp->dbg_name = p; + } + + if (bp->dbg_name == NULL) + return FAIL; + return OK; +} + +/* + * ":breakadd". Also used for ":profile". + */ + void +ex_breakadd(exarg_T *eap) +{ + struct debuggy *bp; + char_u *pat; + garray_T *gap; + + gap = &dbg_breakp; +#ifdef FEAT_PROFILE + if (eap->cmdidx == CMD_profile) + gap = &prof_ga; +#endif + + if (dbg_parsearg(eap->arg, gap) == OK) + { + bp = &DEBUGGY(gap, gap->ga_len); + bp->dbg_forceit = eap->forceit; + + if (bp->dbg_type != DBG_EXPR) + { + 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); + vim_free(pat); + } + if (pat == NULL || bp->dbg_prog == NULL) + vim_free(bp->dbg_name); + else + { + if (bp->dbg_lnum == 0) /* default line number is 1 */ + bp->dbg_lnum = 1; +#ifdef FEAT_PROFILE + if (eap->cmdidx != CMD_profile) +#endif + { + DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp; + ++debug_tick; + } + ++gap->ga_len; + } + } + else + { + /* DBG_EXPR */ + DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp; + ++debug_tick; + } + } +} + +/* + * ":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) + { +#ifdef FEAT_PROFILE + gap = &prof_ga; +#else + ex_ni(eap); + return; +#endif + } + + 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|expr} [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; + } + } + vim_free(bp->dbg_name); + } + + if (todel < 0) + semsg(_("E161: Breakpoint not found: %s"), eap->arg); + else + { + while (gap->ga_len > 0) + { + vim_free(DEBUGGY(gap, todel).dbg_name); +#ifdef FEAT_EVAL + if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR + && DEBUGGY(gap, todel).dbg_val != NULL) + free_tv(DEBUGGY(gap, todel).dbg_val); +#endif + vim_regfree(DEBUGGY(gap, todel).dbg_prog); + --gap->ga_len; + if (todel < gap->ga_len) + mch_memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1), + (gap->ga_len - todel) * sizeof(struct debuggy)); +#ifdef FEAT_PROFILE + if (eap->cmdidx == CMD_breakdel) +#endif + ++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 UNUSED) +{ + 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); + if (bp->dbg_type != DBG_EXPR) + smsg(_("%3d %s %s line %ld"), + bp->dbg_nr, + bp->dbg_type == DBG_FUNC ? "func" : "file", + bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff, + (long)bp->dbg_lnum); + else + smsg(_("%3d expr %s"), + bp->dbg_nr, bp->dbg_name); + } +} + +/* + * 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); +} + +#if defined(FEAT_PROFILE) || defined(PROTO) +/* + * 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); +} +#endif + +/* + * 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; + 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 = alloc((unsigned)STRLEN(fname) + 3); + if (name == NULL) + name = fname; + else + { + 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 && + bp->dbg_type != DBG_EXPR && ( +#ifdef FEAT_PROFILE + gap == &prof_ga || +#endif + (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum))))) + { + /* + * 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_prog(&bp->dbg_prog, FALSE, name, (colnr_T)0)) + { + lnum = bp->dbg_lnum; + if (fp != NULL) + *fp = bp->dbg_forceit; + } + got_int |= prev_got_int; + } +#ifdef FEAT_EVAL + else if (bp->dbg_type == DBG_EXPR) + { + typval_T *tv; + int line = FALSE; + + prev_got_int = got_int; + got_int = FALSE; + + tv = eval_expr(bp->dbg_name, NULL); + if (tv != NULL) + { + if (bp->dbg_val == NULL) + { + debug_oldval = typval_tostring(NULL); + bp->dbg_val = tv; + debug_newval = typval_tostring(bp->dbg_val); + line = TRUE; + } + else + { + if (typval_compare(tv, bp->dbg_val, TYPE_EQUAL, + TRUE, FALSE) == OK + && tv->vval.v_number == FALSE) + { + typval_T *v; + + line = TRUE; + debug_oldval = typval_tostring(bp->dbg_val); + /* Need to evaluate again, typval_compare() overwrites + * "tv". */ + v = eval_expr(bp->dbg_name, NULL); + debug_newval = typval_tostring(v); + free_tv(bp->dbg_val); + bp->dbg_val = v; + } + free_tv(tv); + } + } + else if (bp->dbg_val != NULL) + { + debug_oldval = typval_tostring(bp->dbg_val); + debug_newval = typval_tostring(NULL); + free_tv(bp->dbg_val); + bp->dbg_val = NULL; + line = TRUE; + } + + if (line) + { + lnum = after > 0 ? after : 1; + break; + } + + got_int |= prev_got_int; + } +#endif + } + if (name != fname) + vim_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; +} + + +# if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO) +/* + * Store the current time in "tm". + */ + void +profile_start(proftime_T *tm) +{ +# ifdef WIN3264 + QueryPerformanceCounter(tm); +# else + gettimeofday(tm, NULL); +# endif +} + +/* + * Compute the elapsed time from "tm" till now and store in "tm". + */ + void +profile_end(proftime_T *tm) +{ + proftime_T now; + +# ifdef WIN3264 + QueryPerformanceCounter(&now); + tm->QuadPart = now.QuadPart - tm->QuadPart; +# else + 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; + } +# endif +} + +/* + * Subtract the time "tm2" from "tm". + */ + void +profile_sub(proftime_T *tm, proftime_T *tm2) +{ +# ifdef WIN3264 + tm->QuadPart -= tm2->QuadPart; +# else + tm->tv_usec -= tm2->tv_usec; + tm->tv_sec -= tm2->tv_sec; + if (tm->tv_usec < 0) + { + tm->tv_usec += 1000000; + --tm->tv_sec; + } +# endif +} + +/* + * Return a string that represents the time in "tm". + * Uses a static buffer! + */ + char * +profile_msg(proftime_T *tm) +{ + static char buf[50]; + +# ifdef WIN3264 + LARGE_INTEGER fr; + + QueryPerformanceFrequency(&fr); + sprintf(buf, "%10.6lf", (double)tm->QuadPart / (double)fr.QuadPart); +# else + sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec); +# endif + return buf; +} + +# if defined(FEAT_FLOAT) || defined(PROTO) +/* + * Return a float that represents the time in "tm". + */ + float_T +profile_float(proftime_T *tm) +{ +# ifdef WIN3264 + LARGE_INTEGER fr; + + QueryPerformanceFrequency(&fr); + return (float_T)tm->QuadPart / (float_T)fr.QuadPart; +# else + return (float_T)tm->tv_sec + (float_T)tm->tv_usec / 1000000.0; +# endif +} +# endif + +/* + * Put the time "msec" past now in "tm". + */ + void +profile_setlimit(long msec, proftime_T *tm) +{ + if (msec <= 0) /* no limit */ + profile_zero(tm); + else + { +# ifdef WIN3264 + LARGE_INTEGER fr; + + QueryPerformanceCounter(tm); + QueryPerformanceFrequency(&fr); + tm->QuadPart += (LONGLONG)((double)msec / 1000.0 * (double)fr.QuadPart); +# else + long usec; + + gettimeofday(tm, NULL); + usec = (long)tm->tv_usec + (long)msec * 1000; + tm->tv_usec = usec % 1000000L; + tm->tv_sec += usec / 1000000L; +# endif + } +} + +/* + * Return TRUE if the current time is past "tm". + */ + int +profile_passed_limit(proftime_T *tm) +{ + proftime_T now; + +# ifdef WIN3264 + if (tm->QuadPart == 0) /* timer was not set */ + return FALSE; + QueryPerformanceCounter(&now); + return (now.QuadPart > tm->QuadPart); +# else + 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)); +# endif +} + +/* + * Set the time in "tm" to zero. + */ + void +profile_zero(proftime_T *tm) +{ +# ifdef WIN3264 + tm->QuadPart = 0; +# else + tm->tv_usec = 0; + tm->tv_sec = 0; +# endif +} + +# endif /* FEAT_PROFILE || FEAT_RELTIME */ + +# if defined(FEAT_TIMERS) || defined(PROTO) +static timer_T *first_timer = NULL; +static long last_timer_id = 0; + + long +proftime_time_left(proftime_T *due, proftime_T *now) +{ +# ifdef WIN3264 + LARGE_INTEGER fr; + + if (now->QuadPart > due->QuadPart) + return 0; + QueryPerformanceFrequency(&fr); + return (long)(((double)(due->QuadPart - now->QuadPart) + / (double)fr.QuadPart) * 1000); +# else + if (now->tv_sec > due->tv_sec) + return 0; + return (due->tv_sec - now->tv_sec) * 1000 + + (due->tv_usec - now->tv_usec) / 1000; +# endif +} + +/* + * Insert a timer in the list of timers. + */ + static void +insert_timer(timer_T *timer) +{ + timer->tr_next = first_timer; + timer->tr_prev = NULL; + if (first_timer != NULL) + first_timer->tr_prev = timer; + first_timer = timer; + did_add_timer = TRUE; +} + +/* + * Take a timer out of the list of timers. + */ + static void +remove_timer(timer_T *timer) +{ + if (timer->tr_prev == NULL) + first_timer = timer->tr_next; + else + timer->tr_prev->tr_next = timer->tr_next; + if (timer->tr_next != NULL) + timer->tr_next->tr_prev = timer->tr_prev; +} + + static void +free_timer(timer_T *timer) +{ + free_callback(timer->tr_callback, timer->tr_partial); + vim_free(timer); +} + +/* + * Create a timer and return it. NULL if out of memory. + * Caller should set the callback. + */ + timer_T * +create_timer(long msec, int repeat) +{ + timer_T *timer = (timer_T *)alloc_clear(sizeof(timer_T)); + long prev_id = last_timer_id; + + if (timer == NULL) + return NULL; + if (++last_timer_id <= prev_id) + /* Overflow! Might cause duplicates... */ + last_timer_id = 0; + timer->tr_id = last_timer_id; + insert_timer(timer); + if (repeat != 0) + timer->tr_repeat = repeat - 1; + timer->tr_interval = msec; + + profile_setlimit(msec, &timer->tr_due); + return timer; +} + +/* + * Invoke the callback of "timer". + */ + static void +timer_callback(timer_T *timer) +{ + typval_T rettv; + int dummy; + typval_T argv[2]; + + argv[0].v_type = VAR_NUMBER; + argv[0].vval.v_number = (varnumber_T)timer->tr_id; + argv[1].v_type = VAR_UNKNOWN; + + call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback), + &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, + timer->tr_partial, NULL); + clear_tv(&rettv); +} + +/* + * Call timers that are due. + * Return the time in msec until the next timer is due. + * Returns -1 if there are no pending timers. + */ + long +check_due_timer(void) +{ + timer_T *timer; + timer_T *timer_next; + long this_due; + long next_due = -1; + proftime_T now; + int did_one = FALSE; + int need_update_screen = FALSE; + long current_id = last_timer_id; + + /* Don't run any timers while exiting or dealing with an error. */ + if (exiting || aborting()) + return next_due; + + profile_start(&now); + for (timer = first_timer; timer != NULL && !got_int; timer = timer_next) + { + timer_next = timer->tr_next; + + if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused) + continue; + this_due = proftime_time_left(&timer->tr_due, &now); + if (this_due <= 1) + { + /* Save and restore a lot of flags, because the timer fires while + * waiting for a character, which might be halfway a command. */ + int save_timer_busy = timer_busy; + int save_vgetc_busy = vgetc_busy; + int save_did_emsg = did_emsg; + int save_called_emsg = called_emsg; + int save_must_redraw = must_redraw; + int save_trylevel = trylevel; + int save_did_throw = did_throw; + int save_ex_pressedreturn = get_pressedreturn(); + except_T *save_current_exception = current_exception; + vimvars_save_T vvsave; + + /* Create a scope for running the timer callback, ignoring most of + * the current scope, such as being inside a try/catch. */ + timer_busy = timer_busy > 0 || vgetc_busy > 0; + vgetc_busy = 0; + called_emsg = FALSE; + did_emsg = FALSE; + did_uncaught_emsg = FALSE; + must_redraw = 0; + trylevel = 0; + did_throw = FALSE; + current_exception = NULL; + save_vimvars(&vvsave); + + timer->tr_firing = TRUE; + timer_callback(timer); + timer->tr_firing = FALSE; + + timer_next = timer->tr_next; + did_one = TRUE; + timer_busy = save_timer_busy; + vgetc_busy = save_vgetc_busy; + if (did_uncaught_emsg) + ++timer->tr_emsg_count; + did_emsg = save_did_emsg; + called_emsg = save_called_emsg; + trylevel = save_trylevel; + did_throw = save_did_throw; + current_exception = save_current_exception; + restore_vimvars(&vvsave); + if (must_redraw != 0) + need_update_screen = TRUE; + must_redraw = must_redraw > save_must_redraw + ? must_redraw : save_must_redraw; + set_pressedreturn(save_ex_pressedreturn); + + /* Only fire the timer again if it repeats and stop_timer() wasn't + * called while inside the callback (tr_id == -1). */ + if (timer->tr_repeat != 0 && timer->tr_id != -1 + && timer->tr_emsg_count < 3) + { + profile_setlimit(timer->tr_interval, &timer->tr_due); + this_due = proftime_time_left(&timer->tr_due, &now); + if (this_due < 1) + this_due = 1; + if (timer->tr_repeat > 0) + --timer->tr_repeat; + } + else + { + this_due = -1; + remove_timer(timer); + free_timer(timer); + } + } + if (this_due > 0 && (next_due == -1 || next_due > this_due)) + next_due = this_due; + } + + if (did_one) + redraw_after_callback(need_update_screen); + +#ifdef FEAT_BEVAL_TERM + if (bevalexpr_due_set) + { + this_due = proftime_time_left(&bevalexpr_due, &now); + if (this_due <= 1) + { + bevalexpr_due_set = FALSE; + if (balloonEval == NULL) + { + balloonEval = (BalloonEval *)alloc_clear(sizeof(BalloonEval)); + balloonEvalForTerm = TRUE; + } + if (balloonEval != NULL) + general_beval_cb(balloonEval, 0); + } + else if (next_due == -1 || next_due > this_due) + next_due = this_due; + } +#endif +#ifdef FEAT_TERMINAL + /* Some terminal windows may need their buffer updated. */ + next_due = term_check_timers(next_due, &now); +#endif + + return current_id != last_timer_id ? 1 : next_due; +} + +/* + * Find a timer by ID. Returns NULL if not found; + */ + timer_T * +find_timer(long id) +{ + timer_T *timer; + + if (id >= 0) + { + for (timer = first_timer; timer != NULL; timer = timer->tr_next) + if (timer->tr_id == id) + return timer; + } + return NULL; +} + + +/* + * Stop a timer and delete it. + */ + void +stop_timer(timer_T *timer) +{ + if (timer->tr_firing) + /* Free the timer after the callback returns. */ + timer->tr_id = -1; + else + { + remove_timer(timer); + free_timer(timer); + } +} + + void +stop_all_timers(void) +{ + timer_T *timer; + timer_T *timer_next; + + for (timer = first_timer; timer != NULL; timer = timer_next) + { + timer_next = timer->tr_next; + stop_timer(timer); + } +} + + void +add_timer_info(typval_T *rettv, timer_T *timer) +{ + list_T *list = rettv->vval.v_list; + dict_T *dict = dict_alloc(); + dictitem_T *di; + long remaining; + proftime_T now; + + if (dict == NULL) + return; + list_append_dict(list, dict); + + dict_add_number(dict, "id", timer->tr_id); + dict_add_number(dict, "time", (long)timer->tr_interval); + + profile_start(&now); + remaining = proftime_time_left(&timer->tr_due, &now); + dict_add_number(dict, "remaining", (long)remaining); + + dict_add_number(dict, "repeat", + (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1)); + dict_add_number(dict, "paused", (long)(timer->tr_paused)); + + di = dictitem_alloc((char_u *)"callback"); + if (di != NULL) + { + if (dict_add(dict, di) == FAIL) + vim_free(di); + else if (timer->tr_partial != NULL) + { + di->di_tv.v_type = VAR_PARTIAL; + di->di_tv.vval.v_partial = timer->tr_partial; + ++timer->tr_partial->pt_refcount; + } + else + { + di->di_tv.v_type = VAR_FUNC; + di->di_tv.vval.v_string = vim_strsave(timer->tr_callback); + } + } +} + + void +add_timer_info_all(typval_T *rettv) +{ + timer_T *timer; + + for (timer = first_timer; timer != NULL; timer = timer->tr_next) + if (timer->tr_id != -1) + add_timer_info(rettv, timer); +} + +/* + * Mark references in partials of timers. + */ + int +set_ref_in_timer(int copyID) +{ + int abort = FALSE; + timer_T *timer; + typval_T tv; + + for (timer = first_timer; timer != NULL; timer = timer->tr_next) + { + if (timer->tr_partial != NULL) + { + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = timer->tr_partial; + } + else + { + tv.v_type = VAR_FUNC; + tv.vval.v_string = timer->tr_callback; + } + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); + } + return abort; +} + +# if defined(EXITFREE) || defined(PROTO) + void +timer_free_all() +{ + timer_T *timer; + + while (first_timer != NULL) + { + timer = first_timer; + remove_timer(timer); + free_timer(timer); + } +} +# endif +# endif + +#if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) && defined(FEAT_FLOAT) && defined(FEAT_PROFILE) +# if defined(HAVE_MATH_H) +# include +# endif + +/* + * Divide the time "tm" by "count" and store in "tm2". + */ + void +profile_divide(proftime_T *tm, int count, proftime_T *tm2) +{ + if (count == 0) + profile_zero(tm2); + else + { +# ifdef WIN3264 + tm2->QuadPart = tm->QuadPart / count; +# 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)); +# endif + } +} +#endif + +# if defined(FEAT_PROFILE) || defined(PROTO) +/* + * Functions for profiling. + */ +static void script_dump_profile(FILE *fd); +static proftime_T prof_wait_time; + +/* + * Add the time "tm2" to "tm". + */ + void +profile_add(proftime_T *tm, proftime_T *tm2) +{ +# ifdef WIN3264 + tm->QuadPart += tm2->QuadPart; +# else + tm->tv_usec += tm2->tv_usec; + tm->tv_sec += tm2->tv_sec; + if (tm->tv_usec >= 1000000) + { + tm->tv_usec -= 1000000; + ++tm->tv_sec; + } +# endif +} + +/* + * Add the "self" time from the total time and the children's time. + */ + void +profile_self(proftime_T *self, proftime_T *total, proftime_T *children) +{ + /* Check that the result won't be negative. Can happen with recursive + * calls. */ +#ifdef WIN3264 + if (total->QuadPart <= children->QuadPart) + return; +#else + if (total->tv_sec < children->tv_sec + || (total->tv_sec == children->tv_sec + && total->tv_usec <= children->tv_usec)) + return; +#endif + profile_add(self, total); + profile_sub(self, children); +} + +/* + * Get the current waittime. + */ + void +profile_get_wait(proftime_T *tm) +{ + *tm = prof_wait_time; +} + +/* + * Subtract the passed waittime since "tm" from "tma". + */ + void +profile_sub_wait(proftime_T *tm, proftime_T *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(proftime_T *tm1, proftime_T *tm2) +{ +# ifdef WIN3264 + return (tm1->QuadPart == tm2->QuadPart); +# else + return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec); +# endif +} + +/* + * Return <0, 0 or >0 if "tm1" < "tm2", "tm1" == "tm2" or "tm1" > "tm2" + */ + int +profile_cmp(const proftime_T *tm1, const proftime_T *tm2) +{ +# ifdef WIN3264 + return (int)(tm2->QuadPart - tm1->QuadPart); +# else + if (tm1->tv_sec == tm2->tv_sec) + return tm2->tv_usec - tm1->tv_usec; + return tm2->tv_sec - tm1->tv_sec; +# endif +} + +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) + { + vim_free(profile_fname); + profile_fname = expand_env_save_opt(e, TRUE); + 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 UNUSED, 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) + semsg(_(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_init2(&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( + proftime_T *tm) /* place to store wait time */ +{ + scriptitem_T *si; + + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) + { + si = &SCRIPT_ITEM(current_sctx.sc_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(proftime_T *tm) +{ + scriptitem_T *si; + + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) + { + si = &SCRIPT_ITEM(current_sctx.sc_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 + { + /* Keep going till the end of file, so that trailing + * continuation lines are listed. */ + for (i = 0; ; ++i) + { + if (vim_fgets(IObuff, IOSIZE, sfd)) + break; + /* When a line has been truncated, append NL, taking care + * of multi-byte characters . */ + if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL) + { + int n = IOSIZE - 2; + + if (enc_utf8) + { + /* Move to the first byte of this char. + * utf_head_off() doesn't work, because it checks + * for a truncated character. */ + while (n > 0 && (IObuff[n] & 0xc0) == 0x80) + --n; + } + else if (has_mbyte) + n -= mb_head_off(IObuff, IObuff + n); + IObuff[n] = NL; + IObuff[n + 1] = NUL; + } + if (i < si->sn_prl_ga.ga_len + && (pp = &PRL_ITEM(si, i))->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_sctx.sc_sid > 0) + return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force; + return FALSE; +} + +# endif +#endif + +/* + * 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; + bufref_T bufref; + + if (!(p_aw || p_awa) || !p_write +#ifdef FEAT_QUICKFIX + /* never autowrite a "nofile" or "nowrite" buffer */ + || bt_dontwrite(buf) +#endif + || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL) + return FAIL; + set_bufref(&bufref, buf); + 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 (bufref_valid(&bufref) && bufIsChanged(buf)) + r = FAIL; + return r; +} + +/* + * Flush all buffers, except the ones that are readonly or are never written. + */ + void +autowrite_all(void) +{ + buf_T *buf; + + if (!(p_aw || p_awa) || !p_write) + return; + FOR_ALL_BUFFERS(buf) + if (bufIsChanged(buf) && !buf->b_p_ro && !bt_dontwrite(buf)) + { + bufref_T bufref; + + set_bufref(&bufref, buf); + + (void)buf_write_all(buf, FALSE); + + /* an autocommand may have deleted the buffer */ + if (!bufref_valid(&bufref)) + 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); + bufref_T bufref; + + set_bufref(&bufref, buf); + + if ( !forceit + && bufIsChanged(buf) + && ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1) + && (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL)) + { +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + if ((p_confirm || cmdmod.confirm) && p_write) + { + buf_T *buf2; + int count = 0; + + if (flags & CCGD_ALLBUF) + FOR_ALL_BUFFERS(buf2) + if (bufIsChanged(buf2) + && (buf2->b_ffname != NULL +# ifdef FEAT_BROWSE + || cmdmod.browse +# endif + )) + ++count; + if (!bufref_valid(&bufref)) + /* Autocommand deleted buffer, oops! It's not changed now. */ + return FALSE; + + dialog_changed(buf, count > 1); + + if (!bufref_valid(&bufref)) + /* Autocommand deleted buffer, oops! It's not changed now. */ + return FALSE; + return bufIsChanged(buf); + } +#endif + if (flags & CCGD_EXCMD) + no_write_message(); + else + no_write_message_nobang(curbuf); + return TRUE; + } + return FALSE; +} + +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO) + +#if defined(FEAT_BROWSE) || defined(PROTO) +/* + * When wanting to write a file without a file name, ask the user for a name. + */ + void +browse_save_fname(buf_T *buf) +{ + if (buf->b_fname == NULL) + { + char_u *fname; + + fname = do_browse(BROWSE_SAVE, (char_u *)_("Save As"), + NULL, NULL, NULL, NULL, buf); + if (fname != NULL) + { + if (setfname(buf, fname, NULL, TRUE) == OK) + buf->b_flags |= BF_NOTEDITED; + vim_free(fname); + } + } +} +#endif + +/* + * 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); + 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) + { +#ifdef FEAT_BROWSE + /* May get file name, when there is none */ + browse_save_fname(buf); +#endif + 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_ALL_BUFFERS(buf2) + { + if (bufIsChanged(buf2) + && (buf2->b_ffname != NULL +#ifdef FEAT_BROWSE + || cmdmod.browse +#endif + ) + && !buf2->b_p_ro) + { + bufref_T bufref; + + set_bufref(&bufref, buf2); +#ifdef FEAT_BROWSE + /* May get file name, when there is none */ + browse_save_fname(buf2); +#endif + 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 (!bufref_valid(&bufref)) + buf2 = firstbuf; + } + } + } + else if (ret == VIM_DISCARDALL) + { + /* + * mark all buffers as unchanged + */ + FOR_ALL_BUFFERS(buf2) + unchanged(buf2, TRUE); + } +} +#endif + +/* + * 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 ( buf_hide(buf) + || !bufIsChanged(buf) + || buf->b_nwindows > 1 + || autowrite(buf, forceit) == OK + || forceit); +} + +/* + * 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. + * When "unload" is TRUE the current buffer is unloaded instead of making it + * hidden. This is used for ":q!". + */ + int +check_changed_any( + int hidden, /* Only check hidden buffers */ + int unload) +{ + int ret = FALSE; + buf_T *buf; + int save; + int i; + int bufnum = 0; + int bufcount = 0; + int *bufnrs; + tabpage_T *tp; + win_T *wp; + + /* Make a list of all buffers, with the most important ones first. */ + FOR_ALL_BUFFERS(buf) + ++bufcount; + + if (bufcount == 0) + return FALSE; + + bufnrs = (int *)alloc(sizeof(int) * bufcount); + if (bufnrs == NULL) + return FALSE; + + /* curbuf */ + bufnrs[bufnum++] = curbuf->b_fnum; + + /* buffers in current tab */ + FOR_ALL_WINDOWS(wp) + if (wp->w_buffer != curbuf) + add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); + + /* buffers in other tabs */ + FOR_ALL_TABPAGES(tp) + 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 buffer */ + FOR_ALL_BUFFERS(buf) + 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)) + { + bufref_T bufref; + + set_bufref(&bufref, buf); +#ifdef FEAT_TERMINAL + if (term_job_running(buf->b_term)) + { + if (term_try_stop_job(buf) == FAIL) + break; + } + else +#endif + /* 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) && bufref_valid(&bufref)) + break; /* didn't save - still changes */ + } + } + + if (i >= bufnum) + goto theend; + + /* Get here if "buf" cannot be abandoned. */ + ret = TRUE; + exiting = FALSE; +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + /* + * When ":confirm" used, don't give an error message. + */ + if (!(p_confirm || cmdmod.confirm)) +#endif + { + /* 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 ( +#ifdef FEAT_TERMINAL + term_job_running(buf->b_term) + ? semsg(_("E947: Job still running in buffer \"%s\""), + buf->b_fname) + : +#endif + semsg(_("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) + { + bufref_T bufref; + + set_bufref(&bufref, buf); + + goto_tabpage_win(tp, wp); + + /* Paranoia: did autocms wipe out the buffer with changes? */ + if (!bufref_valid(&bufref)) + goto theend; + goto buf_found; + } +buf_found: + + /* Open the changed buffer in the current window. */ + if (buf != curbuf) + set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO); + +theend: + vim_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 int do_arglist(char_u *str, int what, int after, int will_edit); +static void alist_check_arg_idx(void); +static void alist_add_list(int count, char_u **files, int after, int will_edit); +#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". + */ + static int +get_arglist(garray_T *gap, char_u *str, int escaped) +{ + ga_init2(gap, (int)sizeof(char_u *), 20); + while (*str != NUL) + { + if (ga_grow(gap, 1) == FAIL) + { + ga_clear(gap); + return FAIL; + } + ((char_u **)gap->ga_data)[gap->ga_len++] = str; + + /* If str is escaped, don't handle backslashes or spaces */ + if (!escaped) + return OK; + + /* Isolate one argument, change it in-place, put a NUL after it. */ + str = do_one_arg(str); + } + return OK; +} + +#if defined(FEAT_QUICKFIX) || defined(FEAT_SYN_HL) || defined(PROTO) +/* + * 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; + + if (get_arglist(&ga, str, TRUE) == FAIL) + return FAIL; + 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; +} +#endif + +/* + * Redefine the argument list. + */ + void +set_arglist(char_u *str) +{ + do_arglist(str, AL_SET, 0, FALSE); +} + +/* + * "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 UNUSED, // 0 means before first one + int will_edit) // will edit added argument +{ + garray_T new_ga; + int exp_count; + char_u **exp_files; + int i; + char_u *p; + int match; + int arg_escaped = TRUE; + + /* + * Set default argument for ":argadd" command. + */ + if (what == AL_ADD && *str == NUL) + { + if (curbuf->b_ffname == NULL) + return FAIL; + str = curbuf->b_fname; + arg_escaped = FALSE; + } + + /* + * Collect all file name arguments in "new_ga". + */ + if (get_arglist(&new_ga, str, arg_escaped) == FAIL) + return FAIL; + + 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) + { + vim_free(p); + break; + } + + didone = FALSE; + for (match = 0; match < ARGCOUNT; ++match) + if (vim_regexec(®match, alist_name(&ARGLIST[match]), + (colnr_T)0)) + { + didone = TRUE; + vim_free(ARGLIST[match].ae_fname); + mch_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); + vim_free(p); + if (!didone) + semsg(_(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 || exp_count == 0) + { + emsg(_(e_nomatch)); + return FAIL; + } + + if (what == AL_ADD) + { + alist_add_list(exp_count, exp_files, after, will_edit); + vim_free(exp_files); + } + else /* what == AL_SET */ + alist_set(ALIST(curwin), exp_count, exp_files, will_edit, 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 + || !(fullpathcmp( + alist_name(&WARGLIST(win)[win->w_arg_idx]), + win->w_buffer->b_ffname, TRUE) & FPC_SAME)))); +} + +/* + * 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 + && (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]), + win->w_buffer->b_ffname, TRUE) & FPC_SAME)))) + 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 (*eap->arg != NUL) + { + /* + * ":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) + { + char_u **items = (char_u **)alloc(sizeof(char_u *) * ARGCOUNT); + + if (items != NULL) + { + /* Overwrite the command, for a short list there is no + * scrolling required and no wait_return(). */ + gotocmdline(TRUE); + + for (i = 0; i < ARGCOUNT; ++i) + items[i] = alist_name(&ARGLIST[i]); + list_in_columns(items, ARGCOUNT, curwin->w_arg_idx); + vim_free(items); + } + } + } + else if (eap->cmdidx == CMD_arglocal) + { + garray_T *gap = &curwin->w_alist->al_ga; + + /* + * ":argslocal": make a local copy of the global argument list. + */ + if (ga_grow(gap, GARGCOUNT) == OK) + 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(); +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + + /* 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 (buf_hide(curbuf)) + { + p = fix_fname(alist_name(&ARGLIST[argn])); + other = otherfile(p); + vim_free(p); + } + if ((!buf_hide(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, + (buf_hide(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 ( buf_hide(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, TRUE) == 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 i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1; + // Whether curbuf will be reused, curbuf->b_ffname will be set. + int curbuf_is_reusable = curbuf_reusable(); + + if (do_arglist(eap->arg, AL_ADD, i, TRUE) == FAIL) + return; +#ifdef FEAT_TITLE + maketitle(); +#endif + + if (curwin->w_arg_idx == 0 + && (curbuf->b_ml.ml_flags & ML_EMPTY) + && (curbuf->b_ffname == NULL || curbuf_is_reusable)) + i = 0; + /* Edit the argument. */ + if (i < ARGCOUNT) + 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, + FALSE); +#ifdef FEAT_TITLE + maketitle(); +#endif +} + +/* + * ":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) + /* Can't have both a range and an argument. */ + emsg(_(e_invarg)); + else if (n <= 0) + { + /* Don't give an error for ":%argdel" if the list is empty. */ + if (eap->line1 != 1 || eap->line2 != 0) + emsg(_(e_invrange)); + } + else + { + for (i = eap->line1; i <= eap->line2; ++i) + vim_free(ARGLIST[i - 1].ae_fname); + mch_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; + if (ARGCOUNT == 0) + curwin->w_arg_idx = 0; + else if (curwin->w_arg_idx >= ARGCOUNT) + curwin->w_arg_idx = ARGCOUNT - 1; + } + } + else if (*eap->arg == NUL) + emsg(_(e_argreq)); + else + do_arglist(eap->arg, AL_DEL, 0, FALSE); +#ifdef FEAT_TITLE + maketitle(); +#endif +} + +/* + * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo" + */ + void +ex_listdo(exarg_T *eap) +{ + int i; + win_T *wp; + tabpage_T *tp; + buf_T *buf = curbuf; + int next_fnum = 0; +#if defined(FEAT_SYN_HL) + char_u *save_ei = NULL; +#endif + char_u *p_shm_save; +#ifdef FEAT_QUICKFIX + int qf_size = 0; + int qf_idx; +#endif + +#ifndef FEAT_QUICKFIX + if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo || + eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) + { + ex_ni(eap); + return; + } +#endif + +#if defined(FEAT_SYN_HL) + 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"); +#endif +#ifdef FEAT_CLIPBOARD + start_global_changes(); +#endif + + if (eap->cmdidx == CMD_windo + || eap->cmdidx == CMD_tabdo + || buf_hide(curbuf) + || !check_changed(curbuf, CCGD_AW + | (eap->forceit ? CCGD_FORCEIT : 0) + | CCGD_EXCMD)) + { + i = 0; + /* start at the eap->line1 argument/window/buffer */ + wp = firstwin; + tp = first_tabpage; + switch (eap->cmdidx) + { + case CMD_windo: + for ( ; wp != NULL && i + 1 < eap->line1; wp = wp->w_next) + i++; + break; + case CMD_tabdo: + for( ; tp != NULL && i + 1 < eap->line1; tp = tp->tp_next) + i++; + break; + case CMD_argdo: + i = eap->line1 - 1; + break; + default: + break; + } + /* set pcmark now */ + if (eap->cmdidx == CMD_bufdo) + { + /* Advance to the first listed buffer after "eap->line1". */ + for (buf = firstbuf; buf != NULL && (buf->b_fnum < eap->line1 + || !buf->b_p_bl); buf = buf->b_next) + if (buf->b_fnum > eap->line2) + { + buf = NULL; + break; + } + if (buf != NULL) + goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum); + } +#ifdef FEAT_QUICKFIX + else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) + { + qf_size = qf_get_size(eap); + if (qf_size <= 0 || eap->line1 > qf_size) + buf = NULL; + else + { + ex_cc(eap); + + buf = curbuf; + i = eap->line1 - 1; + if (eap->addr_count <= 0) + /* default is all the quickfix/location list entries */ + eap->line2 = qf_size; + } + } +#endif + else + setpcmark(); + listcmd_busy = TRUE; /* avoids setting pcmark below */ + + while (!got_int && buf != NULL) + { + 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); + vim_free(p_shm_save); + } + if (curwin->w_arg_idx != i) + break; + } + 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; + } + } + + ++i; + + /* 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 || next_fnum > eap->line2) + break; + /* Check if the buffer still exists. */ + FOR_ALL_BUFFERS(buf) + 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); + vim_free(p_shm_save); + + /* If autocommands took us elsewhere, quit here. */ + if (curbuf->b_fnum != next_fnum) + break; + } + +#ifdef FEAT_QUICKFIX + if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) + { + if (i >= qf_size || i >= eap->line2) + break; + + qf_idx = qf_get_cur_idx(eap); + + ex_cnext(eap); + + /* If jumping to the next quickfix entry fails, quit here */ + if (qf_get_cur_idx(eap) == qf_idx) + break; + } +#endif + + 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); + } + + if (eap->cmdidx == CMD_windo || eap->cmdidx == CMD_tabdo) + if (i+1 > eap->line2) + break; + if (eap->cmdidx == CMD_argdo && i >= eap->line2) + break; + } + listcmd_busy = FALSE; + } + +#if defined(FEAT_SYN_HL) + if (save_ei != NULL) + { + au_event_restore(save_ei); + apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, + curbuf->b_fname, TRUE, curbuf); + } +#endif +#ifdef FEAT_CLIPBOARD + end_global_changes(); +#endif +} + +/* + * 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. + */ + static void +alist_add_list( + int count, + char_u **files, + int after, // where to add: 0 = before first one + int will_edit) // will edit adding argument +{ + int i; + int old_argcount = ARGCOUNT; + + if (ga_grow(&ALIST(curwin)->al_ga, count) == OK) + { + if (after < 0) + after = 0; + if (after > ARGCOUNT) + after = ARGCOUNT; + if (after < ARGCOUNT) + mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]), + (ARGCOUNT - after) * sizeof(aentry_T)); + for (i = 0; i < count; ++i) + { + int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0); + + ARGLIST[after + i].ae_fname = files[i]; + ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags); + } + ALIST(curwin)->al_ga.ga_len += count; + if (old_argcount > 0 && curwin->w_arg_idx >= after) + curwin->w_arg_idx += count; + return; + } + + for (i = 0; i < count; ++i) + vim_free(files[i]); +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) +/* + * Function given to ExpandGeneric() to obtain the possible arguments of the + * argedit and argdelete commands. + */ + char_u * +get_arglist_name(expand_T *xp UNUSED, int idx) +{ + if (idx >= ARGCOUNT) + return NULL; + + return alist_name(&ARGLIST[idx]); +} +#endif + + +#ifdef FEAT_EVAL +/* + * ":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 = alloc((unsigned)(STRLEN(eap->arg) + 14)); + if (buf != NULL) + { + 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, DIP_ALL) == FAIL) + semsg(_("E666: compiler not supported: %s"), eap->arg); + vim_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); + vim_free(old_cur_comp); + } + else + do_unlet((char_u *)"g:current_compiler", TRUE); + } + } + } +} +#endif + +/* + * ":runtime [what] {name}" + */ + void +ex_runtime(exarg_T *eap) +{ + char_u *arg = eap->arg; + char_u *p = skiptowhite(arg); + int len = (int)(p - arg); + int flags = eap->forceit ? DIP_ALL : 0; + + if (STRNCMP(arg, "START", len) == 0) + { + flags += DIP_START + DIP_NORTP; + arg = skipwhite(arg + len); + } + else if (STRNCMP(arg, "OPT", len) == 0) + { + flags += DIP_OPT + DIP_NORTP; + arg = skipwhite(arg + len); + } + else if (STRNCMP(arg, "PACK", len) == 0) + { + flags += DIP_START + DIP_OPT + DIP_NORTP; + arg = skipwhite(arg + len); + } + else if (STRNCMP(arg, "ALL", len) == 0) + { + flags += DIP_START + DIP_OPT; + arg = skipwhite(arg + len); + } + + source_runtime(arg, flags); +} + + static void +source_callback(char_u *fname, void *cookie UNUSED) +{ + (void)do_source(fname, FALSE, DOSO_NONE); +} + +/* + * Find the file "name" in all directories in "path" and invoke + * "callback(fname, cookie)". + * "name" can contain wildcards. + * When "flags" has DIP_ALL: source all files, otherwise only the first one. + * When "flags" has DIP_DIR: find directories instead of files. + * When "flags" has DIP_ERR: give an error message if there is no match. + * + * return FAIL when no file could be sourced, OK otherwise. + */ + int +do_in_path( + char_u *path, + char_u *name, + int flags, + 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; +#ifdef AMIGA + struct Process *proc = (struct Process *)FindTask(0L); + APTR save_winptr = proc->pr_WindowPtr; + + /* Avoid a requester here for a volume that doesn't exist. */ + proc->pr_WindowPtr = (APTR)-1L; +#endif + + /* Make a copy of 'runtimepath'. Invoking the callback may change the + * value. */ + rtp_copy = vim_strsave(path); + buf = alloc(MAXPATHL); + if (buf != NULL && rtp_copy != NULL) + { + if (p_verbose > 1 && name != NULL) + { + verbose_enter(); + smsg(_("Searching for \"%s\" in \"%s\""), + (char *)name, (char *)path); + verbose_leave(); + } + + /* Loop over all entries in 'runtimepath'. */ + rtp = rtp_copy; + while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) + { + size_t buflen; + + /* Copy the path from 'runtimepath' to buf[]. */ + copy_option_part(&rtp, buf, MAXPATHL, ","); + buflen = STRLEN(buf); + + /* Skip after or non-after directories. */ + if (flags & (DIP_NOAFTER | DIP_AFTER)) + { + int is_after = buflen >= 5 + && STRCMP(buf + buflen - 5, "after") == 0; + + if ((is_after && (flags & DIP_NOAFTER)) + || (!is_after && (flags & DIP_AFTER))) + continue; + } + + if (name == NULL) + { + (*callback)(buf, (void *) &cookie); + if (!did_one) + did_one = (cookie == NULL); + } + else if (buflen + STRLEN(name) + 2 < MAXPATHL) + { + add_pathsep(buf); + tail = buf + STRLEN(buf); + + /* Loop over all patterns in "name" */ + np = name; + while (*np != NUL && ((flags & DIP_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(_("Searching for \"%s\""), buf); + verbose_leave(); + } + + /* Expand wildcards, invoke the callback for each match. */ + if (gen_expand_wildcards(1, &buf, &num_files, &files, + (flags & DIP_DIR) ? EW_DIR : EW_FILE) == OK) + { + for (i = 0; i < num_files; ++i) + { + (*callback)(files[i], cookie); + did_one = TRUE; + if (!(flags & DIP_ALL)) + break; + } + FreeWild(num_files, files); + } + } + } + } + } + vim_free(buf); + vim_free(rtp_copy); + if (!did_one && name != NULL) + { + char *basepath = path == p_rtp ? "runtimepath" : "packpath"; + + if (flags & DIP_ERR) + semsg(_(e_dirnotf), basepath, name); + else if (p_verbose > 0) + { + verbose_enter(); + smsg(_("not found in '%s': \"%s\""), basepath, name); + verbose_leave(); + } + } + +#ifdef AMIGA + proc->pr_WindowPtr = save_winptr; +#endif + + return did_one ? OK : FAIL; +} + +/* + * Find "name" in "path". When found, invoke the callback function for + * it: callback(fname, "cookie") + * When "flags" has DIP_ALL 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 "path". Cookie is + * passed by reference in this case, setting it to NULL indicates that callback + * has done its job. + */ + static int +do_in_path_and_pp( + char_u *path, + char_u *name, + int flags, + void (*callback)(char_u *fname, void *ck), + void *cookie) +{ + int done = FAIL; + char_u *s; + int len; + char *start_dir = "pack/*/start/*/%s"; + char *opt_dir = "pack/*/opt/*/%s"; + + if ((flags & DIP_NORTP) == 0) + done = do_in_path(path, name, flags, callback, cookie); + + if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) + { + len = (int)(STRLEN(start_dir) + STRLEN(name)); + s = alloc(len); + if (s == NULL) + return FAIL; + vim_snprintf((char *)s, len, start_dir, name); + done = do_in_path(p_pp, s, flags, callback, cookie); + vim_free(s); + } + + if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT)) + { + len = (int)(STRLEN(opt_dir) + STRLEN(name)); + s = alloc(len); + if (s == NULL) + return FAIL; + vim_snprintf((char *)s, len, opt_dir, name); + done = do_in_path(p_pp, s, flags, callback, cookie); + vim_free(s); + } + + return done; +} + +/* + * Just like do_in_path_and_pp(), using 'runtimepath' for "path". + */ + int +do_in_runtimepath( + char_u *name, + int flags, + void (*callback)(char_u *fname, void *ck), + void *cookie) +{ + return do_in_path_and_pp(p_rtp, name, flags, callback, cookie); +} + +/* + * Source the file "name" from all directories in 'runtimepath'. + * "name" can contain wildcards. + * When "flags" has DIP_ALL: 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 flags) +{ + return source_in_path(p_rtp, name, flags); +} + +/* + * Just like source_runtime(), but use "path" instead of 'runtimepath'. + */ + int +source_in_path(char_u *path, char_u *name, int flags) +{ + return do_in_path_and_pp(path, name, flags, source_callback, NULL); +} + + +#if defined(FEAT_EVAL) || defined(PROTO) + +/* + * Expand wildcards in "pat" and invoke do_source() for each match. + */ + static void +source_all_matches(char_u *pat) +{ + int num_files; + char_u **files; + int i; + + if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK) + { + for (i = 0; i < num_files; ++i) + (void)do_source(files[i], FALSE, DOSO_NONE); + FreeWild(num_files, files); + } +} + +/* + * Add the package directory to 'runtimepath'. + */ + static int +add_pack_dir_to_rtp(char_u *fname) +{ + char_u *p4, *p3, *p2, *p1, *p; + char_u *entry; + char_u *insp = NULL; + int c; + char_u *new_rtp; + int keep; + size_t oldlen; + size_t addlen; + size_t new_rtp_len; + char_u *afterdir = NULL; + size_t afterlen = 0; + char_u *after_insp = NULL; + char_u *ffname = NULL; + size_t fname_len; + char_u *buf = NULL; + char_u *rtp_ffname; + int match; + int retval = FAIL; + + p4 = p3 = p2 = p1 = get_past_head(fname); + for (p = p1; *p; MB_PTR_ADV(p)) + if (vim_ispathsep_nocolon(*p)) + { + p4 = p3; p3 = p2; p2 = p1; p1 = p; + } + + /* now we have: + * rtp/pack/name/start/name + * p4 p3 p2 p1 + * + * find the part up to "pack" in 'runtimepath' */ + c = *++p4; /* append pathsep in order to expand symlink */ + *p4 = NUL; + ffname = fix_fname(fname); + *p4 = c; + if (ffname == NULL) + return FAIL; + + // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences. + // Also stop at the first "after" directory. + fname_len = STRLEN(ffname); + buf = alloc(MAXPATHL); + if (buf == NULL) + goto theend; + for (entry = p_rtp; *entry != NUL; ) + { + char_u *cur_entry = entry; + + copy_option_part(&entry, buf, MAXPATHL, ","); + if (insp == NULL) + { + add_pathsep(buf); + rtp_ffname = fix_fname(buf); + if (rtp_ffname == NULL) + goto theend; + match = vim_fnamencmp(rtp_ffname, ffname, fname_len) == 0; + vim_free(rtp_ffname); + if (match) + // Insert "ffname" after this entry (and comma). + insp = entry; + } + + if ((p = (char_u *)strstr((char *)buf, "after")) != NULL + && p > buf + && vim_ispathsep(p[-1]) + && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ',')) + { + if (insp == NULL) + // Did not find "ffname" before the first "after" directory, + // insert it before this entry. + insp = cur_entry; + after_insp = cur_entry; + break; + } + } + + if (insp == NULL) + // Both "fname" and "after" not found, append at the end. + insp = p_rtp + STRLEN(p_rtp); + + // check if rtp/pack/name/start/name/after exists + afterdir = concat_fnames(fname, (char_u *)"after", TRUE); + if (afterdir != NULL && mch_isdir(afterdir)) + afterlen = STRLEN(afterdir) + 1; // add one for comma + + oldlen = STRLEN(p_rtp); + addlen = STRLEN(fname) + 1; // add one for comma + new_rtp = alloc((int)(oldlen + addlen + afterlen + 1)); // add one for NUL + if (new_rtp == NULL) + goto theend; + + // We now have 'rtp' parts: {keep}{keep_after}{rest}. + // Create new_rtp, first: {keep},{fname} + keep = (int)(insp - p_rtp); + mch_memmove(new_rtp, p_rtp, keep); + new_rtp_len = keep; + if (*insp == NUL) + new_rtp[new_rtp_len++] = ','; // add comma before + mch_memmove(new_rtp + new_rtp_len, fname, addlen - 1); + new_rtp_len += addlen - 1; + if (*insp != NUL) + new_rtp[new_rtp_len++] = ','; // add comma after + + if (afterlen > 0 && after_insp != NULL) + { + int keep_after = (int)(after_insp - p_rtp); + + // Add to new_rtp: {keep},{fname}{keep_after},{afterdir} + mch_memmove(new_rtp + new_rtp_len, p_rtp + keep, + keep_after - keep); + new_rtp_len += keep_after - keep; + mch_memmove(new_rtp + new_rtp_len, afterdir, afterlen - 1); + new_rtp_len += afterlen - 1; + new_rtp[new_rtp_len++] = ','; + keep = keep_after; + } + + if (p_rtp[keep] != NUL) + // Append rest: {keep},{fname}{keep_after},{afterdir}{rest} + mch_memmove(new_rtp + new_rtp_len, p_rtp + keep, oldlen - keep + 1); + else + new_rtp[new_rtp_len] = NUL; + + if (afterlen > 0 && after_insp == NULL) + { + // Append afterdir when "after" was not found: + // {keep},{fname}{rest},{afterdir} + STRCAT(new_rtp, ","); + STRCAT(new_rtp, afterdir); + } + + set_option_value((char_u *)"rtp", 0L, new_rtp, 0); + vim_free(new_rtp); + retval = OK; + +theend: + vim_free(buf); + vim_free(ffname); + vim_free(afterdir); + return retval; +} + +/* + * Load scripts in "plugin" and "ftdetect" directories of the package. + */ + static int +load_pack_plugin(char_u *fname) +{ + static char *plugpat = "%s/plugin/**/*.vim"; + static char *ftpat = "%s/ftdetect/*.vim"; + int len; + char_u *ffname = fix_fname(fname); + char_u *pat = NULL; + int retval = FAIL; + + if (ffname == NULL) + return FAIL; + len = (int)STRLEN(ffname) + (int)STRLEN(ftpat); + pat = alloc(len); + if (pat == NULL) + goto theend; + vim_snprintf((char *)pat, len, plugpat, ffname); + source_all_matches(pat); + + { + char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes"); + + /* If runtime/filetype.vim wasn't loaded yet, the scripts will be + * found when it loads. */ + if (cmd != NULL && eval_to_number(cmd) > 0) + { + do_cmdline_cmd((char_u *)"augroup filetypedetect"); + vim_snprintf((char *)pat, len, ftpat, ffname); + source_all_matches(pat); + do_cmdline_cmd((char_u *)"augroup END"); + } + vim_free(cmd); + } + vim_free(pat); + retval = OK; + +theend: + vim_free(ffname); + return retval; +} + +/* used for "cookie" of add_pack_plugin() */ +static int APP_ADD_DIR; +static int APP_LOAD; +static int APP_BOTH; + + static void +add_pack_plugin(char_u *fname, void *cookie) +{ + if (cookie != &APP_LOAD) + { + char_u *buf = alloc(MAXPATHL); + char_u *p; + int found = FALSE; + + if (buf == NULL) + return; + p = p_rtp; + while (*p != NUL) + { + copy_option_part(&p, buf, MAXPATHL, ","); + if (pathcmp((char *)buf, (char *)fname, -1) == 0) + { + found = TRUE; + break; + } + } + vim_free(buf); + if (!found) + /* directory is not yet in 'runtimepath', add it */ + if (add_pack_dir_to_rtp(fname) == FAIL) + return; + } + + if (cookie != &APP_ADD_DIR) + load_pack_plugin(fname); +} + +/* + * Add all packages in the "start" directory to 'runtimepath'. + */ + void +add_pack_start_dirs(void) +{ + do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, + add_pack_plugin, &APP_ADD_DIR); +} + +/* + * Load plugins from all packages in the "start" directory. + */ + void +load_start_packages(void) +{ + did_source_packages = TRUE; + do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, + add_pack_plugin, &APP_LOAD); +} + +/* + * ":packloadall" + * Find plugins in the package directories and source them. + */ + void +ex_packloadall(exarg_T *eap) +{ + if (!did_source_packages || eap->forceit) + { + /* First do a round to add all directories to 'runtimepath', then load + * the plugins. This allows for plugins to use an autoload directory + * of another plugin. */ + add_pack_start_dirs(); + load_start_packages(); + } +} + +/* + * ":packadd[!] {name}" + */ + void +ex_packadd(exarg_T *eap) +{ + static char *plugpat = "pack/*/%s/%s"; + int len; + char *pat; + int round; + int res = OK; + + /* Round 1: use "start", round 2: use "opt". */ + for (round = 1; round <= 2; ++round) + { + /* Only look under "start" when loading packages wasn't done yet. */ + if (round == 1 && did_source_packages) + continue; + + len = (int)STRLEN(plugpat) + (int)STRLEN(eap->arg) + 5; + pat = (char *)alloc(len); + if (pat == NULL) + return; + vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg); + /* The first round don't give a "not found" error, in the second round + * only when nothing was found in the first round. */ + res = do_in_path(p_pp, (char_u *)pat, + DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0), + add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH); + vim_free(pat); + } +} +#endif + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * ":options" + */ + void +ex_options( + exarg_T *eap UNUSED) +{ + vim_setenv((char_u *)"OPTWIN_CMD", (char_u *)(cmdmod.tab ? "tab" : "")); + cmd_source((char_u *)SYS_OPTWIN_FILE, NULL); +} +#endif + +#if defined(FEAT_PYTHON3) || defined(FEAT_PYTHON) || defined(PROTO) + +# if (defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)) || defined(PROTO) +/* + * Detect Python 3 or 2, and initialize 'pyxversion'. + */ + void +init_pyxversion(void) +{ + if (p_pyx == 0) + { + if (python3_enabled(FALSE)) + p_pyx = 3; + else if (python_enabled(FALSE)) + p_pyx = 2; + } +} +# endif + +/* + * Does a file contain one of the following strings at the beginning of any + * line? + * "#!(any string)python2" => returns 2 + * "#!(any string)python3" => returns 3 + * "# requires python 2.x" => returns 2 + * "# requires python 3.x" => returns 3 + * otherwise return 0. + */ + static int +requires_py_version(char_u *filename) +{ + FILE *file; + int requires_py_version = 0; + int i, lines; + + lines = (int)p_mls; + if (lines < 0) + lines = 5; + + file = mch_fopen((char *)filename, "r"); + if (file != NULL) + { + for (i = 0; i < lines; i++) + { + if (vim_fgets(IObuff, IOSIZE, file)) + break; + if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!') + { + /* Check shebang. */ + if (strstr((char *)IObuff + 2, "python2") != NULL) + { + requires_py_version = 2; + break; + } + if (strstr((char *)IObuff + 2, "python3") != NULL) + { + requires_py_version = 3; + break; + } + } + IObuff[21] = '\0'; + if (STRCMP("# requires python 2.x", IObuff) == 0) + { + requires_py_version = 2; + break; + } + if (STRCMP("# requires python 3.x", IObuff) == 0) + { + requires_py_version = 3; + break; + } + } + fclose(file); + } + return requires_py_version; +} + + +/* + * Source a python file using the requested python version. + */ + static void +source_pyx_file(exarg_T *eap, char_u *fname) +{ + exarg_T ex; + int v = requires_py_version(fname); + +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + init_pyxversion(); +# endif + if (v == 0) + { +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + /* user didn't choose a preference, 'pyx' is used */ + v = p_pyx; +# elif defined(FEAT_PYTHON) + v = 2; +# elif defined(FEAT_PYTHON3) + v = 3; +# endif + } + + /* + * now source, if required python version is not supported show + * unobtrusive message. + */ + if (eap == NULL) + vim_memset(&ex, 0, sizeof(ex)); + else + ex = *eap; + ex.arg = fname; + ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3"); + + if (v == 2) + { +# ifdef FEAT_PYTHON + ex_pyfile(&ex); +# else + vim_snprintf((char *)IObuff, IOSIZE, + _("W20: Required python version 2.x not supported, ignoring file: %s"), + fname); + msg((char *)IObuff); +# endif + return; + } + else + { +# ifdef FEAT_PYTHON3 + ex_py3file(&ex); +# else + vim_snprintf((char *)IObuff, IOSIZE, + _("W21: Required python version 3.x not supported, ignoring file: %s"), + fname); + msg((char *)IObuff); +# endif + return; + } +} + +/* + * ":pyxfile {fname}" + */ + void +ex_pyxfile(exarg_T *eap) +{ + source_pyx_file(eap, eap->arg); +} + +/* + * ":pyx" + */ + void +ex_pyx(exarg_T *eap) +{ +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + init_pyxversion(); + if (p_pyx == 2) + ex_python(eap); + else + ex_py3(eap); +# elif defined(FEAT_PYTHON) + ex_python(eap); +# elif defined(FEAT_PYTHON3) + ex_py3(eap); +# endif +} + +/* + * ":pyxdo" + */ + void +ex_pyxdo(exarg_T *eap) +{ +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + init_pyxversion(); + if (p_pyx == 2) + ex_pydo(eap); + else + ex_py3do(eap); +# elif defined(FEAT_PYTHON) + ex_pydo(eap); +# elif defined(FEAT_PYTHON3) + ex_py3do(eap); +# endif +} + +#endif + +/* + * ":source {fname}" + */ + void +ex_source(exarg_T *eap) +{ +#ifdef FEAT_BROWSE + if (cmdmod.browse) + { + char_u *fname = NULL; + + fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg, + NULL, NULL, + (char_u *)_(BROWSE_FILTER_MACROS), NULL); + if (fname != NULL) + { + cmd_source(fname, eap); + vim_free(fname); + } + } + else +#endif + 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 +#ifdef FEAT_EVAL + || eap->cstack->cs_idx >= 0 +#endif + ); + + /* ":source" read ex commands */ + else if (do_source(fname, FALSE, DOSO_NONE) == FAIL) + semsg(_(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 +#ifdef FEAT_EVAL + 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 */ +#endif + vimconv_T conv; /* type of conversion */ +}; + +#ifdef FEAT_EVAL +/* + * 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; +} +#endif + +static char_u *get_one_sourceline(struct source_cookie *sp); + +#if (defined(WIN32) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC) +# define USE_FOPEN_NOINH +/* + * Special function to open a file without handle inheritance. + * When possible the handle is closed on exec(). + */ + static FILE * +fopen_noinh_readbin(char *filename) +{ +# ifdef WIN32 + int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0); +# else + int fd_tmp = mch_open(filename, O_RDONLY, 0); +# endif + + if (fd_tmp == -1) + return NULL; + +# ifdef HAVE_FD_CLOEXEC + { + int fdflags = fcntl(fd_tmp, F_GETFD); + if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) + (void)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; +#ifdef FEAT_EVAL + sctx_T save_current_sctx; + static scid_T last_current_SID = 0; + static int last_current_SID_seq = 0; + funccal_entry_T funccalp_entry; + int save_debug_break_level = debug_break_level; + scriptitem_T *si = NULL; +# ifdef UNIX + stat_T st; + int stat_ok; +# endif +#endif +#ifdef STARTUPTIME + struct timeval tv_rel; + struct timeval tv_start; +#endif +#ifdef FEAT_PROFILE + proftime_T wait_start; +#endif + int trigger_source_post = FALSE; + + p = expand_env_save(fname); + if (p == NULL) + return retval; + fname_exp = fix_fname(p); + vim_free(p); + if (fname_exp == NULL) + return retval; + if (mch_isdir(fname_exp)) + { + smsg(_("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)) + { +#ifdef FEAT_EVAL + retval = aborting() ? FAIL : OK; +#else + retval = OK; +#endif + if (retval == OK) + // Apply SourcePost autocommands. + apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, + FALSE, curbuf); + 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 = gettail(fname_exp); + if ((*p == '.' || *p == '_') + && (STRICMP(p + 1, "vimrc") == 0 + || STRICMP(p + 1, "gvimrc") == 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(_("could not source \"%s\""), fname); + else + smsg(_("line %ld: could not source \"%s\""), + 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(_("sourcing \"%s\""), fname); + else + smsg(_("line %ld: sourcing \"%s\""), + 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; + +#ifdef FEAT_EVAL + /* + * 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; +#endif + + /* + * 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; + +#ifdef STARTUPTIME + if (time_fd != NULL) + time_push(&tv_rel, &tv_start); +#endif + +#ifdef FEAT_EVAL +# ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + prof_child_enter(&wait_start); /* entering a child now */ +# endif + + /* Don't use local function variables, if called from a function. + * Also starts profiling timer for nested script. */ + save_funccal(&funccalp_entry); + + // Check if this script was sourced before to finds its SID. + // If it's new, generate a new SID. + // Always use a new sequence number. + save_current_sctx = current_sctx; + current_sctx.sc_seq = ++last_current_SID_seq; + current_sctx.sc_lnum = 0; +# ifdef UNIX + stat_ok = (mch_stat((char *)fname_exp, &st) >= 0); +# endif + for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0; + --current_sctx.sc_sid) + { + si = &SCRIPT_ITEM(current_sctx.sc_sid); + if (si->sn_name != NULL + && ( +# ifdef UNIX + /* Compare dev/ino when possible, it catches symbolic + * links. Also compare file names, the inode may change + * when the file was edited. */ + ((stat_ok && si->sn_dev_valid) + && (si->sn_dev == st.st_dev + && si->sn_ino == st.st_ino)) || +# endif + fnamecmp(si->sn_name, fname_exp) == 0)) + break; + } + if (current_sctx.sc_sid == 0) + { + current_sctx.sc_sid = ++last_current_SID; + if (ga_grow(&script_items, + (int)(current_sctx.sc_sid - script_items.ga_len)) == FAIL) + goto almosttheend; + while (script_items.ga_len < current_sctx.sc_sid) + { + ++script_items.ga_len; + SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; +# ifdef FEAT_PROFILE + SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE; +# endif + } + si = &SCRIPT_ITEM(current_sctx.sc_sid); + si->sn_name = fname_exp; + fname_exp = vim_strsave(si->sn_name); // used for autocmd +# ifdef UNIX + if (stat_ok) + { + si->sn_dev_valid = TRUE; + si->sn_dev = st.st_dev; + si->sn_ino = st.st_ino; + } + else + si->sn_dev_valid = FALSE; +# endif + + /* Allocate the local script variables to use for this script. */ + new_script_vars(current_sctx.sc_sid); + } + +# ifdef FEAT_PROFILE + 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); + } + } +# endif +#endif + + 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) + { + vim_free(firstline); + firstline = p; + } + } + + /* + * 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; + +#ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + { + /* Get "si" again, "script_items" may have been reallocated. */ + si = &SCRIPT_ITEM(current_sctx.sc_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); + } + } +#endif + + if (got_int) + emsg(_(e_interr)); + sourcing_name = save_sourcing_name; + sourcing_lnum = save_sourcing_lnum; + if (p_verbose > 1) + { + verbose_enter(); + smsg(_("finished sourcing %s"), fname); + if (sourcing_name != NULL) + smsg(_("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 + + if (!got_int) + trigger_source_post = TRUE; + +#ifdef FEAT_EVAL + /* + * 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; +#endif + +#ifdef FEAT_EVAL +almosttheend: + current_sctx = save_current_sctx; + restore_funccal(); +# ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + prof_child_exit(&wait_start); /* leaving a child now */ +# endif +#endif + fclose(cookie.fp); + vim_free(cookie.nextline); + vim_free(firstline); + convert_setup(&cookie.conv, NULL, NULL); + + if (trigger_source_post) + apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, FALSE, curbuf); + +theend: + vim_free(fname_exp); + return retval; +} + +#if defined(FEAT_EVAL) || defined(PROTO) + +/* + * ":scriptnames" + */ + void +ex_scriptnames(exarg_T *eap) +{ + int i; + + if (eap->addr_count > 0) + { + // :script {scriptId}: edit the script + if (eap->line2 < 1 || eap->line2 > script_items.ga_len) + emsg(_(e_invarg)); + else + { + eap->arg = SCRIPT_ITEM(eap->line2).sn_name; + do_exedit(eap, NULL); + } + return; + } + + 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("%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) + vim_free(SCRIPT_ITEM(i).sn_name); + ga_clear(&script_items); +} +# endif + +#endif + +#if defined(USE_CR) || defined(PROTO) + +# if defined(__MSL__) && (__MSL__ >= 22) +/* + * Newer version of the Metrowerks library handle DOS and UNIX files + * without help. + * Test with earlier versions, MSL 2.2 is the library supplied with + * Codewarrior Pro 2. + */ + char * +fgets_cr(char *s, int n, FILE *stream) +{ + return fgets(s, n, stream); +} +# else +/* + * 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 +#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 UNUSED, void *cookie, int indent UNUSED) +{ + struct source_cookie *sp = (struct source_cookie *)cookie; + char_u *line; + char_u *p; + +#ifdef FEAT_EVAL + /* 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; + } +# ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + script_line_end(); +# endif +#endif + /* + * 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; + } +#ifdef FEAT_PROFILE + if (line != NULL && do_profiling == PROF_YES) + script_line_start(); +#endif + + /* 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. + /* Also check for a comment in between continuation lines: "\ */ + sp->nextline = get_one_sourceline(sp); + if (sp->nextline != NULL + && (*(p = skipwhite(sp->nextline)) == '\\' + || (p[0] == '"' && p[1] == '\\' && p[2] == ' '))) + { + garray_T ga; + + ga_init2(&ga, (int)sizeof(char_u), 400); + ga_concat(&ga, line); + if (*p == '\\') + ga_concat(&ga, p + 1); + for (;;) + { + vim_free(sp->nextline); + sp->nextline = get_one_sourceline(sp); + if (sp->nextline == NULL) + break; + p = skipwhite(sp->nextline); + if (*p == '\\') + { + // 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); + } + else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ') + break; + } + ga_append(&ga, NUL); + vim_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) + { + vim_free(line); + line = s; + } + } + +#ifdef FEAT_EVAL + /* 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; + } +#endif + + 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_init2(&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 */ + if (ga_grow(&ga, 120) == FAIL) + break; + 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; + + vim_free(ga.ga_data); + return NULL; +} + +#if defined(FEAT_PROFILE) || defined(PROTO) +/* + * 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_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) + return; + si = &SCRIPT_ITEM(current_sctx.sc_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. */ + (void)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_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) + return; + si = &SCRIPT_ITEM(current_sctx.sc_sid); + if (si->sn_prof_on && si->sn_prl_idx >= 0) + si->sn_prl_execed = TRUE; +} + +/* + * Called when done with a script line. + */ + void +script_line_end(void) +{ + scriptitem_T *si; + sn_prl_T *pp; + + if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) + return; + si = &SCRIPT_ITEM(current_sctx.sc_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; + } +} +#endif + +/* + * ":scriptencoding": Set encoding conversion for a sourced script. + * Without the multi-byte feature it's simply ignored. + */ + void +ex_scriptencoding(exarg_T *eap UNUSED) +{ + 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) + vim_free(name); +} + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * ":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( + char_u *(*fgetline)(int, void *, int), + void *cookie) +{ + return (getline_equal(fgetline, cookie, getsourceline) + && ((struct source_cookie *)getline_cookie( + fgetline, cookie))->finished); +} +#endif + +/* + * ":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_u * +get_locale_val(int what) +{ + char_u *loc; + + /* Obtain the locale value from the libraries. */ + loc = (char_u *)setlocale(what, NULL); + +# ifdef WIN32 + if (loc != NULL) + { + char_u *p; + + /* setocale() returns something like "LC_COLLATE=;LC_..." when + * one of the values (e.g., LC_CTYPE) differs. */ + p = vim_strchr(loc, '='); + if (p != NULL) + { + loc = ++p; + while (*p != NUL) /* remove trailing newline */ + { + if (*p < ' ' || *p == ';') + { + *p = NUL; + break; + } + ++p; + } + } + } +# endif + + return loc; +} +#endif + + +#ifdef WIN32 +/* + * On MS-Windows locale names are strings like "German_Germany.1252", but + * gettext expects "de". Try to translate one into another here for a few + * supported languages. + */ + static char_u * +gettext_lang(char_u *name) +{ + int i; + static char *(mtable[]) = { + "afrikaans", "af", + "czech", "cs", + "dutch", "nl", + "german", "de", + "english_united kingdom", "en_GB", + "spanish", "es", + "french", "fr", + "italian", "it", + "japanese", "ja", + "korean", "ko", + "norwegian", "no", + "polish", "pl", + "russian", "ru", + "slovak", "sk", + "swedish", "sv", + "ukrainian", "uk", + "chinese_china", "zh_CN", + "chinese_taiwan", "zh_TW", + NULL}; + + for (i = 0; mtable[i] != NULL; i += 2) + if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0) + return (char_u *)mtable[i + 1]; + return name; +} +#endif + +#if defined(FEAT_MULTI_LANG) || defined(PROTO) +/* + * Return TRUE when "lang" starts with a valid language name. + * Rejects NULL, empty string, "C", "C.UTF-8" and others. + */ + static int +is_valid_mess_lang(char_u *lang) +{ + return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]); +} + +/* + * 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 = 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 = get_locale_val(LC_COLLATE); +# endif +# else + p = mch_getenv((char_u *)"LC_ALL"); + if (!is_valid_mess_lang(p)) + { + p = mch_getenv((char_u *)"LC_MESSAGES"); + if (!is_valid_mess_lang(p)) + p = mch_getenv((char_u *)"LANG"); + } +# endif +# ifdef WIN32 + p = gettext_lang(p); +# endif + return is_valid_mess_lang(p) ? p : NULL; +} +#endif + +/* Complicated #if; matches with where get_mess_env() is used below. */ +#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ + && defined(LC_MESSAGES))) \ + || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ + && !defined(LC_MESSAGES)) +/* + * Get the language used for messages from the environment. + */ + static char_u * +get_mess_env(void) +{ + char_u *p; + + p = mch_getenv((char_u *)"LC_ALL"); + if (p == NULL || *p == NUL) + { + p = mch_getenv((char_u *)"LC_MESSAGES"); + if (p == NULL || *p == NUL) + { + p = mch_getenv((char_u *)"LANG"); + if (p != NULL && VIM_ISDIGIT(*p)) + p = NULL; /* ignore something like "1043" */ +# ifdef HAVE_GET_LOCALE_VAL + if (p == NULL || *p == NUL) + p = get_locale_val(LC_CTYPE); +# endif + } + } + return p; +} +#endif + +#if defined(FEAT_EVAL) || defined(PROTO) + +/* + * 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 = 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. */ +# if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES) + loc = get_locale_val(LC_MESSAGES); +# else + loc = get_mess_env(); +# endif + set_vim_var_string(VV_LANG, loc, -1); + +# ifdef HAVE_GET_LOCALE_VAL + loc = get_locale_val(LC_TIME); +# endif + set_vim_var_string(VV_LC_TIME, loc, -1); +} +#endif + +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) \ +/* + * ":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) + { +#ifndef LC_MESSAGES + 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(_("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) + semsg(_("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 *)""); +# ifdef WIN32 + /* Apparently MS-Windows printf() may cause a crash when + * we give it 8-bit text while it's expecting text in the + * current locale. This call avoids that. */ + setlocale(LC_CTYPE, "C"); +# endif + } + if (what != LC_CTYPE) + { + char_u *mname; +#ifdef WIN32 + mname = gettext_lang(name); +#else + mname = name; +#endif + vim_setenv((char_u *)"LC_MESSAGES", mname); +#ifdef FEAT_MULTI_LANG + set_helplang_default(mname); +#endif + } + } + +# ifdef FEAT_EVAL + /* Set v:lang, v:lc_time and v:ctype to the final result. */ + set_lang_var(); +# endif +# ifdef FEAT_TITLE + maketitle(); +# endif + } + } +} + +# if defined(FEAT_CMDL_COMPL) || defined(PROTO) + +static char_u **locales = NULL; /* Array of all available locales */ + +# ifndef WIN32 +static int did_init_locales = FALSE; + +/* 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, SHELL_SILENT, NULL); + if (locale_a == NULL) + return NULL; + ga_init2(&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) + { + if (ga_grow(&locales_ga, 1) == FAIL) + break; + 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"); + } + vim_free(locale_a); + if (ga_grow(&locales_ga, 1) == FAIL) + { + ga_clear(&locales_ga); + return NULL; + } + ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL; + return (char_u **)locales_ga.ga_data; +} +# endif + +/* + * Lazy initialization of all available locales. + */ + static void +init_locales(void) +{ +# ifndef WIN32 + if (!did_init_locales) + { + did_init_locales = TRUE; + locales = find_locales(); + } +# endif +} + +# if defined(EXITFREE) || defined(PROTO) + void +free_locales(void) +{ + int i; + if (locales != NULL) + { + for (i = 0; locales[i] != NULL; i++) + vim_free(locales[i]); + VIM_CLEAR(locales); + } +} +# endif + +/* + * Function given to ExpandGeneric() to obtain the possible arguments of the + * ":language" command. + */ + char_u * +get_lang_arg(expand_T *xp UNUSED, 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 UNUSED, int idx) +{ + init_locales(); + if (locales == NULL) + return NULL; + return locales[idx]; +} +# endif + +#endif diff --git a/src/ex_docmd.c b/src/ex_docmd.c new file mode 100644 index 0000000..b90ea7b --- /dev/null +++ b/src/ex_docmd.c @@ -0,0 +1,12587 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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 "vim.h" + +static int quitmore = 0; +static int ex_pressedreturn = FALSE; +#ifndef FEAT_PRINTER +# define ex_hardcopy ex_ni +#endif + +#ifdef FEAT_USR_CMDS +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 */ + int uc_addr_type; /* The command's address type */ +# ifdef FEAT_EVAL + sctx_T uc_script_ctx; /* SCTX where the command was defined */ +# ifdef FEAT_CMDL_COMPL + char_u *uc_compl_arg; /* completion argument if any */ +# endif +# endif +} 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); +# ifdef FEAT_CMDL_COMPL +static char_u *get_user_command_name(int idx); +# endif + +/* Wether a command index indicates a user command. */ +# define IS_USER_CMDIDX(idx) ((int)(idx) < 0) + +#else +# define ex_command ex_ni +# define ex_comclear ex_ni +# define ex_delcommand ex_ni +/* Wether a command index indicates a user command. */ +# define IS_USER_CMDIDX(idx) (FALSE) +#endif + +#ifdef FEAT_EVAL +static char_u *do_one_cmd(char_u **, int, struct condstack *, char_u *(*fgetline)(int, void *, int), void *cookie); +#else +static char_u *do_one_cmd(char_u **, int, char_u *(*fgetline)(int, void *, int), void *cookie); +static int if_level = 0; /* depth in :if */ +#endif +static void free_cmdmod(void); +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); +#ifndef FEAT_MENU +# define ex_emenu ex_ni +# define ex_menu ex_ni +# define ex_menutranslate ex_ni +#endif +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); +#ifndef FEAT_QUICKFIX +# define ex_make ex_ni +# define ex_cbuffer ex_ni +# define ex_cc ex_ni +# define ex_cnext ex_ni +# define ex_cfile ex_ni +# define qf_list ex_ni +# define qf_age ex_ni +# define qf_history ex_ni +# define ex_helpgrep ex_ni +# define ex_vimgrep ex_ni +#endif +#if !defined(FEAT_QUICKFIX) +# define ex_cclose ex_ni +# define ex_copen ex_ni +# define ex_cwindow ex_ni +# define ex_cbottom ex_ni +#endif +#if !defined(FEAT_QUICKFIX) || !defined(FEAT_EVAL) +# define ex_cexpr ex_ni +#endif + +static linenr_T get_address(exarg_T *, char_u **, int addr_type, int skip, int silent, int to_other_file, int address_count); +static void get_flags(exarg_T *eap); +#if !defined(FEAT_PERL) \ + || !defined(FEAT_PYTHON) || !defined(FEAT_PYTHON3) \ + || !defined(FEAT_TCL) \ + || !defined(FEAT_RUBY) \ + || !defined(FEAT_LUA) \ + || !defined(FEAT_MZSCHEME) +# define HAVE_EX_SCRIPT_NI +static void ex_script_ni(exarg_T *eap); +#endif +static char *invalid_range(exarg_T *eap); +static void correct_range(exarg_T *eap); +#ifdef FEAT_QUICKFIX +static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep); +#endif +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); +#if defined(FEAT_QUICKFIX) +static void ex_pclose(exarg_T *eap); +static void ex_ptag(exarg_T *eap); +static void ex_pedit(exarg_T *eap); +#else +# define ex_pclose ex_ni +# define ex_ptag ex_ni +# define ex_pedit ex_ni +#endif +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); +#ifdef FEAT_BYTEOFF +static void ex_goto(exarg_T *eap); +#else +# define ex_goto ex_ni +#endif +static void ex_shell(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); +#ifndef FEAT_GUI +# define ex_gui ex_nogui +static void ex_nogui(exarg_T *eap); +#endif +#if defined(FEAT_GUI_W32) && defined(FEAT_MENU) && defined(FEAT_TEAROFF) +static void ex_tearoff(exarg_T *eap); +#else +# define ex_tearoff ex_ni +#endif +#if (defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \ + || defined(FEAT_TERM_POPUP_MENU)) && defined(FEAT_MENU) +static void ex_popup(exarg_T *eap); +#else +# define ex_popup ex_ni +#endif +#ifndef FEAT_GUI_MSWIN +# define ex_simalt ex_ni +#endif +#if !defined(FEAT_GUI_MSWIN) && !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_MOTIF) +# define gui_mch_find_dialog ex_ni +# define gui_mch_replace_dialog ex_ni +#endif +#if !defined(FEAT_GUI_GTK) +# define ex_helpfind ex_ni +#endif +#ifndef FEAT_CSCOPE +# define ex_cscope ex_ni +# define ex_scscope ex_ni +# define ex_cstag ex_ni +#endif +#ifndef FEAT_SYN_HL +# define ex_syntax ex_ni +# define ex_ownsyntax ex_ni +#endif +#ifndef FEAT_EVAL +# define ex_packadd ex_ni +# define ex_packloadall ex_ni +#endif +#if !defined(FEAT_SYN_HL) || !defined(FEAT_PROFILE) +# define ex_syntime ex_ni +#endif +#ifndef FEAT_SPELL +# define ex_spell ex_ni +# define ex_mkspell ex_ni +# define ex_spelldump ex_ni +# define ex_spellinfo ex_ni +# define ex_spellrepall ex_ni +#endif +#ifndef FEAT_PERSISTENT_UNDO +# define ex_rundo ex_ni +# define ex_wundo ex_ni +#endif +#ifndef FEAT_LUA +# define ex_lua ex_script_ni +# define ex_luado ex_ni +# define ex_luafile ex_ni +#endif +#ifndef FEAT_MZSCHEME +# define ex_mzscheme ex_script_ni +# define ex_mzfile ex_ni +#endif +#ifndef FEAT_PERL +# define ex_perl ex_script_ni +# define ex_perldo ex_ni +#endif +#ifndef FEAT_PYTHON +# define ex_python ex_script_ni +# define ex_pydo ex_ni +# define ex_pyfile ex_ni +#endif +#ifndef FEAT_PYTHON3 +# define ex_py3 ex_script_ni +# define ex_py3do ex_ni +# define ex_py3file ex_ni +#endif +#if !defined(FEAT_PYTHON) && !defined(FEAT_PYTHON3) +# define ex_pyx ex_script_ni +# define ex_pyxdo ex_ni +# define ex_pyxfile ex_ni +#endif +#ifndef FEAT_TCL +# define ex_tcl ex_script_ni +# define ex_tcldo ex_ni +# define ex_tclfile ex_ni +#endif +#ifndef FEAT_RUBY +# define ex_ruby ex_script_ni +# define ex_rubydo ex_ni +# define ex_rubyfile ex_ni +#endif +#ifndef FEAT_KEYMAP +# define ex_loadkeymap ex_ni +#endif +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(VMS) || 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); +#ifdef FEAT_PERSISTENT_UNDO +static void ex_wundo(exarg_T *eap); +static void ex_rundo(exarg_T *eap); +#endif +static void ex_redo(exarg_T *eap); +static void ex_later(exarg_T *eap); +static void ex_redir(exarg_T *eap); +static void ex_redrawstatus(exarg_T *eap); +static void ex_redrawtabline(exarg_T *eap); +static void close_redir(void); +static void ex_mkrc(exarg_T *eap); +static void ex_mark(exarg_T *eap); +#ifdef FEAT_USR_CMDS +static char *uc_fun_cmd(void); +static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *compl); +#endif +static void ex_startinsert(exarg_T *eap); +static void ex_stopinsert(exarg_T *eap); +#ifdef FEAT_FIND_ID +static void ex_checkpath(exarg_T *eap); +static void ex_findpat(exarg_T *eap); +#else +# define ex_findpat ex_ni +# define ex_checkpath ex_ni +#endif +#if defined(FEAT_FIND_ID) && defined(FEAT_QUICKFIX) +static void ex_psearch(exarg_T *eap); +#else +# define ex_psearch ex_ni +#endif +static void ex_tag(exarg_T *eap); +static void ex_tag_cmd(exarg_T *eap, char_u *name); +#ifndef FEAT_EVAL +# define ex_scriptnames ex_ni +# define ex_finish ex_ni +# define ex_echo ex_ni +# define ex_echohl ex_ni +# define ex_execute ex_ni +# define ex_call ex_ni +# define ex_if ex_ni +# define ex_endif ex_ni +# define ex_else ex_ni +# define ex_while ex_ni +# define ex_continue ex_ni +# define ex_break ex_ni +# define ex_endwhile ex_ni +# define ex_throw ex_ni +# define ex_try ex_ni +# define ex_catch ex_ni +# define ex_finally ex_ni +# define ex_endtry ex_ni +# define ex_endfunction ex_ni +# define ex_let ex_ni +# define ex_unlet ex_ni +# define ex_lockvar ex_ni +# define ex_unlockvar ex_ni +# define ex_function ex_ni +# define ex_delfunction ex_ni +# define ex_return ex_ni +# define ex_oldfiles ex_ni +#endif +static char_u *arg_all(void); +#ifdef FEAT_SESSION +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 */ +#else +# define ex_loadview ex_ni +#endif +#ifndef FEAT_EVAL +# define ex_compiler ex_ni +#endif +#ifdef FEAT_VIMINFO +static void ex_viminfo(exarg_T *eap); +#else +# define ex_viminfo ex_ni +#endif +static void ex_behave(exarg_T *eap); +static void ex_filetype(exarg_T *eap); +static void ex_setfiletype(exarg_T *eap); +#ifndef FEAT_DIFF +# define ex_diffoff ex_ni +# define ex_diffpatch ex_ni +# define ex_diffgetput ex_ni +# define ex_diffsplit ex_ni +# define ex_diffthis ex_ni +# define ex_diffupdate ex_ni +#endif +static void ex_digraphs(exarg_T *eap); +static void ex_set(exarg_T *eap); +#if !defined(FEAT_EVAL) +# define ex_options ex_ni +#endif +#ifdef FEAT_SEARCH_EXTRA +static void ex_nohlsearch(exarg_T *eap); +static void ex_match(exarg_T *eap); +#else +# define ex_nohlsearch ex_ni +# define ex_match ex_ni +#endif +#ifdef FEAT_CRYPT +static void ex_X(exarg_T *eap); +#else +# define ex_X ex_ni +#endif +#ifdef FEAT_FOLDING +static void ex_fold(exarg_T *eap); +static void ex_foldopen(exarg_T *eap); +static void ex_folddo(exarg_T *eap); +#else +# define ex_fold ex_ni +# define ex_foldopen ex_ni +# define ex_folddo ex_ni +#endif +#if !(defined(HAVE_LOCALE_H) || defined(X_LOCALE)) +# define ex_language ex_ni +#endif +#ifndef FEAT_SIGNS +# define ex_sign ex_ni +#endif +#ifndef FEAT_NETBEANS_INTG +# define ex_nbclose ex_ni +# define ex_nbkey ex_ni +# define ex_nbstart ex_ni +#endif + +#ifndef FEAT_EVAL +# define ex_debug ex_ni +# define ex_breakadd ex_ni +# define ex_debuggreedy ex_ni +# define ex_breakdel ex_ni +# define ex_breaklist ex_ni +#endif + +#ifndef FEAT_CMDHIST +# define ex_history ex_ni +#endif +#ifndef FEAT_JUMPLIST +# define ex_jumps ex_ni +# define ex_clearjumps ex_ni +# define ex_changes ex_ni +#endif + +#ifndef FEAT_PROFILE +# define ex_profile ex_ni +#endif +#ifndef FEAT_TERMINAL +# define ex_terminal ex_ni +#endif + +/* + * Declare cmdnames[]. + */ +#define DO_DECLARE_EXCMD +#include "ex_cmds.h" +#include "ex_cmdidxs.h" + +static char_u dollar_command[2] = {'$', 0}; + + +#ifdef FEAT_EVAL +/* 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 int 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) +{ + 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; +} +#endif + +/* + * 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; + varnumber_T 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 */ +#ifdef FEAT_GUI + /* Ignore scrollbar and mouse events in Ex mode */ + ++hold_gui_events; +#endif + + 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 = CHANGEDTICK(curbuf); + 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 != CHANGEDTICK(curbuf)) && !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")); + } + } + +#ifdef FEAT_GUI + --hold_gui_events; +#endif + --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( + 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; +#ifdef FEAT_EVAL + 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; +#else +# define cmd_getline fgetline +# define cmd_cookie cookie +#endif + static int call_depth = 0; /* recursiveness */ + +#ifdef FEAT_EVAL + /* 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; +#endif + + /* It's possible to create an endless loop with ":execute", catch that + * here. The value of 200 allows nested function calls, ":source", etc. + * Allow 200 or 'maxfuncdepth', whatever is larger. */ + if (call_depth >= 200 +#ifdef FEAT_EVAL + && call_depth >= p_mfd +#endif + ) + { + emsg(_("E169: Command too recursive")); +#ifdef FEAT_EVAL + /* 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; +#endif + return FAIL; + } + ++call_depth; + +#ifdef FEAT_EVAL + cstack.cs_idx = -1; + cstack.cs_looplevel = 0; + cstack.cs_trylevel = 0; + cstack.cs_emsg_silent_list = NULL; + cstack.cs_lflags = 0; + ga_init2(&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 + vim_memset(&debug_saved, 0, sizeof(debug_saved)); + + initial_trylevel = trylevel; + + /* + * "did_throw" will be set to TRUE when an exception is being thrown. + */ + did_throw = FALSE; +#endif + /* + * "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 + { +#ifdef FEAT_EVAL + getline_is_func = getline_equal(fgetline, cookie, get_func_line); +#endif + + /* stop skipping cmds for an error msg after all endif/while/for */ + if (next_cmdline == NULL +#ifdef FEAT_EVAL + && !force_abort + && cstack.cs_idx < 0 + && !(getline_is_func && func_has_abort(real_cookie)) +#endif + ) + 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. + */ + +#ifdef FEAT_EVAL + /* 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. */ + VIM_CLEAR(cmdline_copy); + + /* Check if a function has returned or, unless it has an unclosed + * try conditional, aborted. */ + if (getline_is_func) + { +# ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + func_line_end(real_cookie); +# endif + if (func_has_ended(real_cookie)) + { + retval = FAIL; + break; + } + } +#ifdef FEAT_PROFILE + else if (do_profiling == PROF_YES + && getline_equal(fgetline, cookie, getsourceline)) + script_line_end(); +#endif + + /* 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; + } +# ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + { + if (getline_is_func) + func_line_start(real_cookie); + else if (getline_equal(fgetline, cookie, getsourceline)) + script_line_start(); + } +# endif + } + + 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; + } +#endif + + /* 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, +#ifdef FEAT_EVAL + cstack.cs_idx < 0 ? 0 : (cstack.cs_idx + 1) * 2 +#else + 0 +#endif + )) == 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) + { + vim_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; + +#ifdef FEAT_EVAL + /* + * 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))) + { + if (store_loop_line(&lines_ga, next_cmdline) == FAIL) + { + retval = FAIL; + break; + } + } + did_endif = FALSE; +#endif + + 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(_("line %ld: %s"), + (long)sourcing_lnum, cmdline_copy); + if (msg_silent == 0) + msg_puts("\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, +#ifdef FEAT_EVAL + &cstack, +#endif + cmd_getline, cmd_cookie); + --recursive; + +#ifdef FEAT_EVAL + 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; +#endif + + if (next_cmdline == NULL) + { + VIM_CLEAR(cmdline_copy); +#ifdef FEAT_CMDHIST + /* + * 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) + { + vim_free(last_cmdline); + last_cmdline = new_last_cmdline; + new_last_cmdline = NULL; + } +#endif + } + 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; + } + + +#ifdef FEAT_EVAL + /* 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; + } + } + + /* Check for the next breakpoint after a watchexpression */ + if (breakpoint != NULL && has_watchexpr()) + { + *breakpoint = dbg_find_breakpoint(FALSE, fname, sourcing_lnum); + *dbg_tick = debug_tick; + } + + /* + * 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); +#endif /* FEAT_EVAL */ + + } + /* + * 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 +#ifdef FEAT_EVAL + || (did_emsg && force_abort) || did_throw +#endif + ) +#ifdef FEAT_EVAL + && cstack.cs_trylevel == 0 +#endif + ) + && !(did_emsg +#ifdef FEAT_EVAL + /* 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) +#endif + && used_getline + && (getline_equal(fgetline, cookie, getexmodeline) + || getline_equal(fgetline, cookie, getexline))) + && (next_cmdline != NULL +#ifdef FEAT_EVAL + || cstack.cs_idx >= 0 +#endif + || (flags & DOCMD_REPEAT))); + + vim_free(cmdline_copy); + did_emsg_syntax = FALSE; +#ifdef FEAT_EVAL + 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; + } + + 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); + vim_free(messages->msg); + vim_free(messages); + messages = next; + } + while (messages != NULL); + } + else if (p != NULL) + { + emsg(p); + vim_free(p); + } + vim_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; +#endif /* FEAT_EVAL */ + + /* + * 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 +#ifdef FEAT_EVAL + || (did_endif && KeyTyped && !did_emsg) +#endif + ) + { + 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); + } + } + +#ifdef FEAT_EVAL + did_endif = FALSE; /* in case do_cmdline used recursively */ +#else + /* + * Reset if_level, in case a sourced script file contains more ":if" than + * ":endif" (could be ":if x | foo | endif"). + */ + if_level = 0; +#endif + + --call_depth; + return retval; +} + +#ifdef FEAT_EVAL +/* + * 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) == OK) + ++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 int +store_loop_line(garray_T *gap, char_u *line) +{ + if (ga_grow(gap, 1) == FAIL) + return FAIL; + ((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; + return OK; +} + +/* + * Free the lines stored for a ":while" or ":for" loop. + */ + static void +free_cmdlines(garray_T *gap) +{ + while (gap->ga_len > 0) + { + vim_free(((wcmd_T *)(gap->ga_data))[gap->ga_len - 1].line); + --gap->ga_len; + } +} +#endif + +/* + * 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( + char_u *(*fgetline)(int, void *, int), + void *cookie UNUSED, /* argument for fgetline() */ + char_u *(*func)(int, void *, int)) +{ +#ifdef FEAT_EVAL + 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; +#else + return fgetline == func; +#endif +} + +/* + * If "fgetline" is get_loop_line(), return the cookie used by the original + * getline function. Otherwise return "cookie". + */ + void * +getline_cookie( + char_u *(*fgetline)(int, void *, int) UNUSED, + void *cookie) /* argument for fgetline() */ +{ +#ifdef FEAT_EVAL + 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; +#else + return cookie; +#endif +} + + +/* + * Helper function to apply an offset for buffer commands, i.e. ":bdelete", + * ":bwipeout", etc. + * Returns the buffer number. + */ + static int +compute_buffer_local_count(int addr_type, int lnum, int offset) +{ + buf_T *buf; + buf_T *nextbuf; + int count = offset; + + buf = firstbuf; + while (buf->b_next != NULL && buf->b_fnum < lnum) + buf = buf->b_next; + while (count != 0) + { + count += (offset < 0) ? 1 : -1; + nextbuf = (offset < 0) ? buf->b_prev : buf->b_next; + if (nextbuf == NULL) + break; + buf = nextbuf; + if (addr_type == ADDR_LOADED_BUFFERS) + /* skip over unloaded buffers */ + while (buf->b_ml.ml_mfp == NULL) + { + nextbuf = (offset < 0) ? buf->b_prev : buf->b_next; + if (nextbuf == NULL) + break; + buf = nextbuf; + } + } + /* we might have gone too far, last buffer is not loadedd */ + if (addr_type == ADDR_LOADED_BUFFERS) + while (buf->b_ml.ml_mfp == NULL) + { + nextbuf = (offset >= 0) ? buf->b_prev : buf->b_next; + if (nextbuf == NULL) + break; + buf = nextbuf; + } + return buf->b_fnum; +} + + static int +current_win_nr(win_T *win) +{ + win_T *wp; + int nr = 0; + + FOR_ALL_WINDOWS(wp) + { + ++nr; + if (wp == win) + break; + } + return nr; +} + + static int +current_tab_nr(tabpage_T *tab) +{ + tabpage_T *tp; + int nr = 0; + + FOR_ALL_TABPAGES(tp) + { + ++nr; + if (tp == tab) + break; + } + return nr; +} + +# define CURRENT_WIN_NR current_win_nr(curwin) +# define LAST_WIN_NR current_win_nr(NULL) +# define CURRENT_TAB_NR current_tab_nr(curtab) +# define LAST_TAB_NR current_tab_nr(NULL) + +/* + * 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. find the command + * 4. parse range + * 5. Parse the command. + * 6. parse arguments + * 7. switch on command name + * + * Note: "fgetline" can be NULL. + * + * This function may be called recursively! + */ +#if (_MSC_VER == 1200) +/* + * Avoid optimisation bug in VC++ version 6.0 + */ + #pragma optimize( "g", off ) +#endif + static char_u * +do_one_cmd( + char_u **cmdlinep, + int sourcing, +#ifdef FEAT_EVAL + struct condstack *cstack, +#endif + char_u *(*fgetline)(int, void *, int), + void *cookie) /* argument for fgetline() */ +{ + char_u *p; + linenr_T lnum; + long n; + char *errormsg = NULL; /* error message */ + char_u *after_modifier = NULL; + exarg_T ea; /* Ex command arguments */ + int save_msg_scroll = msg_scroll; + cmdmod_T save_cmdmod; + int ni; /* set when Not Implemented */ + char_u *cmd; + + vim_memset(&ea, 0, sizeof(ea)); + ea.line1 = 1; + ea.line2 = 1; +#ifdef FEAT_EVAL + ++ex_nesting_level; +#endif + + /* When the last file has not been edited :q has to be typed twice. */ + if (quitmore +#ifdef FEAT_EVAL + /* avoid that a function call in 'statusline' does this */ + && !getline_equal(fgetline, cookie, get_func_line) +#endif + /* 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; + + /* "#!anything" is handled like a comment. */ + if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!') + goto doend; + +/* + * 1. Skip comment lines and leading white space and colons. + * 2. Handle command modifiers. + */ + // The "ea" structure holds the arguments that can be used. + ea.cmd = *cmdlinep; + ea.cmdlinep = cmdlinep; + ea.getline = fgetline; + ea.cookie = cookie; +#ifdef FEAT_EVAL + ea.cstack = cstack; +#endif + if (parse_command_modifiers(&ea, &errormsg, FALSE) == FAIL) + goto doend; + + after_modifier = ea.cmd; + +#ifdef FEAT_EVAL + ea.skip = did_emsg || got_int || did_throw || (cstack->cs_idx >= 0 + && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)); +#else + ea.skip = (if_level > 0); +#endif + +/* + * 3. Skip over the range to find the command. Let "p" point to after it. + * + * We need the command to know what kind of range it uses. + */ + cmd = ea.cmd; + ea.cmd = skip_range(ea.cmd, NULL); + if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL) + ea.cmd = skipwhite(ea.cmd + 1); + p = find_command(&ea, NULL); + +#ifdef FEAT_EVAL +# ifdef FEAT_PROFILE + // Count this line for profiling if skip is TRUE. + if (do_profiling == PROF_YES + && (!ea.skip || cstack->cs_idx == 0 || (cstack->cs_idx > 0 + && (cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))) + { + int skip = did_emsg || got_int || did_throw; + + if (ea.cmdidx == CMD_catch) + skip = !skip && !(cstack->cs_idx >= 0 + && (cstack->cs_flags[cstack->cs_idx] & CSF_THROWN) + && !(cstack->cs_flags[cstack->cs_idx] & CSF_CAUGHT)); + else if (ea.cmdidx == CMD_else || ea.cmdidx == CMD_elseif) + skip = skip || !(cstack->cs_idx >= 0 + && !(cstack->cs_flags[cstack->cs_idx] + & (CSF_ACTIVE | CSF_TRUE))); + else if (ea.cmdidx == CMD_finally) + skip = FALSE; + else if (ea.cmdidx != CMD_endif + && ea.cmdidx != CMD_endfor + && ea.cmdidx != CMD_endtry + && ea.cmdidx != CMD_endwhile) + skip = ea.skip; + + if (!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(); + } + } +# endif + + /* 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); + } +#endif + +/* + * 4. 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. + */ + + /* ea.addr_type for user commands is set by find_ucmd */ + if (!IS_USER_CMDIDX(ea.cmdidx)) + { + if (ea.cmdidx != CMD_SIZE) + ea.addr_type = cmdnames[(int)ea.cmdidx].cmd_addr_type; + else + ea.addr_type = ADDR_LINES; + + /* :wincmd range depends on the argument. */ + if (ea.cmdidx == CMD_wincmd && p != NULL) + get_wincmd_addr_type(skipwhite(p), &ea); + } + + ea.cmd = cmd; + if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL) + goto doend; + +/* + * 5. Parse the 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 = _(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; + } + + /* If this looks like an undefined user command and there are CmdUndefined + * autocommands defined, trigger the matching autocommands. */ + if (p != NULL && ea.cmdidx == CMD_SIZE && !ea.skip + && ASCII_ISUPPER(*ea.cmd) + && has_cmdundefined()) + { + int ret; + + p = ea.cmd; + while (ASCII_ISALNUM(*p)) + ++p; + p = vim_strnsave(ea.cmd, (int)(p - ea.cmd)); + ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, TRUE, NULL); + vim_free(p); + /* If the autocommands did something and didn't cause an error, try + * finding the command again. */ + p = (ret +#ifdef FEAT_EVAL + && !aborting() +#endif + ) ? find_command(&ea, NULL) : ea.cmd; + } + +#ifdef FEAT_USR_CMDS + if (p == NULL) + { + if (!ea.skip) + errormsg = _("E464: Ambiguous use of user-defined command"); + goto doend; + } + /* Check for wrong commands. */ + if (*p == '!' && ea.cmd[1] == 0151 && ea.cmd[0] == 78 + && !IS_USER_CMDIDX(ea.cmdidx)) + { + errormsg = uc_fun_cmd(); + goto doend; + } +#endif + if (ea.cmdidx == CMD_SIZE) + { + if (!ea.skip) + { + STRCPY(IObuff, _("E492: Not an editor command")); + if (!sourcing) + { + /* If the modifier was parsed OK the error must be in the + * following command */ + if (after_modifier != NULL) + append_command(after_modifier); + else + append_command(*cmdlinep); + } + errormsg = (char *)IObuff; + did_emsg_syntax = TRUE; + } + goto doend; + } + + ni = (!IS_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 + )); + +#ifndef FEAT_EVAL + /* + * When the expression evaluation is disabled, recognize the ":if" and + * ":endif" commands and ignore everything in between it. + */ + if (ea.cmdidx == CMD_if) + ++if_level; + if (if_level) + { + if (ea.cmdidx == CMD_endif) + --if_level; + goto doend; + } + +#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; + +/* + * 6. Parse arguments. + */ + if (!IS_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 = _(e_sandbox); + goto doend; + } +#endif + if (!curbuf->b_p_ma && (ea.argt & MODIFY)) + { + /* Command not allowed in non-'modifiable' buffer */ + errormsg = _(e_modifiable); + goto doend; + } + + if (text_locked() && !(ea.argt & CMDWIN) + && !IS_USER_CMDIDX(ea.cmdidx)) + { + /* Command not allowed when editing the command line. */ + errormsg = _(get_text_locked_msg()); + goto doend; + } + + /* Disallow editing another buffer when "curbuf_lock" is set. + * Do allow ":checktime" (it is postponed). + * Do allow ":edit" (check for an argument later). + * Do allow ":file" with no arguments (check for an argument later). */ + if (!(ea.argt & CMDWIN) + && ea.cmdidx != CMD_checktime + && ea.cmdidx != CMD_edit + && ea.cmdidx != CMD_file + && !IS_USER_CMDIDX(ea.cmdidx) + && curbuf_locked()) + goto doend; + + if (!ni && !(ea.argt & RANGE) && ea.addr_count > 0) + { + /* no range allowed */ + errormsg = _(e_norange); + goto doend; + } + } + + if (!ni && !(ea.argt & BANG) && ea.forceit) /* no allowed */ + { + errormsg = _(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 = _("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); + +#ifdef FEAT_FOLDING + if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy + && ea.addr_type == ADDR_LINES) + { + /* 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); + } +#endif + +#ifdef FEAT_QUICKFIX + /* + * 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; +#endif + + /* + * 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); + + // ":file" cannot be run with an argument when "curbuf_lock" is set + if (ea.cmdidx == CMD_file && *ea.arg != NUL && curbuf_locked()) + goto doend; + + /* + * 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 = _(e_invarg); + goto doend; + } + + if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) + { + if (*ea.arg == '>') /* append */ + { + if (*++ea.arg != '>') /* typed wrong */ + { + errormsg = _("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_terminal + || 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) + { + buf_T *buf; + + ea.line1 = 1; + switch (ea.addr_type) + { + case ADDR_LINES: + ea.line2 = curbuf->b_ml.ml_line_count; + break; + case ADDR_LOADED_BUFFERS: + buf = firstbuf; + while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) + buf = buf->b_next; + ea.line1 = buf->b_fnum; + buf = lastbuf; + while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) + buf = buf->b_prev; + ea.line2 = buf->b_fnum; + break; + case ADDR_BUFFERS: + ea.line1 = firstbuf->b_fnum; + ea.line2 = lastbuf->b_fnum; + break; + case ADDR_WINDOWS: + ea.line2 = LAST_WIN_NR; + break; + case ADDR_TABS: + ea.line2 = LAST_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + ea.line2 = 1; + break; + case ADDR_ARGUMENTS: + if (ARGCOUNT == 0) + ea.line1 = ea.line2 = 0; + else + ea.line2 = ARGCOUNT; + break; +#ifdef FEAT_QUICKFIX + case ADDR_QUICKFIX: + ea.line2 = qf_get_size(&ea); + if (ea.line2 == 0) + ea.line2 = 1; + break; +#endif + } + } + + /* accept numbered register only when no count allowed (:put) */ + if ( (ea.argt & REGSTR) + && *ea.arg != NUL + /* Do not allow register = for user commands */ + && (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=') + && !((ea.argt & COUNT) && VIM_ISDIGIT(*ea.arg))) + { +#ifndef FEAT_CLIPBOARD + /* check these explicitly for a more specific error message */ + if (*ea.arg == '*' || *ea.arg == '+') + { + errormsg = _(e_invalidreg); + goto doend; + } +#endif + if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put + && !IS_USER_CMDIDX(ea.cmdidx)))) + { + ea.regname = *ea.arg++; +#ifdef FEAT_EVAL + /* 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); + } +#endif + 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 = _(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.addr_type == ADDR_LINES + && 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 = _(e_trailing); + goto doend; + } + + if (!ni && (ea.argt & NEEDARG) && *ea.arg == NUL) + { + errormsg = _(e_argreq); + goto doend; + } + +#ifdef FEAT_EVAL + /* + * 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_filter: + 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_lua: + case CMD_match: + case CMD_mzscheme: + case CMD_noautocmd: + case CMD_noswapfile: + case CMD_perl: + case CMD_psearch: + case CMD_python: + case CMD_py3: + case CMD_python3: + case CMD_return: + case CMD_rightbelow: + case CMD_ruby: + case CMD_silent: + case CMD_smagic: + case CMD_snomagic: + case CMD_substitute: + case CMD_syntax: + case CMD_tab: + case CMD_tcl: + 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; + } + } +#endif + + 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 + && !IS_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); + } + + /* The :try command saves the emsg_silent flag, reset it here when + * ":silent! try" was used, it should only apply to :try itself. */ + if (ea.cmdidx == CMD_try && ea.did_esilent > 0) + { + emsg_silent -= ea.did_esilent; + if (emsg_silent < 0) + emsg_silent = 0; + ea.did_esilent = 0; + } + +/* + * 7. Execute the command. + */ + +#ifdef FEAT_USR_CMDS + if (IS_USER_CMDIDX(ea.cmdidx)) + { + /* + * Execute a user-defined command. + */ + do_ucmd(&ea); + } + else +#endif + { + /* + * Call the function to execute the command. + */ + ea.errmsg = NULL; + (cmdnames[ea.cmdidx].cmd_func)(&ea); + if (ea.errmsg != NULL) + errormsg = _(ea.errmsg); + } + +#ifdef FEAT_EVAL + /* + * 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; +#endif + +doend: + if (curwin->w_cursor.lnum == 0) /* can happen with zero line number */ + { + curwin->w_cursor.lnum = 1; + curwin->w_cursor.col = 0; + } + + if (errormsg != NULL && *errormsg != NUL && !did_emsg) + { + if (sourcing) + { + if (errormsg != (char *)IObuff) + { + STRCPY(IObuff, errormsg); + errormsg = (char *)IObuff; + } + append_command(*cmdlinep); + } + emsg(errormsg); + } +#ifdef FEAT_EVAL + do_errthrow(cstack, + (ea.cmdidx != CMD_SIZE && !IS_USER_CMDIDX(ea.cmdidx)) + ? cmdnames[(int)ea.cmdidx].cmd_name : (char_u *)NULL); +#endif + + if (ea.verbose_save >= 0) + p_verbose = ea.verbose_save; + + free_cmdmod(); + cmdmod = save_cmdmod; + + if (ea.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 > ea.save_msg_silent) + msg_silent = ea.save_msg_silent; + emsg_silent -= ea.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 (ea.did_sandbox) + --sandbox; +#endif + + if (ea.nextcmd && *ea.nextcmd == NUL) /* not really a next command */ + ea.nextcmd = NULL; + +#ifdef FEAT_EVAL + --ex_nesting_level; +#endif + + return ea.nextcmd; +} +#if (_MSC_VER == 1200) + #pragma optimize( "", on ) +#endif + +/* + * Parse and skip over command modifiers: + * - update eap->cmd + * - store flags in "cmdmod". + * - Set ex_pressedreturn for an empty command line. + * - set msg_silent for ":silent" + * - set 'eventignore' to "all" for ":noautocmd" + * - set p_verbose for ":verbose" + * - Increment "sandbox" for ":sandbox" + * When "skip_only" is TRUE the global variables are not changed, except for + * "cmdmod". + * Return FAIL when the command is not to be executed. + * May set "errormsg" to an error message. + */ + int +parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only) +{ + char_u *p; + + vim_memset(&cmdmod, 0, sizeof(cmdmod)); + eap->verbose_save = -1; + eap->save_msg_silent = -1; + + // Repeat until no more command modifiers are found. + for (;;) + { + while (*eap->cmd == ' ' || *eap->cmd == '\t' || *eap->cmd == ':') + ++eap->cmd; + + /* in ex mode, an empty line works like :+ */ + if (*eap->cmd == NUL && exmode_active + && (getline_equal(eap->getline, eap->cookie, getexmodeline) + || getline_equal(eap->getline, eap->cookie, getexline)) + && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) + { + eap->cmd = (char_u *)"+"; + if (!skip_only) + ex_pressedreturn = TRUE; + } + + /* ignore comment and empty lines */ + if (*eap->cmd == '"') + return FAIL; + if (*eap->cmd == NUL) + { + if (!skip_only) + ex_pressedreturn = TRUE; + return FAIL; + } + + p = skip_range(eap->cmd, NULL); + switch (*p) + { + /* When adding an entry, also modify cmd_exists(). */ + case 'a': if (!checkforcmd(&eap->cmd, "aboveleft", 3)) + break; + cmdmod.split |= WSP_ABOVE; + continue; + + case 'b': if (checkforcmd(&eap->cmd, "belowright", 3)) + { + cmdmod.split |= WSP_BELOW; + continue; + } + if (checkforcmd(&eap->cmd, "browse", 3)) + { +#ifdef FEAT_BROWSE_CMD + cmdmod.browse = TRUE; +#endif + continue; + } + if (!checkforcmd(&eap->cmd, "botright", 2)) + break; + cmdmod.split |= WSP_BOT; + continue; + + case 'c': if (!checkforcmd(&eap->cmd, "confirm", 4)) + break; +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + cmdmod.confirm = TRUE; +#endif + continue; + + case 'k': if (checkforcmd(&eap->cmd, "keepmarks", 3)) + { + cmdmod.keepmarks = TRUE; + continue; + } + if (checkforcmd(&eap->cmd, "keepalt", 5)) + { + cmdmod.keepalt = TRUE; + continue; + } + if (checkforcmd(&eap->cmd, "keeppatterns", 5)) + { + cmdmod.keeppatterns = TRUE; + continue; + } + if (!checkforcmd(&eap->cmd, "keepjumps", 5)) + break; + cmdmod.keepjumps = TRUE; + continue; + + case 'f': /* only accept ":filter {pat} cmd" */ + { + char_u *reg_pat; + + if (!checkforcmd(&p, "filter", 4) + || *p == NUL || ends_excmd(*p)) + break; + if (*p == '!') + { + cmdmod.filter_force = TRUE; + p = skipwhite(p + 1); + if (*p == NUL || ends_excmd(*p)) + break; + } + if (skip_only) + p = skip_vimgrep_pat(p, NULL, NULL); + else + // NOTE: This puts a NUL after the pattern. + p = skip_vimgrep_pat(p, ®_pat, NULL); + if (p == NULL || *p == NUL) + break; + if (!skip_only) + { + cmdmod.filter_regmatch.regprog = + vim_regcomp(reg_pat, RE_MAGIC); + if (cmdmod.filter_regmatch.regprog == NULL) + break; + } + eap->cmd = p; + continue; + } + + /* ":hide" and ":hide | cmd" are not modifiers */ + case 'h': if (p != eap->cmd || !checkforcmd(&p, "hide", 3) + || *p == NUL || ends_excmd(*p)) + break; + eap->cmd = p; + cmdmod.hide = TRUE; + continue; + + case 'l': if (checkforcmd(&eap->cmd, "lockmarks", 3)) + { + cmdmod.lockmarks = TRUE; + continue; + } + + if (!checkforcmd(&eap->cmd, "leftabove", 5)) + break; + cmdmod.split |= WSP_ABOVE; + continue; + + case 'n': if (checkforcmd(&eap->cmd, "noautocmd", 3)) + { + if (cmdmod.save_ei == NULL && !skip_only) + { + /* 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(&eap->cmd, "noswapfile", 3)) + break; + cmdmod.noswapfile = TRUE; + continue; + + case 'r': if (!checkforcmd(&eap->cmd, "rightbelow", 6)) + break; + cmdmod.split |= WSP_BELOW; + continue; + + case 's': if (checkforcmd(&eap->cmd, "sandbox", 3)) + { +#ifdef HAVE_SANDBOX + if (!skip_only) + { + if (!eap->did_sandbox) + ++sandbox; + eap->did_sandbox = TRUE; + } +#endif + continue; + } + if (!checkforcmd(&eap->cmd, "silent", 3)) + break; + if (!skip_only) + { + if (eap->save_msg_silent == -1) + eap->save_msg_silent = msg_silent; + ++msg_silent; + } + if (*eap->cmd == '!' && !VIM_ISWHITE(eap->cmd[-1])) + { + /* ":silent!", but not "silent !cmd" */ + eap->cmd = skipwhite(eap->cmd + 1); + if (!skip_only) + { + ++emsg_silent; + ++eap->did_esilent; + } + } + continue; + + case 't': if (checkforcmd(&p, "tab", 3)) + { + if (!skip_only) + { + long tabnr = get_address(eap, &eap->cmd, + ADDR_TABS, eap->skip, + skip_only, FALSE, 1); + if (tabnr == MAXLNUM) + cmdmod.tab = tabpage_index(curtab) + 1; + else + { + if (tabnr < 0 || tabnr > LAST_TAB_NR) + { + *errormsg = _(e_invrange); + return FAIL; + } + cmdmod.tab = tabnr + 1; + } + } + eap->cmd = p; + continue; + } + if (!checkforcmd(&eap->cmd, "topleft", 2)) + break; + cmdmod.split |= WSP_TOP; + continue; + + case 'u': if (!checkforcmd(&eap->cmd, "unsilent", 3)) + break; + if (!skip_only) + { + if (eap->save_msg_silent == -1) + eap->save_msg_silent = msg_silent; + msg_silent = 0; + } + continue; + + case 'v': if (checkforcmd(&eap->cmd, "vertical", 4)) + { + cmdmod.split |= WSP_VERT; + continue; + } + if (!checkforcmd(&p, "verbose", 4)) + break; + if (!skip_only) + { + if (eap->verbose_save < 0) + eap->verbose_save = p_verbose; + if (vim_isdigit(*eap->cmd)) + p_verbose = atoi((char *)eap->cmd); + else + p_verbose = 1; + } + eap->cmd = p; + continue; + } + break; + } + + return OK; +} + +/* + * Free contents of "cmdmod". + */ + static void +free_cmdmod(void) +{ + 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); + } + + if (cmdmod.filter_regmatch.regprog != NULL) + vim_regfree(cmdmod.filter_regmatch.regprog); +} + +/* + * Parse the address range, if any, in "eap". + * May set the last search pattern, unless "silent" is TRUE. + * Return FAIL and set "errormsg" or return OK. + */ + int +parse_cmd_address(exarg_T *eap, char **errormsg, int silent) +{ + int address_count = 1; + linenr_T lnum; + + // Repeat for all ',' or ';' separated addresses. + for (;;) + { + eap->line1 = eap->line2; + switch (eap->addr_type) + { + case ADDR_LINES: + // default is current line number + eap->line2 = curwin->w_cursor.lnum; + break; + case ADDR_WINDOWS: + eap->line2 = CURRENT_WIN_NR; + break; + case ADDR_ARGUMENTS: + eap->line2 = curwin->w_arg_idx + 1; + if (eap->line2 > ARGCOUNT) + eap->line2 = ARGCOUNT; + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + eap->line2 = curbuf->b_fnum; + break; + case ADDR_TABS: + eap->line2 = CURRENT_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + eap->line2 = 1; + break; +#ifdef FEAT_QUICKFIX + case ADDR_QUICKFIX: + eap->line2 = qf_get_cur_valid_idx(eap); + break; +#endif + } + eap->cmd = skipwhite(eap->cmd); + lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent, + eap->addr_count == 0, address_count++); + if (eap->cmd == NULL) // error detected + return FAIL; + if (lnum == MAXLNUM) + { + if (*eap->cmd == '%') // '%' - all lines + { + ++eap->cmd; + switch (eap->addr_type) + { + case ADDR_LINES: + eap->line1 = 1; + eap->line2 = curbuf->b_ml.ml_line_count; + break; + case ADDR_LOADED_BUFFERS: + { + buf_T *buf = firstbuf; + + while (buf->b_next != NULL + && buf->b_ml.ml_mfp == NULL) + buf = buf->b_next; + eap->line1 = buf->b_fnum; + buf = lastbuf; + while (buf->b_prev != NULL + && buf->b_ml.ml_mfp == NULL) + buf = buf->b_prev; + eap->line2 = buf->b_fnum; + break; + } + case ADDR_BUFFERS: + eap->line1 = firstbuf->b_fnum; + eap->line2 = lastbuf->b_fnum; + break; + case ADDR_WINDOWS: + case ADDR_TABS: + if (IS_USER_CMDIDX(eap->cmdidx)) + { + eap->line1 = 1; + eap->line2 = eap->addr_type == ADDR_WINDOWS + ? LAST_WIN_NR : LAST_TAB_NR; + } + else + { + // there is no Vim command which uses '%' and + // ADDR_WINDOWS or ADDR_TABS + *errormsg = _(e_invrange); + return FAIL; + } + break; + case ADDR_TABS_RELATIVE: + case ADDR_OTHER: + *errormsg = _(e_invrange); + return FAIL; + case ADDR_ARGUMENTS: + if (ARGCOUNT == 0) + eap->line1 = eap->line2 = 0; + else + { + eap->line1 = 1; + eap->line2 = ARGCOUNT; + } + break; +#ifdef FEAT_QUICKFIX + case ADDR_QUICKFIX: + eap->line1 = 1; + eap->line2 = qf_get_size(eap); + if (eap->line2 == 0) + eap->line2 = 1; + break; +#endif + } + ++eap->addr_count; + } + else if (*eap->cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL) + { + pos_T *fp; + + // '*' - visual area + if (eap->addr_type != ADDR_LINES) + { + *errormsg = _(e_invrange); + return FAIL; + } + + ++eap->cmd; + if (!eap->skip) + { + fp = getmark('<', FALSE); + if (check_mark(fp) == FAIL) + return FAIL; + eap->line1 = fp->lnum; + fp = getmark('>', FALSE); + if (check_mark(fp) == FAIL) + return FAIL; + eap->line2 = fp->lnum; + ++eap->addr_count; + } + } + } + else + eap->line2 = lnum; + eap->addr_count++; + + if (*eap->cmd == ';') + { + if (!eap->skip) + { + curwin->w_cursor.lnum = eap->line2; + // don't leave the cursor on an illegal line or column + check_cursor(); + } + } + else if (*eap->cmd != ',') + break; + ++eap->cmd; + } + + // One address given: set start and end lines. + if (eap->addr_count == 1) + { + eap->line1 = eap->line2; + // ... but only implicit: really no address given + if (lnum == MAXLNUM) + eap->addr_count = 0; + } + return OK; +} + +/* + * 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 UNUSED) +{ + 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 :scr[iptnames], + * :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] == NUL || (p[2] != 's' && p[2] != 'r' + && (p[3] == NUL || (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[0])) + { + int c1 = eap->cmd[0]; + int c2 = eap->cmd[1]; + + if (command_count != (int)CMD_SIZE) + { + iemsg(_("E943: Command table needs to be updated, run 'make cmdidxs'")); + getout(1); + } + + /* Use a precomputed index for fast look-up in cmdnames[] + * taking into account the first 2 letters of eap->cmd. */ + eap->cmdidx = cmdidxs1[CharOrdLow(c1)]; + if (ASCII_ISLOWER(c2)) + eap->cmdidx += cmdidxs2[CharOrdLow(c1)][CharOrdLow(c2)]; + } + else + eap->cmdidx = CMD_bang; + + 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) + { +#ifdef FEAT_EVAL + if (full != NULL + && cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) + *full = TRUE; +#endif + break; + } + +#ifdef FEAT_USR_CMDS + /* 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); + } +#endif + if (p == eap->cmd) + eap->cmdidx = CMD_SIZE; + } + + return p; +} + +#ifdef FEAT_USR_CMDS +/* + * 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; + eap->addr_type = uc->uc_addr_type; + +# ifdef FEAT_CMDL_COMPL + if (compl != NULL) + *compl = uc->uc_compl; +# ifdef FEAT_EVAL + if (xp != NULL) + { + xp->xp_arg = uc->uc_compl_arg; + xp->xp_script_ctx = uc->uc_script_ctx; + xp->xp_script_ctx.sc_lnum += sourcing_lnum; + } +# endif +# endif + /* 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; +} +#endif + +#if defined(FEAT_EVAL) || defined(PROTO) +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}, + {"filter", 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)); +} +#endif + +/* + * 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; +#if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL) + int compl = EXPAND_NOTHING; +#endif +#ifdef FEAT_CMDL_COMPL + int delim; +#endif + 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; + +/* + * 1. 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. Skip over the range to find the command. + */ + cmd = skip_range(cmd, &xp->xp_context); + 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; + /* a user command may contain digits */ + if (ASCII_ISUPPER(cmd[0])) + while (ASCII_ISALNUM(*p) || *p == '*') + ++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; + } + /* check for non-alpha command */ + if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) + ++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; + +#ifdef FEAT_USR_CMDS + if (cmd[0] >= 'A' && cmd[0] <= 'Z') + while (ASCII_ISALNUM(*p) || *p == '*') /* Allow * wild card */ + ++p; +#endif + } + + /* + * 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; + } +#ifdef FEAT_USR_CMDS + else if (cmd[0] >= 'A' && cmd[0] <= 'Z') + { + ea.cmd = cmd; + p = find_ucmd(&ea, p, NULL, xp, +# if defined(FEAT_CMDL_COMPL) + &compl +# else + NULL +# endif + ); + if (p == NULL) + ea.cmdidx = CMD_SIZE; /* ambiguous user command */ + } +#endif + } + 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; + } + +/* + * 6. parse arguments + */ + if (!IS_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 || ea.cmdidx == CMD_terminal) + { +#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 == '$' +#if defined(MSWIN) + || *xp->xp_pattern == '%' +#endif + ) + { + 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; +#if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL) + /* Avoid that the assignment uses EXPAND_FILES again. */ + if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST) + compl = EXPAND_ENV_VARS; +#endif + } + } +#if defined(FEAT_CMDL_COMPL) + /* 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; + } + } +#endif + } + +/* + * 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_cdo: + case CMD_cfdo: + 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_ldo: + case CMD_leftabove: + case CMD_lfdo: + 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_filter: + if (*arg != NUL) + arg = skip_vimgrep_pat(arg, NULL, NULL); + if (arg == NULL || *arg == NUL) + { + xp->xp_context = EXPAND_NOTHING; + return NULL; + } + return skipwhite(arg); + +#ifdef FEAT_CMDL_COMPL +# ifdef FEAT_SEARCH_EXTRA + 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); +# endif + +/* + * All completion for the +cmdline_compl feature goes here. + */ + +# ifdef FEAT_USR_CMDS + 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, -nargs and -addr 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; + } + else if (STRNICMP(arg, "addr", p - arg) == 0) + { + xp->xp_context = EXPAND_USER_ADDR_TYPE; + 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; +# endif + + 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; +#ifdef FEAT_SYN_HL + case CMD_syntax: + set_context_in_syntax_cmd(xp, arg); + break; +#endif +#ifdef FEAT_EVAL + 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: + case CMD_cexpr: + case CMD_caddexpr: + case CMD_cgetexpr: + case CMD_lexpr: + case CMD_laddexpr: + case CMD_lgetexpr: + 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; + + if (*xp->xp_pattern == '$') + { + xp->xp_context = EXPAND_ENV_VARS; + ++xp->xp_pattern; + } + + 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; +#endif + case CMD_highlight: + set_context_in_highlight_cmd(xp, arg); + break; +#ifdef FEAT_CSCOPE + case CMD_cscope: + case CMD_lcscope: + case CMD_scscope: + set_context_in_cscope_cmd(xp, arg, ea.cmdidx); + break; +#endif +#ifdef FEAT_SIGNS + case CMD_sign: + set_context_in_sign_cmd(xp, arg); + break; +#endif + 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; +#ifdef FEAT_USR_CMDS + case CMD_USER: + case CMD_USER_BUF: + if (compl != EXPAND_NOTHING) + { + /* XFILE: file names are handled above */ + if (!(ea.argt & XFILE)) + { +# ifdef FEAT_MENU + if (compl == EXPAND_MENUS) + return set_context_in_menu_cmd(xp, cmd, arg, forceit); +# endif + 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; +#endif + 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_tmap: case CMD_tnoremap: + 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_tunmap: + case CMD_xunmap: + return set_context_in_map_cmd(xp, cmd, arg, forceit, + FALSE, TRUE, ea.cmdidx); + case CMD_mapclear: + case CMD_nmapclear: + case CMD_vmapclear: + case CMD_omapclear: + case CMD_imapclear: + case CMD_cmapclear: + case CMD_lmapclear: + case CMD_smapclear: + case CMD_tmapclear: + case CMD_xmapclear: + xp->xp_context = EXPAND_MAPCLEAR; + xp->xp_pattern = arg; + break; + + 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); +#ifdef FEAT_MENU + 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_tlmenu: case CMD_tlnoremenu: case CMD_tlunmenu: + 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); +#endif + + 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; + + case CMD_packadd: + xp->xp_context = EXPAND_PACKADD; + xp->xp_pattern = arg; + break; + +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + 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 +#if defined(FEAT_PROFILE) + case CMD_profile: + set_context_in_profile_cmd(xp, arg); + break; +#endif + case CMD_behave: + xp->xp_context = EXPAND_BEHAVE; + xp->xp_pattern = arg; + break; + + case CMD_messages: + xp->xp_context = EXPAND_MESSAGES; + xp->xp_pattern = arg; + break; + +#if defined(FEAT_CMDHIST) + case CMD_history: + xp->xp_context = EXPAND_HISTORY; + xp->xp_pattern = arg; + break; +#endif +#if defined(FEAT_PROFILE) + case CMD_syntime: + xp->xp_context = EXPAND_SYNTIME; + xp->xp_pattern = arg; + break; +#endif + + case CMD_argdelete: + while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) + arg = xp->xp_pattern + 1; + xp->xp_context = EXPAND_ARGLIST; + xp->xp_pattern = arg; + break; + +#endif /* FEAT_CMDL_COMPL */ + + 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[1] == '?' || cmd[1] == '/' || cmd[1] == '&') + ++cmd; + else + break; + } + else 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. + * This may set the last used search pattern. + * + * Return MAXLNUM when no Ex address was found. + */ + static linenr_T +get_address( + exarg_T *eap UNUSED, + char_u **ptr, + int addr_type, // flag: one of ADDR_LINES, ... + int skip, // only skip the address, don't use it + int silent, // no errors or side effects + int to_other_file, // flag: may jump to other file + int address_count UNUSED) // 1 for first address, >1 after comma +{ + int c; + int i; + long n; + char_u *cmd; + pos_T pos; + pos_T *fp; + linenr_T lnum; + buf_T *buf; + + cmd = skipwhite(*ptr); + lnum = MAXLNUM; + do + { + switch (*cmd) + { + case '.': /* '.' - Cursor position */ + ++cmd; + switch (addr_type) + { + case ADDR_LINES: + lnum = curwin->w_cursor.lnum; + break; + case ADDR_WINDOWS: + lnum = CURRENT_WIN_NR; + break; + case ADDR_ARGUMENTS: + lnum = curwin->w_arg_idx + 1; + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + lnum = curbuf->b_fnum; + break; + case ADDR_TABS: + lnum = CURRENT_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + emsg(_(e_invrange)); + cmd = NULL; + goto error; + break; +#ifdef FEAT_QUICKFIX + case ADDR_QUICKFIX: + lnum = qf_get_cur_valid_idx(eap); + break; +#endif + } + break; + + case '$': /* '$' - last line */ + ++cmd; + switch (addr_type) + { + case ADDR_LINES: + lnum = curbuf->b_ml.ml_line_count; + break; + case ADDR_WINDOWS: + lnum = LAST_WIN_NR; + break; + case ADDR_ARGUMENTS: + lnum = ARGCOUNT; + break; + case ADDR_LOADED_BUFFERS: + buf = lastbuf; + while (buf->b_ml.ml_mfp == NULL) + { + if (buf->b_prev == NULL) + break; + buf = buf->b_prev; + } + lnum = buf->b_fnum; + break; + case ADDR_BUFFERS: + lnum = lastbuf->b_fnum; + break; + case ADDR_TABS: + lnum = LAST_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + emsg(_(e_invrange)); + cmd = NULL; + goto error; + break; +#ifdef FEAT_QUICKFIX + case ADDR_QUICKFIX: + lnum = qf_get_size(eap); + if (lnum == 0) + lnum = 1; + break; +#endif + } + break; + + case '\'': /* ''' - mark */ + if (*++cmd == NUL) + { + cmd = NULL; + goto error; + } + if (addr_type != ADDR_LINES) + { + emsg(_(e_invaddr)); + 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 (addr_type != ADDR_LINES) + { + emsg(_(e_invaddr)); + cmd = NULL; + goto error; + } + if (skip) /* skip "/pat/" */ + { + cmd = skip_regexp(cmd, c, (int)p_magic, NULL); + if (*cmd == c) + ++cmd; + } + else + { + int flags; + + 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 (unless + // before the first 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.lnum > 0) + curwin->w_cursor.col = MAXCOL; + else + curwin->w_cursor.col = 0; + searchcmdlen = 0; + flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG; + if (!do_search(NULL, c, cmd, 1L, flags, NULL, 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 (addr_type != ADDR_LINES) + { + emsg(_(e_invaddr)); + cmd = NULL; + goto error; + } + 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; + pos.coladd = 0; + if (searchit(curwin, curbuf, &pos, NULL, + *cmd == '?' ? BACKWARD : FORWARD, + (char_u *)"", 1L, SEARCH_MSG, + i, (linenr_T)0, NULL, 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) + { + switch (addr_type) + { + case ADDR_LINES: + /* "+1" is same as ".+1" */ + lnum = curwin->w_cursor.lnum; + break; + case ADDR_WINDOWS: + lnum = CURRENT_WIN_NR; + break; + case ADDR_ARGUMENTS: + lnum = curwin->w_arg_idx + 1; + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + lnum = curbuf->b_fnum; + break; + case ADDR_TABS: + lnum = CURRENT_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + lnum = 1; + break; +#ifdef FEAT_QUICKFIX + case ADDR_QUICKFIX: + lnum = qf_get_cur_valid_idx(eap); + break; +#endif + } + } + + 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 (addr_type == ADDR_TABS_RELATIVE) + { + emsg(_(e_invrange)); + cmd = NULL; + goto error; + } + else if (addr_type == ADDR_LOADED_BUFFERS + || addr_type == ADDR_BUFFERS) + lnum = compute_buffer_local_count( + addr_type, lnum, (i == '-') ? -1 * n : n); + else + { +#ifdef FEAT_FOLDING + /* Relative line addressing, need to adjust for folded lines + * now, but only do it after the first address. */ + if (addr_type == ADDR_LINES && (i == '-' || i == '+') + && address_count >= 2) + (void)hasFolding(lnum, NULL, &lnum); +#endif + 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 = 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 + vim_free(script_get(eap, eap->arg)); +} +#endif + +/* + * Check range in Ex command for validity. + * Return NULL when valid, error message when invalid. + */ + static char * +invalid_range(exarg_T *eap) +{ + buf_T *buf; + if ( eap->line1 < 0 + || eap->line2 < 0 + || eap->line1 > eap->line2) + return _(e_invrange); + + if (eap->argt & RANGE) + { + switch(eap->addr_type) + { + case ADDR_LINES: + if (!(eap->argt & NOTADR) + && eap->line2 > curbuf->b_ml.ml_line_count +#ifdef FEAT_DIFF + + (eap->cmdidx == CMD_diffget) +#endif + ) + return _(e_invrange); + break; + case ADDR_ARGUMENTS: + /* add 1 if ARGCOUNT is 0 */ + if (eap->line2 > ARGCOUNT + (!ARGCOUNT)) + return _(e_invrange); + break; + case ADDR_BUFFERS: + if (eap->line1 < firstbuf->b_fnum + || eap->line2 > lastbuf->b_fnum) + return _(e_invrange); + break; + case ADDR_LOADED_BUFFERS: + buf = firstbuf; + while (buf->b_ml.ml_mfp == NULL) + { + if (buf->b_next == NULL) + return _(e_invrange); + buf = buf->b_next; + } + if (eap->line1 < buf->b_fnum) + return _(e_invrange); + buf = lastbuf; + while (buf->b_ml.ml_mfp == NULL) + { + if (buf->b_prev == NULL) + return _(e_invrange); + buf = buf->b_prev; + } + if (eap->line2 > buf->b_fnum) + return _(e_invrange); + break; + case ADDR_WINDOWS: + if (eap->line2 > LAST_WIN_NR) + return _(e_invrange); + break; + case ADDR_TABS: + if (eap->line2 > LAST_TAB_NR) + return _(e_invrange); + break; + case ADDR_TABS_RELATIVE: + /* Do nothing */ + break; +#ifdef FEAT_QUICKFIX + case ADDR_QUICKFIX: + if (eap->line2 != 1 && eap->line2 > qf_get_size(eap)) + return _(e_invrange); + break; +#endif + } + } + 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; + } +} + +#ifdef FEAT_QUICKFIX +/* + * 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 = alloc((int)(STRLEN(program) + i * (len - 2) + 1)); + if (new_cmdline == NULL) + return NULL; /* out of memory */ + 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 = alloc((int)(STRLEN(program) + STRLEN(p) + 2)); + if (new_cmdline == NULL) + return NULL; /* out of memory */ + 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 */ + vim_free(*cmdlinep); + *cmdlinep = new_cmdline; + p = new_cmdline; + } + return p; +} +#endif + +/* + * Expand file name in Ex command argument. + * Return FAIL for failure, OK otherwise. + */ + int +expand_filename( + exarg_T *eap, + char_u **cmdlinep, + char **errormsgp) +{ + int has_wildcards; /* need to expand wildcards */ + char_u *repl; + int srclen; + char_u *p; + int n; + int escaped; + +#ifdef FEAT_QUICKFIX + /* Skip a regexp pattern for ":vimgrep[add] pat file..." */ + p = skip_grep_pat(eap); +#else + p = eap->arg; +#endif + + /* + * 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) + { +#ifdef FEAT_EVAL + /* Skip over `=expr`, wildcards in it are not expanded. */ + if (p[0] == '`' && p[1] == '=') + { + p += 2; + (void)skip_expr(&p); + if (*p == '`') + ++p; + continue; + } +#endif + /* + * 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); + vim_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_grep + && eap->cmdidx != CMD_grepadd + && eap->cmdidx != CMD_hardcopy + && eap->cmdidx != CMD_lgrep + && eap->cmdidx != CMD_lgrepadd + && eap->cmdidx != CMD_lmake + && eap->cmdidx != CMD_make + && eap->cmdidx != CMD_terminal +#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) + { + vim_free(repl); + repl = l; + } + break; + } + } + + /* For a shell command a '!' must be escaped. */ + if ((eap->usefilter || eap->cmdidx == CMD_bang + || eap->cmdidx == CMD_terminal) + && vim_strpbrk(repl, (char_u *)"!") != NULL) + { + char_u *l; + + l = vim_strsave_escaped(repl, (char_u *)"!"); + if (l != NULL) + { + vim_free(repl); + repl = l; + } + } + + p = repl_cmdline(eap, p, srclen, repl, cmdlinep); + vim_free(repl); + if (p == NULL) + return FAIL; + } + + /* + * One file argument: Expand wildcards. + * Don't do this with ":r !command" or ":w !command". + */ + if ((eap->argt & NOSPC) && !eap->usefilter) + { + /* + * May do this twice: + * 1. Replace environment variables. + * 2. Replace any other wildcards, remove backslashes. + */ + for (n = 1; n <= 2; ++n) + { + if (n == 2) + { + /* + * Halve the number of backslashes (this is Vi compatible). + * For Unix and OS/2, when wildcards are expanded, this is + * done by ExpandOne() below. + */ +#if defined(UNIX) + if (!has_wildcards) +#endif + backslash_halve(eap->arg); + } + + if (has_wildcards) + { + if (n == 1) + { + /* + * First loop: 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; + } + else /* n == 2 */ + { + 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); + if (n == 2) /* p came from ExpandOne() */ + vim_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. + * Returns NULL for failure. + */ + 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 */ + if ((new_cmdline = alloc((unsigned)i)) == NULL) + return NULL; /* out of memory! */ + + /* + * 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 */ + mch_memmove(new_cmdline, *cmdlinep, (size_t)i); + + mch_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); + vim_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; + +#ifdef FEAT_QUICKFIX + p = skip_grep_pat(eap); +#else + p = eap->arg; +#endif + + 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; + } + +#ifdef FEAT_EVAL + /* Skip over `=expr` when wildcards are expanded. */ + else if (p[0] == '`' && p[1] == '=' && (eap->argt & XFILE)) + { + p += 2; + (void)skip_expr(&p); + } +#endif + + /* 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 == NUL) + 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; +} + + int +get_bad_opt(char_u *p, exarg_T *eap) +{ + 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; +} + +/* + * 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; + eap->force_ff = eap->cmd[eap->force_ff]; + } + 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". */ + if (get_bad_opt(eap->cmd + bad_char_idx, eap) == FAIL) + 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 autocommands 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); + int did_aucmd; + + (void)do_doautocmd(arg, TRUE, &did_aucmd); + /* Only when there is no . */ + if (call_do_modelines && did_aucmd) + 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); + if (eap->do_ecmd_cmd != NULL) + do_cmdline_cmd(eap->do_ecmd_cmd); + } +} + +/* + * :[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); + if (eap->do_ecmd_cmd != NULL) + do_cmdline_cmd(eap->do_ecmd_cmd); +} + +/* + * :[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); + if (eap->do_ecmd_cmd != NULL) + do_cmdline_cmd(eap->do_ecmd_cmd); +} + +/* + * :[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); + if (eap->do_ecmd_cmd != NULL) + do_cmdline_cmd(eap->do_ecmd_cmd); +} + +/* + * :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); + if (eap->do_ecmd_cmd != NULL) + do_cmdline_cmd(eap->do_ecmd_cmd); +} + +/* + * :blast to last buffer + * :sblast split and to last buffer + */ + static void +ex_blast(exarg_T *eap) +{ + goto_buffer(eap, DOBUF_LAST, BACKWARD, 0); + if (eap->do_ecmd_cmd != NULL) + do_cmdline_cmd(eap->do_ecmd_cmd); +} + + 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, skipping over white space. + * Return NULL if it isn't, the following character if it is. + */ + char_u * +check_nextcmd(char_u *p) +{ + char_u *s = skipwhite(p); + + if (*s == '|' || *s == '\n') + return (s + 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 defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + if ((p_confirm || cmdmod.confirm) && curbuf->b_fname != NULL) + { + char_u buff[DIALOG_MSG_SIZE]; + + vim_snprintf((char *)buff, DIALOG_MSG_SIZE, + NGETTEXT("%d more file to edit. Quit anyway?", + "%d more files to edit. Quit anyway?", n), n); + if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES) + return OK; + return FAIL; + } +#endif + semsg(NGETTEXT("E173: %d more file to edit", + "E173: %d more files to edit", n), n); + quitmore = 2; /* next try to quit is allowed */ + } + return FAIL; + } + return OK; +} + +#ifdef FEAT_CMDL_COMPL +/* + * Function given to ExpandGeneric() to obtain the list of command names. + */ + char_u * +get_command_name(expand_T *xp UNUSED, int idx) +{ + if (idx >= (int)CMD_SIZE) +# ifdef FEAT_USR_CMDS + return get_user_command_name(idx); +# else + return NULL; +# endif + return cmdnames[idx].cmd_name; +} +#endif + +#if defined(FEAT_USR_CMDS) || defined(PROTO) + 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 addr_type, + 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_init2(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) + { + // Command can be replaced with "command!" and when sourcing the + // same script again, but only once. + if (!force && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid + || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq)) + { + semsg(_("E174: Command already exists: add ! to replace it: %s"), + name); + goto fail; + } + + VIM_CLEAR(cmd->uc_rep); +#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + VIM_CLEAR(cmd->uc_compl_arg); +#endif + 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) + { + if (ga_grow(gap, 1) != OK) + goto fail; + if ((p = vim_strnsave(name, (int)name_len)) == NULL) + goto fail; + + cmd = USER_CMD_GA(gap, i); + mch_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; +#ifdef FEAT_EVAL + cmd->uc_script_ctx = current_sctx; + cmd->uc_script_ctx.sc_lnum += sourcing_lnum; +# ifdef FEAT_CMDL_COMPL + cmd->uc_compl_arg = compl_arg; +# endif +#endif + cmd->uc_addr_type = addr_type; + + return OK; + +fail: + vim_free(rep_buf); +#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + vim_free(compl_arg); +#endif + return FAIL; +} +#endif + +#if defined(FEAT_USR_CMDS) +static struct +{ + int expand; + char *name; +} addr_type_complete[] = +{ + {ADDR_ARGUMENTS, "arguments"}, + {ADDR_LINES, "lines"}, + {ADDR_LOADED_BUFFERS, "loaded_buffers"}, + {ADDR_TABS, "tabs"}, + {ADDR_BUFFERS, "buffers"}, + {ADDR_WINDOWS, "windows"}, + {ADDR_QUICKFIX, "quickfix"}, + {ADDR_OTHER, "other"}, + {-1, NULL} +}; +#endif + +#if defined(FEAT_USR_CMDS) || defined(FEAT_EVAL) || defined(PROTO) +/* + * 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_ARGLIST, "arglist"}, + {EXPAND_AUGROUP, "augroup"}, + {EXPAND_BEHAVE, "behave"}, + {EXPAND_BUFFERS, "buffer"}, + {EXPAND_COLORS, "color"}, + {EXPAND_COMMANDS, "command"}, + {EXPAND_COMPILER, "compiler"}, +#if defined(FEAT_CSCOPE) + {EXPAND_CSCOPE, "cscope"}, +#endif +#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + {EXPAND_USER_DEFINED, "custom"}, + {EXPAND_USER_LIST, "customlist"}, +#endif + {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"}, +#if defined(FEAT_CMDHIST) + {EXPAND_HISTORY, "history"}, +#endif +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + {EXPAND_LOCALES, "locale"}, +#endif + {EXPAND_MAPCLEAR, "mapclear"}, + {EXPAND_MAPPINGS, "mapping"}, + {EXPAND_MENUS, "menu"}, + {EXPAND_MESSAGES, "messages"}, + {EXPAND_OWNSYNTAX, "syntax"}, +#if defined(FEAT_PROFILE) + {EXPAND_SYNTIME, "syntime"}, +#endif + {EXPAND_SETTINGS, "option"}, + {EXPAND_PACKADD, "packadd"}, + {EXPAND_SHELLCMD, "shellcmd"}, +#if defined(FEAT_SIGNS) + {EXPAND_SIGN, "sign"}, +#endif + {EXPAND_TAGS, "tag"}, + {EXPAND_TAGS_LISTFILES, "tag_listfiles"}, + {EXPAND_USER, "user"}, + {EXPAND_USER_VARS, "var"}, + {0, NULL} +}; +#endif + +#if defined(FEAT_USR_CMDS) || defined(PROTO) + 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 and + * commands filtered out. */ + if (STRNCMP(name, cmd->uc_name, name_len) != 0 + || message_filtered(cmd->uc_name)) + continue; + + /* Put out the title first time */ + if (!found) + msg_puts_title(_("\n Name Args Address 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, "%ldc", 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, "%ld", cmd->uc_def); + len += (int)STRLEN(IObuff + len); + } + else + IObuff[len++] = '.'; + } + + do { + IObuff[len++] = ' '; + } while (len < 11); + + /* Address Type */ + for (j = 0; addr_type_complete[j].expand != -1; ++j) + if (addr_type_complete[j].expand != ADDR_LINES + && addr_type_complete[j].expand == cmd->uc_addr_type) + { + STRCPY(IObuff + len, addr_type_complete[j].name); + len += (int)STRLEN(IObuff + len); + break; + } + + do { + IObuff[len++] = ' '; + } while (len < 21); + + /* 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 < 35); + + IObuff[len] = '\0'; + msg_outtrans(IObuff); + + msg_outtrans_special(cmd->uc_rep, FALSE); +#ifdef FEAT_EVAL + if (p_verbose > 0) + last_set_msg(cmd->uc_script_ctx); +#endif + 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 * +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 (char *)IObuff; +} + + static int +uc_scan_attr( + char_u *attr, + size_t len, + long *argt, + long *def, + int *flags, + int *compl, + char_u **compl_arg, + int *addr_type_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 if (STRNICMP(attr, "addr", attrlen) == 0) + { + *argt |= RANGE; + if (val == NULL) + { + emsg(_("E179: argument required for -addr")); + return FAIL; + } + if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) + == FAIL) + return FAIL; + if (addr_type_arg != ADDR_LINES) + *argt |= (ZEROR | NOTADR) ; + } + else + { + char_u ch = attr[len]; + attr[len] = '\0'; + semsg(_("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 addr_type_arg = ADDR_LINES; + 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, &addr_type_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, + addr_type_arg, eap->forceit); +} + +/* + * ":comclear" + * Clear all user commands, global and for current buffer. + */ + void +ex_comclear(exarg_T *eap UNUSED) +{ + 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); + vim_free(cmd->uc_name); + vim_free(cmd->uc_rep); +# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + vim_free(cmd->uc_compl_arg); +# endif + } + 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) + { + semsg(_("E184: No such user-defined command: %s"), eap->arg); + return; + } + + vim_free(cmd->uc_name); + vim_free(cmd->uc_rep); +# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + vim_free(cmd->uc_compl_arg); +# endif + + --gap->ga_len; + + if (i < gap->ga_len) + mch_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 = alloc(len + 1); + if (buf == NULL) + { + *lenp = 0; + return buf; + } + + 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; +} + + static size_t +add_cmd_modifier(char_u *buf, char *mod_str, int *multi_mods) +{ + size_t result; + + result = STRLEN(mod_str); + if (*multi_mods) + result += 1; + if (buf != NULL) + { + if (*multi_mods) + STRCAT(buf, " "); + STRCAT(buf, mod_str); + } + + *multi_mods = 1; + + return result; +} + +/* + * 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_RANGE, + ct_MODS, + 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, "range>", l) == 0) + type = ct_RANGE; + 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; + else if (STRNICMP(p, "mods>", l) == 0) + type = ct_MODS; + + 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_RANGE: + case ct_COUNT: + { + char num_buf[20]; + long num = (type == ct_LINE1) ? eap->line1 : + (type == ct_LINE2) ? eap->line2 : + (type == ct_RANGE) ? eap->addr_count : + (eap->addr_count > 0) ? eap->line2 : cmd->uc_def; + size_t num_len; + + sprintf(num_buf, "%ld", 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_MODS: + { + int multi_mods = 0; + typedef struct { + int *varp; + char *name; + } mod_entry_T; + static mod_entry_T mod_entries[] = { +#ifdef FEAT_BROWSE_CMD + {&cmdmod.browse, "browse"}, +#endif +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + {&cmdmod.confirm, "confirm"}, +#endif + {&cmdmod.hide, "hide"}, + {&cmdmod.keepalt, "keepalt"}, + {&cmdmod.keepjumps, "keepjumps"}, + {&cmdmod.keepmarks, "keepmarks"}, + {&cmdmod.keeppatterns, "keeppatterns"}, + {&cmdmod.lockmarks, "lockmarks"}, + {&cmdmod.noswapfile, "noswapfile"}, + {NULL, NULL} + }; + int i; + + result = quote ? 2 : 0; + if (buf != NULL) + { + if (quote) + *buf++ = '"'; + *buf = '\0'; + } + + /* :aboveleft and :leftabove */ + if (cmdmod.split & WSP_ABOVE) + result += add_cmd_modifier(buf, "aboveleft", &multi_mods); + /* :belowright and :rightbelow */ + if (cmdmod.split & WSP_BELOW) + result += add_cmd_modifier(buf, "belowright", &multi_mods); + /* :botright */ + if (cmdmod.split & WSP_BOT) + result += add_cmd_modifier(buf, "botright", &multi_mods); + + /* the modifiers that are simple flags */ + for (i = 0; mod_entries[i].varp != NULL; ++i) + if (*mod_entries[i].varp) + result += add_cmd_modifier(buf, mod_entries[i].name, + &multi_mods); + + /* TODO: How to support :noautocmd? */ +#ifdef HAVE_SANDBOX + /* TODO: How to support :sandbox?*/ +#endif + /* :silent */ + if (msg_silent > 0) + result += add_cmd_modifier(buf, + emsg_silent > 0 ? "silent!" : "silent", &multi_mods); + /* :tab */ + if (cmdmod.tab > 0) + result += add_cmd_modifier(buf, "tab", &multi_mods); + /* :topleft */ + if (cmdmod.split & WSP_TOP) + result += add_cmd_modifier(buf, "topleft", &multi_mods); + /* TODO: How to support :unsilent?*/ + /* :verbose */ + if (p_verbose > 0) + result += add_cmd_modifier(buf, "verbose", &multi_mods); + /* :vertical */ + if (cmdmod.split & WSP_VERT) + result += add_cmd_modifier(buf, "vertical", &multi_mods); + if (quote && buf != NULL) + { + buf += result - 2; + *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; +#ifdef FEAT_EVAL + sctx_T save_current_sctx = current_sctx; +#endif + + 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) +# ifdef FEAT_GUI + || (ksp[1] == KS_EXTRA && ksp[2] == (int)KE_CSI) +# endif + )) + { + /* 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) + { + mch_memmove(q, p, len); + q += len; + } + *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI; + p = ksp + 3; + continue; + } + } + + /* break if 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 + { + mch_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 = alloc((unsigned)(totlen + 1)); + if (buf == NULL) + { + vim_free(split_buf); + return; + } + } + +#ifdef FEAT_EVAL + current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; +#endif + (void)do_cmdline(buf, eap->getline, eap->cookie, + DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); +#ifdef FEAT_EVAL + current_sctx = save_current_sctx; +#endif + vim_free(buf); + vim_free(split_buf); +} + +# if defined(FEAT_CMDL_COMPL) || defined(PROTO) + 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 UNUSED, 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 address type names. + */ + char_u * +get_user_cmd_addr_type(expand_T *xp UNUSED, int idx) +{ + return (char_u *)addr_type_complete[idx].name; +} + +/* + * Function given to ExpandGeneric() to obtain the list of user command + * attributes. + */ + char_u * +get_user_cmd_flags(expand_T *xp UNUSED, int idx) +{ + static char *user_cmd_flags[] = + {"addr", "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 UNUSED, 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 UNUSED, int idx) +{ + return (char_u *)command_complete[idx].name; +} +# endif /* FEAT_CMDL_COMPL */ + +/* + * Parse address type argument + */ + int +parse_addr_type_arg( + char_u *value, + int vallen, + long *argt, + int *addr_type_arg) +{ + int i, a, b; + + for (i = 0; addr_type_complete[i].expand != -1; ++i) + { + a = (int)STRLEN(addr_type_complete[i].name) == vallen; + b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0; + if (a && b) + { + *addr_type_arg = addr_type_complete[i].expand; + break; + } + } + + if (addr_type_complete[i].expand == -1) + { + char_u *err = value; + + for (i = 0; err[i] != NUL && !VIM_ISWHITE(err[i]); i++) + ; + err[i] = NUL; + semsg(_("E180: Invalid address type value: %s"), err); + return FAIL; + } + + if (*addr_type_arg != ADDR_LINES) + *argt |= NOTADR; + + return OK; +} + +#endif /* FEAT_USR_CMDS */ + +#if defined(FEAT_USR_CMDS) || defined(FEAT_EVAL) || defined(PROTO) +/* + * 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 UNUSED) +{ + char_u *arg = NULL; +# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + size_t arglen = 0; +# endif + 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]; +# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + arglen = vallen - i - 1; +# endif + 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) + { + semsg(_("E180: Invalid complete value: %s"), value); + return FAIL; + } + +# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST + && arg != NULL) +# else + if (arg != NULL) +# endif + { + emsg(_("E468: Completion argument only allowed for custom completion")); + return FAIL; + } + +# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + 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); +# endif + return OK; +} + + int +cmdcomplete_str_to_type(char_u *complete_str) +{ + int i; + + for (i = 0; command_complete[i].expand != 0; ++i) + if (STRCMP(complete_str, command_complete[i].name) == 0) + return command_complete[i].expand; + + return EXPAND_NOTHING; +} +#endif + + static void +ex_colorscheme(exarg_T *eap) +{ + if (*eap->arg == NUL) + { +#ifdef FEAT_EVAL + 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; + vim_free(expr); + } + if (p != NULL) + { + msg((char *)p); + vim_free(p); + } + else + msg("default"); +#else + msg(_("unknown")); +#endif + } + else if (load_colors(eap->arg) == FAIL) + semsg(_("E185: Cannot find color scheme '%s'"), eap->arg); + +#ifdef FEAT_VTP + else if (has_vtp_working()) + { + // background color change requires clear + redraw + update_screen(CLEAR); + redrawcmd(); + } +#endif +} + + 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); +} + + static int +before_quit_autocmds(win_T *wp, int quit_all, int forceit) +{ + apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, wp->w_buffer); + + /* Bail out when autocommands closed the window. + * Refuse to quit when the buffer in the last window is being closed (can + * only happen in autocommands). */ + if (!win_valid(wp) + || curbuf_locked() + || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0)) + return TRUE; + + if (quit_all || (check_more(FALSE, forceit) == OK && only_one_window())) + { + apply_autocmds(EVENT_EXITPRE, 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_locked > 0)) + return TRUE; + } + + return FALSE; +} + +/* + * ":quit": quit current window, quit Vim if the last window is closed. + * ":{nr}quit": quit window {nr} + */ + static void +ex_quit(exarg_T *eap) +{ + win_T *wp; + +#ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + { + cmdwin_result = Ctrl_C; + return; + } +#endif + /* Don't quit while editing the command line. */ + if (text_locked()) + { + text_locked_msg(); + return; + } + if (eap->addr_count > 0) + { + int wnr = eap->line2; + + for (wp = firstwin; wp->w_next != NULL; wp = wp->w_next) + if (--wnr <= 0) + break; + } + else + wp = curwin; + + /* Refuse to quit when locked. */ + if (curbuf_locked()) + return; + + /* Trigger QuitPre and maybe ExitPre */ + if (before_quit_autocmds(wp, FALSE, eap->forceit)) + return; + +#ifdef FEAT_NETBEANS_INTG + netbeansForcedQuit = eap->forceit; +#endif + + /* + * If there are more files or windows we won't exit. + */ + if (check_more(FALSE, eap->forceit) == OK && only_one_window()) + exiting = TRUE; + if ((!buf_hide(wp->w_buffer) + && check_changed(wp->w_buffer, (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, TRUE))) + { + not_exiting(); + } + else + { + /* quit last window + * Note: only_one_window() returns true, even so a help window is + * still open. In that case only quit, if no address has been + * specified. Example: + * :h|wincmd w|1q - don't quit + * :h|wincmd w|q - quit + */ + if (only_one_window() && (ONE_WINDOW || eap->addr_count == 0)) + getout(0); + not_exiting(); +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + /* close window; may free buffer */ + win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit); + } +} + +/* + * ":cquit". + */ + static void +ex_cquit(exarg_T *eap UNUSED) +{ + 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) +{ +# ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + { + if (eap->forceit) + cmdwin_result = K_XF1; /* ex_window() takes care of this */ + else + cmdwin_result = K_XF2; + return; + } +# endif + + /* Don't quit while editing the command line. */ + if (text_locked()) + { + text_locked_msg(); + return; + } + + if (before_quit_autocmds(curwin, TRUE, eap->forceit)) + return; + + exiting = TRUE; + if (eap->forceit || !check_changed_any(FALSE, FALSE)) + getout(0); + not_exiting(); +} + +/* + * ":close": close current window, unless it is the last one + */ + static void +ex_close(exarg_T *eap) +{ + win_T *win; + int winnr = 0; +#ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + cmdwin_result = Ctrl_C; + else +#endif + if (!text_locked() && !curbuf_locked()) + { + if (eap->addr_count == 0) + ex_win_close(eap->forceit, curwin, NULL); + else + { + FOR_ALL_WINDOWS(win) + { + winnr++; + if (winnr == eap->line2) + break; + } + if (win == NULL) + win = lastwin; + ex_win_close(eap->forceit, win, NULL); + } + } +} + +#ifdef FEAT_QUICKFIX +/* + * ":pclose": Close any preview window. + */ + static void +ex_pclose(exarg_T *eap) +{ + win_T *win; + + FOR_ALL_WINDOWS(win) + if (win->w_p_pvw) + { + ex_win_close(eap->forceit, win, NULL); + break; + } +} +#endif + +/* + * 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 && !buf_hide(buf) && !forceit) + { +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + if ((p_confirm || cmdmod.confirm) && p_write) + { + bufref_T bufref; + + set_bufref(&bufref, buf); + dialog_changed(buf, FALSE); + if (bufref_valid(&bufref) && bufIsChanged(buf)) + return; + need_hide = FALSE; + } + else +#endif + { + no_write_message(); + return; + } + } + +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + + /* free buffer when not hiding it or when it's a scratch buffer */ + if (tp == NULL) + win_close(win, !need_hide && !buf_hide(buf)); + else + win_close_othertab(win, !need_hide && !buf_hide(buf), tp); +} + +/* + * Handle the argument for a tabpage related ex command. + * Returns a tabpage number. + * When an error is encountered then eap->errmsg is set. + */ + static int +get_tabpage_arg(exarg_T *eap) +{ + int tab_number; + int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; + + if (eap->arg && *eap->arg != NUL) + { + char_u *p = eap->arg; + char_u *p_save; + int relative = 0; /* argument +N/-N means: go to N places to the + * right/left relative to the current position. */ + + if (*p == '-') + { + relative = -1; + p++; + } + else if (*p == '+') + { + relative = 1; + p++; + } + + p_save = p; + tab_number = getdigits(&p); + + if (relative == 0) + { + if (STRCMP(p, "$") == 0) + tab_number = LAST_TAB_NR; + else if (p == p_save || *p_save == '-' || *p != NUL + || tab_number > LAST_TAB_NR) + { + /* No numbers as argument. */ + eap->errmsg = e_invarg; + goto theend; + } + } + else + { + if (*p_save == NUL) + tab_number = 1; + else if (p == p_save || *p_save == '-' || *p != NUL + || tab_number == 0) + { + /* No numbers as argument. */ + eap->errmsg = e_invarg; + goto theend; + } + tab_number = tab_number * relative + tabpage_index(curtab); + if (!unaccept_arg0 && relative == -1) + --tab_number; + } + if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR) + eap->errmsg = e_invarg; + } + else if (eap->addr_count > 0) + { + if (unaccept_arg0 && eap->line2 == 0) + { + eap->errmsg = e_invrange; + tab_number = 0; + } + else + { + tab_number = eap->line2; + if (!unaccept_arg0 && *skipwhite(*eap->cmdlinep) == '-') + { + --tab_number; + if (tab_number < unaccept_arg0) + eap->errmsg = e_invarg; + } + } + } + else + { + switch (eap->cmdidx) + { + case CMD_tabnext: + tab_number = tabpage_index(curtab) + 1; + if (tab_number > LAST_TAB_NR) + tab_number = 1; + break; + case CMD_tabmove: + tab_number = LAST_TAB_NR; + break; + default: + tab_number = tabpage_index(curtab); + } + } + +theend: + return tab_number; +} + +/* + * ":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; + int tab_number; + +# ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + cmdwin_result = K_IGNORE; + else +# endif + if (first_tabpage->tp_next == NULL) + emsg(_("E784: Cannot close last tab page")); + else + { + tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) + { + tp = find_tabpage(tab_number); + if (tp == NULL) + { + beep_flush(); + return; + } + if (tp != curtab) + { + tabpage_close_other(tp, eap->forceit); + return; + } + else 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; + int tab_number; + +# ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + cmdwin_result = K_IGNORE; + else +# endif + if (first_tabpage->tp_next == NULL) + msg(_("Already only one tab page")); + else + { + tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) + { + goto_tabpage(tab_number); + /* Repeat this up to a 1000 times, because autocommands may + * mess up the lists. */ + for (done = 0; done < 1000; ++done) + { + FOR_ALL_TABPAGES(tp) + 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 (!ONE_WINDOW) + close_others(TRUE, forceit); + if (ONE_WINDOW) + ex_win_close(forceit, curwin, NULL); +# ifdef FEAT_GUI + need_mouse_correct = TRUE; +# endif +} + +/* + * 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; + } + + apply_autocmds(EVENT_TABCLOSED, NULL, NULL, FALSE, curbuf); + + redraw_tabline = TRUE; + if (h != tabline_height()) + shell_new_rows(); +} + +/* + * ":only". + */ + static void +ex_only(exarg_T *eap) +{ + win_T *wp; + int wnr; +# ifdef FEAT_GUI + need_mouse_correct = TRUE; +# endif + if (eap->addr_count > 0) + { + wnr = eap->line2; + for (wp = firstwin; --wnr > 0; ) + { + if (wp->w_next == NULL) + break; + else + wp = wp->w_next; + } + win_goto(wp); + } + 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 UNUSED) +{ + /* ":hide" or ":hide | cmd": hide current window */ + if (!eap->skip) + { +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + if (eap->addr_count == 0) + win_close(curwin, FALSE); /* don't free buffer */ + else + { + int winnr = 0; + win_T *win; + + FOR_ALL_WINDOWS(win) + { + winnr++; + if (winnr == eap->line2) + break; + } + if (win == NULL) + win = lastwin; + win_close(win, FALSE); + } + } +} + +/* + * ":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 */ +#ifdef FEAT_TITLE + mch_restore_title(SAVE_RESTORE_BOTH); /* restore window titles */ +#endif + ui_suspend(); /* call machine specific function */ +#ifdef FEAT_TITLE + maketitle(); + resettitle(); /* force updating the title */ +#endif + starttermcap(); + scroll_start(); /* scroll screen before redrawing */ + redraw_later_clear(); + shell_resized(); /* may have resized window */ + } +} + +/* + * ":exit", ":xit" and ":wq": Write file and quite the current window. + */ + static void +ex_exit(exarg_T *eap) +{ +#ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + { + cmdwin_result = Ctrl_C; + return; + } +#endif + /* Don't quit while editing the command line. */ + if (text_locked()) + { + text_locked_msg(); + return; + } + + if (before_quit_autocmds(curwin, FALSE, eap->forceit)) + 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, FALSE))) + { + not_exiting(); + } + else + { + if (only_one_window()) /* quit last window, exit Vim */ + getout(0); + not_exiting(); +# ifdef FEAT_GUI + need_mouse_correct = TRUE; +# endif + /* Quit current window, may free the buffer. */ + win_close(curwin, !buf_hide(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; +} + +#ifdef FEAT_BYTEOFF + static void +ex_goto(exarg_T *eap) +{ + goto_byte(eap->line2); +} +#endif + +/* + * ":shell". + */ + static void +ex_shell(exarg_T *eap UNUSED) +{ + do_shell(NULL, 0); +} + +#if defined(HAVE_DROP_FILE) || defined(PROTO) + +static int drop_busy = FALSE; +static int drop_filec; +static char_u **drop_filev = NULL; +static int drop_split; +static void (*drop_callback)(void *); +static void *drop_cookie; + + static void +handle_drop_internal(void) +{ + exarg_T ea; + int save_msg_scroll = msg_scroll; + + // Setting the argument list may cause screen updates and being called + // recursively. Avoid that by setting drop_busy. + drop_busy = TRUE; + + /* Check whether the current buffer is changed. If so, we will need + * to split the current window or data could be lost. + * We don't need to check if the 'hidden' option is set, as in this + * case the buffer won't be lost. + */ + if (!buf_hide(curbuf) && !drop_split) + { + ++emsg_off; + drop_split = check_changed(curbuf, CCGD_AW); + --emsg_off; + } + if (drop_split) + { + if (win_split(0, 0) == FAIL) + return; + RESET_BINDING(curwin); + + /* When splitting the window, create a new alist. Otherwise the + * existing one is overwritten. */ + alist_unlink(curwin->w_alist); + alist_new(); + } + + /* + * Set up the new argument list. + */ + alist_set(ALIST(curwin), drop_filec, drop_filev, FALSE, NULL, 0); + + /* + * Move to the first file. + */ + /* Fake up a minimal "next" command for do_argfile() */ + vim_memset(&ea, 0, sizeof(ea)); + ea.cmd = (char_u *)"next"; + do_argfile(&ea, 0); + + /* do_ecmd() may set need_start_insertmode, but since we never left Insert + * mode that is not needed here. */ + need_start_insertmode = FALSE; + + /* Restore msg_scroll, otherwise a following command may cause scrolling + * unexpectedly. The screen will be redrawn by the caller, thus + * msg_scroll being set by displaying a message is irrelevant. */ + msg_scroll = save_msg_scroll; + + if (drop_callback != NULL) + drop_callback(drop_cookie); + + drop_filev = NULL; + drop_busy = FALSE; +} + +/* + * Handle a file drop. The code is here because a drop is *nearly* like an + * :args command, but not quite (we have a list of exact filenames, so we + * don't want to (a) parse a command line, or (b) expand wildcards). So the + * code is very similar to :args and hence needs access to a lot of the static + * functions in this file. + * + * The "filev" list must have been allocated using alloc(), as should each item + * in the list. This function takes over responsibility for freeing the "filev" + * list. + */ + void +handle_drop( + int filec, // the number of files dropped + char_u **filev, // the list of files dropped + int split, // force splitting the window + void (*callback)(void *), // to be called after setting the argument + // list + void *cookie) // argument for "callback" (allocated) +{ + // Cannot handle recursive drops, finish the pending one. + if (drop_busy) + { + FreeWild(filec, filev); + vim_free(cookie); + return; + } + + // When calling handle_drop() more than once in a row we only use the last + // one. + if (drop_filev != NULL) + { + FreeWild(drop_filec, drop_filev); + vim_free(drop_cookie); + } + + drop_filec = filec; + drop_filev = filev; + drop_split = split; + drop_callback = callback; + drop_cookie = cookie; + + // Postpone this when: + // - editing the command line + // - not possible to change the current buffer + // - updating the screen + // As it may change buffers and window structures that are in use and cause + // freed memory to be used. + if (text_locked() || curbuf_locked() || updating_screen) + return; + + handle_drop_internal(); +} + +/* + * To be called when text is unlocked, curbuf is unlocked or updating_screen is + * reset: Handle a postponed drop. + */ + void +handle_any_postponed_drop(void) +{ + if (!drop_busy && drop_filev != NULL + && !text_locked() && !curbuf_locked() && !updating_screen) + handle_drop_internal(); +} +#endif + +/* + * 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) + vim_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_init2(&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); + vim_free(al); + } +} + +/* + * Create a new argument list and use it for the current window. + */ + void +alist_new(void) +{ + curwin->w_alist = (alist_T *)alloc((unsigned)sizeof(alist_T)); + if (curwin->w_alist == NULL) + { + curwin->w_alist = &global_alist; + ++global_alist.al_refcount; + } + else + { + curwin->w_alist->al_refcount = 1; + curwin->w_alist->id = ++max_alist_id; + 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 = (char_u **)alloc((unsigned)(sizeof(char_u *) * GARGCOUNT)); + if (old_arg_files != NULL) + { + 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; + static int recursive = 0; + + if (recursive) + { + emsg(_(e_au_recursive)); + return; + } + ++recursive; + + alist_clear(al); + if (ga_grow(&al->al_ga, count) == OK) + { + 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) + vim_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(); + } + vim_free(files); + } + else + FreeWild(count, files); + if (al == &global_alist) + arg_had_last = FALSE; + + --recursive; +} + +/* + * 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 UNUSED) +{ + 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; +#if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE) + char_u *fname = NULL; +#endif +#ifdef FEAT_BROWSE + int browse_flag = cmdmod.browse; +#endif + int use_tab = eap->cmdidx == CMD_tabedit + || eap->cmdidx == CMD_tabfind + || eap->cmdidx == CMD_tabnew; + +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + +#ifdef FEAT_QUICKFIX + /* 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; + } +#endif + +#ifdef FEAT_SEARCHPATH + 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; + } +# ifdef FEAT_BROWSE + else +# endif +#endif +#ifdef FEAT_BROWSE + if (cmdmod.browse + && eap->cmdidx != CMD_vnew + && eap->cmdidx != CMD_new) + { + if ( +# ifdef FEAT_GUI + !gui.in_use && +# endif + au_has_group((char_u *)"FileExplorer")) + { + /* No browsing supported but we do have the file explorer: + * Edit the directory. */ + if (*eap->arg == NUL || !mch_isdir(eap->arg)) + eap->arg = (char_u *)"."; + } + else + { + fname = do_browse(0, (char_u *)(use_tab + ? _("Edit File in new tab page") + : _("Edit File in new window")), + eap->arg, NULL, NULL, NULL, curbuf); + if (fname == NULL) + goto theend; + eap->arg = fname; + } + } + cmdmod.browse = FALSE; /* Don't browse again in do_ecmd(). */ +#endif + + /* + * Either open new tab page or split the window. + */ + if (use_tab) + { + 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 +# ifdef FEAT_BROWSE + || cmdmod.browse +# endif + ) + { + RESET_BINDING(curwin); + } + else + do_check_scrollbind(FALSE); + do_exedit(eap, old_curwin); + } + +# ifdef FEAT_BROWSE + cmdmod.browse = browse_flag; +# endif + +# if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE) +theend: + vim_free(fname); +# endif +} + +/* + * Open a new tab page. + */ + void +tabpage_new(void) +{ + exarg_T ea; + + vim_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) +{ + int tab_number; + + 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: + if (eap->arg && *eap->arg != NUL) + { + char_u *p = eap->arg; + char_u *p_save = p; + + tab_number = getdigits(&p); + if (p == p_save || *p_save == '-' || *p != NUL + || tab_number == 0) + { + /* No numbers as argument. */ + eap->errmsg = e_invarg; + return; + } + } + else + { + if (eap->addr_count == 0) + tab_number = 1; + else + { + tab_number = eap->line2; + if (tab_number < 1) + { + eap->errmsg = e_invrange; + return; + } + } + } + goto_tabpage(-tab_number); + break; + default: /* CMD_tabnext */ + tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) + goto_tabpage(tab_number); + break; + } +} + +/* + * :tabmove command + */ + static void +ex_tabmove(exarg_T *eap) +{ + int tab_number; + + tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) + tabpage_move(tab_number); +} + +/* + * :tabs command: List tabs and their contents. + */ + static void +ex_tabs(exarg_T *eap UNUSED) +{ + 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": Set screen mode. + * If no argument given, just get the screen size and redraw. + */ + static void +ex_mode(exarg_T *eap) +{ + if (*eap->arg == NUL) + shell_resized(); + else + mch_screenmode(eap->arg); +} + +/* + * ":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) + ; + } + +# ifdef FEAT_GUI + need_mouse_correct = TRUE; +# endif + n = atol((char *)eap->arg); + if (cmdmod.split & WSP_VERT) + { + if (*eap->arg == '-' || *eap->arg == '+') + n += curwin->w_width; + 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 high */ + n = 9999; + win_setheight_win((int)n, wp); + } +} + +/* + * ":find [+command] " command. + */ + static void +ex_find(exarg_T *eap) +{ +#ifdef FEAT_SEARCHPATH + 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) + { + vim_free(fname); + fname = find_file_in_path(NULL, 0, FNAME_MESS, + FALSE, curbuf->b_ffname); + } + } + + if (fname != NULL) + { + eap->arg = fname; +#endif + do_exedit(eap, NULL); +#ifdef FEAT_SEARCHPATH + vim_free(fname); + } +#endif +} + +/* + * ":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; +#ifdef FEAT_GUI + int he = hold_gui_events; +#endif + + 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; +#ifdef FEAT_GUI + hold_gui_events = 0; +#endif + must_redraw = CLEAR; + + main_loop(FALSE, TRUE); + + RedrawingDisabled = rd; + no_wait_return = nwr; + msg_scroll = ms; +#ifdef FEAT_GUI + hold_gui_events = he; +#endif + } + 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 +#ifdef FEAT_BROWSE + || cmdmod.browse +#endif + ) + { + /* 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, + (buf_hide(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 || buf_hide(curbuf)) + { +#if defined(FEAT_EVAL) + cleanup_T cs; + + /* Reset the error/interrupt/exception state here so that + * aborting() returns FALSE when closing a window. */ + enter_cleanup(&cs); +#endif +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + win_close(curwin, !need_hide && !buf_hide(curbuf)); + +#if defined(FEAT_EVAL) + /* Restore the error/interrupt/exception state if not + * discarded by a new aborting error, interrupt, or + * uncaught exception. */ + leave_cleanup(&cs); +#endif + } + } + } + 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); +#ifdef FEAT_TITLE + n = curwin->w_arg_idx_invalid; +#endif + check_arg_idx(curwin); +#ifdef FEAT_TITLE + if (n != curwin->w_arg_idx_invalid) + maketitle(); +#endif + } + + /* + * 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; +} + +#ifndef FEAT_GUI +/* + * ":gui" and ":gvim" when there is no GUI. + */ + static void +ex_nogui(exarg_T *eap) +{ + eap->errmsg = e_nogvim; +} +#endif + +#if defined(FEAT_GUI_W32) && defined(FEAT_MENU) && defined(FEAT_TEAROFF) + static void +ex_tearoff(exarg_T *eap) +{ + gui_make_tearoff(eap->arg); +} +#endif + +#if (defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \ + || defined(FEAT_TERM_POPUP_MENU)) && defined(FEAT_MENU) + static void +ex_popup(exarg_T *eap) +{ +# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) + if (gui.in_use) + gui_make_popup(eap->arg, eap->forceit); +# ifdef FEAT_TERM_POPUP_MENU + else +# endif +# endif +# ifdef FEAT_TERM_POPUP_MENU + pum_make_popup(eap->arg, eap->forceit); +# endif +} +#endif + + static void +ex_swapname(exarg_T *eap UNUSED) +{ + if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) + msg(_("No swap file")); + else + msg((char *)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 UNUSED) +{ + 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_ALL_WINDOWS(wp) + { + if (wp->w_p_scb && wp->w_buffer) + { + y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value(); + if (topline > y) + topline = y; + } + } + if (topline < 1) + topline = 1; + } + else + { + topline = 1; + } + + + /* + * Set all scrollbind windows to the same topline. + */ + FOR_ALL_WINDOWS(curwin) + { + 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; + +#ifdef FEAT_BROWSE + if (cmdmod.browse) + { + char_u *browseFile; + + browseFile = do_browse(0, (char_u *)_("Append File"), eap->arg, + NULL, NULL, NULL, curbuf); + if (browseFile != NULL) + { + i = readfile(browseFile, NULL, + eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); + vim_free(browseFile); + } + else + i = OK; + } + else +#endif + 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 != OK) + { +#if defined(FEAT_EVAL) + if (!aborting()) +#endif + semsg(_(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) +{ + VIM_CLEAR(prev_dir); + VIM_CLEAR(globaldir); +} +#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) +{ + VIM_CLEAR(curwin->w_localdir); + 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 (mch_dirname(NameBuff, MAXPATHL) == OK) + curwin->w_localdir = vim_strsave(NameBuff); + } + else + { + /* We are now in the global directory, no need to remember its + * name. */ + VIM_CLEAR(globaldir); + } + + shorten_fnames(TRUE); +} + + +/* + * ":cd", ":lcd", ":chdir" and ":lchdir". + */ + void +ex_cd(exarg_T *eap) +{ + char_u *new_dir; + char_u *tofree; + int dir_differs; + + new_dir = eap->arg; +#if !defined(UNIX) && !defined(VMS) + /* 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 (mch_dirname(NameBuff, MAXPATHL) == OK) + prev_dir = vim_strsave(NameBuff); + else + prev_dir = NULL; + +#if defined(UNIX) || defined(VMS) + /* for UNIX ":cd" means: go to home directory */ + if (*new_dir == NUL) + { + /* use NameBuff for home directory name */ +# ifdef VMS + char_u *p; + + p = mch_getenv((char_u *)"SYS$LOGIN"); + if (p == NULL || *p == NUL) /* empty is the same as not set */ + NameBuff[0] = NUL; + else + vim_strncpy(NameBuff, p, MAXPATHL - 1); +# else + expand_env((char_u *)"$HOME", NameBuff, MAXPATHL); +# endif + new_dir = NameBuff; + } +#endif + dir_differs = new_dir == NULL || prev_dir == NULL + || pathcmp((char *)prev_dir, (char *)new_dir, -1) != 0; + if (new_dir == NULL || (dir_differs && vim_chdir(new_dir))) + emsg(_(e_failed)); + else + { + int is_local_chdir = eap->cmdidx == CMD_lcd + || eap->cmdidx == CMD_lchdir; + + post_chdir(is_local_chdir); + + /* Echo the new current directory if the command was typed. */ + if (KeyTyped || p_verbose >= 5) + ex_pwd(eap); + + if (dir_differs) + apply_autocmds(EVENT_DIRCHANGED, + is_local_chdir ? (char_u *)"window" : (char_u *)"global", + new_dir, FALSE, curbuf); + } + vim_free(tofree); + } +} + +/* + * ":pwd". + */ + static void +ex_pwd(exarg_T *eap UNUSED) +{ + if (mch_dirname(NameBuff, MAXPATHL) == OK) + { +#ifdef BACKSLASH_IN_FILENAME + slash_adjust(NameBuff); +#endif + msg((char *)NameBuff); + } + else + emsg(_("E187: Unknown")); +} + +/* + * ":=". + */ + static void +ex_equal(exarg_T *eap) +{ + smsg("%ld", (long)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, curwin->w_wincol + curwin->w_wcol); + } + + len = eap->line2; + switch (*eap->arg) + { + case 'm': break; + case NUL: len *= 1000L; break; + default: semsg(_(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; + long wait_now; + + cursor_on(); + out_flush_cursor(FALSE, FALSE); + for (done = 0; !got_int && done < msec; done += wait_now) + { + wait_now = msec - done > 1000L ? 1000L : msec - done; +#ifdef FEAT_TIMERS + { + long due_time = check_due_timer(); + + if (due_time > 0 && due_time < wait_now) + wait_now = due_time; + } +#endif +#ifdef FEAT_JOB_CHANNEL + if (has_any_channel() && wait_now > 100L) + wait_now = 100L; +#endif + ui_delay(wait_now, TRUE); +#ifdef FEAT_JOB_CHANNEL + if (has_any_channel()) + ui_breakcheck_force(TRUE); + else +#endif + ui_breakcheck(); +#ifdef MESSAGE_QUEUE + /* Process the netbeans and clientserver messages that may have been + * received in the call to ui_breakcheck() when the GUI is in use. This + * may occur when running a test case. */ + parse_queued_messages(); +#endif + } + + // If CTRL-C was typed to interrupt the sleep, drop the CTRL-C from the + // input buffer, otherwise a following call to input() fails. + if (got_int) + (void)vpeekc(); +} + + 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(VMS) || defined(MSWIN) +/* + * ":winpos". + */ + static void +ex_winpos(exarg_T *eap) +{ + int x, y; + char_u *arg = eap->arg; + char_u *p; + + if (*arg == NUL) + { +# if defined(FEAT_GUI) || defined(MSWIN) +# ifdef FEAT_GUI + if (gui.in_use && gui_mch_get_winpos(&x, &y) != FAIL) +# else + if (mch_get_winpos(&x, &y) != FAIL) +# endif + { + sprintf((char *)IObuff, _("Window position: X %d, Y %d"), x, y); + msg((char *)IObuff); + } + else +# endif + 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 FEAT_GUI + if (gui.in_use) + gui_mch_set_winpos(x, y); + else if (gui.starting) + { + /* Remember the coordinates for when the window is opened. */ + gui_win_x = x; + gui_win_y = y; + } +# ifdef HAVE_TGETENT + else +# endif +# else +# ifdef MSWIN + mch_set_winpos(x, y); +# endif +# endif +# 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 ( +#ifdef FEAT_RIGHTLEFT + (eap->cmdidx == CMD_rshift) ^ curwin->w_p_rl +#else + eap->cmdidx == CMD_rshift +#endif + ) + 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, &eap->arg, eap->addr_type, FALSE, FALSE, FALSE, 1); + 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, 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; + check_cursor_col(); + +#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); +} + +#ifdef FEAT_PERSISTENT_UNDO + 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); +} +#endif + +/* + * ":redo". + */ + static void +ex_redo(exarg_T *eap UNUSED) +{ + 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) + semsg(_(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; + +#ifdef FEAT_EVAL + if (redir_execute) + { + emsg(_("E930: Cannot use :redir inside execute()")); + return; + } +#endif + + 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; +#ifdef FEAT_BROWSE + if (cmdmod.browse) + { + char_u *browseFile; + + browseFile = do_browse(BROWSE_SAVE, + (char_u *)_("Save Redirection"), + fname, NULL, NULL, + (char_u *)_(BROWSE_FILTER_ALL_FILES), curbuf); + if (browseFile == NULL) + return; /* operation cancelled */ + vim_free(fname); + fname = browseFile; + eap->forceit = TRUE; /* since dialog already asked */ + } +#endif + + redir_fd = open_exfile(fname, eap->forceit, mode); + vim_free(fname); + } +#ifdef FEAT_EVAL + else if (*arg == '@') + { + /* redirect to a register a-z (resp. A-Z for appending) */ + close_redir(); + ++arg; + if (ASCII_ISALPHA(*arg) +# ifdef FEAT_CLIPBOARD + || *arg == '*' + || *arg == '+' +# endif + || *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; + semsg(_(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; + } +#endif + + /* TODO: redirect to a buffer */ + + else + semsg(_(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 +#ifdef FEAT_EVAL + || redir_reg || redir_vname +#endif + ) + redir_off = FALSE; +} + +/* + * ":redraw": force redraw + */ + void +ex_redraw(exarg_T *eap) +{ + int r = RedrawingDisabled; + int p = p_lz; + + RedrawingDisabled = 0; + p_lz = FALSE; + validate_cursor(); + update_topline(); + update_screen(eap->forceit ? CLEAR : VIsual_active ? INVERTED : 0); +#ifdef FEAT_TITLE + if (need_maketitle) + maketitle(); +#endif + 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 UNUSED) +{ + 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(); +} + +/* + * ":redrawtabline": force redraw of the tabline + */ + static void +ex_redrawtabline(exarg_T *eap UNUSED) +{ + int r = RedrawingDisabled; + int p = p_lz; + + RedrawingDisabled = 0; + p_lz = FALSE; + + draw_tabline(); + + RedrawingDisabled = r; + p_lz = p; + out_flush(); +} + + static void +close_redir(void) +{ + if (redir_fd != NULL) + { + fclose(redir_fd); + redir_fd = NULL; + } +#ifdef FEAT_EVAL + redir_reg = 0; + if (redir_vname) + { + var_redir_stop(); + redir_vname = 0; + } +#endif +} + +#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; +#ifdef FEAT_BROWSE + char_u *browseFile = NULL; +#endif +#ifdef FEAT_SESSION + int view_session = FALSE; + int using_vdir = FALSE; /* using 'viewdir'? */ + char_u *viewFile = NULL; + unsigned *flagp; +#endif + + if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) + { +#ifdef FEAT_SESSION + view_session = TRUE; +#else + ex_ni(eap); + return; +#endif + } + +#ifdef FEAT_SESSION + /* 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 +#endif + if (*eap->arg != NUL) + fname = eap->arg; + else if (eap->cmdidx == CMD_mkvimrc) + fname = (char_u *)VIMRC_FILE; +#ifdef FEAT_SESSION + else if (eap->cmdidx == CMD_mksession) + fname = (char_u *)SESSION_FILE; +#endif + else + fname = (char_u *)EXRC_FILE; + +#ifdef FEAT_BROWSE + if (cmdmod.browse) + { + browseFile = do_browse(BROWSE_SAVE, +# ifdef FEAT_SESSION + eap->cmdidx == CMD_mkview ? (char_u *)_("Save View") : + eap->cmdidx == CMD_mksession ? (char_u *)_("Save Session") : +# endif + (char_u *)_("Save Setup"), + fname, (char_u *)"vim", NULL, + (char_u *)_(BROWSE_FILTER_MACROS), NULL); + if (browseFile == NULL) + goto theend; + fname = browseFile; + eap->forceit = TRUE; /* since dialog already asked */ + } +#endif + +#if defined(FEAT_SESSION) && defined(vim_mkdir) + /* When using 'viewdir' may have to create the directory. */ + if (using_vdir && !mch_isdir(p_vdir)) + vim_mkdir_emsg(p_vdir, 0755); +#endif + + fd = open_exfile(fname, eap->forceit, WRITEBIN); + if (fd != NULL) + { +#ifdef FEAT_SESSION + if (eap->cmdidx == CMD_mkview) + flagp = &vop_flags; + else + flagp = &ssop_flags; +#endif + +#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"); + +#ifdef FEAT_SESSION + if (eap->cmdidx == CMD_mksession) + { + if (put_line(fd, "let SessionLoad = 1") == FAIL) + failed = TRUE; + } + + if (eap->cmdidx != CMD_mkview) +#endif + { + /* 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"); + } + +#ifdef FEAT_SESSION + if (!view_session + || (eap->cmdidx == CMD_mksession + && (*flagp & SSOP_OPTIONS))) +#endif + failed |= (makemap(fd, NULL) == FAIL + || makeset(fd, OPT_GLOBAL, FALSE) == FAIL); + +#ifdef FEAT_SESSION + 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 = alloc(MAXPATHL); + if (dirnow == NULL) + failed = TRUE; + else + { + /* + * Change to session file's dir. + */ + if (mch_dirname(dirnow, MAXPATHL) == FAIL + || mch_chdir((char *)dirnow) != 0) + *dirnow = NUL; + if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) + { + if (vim_chdirfile(fname, NULL) == OK) + shorten_fnames(TRUE); + } + else if (*dirnow != NUL + && (ssop_flags & SSOP_CURDIR) && globaldir != NULL) + { + if (mch_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 (mch_chdir((char *)dirnow) != 0) + emsg(_(e_prev_dir)); + shorten_fnames(TRUE); + } + vim_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; +# ifdef FEAT_SEARCH_EXTRA + if (no_hlsearch && put_line(fd, "nohlsearch") == FAIL) + failed = TRUE; +# endif + if (put_line(fd, "doautoall SessionLoadPost") == FAIL) + failed = TRUE; + if (eap->cmdidx == CMD_mksession) + { + if (put_line(fd, "unlet SessionLoad") == FAIL) + failed = TRUE; + } + } +#endif + if (put_line(fd, "\" vim: set ft=vim :") == FAIL) + failed = TRUE; + + failed |= fclose(fd); + + if (failed) + emsg(_(e_write)); +#if defined(FEAT_EVAL) && defined(FEAT_SESSION) + else if (eap->cmdidx == CMD_mksession) + { + /* successful session write - set this_session var */ + char_u *tbuf; + + tbuf = alloc(MAXPATHL); + if (tbuf != NULL) + { + if (vim_FullName(fname, tbuf, MAXPATHL, FALSE) == OK) + set_vim_var_string(VV_THIS_SESSION, tbuf, -1); + vim_free(tbuf); + } + } +#endif +#ifdef MKSESSION_NL + mksession_nl = FALSE; +#endif + } + +#ifdef FEAT_BROWSE +theend: + vim_free(browseFile); +#endif +#ifdef FEAT_SESSION + vim_free(viewFile); +#endif +} + +#if ((defined(FEAT_SESSION) || defined(FEAT_EVAL)) && defined(vim_mkdir)) \ + || defined(PROTO) + int +vim_mkdir_emsg(char_u *name, int prot) +{ + if (vim_mkdir(name, prot) != 0) + { + semsg(_("E739: Cannot create directory: %s"), name); + return FAIL; + } + return OK; +} +#endif + +/* + * 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 (mch_isdir(fname)) + { + semsg(_(e_isadir2), fname); + return NULL; + } +#endif + if (!forceit && *mode != 'a' && vim_fexists(fname)) + { + semsg(_("E189: \"%s\" exists (add ! to override)"), fname); + return NULL; + } + + if ((fd = mch_fopen((char *)fname, mode)) == NULL) + semsg(_("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(); +} + +/* + * Save the current State and go to Normal mode. + * Return TRUE if the typeahead could be saved. + */ + int +save_current_state(save_state_T *sst) +{ + sst->save_msg_scroll = msg_scroll; + sst->save_restart_edit = restart_edit; + sst->save_msg_didout = msg_didout; + sst->save_State = State; + sst->save_insertmode = p_im; + sst->save_finish_op = finish_op; + sst->save_opcount = opcount; + + 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' */ + + /* + * 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(&sst->tabuf); + return sst->tabuf.typebuf_valid; +} + + void +restore_current_state(save_state_T *sst) +{ + /* Restore the previous typeahead. */ + restore_typeahead(&sst->tabuf); + + msg_scroll = sst->save_msg_scroll; + restart_edit = sst->save_restart_edit; + p_im = sst->save_insertmode; + finish_op = sst->save_finish_op; + opcount = sst->save_opcount; + msg_didout |= sst->save_msg_didout; /* don't reset msg_didout now */ + + /* Restore the state (needed when called from a function executed for + * 'indentexpr'). Update the mouse and cursor, they may have changed. */ + State = sst->save_State; +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may show different cursor shape */ +#endif +} + +/* + * ":normal[!] {commands}": Execute normal mode commands. + */ + void +ex_normal(exarg_T *eap) +{ + save_state_T save_state; + 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; + } + + /* + * 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) + { +#ifdef FEAT_GUI + if (*p == CSI) /* leadbyte CSI */ + len += 2; +#endif + for (l = (*mb_ptr2len)(p) - 1; l > 0; --l) + if (*++p == K_SPECIAL /* trailbyte K_SPECIAL or CSI */ +#ifdef FEAT_GUI + || *p == CSI +#endif + ) + len += 2; + } + if (len > 0) + { + arg = alloc((unsigned)(STRLEN(eap->arg) + len + 1)); + if (arg != NULL) + { + len = 0; + for (p = eap->arg; *p != NUL; ++p) + { + arg[len++] = *p; +#ifdef FEAT_GUI + if (*p == CSI) + { + arg[len++] = KS_EXTRA; + arg[len++] = (int)KE_CSI; + } +#endif + for (l = (*mb_ptr2len)(p) - 1; l > 0; --l) + { + arg[len++] = *++p; + if (*p == K_SPECIAL) + { + arg[len++] = KS_SPECIAL; + arg[len++] = KE_FILLER; + } +#ifdef FEAT_GUI + else if (*p == CSI) + { + arg[len++] = KS_EXTRA; + arg[len++] = (int)KE_CSI; + } +#endif + } + arg[len] = NUL; + } + } + } + } + + ++ex_normal_busy; + if (save_current_state(&save_state)) + { + /* + * 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; + check_cursor_moved(curwin); + } + + 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_current_state(&save_state); + --ex_normal_busy; +#ifdef FEAT_MOUSE + setmouse(); +#endif +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may show different cursor shape */ +#endif + + vim_free(arg); +} + +/* + * ":startinsert", ":startreplace" and ":startgreplace" + */ + static void +ex_startinsert(exarg_T *eap) +{ + if (eap->forceit) + { + /* cursor line can be zero on startup */ + if (!curwin->w_cursor.lnum) + curwin->w_cursor.lnum = 1; + 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 UNUSED) +{ + restart_edit = 0; + stop_insert_mode = TRUE; + clearmode(); +} + +/* + * Execute normal mode command "cmd". + * "remap" can be REMAP_NONE or REMAP_YES. + */ + void +exec_normal_cmd(char_u *cmd, int remap, int silent) +{ + /* Stuff the argument into the typeahead buffer. */ + ins_typebuf(cmd, remap, 0, TRUE, silent); + exec_normal(FALSE, FALSE, FALSE); +} + +/* + * Execute normal_cmd() until there is no typeahead left. + * When "use_vpeekc" is TRUE use vpeekc() to check for available chars. + */ + void +exec_normal(int was_typed, int use_vpeekc, int may_use_terminal_loop UNUSED) +{ + oparg_T oa; + + clear_oparg(&oa); + finish_op = FALSE; + while ((!stuff_empty() + || ((was_typed || !typebuf_typed()) && typebuf.tb_len > 0) + || (use_vpeekc && vpeekc() != NUL)) + && !got_int) + { + update_topline_cursor(); +#ifdef FEAT_TERMINAL + if (may_use_terminal_loop && term_use_loop() + && oa.op_type == OP_NOP && oa.regname == NUL + && !VIsual_active) + { + /* If terminal_loop() returns OK we got a key that is handled + * in Normal model. With FAIL we first need to position the + * cursor and the screen needs to be redrawn. */ + if (terminal_loop(TRUE) == OK) + normal_cmd(&oa, TRUE); + } + else +#endif + /* execute a Normal mode cmd */ + normal_cmd(&oa, TRUE); + } +} + +#ifdef FEAT_FIND_ID + 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); +} + +#if defined(FEAT_QUICKFIX) +/* + * ":psearch" + */ + static void +ex_psearch(exarg_T *eap) +{ + g_do_tagpreview = p_pvh; + ex_findpat(eap); + g_do_tagpreview = 0; +} +#endif + + 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); +} +#endif + + +#ifdef FEAT_QUICKFIX +/* + * ":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 = bt_help(curwin_save->w_buffer); + 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; +} +#endif + +/* + * ":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" */ +#ifdef FEAT_CSCOPE + if (p_cst && *eap->arg != NUL) + { + ex_cstag(eap); + return; + } +#endif + cmd = DT_TAG; + break; + } + + if (name[0] == 'l') + { +#ifndef FEAT_QUICKFIX + ex_ni(eap); + return; +#else + cmd = DT_LTAG; +#endif + } + + 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 (SPEC_PERC + 1) + "", /* cursor word */ +#define SPEC_CWORD (SPEC_HASH + 1) + "", /* cursor WORD */ +#define SPEC_CCWORD (SPEC_CWORD + 1) + "", /* expr under cursor */ +#define SPEC_CEXPR (SPEC_CCWORD + 1) + "", /* cursor path name */ +#define SPEC_CFILE (SPEC_CEXPR + 1) + "", /* ":so" file name */ +#define SPEC_SFILE (SPEC_CFILE + 1) + "", /* ":so" file line number */ +#define SPEC_SLNUM (SPEC_SFILE + 1) + "", /* autocommand file name */ +#define SPEC_AFILE (SPEC_SLNUM + 1) + "", /* autocommand buffer number */ +#define SPEC_ABUF (SPEC_AFILE + 1) + "", /* autocommand match name */ +#define SPEC_AMATCH (SPEC_ABUF + 1) + "", /* script file line number */ +#define SPEC_SFLNUM (SPEC_AMATCH + 1) +#ifdef FEAT_CLIENTSERVER + "" +# define SPEC_CLIENT (SPEC_SFLNUM + 1) +#endif + }; + + 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 **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; +#ifdef FEAT_MODIFY_FNAME + int tilde_file = FALSE; + int skip_mod = FALSE; +#endif + 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 + || spec_idx == SPEC_CEXPR) + { + resultlen = find_ident_under_cursor(&result, + spec_idx == SPEC_CWORD ? (FIND_IDENT | FIND_STRING) + : spec_idx == SPEC_CEXPR ? (FIND_IDENT | FIND_STRING | FIND_EVAL) + : FIND_STRING); + if (resultlen == 0) + { + *errormsg = ""; + 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; +#ifdef FEAT_MODIFY_FNAME + tilde_file = STRCMP(result, "~") == 0; +#endif + } + 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; +#ifdef FEAT_MODIFY_FNAME + skip_mod = TRUE; +#endif + break; + } + s = src + 1; + if (*s == '<') /* "#<99" uses v:oldfiles */ + ++s; + i = (int)getdigits(&s); + if (s == src + 2 && src[1] == '-') + /* just a minus sign, don't skip over it */ + s--; + *usedlen = (int)(s - src); /* length of what we expand */ + + if (src[1] == '<' && i != 0) + { + if (*usedlen < 2) + { + /* Should we give an error message for # 1) + *usedlen = 1; + buf = buflist_findnr(i); + if (buf == NULL) + { + *errormsg = _("E194: No alternate file name to substitute for '#'"); + return NULL; + } + if (lnump != NULL) + *lnump = ECMD_LAST; + if (buf->b_fname == NULL) + { + result = (char_u *)""; + valid = 0; /* Must have ":p:h" to be valid */ + } + else + { + result = buf->b_fname; +#ifdef FEAT_MODIFY_FNAME + tilde_file = STRCMP(result, "~") == 0; +#endif + } + } + break; + +#ifdef FEAT_SEARCHPATH + case SPEC_CFILE: /* file name under cursor */ + result = file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL); + if (result == NULL) + { + *errormsg = ""; + return NULL; + } + resultbuf = result; /* remember allocated string */ + break; +#endif + + 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); + vim_free(autocmd_fname); + autocmd_fname = result; + } + if (result == NULL) + { + *errormsg = _("E495: no autocommand file name to substitute for \"\""); + return NULL; + } + result = shorten_fname1(result); + break; + + case SPEC_ABUF: /* buffer number for autocommand */ + if (autocmd_bufnr <= 0) + { + *errormsg = _("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 = _("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 = _("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 = _("E842: no line number to use for \"\""); + return NULL; + } + sprintf((char *)strbuf, "%ld", (long)sourcing_lnum); + result = strbuf; + break; + +#ifdef FEAT_EVAL + case SPEC_SFLNUM: /* line in script file */ + if (current_sctx.sc_lnum + sourcing_lnum == 0) + { + *errormsg = _("E961: no line number to use for \"\""); + return NULL; + } + sprintf((char *)strbuf, "%ld", + (long)(current_sctx.sc_lnum + sourcing_lnum)); + result = strbuf; + break; +#endif + +#ifdef FEAT_CLIENTSERVER + case SPEC_CLIENT: /* Source of last submitted input */ + sprintf((char *)strbuf, PRINTF_HEX_LONG_U, + (long_u)clientWindow); + result = strbuf; + break; +#endif + + default: + result = (char_u *)""; /* avoid gcc warning */ + 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 >= gettail(result)) + resultlen = (int)(s - result); + } +#ifdef FEAT_MODIFY_FNAME + else if (!skip_mod) + { + valid |= modify_fname(src, tilde_file, usedlen, &result, &resultbuf, + &resultlen); + if (result == NULL) + { + *errormsg = ""; + return NULL; + } + } +#endif + } + + if (resultlen == 0 || valid != VALID_HEAD + VALID_PATH) + { + if (valid != VALID_HEAD + VALID_PATH) + /* xgettext:no-c-format */ + *errormsg = _("E499: Empty file name for '%' or '#', only works with \":p:h\""); + else + *errormsg = _("E500: Evaluates to an empty string"); + result = NULL; + } + else + result = vim_strnsave(result, resultlen); + vim_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. + * Returns NULL when out of memory. + */ + 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 == ' ' +#ifndef BACKSLASH_IN_FILENAME + || *p == '\\' +#endif + || *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 = alloc((unsigned)len + 1); + if (retval == NULL) + break; + } + + return retval; +} + +/* + * Expand the string in "arg". + * + * Returns an allocated string, or NULL for any error. + */ + char_u * +expand_sfile(char_u *arg) +{ + char *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); + vim_free(result); + return NULL; + } + if (repl == NULL) /* no match (cannot happen) */ + { + p += srclen; + continue; + } + len = (int)STRLEN(result) - srclen + (int)STRLEN(repl) + 1; + newres = alloc(len); + if (newres == NULL) + { + vim_free(repl); + vim_free(result); + return NULL; + } + mch_memmove(newres, result, (size_t)(p - result)); + STRCPY(newres + (p - result), repl); + len = (int)STRLEN(newres); + STRCAT(newres, p + srclen); + vim_free(repl); + vim_free(result); + result = newres; + p = newres + len; /* continue after the match */ + } + } + + return result; +} + +#ifdef FEAT_SESSION +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, int add_eol); + +/* + * 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 restore_size = TRUE; + win_T *wp; + char_u *sname; + win_T *edited_win = NULL; + int tabnr; + int restore_stal = FALSE; + 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. + */ +#ifdef FEAT_EVAL + 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; +#endif + + /* + * Close all windows and tabs but one. + */ + if (put_line(fd, "silent only") == FAIL) + return FAIL; + if ((ssop_flags & SSOP_TABPAGES) + && put_line(fd, "silent tabonly") == 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) + { + vim_free(sname); + return FAIL; + } + vim_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; + + /* 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=%ld columns=%ld" , Rows, Columns) < 0 + || put_eol(fd) == FAIL) + return FAIL; + } + +#ifdef FEAT_GUI + if (gui.in_use && (ssop_flags & SSOP_WINPOS)) + { + int x, y; + + if (gui_mch_get_winpos(&x, &y) == OK) + { + /* Note: after the restore we still check it worked!*/ + if (fprintf(fd, "winpos %d %d", x, y) < 0 || put_eol(fd) == FAIL) + return FAIL; + } + } +#endif + + /* + * When there are two or more tabpages and 'showtabline' is 1 the tabline + * will be displayed when creating the next tab. That resizes the windows + * in the first tab, which may cause problems. Set 'showtabline' to 2 + * temporarily to avoid that. + */ + if (p_stal == 1 && first_tabpage->tp_next != NULL) + { + if (put_line(fd, "set stal=2") == FAIL) + return FAIL; + restore_stal = TRUE; + } + + /* + * 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; + if ((ssop_flags & SSOP_TABPAGES)) + { + tabpage_T *tp; + + // Similar to ses_win_rec() below, populate the tab pages first so + // later local options won't be copied to the new tabs. + FOR_ALL_TABPAGES(tp) + if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL) + return FAIL; + if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL) + return FAIL; + } + for (tabnr = 1; ; ++tabnr) + { + int need_tabnext = FALSE; + int cnr = 1; + + 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_tabnext = 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 + && !bt_help(wp->w_buffer) +#ifdef FEAT_QUICKFIX + && !bt_nofile(wp->w_buffer) +#endif + ) + { + if (need_tabnext && put_line(fd, "tabnext") == FAIL) + return FAIL; + need_tabnext = FALSE; + + if (fputs("edit ", fd) < 0 + || ses_fname(fd, wp->w_buffer, &ssop_flags, TRUE) + == FAIL) + return FAIL; + if (!wp->w_arg_idx_invalid) + edited_win = wp; + break; + } + } + + /* If no file got edited create an empty tab page. */ + if (need_tabnext && put_line(fd, "tabnext") == 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. + * winminheight and winminwidth need to be set to avoid an error if the + * user has set winheight or winwidth. + */ + if (put_line(fd, "set winminheight=0") == FAIL + || put_line(fd, "set winheight=1") == FAIL + || put_line(fd, "set winminwidth=0") == FAIL + || put_line(fd, "set 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; + } + if (restore_stal && put_line(fd, "set stal=1") == FAIL) + return FAIL; + + // Now put the remaining buffers into the buffer list. + // This is near the end, so that when 'hidden' is set we don't create extra + // buffers. If the buffer was already created with another command the + // ":badd" will have no effect. + FOR_ALL_BUFFERS(buf) + { + if (!(only_save_windows && buf->b_nwindows == 0) + && !(buf->b_help && !(ssop_flags & SSOP_HELP)) +#ifdef FEAT_TERMINAL + // Skip terminal buffers: finished ones are not useful, others + // will be resurrected and result in a new buffer. + && !bt_terminal(buf) +#endif + && buf->b_fname != NULL + && buf->b_p_bl) + { + if (fprintf(fd, "badd +%ld ", buf->b_wininfo == NULL ? 1L + : buf->b_wininfo->wi_fpos.lnum) < 0 + || ses_fname(fd, buf, &ssop_flags, TRUE) == FAIL) + return FAIL; + } + } + + /* + * Wipe out an empty unnamed buffer we started in. + */ + if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0") + == 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=%ld winwidth=%ld shortmess=%s", + p_wh, p_wiw, p_shm) < 0 || put_eol(fd) == FAIL) + return FAIL; + /* Re-apply 'winminheight' and 'winminwidth'. */ + if (fprintf(fd, "set winminheight=%ld winminwidth=%ld", + p_wmh, p_wmw) < 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 * %ld + %ld) / %ld)", + n, (long)wp->w_height, Rows / 2, 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 * %ld + %ld) / %ld)", + n, (long)wp->w_width, Columns / 2, 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_ALL_FRAMES(frc, fr) + 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_ALL_FRAMES(frc, fr->fr_child) + 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) +{ +#ifdef FEAT_TERMINAL + if (bt_terminal(wp->w_buffer)) + return !term_is_finished(wp->w_buffer) + && (ssop_flags & SSOP_TERMINAL) + && term_should_restore(wp->w_buffer); +#endif + if (wp->w_buffer->b_fname == NULL +#ifdef FEAT_QUICKFIX + /* When 'buftype' is "nofile" can't restore the window contents. */ + || bt_nofile(wp->w_buffer) +#endif + ) + return (ssop_flags & SSOP_BLANK); + if (bt_help(wp->w_buffer)) + return (ssop_flags & SSOP_HELP); + return TRUE; +} + + static int +put_view_curpos(FILE *fd, win_T *wp, char *spaces) +{ + int r; + + if (wp->w_curswant == MAXCOL) + r = fprintf(fd, "%snormal! $", spaces); + else + r = fprintf(fd, "%snormal! 0%d|", spaces, wp->w_virtcol + 1); + return r < 0 || put_eol(fd) == FAIL ? FALSE : OK; +} + +/* + * 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, "%ldargu", (long)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)) + { +# ifdef FEAT_TERMINAL + if (bt_terminal(wp->w_buffer)) + { + if (term_write_session(fd, wp) == FAIL) + return FAIL; + } + else +# endif + /* + * Load the file. + */ + if (wp->w_buffer->b_ffname != NULL +# ifdef FEAT_QUICKFIX + && !bt_nofile(wp->w_buffer) +# endif + ) + { + /* + * Editing a file in this buffer: use ":edit file". + * This may have side effects! (e.g., compressed or network file). + * + * Note, if a buffer for that file already exists, use :badd to + * edit that buffer, to not lose folding information (:edit resets + * folds in other buffers) + */ + if (fputs("if bufexists(\"", fd) < 0 + || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL + || fputs("\") | buffer ", fd) < 0 + || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL + || fputs(" | else | edit ", fd) < 0 + || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL + || fputs(" | endif", fd) < 0 + || put_eol(fd) == FAIL) + return FAIL; + } + else + { + /* No file in this buffer, just make it empty. */ + if (put_line(fd, "enew") == FAIL) + return FAIL; +#ifdef FEAT_QUICKFIX + 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, TRUE) == FAIL) + return FAIL; + } +#endif + 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)); +#ifdef FEAT_FOLDING + else if (*flagp & SSOP_FOLDS) + f = makefoldset(fd); +#endif + else + f = OK; + curwin = save_curwin; + curbuf = curwin->w_buffer; + if (f == FAIL) + return FAIL; + +#ifdef FEAT_FOLDING + /* + * Save Folds when 'buftype' is empty and for help files. + */ + if ((*flagp & SSOP_FOLDS) + && wp->w_buffer->b_ffname != NULL + && (bt_normal(wp->w_buffer) || bt_help(wp->w_buffer))) + { + if (put_folds(fd, wp) == FAIL) + return FAIL; + } +#endif + + /* + * 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 = %ld - ((%ld * winheight(0) + %ld) / %ld)", + (long)wp->w_cursor.lnum, + (long)(wp->w_cursor.lnum - wp->w_topline), + (long)wp->w_height / 2, (long)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, "%ld", (long)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 = %ld - ((%ld * winwidth(0) + %ld) / %ld)", + (long)wp->w_virtcol + 1, + (long)(wp->w_virtcol - wp->w_leftcol), + (long)wp->w_width / 2, (long)wp->w_width) < 0 + || put_eol(fd) == FAIL + || put_line(fd, "if s:c > 0") == FAIL + || fprintf(fd, + " exe 'normal! ' . s:c . '|zs' . %ld . '|'", + (long)wp->w_virtcol + 1) < 0 + || put_eol(fd) == FAIL + || put_line(fd, "else") == FAIL + || put_view_curpos(fd, wp, " ") == FAIL + || put_line(fd, "endif") == FAIL) + return FAIL; + } + else if (put_view_curpos(fd, wp, "") == FAIL) + return FAIL; + } + } + + /* + * Local directory, if the current flag is not view options or the "curdir" + * option is included. + */ + if (wp->w_localdir != NULL + && (flagp != &vop_flags || (*flagp & SSOP_CURDIR))) + { + 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, "%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 = alloc(MAXPATHL); + if (buf != NULL) + { + (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) + { + vim_free(buf); + return FAIL; + } + vim_free(buf); + } + } + return OK; +} + +/* + * Write a buffer name to the session file. + * Also ends the line, if "add_eol" is TRUE. + * Returns FAIL if writing fails. + */ + static int +ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, int add_eol) +{ + 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)) +#ifdef FEAT_AUTOCHDIR + && !p_acd +#endif + && !did_lcd) + name = buf->b_sfname; + else + name = buf->b_ffname; + if (ses_put_fname(fd, name, flagp) == FAIL + || (add_eol && 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); + vim_free(sname); + if (p == NULL) + return FAIL; + + /* write the result */ + if (fputs((char *)p, fd) < 0) + retval = FAIL; + + vim_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); + vim_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 = alloc((unsigned)(STRLEN(sname) + len + STRLEN(p_vdir) + 9)); + if (retval != NULL) + { + 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) || defined(AMIGA) || defined(VMS) + if (*p == ':') + *s++ = '-'; + else +#endif + *s++ = '+'; + } + else + *s++ = *p; + } + *s++ = '='; + *s++ = c; + STRCPY(s, ".vim"); + } + + vim_free(sname); + return retval; +} + +#endif /* FEAT_SESSION */ + +/* + * 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; +} + +#ifdef FEAT_VIMINFO +/* + * ":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; +} +#endif + +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO) +/* + * 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); +} +#endif + +/* + * ":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 + semsg(_(e_invarg2), eap->arg); +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) +/* + * Function given to ExpandGeneric() to obtain the possible arguments of the + * ":behave {mswin,xterm}" command. + */ + char_u * +get_behave_arg(expand_T *xp UNUSED, int idx) +{ + if (idx == 0) + return (char_u *)"mswin"; + if (idx == 1) + return (char_u *)"xterm"; + return NULL; +} + +/* + * Function given to ExpandGeneric() to obtain the possible arguments of the + * ":messages {clear}" command. + */ + char_u * +get_messages_arg(expand_T *xp UNUSED, int idx) +{ + if (idx == 0) + return (char_u *)"clear"; + return NULL; +} +#endif + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + char_u * +get_mapclear_arg(expand_T *xp UNUSED, int idx) +{ + if (idx == 0) + return (char_u *)""; + return NULL; +} +#endif + +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("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, DIP_ALL); + filetype_detect = TRUE; + if (plugin) + { + source_runtime((char_u *)FTPLUGIN_FILE, DIP_ALL); + filetype_plugin = TRUE; + } + if (indent) + { + source_runtime((char_u *)INDENT_FILE, DIP_ALL); + filetype_indent = TRUE; + } + } + if (*arg == 'd') + { + (void)do_doautocmd((char_u *)"filetypedetect BufRead", TRUE, NULL); + do_modelines(0); + } + } + else if (STRCMP(arg, "off") == 0) + { + if (plugin || indent) + { + if (plugin) + { + source_runtime((char_u *)FTPLUGOF_FILE, DIP_ALL); + filetype_plugin = FALSE; + } + if (indent) + { + source_runtime((char_u *)INDOFF_FILE, DIP_ALL); + filetype_indent = FALSE; + } + } + else + { + source_runtime((char_u *)FTOFF_FILE, DIP_ALL); + filetype_detect = FALSE; + } + } + else + semsg(_(e_invarg2), arg); +} + +/* + * ":setfiletype [FALLBACK] {name}" + */ + static void +ex_setfiletype(exarg_T *eap) +{ + if (!did_filetype) + { + char_u *arg = eap->arg; + + if (STRNCMP(arg, "FALLBACK ", 9) == 0) + arg += 9; + + set_option_value((char_u *)"filetype", 0L, arg, OPT_LOCAL); + if (arg != eap->arg) + did_filetype = FALSE; + } +} + + static void +ex_digraphs(exarg_T *eap UNUSED) +{ +#ifdef FEAT_DIGRAPHS + if (*eap->arg != NUL) + putdigraph(eap->arg); + else + listdigraphs(eap->forceit); +#else + emsg(_("E196: No digraphs in this version")); +#endif +} + + 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; +#if defined(FEAT_EVAL) && defined(FEAT_BROWSE) + if (cmdmod.browse && flags == 0) + ex_options(eap); + else +#endif + (void)do_set(eap->arg, flags); +} + +#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) + void +set_no_hlsearch(int flag) +{ + no_hlsearch = flag; +# ifdef FEAT_EVAL + set_vim_var_nr(VV_HLSEARCH, !no_hlsearch && p_hls); +# endif +} + +/* + * ":nohlsearch" + */ + static void +ex_nohlsearch(exarg_T *eap UNUSED) +{ + 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. */ + vim_free(g); + semsg(_(e_invarg2), eap->arg); + return; + } + end = skip_regexp(p + 1, *p, TRUE, NULL); + if (!eap->skip) + { + if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) + { + vim_free(g); + eap->errmsg = e_trailing; + return; + } + if (*end != *p) + { + vim_free(g); + semsg(_(e_invarg2), p); + return; + } + + c = *end; + *end = NUL; + match_add(curwin, g, p + 1, 10, id, NULL, NULL); + vim_free(g); + *end = c; + } + } + eap->nextcmd = find_nextcmd(end); +} +#endif + +#ifdef FEAT_CRYPT +/* + * ":X": Get crypt key + */ + static void +ex_X(exarg_T *eap UNUSED) +{ + crypt_check_current_method(); + (void)crypt_get_key(TRUE, TRUE); +} +#endif + +#ifdef FEAT_FOLDING + 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; + +#ifdef FEAT_CLIPBOARD + start_global_changes(); +#endif + + /* 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 */ +#ifdef FEAT_CLIPBOARD + end_global_changes(); +#endif +} +#endif + +#ifdef FEAT_QUICKFIX +/* + * Returns TRUE if the supplied Ex cmdidx is for a location list command + * instead of a quickfix command. + */ + int +is_loclist_cmd(int cmdidx) +{ + if (cmdidx < 0 || cmdidx >= CMD_SIZE) + return FALSE; + return cmdnames[cmdidx].cmd_name[0] == 'l'; +} +#endif + +# if defined(FEAT_TIMERS) || defined(PROTO) + int +get_pressedreturn(void) +{ + return ex_pressedreturn; +} + + void +set_pressedreturn(int val) +{ + ex_pressedreturn = val; +} +#endif diff --git a/src/ex_eval.c b/src/ex_eval.c new file mode 100644 index 0000000..63bca67 --- /dev/null +++ b/src/ex_eval.c @@ -0,0 +1,2294 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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" + +#if defined(FEAT_EVAL) || defined(PROTO) + +static int throw_exception(void *, except_type_T, char_u *); +static char *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. + */ +#if 0 +/* Expressions used for testing during the development phase. */ +# define THROW_ON_ERROR (!eval_to_number("$VIMNOERRTHROW")) +# define THROW_ON_INTERRUPT (!eval_to_number("$VIMNOINTTHROW")) +# define THROW_TEST +#else +/* 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 +#endif + +/* + * 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 = (char *)vim_strsave(mesg); + if (elem->msg == NULL) + { + vim_free(elem); + suppress_errthrow = TRUE; + emsg(_(e_outofmem)); + } + else + { + elem->next = NULL; + elem->throw_msg = NULL; + *plist = elem; + if (plist == msg_list || severe) + { + char *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; + vim_free(messages->msg); + vim_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 * +get_exception_string( + void *value, + except_type_T type, + char_u *cmdname, + int *should_free) +{ + char *ret; + char *mesg; + int cmdlen; + char *p, *val; + + if (type == ET_ERROR) + { + *should_free = TRUE; + mesg = ((struct msglist *)value)->throw_msg; + if (cmdname != NULL && *cmdname != NUL) + { + cmdlen = (int)STRLEN(cmdname); + ret = (char *)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 = (char *)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 = 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, except_type_T 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) + vim_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(_("Exception thrown: %s"), excp->value); + msg_puts("\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: + vim_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) + { + internal_error("discard_exception()"); + 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 + ? _("Exception finished: %s") + : _("Exception discarded: %s"), + excp->value); + msg_puts("\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); + vim_free(saved_IObuff); + } + if (excp->type != ET_INTERRUPT) + vim_free(excp->value); + if (excp->type == ET_ERROR) + free_msglist(excp->messages); + vim_free(excp->throw_name); + vim_free(excp); +} + +/* + * Discard the exception currently being thrown. + */ + void +discard_current_exception(void) +{ + if (current_exception != NULL) + { + 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, (char_u *)excp->value, -1); + if (*excp->throw_name != NUL) + { + if (excp->throw_lnum != 0) + vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %ld"), + excp->throw_name, (long)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(_("Exception caught: %s"), excp->value); + msg_puts("\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) + internal_error("finish_exception()"); + caught_stack = caught_stack->caught; + if (caught_stack != NULL) + { + set_vim_var_string(VV_EXCEPTION, (char_u *)caught_stack->value, -1); + if (*caught_stack->throw_name != NUL) + { + if (caught_stack->throw_lnum != 0) + vim_snprintf((char *)IObuff, IOSIZE, + _("%s, line %ld"), caught_stack->throw_name, + (long)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 *mesg; + char *s; + int save_msg_silent; + + + switch (action) + { + case RP_MAKE: + mesg = _("%s made pending"); + break; + case RP_RESUME: + mesg = _("%s resumed"); + break; + /* case RP_DISCARD: */ + default: + mesg = _("%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, mesg, _("Exception")); + mesg = (char *)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, s); + msg_puts("\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) + vim_free(s); + else if (pending & CSTP_THROW) + vim_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 = 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 = 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 = N_("E581: :else without :if"); + return; + } + eap->errmsg = N_("E582: :elseif without :if"); + skip = TRUE; + } + else if (cstack->cs_flags[cstack->cs_idx] & CSF_ELSE) + { + if (eap->cmdidx == CMD_else) + { + eap->errmsg = N_("E583: multiple :else"); + return; + } + eap->errmsg = 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 = 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 = 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 = 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 *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 = _("E732: Using :endfor with :while"); + else if (fl & CSF_FOR) + eap->errmsg = _("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) + vim_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; + } +#if 0 + /* TODO: Add optimization below. Not yet done because of interface + * problems to eval.c and ex_cmds2.c. (Servatius) */ + else + { + /* + * There are no catch clauses to check or finally clauses to execute. + * End the current script or function. The exception will be rethrown + * in the caller. + */ + if (getline_equal(eap->getline, eap->cookie, get_func_line)) + current_funccal->returned = TRUE; + elseif (eap->get_func_line == getsourceline) + ((struct source_cookie *)eap->cookie)->finished = TRUE; + } +#endif + + 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 = 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 = 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 = 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 *)""; + /* Disable error messages, it will make current_exception + * invalid. */ + ++emsg_off; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + --emsg_off; + regmatch.rm_ic = FALSE; + if (end != NULL) + *end = save_char; + p_cpo = save_cpo; + if (regmatch.regprog == NULL) + semsg(_(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, + (char_u *)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) + internal_error("ex_catch()"); + } + 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 = 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 = 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) + internal_error("ex_finally()"); + } + + /* + * 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 = 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; + current_exception = NULL; + } + 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; + vim_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 * +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 UNUSED) +{ + 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; +} + +#endif /* FEAT_EVAL */ diff --git a/src/ex_getln.c b/src/ex_getln.c new file mode 100644 index 0000000..cba082a --- /dev/null +++ b/src/ex_getln.c @@ -0,0 +1,7484 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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 "vim.h" + +#ifndef MAX +# define MAX(x,y) ((x) > (y) ? (x) : (y)) +#endif + +/* + * 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 */ +# ifdef FEAT_EVAL + char_u *xp_arg; /* user-defined expansion arg */ + int input_fn; /* when TRUE Invoked for input() function */ +# endif +}; + +// 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(). +static struct cmdline_info ccline; + +static int cmd_showtail; /* Only show path tail in lists ? */ + +#ifdef FEAT_EVAL +static int new_cmdpos; /* position set by set_cmdline_pos() */ +#endif + +static int extra_char = NUL; /* extra character to display when redrawing + * the command line */ +static int extra_char_shift; + +#ifdef FEAT_CMDHIST +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 */ + time_t time_set; /* when it was typed, zero if unknown */ +} 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); +#endif + +#ifdef FEAT_RIGHTLEFT +static int cmd_hkmap = 0; /* Hebrew mapping during command line */ +#endif + +#ifdef FEAT_FKMAP +static int cmd_fkmap = 0; /* Farsi mapping during command line */ +#endif + +static char_u *getcmdline_int(int firstc, long count, int indent, int init_ccline); +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); +#ifdef FEAT_WILDMENU +static void cmdline_del(int from); +#endif +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); +#ifdef FEAT_CMDL_COMPL +static int expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int flagsarg); +static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char *dirname[]); +static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file); +# ifdef FEAT_CMDHIST +static char_u *get_history_arg(expand_T *xp, int idx); +# endif +# if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) +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); +# endif +#endif +#ifdef FEAT_CMDHIST +static void clear_hist_entry(histentry_T *hisptr); +#endif + +#ifdef FEAT_CMDWIN +static int open_cmdwin(void); +#endif + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) +static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +sort_func_compare(const void *s1, const void *s2); +#endif + + + static void +trigger_cmd_autocmd(int typechar, int evt) +{ + char_u typestr[2]; + + typestr[0] = typechar; + typestr[1] = NUL; + apply_autocmds(evt, typestr, typestr, FALSE, curbuf); +} + +/* + * Abandon the command line. + */ + static void +abandon_cmdline(void) +{ + VIM_CLEAR(ccline.cmdbuff); + if (msg_scrolled == 0) + compute_cmdrow(); + msg(""); + redraw_cmdline = TRUE; +} + +#ifdef FEAT_SEARCH_EXTRA +/* + * Guess that the pattern matches everything. Only finds specific cases, such + * as a trailing \|, which can happen while typing a pattern. + */ + static int +empty_pattern(char_u *p) +{ + size_t n = STRLEN(p); + + /* remove trailing \v and the like */ + while (n >= 2 && p[n - 2] == '\\' + && vim_strchr((char_u *)"mMvVcCZ", p[n - 1]) != NULL) + n -= 2; + return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|'); +} + +// Struct to store the viewstate during 'incsearch' highlighting. +typedef struct { + colnr_T vs_curswant; + colnr_T vs_leftcol; + linenr_T vs_topline; +# ifdef FEAT_DIFF + int vs_topfill; +# endif + linenr_T vs_botline; + linenr_T vs_empty_rows; +} viewstate_T; + + static void +save_viewstate(viewstate_T *vs) +{ + vs->vs_curswant = curwin->w_curswant; + vs->vs_leftcol = curwin->w_leftcol; + vs->vs_topline = curwin->w_topline; +# ifdef FEAT_DIFF + vs->vs_topfill = curwin->w_topfill; +# endif + vs->vs_botline = curwin->w_botline; + vs->vs_empty_rows = curwin->w_empty_rows; +} + + static void +restore_viewstate(viewstate_T *vs) +{ + curwin->w_curswant = vs->vs_curswant; + curwin->w_leftcol = vs->vs_leftcol; + curwin->w_topline = vs->vs_topline; +# ifdef FEAT_DIFF + curwin->w_topfill = vs->vs_topfill; +# endif + curwin->w_botline = vs->vs_botline; + curwin->w_empty_rows = vs->vs_empty_rows; +} + +// Struct to store the state of 'incsearch' highlighting. +typedef struct { + pos_T search_start; // where 'incsearch' starts searching + pos_T save_cursor; + viewstate_T init_viewstate; + viewstate_T old_viewstate; + pos_T match_start; + pos_T match_end; + int did_incsearch; + int incsearch_postponed; + int magic_save; +} incsearch_state_T; + + static void +init_incsearch_state(incsearch_state_T *is_state) +{ + is_state->match_start = curwin->w_cursor; + is_state->did_incsearch = FALSE; + is_state->incsearch_postponed = FALSE; + is_state->magic_save = p_magic; + CLEAR_POS(&is_state->match_end); + is_state->save_cursor = curwin->w_cursor; // may be restored later + is_state->search_start = curwin->w_cursor; + save_viewstate(&is_state->init_viewstate); + save_viewstate(&is_state->old_viewstate); +} + +/* + * First move cursor to end of match, then to the start. This + * moves the whole match onto the screen when 'nowrap' is set. + */ + static void +set_search_match(pos_T *t) +{ + t->lnum += search_match_lines; + t->col = search_match_endcol; + if (t->lnum > curbuf->b_ml.ml_line_count) + { + t->lnum = curbuf->b_ml.ml_line_count; + coladvance((colnr_T)MAXCOL); + } +} + +/* + * Return TRUE when 'incsearch' highlighting is to be done. + * Sets search_first_line and search_last_line to the address range. + * May change the last search pattern. + */ + static int +do_incsearch_highlighting(int firstc, incsearch_state_T *is_state, + int *skiplen, int *patlen) +{ + char_u *cmd; + cmdmod_T save_cmdmod = cmdmod; + char_u *p; + int delim_optional = FALSE; + int delim; + char_u *end; + char *dummy; + exarg_T ea; + pos_T save_cursor; + int use_last_pat; + + *skiplen = 0; + *patlen = ccline.cmdlen; + + if (!p_is || cmd_silent) + return FALSE; + + // by default search all lines + search_first_line = 0; + search_last_line = MAXLNUM; + + if (firstc == '/' || firstc == '?') + return TRUE; + if (firstc != ':') + return FALSE; + + vim_memset(&ea, 0, sizeof(ea)); + ea.line1 = 1; + ea.line2 = 1; + ea.cmd = ccline.cmdbuff; + ea.addr_type = ADDR_LINES; + + parse_command_modifiers(&ea, &dummy, TRUE); + cmdmod = save_cmdmod; + + cmd = skip_range(ea.cmd, NULL); + if (vim_strchr((char_u *)"sgvl", *cmd) == NULL) + return FALSE; + + // Skip over "substitute" to find the pattern separator. + for (p = cmd; ASCII_ISALPHA(*p); ++p) + ; + if (*skipwhite(p) == NUL) + return FALSE; + + if (STRNCMP(cmd, "substitute", p - cmd) == 0 + || STRNCMP(cmd, "smagic", p - cmd) == 0 + || STRNCMP(cmd, "snomagic", MAX(p - cmd, 3)) == 0 + || STRNCMP(cmd, "vglobal", p - cmd) == 0) + { + if (*cmd == 's' && cmd[1] == 'm') + p_magic = TRUE; + else if (*cmd == 's' && cmd[1] == 'n') + p_magic = FALSE; + } + else if (STRNCMP(cmd, "sort", MAX(p - cmd, 3)) == 0) + { + // skip over flags + while (ASCII_ISALPHA(*(p = skipwhite(p)))) + ++p; + if (*p == NUL) + return FALSE; + } + else if (STRNCMP(cmd, "vimgrep", MAX(p - cmd, 3)) == 0 + || STRNCMP(cmd, "vimgrepadd", MAX(p - cmd, 8)) == 0 + || STRNCMP(cmd, "lvimgrep", MAX(p - cmd, 2)) == 0 + || STRNCMP(cmd, "lvimgrepadd", MAX(p - cmd, 9)) == 0 + || STRNCMP(cmd, "global", p - cmd) == 0) + { + // skip over "!" + if (*p == '!') + { + p++; + if (*skipwhite(p) == NUL) + return FALSE; + } + if (*cmd != 'g') + delim_optional = TRUE; + } + else + return FALSE; + + p = skipwhite(p); + delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++; + end = skip_regexp(p, delim, p_magic, NULL); + + use_last_pat = end == p && *end == delim; + + if (end == p && !use_last_pat) + return FALSE; + + // Don't do 'hlsearch' highlighting if the pattern matches everything. + if (!use_last_pat) + { + char c = *end; + int empty; + + *end = NUL; + empty = empty_pattern(p); + *end = c; + if (empty) + return FALSE; + } + + // found a non-empty pattern or // + *skiplen = (int)(p - ccline.cmdbuff); + *patlen = (int)(end - p); + + // parse the address range + save_cursor = curwin->w_cursor; + curwin->w_cursor = is_state->search_start; + parse_cmd_address(&ea, &dummy, TRUE); + if (ea.addr_count > 0) + { + // Allow for reverse match. + if (ea.line2 < ea.line1) + { + search_first_line = ea.line2; + search_last_line = ea.line1; + } + else + { + search_first_line = ea.line1; + search_last_line = ea.line2; + } + } + else if (cmd[0] == 's' && cmd[1] != 'o') + { + // :s defaults to the current line + search_first_line = curwin->w_cursor.lnum; + search_last_line = curwin->w_cursor.lnum; + } + + curwin->w_cursor = save_cursor; + return TRUE; +} + + static void +finish_incsearch_highlighting( + int gotesc, + incsearch_state_T *is_state, + int call_update_screen) +{ + if (is_state->did_incsearch) + { + is_state->did_incsearch = FALSE; + if (gotesc) + curwin->w_cursor = is_state->save_cursor; + else + { + if (!EQUAL_POS(is_state->save_cursor, is_state->search_start)) + { + // put the '" mark at the original position + curwin->w_cursor = is_state->save_cursor; + setpcmark(); + } + curwin->w_cursor = is_state->search_start; + } + restore_viewstate(&is_state->old_viewstate); + highlight_match = FALSE; + + // by default search all lines + search_first_line = 0; + search_last_line = MAXLNUM; + + p_magic = is_state->magic_save; + + validate_cursor(); /* needed for TAB */ + redraw_all_later(SOME_VALID); + if (call_update_screen) + update_screen(SOME_VALID); + } +} + +/* + * Do 'incsearch' highlighting if desired. + */ + static void +may_do_incsearch_highlighting( + int firstc, + long count, + incsearch_state_T *is_state) +{ + int skiplen, patlen; + int found; // do_search() result + pos_T end_pos; +#ifdef FEAT_RELTIME + proftime_T tm; +#endif + int next_char; + int use_last_pat; + + // Parsing range may already set the last search pattern. + // NOTE: must call restore_last_search_pattern() before returning! + save_last_search_pattern(); + + if (!do_incsearch_highlighting(firstc, is_state, &skiplen, &patlen)) + { + restore_last_search_pattern(); + finish_incsearch_highlighting(FALSE, is_state, TRUE); + return; + } + + // If there is a character waiting, search and redraw later. + if (char_avail()) + { + restore_last_search_pattern(); + is_state->incsearch_postponed = TRUE; + return; + } + is_state->incsearch_postponed = FALSE; + + if (search_first_line == 0) + // start at the original cursor position + curwin->w_cursor = is_state->search_start; + else if (search_first_line > curbuf->b_ml.ml_line_count) + { + // start after the last line + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + curwin->w_cursor.col = MAXCOL; + } + else + { + // start at the first line in the range + curwin->w_cursor.lnum = search_first_line; + curwin->w_cursor.col = 0; + } + + // Use the previous pattern for ":s//". + next_char = ccline.cmdbuff[skiplen + patlen]; + use_last_pat = patlen == 0 && skiplen > 0 + && ccline.cmdbuff[skiplen - 1] == next_char; + + // If there is no pattern, don't do anything. + if (patlen == 0 && !use_last_pat) + { + found = 0; + set_no_hlsearch(TRUE); // turn off previous highlight + redraw_all_later(SOME_VALID); + } + else + { + int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK; + + cursor_off(); // so the user knows we're busy + out_flush(); + ++emsg_off; // so it doesn't beep if bad expr +#ifdef FEAT_RELTIME + // Set the time limit to half a second. + profile_setlimit(500L, &tm); +#endif + if (!p_hls) + search_flags += SEARCH_KEEP; + if (search_first_line != 0) + search_flags += SEARCH_START; + ccline.cmdbuff[skiplen + patlen] = NUL; + found = do_search(NULL, firstc == ':' ? '/' : firstc, + ccline.cmdbuff + skiplen, count, search_flags, +#ifdef FEAT_RELTIME + &tm, NULL +#else + NULL, NULL +#endif + ); + ccline.cmdbuff[skiplen + patlen] = next_char; + --emsg_off; + + if (curwin->w_cursor.lnum < search_first_line + || curwin->w_cursor.lnum > search_last_line) + { + // match outside of address range + found = 0; + curwin->w_cursor = is_state->search_start; + } + + // 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 + found = 0; + } + else if (char_avail()) + // cancelled searching because a char was typed + is_state->incsearch_postponed = TRUE; + } + if (found != 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. + restore_viewstate(&is_state->old_viewstate); + changed_cline_bef_curs(); + update_topline(); + + if (found != 0) + { + pos_T save_pos = curwin->w_cursor; + + is_state->match_start = curwin->w_cursor; + set_search_match(&curwin->w_cursor); + validate_cursor(); + end_pos = curwin->w_cursor; + is_state->match_end = end_pos; + curwin->w_cursor = save_pos; + } + else + end_pos = curwin->w_cursor; // shutup gcc 4 + + // Disable 'hlsearch' highlighting if the pattern matches everything. + // Avoids a flash when typing "foo\|". + if (!use_last_pat) + { + next_char = ccline.cmdbuff[skiplen + patlen]; + ccline.cmdbuff[skiplen + patlen] = NUL; + if (empty_pattern(ccline.cmdbuff) && !no_hlsearch) + { + redraw_all_later(SOME_VALID); + set_no_hlsearch(TRUE); + } + ccline.cmdbuff[skiplen + patlen] = next_char; + } + + 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; + + update_screen(SOME_VALID); + restore_last_search_pattern(); + + // Leave it at the end to make CTRL-R CTRL-W work. But not when beyond the + // end of the pattern, e.g. for ":s/pat/". + if (ccline.cmdbuff[skiplen + patlen] != NUL) + curwin->w_cursor = is_state->search_start; + else if (found != 0) + curwin->w_cursor = end_pos; + + msg_starthere(); + redrawcmdline(); + is_state->did_incsearch = TRUE; +} + +/* + * May adjust 'incsearch' highlighting for typing CTRL-G and CTRL-T, go to next + * or previous match. + * Returns FAIL when jumping to cmdline_not_changed; + */ + static int +may_adjust_incsearch_highlighting( + int firstc, + long count, + incsearch_state_T *is_state, + int c) +{ + int skiplen, patlen; + pos_T t; + char_u *pat; + int search_flags = SEARCH_NOOF; + int i; + int save; + + // Parsing range may already set the last search pattern. + // NOTE: must call restore_last_search_pattern() before returning! + save_last_search_pattern(); + + if (!do_incsearch_highlighting(firstc, is_state, &skiplen, &patlen)) + { + restore_last_search_pattern(); + return OK; + } + if (patlen == 0 && ccline.cmdbuff[skiplen] == NUL) + { + restore_last_search_pattern(); + return FAIL; + } + + if (firstc == ccline.cmdbuff[skiplen]) + { + pat = last_search_pattern(); + skiplen = 0; + patlen = (int)STRLEN(pat); + } + else + pat = ccline.cmdbuff + skiplen; + + cursor_off(); + out_flush(); + if (c == Ctrl_G) + { + t = is_state->match_end; + if (LT_POS(is_state->match_start, is_state->match_end)) + // Start searching at the end of the match not at the beginning of + // the next column. + (void)decl(&t); + search_flags += SEARCH_COL; + } + else + t = is_state->match_start; + if (!p_hls) + search_flags += SEARCH_KEEP; + ++emsg_off; + save = pat[patlen]; + pat[patlen] = NUL; + i = searchit(curwin, curbuf, &t, NULL, + c == Ctrl_G ? FORWARD : BACKWARD, + pat, count, search_flags, + RE_SEARCH, 0, NULL, NULL); + --emsg_off; + pat[patlen] = save; + if (i) + { + is_state->search_start = is_state->match_start; + is_state->match_end = t; + is_state->match_start = t; + if (c == Ctrl_T && firstc != '?') + { + // Move just before the current match, so that when nv_search + // finishes the cursor will be put back on the match. + is_state->search_start = t; + (void)decl(&is_state->search_start); + } + else if (c == Ctrl_G && firstc == '?') + { + // Move just after the current match, so that when nv_search + // finishes the cursor will be put back on the match. + is_state->search_start = t; + (void)incl(&is_state->search_start); + } + if (LT_POS(t, is_state->search_start) && c == Ctrl_G) + { + // wrap around + is_state->search_start = t; + if (firstc == '?') + (void)incl(&is_state->search_start); + else + (void)decl(&is_state->search_start); + } + + set_search_match(&is_state->match_end); + curwin->w_cursor = is_state->match_start; + changed_cline_bef_curs(); + update_topline(); + validate_cursor(); + highlight_match = TRUE; + save_viewstate(&is_state->old_viewstate); + update_screen(NOT_VALID); + redrawcmdline(); + } + else + vim_beep(BO_ERROR); + restore_last_search_pattern(); + return FAIL; +} + +/* + * When CTRL-L typed: add character from the match to the pattern. + * May set "*c" to the added character. + * Return OK when jumping to cmdline_not_changed. + */ + static int +may_add_char_to_search(int firstc, int *c, incsearch_state_T *is_state) +{ + int skiplen, patlen; + + // Parsing range may already set the last search pattern. + // NOTE: must call restore_last_search_pattern() before returning! + save_last_search_pattern(); + + if (!do_incsearch_highlighting(firstc, is_state, &skiplen, &patlen)) + { + restore_last_search_pattern(); + return FAIL; + } + restore_last_search_pattern(); + + // Add a character from under the cursor for 'incsearch'. + if (is_state->did_incsearch) + { + curwin->w_cursor = is_state->match_end; + if (!EQUAL_POS(curwin->w_cursor, is_state->search_start)) + { + *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 + skiplen)) + *c = MB_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 = '\\'; + } + // add any composing characters + if (mb_char2len(*c) != mb_ptr2len(ml_get_cursor())) + { + int save_c = *c; + + while (mb_char2len(*c) != mb_ptr2len(ml_get_cursor())) + { + curwin->w_cursor.col += mb_char2len(*c); + *c = gchar_cursor(); + stuffcharReadbuff(*c); + } + *c = save_c; + } + return FAIL; + } + } + } + return OK; +} +#endif + + void +cmdline_init(void) +{ + vim_memset(&ccline, 0, sizeof(struct cmdline_info)); +} + +/* + * 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 +{ + return getcmdline_int(firstc, count, indent, TRUE); +} + + static char_u * +getcmdline_int( + int firstc, + long count UNUSED, // only used for incremental search + int indent, // indent for inside conditionals + int init_ccline) // clear ccline first +{ + int c; + int i; + int j; + int gotesc = FALSE; /* TRUE when just typed */ + int do_abbr; /* when TRUE check for abbr. */ +#ifdef FEAT_CMDHIST + char_u *lookfor = NULL; /* string to match */ + int hiscnt; /* current history line in use */ + int histype; /* history type to be used */ +#endif +#ifdef FEAT_SEARCH_EXTRA + incsearch_state_T is_state; +#endif + 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 */ +#ifdef FEAT_MOUSE + /* mouse drag and release events are ignored, unless they are + * preceded with a mouse down event */ + int ignore_drag_release = TRUE; +#endif +#ifdef FEAT_EVAL + int break_ctrl_c = FALSE; +#endif + expand_T xpc; + long *b_im_ptr = NULL; + struct cmdline_info save_ccline; + int did_save_ccline = FALSE; + int cmdline_type; + + if (ccline.cmdbuff != NULL) + { + // Being called recursively. Since ccline is global, we need to save + // the current buffer and restore it when returning. + save_cmdline(&save_ccline); + did_save_ccline = TRUE; + } + if (init_ccline) + vim_memset(&ccline, 0, sizeof(struct cmdline_info)); + +#ifdef FEAT_EVAL + if (firstc == -1) + { + firstc = NUL; + break_ctrl_c = TRUE; + } +#endif +#ifdef FEAT_RIGHTLEFT + /* start without Hebrew mapping for a command line */ + if (firstc == ':' || firstc == '=' || firstc == '>') + cmd_hkmap = 0; +#endif + + ccline.overstrike = FALSE; /* always start in insert mode */ + +#ifdef FEAT_SEARCH_EXTRA + init_incsearch_state(&is_state); +#endif + + /* + * 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) + goto theend; // out of memory + ccline.cmdlen = ccline.cmdpos = 0; + ccline.cmdbuff[0] = NUL; + sb_text_start_cmdline(); + + /* autoindent for :insert and :append */ + if (firstc <= 0) + { + vim_memset(ccline.cmdbuff, ' ', indent); + ccline.cmdbuff[indent] = NUL; + ccline.cmdpos = indent; + ccline.cmdspos = indent; + ccline.cmdlen = indent; + } + + ExpandInit(&xpc); + ccline.xpc = &xpc; + +#ifdef FEAT_RIGHTLEFT + if (curwin->w_p_rl && *curwin->w_p_rlc == 's' + && (firstc == '/' || firstc == '?')) + cmdmsg_rl = TRUE; + else + cmdmsg_rl = FALSE; +#endif + + 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 defined(FEAT_EVAL) + if (ccline.input_fn) + { + xpc.xp_context = ccline.xp_context; + xpc.xp_pattern = ccline.cmdbuff; +# if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL) + xpc.xp_arg = ccline.xp_arg; +# endif + } +#endif + + /* + * 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 HAVE_INPUT_METHOD + im_set_active(*b_im_ptr == B_IMODE_IM); +#endif + } +#ifdef HAVE_INPUT_METHOD + else if (p_imcmdline) + im_set_active(TRUE); +#endif + +#ifdef FEAT_MOUSE + setmouse(); +#endif +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may show different cursor shape */ +#endif + + /* 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); + + /* Trigger CmdlineEnter autocommands. */ + cmdline_type = firstc == NUL ? '-' : firstc; + trigger_cmd_autocmd(cmdline_type, EVENT_CMDLINEENTER); + +#ifdef FEAT_CMDHIST + init_history(); + hiscnt = hislen; /* set hiscnt to impossible history value */ + histype = hist_char2type(firstc); +#endif + +#ifdef FEAT_DIGRAPHS + do_digraph(-1); /* init digraph typeahead */ +#endif + + /* If something above caused an error, reset the flags, we do want to type + * and execute commands. Display may be messed up a bit. */ + if (did_emsg) + redrawcmd(); + did_emsg = FALSE; + got_int = FALSE; + + /* + * 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 */ + + did_emsg = FALSE; /* There can't really be a reason why an error + that occurs while typing a command should + cause the command not to be executed. */ + + cursorcmd(); /* set the cursor on the right spot */ + + /* Get a character. Ignore K_IGNORE and K_NOP, they should not do + * anything, such as stop completion. */ + do + { + c = safe_vgetc(); + } while (c == K_IGNORE || c == K_NOP); + + if (KeyTyped) + { + some_key_typed = TRUE; +#ifdef FEAT_RIGHTLEFT + if (cmd_hkmap) + c = hkmap(c); +# ifdef FEAT_FKMAP + if (cmd_fkmap) + c = cmdl_fkmap(c); +# endif + 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; + } + } +#endif + } + + /* + * 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 + ) +#if defined(FEAT_EVAL) || defined(FEAT_CRYPT) + && firstc != '@' +#endif +#ifdef FEAT_EVAL + && !break_ctrl_c +#endif + && !global_busy) + got_int = FALSE; + +#ifdef FEAT_CMDHIST + /* 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))) + VIM_CLEAR(lookfor); +#endif + + /* + * 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; + +#ifdef FEAT_WILDMENU + /* 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; +#endif + + /* 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; +#ifdef FEAT_WILDMENU + if (!p_wmnu || (c != K_UP && c != K_DOWN)) +#endif + xpc.xp_context = EXPAND_NOTHING; + wim_index = 0; +#ifdef FEAT_WILDMENU + 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); + update_screen(VALID); /* redraw the screen NOW */ + 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; + } +#endif + } + +#ifdef FEAT_WILDMENU + /* 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((char_u *)" *?[{`$%#", + 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; + } + } + +#endif /* FEAT_WILDMENU */ + + /* 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) +#ifdef FEAT_EVAL + || cmdline_star > 0 +#endif + )) + { + vungetc(c); + c = Ctrl_BSL; + } +#ifdef FEAT_EVAL + 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; + + c = get_expr_register(); + if (c == '=') + { + /* Need to save and restore ccline. And set "textlock" + * to avoid nasty things like going to another buffer when + * evaluating an expression. */ + ++textlock; + p = get_expr_line(); + --textlock; + + if (p != NULL) + { + len = (int)STRLEN(p); + if (realloc_cmdbuff(len + 1) == OK) + { + ccline.cmdlen = len; + STRCPY(ccline.cmdbuff, p); + vim_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; + } + vim_free(p); + } + } + beep_flush(); + got_int = FALSE; /* don't abandon the command line */ + did_emsg = FALSE; + emsg_on_display = FALSE; + redrawcmd(); + goto cmdline_not_changed; + } +#endif + 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 */ + } + } + +#ifdef FEAT_CMDWIN + if (c == cedit_key || c == K_CMDWIN) + { + if (ex_normal_busy == 0 && got_int == FALSE) + { + /* + * Open a window to edit the command line (and history). + */ + c = open_cmdwin(); + some_key_typed = TRUE; + } + } +# ifdef FEAT_DIGRAPHS + else +# endif +#endif +#ifdef FEAT_DIGRAPHS + c = do_digraph(c); +#endif + + 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); +#ifdef FEAT_WILDMENU + xpc.xp_context = EXPAND_NOTHING; +#endif + 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) +#ifdef FEAT_WILDMENU + || (p_wmnu && (wim_flags[wim_index] & WIM_FULL) != 0) +#endif + ) + { + if (!(wim_flags[0] & WIM_LONGEST)) + { +#ifdef FEAT_WILDMENU + int p_wmnu_save = p_wmnu; + p_wmnu = 0; +#endif + /* remove match */ + nextwild(&xpc, WILD_PREV, 0, firstc != '@'); +#ifdef FEAT_WILDMENU + p_wmnu = p_wmnu_save; +#endif + } +#ifdef FEAT_WILDMENU + (void)showmatches(&xpc, p_wmnu + && ((wim_flags[wim_index] & WIM_LIST) == 0)); +#else + (void)showmatches(&xpc, FALSE); +#endif + 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(BO_WILD); + } +#ifdef FEAT_WILDMENU + else if (xpc.xp_numfiles == -1) + xpc.xp_context = EXPAND_NOTHING; +#endif + } + 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_BS: + case Ctrl_H: + case K_DEL: + case K_KDEL: + case Ctrl_W: +#ifdef FEAT_FKMAP + if (cmd_fkmap && c == K_BS) + c = K_DEL; +#endif + 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; +#ifdef FEAT_SEARCH_EXTRA + if (ccline.cmdlen == 0) + { + is_state.search_start = is_state.save_cursor; + /* save view settings, so that the screen + * won't be restored at the wrong position */ + is_state.old_viewstate = is_state.init_viewstate; + } +#endif + 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 +#ifdef FEAT_EVAL + || ccline.cmdfirstc == '>' +#endif + ) + goto cmdline_not_changed; + + VIM_CLEAR(ccline.cmdbuff); /* no commandline to return */ + if (!cmd_silent) + { +#ifdef FEAT_RIGHTLEFT + if (cmdmsg_rl) + msg_col = Columns; + else +#endif + msg_col = 0; + msg_putchar(' '); /* delete ':' */ + } +#ifdef FEAT_SEARCH_EXTRA + if (ccline.cmdlen == 0) + is_state.search_start = is_state.save_cursor; +#endif + redraw_cmdline = TRUE; + goto returncmd; /* back to cmd mode */ + } + goto cmdline_changed; + + case K_INS: + case K_KINS: +#ifdef FEAT_FKMAP + /* if Farsi mode set, we are in reverse insert mode - + Do not change the mode */ + if (cmd_fkmap) + beep_flush(); + else +#endif + ccline.overstrike = !ccline.overstrike; +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may show different cursor shape */ +#endif + 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 HAVE_INPUT_METHOD + 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 HAVE_INPUT_METHOD + 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(); + } +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may show different cursor shape */ +#endif +#if defined(FEAT_KEYMAP) + /* Show/unshow value of 'keymap' in status lines later. */ + status_redraw_curbuf(); +#endif + 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; +#ifdef FEAT_SEARCH_EXTRA + if (ccline.cmdlen == 0) + is_state.search_start = is_state.save_cursor; +#endif + redrawcmd(); + goto cmdline_changed; + +#ifdef FEAT_CLIPBOARD + case Ctrl_Y: + /* Copy the modeless selection, if there is one. */ + if (clip_star.state != SELECT_CLEARED) + { + if (clip_star.state == SELECT_DONE) + clip_copy_modeless_selection(TRUE); + goto cmdline_not_changed; + } + break; +#endif + + 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 */ + extra_char = NUL; + --no_mapping; +#ifdef FEAT_EVAL + /* + * 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 + || cmdline_star > 0) // or when typing a password + { + beep_flush(); + c = ESC; + } + else + c = get_expr_register(); + } +#endif + if (c != ESC) /* use ESC to cancel inserting register */ + { + cmdline_paste(c, i == Ctrl_R, FALSE); + +#ifdef FEAT_EVAL + /* 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 */ + } +#endif + KeyTyped = FALSE; /* Don't do p_wc completion. */ +#ifdef FEAT_EVAL + if (new_cmdpos >= 0) + { + /* set_cmdline_pos() was used */ + if (new_cmdpos > ccline.cmdlen) + ccline.cmdpos = ccline.cmdlen; + else + ccline.cmdpos = new_cmdpos; + } +#endif + } + 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 open_cmdwin() result. */ + goto cmdline_not_changed; + +#ifdef FEAT_GUI_W32 + /* On Win32 ignore , we get it when closing the window was + * cancelled. */ + case K_F4: + if (mod_mask == MOD_MASK_ALT) + { + redrawcmd(); /* somehow the cmdline is cleared */ + goto cmdline_not_changed; + } + break; +#endif + +#ifdef FEAT_MOUSE + case K_MIDDLEDRAG: + case K_MIDDLERELEASE: + goto cmdline_not_changed; /* Ignore mouse */ + + case K_MIDDLEMOUSE: +# ifdef FEAT_GUI + /* When GUI is active, also paste when 'mouse' is empty */ + if (!gui.in_use) +# endif + if (!mouse_has(MOUSE_COMMAND)) + goto cmdline_not_changed; /* Ignore mouse */ +# ifdef FEAT_CLIPBOARD + if (clip_star.available) + cmdline_paste('*', TRUE, TRUE); + else +# endif + cmdline_paste(0, TRUE, TRUE); + redrawcmd(); + goto cmdline_changed; + +# ifdef FEAT_DND + case K_DROP: + cmdline_paste('~', TRUE, FALSE); + redrawcmd(); + goto cmdline_changed; +# endif + + 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; +# ifdef FEAT_GUI + /* When GUI is active, also move when 'mouse' is empty */ + if (!gui.in_use) +# endif + if (!mouse_has(MOUSE_COMMAND)) + goto cmdline_not_changed; /* Ignore mouse */ +# ifdef FEAT_CLIPBOARD + if (mouse_row < cmdline_row && clip_star.available) + { + int button, is_click, is_drag; + + /* + * Handle modeless selection. + */ + button = get_mouse_button(KEY2TERMCAP1(c), + &is_click, &is_drag); + if (mouse_model_popup() && button == MOUSE_LEFT + && (mod_mask & MOD_MASK_SHIFT)) + { + /* Translate shift-left to right button. */ + button = MOUSE_RIGHT; + mod_mask &= ~MOD_MASK_SHIFT; + } + clip_modeless(button, is_click, is_drag); + goto cmdline_not_changed; + } +# endif + + 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: + case K_MOUSEMOVE: + goto cmdline_not_changed; + +#endif /* FEAT_MOUSE */ + +#ifdef FEAT_GUI + case K_LEFTMOUSE_NM: /* mousefocus click, ignored */ + case K_LEFTRELEASE_NM: + goto cmdline_not_changed; + + case K_VER_SCROLLBAR: + if (msg_scrolled == 0) + { + gui_do_scroll(); + redrawcmd(); + } + goto cmdline_not_changed; + + case K_HOR_SCROLLBAR: + if (msg_scrolled == 0) + { + gui_do_horiz_scroll(scrollbar_value, FALSE); + redrawcmd(); + } + goto cmdline_not_changed; +#endif +#ifdef FEAT_GUI_TABLINE + case K_TABLINE: + case K_TABMENU: + /* Don't want to change any tabs here. Make sure the same tab + * is still selected. */ + if (gui_use_tabline()) + gui_mch_set_curtab(tabpage_index(curtab)); + goto cmdline_not_changed; +#endif + + 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: +#ifdef FEAT_SEARCH_EXTRA + if (may_add_char_to_search(firstc, &c, &is_state) == OK) + goto cmdline_not_changed; +#endif + + /* 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_not_changed; + } +#ifdef FEAT_CMDHIST + /* FALLTHROUGH */ + 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; + + VIM_CLEAR(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(); +#endif + goto cmdline_not_changed; + +#ifdef FEAT_SEARCH_EXTRA + case Ctrl_G: /* next match */ + case Ctrl_T: /* previous match */ + if (may_adjust_incsearch_highlighting( + firstc, count, &is_state, c) == FAIL) + goto cmdline_not_changed; + break; +#endif + + case Ctrl_V: + case Ctrl_Q: +#ifdef FEAT_MOUSE + ignore_drag_release = TRUE; +#endif + putcmdline('^', TRUE); + c = get_literal(); /* get next (two) character(s) */ + do_abbr = FALSE; /* don't do abbreviation now */ + extra_char = NUL; + /* 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; + +#ifdef FEAT_DIGRAPHS + case Ctrl_K: +#ifdef FEAT_MOUSE + ignore_drag_release = TRUE; +#endif + putcmdline('?', TRUE); +#ifdef USE_ON_FLY_SCROLL + dont_scroll = TRUE; /* disallow scrolling here */ +#endif + c = get_digraph(TRUE); + extra_char = NUL; + if (c != NUL) + break; + + redrawcmd(); + goto cmdline_not_changed; +#endif /* FEAT_DIGRAPHS */ + +#ifdef FEAT_RIGHTLEFT + case Ctrl__: /* CTRL-_: switch language mode */ + if (!p_ari) + break; +# ifdef FEAT_FKMAP + if (p_altkeymap) + { + cmd_fkmap = !cmd_fkmap; + if (cmd_fkmap) /* in Farsi always in Insert mode */ + ccline.overstrike = FALSE; + } + else /* Hebrew is default */ +# endif + cmd_hkmap = !cmd_hkmap; + goto cmdline_not_changed; +#endif + + case K_PS: + bracketed_paste(PASTE_CMDLINE, FALSE, NULL); + goto cmdline_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: +#ifdef FEAT_SEARCH_EXTRA + if (!is_state.incsearch_postponed) + continue; +#endif + +cmdline_changed: + /* Trigger CmdlineChanged autocommands. */ + trigger_cmd_autocmd(cmdline_type, EVENT_CMDLINECHANGED); + +#ifdef FEAT_SEARCH_EXTRA + may_do_incsearch_highlighting(firstc, count, &is_state); +#endif + +#ifdef FEAT_RIGHTLEFT + if (cmdmsg_rl +# ifdef FEAT_ARABIC + || (p_arshape && !p_tbidi && enc_utf8) +# endif + ) + /* 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(); +#endif + } + +returncmd: + +#ifdef FEAT_RIGHTLEFT + cmdmsg_rl = FALSE; +#endif + +#ifdef FEAT_FKMAP + cmd_fkmap = 0; +#endif + + ExpandCleanup(&xpc); + ccline.xpc = NULL; + +#ifdef FEAT_SEARCH_EXTRA + finish_incsearch_highlighting(gotesc, &is_state, FALSE); +#endif + + if (ccline.cmdbuff != NULL) + { + /* + * Put line in history buffer (":" and "=" only when it was typed). + */ +#ifdef FEAT_CMDHIST + 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 == ':') + { + vim_free(new_last_cmdline); + new_last_cmdline = vim_strsave(ccline.cmdbuff); + } + } +#endif + + if (gotesc) + abandon_cmdline(); + } + + /* + * 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; + + /* Trigger CmdlineLeave autocommands. */ + trigger_cmd_autocmd(cmdline_type, EVENT_CMDLINELEAVE); + + State = save_State; +#ifdef HAVE_INPUT_METHOD + if (b_im_ptr != NULL && *b_im_ptr != B_IMODE_LMAP) + im_save_status(b_im_ptr); + im_set_active(FALSE); +#endif +#ifdef FEAT_MOUSE + setmouse(); +#endif +#ifdef CURSOR_SHAPE + ui_cursor_shape(); /* may show different cursor shape */ +#endif + sb_text_end_cmdline(); + +theend: + { + char_u *p = ccline.cmdbuff; + + if (did_save_ccline) + restore_cmdline(&save_ccline); + else + ccline.cmdbuff = NULL; + return p; + } +} + +#if (defined(FEAT_CRYPT) || defined(FEAT_EVAL)) || defined(PROTO) +/* + * 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 did_save_ccline = FALSE; + int msg_col_save = msg_col; + int msg_silent_save = msg_silent; + + if (ccline.cmdbuff != NULL) + { + // Save the values of the current cmdline and restore them below. + save_cmdline(&save_ccline); + did_save_ccline = TRUE; + } + + vim_memset(&ccline, 0, sizeof(struct cmdline_info)); + ccline.cmdprompt = prompt; + ccline.cmdattr = attr; +# ifdef FEAT_EVAL + ccline.xp_context = xp_context; + ccline.xp_arg = xp_arg; + ccline.input_fn = (firstc == '@'); +# endif + msg_silent = 0; + s = getcmdline_int(firstc, 1L, 0, FALSE); + + if (did_save_ccline) + restore_cmdline(&save_ccline); + + msg_silent = msg_silent_save; + /* 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; +} +#endif + +/* + * 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, evaluating + * 'balloonexpr', etc. + */ + int +text_locked(void) +{ +#ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + return TRUE; +#endif + 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) +{ + emsg(_(get_text_locked_msg())); +} + + char * +get_text_locked_msg(void) +{ +#ifdef FEAT_CMDWIN + if (cmdwin_type != 0) + return e_cmdwin; +#endif + return 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 defined(FEAT_CRYPT) || defined(FEAT_EVAL) + if (cmdline_star > 0) /* showing '*', always 1 position */ + return 1; +#endif + 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 UNUSED, + 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 UNUSED, + 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; + int len; + + /* 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_init2(&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(" "); + 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) + { + long sw; + char_u *s; + + if (ga_grow(&line_ga, 40) == FAIL) + break; + + /* + * Get one character at a time. + */ + prev_char = c1; + + /* Check for a ":normal" command and no more characters left. */ + if (ex_normal_busy > 0 && typebuf.tb_len == 0) + c1 = '\n'; + else + 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 (c1 == K_PS) + { + bracketed_paste(PASTE_EX, FALSE, &line_ga); + goto redraw; + } + + 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) + { + if (has_mbyte) + { + p = (char_u *)line_ga.ga_data; + p[line_ga.ga_len] = NUL; + len = (*mb_head_off)(p, p + line_ga.ga_len - 1) + 1; + line_ga.ga_len -= len; + } + else + --line_ga.ga_len; + goto redraw; + } + continue; + } + + if (c1 == Ctrl_U) + { + msg_col = startcol; + msg_clr_eos(); + line_ga.ga_len = 0; + goto redraw; + } + + if (c1 == Ctrl_T) + { + sw = get_sw_value(curbuf); + p = (char_u *)line_ga.ga_data; + p[line_ga.ga_len] = NUL; + indent = get_indent_str(p, 8, FALSE); + indent += sw - indent % sw; +add_indent: + while (get_indent_str(p, 8, FALSE) < indent) + { + (void)ga_grow(&line_ga, 2); /* one more for the NUL */ + p = (char_u *)line_ga.ga_data; + s = skipwhite(p); + mch_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; + p = (char_u *)line_ga.ga_data; + p[line_ga.ga_len] = NUL; + while (p < (char_u *)line_ga.ga_data + line_ga.ga_len) + { + if (*p == TAB) + { + do + { + msg_putchar(' '); + } while (++vcol % 8); + ++p; + } + else + { + len = MB_PTR2LEN(p); + msg_outtrans_len(p, len); + vcol += ptr2cells(p); + p += len; + } + } + 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, FALSE); + if (indent > 0) + { + --indent; + indent -= indent % get_sw_value(curbuf); + } + } + while (get_indent_str(p, 8, FALSE) > indent) + { + s = skipwhite(p); + mch_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; + } + + /* Ignore special key codes: mouse movement, K_IGNORE, etc. */ + if (IS_SPECIAL(c1)) + continue; + } + + if (IS_SPECIAL(c1)) + c1 = '?'; + if (has_mbyte) + len = (*mb_char2bytes)(c1, + (char_u *)line_ga.ga_data + line_ga.ga_len); + else + { + len = 1; + ((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, len); + vcol += char2cells(c1); + } + line_ga.ga_len += 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; +} + +# if defined(MCH_CURSOR_SHAPE) || defined(FEAT_GUI) \ + || defined(FEAT_MOUSESHAPE) || defined(PROTO) +/* + * Return TRUE if ccline.overstrike is on. + */ + int +cmdline_overstrike(void) +{ + return ccline.overstrike; +} + +/* + * Return TRUE if the cursor is at the end of the cmdline. + */ + int +cmdline_at_end(void) +{ + return (ccline.cmdpos >= ccline.cmdlen); +} +#endif + +#if (defined(FEAT_XIM) && (defined(FEAT_GUI_GTK))) || defined(PROTO) +/* + * Return the virtual column number at the current cursor position. + * This is used by the IM code to obtain the start of the preedit string. + */ + colnr_T +cmdline_getvcol_cursor(void) +{ + if (ccline.cmdbuff == NULL || ccline.cmdpos > ccline.cmdlen) + return MAXCOL; + + if (has_mbyte) + { + colnr_T col; + int i = 0; + + for (col = 0; i < ccline.cmdpos; ++col) + i += (*mb_ptr2len)(ccline.cmdbuff + i); + + return col; + } + else + return ccline.cmdpos; +} +#endif + +#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) +/* + * If part of the command line is an IM preedit string, redraw it with + * IM feedback attributes. The cursor position is restored after drawing. + */ + static void +redrawcmd_preedit(void) +{ + if ((State & CMDLINE) + && xic != NULL + /* && im_get_status() doesn't work when using SCIM */ + && !p_imdisable + && im_is_preediting()) + { + int cmdpos = 0; + int cmdspos; + int old_row; + int old_col; + colnr_T col; + + old_row = msg_row; + old_col = msg_col; + cmdspos = ((ccline.cmdfirstc != NUL) ? 1 : 0) + ccline.cmdindent; + + if (has_mbyte) + { + for (col = 0; col < preedit_start_col + && cmdpos < ccline.cmdlen; ++col) + { + cmdspos += (*mb_ptr2cells)(ccline.cmdbuff + cmdpos); + cmdpos += (*mb_ptr2len)(ccline.cmdbuff + cmdpos); + } + } + else + { + cmdspos += preedit_start_col; + cmdpos += preedit_start_col; + } + + msg_row = cmdline_row + (cmdspos / (int)Columns); + msg_col = cmdspos % (int)Columns; + if (msg_row >= Rows) + msg_row = Rows - 1; + + for (col = 0; cmdpos < ccline.cmdlen; ++col) + { + int char_len; + int char_attr; + + char_attr = im_get_feedback_attr(col); + if (char_attr < 0) + break; /* end of preedit string */ + + if (has_mbyte) + char_len = (*mb_ptr2len)(ccline.cmdbuff + cmdpos); + else + char_len = 1; + + msg_outtrans_len_attr(ccline.cmdbuff + cmdpos, char_len, char_attr); + cmdpos += char_len; + } + + msg_row = old_row; + msg_col = old_col; + } +} +#endif /* FEAT_XIM && FEAT_GUI_GTK */ + +/* + * Allocate a new command line buffer. + * Assigns the new buffer to 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. */ + mch_memmove(ccline.cmdbuff, p, (size_t)ccline.cmdlen); + ccline.cmdbuff[ccline.cmdlen] = NUL; + vim_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; +} + +#if defined(FEAT_ARABIC) || defined(PROTO) +static char_u *arshape_buf = NULL; + +# if defined(EXITFREE) || defined(PROTO) + void +free_cmdline_buf(void) +{ + vim_free(arshape_buf); +} +# endif +#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) +{ +#if defined(FEAT_CRYPT) || defined(FEAT_EVAL) + 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 +#endif +#ifdef FEAT_ARABIC + 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. */ + vim_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; + mch_memmove(arshape_buf + newlen, p, mb_l); + newlen += mb_l; + } + } + + msg_outtrans_len(arshape_buf, newlen); + } + else +#endif + 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(); + extra_char = c; + extra_char_shift = shift; +} + +/* + * 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(); + extra_char = NUL; +} + +/* + * 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) + { + mch_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) + { + mch_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; + } + mch_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); + } +#ifdef FEAT_ARABIC + 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; + } +#endif + 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; + } +#ifdef FEAT_FKMAP + /* + * If we are in Farsi command mode, the character input must be in + * Insert mode. So do not advance the cmdpos. + */ + if (!cmd_fkmap) +#endif + { + 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) + { + vim_memset(&prev_ccline, 0, sizeof(struct cmdline_info)); + prev_ccline_used = TRUE; + } + *ccp = prev_ccline; + prev_ccline = ccline; + ccline.cmdbuff = NULL; // signal that ccline is not in use +} + +/* + * Restore ccline after it has been saved with save_cmdline(). + */ + static void +restore_cmdline(struct cmdline_info *ccp) +{ + ccline = prev_ccline; + prev_ccline = *ccp; +} + +/* + * 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; + + /* 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 && regname != Ctrl_L + && !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; + +#ifdef FEAT_CLIPBOARD + regname = may_get_selection(regname); +#endif + + // Need to set "textlock" to avoid nasty things like going to another + // buffer when evaluating an expression. + ++textlock; + i = get_spec_reg(regname, &arg, &allocated, TRUE); + --textlock; + + 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) + vim_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); + } +} + +#ifdef FEAT_WILDMENU +/* + * Delete characters on the command line, from "from" to the current + * position. + */ + static void +cmdline_del(int from) +{ + mch_memmove(ccline.cmdbuff + from, ccline.cmdbuff + ccline.cmdpos, + (size_t)(ccline.cmdlen - ccline.cmdpos + 1)); + ccline.cmdlen -= ccline.cmdpos - from; + ccline.cmdpos = from; +} +#endif + +/* + * This function is called when the screen size changes and with incremental + * search and in other situations where the command line may have been + * overwritten. + */ + void +redrawcmdline(void) +{ + redrawcmdline_ex(TRUE); +} + + void +redrawcmdline_ex(int do_compute_cmdrow) +{ + if (cmd_silent) + return; + need_wait_return = FALSE; + if (do_compute_cmdrow) + 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((char *)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(); + if (extra_char != NUL) + putcmdline(extra_char, extra_char_shift); + + /* + * 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 + + lastwin->w_status_height; +} + + static void +cursorcmd(void) +{ + if (cmd_silent) + return; + +#ifdef FEAT_RIGHTLEFT + 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 +#endif + { + 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); +#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) + if (p_imst == IM_ON_THE_SPOT) + redrawcmd_preedit(); +#endif +#ifdef MCH_CURSOR_SHAPE + mch_update_cursor(); +#endif +} + + void +gotocmdline(int clr) +{ + msg_start(); +#ifdef FEAT_RIGHTLEFT + if (cmdmsg_rl) + msg_col = Columns - 1; + else +#endif + 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) +{ + int spos = 0; + + if (p_paste || no_abbr) /* no abbreviations or in paste mode */ + return FALSE; + + /* Do not consider '<,'> be part of the mapping, skip leading whitespace. + * Actually accepts any mark. */ + while (VIM_ISWHITE(ccline.cmdbuff[spos]) && spos < ccline.cmdlen) + spos++; + if (ccline.cmdlen - spos > 5 + && ccline.cmdbuff[spos] == '\'' + && ccline.cmdbuff[spos + 2] == ',' + && ccline.cmdbuff[spos + 3] == '\'') + spos += 5; + else + /* check abbreviation from the beginning of the commandline */ + spos = 0; + + return check_abbr(c, ccline.cmdbuff, ccline.cmdpos, spos); +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +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); +} +#endif + +/* + * 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); + vim_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) + VIM_CLEAR(p2); + } + } + } + + 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) + { + mch_memmove(&ccline.cmdbuff[ccline.cmdpos + difflen], + &ccline.cmdbuff[ccline.cmdpos], + (size_t)(ccline.cmdlen - ccline.cmdpos + 1)); + mch_memmove(&ccline.cmdbuff[i], p2, STRLEN(p2)); + ccline.cmdlen += difflen; + ccline.cmdpos += difflen; + } + } + vim_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; + long_u len; + 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; + } +#ifdef FEAT_WILDMENU + if (p_wmnu) + win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, + findex, cmd_showtail); +#endif + 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; + VIM_CLEAR(orig_save); + } + findex = 0; + + if (mode == WILD_FREE) /* only release file name */ + return NULL; + + if (xp->xp_numfiles == -1) + { + vim_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)) + semsg(_(e_nomatch2), str); +#endif + } + else if (xp->xp_numfiles == 0) + { + if (!(options & WILD_SILENT)) + semsg(_(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) + { + int mb_len = 1; + int c0, ci; + + for (len = 0; xp->xp_files[0][len]; len += mb_len) + { + if (has_mbyte) + { + mb_len = (*mb_ptr2len)(&xp->xp_files[0][len]); + c0 =(* mb_ptr2char)(&xp->xp_files[0][len]); + } + else + c0 = xp->xp_files[0][len]; + for (i = 1; i < xp->xp_numfiles; ++i) + { + if (has_mbyte) + ci =(* mb_ptr2char)(&xp->xp_files[i][len]); + else + ci = xp->xp_files[i][len]; + if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES + || xp->xp_context == EXPAND_FILES + || xp->xp_context == EXPAND_SHELLCMD + || xp->xp_context == EXPAND_BUFFERS)) + { + if (MB_TOLOWER(c0) != MB_TOLOWER(ci)) + break; + } + else if (c0 != ci) + break; + } + if (i < xp->xp_numfiles) + { + if (!(options & WILD_NO_BEEP)) + vim_beep(BO_WILD); + break; + } + } + + ss = alloc((unsigned)len + 1); + if (ss) + vim_strncpy(ss, xp->xp_files[0], (size_t)len); + findex = -1; /* next p_wc gets first one */ + } + + /* Concatenate all matching names */ + if (mode == WILD_ALL && xp->xp_numfiles > 0) + { + len = 0; + for (i = 0; i < xp->xp_numfiles; ++i) + len += (long_u)STRLEN(xp->xp_files[i]) + 1; + ss = lalloc(len, TRUE); + if (ss != NULL) + { + *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) + vim_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; +#if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + xp->xp_arg = NULL; +#endif + 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) + { + vim_free(files[i]); + files[i] = p; +#if defined(BACKSLASH_IN_FILENAME) + p = vim_strsave_escaped(files[i], (char_u *)" "); + if (p != NULL) + { + vim_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) + { + vim_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) + { + vim_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 *)"!"); + vim_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); + vim_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) + { + vim_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 UNUSED) +{ +#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; + } + +#ifdef FEAT_WILDMENU + if (!wildmenu) + { +#endif + 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 */ +#ifdef FEAT_WILDMENU + } +#endif + + if (got_int) + got_int = FALSE; /* only int. the completion, not the cmd line */ +#ifdef FEAT_WILDMENU + else if (wildmenu) + win_redr_status_matches(xp, num_files, files_found, -1, showtail); +#endif + 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((char *)p); + msg_advance(maxlen + 3); + msg_outtrans_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 = mch_isdir(halved_slash != NULL ? halved_slash + : files_found[k]); + vim_free(exp_path); + vim_free(halved_slash); + } + else + /* Expansion was done here, file names are literal. */ + j = mch_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 gettail 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 = gettail(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_PACKADD + || ((context == EXPAND_TAGS_LISTFILES + || 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 = gettail(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 != ':' +#ifdef FEAT_EVAL + && ccline.cmdfirstc != '>' && ccline.cmdfirstc != '=' + && !ccline.input_fn +#endif + ) + { + xp->xp_context = EXPAND_NOTHING; + return; + } + set_cmd_context(xp, ccline.cmdbuff, ccline.cmdlen, ccline.cmdpos, TRUE); +} + + 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 use_ccline UNUSED) /* use ccline for info */ +{ + 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; + +#ifdef FEAT_EVAL + if (use_ccline && ccline.cmdfirstc == '=') + { +# ifdef FEAT_CMDL_COMPL + /* pass CMD_SIZE because there is no real command */ + set_context_for_expression(xp, str, CMD_SIZE); +# endif + } + else if (use_ccline && ccline.input_fn) + { + xp->xp_context = ccline.xp_context; + xp->xp_pattern = ccline.cmdbuff; +# if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL) + xp->xp_arg = ccline.xp_arg; +# endif + } + else +#endif + 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; + } + vim_free(file_str); + + return EXPAND_OK; +} + +#ifdef FEAT_MULTI_LANG +/* + * Cleanup matches for help tags: + * Remove "@ab" if the top of 'helplang' is "ab" and the language of the first + * tag matches it. Otherwise remove "@en" if "en" is the only language. + */ + static void +cleanup_help_tags(int num_file, char_u **file) +{ + int i, j; + int len; + char_u buf[4]; + char_u *p = buf; + + if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n')) + { + *p++ = '@'; + *p++ = p_hlg[0]; + *p++ = p_hlg[1]; + } + *p = NUL; + + for (i = 0; i < num_file; ++i) + { + len = (int)STRLEN(file[i]) - 3; + if (len <= 0) + continue; + if (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) + /* item only exists with @en, remove it */ + file[i][len] = NUL; + } + } + + if (*buf != NUL) + for (i = 0; i < num_file; ++i) + { + len = (int)STRLEN(file[i]) - 3; + if (len <= 0) + continue; + if (STRCMP(file[i] + len, buf) == 0) + { + /* remove the default language */ + file[i][len] = NUL; + } + } +} +#endif + +/* + * 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 */ +{ +#ifdef FEAT_CMDL_COMPL + regmatch_T regmatch; +#endif + 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 (options & WILD_ALLLINKS) + flags |= EW_ALLLINKS; + + 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) + vim_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) + { +#ifdef FEAT_MULTI_LANG + cleanup_help_tags(*num_file, *file); +#endif + return OK; + } + return FAIL; + } + +#ifndef FEAT_CMDL_COMPL + return FAIL; +#else + 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, DIP_START + DIP_OPT, num_file, file, + directories); + } + if (xp->xp_context == EXPAND_COMPILER) + { + char *directories[] = {"compiler", NULL}; + return ExpandRTDir(pat, 0, num_file, file, directories); + } + if (xp->xp_context == EXPAND_OWNSYNTAX) + { + char *directories[] = {"syntax", NULL}; + return ExpandRTDir(pat, 0, num_file, file, directories); + } + if (xp->xp_context == EXPAND_FILETYPE) + { + char *directories[] = {"syntax", "indent", "ftplugin", NULL}; + return ExpandRTDir(pat, 0, num_file, file, directories); + } +# if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) + if (xp->xp_context == EXPAND_USER_LIST) + return ExpandUserList(xp, num_file, file); +# endif + if (xp->xp_context == EXPAND_PACKADD) + return ExpandPackAddDir(pat, 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); +# if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) + else if (xp->xp_context == EXPAND_USER_DEFINED) + ret = ExpandUserDefined(xp, ®match, num_file, file); +# endif + 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_MAPCLEAR, get_mapclear_arg, TRUE, TRUE}, + {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE}, +#ifdef FEAT_CMDHIST + {EXPAND_HISTORY, get_history_arg, TRUE, TRUE}, +#endif +#ifdef FEAT_USR_CMDS + {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE}, + {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, 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}, +#endif +#ifdef FEAT_EVAL + {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}, +#endif +#ifdef FEAT_MENU + {EXPAND_MENUS, get_menu_name, FALSE, TRUE}, + {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE}, +#endif +#ifdef FEAT_SYN_HL + {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE}, +#endif +#ifdef FEAT_PROFILE + {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE}, +#endif + {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE}, + {EXPAND_EVENTS, get_event_name, TRUE, TRUE}, + {EXPAND_AUGROUP, get_augroup_name, TRUE, TRUE}, +#ifdef FEAT_CSCOPE + {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE}, +#endif +#ifdef FEAT_SIGNS + {EXPAND_SIGN, get_sign_name, TRUE, TRUE}, +#endif +#ifdef FEAT_PROFILE + {EXPAND_PROFILE, get_profile_name, TRUE, TRUE}, +#endif +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + {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}, + {EXPAND_ARGLIST, get_arglist_name, 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; +#endif /* FEAT_CMDL_COMPL */ +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) +/* + * 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( + 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; + int round; + char_u *str; + + /* do this loop twice: + * round == 0: count the number of matching names + * round == 1: copy the matching names into allocated memory + */ + for (round = 0; round <= 1; ++round) + { + 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 (round) + { + if (escaped) + str = vim_strsave_escaped(str, (char_u *)" \t\\."); + else + str = vim_strsave(str); + (*file)[count] = str; +#ifdef FEAT_MENU + if (func == get_menu_names && str != NULL) + { + /* test for separator added by get_menu_names() */ + str += STRLEN(str) - 1; + if (*str == '\001') + *str = '.'; + } +#endif + } + ++count; + } + } + if (round == 0) + { + 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; + } + count = 0; + } + } + + /* 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); + } + +#ifdef FEAT_CMDL_COMPL + /* 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(); +#endif + + 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 = NULL; + int mustfree = FALSE; + garray_T ga; + char_u *buf = alloc(MAXPATHL); + size_t l; + char_u *s, *e; + int flags = flagsarg; + int ret; + int did_curdir = FALSE; + hashtab_T found_ht; + hashitem_T *hi; + hash_T hash; + + 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 | EW_SHELLCMD; + + if (pat[0] == '.' && (vim_ispathsep(pat[1]) + || (pat[1] == '.' && vim_ispathsep(pat[2])))) + path = (char_u *)"."; + else + { + /* For an absolute name we don't use $PATH. */ + if (!mch_isFullName(pat)) + 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". When "." is not in $PATH also expand for the + * current directory, to find "subdir/cmd". + */ + ga_init2(&ga, (int)sizeof(char *), 10); + hash_init(&found_ht); + for (s = path; ; s = e) + { +#if defined(MSWIN) + e = vim_strchr(s, ';'); +#else + e = vim_strchr(s, ':'); +#endif + if (e == NULL) + e = s + STRLEN(s); + + if (*s == NUL) + { + if (did_curdir) + break; + // Find directories in the current directory, path is empty. + did_curdir = TRUE; + flags |= EW_DIR; + } + else if (STRNCMP(s, ".", (int)(e - s)) == 0) + { + did_curdir = TRUE; + flags |= EW_DIR; + } + else + // Do not match directories inside a $PATH item. + flags &= ~EW_DIR; + + 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) + { + if (ga_grow(&ga, *num_file) == FAIL) + FreeWild(*num_file, *file); + else + { + for (i = 0; i < *num_file; ++i) + { + char_u *name = (*file)[i]; + + if (STRLEN(name) > l) + { + // Check if this name was already found. + hash = hash_hash(name + l); + hi = hash_lookup(&found_ht, name + l, hash); + if (HASHITEM_EMPTY(hi)) + { + // Remove the path that was prepended. + STRMOVE(name, name + l); + ((char_u **)ga.ga_data)[ga.ga_len++] = name; + hash_add_item(&found_ht, hi, name, hash); + name = NULL; + } + } + vim_free(name); + } + vim_free(*file); + } + } + if (*e != NUL) + ++e; + } + *file = ga.ga_data; + *num_file = ga.ga_len; + + vim_free(buf); + vim_free(pat); + if (mustfree) + vim_free(path); + hash_clear(&found_ht); + return OK; +} + + +# if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) +/* + * Call "user_expand_func()" to invoke a user defined Vim script function and + * return the result (either a string or a List). + */ + static void * +call_user_expand_func( + void *(*user_expand_func)(char_u *, int, typval_T *), + expand_T *xp, + int *num_file, + char_u ***file) +{ + int keep = 0; + typval_T args[4]; + sctx_T save_current_sctx = current_sctx; + char_u *pat = NULL; + void *ret; + + 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; + } + + pat = vim_strnsave(xp->xp_pattern, xp->xp_pattern_len); + + args[0].v_type = VAR_STRING; + args[0].vval.v_string = pat; + args[1].v_type = VAR_STRING; + args[1].vval.v_string = xp->xp_line; + args[2].v_type = VAR_NUMBER; + args[2].vval.v_number = xp->xp_col; + args[3].v_type = VAR_UNKNOWN; + + current_sctx = xp->xp_script_ctx; + + ret = user_expand_func(xp->xp_arg, 3, args); + + current_sctx = save_current_sctx; + if (ccline.cmdbuff != NULL) + ccline.cmdbuff[ccline.cmdlen] = keep; + + vim_free(pat); + 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; + int keep; + garray_T ga; + int skip; + + retstr = call_user_expand_func(call_func_retstr, xp, num_file, file); + if (retstr == NULL) + return FAIL; + + ga_init2(&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 = NUL; + + skip = xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0; + *e = keep; + + if (!skip) + { + if (ga_grow(&ga, 1) == FAIL) + break; + ((char_u **)ga.ga_data)[ga.ga_len] = vim_strnsave(s, (int)(e - s)); + ++ga.ga_len; + } + + if (*e != NUL) + ++e; + } + vim_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_init2(&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 */ + + if (ga_grow(&ga, 1) == FAIL) + break; + + ((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; +} +#endif + +/* + * Expand color scheme, compiler or filetype names. + * Search from 'runtimepath': + * 'runtimepath'/{dirnames}/{pat}.vim + * When "flags" has DIP_START: search also from 'start' of 'packpath': + * 'packpath'/pack/ * /start/ * /{dirnames}/{pat}.vim + * When "flags" has DIP_OPT: search also from 'opt' of 'packpath': + * 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim + * "dirnames" is an array with one or more directory names. + */ + static int +ExpandRTDir( + char_u *pat, + int flags, + int *num_file, + char_u ***file, + char *dirnames[]) +{ + char_u *s; + char_u *e; + char_u *match; + garray_T ga; + int i; + int pat_len; + + *num_file = 0; + *file = NULL; + pat_len = (int)STRLEN(pat); + ga_init2(&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); + globpath(p_rtp, s, &ga, 0); + vim_free(s); + } + + if (flags & DIP_START) { + for (i = 0; dirnames[i] != NULL; ++i) + { + s = alloc((unsigned)(STRLEN(dirnames[i]) + pat_len + 22)); + if (s == NULL) + { + ga_clear_strings(&ga); + return FAIL; + } + sprintf((char *)s, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); + globpath(p_pp, s, &ga, 0); + vim_free(s); + } + } + + if (flags & DIP_OPT) { + for (i = 0; dirnames[i] != NULL; ++i) + { + s = alloc((unsigned)(STRLEN(dirnames[i]) + pat_len + 20)); + if (s == NULL) + { + ga_clear_strings(&ga); + return FAIL; + } + sprintf((char *)s, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); + globpath(p_pp, s, &ga, 0); + vim_free(s); + } + } + + for (i = 0; i < ga.ga_len; ++i) + { + match = ((char_u **)ga.ga_data)[i]; + s = match; + e = s + STRLEN(s); + if (e - 4 > s && STRNICMP(e - 4, ".vim", 4) == 0) + { + e -= 4; + for (s = e; s > match; MB_PTR_BACK(match, s)) + if (s < match || vim_ispathsep(*s)) + break; + ++s; + *e = NUL; + mch_memmove(match, s, e - s + 1); + } + } + + if (ga.ga_len == 0) + return FAIL; + + /* Sort and remove duplicates which can happen when specifying multiple + * directories in dirnames. */ + remove_duplicates(&ga); + + *file = ga.ga_data; + *num_file = ga.ga_len; + return OK; +} + +/* + * Expand loadplugin names: + * 'packpath'/pack/ * /opt/{pat} + */ + static int +ExpandPackAddDir( + char_u *pat, + int *num_file, + char_u ***file) +{ + char_u *s; + char_u *e; + char_u *match; + garray_T ga; + int i; + int pat_len; + + *num_file = 0; + *file = NULL; + pat_len = (int)STRLEN(pat); + ga_init2(&ga, (int)sizeof(char *), 10); + + s = alloc((unsigned)(pat_len + 26)); + if (s == NULL) + { + ga_clear_strings(&ga); + return FAIL; + } + sprintf((char *)s, "pack/*/opt/%s*", pat); + globpath(p_pp, s, &ga, 0); + vim_free(s); + + for (i = 0; i < ga.ga_len; ++i) + { + match = ((char_u **)ga.ga_data)[i]; + s = gettail(match); + e = s + STRLEN(s); + mch_memmove(match, s, e - s + 1); + } + + if (ga.ga_len == 0) + return FAIL; + + /* Sort and remove duplicates which can happen when specifying multiple + * directories in dirnames. */ + remove_duplicates(&ga); + + *file = ga.ga_data; + *num_file = ga.ga_len; + return OK; +} + +#endif + +#if defined(FEAT_CMDL_COMPL) || defined(FEAT_EVAL) || defined(PROTO) +/* + * Expand "file" for all comma-separated directories in "path". + * Adds the matches to "ga". Caller must init "ga". + */ + void +globpath( + char_u *path, + char_u *file, + garray_T *ga, + int expand_options) +{ + expand_T xpc; + char_u *buf; + int i; + int num_p; + char_u **p; + + buf = alloc(MAXPATHL); + if (buf == NULL) + return; + + ExpandInit(&xpc); + xpc.xp_context = EXPAND_FILES; + + /* 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) + { +# if defined(MSWIN) + /* Using the platform's path separator (\) makes vim incorrectly + * treat it as an escape character, use '/' instead. */ + if (*buf != NUL && !after_pathsep(buf, buf + STRLEN(buf))) + STRCAT(buf, "/"); +# else + add_pathsep(buf); +# endif + 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); + + if (ga_grow(ga, num_p) == OK) + { + for (i = 0; i < num_p; ++i) + { + ((char_u **)ga->ga_data)[ga->ga_len] = + vim_strnsave(p[i], (int)STRLEN(p[i])); + ++ga->ga_len; + } + } + + FreeWild(num_p, p); + } + } + } + + vim_free(buf); +} + +#endif + +#if defined(FEAT_CMDHIST) || defined(PROTO) + +/********************************* + * 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 +}; + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) +/* + * Function given to ExpandGeneric() to obtain the possible first + * arguments of the ":history command. + */ + static char_u * +get_history_arg(expand_T *xp UNUSED, 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; +} +#endif + +/* + * init_history() - Initialize the command line history. + * Also used to re-allocate the history when the size changes. + */ + void +init_history(void) +{ + int newlen; /* new length of history table */ + histentry_T *temp; + int i; + int j; + int type; + + /* + * If size of history table changed, reallocate it + */ + newlen = (int)p_hi; + if (newlen != hislen) /* history length changed */ + { + for (type = 0; type < HIST_COUNT; ++type) /* adjust the tables */ + { + if (newlen) + { + temp = (histentry_T *)lalloc( + (long_u)(newlen * sizeof(histentry_T)), TRUE); + if (temp == NULL) /* out of memory! */ + { + if (type == 0) /* first one: just keep the old length */ + { + newlen = hislen; + break; + } + /* Already changed one table, now we can only have zero + * length for all tables. */ + newlen = 0; + type = -1; + continue; + } + } + 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 */ + vim_free(history[type][j].hisstr); + if (--j < 0) + j = hislen - 1; + if (j == hisidx[type]) + break; + } + hisidx[type] = newlen - 1; + } + vim_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; + hisptr->time_set = 0; +} + +/* + * 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; + history[type][i].time_set = vim_time(); + 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 && hisidx[HIST_SEARCH] >= 0) + { + /* Current line is from the same mapping, remove it */ + hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]]; + vim_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]]; + vim_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; + hisptr->time_set = vim_time(); + if (histype == HIST_SEARCH && in_map) + last_maptick = maptick; + } +} + +#if defined(FEAT_EVAL) || defined(PROTO) + +/* + * 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; +} + +/* + * 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--;) + { + vim_free(hisptr->hisstr); + clear_hist_entry(hisptr); + 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; + vim_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]; + vim_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; +} + +#endif /* FEAT_EVAL */ + +#if defined(FEAT_CRYPT) || defined(PROTO) +/* + * 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; + } +} +#endif + +#endif /* FEAT_CMDHIST */ + +#if defined(FEAT_EVAL) || defined(FEAT_CMDWIN) || defined(PROTO) +/* + * Get pointer to the command line info to use. save_ccline() 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; +} +#endif + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * 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; + + if (cmdline_star > 0) + return NULL; + 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; +} +#endif + +#if defined(FEAT_EVAL) || defined(FEAT_CMDWIN) || defined(PROTO) +/* + * 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 +# ifdef FEAT_EVAL + (p->input_fn) ? '@' : +# endif + '-'; + return p->cmdfirstc; +} +#endif + +#if defined(FEAT_QUICKFIX) || defined(FEAT_CMDHIST) || defined(PROTO) +/* + * 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; + varnumber_T num; + + *str = skipwhite(*str); + if (**str == '-' || vim_isdigit(**str)) /* parse "from" part of range */ + { + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); + *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, 0, &num, NULL, 0); + 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; +} +#endif + +#if defined(FEAT_CMDHIST) || defined(PROTO) +/* + * :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((char *)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; + } + } +} +#endif + +#if (defined(FEAT_VIMINFO) && defined(FEAT_CMDHIST)) || defined(PROTO) +/* + * Buffers for history read from a viminfo file. Only valid while reading. + */ +static histentry_T *viminfo_history[HIST_COUNT] = + {NULL, NULL, NULL, NULL, NULL}; +static int viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0, 0}; +static int viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0, 0}; +static int viminfo_add_at_front = FALSE; + +/* + * 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; + int type; + int len; + + init_history(); + viminfo_add_at_front = (asklen != 0 && !writing); + if (asklen > hislen) + asklen = hislen; + + for (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++; + len = asklen; + if (num > len) + len = num; + if (len <= 0) + viminfo_history[type] = NULL; + else + viminfo_history[type] = (histentry_T *)lalloc( + (long_u)(len * sizeof(histentry_T)), FALSE); + 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; + long_u len; + char_u *val; + char_u *p; + + 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. */ + len = STRLEN(val); + p = lalloc(len + 2, TRUE); + if (p != NULL) + { + if (type == HIST_SEARCH) + { + /* Search entry: Move the separator from the first + * column to after the NUL. */ + mch_memmove(p, val + 1, (size_t)len); + p[len] = sep; + } + else + { + /* Not a search entry: No separator in the viminfo + * file, add a NUL separator. */ + mch_memmove(p, val, (size_t)len + 1); + p[len + 1] = NUL; + } + viminfo_history[type][viminfo_hisidx[type]].hisstr = p; + viminfo_history[type][viminfo_hisidx[type]].time_set = 0; + viminfo_history[type][viminfo_hisidx[type]].viminfo = TRUE; + viminfo_history[type][viminfo_hisidx[type]].hisnum = 0; + viminfo_hisidx[type]++; + } + } + } + vim_free(val); + } + return viminfo_readline(virp); +} + +/* + * Accept a new style history line from the viminfo, store it in the history + * array when it's new. + */ + void +handle_viminfo_history( + garray_T *values, + int writing) +{ + int type; + long_u len; + char_u *val; + char_u *p; + bval_T *vp = (bval_T *)values->ga_data; + + /* Check the format: + * |{bartype},{histtype},{timestamp},{separator},"text" */ + if (values->ga_len < 4 + || vp[0].bv_type != BVAL_NR + || vp[1].bv_type != BVAL_NR + || (vp[2].bv_type != BVAL_NR && vp[2].bv_type != BVAL_EMPTY) + || vp[3].bv_type != BVAL_STRING) + return; + + type = vp[0].bv_nr; + if (type >= HIST_COUNT) + return; + if (viminfo_hisidx[type] < viminfo_hislen[type]) + { + val = vp[3].bv_string; + if (val != NULL && *val != NUL) + { + int sep = type == HIST_SEARCH && vp[2].bv_type == BVAL_NR + ? vp[2].bv_nr : NUL; + int idx; + int overwrite = FALSE; + + if (!in_history(type, val, viminfo_add_at_front, sep, writing)) + { + /* If lines were written by an older Vim we need to avoid + * getting duplicates. See if the entry already exists. */ + for (idx = 0; idx < viminfo_hisidx[type]; ++idx) + { + p = viminfo_history[type][idx].hisstr; + if (STRCMP(val, p) == 0 + && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) + { + overwrite = TRUE; + break; + } + } + + if (!overwrite) + { + /* Need to re-allocate to append the separator byte. */ + len = vp[3].bv_len; + p = lalloc(len + 2, TRUE); + } + else + len = 0; /* for picky compilers */ + if (p != NULL) + { + viminfo_history[type][idx].time_set = vp[1].bv_nr; + if (!overwrite) + { + mch_memmove(p, val, (size_t)len + 1); + /* Put the separator after the NUL. */ + p[len + 1] = sep; + viminfo_history[type][idx].hisstr = p; + viminfo_history[type][idx].hisnum = 0; + viminfo_history[type][idx].viminfo = TRUE; + viminfo_hisidx[type]++; + } + } + } + } + } +} + +/* + * Concatenate history lines from viminfo after the lines typed in this Vim. + */ + static void +concat_history(int type) +{ + int idx; + int i; + + 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++) + { + vim_free(history[type][idx].hisstr); + history[type][idx].hisstr = viminfo_history[type][i].hisstr; + history[type][idx].viminfo = TRUE; + history[type][idx].time_set = viminfo_history[type][i].time_set; + 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; + } +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +sort_hist(const void *s1, const void *s2) +{ + histentry_T *p1 = *(histentry_T **)s1; + histentry_T *p2 = *(histentry_T **)s2; + + if (p1->time_set < p2->time_set) return -1; + if (p1->time_set > p2->time_set) return 1; + return 0; +} +#endif + +/* + * Merge history lines from viminfo and lines typed in this Vim based on the + * timestamp; + */ + static void +merge_history(int type) +{ + int max_len; + histentry_T **tot_hist; + histentry_T *new_hist; + int i; + int len; + + /* Make one long list with all entries. */ + max_len = hislen + viminfo_hisidx[type]; + tot_hist = (histentry_T **)alloc(max_len * (int)sizeof(histentry_T *)); + new_hist = (histentry_T *)alloc(hislen * (int)sizeof(histentry_T)); + if (tot_hist == NULL || new_hist == NULL) + { + vim_free(tot_hist); + vim_free(new_hist); + return; + } + for (i = 0; i < viminfo_hisidx[type]; i++) + tot_hist[i] = &viminfo_history[type][i]; + len = i; + for (i = 0; i < hislen; i++) + if (history[type][i].hisstr != NULL) + tot_hist[len++] = &history[type][i]; + + /* Sort the list on timestamp. */ + qsort((void *)tot_hist, (size_t)len, sizeof(histentry_T *), sort_hist); + + /* Keep the newest ones. */ + for (i = 0; i < hislen; i++) + { + if (i < len) + { + new_hist[i] = *tot_hist[i]; + tot_hist[i]->hisstr = NULL; + if (new_hist[i].hisnum == 0) + new_hist[i].hisnum = ++hisnum[type]; + } + else + clear_hist_entry(&new_hist[i]); + } + hisidx[type] = (i < len ? i : len) - 1; + + /* Free what is not kept. */ + for (i = 0; i < viminfo_hisidx[type]; i++) + vim_free(viminfo_history[type][i].hisstr); + for (i = 0; i < hislen; i++) + vim_free(history[type][i].hisstr); + vim_free(history[type]); + history[type] = new_hist; + vim_free(tot_hist); +} + +/* + * Finish reading history lines from viminfo. Not used when writing viminfo. + */ + void +finish_viminfo_history(vir_T *virp) +{ + int type; + int merge = virp->vir_version >= VIMINFO_VERSION_WITH_HISTORY; + + for (type = 0; type < HIST_COUNT; ++type) + { + if (history[type] == NULL) + continue; + + if (merge) + merge_history(type); + else + concat_history(type); + + VIM_CLEAR(viminfo_history[type]); + 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; + 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") : + type == HIST_INPUT ? _("Input Line") : + _("Debug 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])) + { + char_u *p; + time_t timestamp; + int c = NUL; + + if (round == 1) + { + p = history[type][i].hisstr; + timestamp = history[type][i].time_set; + } + else + { + p = viminfo_history[type] == NULL ? NULL + : viminfo_history[type][i].hisstr; + timestamp = viminfo_history[type] == NULL ? 0 + : viminfo_history[type][i].time_set; + } + + 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); + + { + char cbuf[NUMBUFLEN]; + + /* New style history with a bar line. Format: + * |{bartype},{histtype},{timestamp},{separator},"text" */ + if (c == NUL) + cbuf[0] = NUL; + else + sprintf(cbuf, "%d", c); + fprintf(fp, "|%d,%d,%ld,%s,", BARTYPE_HISTORY, + type, (long)timestamp, cbuf); + barline_writestring(fp, p, LSIZE - 20); + putc('\n', fp); + } + } + 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) + vim_free(viminfo_history[type][i].hisstr); + VIM_CLEAR(viminfo_history[type]); + viminfo_hisidx[type] = 0; + } +} +#endif /* FEAT_VIMINFO */ + +#if defined(FEAT_FKMAP) || defined(PROTO) +/* + * 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]; +} +#endif + +#if defined(FEAT_CMDWIN) || defined(PROTO) +/* + * 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 +open_cmdwin(void) +{ + bufref_T old_curbuf; + win_T *old_curwin = curwin; + bufref_T bufref; + win_T *wp; + int i; + linenr_T lnum; + int histtype; + garray_T winsizes; + int save_restart_edit = restart_edit; + int save_State = State; + int save_exmode = exmode_active; +#ifdef FEAT_RIGHTLEFT + int save_cmdmsg_rl = cmdmsg_rl; +#endif +#ifdef FEAT_FOLDING + int save_KeyTyped; +#endif + + /* Can't do this recursively. Can't do it when typing a password. */ + if (cmdwin_type != 0 +# if defined(FEAT_CRYPT) || defined(FEAT_EVAL) + || cmdline_star > 0 +# endif + ) + { + beep_flush(); + return K_IGNORE; + } + set_bufref(&old_curbuf, curbuf); + + /* Save current window sizes. */ + win_size_save(&winsizes); + + /* Don't execute autocommands while creating the window. */ + block_autocmds(); + +#if defined(FEAT_INS_EXPAND) + // When using completion in Insert mode with = one can open the + // command line window, but we don't want the popup menu then. + pum_undisplay(); +#endif + + /* don't use a new tab page */ + cmdmod.tab = 0; + cmdmod.noswapfile = 1; + + /* 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); + curbuf->b_p_ma = TRUE; +#ifdef FEAT_FOLDING + curwin->w_p_fen = FALSE; +#endif +# ifdef FEAT_RIGHTLEFT + curwin->w_p_rl = cmdmsg_rl; + cmdmsg_rl = FALSE; +# endif + RESET_BINDING(curwin); + + /* Do execute autocommands for setting the filetype (load syntax). */ + unblock_autocmds(); + /* But don't allow switching to another buffer. */ + ++curbuf_lock; + + /* 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); + } + --curbuf_lock; + + /* 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); + + /* No Ex mode here! */ + exmode_active = 0; + + State = NORMAL; +# ifdef FEAT_MOUSE + setmouse(); +# endif + + /* Trigger CmdwinEnter autocommands. */ + trigger_cmd_autocmd(cmdwin_type, EVENT_CMDWINENTER); + 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; + +# ifdef FEAT_FOLDING + save_KeyTyped = KeyTyped; +# endif + + /* Trigger CmdwinLeave autocommands. */ + trigger_cmd_autocmd(cmdwin_type, EVENT_CMDWINLEAVE); + +# ifdef FEAT_FOLDING + /* Restore KeyTyped in case it is modified by autocommands */ + KeyTyped = save_KeyTyped; +# endif + + 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) || !bufref_valid(&old_curbuf)) + { + cmdwin_result = Ctrl_C; + emsg(_("E199: Active window or buffer deleted")); + } + else + { +# if defined(FEAT_EVAL) + /* autocmds may abort script processing */ + if (aborting() && cmdwin_result != K_IGNORE) + cmdwin_result = Ctrl_C; +# endif + /* Set the new command line from the cmdline buffer. */ + vim_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) + { + ccline.cmdbuff = vim_strsave((char_u *)""); + ccline.cmdlen = 0; + ccline.cmdbufflen = 1; + ccline.cmdpos = 0; + 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(); +# ifdef FEAT_CONCEAL + /* Avoid command-line window first character being concealed. */ + curwin->w_p_cole = 0; +# endif + wp = curwin; + set_bufref(&bufref, curbuf); + win_goto(old_curwin); + win_close(wp, TRUE); + + /* win_close() may have already wiped the buffer when 'bh' is + * set to 'wipe' */ + if (bufref_valid(&bufref)) + close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, FALSE); + + /* Restore window sizes. */ + win_size_restore(&winsizes); + + unblock_autocmds(); + } + + ga_clear(&winsizes); + restart_edit = save_restart_edit; +# ifdef FEAT_RIGHTLEFT + cmdmsg_rl = save_cmdmsg_rl; +# endif + + State = save_State; +# ifdef FEAT_MOUSE + setmouse(); +# endif + + return cmdwin_result; +} +#endif /* FEAT_CMDWIN */ + +/* + * 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_init2(&ga, 1, 0x400); + + if (cmd[2] != NUL) + end_pattern = (char *)skipwhite(cmd + 2); + else + end_pattern = dot; + + for (;;) + { + theline = eap->getline( +#ifdef FEAT_EVAL + eap->cstack->cs_looplevel > 0 ? -1 : +#endif + NUL, eap->cookie, 0); + + if (theline == NULL || STRCMP(end_pattern, theline) == 0) + { + vim_free(theline); + break; + } + + ga_concat(&ga, theline); + ga_append(&ga, '\n'); + vim_free(theline); + } + ga_append(&ga, NUL); + + return (char_u *)ga.ga_data; +} diff --git a/src/farsi.c b/src/farsi.c new file mode 100644 index 0000000..48dd991 --- /dev/null +++ b/src/farsi.c @@ -0,0 +1,2179 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * farsi.c: functions for Farsi language + */ + +#include "vim.h" + +#if defined(FEAT_FKMAP) || defined(PROTO) + +static int F_is_TyB_TyC_TyD(int src, int offset); + +/* + * Convert the given Farsi character into 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. + */ + static 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. + */ + 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. + */ + 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. + */ + 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. + */ + 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_: return AYN; /* exception - there are many of them */ + case _GHAYN_: return _GHAYN; + case GHAYN_: return GHAYN; /* exception - there are many of them */ + case _HE_: return _HE; + case YE_: return YE; + case IE_: return IE; + case TEE_: return TEE; + case YEE_: return YEE; + } + return c; +} + + + static void +put_and_redo(int c) +{ + pchar_cursor(c); + AppendCharToRedobuff(K_BS); + AppendCharToRedobuff(c); +} + +/* + * Overwrite the current redo and cursor characters + left adjust. + */ + 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); +} + +/* + * 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; + int insert_mode = (State & INSERT); + static int revins = 0; + + if (IS_SPECIAL(c)) + return c; + + if (insert_mode) + { + if (VIM_ISDIGIT(c) || ((c == '.' || c == '+' || c == '-' || + c == '^' || c == '%' || c == '#' || c == '=') && revins)) + { + /* Numbers are entered left-to-right. */ + 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) + { + /* Stop entering number. */ + 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 && insert_mode) + { + /* + * 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 && !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 (insert_mode) + { + 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. + */ + 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. + */ + 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. + */ + 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. + */ + 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. + */ + 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 . + */ + 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. + */ + static 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 (i < llen && canF_Rjoin(ptr[i])) + { + 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/ge"); + do_cmdline_cmd((char_u *)"%s/\201\231/\370\334/ge"); + + /* 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. + */ + static 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/ge"); + + 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]. + */ + 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 + */ + 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 + */ + 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(). + */ + 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. + */ + 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 + */ + 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 + */ + int +F_isdigit(int c) +{ + return (c >= FARSI_0 && c <= FARSI_9); +} + +/* + * F_ischar returns TRUE if 'c' is a Farsi character. + */ + int +F_ischar(int c) +{ + return (c >= TEE_ && c <= YE_); +} + + void +farsi_f8(cmdarg_T *cap UNUSED) +{ + 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; + } +} + + void +farsi_f9(cmdarg_T *cap UNUSED) +{ + 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(); + } +} + +#endif /* FEAT_FKMAP */ diff --git a/src/farsi.h b/src/farsi.h new file mode 100644 index 0000000..e91bdf7 --- /dev/null +++ b/src/farsi.h @@ -0,0 +1,234 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ + +/* + * 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) + */ + +/* + * 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 + +/* + * global definitions + * ================== + */ + +#define SRC_EDT 0 +#define SRC_CMD 1 + +#define AT_CURSOR 0 + +/* + * definitions for the window dependent functions (w_farsi). + */ +#define W_CONV 0x1 +#define W_R_L 0x2 + + +/* special Farsi text messages */ + +EXTERN char farsi_text_1[] +#ifdef DO_INIT + = { YE_, _SIN, RE, ALEF_, _FE, ' ', 'V', 'I', 'M', + ' ', F_HE, _BE, ' ', SHIN, RE, _GAF, DAL,' ', NOON, + ALEF_, _YE, ALEF_, _PE, '\0'} +#endif + ; + +EXTERN char farsi_text_2[] +#ifdef DO_INIT + = { 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'} +#endif + ; + +EXTERN char farsi_text_3[] +#ifdef DO_INIT + = { 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'} +#endif + ; + +#if 0 /* not used */ +EXTERN char_u farsi_text_4[] +#ifdef DO_INIT + = { DAL, WAW, _SHIN, _YE, _MIM, _NOON, ' ', YE_, _NOON, + ALEF_, _BE, _YE, _TE, _SHIN, _PE, ' ', '<', 'C','T','R', + 'L','-','B','>', ' ', NOON, ALEF_, _MIM, RE, _FE, ZE, + ALEF, ' ', YE_, _SIN, RE, ALEF_, _FE, ' ', RE, DAL, ' ', + RE, ALEF_, _KAF,' ', MIM, ALEF_, _GAF, _NOON, _HE, '\0'} +#endif + ; +#endif + +EXTERN char farsi_text_5[] +#ifdef DO_INIT + = { ' ', YE_, _SIN, RE, ALEF_, _FE, '\0'} +#endif + ; diff --git a/src/feature.h b/src/feature.h new file mode 100644 index 0000000..a41e79f --- /dev/null +++ b/src/feature.h @@ -0,0 +1,1352 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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. + */ +/* + * feature.h: Defines for optional code and preferences + * + * Edit this file to include/exclude parts of Vim, before compiling. + * The only other file that may be edited is Makefile, it contains machine + * specific options. + * + * To include specific options, change the "#if*" and "#endif" into comments, + * or uncomment the "#define". + * To exclude specific options, change the "#define" into a comment. + */ + +/* + * When adding a new feature: + * - Add a #define below. + * - Add a message in the table above ex_version(). + * - Add a string to f_has(). + * - Add a feature to ":help feature-list" in doc/eval.txt. + * - Add feature to ":help +feature-list" in doc/various.txt. + * - Add comment for the documentation of commands that use the feature. + */ + +/* + * Basic choices: + * ============== + * + * +tiny almost no features enabled, not even multiple windows + * +small few features enabled, as basic as possible + * +normal A default selection of features enabled + * +big many features enabled, as rich as possible. + * +huge all possible features enabled. + * + * When +small is used, +tiny is also included. +normal implies +small, etc. + */ + +/* + * Uncomment one of these to override the default. For unix use a configure + * argument, see Makefile. + */ +#if !defined(FEAT_TINY) && !defined(FEAT_SMALL) && !defined(FEAT_NORMAL) \ + && !defined(FEAT_BIG) && !defined(FEAT_HUGE) +/* #define FEAT_TINY */ +/* #define FEAT_SMALL */ +/* #define FEAT_NORMAL */ +/* #define FEAT_BIG */ +/* #define FEAT_HUGE */ +#endif + +/* + * For Unix, Mac and Win32 use +huge by default. These days CPUs are fast and + * Memory is cheap. + * Use +big for older systems: Other MS-Windows and VMS. + * Otherwise use +normal + */ +#if !defined(FEAT_TINY) && !defined(FEAT_SMALL) && !defined(FEAT_NORMAL) \ + && !defined(FEAT_BIG) && !defined(FEAT_HUGE) +# if defined(UNIX) || defined(WIN3264) || defined(MACOS_X) +# define FEAT_HUGE +# else +# if defined(MSWIN) || defined(VMS) || defined(AMIGA) +# define FEAT_BIG +# else +# define FEAT_NORMAL +# endif +# endif +#endif + +/* + * Each feature implies including the "smaller" ones. + */ +#ifdef FEAT_HUGE +# define FEAT_BIG +#endif +#ifdef FEAT_BIG +# define FEAT_NORMAL +#endif +#ifdef FEAT_NORMAL +# define FEAT_SMALL +#endif +#ifdef FEAT_SMALL +# define FEAT_TINY +#endif + +/* + * Optional code (see ":help +feature-list") + * ============= + */ + +/* + * These features used to be optional but are now always enabled. + * +windows Multiple windows. Without this there is no help + * window and no status lines. + * +vertsplit Vertically split windows. + */ + +/* + * +cmdhist Command line history. + */ +#ifdef FEAT_SMALL +# define FEAT_CMDHIST +#endif + +/* + * Message history is fixed at 200 message, 20 for the tiny version. + */ +#ifdef FEAT_SMALL +# define MAX_MSG_HIST_LEN 200 +#else +# define MAX_MSG_HIST_LEN 20 +#endif + +/* + * +jumplist Jumplist, CTRL-O and CTRL-I commands. + */ +#ifdef FEAT_SMALL +# define FEAT_JUMPLIST +#endif + +/* the cmdline-window requires FEAT_CMDHIST */ +#if defined(FEAT_CMDHIST) +# define FEAT_CMDWIN +#endif + +/* + * +folding Fold lines. + */ +#ifdef FEAT_NORMAL +# define FEAT_FOLDING +#endif + +/* + * +digraphs Digraphs. + * In insert mode and on the command line you will be + * able to use digraphs. The CTRL-K command will work. + * Define OLD_DIGRAPHS to get digraphs compatible with + * Vim 5.x. The new ones are from RFC 1345. + */ +#ifdef FEAT_NORMAL +# define FEAT_DIGRAPHS +/* #define OLD_DIGRAPHS */ +#endif + +/* + * +langmap 'langmap' option. Only useful when you put your + * keyboard in a special language mode, e.g. for typing + * greek. + */ +#ifdef FEAT_BIG +# define FEAT_LANGMAP +#endif + +/* + * +keymap 'keymap' option. Allows you to map typed keys in + * Insert mode for a special language. + */ +#ifdef FEAT_BIG +# define FEAT_KEYMAP +#endif + +/* + * +localmap Mappings and abbreviations local to a buffer. + */ +#ifdef FEAT_NORMAL +# define FEAT_LOCALMAP +#endif + +/* + * +insert_expand CTRL-N/CTRL-P/CTRL-X in insert mode. Takes about + * 4Kbyte of code. + */ +#ifdef FEAT_NORMAL +# define FEAT_INS_EXPAND +#endif + +/* + * +cmdline_compl completion of mappings/abbreviations in cmdline mode. + * Takes a few Kbyte of code. + */ +#ifdef FEAT_NORMAL +# define FEAT_CMDL_COMPL +#endif + +#ifdef FEAT_NORMAL +# define VIM_BACKTICK /* internal backtick expansion */ +#endif + +/* + * +visual Visual mode - now always included. + * +visualextra Extra features for Visual mode (mostly block operators). + * Now always included. + */ + +/* + * +virtualedit 'virtualedit' option and its implementation + * Now always included. + */ + +/* + * +cmdline_info 'showcmd' and 'ruler' options. + */ +#ifdef FEAT_NORMAL +# define FEAT_CMDL_INFO +#endif + +/* + * +linebreak 'showbreak', 'breakat' and 'linebreak' options. + * Also 'numberwidth'. + */ +#ifdef FEAT_NORMAL +# define FEAT_LINEBREAK +#endif + +/* + * +extra_search 'hlsearch' and 'incsearch' options. + */ +#ifdef FEAT_NORMAL +# define FEAT_SEARCH_EXTRA +#endif + +/* + * +quickfix Quickfix commands. + */ +#ifdef FEAT_NORMAL +# define FEAT_QUICKFIX +#endif + +/* + * +file_in_path "gf" and "" commands. + */ +#ifdef FEAT_NORMAL +# define FEAT_SEARCHPATH +#endif + +/* + * +find_in_path "[I" ":isearch" "^W^I", ":checkpath", etc. + */ +#ifdef FEAT_NORMAL +# ifdef FEAT_SEARCHPATH /* FEAT_SEARCHPATH is required */ +# define FEAT_FIND_ID +# endif +#endif + +/* + * +path_extra up/downwards searching in 'path' and 'tags'. + */ +#ifdef FEAT_NORMAL +# define FEAT_PATH_EXTRA +#endif + +/* + * +rightleft Right-to-left editing/typing support. + * + * Disabled for EBCDIC as it requires multibyte. + */ +#if defined(FEAT_BIG) && !defined(DISABLE_RIGHTLEFT) && !defined(EBCDIC) +# define FEAT_RIGHTLEFT +#endif + +/* + * +farsi Farsi (Persian language) Keymap support. + * Requires FEAT_RIGHTLEFT. + * + * Disabled for EBCDIC as it requires multibyte. + */ +#if defined(FEAT_BIG) && !defined(DISABLE_FARSI) && !defined(EBCDIC) +# define FEAT_FKMAP +#endif +#ifdef FEAT_FKMAP +# ifndef FEAT_RIGHTLEFT +# define FEAT_RIGHTLEFT +# endif +#endif + +/* + * +arabic Arabic keymap and shaping support. + * Requires FEAT_RIGHTLEFT + * + * Disabled for EBCDIC as it requires multibyte. + */ +#if defined(FEAT_BIG) && !defined(DISABLE_ARABIC) && !defined(EBCDIC) +# define FEAT_ARABIC +#endif +#ifdef FEAT_ARABIC +# ifndef FEAT_RIGHTLEFT +# define FEAT_RIGHTLEFT +# endif +#endif + +/* + * +emacs_tags When FEAT_EMACS_TAGS defined: Include support for + * emacs style TAGS file. + */ +#ifdef FEAT_BIG +# define FEAT_EMACS_TAGS +#endif + +/* + * +tag_binary Can use a binary search for the tags file. + * + * Disabled for EBCDIC: + * On z/OS Unix we have the problem that /bin/sort sorts ASCII instead of + * EBCDIC. With this binary search doesn't work, as VIM expects a tag file + * sorted by character values. I'm not sure how to fix this. Should we really + * do a EBCDIC to ASCII conversion for this?? + */ +#if !defined(EBCDIC) +# define FEAT_TAG_BINS +#endif + +/* + * +tag_old_static Old style static tags: "file:tag file ..". Slows + * down tag searching a bit. + */ +#ifdef FEAT_NORMAL +# define FEAT_TAG_OLDSTATIC +#endif + +/* + * +tag_any_white Allow any white space to separate the fields in a tags + * file. When not defined, only a TAB is allowed. + */ +/* #define FEAT_TAG_ANYWHITE */ + +/* + * +cscope Unix only: Cscope support. + */ +#if defined(UNIX) && defined(FEAT_BIG) && !defined(FEAT_CSCOPE) && !defined(MACOS_X) +# define FEAT_CSCOPE +#endif + +/* + * +eval Built-in script language and expression evaluation, + * ":let", ":if", etc. + * +float Floating point variables. + * +num64 64-bit Number. + */ +#ifdef FEAT_NORMAL +# define FEAT_EVAL +# if defined(HAVE_FLOAT_FUNCS) || defined(WIN3264) || defined(MACOS_X) +# define FEAT_FLOAT +# endif +# if defined(HAVE_STDINT_H) || defined(WIN3264) || (VIM_SIZEOF_LONG >= 8) +# define FEAT_NUM64 +# endif +#endif + +#ifdef FEAT_EVAL +# define HAVE_SANDBOX +#endif + +/* + * +profile Profiling for functions and scripts. + */ +#if defined(FEAT_HUGE) \ + && defined(FEAT_EVAL) \ + && ((defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)) \ + || defined(WIN3264)) +# define FEAT_PROFILE +#endif + +/* + * +reltime reltime() function + */ +#if defined(FEAT_NORMAL) \ + && defined(FEAT_EVAL) \ + && ((defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)) \ + || defined(WIN3264)) +# define FEAT_RELTIME +#endif + +/* + * +timers timer_start() + */ +#if defined(FEAT_RELTIME) && (defined(UNIX) || defined(WIN32) || defined(VMS) ) +# define FEAT_TIMERS +#endif + +/* + * +textobjects Text objects: "vaw", "das", etc. + */ +#if defined(FEAT_NORMAL) && defined(FEAT_EVAL) +# define FEAT_TEXTOBJ +#endif + +/* + * Insert mode completion with 'completefunc'. + */ +#if defined(FEAT_INS_EXPAND) && defined(FEAT_EVAL) +# define FEAT_COMPL_FUNC +#endif + +/* + * +user_commands Allow the user to define his own commands. + */ +#ifdef FEAT_NORMAL +# define FEAT_USR_CMDS +#endif + +/* + * +printer ":hardcopy" command + * +postscript Printing uses PostScript file output. + */ +#if defined(FEAT_NORMAL) && (defined(MSWIN) || defined(FEAT_EVAL)) \ + && !defined(AMIGA) +# define FEAT_PRINTER +#endif +#if defined(FEAT_PRINTER) && ((defined(MSWIN) && defined(MSWINPS)) \ + || (!defined(MSWIN) && defined(FEAT_EVAL))) +# define FEAT_POSTSCRIPT +#endif + +/* + * +modify_fname modifiers for file name. E.g., "%:p:h". + */ +#ifdef FEAT_NORMAL +# define FEAT_MODIFY_FNAME +#endif + +/* + * +diff Displaying diffs in a nice way. + * Requires +windows and +autocmd. + */ +#if defined(FEAT_NORMAL) +# define FEAT_DIFF +#endif + +/* + * +title 'title' and 'icon' options + * +statusline 'statusline', 'rulerformat' and special format of + * 'titlestring' and 'iconstring' options. + * +byte_offset '%o' in 'statusline' and builtin functions line2byte() + * and byte2line(). + * Note: Required for Macintosh. + */ +#if defined(FEAT_NORMAL) +# define FEAT_TITLE +#endif + +#ifdef FEAT_NORMAL +# define FEAT_STL_OPT +# ifndef FEAT_CMDL_INFO +# define FEAT_CMDL_INFO /* 'ruler' is required for 'statusline' */ +# endif +#endif + +#ifdef FEAT_NORMAL +# define FEAT_BYTEOFF +#endif + +/* + * +wildignore 'wildignore' and 'backupskip' options + * Needed for Unix to make "crontab -e" work. + */ +#if defined(FEAT_NORMAL) || defined(UNIX) +# define FEAT_WILDIGN +#endif + +/* + * +wildmenu 'wildmenu' option + */ +#if defined(FEAT_NORMAL) +# define FEAT_WILDMENU +#endif + +/* + * +viminfo reading/writing the viminfo file. Takes about 8Kbyte + * of code. + * VIMINFO_FILE Location of user .viminfo file (should start with $). + * VIMINFO_FILE2 Location of alternate user .viminfo file. + */ +#ifdef FEAT_NORMAL +# define FEAT_VIMINFO +/* #define VIMINFO_FILE "$HOME/foo/.viminfo" */ +/* #define VIMINFO_FILE2 "~/bar/.viminfo" */ +#endif + +/* + * +syntax syntax highlighting. When using this, it's a good + * idea to have +autocmd and +eval too. + */ +#if defined(FEAT_NORMAL) || defined(PROTO) +# define FEAT_SYN_HL +#endif + +/* + * +conceal 'conceal' option. Needs syntax highlighting + * as this is how the concealed text is defined. + */ +#if defined(FEAT_BIG) && defined(FEAT_SYN_HL) +# define FEAT_CONCEAL +#endif + +/* + * +textprop Text properties + */ +#if defined(FEAT_EVAL) && defined(FEAT_SYN_HL) +# define FEAT_TEXT_PROP +#endif + +/* + * +spell spell checking + * + * Disabled for EBCDIC: * Doesn't work (SIGSEGV). + */ +#if (defined(FEAT_NORMAL) || defined(PROTO)) && !defined(EBCDIC) +# define FEAT_SPELL +#endif + +/* + * +builtin_terms Choose one out of the following four: + * + * NO_BUILTIN_TCAPS Do not include any builtin termcap entries (used only + * with HAVE_TGETENT defined). + * + * (nothing) Machine specific termcap entries will be included. + * + * SOME_BUILTIN_TCAPS Include most useful builtin termcap entries (used only + * with NO_BUILTIN_TCAPS not defined). + * This is the default. + * + * ALL_BUILTIN_TCAPS Include all builtin termcap entries + * (used only with NO_BUILTIN_TCAPS not defined). + */ +#ifdef HAVE_TGETENT +/* #define NO_BUILTIN_TCAPS */ +#endif + +#if !defined(NO_BUILTIN_TCAPS) +# ifdef FEAT_BIG +# define ALL_BUILTIN_TCAPS +# else +# define SOME_BUILTIN_TCAPS /* default */ +# endif +#endif + +/* + * +lispindent lisp indenting (From Eric Fischer). + * +cindent C code indenting (From Eric Fischer). + * +smartindent smart C code indenting when the 'si' option is set. + * + * These two need to be defined when making prototypes. + */ +#if defined(FEAT_NORMAL) || defined(PROTO) +# define FEAT_LISP +#endif + +#if defined(FEAT_NORMAL) || defined(PROTO) +# define FEAT_CINDENT +#endif + +#ifdef FEAT_NORMAL +# define FEAT_SMARTINDENT +#endif + +/* + * +comments 'comments' option. + */ +#ifdef FEAT_NORMAL +# define FEAT_COMMENTS +#endif + +/* + * +cryptv Encryption (by Mohsin Ahmed ). + */ +#if defined(FEAT_NORMAL) && !defined(FEAT_CRYPT) || defined(PROTO) +# define FEAT_CRYPT +#endif + +/* + * +mksession ":mksession" command. + * Requires +windows and +vertsplit. + */ +#if defined(FEAT_NORMAL) +# define FEAT_SESSION +#endif + +/* + * +multi_lang Multi language support. ":menutrans", ":language", etc. + * +gettext Message translations (requires +multi_lang) + * (only when "lang" archive unpacked) + */ +#ifdef FEAT_NORMAL +# define FEAT_MULTI_LANG +#endif +#if defined(HAVE_GETTEXT) && defined(FEAT_MULTI_LANG) \ + && (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) +# define FEAT_GETTEXT +#endif + +/* + * +multi_byte Generic multi-byte character handling. + * Now always enabled. + */ + +/* + * +multi_byte_ime Win32 IME input method. Only for far-east Windows, so + * IME can be used to input chars. Not tested much! + */ +#if defined(FEAT_GUI_W32) && !defined(FEAT_MBYTE_IME) +/* #define FEAT_MBYTE_IME */ +# endif + +/* Use iconv() when it's available. */ +#if (defined(HAVE_ICONV_H) && defined(HAVE_ICONV)) || defined(DYNAMIC_ICONV) +# define USE_ICONV +#endif + +/* + * +xim X Input Method. For entering special languages like + * chinese and Japanese. + * +hangul_input Internal Hangul input method. Must be included + * through configure: "--enable-hangulin" + * Both are for Unix and VMS only. + */ +#ifndef FEAT_XIM +/* #define FEAT_XIM */ +#endif + +#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) +# define USE_XIM 1 /* needed for GTK include files */ +#endif + +#ifdef FEAT_HANGULIN +# define HANGUL_DEFAULT_KEYBOARD 2 /* 2 or 3 bulsik keyboard */ +# define ESC_CHG_TO_ENG_MODE /* if defined, when ESC pressed, + * turn to english mode + */ +# if defined(FEAT_XIM) && !defined(LINT) + Error: You should select only ONE of XIM and HANGUL INPUT +# endif +#endif +#if defined(FEAT_HANGULIN) || defined(FEAT_XIM) +/* # define X_LOCALE */ /* for OS with incomplete locale + support, like old linux versions. */ +#endif + +/* + * +xfontset X fontset support. For outputting wide characters. + */ +#ifndef FEAT_XFONTSET +# if defined(HAVE_X11) && !defined(FEAT_GUI_GTK) +# define FEAT_XFONTSET +# else +/* # define FEAT_XFONTSET */ +# endif +#endif + +/* + * +libcall libcall() function + */ +/* Using dlopen() also requires dlsym() to be available. */ +#if defined(HAVE_DLOPEN) && defined(HAVE_DLSYM) +# define USE_DLOPEN +#endif +#if defined(FEAT_EVAL) && (defined(WIN3264) || ((defined(UNIX) || defined(VMS)) \ + && (defined(USE_DLOPEN) || defined(HAVE_SHL_LOAD)))) +# define FEAT_LIBCALL +#endif + +/* + * +menu ":menu" command + */ +#ifdef FEAT_NORMAL +# define FEAT_MENU +# ifdef FEAT_GUI_W32 +# define FEAT_TEAROFF +# endif +#endif + +/* + * popup menu in a terminal + */ +#if defined(FEAT_MENU) && !defined(ALWAYS_USE_GUI) && defined(FEAT_INS_EXPAND) +# define FEAT_TERM_POPUP_MENU +#endif + +/* There are two ways to use XPM. */ +#if (defined(HAVE_XM_XPMP_H) && defined(FEAT_GUI_MOTIF)) \ + || defined(HAVE_X11_XPM_H) +# define HAVE_XPM 1 +#endif + +/* + * +toolbar Include code for a toolbar (for the Win32 GUI, GTK + * always has it). But only if menus are enabled. + */ +#if defined(FEAT_NORMAL) && defined(FEAT_MENU) \ + && (defined(FEAT_GUI_GTK) \ + || defined(FEAT_GUI_MSWIN) \ + || ((defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) \ + && defined(HAVE_XPM)) \ + || defined(FEAT_GUI_PHOTON)) +# define FEAT_TOOLBAR +#endif + + +#if defined(FEAT_TOOLBAR) && !defined(FEAT_MENU) +# define FEAT_MENU +#endif + +/* + * GUI tabline + */ +#if defined(FEAT_NORMAL) \ + && (defined(FEAT_GUI_GTK) \ + || (defined(FEAT_GUI_MOTIF) && defined(HAVE_XM_NOTEBOOK_H)) \ + || defined(FEAT_GUI_MAC) \ + || (defined(FEAT_GUI_MSWIN) \ + && (!defined(_MSC_VER) || _MSC_VER > 1020))) +# define FEAT_GUI_TABLINE +#endif + +/* + * +browse ":browse" command. + * or just the ":browse" command modifier + */ +#if defined(FEAT_NORMAL) +# define FEAT_BROWSE_CMD +# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_MAC) +# define FEAT_BROWSE +# endif +#endif + +/* + * On some systems, when we compile with the GUI, we always use it. On Mac + * there is no terminal version, and on Windows we can't figure out how to + * fork one off with :gui. + */ +#if defined(FEAT_GUI_MSWIN) || (defined(FEAT_GUI_MAC) && !defined(MACOS_X_DARWIN)) +# define ALWAYS_USE_GUI +#endif + +/* + * +dialog_gui Use GUI dialog. + * +dialog_con May use Console dialog. + * When none of these defined there is no dialog support. + */ +#ifdef FEAT_NORMAL +# if ((defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF)) \ + && defined(HAVE_X11_XPM_H)) \ + || defined(FEAT_GUI_GTK) \ + || defined(FEAT_GUI_PHOTON) \ + || defined(FEAT_GUI_MSWIN) \ + || defined(FEAT_GUI_MAC) +# define FEAT_CON_DIALOG +# define FEAT_GUI_DIALOG +# else +# define FEAT_CON_DIALOG +# endif +#endif +#if !defined(FEAT_GUI_DIALOG) && (defined(FEAT_GUI_MOTIF) \ + || defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_GTK) \ + || defined(FEAT_GUI_W32)) +/* need a dialog to show error messages when starting from the desktop */ +# define FEAT_GUI_DIALOG +#endif +#if defined(FEAT_GUI_DIALOG) && \ + (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA) \ + || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MSWIN) \ + || defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_MAC)) +# define FEAT_GUI_TEXTDIALOG +# ifndef ALWAYS_USE_GUI +# define FEAT_CON_DIALOG +# endif +#endif + +/* + * +termguicolors 'termguicolors' option. + */ +#if (defined(FEAT_BIG) && defined(FEAT_SYN_HL)) && !defined(ALWAYS_USE_GUI) +# define FEAT_TERMGUICOLORS +#endif + +/* Mac specific thing: Codewarrior interface. */ +#ifdef FEAT_GUI_MAC +# define FEAT_CW_EDITOR +#endif + +/* + * +vartabs 'vartabstop' and 'varsofttabstop' options. + */ +#ifdef FEAT_BIG +# define FEAT_VARTABS +#endif + +/* + * Preferences: + * ============ + */ + +/* + * +writebackup 'writebackup' is default on: + * Use a backup file while overwriting a file. But it's + * deleted again when 'backup' is not set. Changing this + * is strongly discouraged: You can lose all your + * changes when the computer crashes while writing the + * file. + * VMS note: It does work on VMS as well, but because of + * version handling it does not have any purpose. + * Overwrite will write to the new version. + */ +#ifndef VMS +# define FEAT_WRITEBACKUP +#endif + +/* + * +xterm_save The t_ti and t_te entries for the builtin xterm will + * be set to save the screen when starting Vim and + * restoring it when exiting. + */ +/* #define FEAT_XTERM_SAVE */ + +/* + * DEBUG Output a lot of debugging garbage. + */ +/* #define DEBUG */ + +/* + * STARTUPTIME Time the startup process. Writes a file with + * timestamps. + */ +#if defined(FEAT_NORMAL) \ + && ((defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)) \ + || defined(WIN3264)) +# define STARTUPTIME 1 +#endif + +/* + * MEM_PROFILE Debugging of memory allocation and freeing. + */ +/* #define MEM_PROFILE */ + +/* + * VIMRC_FILE Name of the .vimrc file in current dir. + */ +/* #define VIMRC_FILE ".vimrc" */ + +/* + * EXRC_FILE Name of the .exrc file in current dir. + */ +/* #define EXRC_FILE ".exrc" */ + +/* + * GVIMRC_FILE Name of the .gvimrc file in current dir. + */ +/* #define GVIMRC_FILE ".gvimrc" */ + +/* + * SESSION_FILE Name of the default ":mksession" file. + */ +#define SESSION_FILE "Session.vim" + +/* + * USR_VIMRC_FILE Name of the user .vimrc file. + * USR_VIMRC_FILE2 Name of alternate user .vimrc file. + * USR_VIMRC_FILE3 Name of alternate user .vimrc file. + */ +/* #define USR_VIMRC_FILE "~/foo/.vimrc" */ +/* #define USR_VIMRC_FILE2 "~/bar/.vimrc" */ +/* #define USR_VIMRC_FILE3 "$VIM/.vimrc" */ + +/* + * VIM_DEFAULTS_FILE Name of the defaults.vim script file + */ +/* #define VIM_DEFAULTS_FILE "$VIMRUNTIME/defaults.vim" */ + +/* + * EVIM_FILE Name of the evim.vim script file + */ +/* #define EVIM_FILE "$VIMRUNTIME/evim.vim" */ + +/* + * USR_EXRC_FILE Name of the user .exrc file. + * USR_EXRC_FILE2 Name of the alternate user .exrc file. + */ +/* #define USR_EXRC_FILE "~/foo/.exrc" */ +/* #define USR_EXRC_FILE2 "~/bar/.exrc" */ + +/* + * USR_GVIMRC_FILE Name of the user .gvimrc file. + * USR_GVIMRC_FILE2 Name of the alternate user .gvimrc file. + */ +/* #define USR_GVIMRC_FILE "~/foo/.gvimrc" */ +/* #define USR_GVIMRC_FILE2 "~/bar/.gvimrc" */ +/* #define USR_GVIMRC_FILE3 "$VIM/.gvimrc" */ + +/* + * SYS_VIMRC_FILE Name of the system-wide .vimrc file. + */ +/* #define SYS_VIMRC_FILE "/etc/vimrc" */ + +/* + * SYS_GVIMRC_FILE Name of the system-wide .gvimrc file. + */ +/* #define SYS_GVIMRC_FILE "/etc/gvimrc" */ + +/* + * DFLT_HELPFILE Name of the help file. + */ +/* # define DFLT_HELPFILE "$VIMRUNTIME/doc/help.txt.gz" */ + +/* + * File names for: + * FILETYPE_FILE switch on file type detection + * FTPLUGIN_FILE switch on loading filetype plugin files + * INDENT_FILE switch on loading indent files + * FTOFF_FILE switch off file type detection + * FTPLUGOF_FILE switch off loading settings files + * INDOFF_FILE switch off loading indent files + */ +/* # define FILETYPE_FILE "filetype.vim" */ +/* # define FTPLUGIN_FILE "ftplugin.vim" */ +/* # define INDENT_FILE "indent.vim" */ +/* # define FTOFF_FILE "ftoff.vim" */ +/* # define FTPLUGOF_FILE "ftplugof.vim" */ +/* # define INDOFF_FILE "indoff.vim" */ + +/* + * SYS_MENU_FILE Name of the default menu.vim file. + */ +/* # define SYS_MENU_FILE "$VIMRUNTIME/menu.vim" */ + +/* + * SYS_OPTWIN_FILE Name of the default optwin.vim file. + */ +#ifndef SYS_OPTWIN_FILE +# define SYS_OPTWIN_FILE "$VIMRUNTIME/optwin.vim" +#endif + +/* + * SYNTAX_FNAME Name of a syntax file, where %s is the syntax name. + */ +/* #define SYNTAX_FNAME "/foo/%s.vim" */ + +/* + * RUNTIME_DIRNAME Generic name for the directory of the runtime files. + */ +#ifndef RUNTIME_DIRNAME +# define RUNTIME_DIRNAME "runtime" +#endif + +/* + * RUNTIME_GLOBAL Comma-separated list of directory names for global Vim + * runtime directories. + * Don't define this if the preprocessor can't handle + * string concatenation. + * Also set by "--with-global-runtime" configure argument. + */ +/* #define RUNTIME_GLOBAL "/etc/vim" */ + +/* + * RUNTIME_GLOBAL_AFTER Comma-separated list of directory names for global Vim + * runtime after directories. + * Don't define this if the preprocessor can't handle + * string concatenation. + * Also set by "--with-global-runtime" configure argument. + */ +/* #define RUNTIME_GLOBAL_AFTER "/etc/vim/after" */ + +/* + * MODIFIED_BY Name of who modified Vim. Required when distributing + * a modified version of Vim. + * Also from the "--with-modified-by" configure argument. + */ +/* #define MODIFIED_BY "John Doe" */ + +/* + * Machine dependent: + * ================== + */ + +/* + * +fork Unix only: fork() support (detected by configure) + * +system Use system() instead of fork/exec for starting a + * shell. Doesn't work for the GUI! + */ +/* #define USE_SYSTEM */ + +/* + * +X11 Unix only. Include code for xterm title saving and X + * clipboard. Only works if HAVE_X11 is also defined. + */ +#if (defined(FEAT_NORMAL) || defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) +# define WANT_X11 +#endif + +/* + * XSMP - X11 Session Management Protocol + * It may be preferred to disable this if the GUI supports it (e.g., + * GNOME/KDE) and implement save-yourself etc. through that, but it may also + * be cleaner to have all SM-aware vims do the same thing (libSM does not + * depend upon X11). + * If your GUI wants to support SM itself, change this ifdef. + * I'm assuming that any X11 implementation will cope with this for now. + */ +#if defined(HAVE_X11) && defined(WANT_X11) && defined(HAVE_X11_SM_SMLIB_H) +# define USE_XSMP +#endif +#if defined(USE_XSMP_INTERACT) && !defined(USE_XSMP) +# undef USE_XSMP_INTERACT +#endif + +/* + * +mouse_xterm Unix only: Include code for xterm mouse handling. + * +mouse_dec idem, for Dec mouse handling. + * +mouse_jsbterm idem, for Jsbterm mouse handling. + * +mouse_netterm idem, for Netterm mouse handling. + * (none) MS-DOS mouse support. + * +mouse_gpm Unix only: Include code for Linux console mouse + * handling. + * +mouse_pterm PTerm mouse support for QNX + * +mouse_sgr Unix only: Include code for for SGR-styled mouse. + * +mouse_sysmouse Unix only: Include code for FreeBSD and DragonFly + * console mouse handling. + * +mouse_urxvt Unix only: Include code for for urxvt mosue handling. + * +mouse Any mouse support (any of the above enabled). + */ +/* OS/2 and Amiga console have no mouse support */ +#if !defined(AMIGA) +# ifdef FEAT_NORMAL +# define FEAT_MOUSE_XTERM +# endif +# ifdef FEAT_BIG +# define FEAT_MOUSE_NET +# endif +# ifdef FEAT_BIG +# define FEAT_MOUSE_DEC +# endif +# ifdef FEAT_BIG +# define FEAT_MOUSE_URXVT +# endif +# ifdef FEAT_BIG +# define FEAT_MOUSE_SGR +# endif +# if defined(FEAT_NORMAL) && defined(WIN3264) +# define DOS_MOUSE +# endif +# if defined(FEAT_NORMAL) && defined(__QNX__) +# define FEAT_MOUSE_PTERM +# endif +#endif + +/* + * Note: Only one of the following may be defined: + * FEAT_MOUSE_GPM + * FEAT_SYSMOUSE + * FEAT_MOUSE_JSB + * FEAT_MOUSE_PTERM + */ +#if defined(FEAT_NORMAL) && defined(HAVE_GPM) +# define FEAT_MOUSE_GPM +#endif + +#if defined(FEAT_NORMAL) && defined(HAVE_SYSMOUSE) +# define FEAT_SYSMOUSE +#endif + +/* urxvt is a small variation of mouse_xterm, and shares its code */ +#if defined(FEAT_MOUSE_URXVT) && !defined(FEAT_MOUSE_XTERM) +# define FEAT_MOUSE_XTERM +#endif + +/* sgr is a small variation of mouse_xterm, and shares its code */ +#if defined(FEAT_MOUSE_SGR) && !defined(FEAT_MOUSE_XTERM) +# define FEAT_MOUSE_XTERM +#endif + +/* Define FEAT_MOUSE when any of the above is defined or FEAT_GUI. */ +#if !defined(FEAT_MOUSE_TTY) \ + && (defined(FEAT_MOUSE_XTERM) \ + || defined(FEAT_MOUSE_NET) \ + || defined(FEAT_MOUSE_DEC) \ + || defined(DOS_MOUSE) \ + || defined(FEAT_MOUSE_GPM) \ + || defined(FEAT_MOUSE_JSB) \ + || defined(FEAT_MOUSE_PTERM) \ + || defined(FEAT_SYSMOUSE) \ + || defined(FEAT_MOUSE_URXVT) \ + || defined(FEAT_MOUSE_SGR)) +# define FEAT_MOUSE_TTY /* include non-GUI mouse support */ +#endif +#if !defined(FEAT_MOUSE) && (defined(FEAT_MOUSE_TTY) || defined(FEAT_GUI)) +# define FEAT_MOUSE /* include generic mouse support */ +#endif + +/* + * +clipboard Clipboard support. Always used for the GUI. + * +xterm_clipboard Unix only: Include code for handling the clipboard + * in an xterm like in the GUI. + */ + +#ifdef FEAT_CYGWIN_WIN32_CLIPBOARD +# define FEAT_CLIPBOARD +#endif + +#ifdef FEAT_GUI +# ifndef FEAT_CLIPBOARD +# define FEAT_CLIPBOARD +# endif +#endif + +#if defined(FEAT_NORMAL) \ + && (defined(UNIX) || defined(VMS)) \ + && defined(WANT_X11) && defined(HAVE_X11) +# define FEAT_XCLIPBOARD +# ifndef FEAT_CLIPBOARD +# define FEAT_CLIPBOARD +# endif +#endif + +/* + * +dnd Drag'n'drop support. Always used for the GTK+ GUI. + */ +#if defined(FEAT_CLIPBOARD) && defined(FEAT_GUI_GTK) +# define FEAT_DND +#endif + +#if defined(FEAT_GUI_MSWIN) && defined(FEAT_SMALL) +# define MSWIN_FIND_REPLACE /* include code for find/replace dialog */ +# define MSWIN_FR_BUFSIZE 256 +#endif + +#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MOTIF) \ + || defined(MSWIN_FIND_REPLACE) +# define FIND_REPLACE_DIALOG 1 +#endif + +/* + * +clientserver Remote control via the remote_send() function + * and the --remote argument + */ +#if (defined(WIN32) || defined(FEAT_XCLIPBOARD)) && defined(FEAT_EVAL) +# define FEAT_CLIENTSERVER +#endif + +/* + * +autoservername Automatically generate a servername for clientserver + * when --servername is not passed on the command line. + */ +#if defined(FEAT_CLIENTSERVER) && !defined(FEAT_AUTOSERVERNAME) +# ifdef WIN3264 + /* Always enabled on MS-Windows. */ +# define FEAT_AUTOSERVERNAME +# else + /* Enable here if you don't use configure. */ +/* # define FEAT_AUTOSERVERNAME */ +# endif +#endif + +/* + * +termresponse send t_RV to obtain terminal response. Used for xterm + * to check if mouse dragging can be used and if term + * codes can be obtained. + */ +#if (defined(FEAT_NORMAL) || defined(FEAT_MOUSE)) && defined(HAVE_TGETENT) +# define FEAT_TERMRESPONSE +#endif + +/* + * cursor shape Adjust the shape of the cursor to the mode. + * mouse shape Adjust the shape of the mouse pointer to the mode. + */ +#ifdef FEAT_NORMAL +/* MS-DOS console and Win32 console can change cursor shape */ +# if defined(WIN3264) && !defined(FEAT_GUI_W32) +# define MCH_CURSOR_SHAPE +# endif +# if defined(FEAT_GUI_W32) || defined(FEAT_GUI_MOTIF) \ + || defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_GTK) \ + || defined(FEAT_GUI_PHOTON) +# define FEAT_MOUSESHAPE +# endif +#endif + +/* GUI and some consoles can change the shape of the cursor. The code is also + * needed for the 'mouseshape' and 'concealcursor' options. */ +#if defined(FEAT_GUI) \ + || defined(MCH_CURSOR_SHAPE) \ + || defined(FEAT_MOUSESHAPE) \ + || defined(FEAT_CONCEAL) \ + || (defined(UNIX) && defined(FEAT_NORMAL)) +# define CURSOR_SHAPE +#endif + +#if defined(FEAT_MZSCHEME) && (defined(FEAT_GUI_W32) || defined(FEAT_GUI_GTK) \ + || defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA) \ + || defined(FEAT_GUI_MAC)) +# define MZSCHEME_GUI_THREADS +#endif + +/* + * +ARP Amiga only. Use arp.library, DOS 2.0 is not required. + */ +#if !defined(NO_ARP) && !defined(__amigaos4__) +# define FEAT_ARP +#endif + +/* + * +GUI_Athena To compile Vim with or without the GUI (gvim) you have + * +GUI_Motif to edit the Makefile. + */ + +/* + * +ole Win32 OLE automation: Use Makefile.ovc. + */ + +/* + * These features can only be included by using a configure argument. See the + * Makefile for a line to uncomment. + * +lua Lua interface: "--enable-luainterp" + * +mzscheme MzScheme interface: "--enable-mzscheme" + * +perl Perl interface: "--enable-perlinterp" + * +python Python interface: "--enable-pythoninterp" + * +tcl TCL interface: "--enable-tclinterp" + * +netbeans_intg Netbeans integration + * +channel Inter process communication + */ + +/* + * These features are automatically detected: + * +terminfo + * +tgetent + */ + +/* + * The Netbeans feature requires +eval. + */ +#if !defined(FEAT_EVAL) && defined(FEAT_NETBEANS_INTG) +# undef FEAT_NETBEANS_INTG +#endif + +/* + * The +channel feature requires +eval. + */ +#if !defined(FEAT_EVAL) && defined(FEAT_JOB_CHANNEL) +# undef FEAT_JOB_CHANNEL +#endif + +/* + * +terminal ":terminal" command. Runs a terminal in a window. + * requires +channel + */ +#if defined(FEAT_TERMINAL) && !defined(FEAT_JOB_CHANNEL) +# undef FEAT_TERMINAL +#endif +#if defined(FEAT_TERMINAL) && !defined(CURSOR_SHAPE) +# define CURSOR_SHAPE +#endif + +/* + * +signs Allow signs to be displayed to the left of text lines. + * Adds the ":sign" command. + */ +#if defined(FEAT_BIG) || defined(FEAT_NETBEANS_INTG) +# define FEAT_SIGNS +# if ((defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) \ + && defined(HAVE_X11_XPM_H)) \ + || defined(FEAT_GUI_GTK) \ + || (defined(WIN32) && defined(FEAT_GUI)) +# define FEAT_SIGN_ICONS +# endif +#endif + +/* + * +balloon_eval Allow balloon expression evaluation. Used with a + * debugger and for tooltips. + * Only for GUIs where it was implemented. + */ +#if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA) \ + || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_W32)) \ + && ( ((defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)) \ + && !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_W32)) \ + || defined(FEAT_NETBEANS_INTG) || defined(FEAT_EVAL)) +# define FEAT_BEVAL_GUI +# if !defined(FEAT_XFONTSET) && !defined(FEAT_GUI_GTK) \ + && !defined(FEAT_GUI_W32) +# define FEAT_XFONTSET +# endif +#endif + +#if defined(FEAT_BEVAL_GUI) && (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) +# define FEAT_BEVAL_TIP /* balloon eval used for toolbar tooltip */ +#endif + +/* + * +balloon_eval_term Allow balloon expression evaluation in the terminal. + */ +#if defined(FEAT_HUGE) && defined(FEAT_TIMERS) && \ + (defined(UNIX) || defined(VMS) || (defined(WIN32) && !defined(FEAT_GUI_W32))) +# define FEAT_BEVAL_TERM +#endif + +#if defined(FEAT_BEVAL_GUI) || defined(FEAT_BEVAL_TERM) +# define FEAT_BEVAL +#endif + +/* both Motif and Athena are X11 and share some code */ +#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA) +# define FEAT_GUI_X11 +#endif + +#if defined(FEAT_NETBEANS_INTG) +// NetBeans uses menus. +# if !defined(FEAT_MENU) +# define FEAT_MENU +# endif +#endif + +#if 0 +/* + * +footer Motif only: Add a message area at the bottom of the + * main window area. + */ +# define FEAT_FOOTER +#endif + +/* + * +autochdir 'autochdir' option. + */ +#if defined(FEAT_NETBEANS_INTG) || defined(FEAT_BIG) +# define FEAT_AUTOCHDIR +#endif + +/* + * +persistent_undo 'undofile', 'undodir' options, :wundo and :rundo, and + * implementation. + */ +#ifdef FEAT_NORMAL +# define FEAT_PERSISTENT_UNDO +#endif + +/* + * +filterpipe + */ +#if (defined(UNIX) && !defined(USE_SYSTEM)) \ + || (defined(WIN3264) && defined(FEAT_GUI_W32)) +# define FEAT_FILTERPIPE +#endif + +/* + * +vtp: Win32 virtual console. + */ +#if !defined(FEAT_GUI) && defined(WIN3264) +# define FEAT_VTP +#endif diff --git a/src/fileio.c b/src/fileio.c new file mode 100644 index 0000000..bf724f6 --- /dev/null +++ b/src/fileio.c @@ -0,0 +1,7872 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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 "vim.h" + +#if defined(__TANDEM) || defined(__MINT__) +# include /* for SSIZE_MAX */ +#endif + +#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 */ + +/* Is there any system that doesn't have access()? */ +#define USE_MCH_ACCESS + +static char_u *next_fenc(char_u **pp); +#ifdef FEAT_EVAL +static char_u *readfile_charconvert(char_u *fname, char_u *fenc, int *fdp); +#endif +#ifdef FEAT_VIMINFO +static void check_marks_read(void); +#endif +#ifdef FEAT_CRYPT +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); +#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, stat_T *s); +static int time_differs(long t1, long t2); + +#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 */ +#ifdef WIN3264 +# define FIO_CODEPAGE 0x20 /* convert MS-Windows codepage */ +# define FIO_PUT_CP(x) (((x) & 0xffff) << 16) /* put codepage in top word */ +# define FIO_GET_CP(x) (((x)>>16) & 0xffff) /* get codepage from top word */ +#endif +#ifdef MACOS_CONVERT +# define FIO_MACROMAN 0x20 /* convert MacRoman */ +#endif +#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 +#ifdef FEAT_CRYPT + buf_T *bw_buffer; /* buffer being written */ +#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); +#ifdef WIN3264 +static int get_win_fio_flags(char_u *ptr); +#endif +#ifdef MACOS_CONVERT +static int get_mac_fio_flags(char_u *ptr); +#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 lalloc, 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 + * READ_FIFO read from fifo/socket instead of a file + * + * return FAIL for failure, NOTDONE for directory (failure), or OK + */ + 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 read_fifo = (flags & READ_FIFO); + 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; +#ifdef FEAT_CRYPT + char_u *cryptkey = NULL; + int did_ask_for_key = FALSE; +#endif +#ifdef FEAT_PERSISTENT_UNDO + context_sha256_T sha_ctx; + int read_undo_file = FALSE; +#endif + int split = 0; /* number of split lines */ +#define UNKNOWN 0x0fffffff /* file size is unknown */ + 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; + stat_T st; + 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; + int try_dos; + int try_unix; + 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 */ +# ifdef FEAT_EVAL + int did_iconv = FALSE; /* TRUE when iconv() failed and trying + 'charconvert' next */ +# endif +#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; + + au_did_filetype = FALSE; /* reset before triggering any autocommands */ + + 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)) +#ifdef FEAT_EVAL + return aborting() ? FAIL : OK; +#else + return OK; +#endif + } + else if (apply_autocmds_exarg(EVENT_FILEREADCMD, sfname, sfname, + FALSE, NULL, eap)) +#ifdef FEAT_EVAL + return aborting() ? FAIL : OK; +#else + return OK; +#endif + + 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 && !read_fifo) + { +#ifdef UNIX + /* + * On Unix it is possible to read a directory, so we have to + * check for it before the mch_open(). + */ + perm = mch_getperm(fname); + if (perm >= 0 && !S_ISREG(perm) /* not a regular file ... */ + && !S_ISFIFO(perm) /* ... or fifo */ + && !S_ISSOCK(perm) /* ... or socket */ +# ifdef OPEN_CHR_FILES + && !(S_ISCHR(perm) && is_dev_fd_file(fname)) + /* ... or a character special file named /dev/fd/ */ +# endif + ) + { + int retval = FAIL; + + if (S_ISDIR(perm)) + { + filemess(curbuf, fname, (char_u *)_("is a directory"), 0); + retval = NOTDONE; + } + else + filemess(curbuf, fname, (char_u *)_("is not a file"), 0); + msg_end(); + msg_scroll = msg_save; + return retval; + } +#endif +#if defined(MSWIN) + /* + * MS-Windows allows opening a device, but we will probably get stuck + * trying to read it. + */ + if (!p_odev && mch_nodetype(fname) == NODE_WRITABLE) + { + filemess(curbuf, fname, (char_u *)_("is a device (disabled with 'opendevice' option)"), 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 && !read_fifo) + { + /* Remember time of file. */ + if (mch_stat((char *)fname, &st) >= 0) + { + buf_store_time(curbuf, &st, fname); + 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 = (st.st_mode & 0644) | 0600; +#endif +#ifdef FEAT_CW_EDITOR + /* Get the FSSpec on MacOS + * TODO: Update it properly when the buffer name changes + */ + (void)GetFSSpecFromPath(curbuf->b_ffname, &curbuf->b_FSSpec); +#endif +#ifdef VMS + curbuf->b_fab_rfm = st.st_fab_rfm; + curbuf->b_fab_rat = st.st_fab_rat; + curbuf->b_fab_mrs = st.st_fab_mrs; +#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); + } + +/* + * for UNIX: check readonly with perm and mch_access() + * for Amiga: check readonly by trying to open the file for writing + */ + file_readonly = FALSE; + if (read_stdin) + { +#if defined(MSWIN) + /* Force binary I/O on stdin to avoid CR-LF -> LF conversion. */ + setmode(0, O_BINARY); +#endif + } + else if (!read_buffer) + { +#ifdef USE_MCH_ACCESS + if ( +# ifdef UNIX + !(perm & 0222) || +# endif + mch_access((char *)fname, W_OK)) + file_readonly = TRUE; + fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); +#else + if (!newfile + || readonlymode + || (fd = mch_open((char *)fname, O_RDWR | O_EXTRA, 0)) < 0) + { + file_readonly = TRUE; + /* try to open ro */ + fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); + } +#endif + } + + if (fd < 0) /* cannot open at all */ + { +#ifndef UNIX + int isdir_f; +#endif + msg_scroll = msg_save; +#ifndef UNIX + /* + * On Amiga we can't open a directory, check here. + */ + isdir_f = (mch_isdir(fname)); + perm = mch_getperm(fname); /* check if the file exists */ + if (isdir_f) + { + 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. */ +#ifdef FEAT_QUICKFIX + if (!bt_dontwrite(curbuf)) +#endif + { + 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); +#ifdef FEAT_VIMINFO + /* Even though this is a new file, it might have been + * edited before and deleted. Get the old marks. */ + check_marks_read(); +#endif + /* 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 defined(FEAT_EVAL) + if (aborting()) /* autocmds may abort script processing */ + return FAIL; +#endif + 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. */ +#ifdef FEAT_QUICKFIX + if (!bt_dontwrite(curbuf)) +#endif + { + 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) + { + char_u *swap_fname = curbuf->b_ml.ml_mfp->mf_fname; + + /* + * If the group-read bit is set but not the world-read bit, then + * the group must be equal to the group of the original file. If + * we can't make that happen then reset the group-read bit. This + * avoids making the swap file readable to more users when the + * primary group of the user is too permissive. + */ + if ((swap_mode & 044) == 040) + { + stat_T swap_st; + + if (mch_stat((char *)swap_fname, &swap_st) >= 0 + && st.st_gid != swap_st.st_gid +# ifdef HAVE_FCHOWN + && fchown(curbuf->b_ml.ml_mfp->mf_fd, -1, st.st_gid) + == -1 +# endif + ) + swap_mode &= 0600; + } + + (void)mch_setperm(swap_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; + + try_mac = (vim_strchr(p_ffs, 'm') != NULL); + try_dos = (vim_strchr(p_ffs, 'd') != NULL); + try_unix = (vim_strchr(p_ffs, 'x') != NULL); + + 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); + /* autocommands may have changed it */ + try_mac = (vim_strchr(p_ffs, 'm') != NULL); + try_dos = (vim_strchr(p_ffs, 'd') != NULL); + try_unix = (vim_strchr(p_ffs, 'x') != NULL); + + if (msg_scrolled == n) + msg_scroll = m; + +#ifdef FEAT_EVAL + 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; + } +#endif + /* + * 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 | O_EXTRA, 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) + { + if (!is_not_a_term()) + { +#ifndef ALWAYS_USE_GUI + mch_msg(_("Vim: Reading from stdin...\n")); +#endif +#ifdef FEAT_GUI + /* Also write a message in the GUI window, if there is one. */ + if (gui.in_use && !gui.dying && !gui.starting) + { + p = (char_u *)_("Reading from stdin..."); + gui_write(p, (int)STRLEN(p)); + } +#endif + } + } + 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); + vim_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 || vim_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; + } + + /* + * 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) + vim_free(fenc); + fenc = (char_u *)""; + fenc_alloced = FALSE; + } + else + { + if (fenc_alloced) + vim_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) + { + mch_remove(tmpname); /* delete converted file */ + VIM_CLEAR(tmpname); + } + } + + /* + * 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 WIN3264 + /* + * Conversion from an MS-Windows codepage to UTF-8 or another codepage + * is handled with MultiByteToWideChar(). + */ + if (fio_flags == 0) + fio_flags = get_win_fio_flags(fenc); +#endif + +#ifdef MACOS_CONVERT + /* Conversion from Apple MacRoman to latin1 or UTF-8 */ + if (fio_flags == 0) + fio_flags = get_mac_fio_flags(fenc); +#endif + +#ifdef USE_ICONV + /* + * Try using iconv() if we can't convert internally. + */ + if (fio_flags == 0 +# ifdef FEAT_EVAL + && !did_iconv +# endif + ) + iconv_fd = (iconv_t)my_iconv_open( + enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc); +#endif + +#ifdef FEAT_EVAL + /* + * 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 + && !read_fifo +# 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 +#endif + { + 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 && !read_fifo && !keep_dest_enc); + + if (!skip_read) + { + linerest = 0; + filesize = 0; + skip_count = lines_to_skip; + read_count = lines_to_read; + conv_restlen = 0; +#ifdef FEAT_PERSISTENT_UNDO + read_undo_file = (newfile && (flags & READ_KEEP_UNDO) == 0 + && curbuf->b_ffname != NULL + && curbuf->b_p_udf + && !filtering + && !read_fifo + && !read_stdin + && !read_buffer); + if (read_undo_file) + sha256_start(&sha_ctx); +#endif +#ifdef FEAT_CRYPT + if (curbuf->b_cryptstate != NULL) + { + /* Need to free the state, but keep the key, don't want to ask for + * it again. */ + crypt_free_state(curbuf->b_cryptstate); + curbuf->b_cryptstate = NULL; + } +#endif + } + + 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) + { +#if defined(SSIZE_MAX) && (SSIZE_MAX < 0x10000L) + size = SSIZE_MAX; /* use max I/O size, 52K */ +#else + /* Use buffer >= 64K. Add linerest to double the size if the + * line gets very long, to avoid a lot of copying. But don't + * read more than 1 Mbyte at a time, so we can be interrupted. + */ + size = 0x10000L + linerest; + if (size > 0x100000L) + size = 0x100000L; +#endif + } + + /* Protect against the argument of lalloc() going negative. */ + if (size < 0 || size + linerest + 1 < 0 || linerest >= MAXCOL) + { + ++split; + *ptr = NL; /* split line by inserting a NL */ + size = 1; + } + else + { + if (!skip_read) + { + for ( ; size >= 10; size = (long)((long_u)size >> 1)) + { + if ((new_buffer = lalloc((long_u)(size + linerest + 1), + FALSE)) != NULL) + break; + } + if (new_buffer == NULL) + { + do_outofmem_msg((long_u)(size * 2 + linerest + 1)); + error = TRUE; + break; + } + if (linerest) /* copy characters from the previous buffer */ + mch_memmove(new_buffer, ptr - linerest, (size_t)linerest); + vim_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 */ +#ifdef WIN3264 + else if (fio_flags & FIO_CODEPAGE) + size = size / ICONV_MULT; /* also worst case */ +#endif +#ifdef MACOS_CONVERT + else if (fio_flags & FIO_MACROMAN) + size = size / ICONV_MULT; /* also worst case */ +#endif + + if (conv_restlen > 0) + { + /* Insert unconverted bytes from previous line. */ + mch_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); + } + +#ifdef FEAT_CRYPT + /* + * At start of file: Check for magic number of encryption. + */ + if (filesize == 0 && size > 0) + cryptkey = check_for_cryptkey(cryptkey, ptr, &size, + &filesize, newfile, sfname, + &did_ask_for_key); + /* + * Decrypt the read bytes. This is done before checking for + * EOF because the crypt layer may be buffering. + */ + if (cryptkey != NULL && curbuf->b_cryptstate != NULL + && size > 0) + { +# ifdef CRYPT_NOT_INPLACE + if (crypt_works_inplace(curbuf->b_cryptstate)) + { +# endif + crypt_decode_inplace(curbuf->b_cryptstate, ptr, size); +# ifdef CRYPT_NOT_INPLACE + } + else + { + char_u *newptr = NULL; + int decrypted_size; + + decrypted_size = crypt_decode_alloc( + curbuf->b_cryptstate, ptr, size, &newptr); + + /* If the crypt layer is buffering, not producing + * anything yet, need to read more. */ + if (decrypted_size == 0) + continue; + + if (linerest == 0) + { + /* Simple case: reuse returned buffer (may be + * NULL, checked later). */ + new_buffer = newptr; + } + else + { + long_u new_size; + + /* Need new buffer to add bytes carried over. */ + new_size = (long_u)(decrypted_size + linerest + 1); + new_buffer = lalloc(new_size, FALSE); + if (new_buffer == NULL) + { + do_outofmem_msg(new_size); + error = TRUE; + break; + } + + mch_memmove(new_buffer, buffer, linerest); + if (newptr != NULL) + mch_memmove(new_buffer + linerest, newptr, + decrypted_size); + } + + if (new_buffer != NULL) + { + vim_free(buffer); + buffer = new_buffer; + new_buffer = NULL; + line_start = buffer; + ptr = buffer + linerest; + } + size = decrypted_size; + } +# endif + } +#endif + + 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 + } + } + } + } + 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 +#ifdef FEAT_CRYPT + || (cryptkey != NULL + && filesize == crypt_get_header_len( + crypt_get_method_nr(curbuf))) +#endif + ) + && (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; + mch_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) + vim_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. */ + mch_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; + mch_memmove(line_start, buffer, (size_t)linerest); + size = (long)((char_u *)top - ptr); + } +#endif + +#ifdef WIN3264 + if (fio_flags & FIO_CODEPAGE) + { + char_u *src, *dst; + WCHAR ucs2buf[3]; + int ucs2len; + int codepage = FIO_GET_CP(fio_flags); + int bytelen; + int found_bad; + char replstr[2]; + + /* + * Conversion from an MS-Windows codepage or UTF-8 to UTF-8 or + * a codepage, using standard MS-Windows functions. This + * requires two steps: + * 1. convert from 'fileencoding' to ucs-2 + * 2. convert from ucs-2 to 'encoding' + * + * Because there may be illegal bytes AND an incomplete byte + * sequence at the end, we may have to do the conversion one + * character at a time to get it right. + */ + + /* Replacement string for WideCharToMultiByte(). */ + if (bad_char_behavior > 0) + replstr[0] = bad_char_behavior; + else + replstr[0] = '?'; + replstr[1] = NUL; + + /* + * Move the bytes to the end of the buffer, so that we have + * room to put the result at the start. + */ + src = ptr + real_size - size; + mch_memmove(src, ptr, size); + + /* + * Do the conversion. + */ + dst = ptr; + size = size; + while (size > 0) + { + found_bad = FALSE; + +# ifdef CP_UTF8 /* VC 4.1 doesn't define CP_UTF8 */ + if (codepage == CP_UTF8) + { + /* Handle CP_UTF8 input ourselves to be able to handle + * trailing bytes properly. + * Get one UTF-8 character from src. */ + bytelen = (int)utf_ptr2len_len(src, size); + if (bytelen > size) + { + /* Only got some bytes of a character. Normally + * it's put in "conv_rest", but if it's too long + * deal with it as if they were illegal bytes. */ + if (bytelen <= CONV_RESTLEN) + break; + + /* weird overlong byte sequence */ + bytelen = size; + found_bad = TRUE; + } + else + { + int u8c = utf_ptr2char(src); + + if (u8c > 0xffff || (*src >= 0x80 && bytelen == 1)) + found_bad = TRUE; + ucs2buf[0] = u8c; + ucs2len = 1; + } + } + else +# endif + { + /* We don't know how long the byte sequence is, try + * from one to three bytes. */ + for (bytelen = 1; bytelen <= size && bytelen <= 3; + ++bytelen) + { + ucs2len = MultiByteToWideChar(codepage, + MB_ERR_INVALID_CHARS, + (LPCSTR)src, bytelen, + ucs2buf, 3); + if (ucs2len > 0) + break; + } + if (ucs2len == 0) + { + /* If we have only one byte then it's probably an + * incomplete byte sequence. Otherwise discard + * one byte as a bad character. */ + if (size == 1) + break; + found_bad = TRUE; + bytelen = 1; + } + } + + if (!found_bad) + { + int i; + + /* Convert "ucs2buf[ucs2len]" to 'enc' in "dst". */ + if (enc_utf8) + { + /* From UCS-2 to UTF-8. Cannot fail. */ + for (i = 0; i < ucs2len; ++i) + dst += utf_char2bytes(ucs2buf[i], dst); + } + else + { + BOOL bad = FALSE; + int dstlen; + + /* From UCS-2 to "enc_codepage". If the + * conversion uses the default character "?", + * the data doesn't fit in this encoding. */ + dstlen = WideCharToMultiByte(enc_codepage, 0, + (LPCWSTR)ucs2buf, ucs2len, + (LPSTR)dst, (int)(src - dst), + replstr, &bad); + if (bad) + found_bad = TRUE; + else + dst += dstlen; + } + } + + if (found_bad) + { + /* Deal with bytes we can't convert. */ + if (can_retry) + goto rewind_retry; + if (conv_error == 0) + conv_error = readfile_linenr(linecnt, ptr, dst); + if (bad_char_behavior != BAD_DROP) + { + if (bad_char_behavior == BAD_KEEP) + { + mch_memmove(dst, src, bytelen); + dst += bytelen; + } + else + *dst++ = bad_char_behavior; + } + } + + src += bytelen; + size -= bytelen; + } + + if (size > 0) + { + /* An incomplete byte sequence remaining. */ + mch_memmove(conv_rest, src, size); + conv_restlen = size; + } + + /* The new size is equal to how much "dst" was advanced. */ + size = (long)(dst - ptr); + } + else +#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); + mch_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 = (unsigned)*--p << 24; + u8c += (unsigned)*--p << 16; + u8c += (unsigned)*--p << 8; + u8c += *--p; + } + else /* big endian */ + { + u8c = *--p; + u8c += (unsigned)*--p << 8; + u8c += (unsigned)*--p << 16; + u8c += (unsigned)*--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; + mch_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; + mch_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) + { + mch_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) + { + /* Reset the carriage return counter. */ + if (try_mac) + try_mac = 1; + + 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; + } + else if (*p == CAR && try_mac) + try_mac++; + } + + /* 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; + } + } + else if (fileformat == EOL_UNKNOWN && try_mac == 1) + /* Looking for CR but found no end-of-line markers at + * all: use the default format. */ + fileformat = default_fileformat(); + } + + /* 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(); + + /* if editing a new file: may set p_tx and p_ff */ + 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; + } +#ifdef FEAT_PERSISTENT_UNDO + if (read_undo_file) + sha256_update(&sha_ctx, line_start, len); +#endif + ++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 > line_start && ptr[-1] == CAR) + { + /* remove CR before NL */ + 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 + || vim_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; + } +#ifdef FEAT_PERSISTENT_UNDO + if (read_undo_file) + sha256_update(&sha_ctx, line_start, len); +#endif + ++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 + { +#ifdef FEAT_PERSISTENT_UNDO + if (read_undo_file) + sha256_update(&sha_ctx, line_start, len); +#endif + read_no_eol_lnum = ++lnum; + } + } + + if (set_options) + save_file_ff(curbuf); /* remember the current file format */ + +#ifdef FEAT_CRYPT + if (curbuf->b_cryptstate != NULL) + { + crypt_free_state(curbuf->b_cryptstate); + curbuf->b_cryptstate = NULL; + } + if (cryptkey != NULL && cryptkey != curbuf->b_p_key) + crypt_free_key(cryptkey); + /* Don't set cryptkey to NULL, it's used below as a flag that + * encryption was used. */ +#endif + + /* 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) + vim_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) + (void)fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC); + } +#endif + vim_free(buffer); + +#ifdef HAVE_DUP + if (read_stdin) + { + /* Use stderr for stdin, makes shell commands work. */ + close(0); + vim_ignored = dup(2); + } +#endif + + if (tmpname != NULL) + { + mch_remove(tmpname); /* delete converted file */ + vim_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)) + { +#ifdef FEAT_NETBEANS_INTG + netbeansFireChanges = 0; +#endif + ml_delete(curbuf->b_ml.ml_line_count, FALSE); +#ifdef FEAT_NETBEANS_INTG + netbeansFireChanges = 1; +#endif + --linecnt; + } + linecnt = curbuf->b_ml.ml_line_count - linecnt; + if (filesize == 0) + linecnt = 0; + if (newfile || read_buffer) + { + redraw_curbuf_later(NOT_VALID); +#ifdef FEAT_DIFF + /* After reading the text into the buffer the diff info needs to + * be updated. */ + diff_invalidate(curbuf); +#endif +#ifdef FEAT_FOLDING + /* All folds in the window are invalid now. Mark them for update + * before triggering autocommands. */ + foldUpdateAll(curwin); +#endif + } + else if (linecnt) /* appended at least one line */ + appended_lines_mark(from, linecnt); + +#ifndef ALWAYS_USE_GUI + /* + * 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(); + } +#endif + + 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; +#ifdef FEAT_VIMINFO + check_marks_read(); +#endif + 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 + if (S_ISFIFO(perm)) /* fifo */ + { + STRCAT(IObuff, _("[fifo]")); + c = TRUE; + } + if (S_ISSOCK(perm)) /* or socket */ + { + STRCAT(IObuff, _("[socket]")); + c = TRUE; + } +# 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; + } +#ifdef FEAT_CRYPT + if (cryptkey != NULL) + { + crypt_append_msg(curbuf); + c = TRUE; + } +#endif + if (conv_error != 0) + { + sprintf((char *)IObuff + STRLEN(IObuff), + _("[CONVERSION ERROR in line %ld]"), (long)conv_error); + c = TRUE; + } + else if (illegal_byte > 0) + { + sprintf((char *)IObuff + STRLEN(IObuff), + _("[ILLEGAL BYTE in line %ld]"), (long)illegal_byte); + c = TRUE; + } + else if (error) + { + STRCAT(IObuff, _("[READ ERRORS]")); + c = TRUE; + } + if (msg_add_fileformat(fileformat)) + c = TRUE; +#ifdef FEAT_CRYPT + if (cryptkey != NULL) + msg_add_lines(c, (long)linecnt, filesize + - crypt_get_header_len(crypt_get_method_nr(curbuf))); + else +#endif + msg_add_lines(c, (long)linecnt, filesize); + + VIM_CLEAR(keep_msg); + msg_scrolled_ign = TRUE; +#ifdef ALWAYS_USE_GUI + /* Don't show the message when reading stdin, it would end up in a + * message box (which might be shown when exiting!) */ + if (read_stdin || read_buffer) + p = msg_may_trunc(FALSE, IObuff); + else +#endif + p = (char_u *)msg_trunc_attr((char *)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; + +#ifdef WIN32 + /* + * Work around a weird problem: When a file has two links (only + * possible on NTFS) and we write through one link, then stat() it + * through the other link, the timestamp information may be wrong. + * It's correct again after reading the file, thus reset the timestamp + * here. + */ + if (newfile && !read_stdin && !read_buffer + && mch_stat((char *)fname, &st) >= 0) + { + buf_store_time(curbuf, &st, fname); + curbuf->b_mtime_read = curbuf->b_mtime; + } +#endif + } + msg_scroll = msg_save; + +#ifdef FEAT_VIMINFO + /* + * Get the marks before executing autocommands, so they can be used there. + */ + check_marks_read(); +#endif + + /* + * We remember if the last line of the read didn't have + * an eol even when 'binary' is off, to support turning 'fixeol' off, + * or writing the read again with 'binary' on. The latter 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(); + +#ifdef FEAT_PERSISTENT_UNDO + /* + * 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); + } +#endif + + if (!read_stdin && !read_fifo && (!read_buffer || sfname != NULL)) + { + 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 || (read_buffer && sfname != NULL)) + { + apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname, + FALSE, curbuf, eap); + if (!au_did_filetype && *curbuf->b_p_ft != NUL) + /* + * EVENT_FILETYPE was not triggered but the buffer already has a + * filetype. Trigger EVENT_FILETYPE using the existing filetype. + */ + apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname, + TRUE, curbuf); + } + else + apply_autocmds_exarg(EVENT_FILEREADPOST, sfname, sfname, + FALSE, NULL, eap); + if (msg_scrolled == n) + msg_scroll = m; +# ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return FAIL; +# endif + } + + if (recoverymode && error) + return FAIL; + return OK; +} + +#if defined(OPEN_CHR_FILES) || defined(PROTO) +/* + * 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. + */ + 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(). + * Returns OK or FAIL. + */ + int +prep_exarg(exarg_T *eap, buf_T *buf) +{ + eap->cmd = alloc(15 + (unsigned)STRLEN(buf->b_p_fenc)); + if (eap->cmd == NULL) + return FAIL; + + sprintf((char *)eap->cmd, "e ++enc=%s", buf->b_p_fenc); + eap->force_enc = 8; + eap->bad_char = buf->b_bad_char; + eap->force_ff = *buf->b_p_ff; + + eap->force_bin = buf->b_p_bin ? FORCE_BIN : FORCE_NOBIN; + eap->read_edit = FALSE; + eap->forceit = FALSE; + return OK; +} + +/* + * 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); + vim_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); + vim_free(r); + r = p; + } + } + if (r == NULL) /* out of memory */ + { + r = (char_u *)""; + *pp = NULL; + } + return r; +} + +#ifdef FEAT_EVAL +/* + * 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 *errmsg = NULL; + + tmpname = vim_tempname('r', FALSE); + if (tmpname == NULL) + errmsg = _("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 = _("Conversion with 'charconvert' failed"); + if (errmsg == NULL && (*fdp = mch_open((char *)tmpname, + O_RDONLY | O_EXTRA, 0)) < 0) + errmsg = _("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) + { + mch_remove(tmpname); /* delete converted file */ + VIM_CLEAR(tmpname); + } + } + + /* If the input file is closed, open it (caller should check for error). */ + if (*fdp < 0) + *fdp = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); + + return tmpname; +} +#endif + + +#ifdef FEAT_VIMINFO +/* + * 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; +} +#endif + +#if defined(FEAT_CRYPT) || defined(PROTO) +/* + * 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_nr_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 the cryptmethod local to the buffer. */ + crypt_set_cm_option(curbuf, method); + 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(_(need_key_msg), fname); + msg_scroll = TRUE; + crypt_check_method(method); + cryptkey = crypt_get_key(newfile, FALSE); + *did_ask = TRUE; + + /* check if empty key entered */ + if (cryptkey != NULL && *cryptkey == NUL) + { + if (cryptkey != curbuf->b_p_key) + vim_free(cryptkey); + cryptkey = NULL; + } + } + } + + if (cryptkey != NULL) + { + int header_len; + + curbuf->b_cryptstate = crypt_create_from_header( + method, cryptkey, ptr); + crypt_set_cm_option(curbuf, method); + + /* Remove cryptmethod specific header from the text. */ + header_len = crypt_get_header_len(method); + if (*sizep <= header_len) + /* invalid header, buffer can't be encrypted */ + return NULL; + *filesizep += header_len; + *sizep -= header_len; + mch_memmove(ptr, ptr + header_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; +} +#endif /* FEAT_CRYPT */ + +#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; +# ifdef NeXT + (void)utimes((char *)fname, tvp); +# else + (void)utimes((char *)fname, (const struct timeval *)&tvp); +# endif +# endif +# endif +} +#endif /* UNIX */ + +#if defined(VMS) && !defined(MIN) +/* Older DECC compiler for VAX doesn't define MIN() */ +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * Return TRUE if a file appears to be read-only from the file permissions. + */ + int +check_file_readonly( + char_u *fname, /* full path to file */ + int perm) /* known permissions on file */ +{ +#ifndef USE_MCH_ACCESS + int fd = 0; +#endif + + return ( +#ifdef USE_MCH_ACCESS +# ifdef UNIX + (perm & 0222) == 0 || +# endif + mch_access((char *)fname, W_OK) +#else + (fd = mch_open((char *)fname, O_RDWR | O_EXTRA, 0)) < 0 + ? TRUE : (close(fd), FALSE) +#endif + ); +} + + +/* + * 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 */ + stat_T st_old; + int prev_got_int = got_int; + int checking_conversion; + int file_readonly = FALSE; /* overwritten file is read-only */ + static char *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')"; +#if defined(UNIX) /*XXX fix me sometime? */ + 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 +#ifdef FEAT_PERSISTENT_UNDO + int write_undo_file = FALSE; + context_sha256_T sha_ctx; +#endif + unsigned int bkc = get_bkc_value(buf); + + 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 +#ifdef FEAT_CRYPT + write_info.bw_buffer = buf; +#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 +#ifdef FEAT_QUICKFIX + && !bt_nofile(buf) +#endif + && !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); + bufref_T bufref; + + /* + * 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); + set_bufref(&bufref, buf); + + if (append) + { + if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD, + sfname, sfname, FALSE, curbuf, eap))) + { +#ifdef FEAT_QUICKFIX + if (overwriting && bt_nofile(curbuf)) + nofile_err = TRUE; + else +#endif + 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 + { +#ifdef FEAT_QUICKFIX + if (overwriting && bt_nofile(curbuf)) + nofile_err = TRUE; + else +#endif + apply_autocmds_exarg(EVENT_BUFWRITEPRE, + sfname, sfname, FALSE, curbuf, eap); + } + } + else + { + if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEWRITECMD, + sfname, sfname, FALSE, curbuf, eap))) + { +#ifdef FEAT_QUICKFIX + if (overwriting && bt_nofile(curbuf)) + nofile_err = TRUE; + else +#endif + 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 (!bufref_valid(&bufref)) + buf = NULL; + if (buf == NULL || (buf->b_ml.ml_mfp == NULL && !empty_memline) + || did_cmd || nofile_err +#ifdef FEAT_EVAL + || aborting() +#endif + ) + { + --no_wait_return; + msg_scroll = msg_save; + if (nofile_err) + emsg(_("E676: No matching autocommands for acwrite buffer")); + + if (nofile_err +#ifdef FEAT_EVAL + || aborting() +#endif + ) + /* 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; + } +#ifdef FEAT_EVAL + if (!aborting()) +#endif + 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; + } + +#ifdef FEAT_NETBEANS_INTG + if (netbeans_active() && isNetbeansBuffer(buf)) + { + if (whole) + { + /* + * b_changed can be 0 after an undo, but we still need to write + * the buffer to NetBeans. + */ + if (buf->b_changed || isNetbeansModified(buf)) + { + --no_wait_return; /* may wait for return now */ + msg_scroll = msg_save; + netbeans_save_buffer(buf); /* no error checking... */ + return retval; + } + else + { + errnum = (char_u *)"E656: "; + errmsg = (char_u *)_("NetBeans disallows writes of unmodified buffers"); + buffer = NULL; + goto fail; + } + } + else + { + errnum = (char_u *)"E657: "; + errmsg = (char_u *)_("Partial writes disallowed for NetBeans buffers"); + buffer = NULL; + goto fail; + } + } +#endif + + 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 = alloc(BUFSIZE); + if (buffer == NULL) /* can't allocate big buffer, use small + * one (to be able to write when out of + * memory) */ + { + buffer = smallbuf; + bufsize = SMBUFSIZE; + } + else + bufsize = BUFSIZE; + + /* + * Get information about original file (if there is one). + */ +#if defined(UNIX) + st_old.st_dev = 0; + st_old.st_ino = 0; + perm = -1; + if (mch_stat((char *)fname, &st_old) < 0) + newfile = TRUE; + else + { + perm = st_old.st_mode; + if (!S_ISREG(st_old.st_mode)) /* not a file */ + { + if (S_ISDIR(st_old.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) + { +# if defined(MSWIN) + /* MS-Windows allows opening a device, but we will probably get stuck + * trying to write to it. */ + if (!p_odev) + { + errnum = (char_u *)"E796: "; + errmsg = (char_u *)_("writing to device disabled with 'opendevice' option"); + goto fail; + } +# endif + device = TRUE; + newfile = TRUE; + perm = -1; + } + else + { + perm = mch_getperm(fname); + if (perm < 0) + newfile = TRUE; + else if (mch_isdir(fname)) + { + errnum = (char_u *)"E502: "; + errmsg = (char_u *)_("is a directory"); + goto fail; + } + if (overwriting) + (void)mch_stat((char *)fname, &st_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 = check_file_readonly(fname, (int)perm); + + 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, &st_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); +#ifdef FEAT_WILDIGN + if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, sfname, ffname)) + dobackup = FALSE; +#endif + + /* + * 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) + { +#if defined(UNIX) || defined(WIN32) + stat_T st; +#endif + + if ((bkc & BKC_YES) || append) /* "yes" */ + backup_copy = TRUE; +#if defined(UNIX) || defined(WIN32) + else if ((bkc & 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 (st_old.st_nlink > 1 + || mch_lstat((char *)fname, &st) < 0 + || st.st_dev != st_old.st_dev + || st.st_ino != st_old.st_ino +# ifndef HAVE_FCHOWN + || st.st_uid != st_old.st_uid + || st.st_gid != st_old.st_gid +# endif + ) + backup_copy = TRUE; + else +# else +# ifdef WIN32 + /* On NTFS file systems hard links are possible. */ + if (mch_is_linked(fname)) + backup_copy = TRUE; + else +# endif +# 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 *)gettail(IObuff), "%d", i); + if (mch_lstat((char *)IObuff, &st) < 0) + 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 + vim_ignored = fchown(fd, st_old.st_uid, st_old.st_gid); +# endif + if (mch_stat((char *)IObuff, &st) < 0 + || st.st_uid != st_old.st_uid + || st.st_gid != st_old.st_gid + || (long)st.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); + mch_remove(IObuff); +# ifdef MSWIN + /* MS-Windows may trigger a virus scanner to open the + * file, we can't delete it then. Keep trying for half a + * second. */ + { + int try; + + for (try = 0; try < 10; ++try) + { + if (mch_lstat((char *)IObuff, &st) < 0) + break; + ui_delay(50L, TRUE); /* wait 50 msec */ + mch_remove(IObuff); + } + } +# endif + } + } + } + + /* + * Break symlinks and/or hardlinks if we've been asked to. + */ + if ((bkc & BKC_BREAKSYMLINK) || (bkc & BKC_BREAKHARDLINK)) + { +# ifdef UNIX + int lstat_res; + + lstat_res = mch_lstat((char *)fname, &st); + + /* Symlinks. */ + if ((bkc & BKC_BREAKSYMLINK) + && lstat_res == 0 + && st.st_ino != st_old.st_ino) + backup_copy = FALSE; + + /* Hardlinks. */ + if ((bkc & BKC_BREAKHARDLINK) + && st_old.st_nlink > 1 + && (lstat_res != 0 || st.st_ino == st_old.st_ino)) + backup_copy = FALSE; +# else +# if defined(WIN32) + /* Symlinks. */ + if ((bkc & BKC_BREAKSYMLINK) && mch_is_symbolic_link(fname)) + backup_copy = FALSE; + + /* Hardlinks. */ + if ((bkc & BKC_BREAKHARDLINK) && mch_is_hard_link(fname)) + backup_copy = FALSE; +# endif +# endif + } + +#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 | O_EXTRA, 0)) >= 0) + { + int bfd; + char_u *copybuf, *wp; + int some_error = FALSE; + stat_T st_new; + char_u *dirp; + char_u *rootname; +#if defined(UNIX) || defined(WIN3264) + char_u *p; +#endif +#if defined(UNIX) + int did_set_shortname; + mode_t umask_save; +#endif + + copybuf = alloc(BUFSIZE + 1); + if (copybuf == NULL) + { + some_error = TRUE; /* out of memory */ + 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) + { +#ifdef UNIX + st_new.st_ino = 0; + st_new.st_dev = 0; + st_new.st_gid = 0; +#endif + + /* + * Isolate one directory name, using an entry in 'bdir'. + */ + (void)copy_option_part(&dirp, copybuf, BUFSIZE, ","); + +#if defined(UNIX) || defined(WIN3264) + p = copybuf + STRLEN(copybuf); + if (after_pathsep(copybuf, p) && p[-1] == p[-2]) + // Ends with '//', use full path + if ((p = make_percent_swname(copybuf, fname)) != NULL) + { + backup = modname(p, backup_ext, FALSE); + vim_free(p); + } +#endif + rootname = get_file_in_dir(fname, copybuf); + if (rootname == NULL) + { + some_error = TRUE; /* out of memory */ + goto nobackup; + } + +#if defined(UNIX) + did_set_shortname = FALSE; +#endif + + /* + * May try twice if 'shortname' not set. + */ + for (;;) + { + /* + * Make the backup file name. + */ + if (backup == NULL) + backup = buf_modname((buf->b_p_sn || buf->b_shortname), + rootname, backup_ext, FALSE); + if (backup == NULL) + { + vim_free(rootname); + some_error = TRUE; /* out of memory */ + goto nobackup; + } + + /* + * Check if backup file already exists. + */ + if (mch_stat((char *)backup, &st_new) >= 0) + { +#ifdef UNIX + /* + * Check if backup file is same as original file. + * May happen when modname() gave the same file back. + * E.g. silly link, or file name-length reached. + * If we don't check here, we either ruin the file + * when copying or erase it after writing. jw. + */ + if (st_new.st_dev == st_old.st_dev + && st_new.st_ino == st_old.st_ino) + { + VIM_CLEAR(backup); /* no backup file to delete */ + /* + * may try again with 'shortname' set + */ + if (!(buf->b_shortname || buf->b_p_sn)) + { + buf->b_shortname = TRUE; + did_set_shortname = TRUE; + continue; + } + /* setting shortname didn't help */ + if (did_set_shortname) + buf->b_shortname = FALSE; + break; + } +#endif + + /* + * 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' + && mch_stat((char *)backup, &st_new) >= 0) + --*wp; + /* They all exist??? Must be something wrong. */ + if (*wp == 'a') + VIM_CLEAR(backup); + } + } + break; + } + vim_free(rootname); + + /* + * Try to create the backup file + */ + if (backup != NULL) + { + /* remove old backup, if present */ + mch_remove(backup); + /* Open with O_EXCL to avoid the file being created while + * we were sleeping (symlink hacker attack?). Reset umask + * if possible to avoid mch_setperm() below. */ +#ifdef UNIX + umask_save = umask(0); +#endif + bfd = mch_open((char *)backup, + O_WRONLY|O_CREAT|O_EXTRA|O_EXCL|O_NOFOLLOW, + perm & 0777); +#ifdef UNIX + (void)umask(umask_save); +#endif + if (bfd < 0) + VIM_CLEAR(backup); + else + { + /* Set file protection same as original file, but + * strip s-bit. Only needed if umask() wasn't used + * above. */ +#ifndef UNIX + (void)mch_setperm(backup, perm & 0777); +#else + /* + * 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 (st_new.st_gid != st_old.st_gid +# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */ + && fchown(bfd, (uid_t)-1, st_old.st_gid) != 0 +# endif + ) + mch_setperm(backup, + (perm & 0707) | ((perm & 07) << 3)); +# if defined(HAVE_SELINUX) || defined(HAVE_SMACK) + 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, st_old.st_atime, st_old.st_mtime); +#endif +#ifdef HAVE_ACL + mch_set_acl(backup, acl); +#endif +#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) + mch_copy_sec(fname, backup); +#endif + break; + } + } + } + nobackup: + close(fd); /* ignore errors for closing read file */ + vim_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, ","); + +#if defined(UNIX) || defined(WIN3264) + p = IObuff + STRLEN(IObuff); + if (after_pathsep(IObuff, p) && p[-1] == p[-2]) + // path ends with '//', use full path + if ((p = make_percent_swname(IObuff, fname)) != NULL) + { + backup = modname(p, backup_ext, FALSE); + vim_free(p); + } +#endif + if (backup == NULL) + { + rootname = get_file_in_dir(fname, IObuff); + if (rootname == NULL) + backup = NULL; + else + { + backup = buf_modname( + (buf->b_p_sn || buf->b_shortname), + rootname, backup_ext, FALSE); + vim_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 && mch_getperm(backup) >= 0) + { + p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext); + if (p < backup) /* empty file name ??? */ + p = backup; + *p = 'z'; + while (*p > 'a' && mch_getperm(backup) >= 0) + --*p; + /* They all exist??? Must be something wrong! */ + if (*p == 'a') + VIM_CLEAR(backup); + } + } + 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; + + VIM_CLEAR(backup); /* don't do the rename below */ + } + } + 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) && st_old.st_uid == getuid() + && vim_strchr(p_cpo, CPO_FWRITE) == NULL) + { + perm |= 0200; + (void)mch_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; +#ifdef FEAT_TITLE + need_maketitle = TRUE; /* set window title later */ +#endif + 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; + } + } + +#ifdef VMS + vms_remove_version(fname); /* remove version */ +#endif + /* 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 + = lalloc((long_u)write_info.bw_conv_buflen, TRUE); + if (write_info.bw_conv_buf == NULL) + end = 0; + } + } + +#ifdef WIN3264 + if (converted && wb_flags == 0 && (wb_flags = get_win_fio_flags(fenc)) != 0) + { + /* Convert UTF-8 -> UCS-2 and UCS-2 -> DBCS. Worst-case * 4: */ + write_info.bw_conv_buflen = bufsize * 4; + write_info.bw_conv_buf + = lalloc((long_u)write_info.bw_conv_buflen, TRUE); + if (write_info.bw_conv_buf == NULL) + end = 0; + } +#endif + +#ifdef MACOS_CONVERT + if (converted && wb_flags == 0 && (wb_flags = get_mac_fio_flags(fenc)) != 0) + { + write_info.bw_conv_buflen = bufsize * 3; + write_info.bw_conv_buf + = lalloc((long_u)write_info.bw_conv_buflen, TRUE); + if (write_info.bw_conv_buf == NULL) + end = 0; + } +#endif + +#if defined(FEAT_EVAL) || defined(USE_ICONV) + 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 + = lalloc((long_u)write_info.bw_conv_buflen, TRUE); + if (write_info.bw_conv_buf == NULL) + end = 0; + write_info.bw_first = TRUE; + } +# ifdef FEAT_EVAL + else +# endif +# endif + +# ifdef FEAT_EVAL + /* + * 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', FALSE); + if (wfname == NULL) /* Can't write without a tempfile! */ + { + errmsg = (char_u *)_("E214: Can't find temp file for writing"); + goto restore_backup; + } + } +# endif + } +#endif + if (converted && wb_flags == 0 +#ifdef USE_ICONV + && write_info.bw_iconv_fd == (iconv_t)-1 +# endif +# ifdef FEAT_EVAL + && wfname == fname +# endif + ) + { + if (!forceit) + { + errmsg = (char_u *)_("E213: Cannot convert (add ! to write without conversion)"); + goto restore_backup; + } + notconverted = TRUE; + } + + /* + * If conversion is taking place, we may first pretend to write and check + * for conversion errors. Then loop again to write for real. + * When not doing conversion this writes for real right away. + */ + for (checking_conversion = TRUE; ; checking_conversion = FALSE) + { + /* + * There is no need to check conversion when: + * - there is no conversion + * - we make a backup file, that can be restored in case of conversion + * failure. + */ + if (!converted || dobackup) + checking_conversion = FALSE; + + if (checking_conversion) + { + /* Make sure we don't write anything. */ + fd = -1; + write_info.bw_fd = fd; + } + else + { +#ifdef HAVE_FTRUNCATE +# define TRUNC_ON_OPEN 0 +#else +# define TRUNC_ON_OPEN O_TRUNC +#endif + /* + * 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 | O_EXTRA | (append + ? (forceit ? (O_APPEND | O_CREAT) : O_APPEND) + : (O_CREAT | TRUNC_ON_OPEN)) + , 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 mch_remove() will fail. + */ + if (errmsg == NULL) + { +#ifdef UNIX + stat_T st; + + /* Don't delete the file when it's a hard or symbolic link. + */ + if ((!newfile && st_old.st_nlink > 1) + || (mch_lstat((char *)fname, &st) == 0 + && (st.st_dev != st_old.st_dev + || st.st_ino != st_old.st_ino))) + 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 (st_old.st_uid != getuid() + || st_old.st_gid != getgid()) + perm &= 0777; +#endif + if (!append) /* don't remove when appending */ + mch_remove(wfname); + continue; + } + } + } + +restore_backup: + { + stat_T st; + + /* + * 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 (mch_stat((char *)fname, &st) < 0) + vim_rename(backup, fname); + /* if original file does exist throw away the copy + */ + if (mch_stat((char *)fname, &st) >= 0) + mch_remove(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 && mch_stat((char *)fname, &st) < 0) + end = 0; + } + + if (wfname != fname) + vim_free(wfname); + goto fail; + } + write_info.bw_fd = fd; + +#if defined(UNIX) + { + stat_T st; + + /* Double check we are writing the intended file before making + * any changes. */ + if (overwriting + && (!dobackup || backup_copy) + && fname == wfname + && perm >= 0 + && mch_fstat(fd, &st) == 0 + && st.st_ino != st_old.st_ino) + { + close(fd); + errmsg = (char_u *)_("E949: File changed while writing"); + goto fail; + } + } +#endif +#ifdef HAVE_FTRUNCATE + if (!append) + vim_ignored = ftruncate(fd, (off_t)0); +#endif + +#if defined(WIN3264) + if (backup != NULL && overwriting && !append) + { + if (backup_copy) + (void)mch_copy_file_attribute(wfname, backup); + else + (void)mch_copy_file_attribute(backup, wfname); + } + + if (!overwriting && !append) + { + if (buf->b_ffname != NULL) + (void)mch_copy_file_attribute(buf->b_ffname, wfname); + /* Should copy resource fork */ + } +#endif + +#ifdef FEAT_CRYPT + if (*buf->b_p_key != NUL && !filtering) + { + char_u *header; + int header_len; + + buf->b_cryptstate = crypt_create_for_writing( + crypt_get_method_nr(buf), + buf->b_p_key, &header, &header_len); + if (buf->b_cryptstate == NULL || header == NULL) + end = 0; + else + { + /* Write magic number, so that Vim knows how this file is + * encrypted when reading it back. */ + 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; + vim_free(header); + } + } +#endif + } + errmsg = NULL; + + 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; + +#ifdef FEAT_PERSISTENT_UNDO + write_undo_file = (buf->b_p_udf + && overwriting + && !append + && !filtering + && reset_changed + && !checking_conversion); + if (write_undo_file) + /* Prepare for computing the hash value of the text. */ + sha256_start(&sha_ctx); +#endif + + 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; +#ifdef FEAT_PERSISTENT_UNDO + if (write_undo_file) + sha256_update(&sha_ctx, ptr + 1, + (UINT32_T)(STRLEN(ptr + 1) + 1)); +#endif + 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 || !buf->b_p_fixeol) + && (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; + } + } +#ifdef VMS + /* + * On VMS there is a problem: newlines get added when writing + * blocks at a time. Fix it by writing a line at a time. + * This is much slower! + * Explanation: VAX/DECC RTL insists that records in some RMS + * structures end with a newline (carriage return) character, and + * if they don't it adds one. + * With other RMS structures it works perfect without this fix. + */ + if (buf->b_fab_rfm == FAB$C_VFC + || ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0)) + { + int b2write; + + buf->b_fab_mrs = (buf->b_fab_mrs == 0 + ? MIN(4096, bufsize) + : MIN(buf->b_fab_mrs, bufsize)); + + b2write = len; + while (b2write > 0) + { + write_info.bw_len = MIN(b2write, buf->b_fab_mrs); + if (buf_write_bytes(&write_info) == FAIL) + { + end = 0; + break; + } + b2write -= MIN(b2write, buf->b_fab_mrs); + } + write_info.bw_len = bufsize; + nchars += len; + s = buffer; + len = 0; + } +#endif + } + if (len > 0 && end > 0) + { + write_info.bw_len = len; + if (buf_write_bytes(&write_info) == FAIL) + end = 0; /* write error */ + nchars += len; + } + + /* Stop when writing done or an error was encountered. */ + if (!checking_conversion || end == 0) + break; + + /* If no error happened until now, writing should be ok, so loop to + * really write the buffer. */ + } + + /* If we started writing, finish writing. Also when an error was + * encountered. */ + if (!checking_conversion) + { +#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 *)_(e_fsync); + end = 0; + } +#endif + +#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) + /* 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 + stat_T st; + + /* Don't change the owner when it's already OK, some systems remove + * permission or ACL stuff. */ + if (mch_stat((char *)wfname, &st) < 0 + || st.st_uid != st_old.st_uid + || st.st_gid != st_old.st_gid) + { + /* changing owner might not be possible */ + vim_ignored = fchown(fd, st_old.st_uid, -1); + /* if changing group fails clear the group permissions */ + if (fchown(fd, -1, st_old.st_gid) == -1 && perm > 0) + perm &= ~070; + } +# endif + buf_setino(buf); + } + else if (!buf->b_dev_valid) + /* Set the inode when creating a new file. */ + buf_setino(buf); +#endif + +#ifdef UNIX + if (made_writable) + perm &= ~0200; /* reset 'w' bit for security reasons */ +#endif +#ifdef HAVE_FCHMOD + /* set permission of new file same as old file */ + if (perm >= 0) + (void)mch_fsetperm(fd, perm); +#endif + if (close(fd) != 0) + { + errmsg = (char_u *)_("E512: Close failed"); + end = 0; + } + +#ifndef HAVE_FCHMOD + /* set permission of new file same as old file */ + if (perm >= 0) + (void)mch_setperm(wfname, perm); +#endif +#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). + * On Solaris, with ZFS and the aclmode property set to "discard" (the + * default), chmod() discards all part of a file's ACL that don't + * represent the mode of the file. It's non-trivial for us to discover + * whether we're in that situation, so we simply always re-set the ACL. + */ +# ifndef HAVE_SOLARIS_ZFS_ACL + if (!backup_copy) +# endif + mch_set_acl(wfname, acl); +#endif +#ifdef FEAT_CRYPT + if (buf->b_cryptstate != NULL) + { + crypt_free_state(buf->b_cryptstate); + buf->b_cryptstate = NULL; + } +#endif + +#if defined(FEAT_EVAL) + 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; + } + } + mch_remove(wfname); + vim_free(wfname); + } +#endif + } + + if (end == 0) + { + /* + * Error encountered. + */ + 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 %ld (make 'fenc' empty to override)"), + (long)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 | O_EXTRA, 0)) >= 0) + { + if ((write_info.bw_fd = mch_open((char *)fname, + O_WRONLY | O_CREAT | O_TRUNC | O_EXTRA, + 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) || defined(VMS)) + 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 %ld;"), + (long)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; +#ifdef FEAT_CRYPT + if (wb_flags & FIO_ENCRYPTED) + { + crypt_append_msg(buf); + c = TRUE; + } +#endif + 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((char_u *)msg_trunc_attr((char *)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); + /* b:changedtick is always incremented in unchanged() but that + * should not trigger a TextChanged event. */ + if (buf->b_last_changedtick + 1 == CHANGEDTICK(buf)) + buf->b_last_changedtick = CHANGEDTICK(buf); + 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 *)buf_modname((buf->b_p_sn || buf->b_shortname), + fname, p_pm, FALSE); + + if (backup != NULL) + { + stat_T st; + + /* + * 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 (mch_stat(org, &st) < 0) + { + vim_rename(backup, (char_u *)org); + VIM_CLEAR(backup); /* don't delete the file */ +#ifdef UNIX + set_file_time((char_u *)org, st_old.st_atime, st_old.st_mtime); +#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_EXTRA | 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) + { + mch_setperm((char_u *)org, mch_getperm(fname) & 0777); + vim_free(org); + } + } + + /* + * Remove the backup unless 'backup' option is set + */ + if (!p_bk && backup != NULL && mch_remove(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; + + vim_free(backup); + if (buffer != smallbuf) + vim_free(buffer); + vim_free(fenc_tofree); + vim_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); + mch_memmove(IObuff, errnum, (size_t)numlen); + } + STRCAT(IObuff, errmsg); + emsg((char *)IObuff); + if (errmsg_allocated) + vim_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 (mch_stat((char *)fname, &st_old) >= 0) + { + buf_store_time(buf, &st_old, fname); + buf->b_mtime_read = buf->b_mtime; + } + } + } + msg_scroll = msg_save; + +#ifdef FEAT_PERSISTENT_UNDO + /* + * 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); + } +#endif + +#ifdef FEAT_EVAL + if (!should_abort(retval)) +#else + if (!got_int) +#endif + { + 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); + +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + retval = FALSE; +#endif + } + + 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); +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return FAIL; +#endif + 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); +#ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return FAIL; +#endif + + /* 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, NULL); + 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)) + vim_snprintf((char *)p, IOSIZE - (p - IObuff), + "%ldL, %lldC", lnum, (long_long_T)nchars); + else + { + sprintf((char *)p, NGETTEXT("%ld line, ", "%ld lines, ", lnum), lnum); + p += STRLEN(p); + vim_snprintf((char *)p, IOSIZE - (p - IObuff), + NGETTEXT("%lld character", "%lld characters", nchars), + (long_long_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, stat_T *st) +{ + if (buf->b_mtime_read != 0 + && time_differs((long)st->st_mtime, 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; + mch_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; + mch_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; + mch_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 WIN3264 + else if (flags & FIO_CODEPAGE) + { + /* + * Convert UTF-8 or codepage to UCS-2 and then to MS-Windows + * codepage. + */ + char_u *from; + size_t fromlen; + char_u *to; + int u8c; + BOOL bad = FALSE; + int needed; + + 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; + mch_memmove(from, ip->bw_rest, (size_t)ip->bw_restlen); + mch_memmove(from + ip->bw_restlen, buf, (size_t)len); + } + else + { + from = buf; + fromlen = len; + } + + to = ip->bw_conv_buf; + if (enc_utf8) + { + /* Convert from UTF-8 to UCS-2, to the start of the buffer. + * The buffer has been allocated to be big enough. */ + while (fromlen > 0) + { + n = (int)utf_ptr2len_len(from, (int)fromlen); + if (n > (int)fromlen) /* incomplete byte sequence */ + break; + u8c = utf_ptr2char(from); + *to++ = (u8c & 0xff); + *to++ = (u8c >> 8); + fromlen -= n; + from += n; + } + + /* Copy remainder to ip->bw_rest[] to be used for the next + * call. */ + if (fromlen > CONV_RESTLEN) + { + /* weird overlong sequence */ + ip->bw_conv_error = TRUE; + return FAIL; + } + mch_memmove(ip->bw_rest, from, fromlen); + ip->bw_restlen = (int)fromlen; + } + else + { + /* Convert from enc_codepage to UCS-2, to the start of the + * buffer. The buffer has been allocated to be big enough. */ + ip->bw_restlen = 0; + needed = MultiByteToWideChar(enc_codepage, + MB_ERR_INVALID_CHARS, (LPCSTR)from, (int)fromlen, + NULL, 0); + if (needed == 0) + { + /* When conversion fails there may be a trailing byte. */ + needed = MultiByteToWideChar(enc_codepage, + MB_ERR_INVALID_CHARS, (LPCSTR)from, (int)fromlen - 1, + NULL, 0); + if (needed == 0) + { + /* Conversion doesn't work. */ + ip->bw_conv_error = TRUE; + return FAIL; + } + /* Save the trailing byte for the next call. */ + ip->bw_rest[0] = from[fromlen - 1]; + ip->bw_restlen = 1; + } + needed = MultiByteToWideChar(enc_codepage, MB_ERR_INVALID_CHARS, + (LPCSTR)from, (int)(fromlen - ip->bw_restlen), + (LPWSTR)to, needed); + if (needed == 0) + { + /* Safety check: Conversion doesn't work. */ + ip->bw_conv_error = TRUE; + return FAIL; + } + to += needed * 2; + } + + fromlen = to - ip->bw_conv_buf; + buf = to; +# ifdef CP_UTF8 /* VC 4.1 doesn't define CP_UTF8 */ + if (FIO_GET_CP(flags) == CP_UTF8) + { + /* Convert from UCS-2 to UTF-8, using the remainder of the + * conversion buffer. Fails when out of space. */ + for (from = ip->bw_conv_buf; fromlen > 1; fromlen -= 2) + { + u8c = *from++; + u8c += (*from++ << 8); + to += utf_char2bytes(u8c, to); + if (to + 6 >= ip->bw_conv_buf + ip->bw_conv_buflen) + { + ip->bw_conv_error = TRUE; + return FAIL; + } + } + len = (int)(to - buf); + } + else +# endif + { + /* Convert from UCS-2 to the codepage, using the remainder of + * the conversion buffer. If the conversion uses the default + * character "0", the data doesn't fit in this encoding, so + * fail. */ + len = WideCharToMultiByte(FIO_GET_CP(flags), 0, + (LPCWSTR)ip->bw_conv_buf, (int)fromlen / sizeof(WCHAR), + (LPSTR)to, (int)(ip->bw_conv_buflen - fromlen), 0, + &bad); + if (bad) + { + ip->bw_conv_error = TRUE; + return FAIL; + } + } + } +#endif + +#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; + mch_memmove(from, ip->bw_rest, (size_t)ip->bw_restlen); + mch_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; + mch_memmove(fp, ip->bw_rest, (size_t)ip->bw_restlen); + mch_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) + mch_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 (ip->bw_fd < 0) + /* Only checking conversion, which is OK if we get here. */ + return OK; + +#ifdef FEAT_CRYPT + if (flags & FIO_ENCRYPTED) + { + /* Encrypt the data. Do it in-place if possible, otherwise use an + * allocated buffer. */ +# ifdef CRYPT_NOT_INPLACE + if (crypt_works_inplace(ip->bw_buffer->b_cryptstate)) + { +# endif + crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len); +# ifdef CRYPT_NOT_INPLACE + } + else + { + char_u *outbuf; + + len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf); + if (len == 0) + return OK; /* Crypt layer is buffering, will flush later. */ + wlen = write_eintr(ip->bw_fd, outbuf, len); + vim_free(outbuf); + return (wlen < len) ? FAIL : OK; + } +# endif + } +#endif + + 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; +} + +#ifdef WIN3264 +/* + * Check "ptr" for a MS-Windows codepage name and return the FIO_ flags needed + * for the conversion MS-Windows can do for us. Also accept "utf-8". + * Used for conversion between 'encoding' and 'fileencoding'. + */ + static int +get_win_fio_flags(char_u *ptr) +{ + int cp; + + /* Cannot do this when 'encoding' is not utf-8 and not a codepage. */ + if (!enc_utf8 && enc_codepage <= 0) + return 0; + + cp = encname2codepage(ptr); + if (cp == 0) + { +# ifdef CP_UTF8 /* VC 4.1 doesn't define CP_UTF8 */ + if (STRCMP(ptr, "utf-8") == 0) + cp = CP_UTF8; + else +# endif + return 0; + } + return FIO_PUT_CP(cp) | FIO_CODEPAGE; +} +#endif + +#ifdef MACOS_CONVERT +/* + * Check "ptr" for a Carbon supported encoding and return the FIO_ flags + * needed for the internal conversion to/from utf-8 or latin1. + */ + static int +get_mac_fio_flags(char_u *ptr) +{ + if ((enc_utf8 || STRCMP(p_enc, "latin1") == 0) + && (enc_canon_props(ptr) & ENC_MACROMAN)) + return FIO_MACROMAN; + return 0; +} +#endif + +/* + * 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); +} + +/* + * Try to find a shortname by comparing the fullname with the current + * directory. + * Returns "full_path" or pointer into "full_path" if shortened. + */ + char_u * +shorten_fname1(char_u *full_path) +{ + char_u *dirname; + char_u *p = full_path; + + dirname = alloc(MAXPATHL); + if (dirname == NULL) + return full_path; + if (mch_dirname(dirname, MAXPATHL) == OK) + { + p = shorten_fname(full_path, dirname); + if (p == NULL || *p == NUL) + p = full_path; + } + vim_free(dirname); + return p; +} + +/* + * Try to find a shortname by comparing the fullname with the current + * directory. + * Returns NULL if not shorter name possible, pointer into "full_path" + * otherwise. + */ + char_u * +shorten_fname(char_u *full_path, char_u *dir_name) +{ + int len; + char_u *p; + + if (full_path == NULL) + return NULL; + len = (int)STRLEN(dir_name); + if (fnamencmp(dir_name, full_path, len) == 0) + { + p = full_path + len; +#if defined(MSWIN) + /* + * MSWIN: when a file is in the root directory, dir_name will end in a + * slash, since C: by itself does not define a specific dir. In this + * case p may already be correct. + */ + if (!((len > 2) && (*(p - 2) == ':'))) +#endif + { + if (vim_ispathsep(*p)) + ++p; +#ifndef VMS /* the path separator is always part of the path */ + else + p = NULL; +#endif + } + } +#if defined(MSWIN) + /* + * When using a file in the current drive, remove the drive name: + * "A:\dir\file" -> "\dir\file". This helps when moving a session file on + * a floppy from "A:\dir" to "B:\dir". + */ + else if (len > 3 + && TOUPPER_LOC(full_path[0]) == TOUPPER_LOC(dir_name[0]) + && full_path[1] == ':' + && vim_ispathsep(full_path[2])) + p = full_path + 2; +#endif + else + p = NULL; + return p; +} + +/* + * Shorten filename of a buffer. + * 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_buf_fname(buf_T *buf, char_u *dirname, int force) +{ + char_u *p; + + if (buf->b_fname != NULL +#ifdef FEAT_QUICKFIX + && !bt_nofile(buf) +#endif + && !path_with_url(buf->b_fname) + && (force + || buf->b_sfname == NULL + || mch_isFullName(buf->b_sfname))) + { + if (buf->b_sfname != buf->b_ffname) + VIM_CLEAR(buf->b_sfname); + p = 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; + } +} + +/* + * Shorten filenames for all buffers. + */ + void +shorten_fnames(int force) +{ + char_u dirname[MAXPATHL]; + buf_T *buf; + + mch_dirname(dirname, MAXPATHL); + FOR_ALL_BUFFERS(buf) + { + shorten_buf_fname(buf, dirname, force); + + /* 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; +} + +#if (defined(FEAT_DND) && defined(FEAT_GUI_GTK)) \ + || defined(FEAT_GUI_MSWIN) \ + || defined(FEAT_GUI_MAC) \ + || defined(PROTO) +/* + * Shorten all filenames in "fnames[count]" by current directory. + */ + void +shorten_filenames(char_u **fnames, int count) +{ + int i; + char_u dirname[MAXPATHL]; + char_u *p; + + if (fnames == NULL || count < 1) + return; + mch_dirname(dirname, sizeof(dirname)); + for (i = 0; i < count; ++i) + { + if ((p = shorten_fname(fnames[i], dirname)) != NULL) + { + /* shorten_fname() returns pointer in given "fnames[i]". If free + * "fnames[i]" first, "p" becomes invalid. So we need to copy + * "p" first then free fnames[i]. */ + p = vim_strsave(p); + vim_free(fnames[i]); + fnames[i] = p; + } + } +} +#endif + +/* + * Add extension to file name - change path/fo.o.h to path/fo.o.h.ext or + * fo_o_h.ext for MSDOS or when shortname option set. + * + * 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 */ +{ + return buf_modname((curbuf->b_p_sn || curbuf->b_shortname), + fname, ext, prepend_dot); +} + + char_u * +buf_modname( + int shortname, /* use 8.3 file name */ + 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 (retval == NULL) + return NULL; + if (mch_dirname(retval, MAXPATHL) == FAIL || + (fnamelen = (int)STRLEN(retval)) == 0) + { + vim_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)); + if (retval == NULL) + return NULL; + STRCPY(retval, fname); +#ifdef VMS + vms_remove_version(retval); /* we do not need versions here */ +#endif + } + + /* + * search backwards until we hit a '/', '\' or ':' replacing all '.' + * by '_' for MSDOS or when shortname option set and ext starts with a dot. + * Then truncate what is after the '/', '\' or ':' to 8 characters for + * MSDOS and 26 characters for AMIGA, a lot more for UNIX. + */ + for (ptr = retval + fnamelen; ptr > retval; MB_PTR_BACK(retval, ptr)) + { + if (*ext == '.' +#ifdef USE_LONG_FNAME + && (!USE_LONG_FNAME || shortname) +#else + && shortname +#endif + ) + if (*ptr == '.') /* replace '.' by '_' */ + *ptr = '_'; + if (vim_ispathsep(*ptr)) + { + ++ptr; + break; + } + } + + /* the file name has at most BASENAMELEN characters. */ + if (STRLEN(ptr) > (unsigned)BASENAMELEN) + ptr[BASENAMELEN] = '\0'; + + s = ptr + STRLEN(ptr); + + /* + * For 8.3 file names we may have to reduce the length. + */ +#ifdef USE_LONG_FNAME + if (!USE_LONG_FNAME || shortname) +#else + if (shortname) +#endif + { + /* + * If there is no file name, or the file name ends in '/', and the + * extension starts with '.', put a '_' before the dot, because just + * ".ext" is invalid. + */ + if (fname == NULL || *fname == NUL + || vim_ispathsep(fname[STRLEN(fname) - 1])) + { + if (*ext == '.') + *s++ = '_'; + } + /* + * If the extension starts with '.', truncate the base name at 8 + * characters + */ + else if (*ext == '.') + { + if ((size_t)(s - ptr) > (size_t)8) + { + s = ptr + 8; + *s = '\0'; + } + } + /* + * If the extension doesn't start with '.', and the file name + * doesn't have an extension yet, append a '.' + */ + else if ((e = vim_strchr(ptr, '.')) == NULL) + *s++ = '.'; + /* + * If the extension doesn't start with '.', and there already is an + * extension, it may need to be truncated + */ + else if ((int)STRLEN(e) + extlen > 4) + s = e + 4 - extlen; + } +#if defined(USE_LONG_FNAME) || 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 && !shortname && *(e = gettail(retval)) != '.' +#ifdef USE_LONG_FNAME + && USE_LONG_FNAME +#endif + ) + { + 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. + * If the line is truncated then buf[size - 2] will not be NUL. + */ + 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 + vim_ignoredp = fgets_cr((char *)tbuf, FGETS_SIZE, fp); +#else + vim_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 + +/* + * 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; +#ifdef AMIGA + BPTR flock; +#endif + stat_T st; + long perm; +#ifdef HAVE_ACL + vim_acl_T acl; /* ACL from original file */ +#endif + int 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(gettail(from), gettail(to)) != 0) + use_tmp_file = TRUE; + else + return 0; + } + + /* + * Fail if the "from" file doesn't exist. Avoids that "to" is deleted. + */ + if (mch_stat((char *)from, &st) < 0) + return -1; + +#ifdef UNIX + { + stat_T st_to; + + /* 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. */ + if (mch_stat((char *)to, &st_to) >= 0 + && st.st_dev == st_to.st_dev + && st.st_ino == st_to.st_ino) + use_tmp_file = TRUE; + } +#endif +#ifdef WIN3264 + { + BY_HANDLE_FILE_INFORMATION info1, info2; + + /* It's possible for the source and destination to be the same file. + * In that case go through a temp file name. This makes rename("foo", + * "./foo") a no-op (in a complicated way). */ + if (win32_fileinfo(from, &info1) == FILEINFO_OK + && win32_fileinfo(to, &info2) == FILEINFO_OK + && info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber + && info1.nFileIndexHigh == info2.nFileIndexHigh + && info1.nFileIndexLow == info2.nFileIndexLow) + use_tmp_file = TRUE; + } +#endif + + if (use_tmp_file) + { + char 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 *)gettail((char_u *)tempname), "%d", n); + if (mch_stat(tempname, &st) < 0) + { + if (mch_rename((char *)from, tempname) == 0) + { + if (mch_rename(tempname, (char *)to) == 0) + return 0; + /* Strange, the second step failed. Try moving the + * file back and return failure. */ + mch_rename(tempname, (char *)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 + * mch_rename() work, on other systems it makes sure that we don't have + * two files when the mch_rename() fails. + */ + +#ifdef AMIGA + /* + * With MSDOS-compatible filesystems (crossdos, messydos) it is possible + * that the name of the "to" file is the same as the "from" file, even + * though the names are different. To avoid the chance of accidentally + * deleting the "from" file (horror!) we lock it during the remove. + * + * When used for making a backup before writing the file: This should not + * happen with ":w", because startscript() should detect this problem and + * set buf->b_shortname, causing modname() to return a correct ".bak" file + * name. This problem does exist with ":w filename", but then the + * original file will be somewhere else so the backup isn't really + * important. If autoscripting is off the rename may fail. + */ + flock = Lock((UBYTE *)from, (long)ACCESS_READ); +#endif + mch_remove(to); +#ifdef AMIGA + if (flock) + UnLock(flock); +#endif + + /* + * First try a normal rename, return if it works. + */ + if (mch_rename((char *)from, (char *)to) == 0) + return 0; + + /* + * Rename() failed, try copying the file. + */ + perm = mch_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|O_EXTRA, 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_EXTRA|O_NOFOLLOW, (int)perm); + if (fd_out == -1) + { + close(fd_in); +#ifdef HAVE_ACL + mch_free_acl(acl); +#endif + return -1; + } + + buffer = (char *)alloc(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; + } + + vim_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 */ + mch_setperm(to, perm); +#endif +#ifdef HAVE_ACL + mch_set_acl(to, acl); + mch_free_acl(acl); +#endif +#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) + mch_copy_sec(from, to); +#endif + if (errmsg != NULL) + { + semsg(errmsg, to); + return -1; + } + mch_remove(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_ALL_BUFFERS(buf) + { + /* Only check buffers in a window. */ + if (buf->b_nwindows > 0) + { + bufref_T bufref; + + set_bufref(&bufref, buf); + n = buf_check_timestamp(buf, focus); + if (didit < n) + didit = n; + if (n > 0 && !bufref_valid(&bufref)) + { + /* Autocommands have removed the buffer, start at the + * first one again. */ + buf = firstbuf; + continue; + } + } + } + --no_wait_return; + need_check_timestamps = FALSE; + if (need_wait_return && didit == 2) + { + /* make sure msg isn't overwritten */ + msg_puts("\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) + { + vim_free(p); + retval = FAIL; + break; + } + vim_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 UNUSED) /* called for GUI focus event */ +{ + stat_T st; + int stat_res; + int retval = 0; + char_u *path; + char *tbuf; + char *mesg = NULL; + char *mesg2 = ""; + int helpmesg = FALSE; + int reload = FALSE; + char *reason; +#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG) + int can_reload = FALSE; +#endif + off_T orig_size = buf->b_orig_size; + int orig_mode = buf->b_orig_mode; +#ifdef FEAT_GUI + int save_mouse_correct = need_mouse_correct; +#endif + static int busy = FALSE; + int n; +#ifdef FEAT_EVAL + char_u *s; +#endif + bufref_T bufref; + + set_bufref(&bufref, buf); + + /* 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 + || !bt_normal(buf) + || buf->b_saving + || busy +#ifdef FEAT_NETBEANS_INTG + || isNetbeansBuffer(buf) +#endif +#ifdef FEAT_TERMINAL + || buf->b_term != NULL +#endif + ) + return 0; + + if ( !(buf->b_flags & BF_NOTEDITED) + && buf->b_mtime != 0 + && ((stat_res = mch_stat((char *)buf->b_ffname, &st)) < 0 + || time_differs((long)st.st_mtime, buf->b_mtime) + || st.st_size != buf->b_orig_size +#ifdef HAVE_ST_MODE + || (int)st.st_mode != buf->b_orig_mode +#else + || mch_getperm(buf->b_ffname) != buf->b_orig_mode +#endif + )) + { + retval = 1; + + // set b_mtime to stop further warnings (e.g., when executing + // FileChangedShell autocmd) + if (stat_res < 0) + { + // When 'autoread' is set we'll check the file again to see if it + // re-appears. + buf->b_mtime = buf->b_p_ar; + buf->b_orig_size = 0; + buf->b_orig_mode = 0; + } + else + buf_store_time(buf, &st, buf->b_ffname); + + /* Don't do anything for a directory. Might contain the file + * explorer. */ + if (mch_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) && stat_res >= 0) + reload = TRUE; + else + { + if (stat_res < 0) + reason = "deleted"; + else if (bufIsChanged(buf)) + reason = "conflict"; + /* + * 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. + */ + 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; +#ifdef FEAT_EVAL + set_vim_var_string(VV_FCS_REASON, (char_u *)reason, -1); + set_vim_var_string(VV_FCS_CHOICE, (char_u *)"", -1); +#endif + ++allbuf_lock; + n = apply_autocmds(EVENT_FILECHANGEDSHELL, + buf->b_fname, buf->b_fname, FALSE, buf); + --allbuf_lock; + busy = FALSE; + if (n) + { + if (!bufref_valid(&bufref)) + emsg(_("E246: FileChangedShell autocommand deleted buffer")); +#ifdef FEAT_EVAL + 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 +#endif + return 2; + } + if (!n) + { + if (*reason == 'd') + mesg = _("E211: File \"%s\" no longer available"); + else + { + helpmesg = TRUE; +#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG) + can_reload = TRUE; +#endif + 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) + && vim_fexists(buf->b_ffname)) + { + retval = 1; + mesg = _("W13: Warning: File \"%s\" has been created after editing started"); + buf->b_flags |= BF_NEW_W; +#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG) + can_reload = TRUE; +#endif + } + + if (mesg != NULL) + { + path = home_replace_save(buf, buf->b_fname); + if (path != NULL) + { + if (!helpmesg) + mesg2 = ""; + tbuf = (char *)alloc((unsigned)(STRLEN(path) + STRLEN(mesg) + + STRLEN(mesg2) + 2)); + sprintf(tbuf, mesg, path); +#ifdef FEAT_EVAL + /* Set warningmsg here, before the unimportant and output-specific + * mesg2 has been appended. */ + set_vim_var_string(VV_WARNINGMSG, (char_u *)tbuf, -1); +#endif +#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG) + if (can_reload) + { + if (*mesg2 != NUL) + { + STRCAT(tbuf, "\n"); + STRCAT(tbuf, mesg2); + } + if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), + (char_u *)tbuf, + (char_u *)_("&OK\n&Load File"), 1, NULL, TRUE) == 2) + reload = TRUE; + } + else +#endif + 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(mesg2, HL_ATTR(HLF_W) + MSG_HIST); + msg_clr_eos(); + (void)msg_end(); + if (emsg_silent == 0) + { + out_flush(); +#ifdef FEAT_GUI + if (!focus) +#endif + /* 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; + } + + vim_free(path); + vim_free(tbuf); + } + } + + if (reload) + { + /* Reload the buffer. */ + buf_reload(buf, orig_mode); +#ifdef FEAT_PERSISTENT_UNDO + 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; + } +#endif + } + + /* Trigger FileChangedShell when the file was changed in any way. */ + if (bufref_valid(&bufref) && retval != 0) + (void)apply_autocmds(EVENT_FILECHANGEDSHELLPOST, + buf->b_fname, buf->b_fname, FALSE, buf); +#ifdef FEAT_GUI + /* restore this in case an autocommand has set it; it would break + * 'mousefocus' */ + need_mouse_correct = save_mouse_correct; +#endif + + 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; + bufref_T bufref; + 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. */ + if (prep_exarg(&ea, buf) == OK) + { + 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); + set_bufref(&bufref, savebuf); + 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) + { + semsg(_("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) != OK) + { +#if defined(FEAT_EVAL) + if (!aborting()) +#endif + semsg(_("E321: Could not reload \"%s\""), buf->b_fname); + if (savebuf != NULL && bufref_valid(&bufref) && 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); + } + } + } + vim_free(ea.cmd); + + if (savebuf != NULL && bufref_valid(&bufref)) + wipe_buffer(savebuf, FALSE); + +#ifdef FEAT_DIFF + /* Invalidate diff info if necessary. */ + diff_invalidate(curbuf); +#endif + + /* 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; +#ifdef FEAT_FOLDING + { + 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); + } +#endif + /* 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_time(buf_T *buf, stat_T *st, char_u *fname UNUSED) +{ + buf->b_mtime = (long)st->st_mtime; + buf->b_orig_size = st->st_size; +#ifdef HAVE_ST_MODE + buf->b_orig_mode = (int)st->st_mode; +#else + buf->b_orig_mode = mch_getperm(fname); +#endif +} + +/* + * 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(FEAT_EVAL) || defined(PROTO) +/* + * Delete "name" and everything in it, recursively. + * return 0 for succes, -1 if some file was not deleted. + */ + int +delete_recursive(char_u *name) +{ + int result = 0; + char_u **files; + int file_count; + int i; + char_u *exp; + + /* A symbolic link to a directory itself is deleted, not the directory it + * points to. */ + if ( +# if defined(UNIX) || defined(WIN32) + mch_isrealdir(name) +# else + mch_isdir(name) +# endif + ) + { + vim_snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); + exp = vim_strsave(NameBuff); + if (exp == NULL) + return -1; + if (gen_expand_wildcards(1, &exp, &file_count, &files, + EW_DIR|EW_FILE|EW_SILENT|EW_ALLLINKS|EW_DODOT|EW_EMPTYOK) == OK) + { + for (i = 0; i < file_count; ++i) + if (delete_recursive(files[i]) != 0) + result = -1; + FreeWild(file_count, files); + } + else + result = -1; + vim_free(exp); + (void)mch_rmdir(name); + } + else + result = mch_remove(name) == 0 ? 0 : -1; + + return result; +} +#endif + +#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) +{ + if (vim_tempdir != NULL) + { + /* remove the trailing path separator */ + gettail(vim_tempdir)[-1] = NUL; + delete_recursive(vim_tempdir); + VIM_CLEAR(vim_tempdir); + } +} + +/* + * 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; + + buf = alloc((unsigned)MAXPATHL + 2); + if (buf != NULL) + { + if (vim_FullName(tempdir, buf, MAXPATHL, FALSE) == FAIL) + STRCPY(buf, tempdir); + add_pathsep(buf); + vim_tempdir = vim_strsave(buf); + vim_free(buf); + } +} +#endif + +/* + * vim_tempname(): Return a unique name that can be used for a temp file. + * + * The temp file is NOT guaranteed to be created. If "keep" is FALSE it is + * guaranteed to NOT be 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 UNUSED, /* char to use in the name instead of '?' */ + int keep UNUSED) +{ +#ifdef USE_TMPNAM + char_u itmp[L_tmpnam]; /* use tmpnam() */ +#else + char_u itmp[TEMPNAMELEN]; +#endif + +#ifdef TEMPDIRNAMES + static char *(tempdirs[]) = {TEMPDIRNAMES}; + int i; +# ifndef EEXIST + stat_T st; +# endif + + /* + * 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". + * Skip the directory check if the expansion fails. */ + expand_env((char_u *)tempdirs[i], itmp, TEMPNAMELEN - 20); + if (itmp[0] != '$' && mch_isdir(itmp)) + { + /* directory exists */ + add_pathsep(itmp); + +# ifdef HAVE_MKDTEMP + { +# if defined(UNIX) || defined(VMS) + /* Make sure the umask doesn't remove the executable bit. + * "repl" has been reported to use "177". */ + mode_t umask_save = umask(077); +# endif + /* Leave room for filename */ + STRCAT(itmp, "vXXXXXX"); + if (mkdtemp((char *)itmp) != NULL) + vim_settempdir(itmp); +# if defined(UNIX) || defined(VMS) + (void)umask(umask_save); +# endif + } +# 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 = (mch_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) || defined(VMS) + mode_t umask_save; +# endif + + sprintf((char *)itmp + itmplen, "v%ld", 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 (mch_stat((char *)itmp, &st) >= 0) + continue; +# endif +# if defined(UNIX) || defined(VMS) + /* Make sure the umask doesn't remove the executable bit. + * "repl" has been reported to use "177". */ + umask_save = umask(077); +# endif + r = vim_mkdir(itmp, 0700); +# if defined(UNIX) || defined(VMS) + (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%ld", vim_tempdir, temp_count++); + return vim_strsave(itmp); + } + + return NULL; + +#else /* TEMPDIRNAMES */ + +# ifdef WIN3264 + char szTempFile[_MAX_PATH + 1]; + char buf4[4]; + char_u *retval; + char_u *p; + + STRCPY(itmp, ""); + if (GetTempPath(_MAX_PATH, szTempFile) == 0) + { + szTempFile[0] = '.'; /* GetTempPath() failed, use current dir */ + szTempFile[1] = NUL; + } + strcpy(buf4, "VIM"); + buf4[2] = extra_char; /* make it "VIa", "VIb", etc. */ + if (GetTempFileName(szTempFile, buf4, 0, (LPSTR)itmp) == 0) + return NULL; + if (!keep) + /* GetTempFileName() will create the file, we don't want that */ + (void)DeleteFile((LPSTR)itmp); + + /* Backslashes in a temp file name cause problems when filtering with + * "sh". NOTE: This also checks 'shellcmdflag' to help those people who + * didn't set 'shellslash'. */ + retval = vim_strsave(itmp); + if (*p_shcf == '-' || p_ssl) + for (p = retval; *p; ++p) + if (*p == '\\') + *p = '/'; + return retval; + +# else /* WIN3264 */ + +# 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; + +# ifdef VMS_TEMPNAM + /* mktemp() is not working on VMS. It seems to be + * a do-nothing function. Therefore we use tempnam(). + */ + sprintf((char *)itmp, "VIM%c", extra_char); + p = (char_u *)tempnam("tmp:", (char *)itmp); + if (p != NULL) + { + /* VMS will use '.LIS' if we don't explicitly specify an extension, + * and VIM will then be unable to find the file later */ + STRCPY(itmp, p); + STRCAT(itmp, ".txt"); + free(p); + } + else + return NULL; +# else + STRCPY(itmp, TEMPNAME); + if ((p = vim_strchr(itmp, '?')) != NULL) + *p = extra_char; + if (mktemp((char *)itmp) == NULL) + return NULL; +# endif +# endif + + return vim_strsave(itmp); +# endif /* WIN3264 */ +#endif /* TEMPDIRNAMES */ +} + +#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) +/* + * Convert all backslashes in fname to forward slashes in-place, unless when + * it looks like a URL. + */ + void +forward_slash(char_u *fname) +{ + char_u *p; + + if (path_with_url(fname)) + return; + 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 + +/* + * 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; + + regmatch.rm_ic = p_fic; /* ignore case if 'fileignorecase' is set */ + 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 (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) + *prog = regmatch.regprog; + else + vim_regfree(regmatch.regprog); + return result; +} + +#if defined(FEAT_WILDIGN) || defined(PROTO) +/* + * 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 = gettail(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); + vim_free(regpat); + if (match) + return TRUE; + } + return FALSE; +} +#endif + +/* + * 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 "\ ". + * + * Returns NULL when out of memory. + */ + 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 UNUSED) /* Don't use a backward slash as pathsep */ +{ + int size = 2; /* '^' at start, '$' at end */ + char_u *endp; + char_u *reg_pat; + char_u *p; + int i; + int nested = 0; + int add_dollar = TRUE; + + if (allow_dirs != NULL) + *allow_dirs = FALSE; + if (pat_end == NULL) + pat_end = pat + STRLEN(pat); + + 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 = alloc(size + 1); + if (reg_pat == NULL) + return NULL; + + i = 0; + + if (pat[0] == '*') + while (pat[0] == '*' && pat < pat_end - 1) + pat++; + else + reg_pat[i++] = '^'; + endp = pat_end - 1; + if (endp >= pat && *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 == '#' + || vim_isspace(*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 }.")); + VIM_CLEAR(reg_pat); + } + return reg_pat; +} + +#if defined(EINTR) || defined(PROTO) +/* + * Version of read() that retries when interrupted by EINTR (possibly + * by a SIGWINCH). + */ + long +read_eintr(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(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/fold.c b/src/fold.c new file mode 100644 index 0000000..7446f7c --- /dev/null +++ b/src/fold.c @@ -0,0 +1,3608 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * vim600: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 "vim.h" + +#if defined(FEAT_FOLDING) || defined(PROTO) + +/* 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 (last > win->w_buffer->b_ml.ml_line_count) + last = win->w_buffer->b_ml.ml_line_count; + 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); + +#ifdef FEAT_DIFF + 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); + } + } + } +#endif +} + + 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); + if (ga_grow(gap, 1) == OK) + { + fp = (fold_T *)gap->ga_data + i; + ga_init2(&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) == OK) + { + /* 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. */ + mch_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) + mch_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; + + if (disable_fold_update > 0) + return; + + /* Mark all folds from top to bot as maybe-small. */ + (void)foldFind(&wp->w_folds, top, &fp); + while (fp < (fold_T *)wp->w_folds.ga_data + wp->w_folds.ga_len + && fp->fd_top < bot) + { + fp->fd_small = MAYBE; + ++fp; + } + + if (foldmethodIsIndent(wp) + || foldmethodIsExpr(wp) + || foldmethodIsMarker(wp) +#ifdef FEAT_DIFF + || foldmethodIsDiff(wp) +#endif + || 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_init2(&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_POS(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. + * + * Return FAIL if the operation cannot be completed, otherwise OK. + */ + void +cloneFoldGrowArray(garray_T *from, garray_T *to) +{ + int i; + fold_T *from_p; + fold_T *to_p; + + ga_init2(to, from->ga_itemsize, from->ga_growsize); + if (from->ga_len == 0 || ga_grow(to, from->ga_len) == FAIL) + return; + + 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) +{ +#ifdef FEAT_DIFF + 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); + } + } + } +#endif + + 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) + mch_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; + if (ga_grow(gap, (int)(moved - 1)) == OK) + { + /* 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) + mch_memmove(fp + moved, fp + 1, + sizeof(fold_T) * (gap->ga_len - (idx + 1))); + /* move the contained folds one level up */ + mch_memmove(fp, nfp, (size_t)(sizeof(fold_T) * moved)); + vim_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 */ + if (amount == MAXLNUM) + { + foldMarkAdjustRecurse(&fp->fd_nested, + line1 - fp->fd_top, + line2 - fp->fd_top, + amount, + amount_after + (fp->fd_top - top)); + fp->fd_len -= line2 - fp->fd_top + 1; + fp->fd_top = line1; + } + else + { + foldMarkAdjustRecurse(&fp->fd_nested, + line1 - fp->fd_top, + line2 - fp->fd_top, + amount, + amount_after - amount); + 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"); + int line_is_comment = FALSE; + + /* 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) + { +#if defined(FEAT_COMMENTS) + /* Check if the line ends with an unclosed comment */ + (void)skip_comment(line, FALSE, FALSE, &line_is_comment); +#endif + newline = alloc((unsigned)(line_len + markerlen + STRLEN(cms) + 1)); + if (newline == NULL) + return; + STRCPY(newline, line); + /* Append the marker to the end of the line */ + if (p == NULL || line_is_comment) + 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)); + if (newline != NULL) + { + 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[FOLD_TEXT_LEN]". + * 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; +#ifdef FEAT_EVAL + /* 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; + vim_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); + vim_free(text); + text = p; + } + } + } + if (text == NULL) +#endif + { + long count = (long)(lnume - lnum + 1); + + vim_snprintf((char *)buf, FOLD_TEXT_LEN, + NGETTEXT("+--%3ld line folded ", + "+--%3ld lines folded ", count), + count); + 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 int 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); +#ifdef FEAT_DIFF +static void foldlevelDiff(fline_T *flp); +#endif +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); + } + +#ifdef FEAT_DIFF + /* add the context for "diff" folding */ + if (foldmethodIsDiff(wp)) + { + if (top > diff_context) + top -= diff_context; + else + top = 1; + bot += diff_context; + } +#endif + + /* 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; +#ifdef FEAT_DIFF + else if (foldmethodIsDiff(wp)) + getlevel = foldlevelDiff; +#endif + 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( + 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) + { + (void)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) + { + (void)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 == firstlnum) + { + /* have found a fold beginning where we want */ + } + else if (fp->fd_top >= startlnum) + { + 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) + || firstlnum != startlnum) + { + linenr_T breakstart; + linenr_T breakend; + + /* + * Before there was a fold spanning from above + * startlnum to below firstlnum. This fold is valid + * above startlnum (because we are not updating + * that range), but there should now be a break in + * it. + * If the break is because we are now forced to + * start a new fold at the level "level" at line + * fline->lnum, then we need to split the fold at + * fline->lnum. + * If the break is because the range + * [startlnum, firstlnum) is now at a lower indent + * than "level", we need to split the fold in this + * range. + * Any splits have to be done recursively. + */ + if (firstlnum != startlnum) + { + breakstart = startlnum; + breakend = firstlnum; + } + else + { + breakstart = flp->lnum; + breakend = flp->lnum; + } + foldRemove(&fp->fd_nested, breakstart - fp->fd_top, + breakend - fp->fd_top); + i = (int)(fp - (fold_T *)gap->ga_data); + foldSplit(gap, i, breakstart, breakend - 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; + } + + if (fp->fd_top == startlnum && concat) + { + i = (int)(fp - (fold_T *)gap->ga_data); + if (i != 0) + { + fp2 = fp - 1; + if (fp2->fd_top + fp2->fd_len == fp->fd_top) + { + foldMerge(fp2, gap, fp); + fp = fp2; + } + } + } + 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); + if (foldInsert(gap, i) != OK) + return bot; + 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 - 1 > bot) + { + /* 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". + * Returns OK for success, FAIL for failure. + */ + static int +foldInsert(garray_T *gap, int i) +{ + fold_T *fp; + + if (ga_grow(gap, 1) != OK) + return FAIL; + fp = (fold_T *)gap->ga_data + i; + if (i < gap->ga_len) + mch_memmove(fp + 1, fp, sizeof(fold_T) * (gap->ga_len - i)); + ++gap->ga_len; + ga_init2(&fp->fd_nested, (int)sizeof(fold_T), 10); + return OK; +} + +/* 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. */ + if (foldInsert(gap, i + 1) == FAIL) + return; + 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) == OK) + { + 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 - 1 > bot) + { + /* 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); + } + } +} + +/* foldReverseOrder() {{{2 */ + static void +foldReverseOrder(garray_T *gap, linenr_T start_arg, linenr_T end_arg) +{ + fold_T *left, *right; + fold_T tmp; + linenr_T start = start_arg; + linenr_T end = end_arg; + + for (; start < end; start++, end--) + { + left = (fold_T *)gap->ga_data + start; + right = (fold_T *)gap->ga_data + end; + tmp = *left; + *left = *right; + *right = tmp; + } +} + +/* foldMoveRange() {{{2 */ +/* + * Move folds within the inclusive range "line1" to "line2" to after "dest" + * requires "line1" <= "line2" <= "dest" + * + * There are the following situations for the first fold at or below line1 - 1. + * 1 2 3 4 + * 1 2 3 4 + * line1 2 3 4 + * 2 3 4 5 6 7 + * line2 3 4 5 6 7 + * 3 4 6 7 8 9 + * dest 4 7 8 9 + * 4 7 8 10 + * 4 7 8 10 + * + * In the following descriptions, "moved" means moving in the buffer, *and* in + * the fold array. + * Meanwhile, "shifted" just means moving in the buffer. + * 1. not changed + * 2. truncated above line1 + * 3. length reduced by line2 - line1, folds starting between the end of 3 and + * dest are truncated and shifted up + * 4. internal folds moved (from [line1, line2] to dest) + * 5. moved to dest. + * 6. truncated below line2 and moved. + * 7. length reduced by line2 - dest, folds starting between line2 and dest are + * removed, top is moved down by move_len. + * 8. truncated below dest and shifted up. + * 9. shifted up + * 10. not changed + */ + + static void +truncate_fold(fold_T *fp, linenr_T end) +{ + end += 1; + foldRemove(&fp->fd_nested, end - fp->fd_top, MAXLNUM); + fp->fd_len = end - fp->fd_top; +} + +#define fold_end(fp) ((fp)->fd_top + (fp)->fd_len - 1) +#define valid_fold(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len)) +#define fold_index(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data))) + + void +foldMoveRange(garray_T *gap, linenr_T line1, linenr_T line2, linenr_T dest) +{ + fold_T *fp; + linenr_T range_len = line2 - line1 + 1; + linenr_T move_len = dest - line2; + int at_start = foldFind(gap, line1 - 1, &fp); + size_t move_start = 0, move_end = 0, dest_index = 0; + + if (at_start) + { + if (fold_end(fp) > dest) + { + /* Case 4 + * don't have to change this fold, but have to move nested folds. + */ + foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 - + fp->fd_top, dest - fp->fd_top); + return; + } + else if (fold_end(fp) > line2) + { + /* Case 3 + * Remove nested folds between line1 and line2 & reduce the + * length of fold by "range_len". + * Folds after this one must be dealt with. + */ + foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top, line2 - + fp->fd_top, MAXLNUM, -range_len); + fp->fd_len -= range_len; + } + else + /* Case 2 truncate fold, folds after this one must be dealt with. */ + truncate_fold(fp, line1 - 1); + + /* Look at the next fold, and treat that one as if it were the first + * after "line1" (because now it is). */ + fp = fp + 1; + } + + if (!valid_fold(fp, gap) || fp->fd_top > dest) + { + /* Case 10 + * No folds after "line1" and before "dest" + */ + return; + } + else if (fp->fd_top > line2) + { + for (; valid_fold(fp, gap) && fold_end(fp) <= dest; fp++) + /* Case 9. (for all case 9's) -- shift up. */ + fp->fd_top -= range_len; + + if (valid_fold(fp, gap) && fp->fd_top <= dest) + { + /* Case 8. -- ensure truncated at dest, shift up */ + truncate_fold(fp, dest); + fp->fd_top -= range_len; + } + return; + } + else if (fold_end(fp) > dest) + { + /* Case 7 -- remove nested folds and shrink */ + foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top, dest - + fp->fd_top, MAXLNUM, -move_len); + fp->fd_len -= move_len; + fp->fd_top += move_len; + return; + } + + /* Case 5 or 6 + * changes rely on whether there are folds between the end of + * this fold and "dest". + */ + move_start = fold_index(fp, gap); + + for (; valid_fold(fp, gap) && fp->fd_top <= dest; fp++) + { + if (fp->fd_top <= line2) + { + /* 1. 2. or 3. */ + if (fold_end(fp) > line2) + /* 2. or 3., truncate before moving */ + truncate_fold(fp, line2); + + fp->fd_top += move_len; + continue; + } + + /* Record index of the first fold after the moved range. */ + if (move_end == 0) + move_end = fold_index(fp, gap); + + if (fold_end(fp) > dest) + truncate_fold(fp, dest); + + fp->fd_top -= range_len; + } + + dest_index = fold_index(fp, gap); + + /* + * All folds are now correct, but not necessarily in the correct order. We + * must swap folds in the range [move_end, dest_index) with those in the + * range [move_start, move_end). + */ + if (move_end == 0) + /* There are no folds after those moved, hence no folds have been moved + * out of order. */ + return; + foldReverseOrder(gap, (linenr_T)move_start, (linenr_T)dest_index - 1); + foldReverseOrder(gap, (linenr_T)move_start, + (linenr_T)(move_start + dest_index - move_end - 1)); + foldReverseOrder(gap, (linenr_T)(move_start + dest_index - move_end), + (linenr_T)(dest_index - 1)); +} +#undef fold_end +#undef valid_fold +#undef fold_index + +/* 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) == OK) + { + 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(buf); + if (flp->lvl > flp->wp->w_p_fdn) + { + flp->lvl = flp->wp->w_p_fdn; + if (flp->lvl < 0) + flp->lvl = 0; + } +} + +/* foldlevelDiff() {{{2 */ +#ifdef FEAT_DIFF +/* + * 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; +} +#endif + +/* 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) +{ +#ifndef FEAT_EVAL + flp->start = FALSE; + flp->lvl = 0; +#else + 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 = (int)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; +#endif +} + +/* 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) +{ +#ifndef FEAT_SYN_HL + flp->start = 0; + flp->lvl = 0; +#else + 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; + } + } +#endif +} + +/* functions for storing the fold state in a View {{{1 */ +/* put_folds() {{{2 */ +#if defined(FEAT_SESSION) || defined(PROTO) +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, "%ld,%ldfold", fp->fd_top + off, + 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, "%ld", 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, "%ld", 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; +} +#endif /* FEAT_SESSION */ + +/* }}}1 */ +#endif /* defined(FEAT_FOLDING) || defined(PROTO) */ diff --git a/src/getchar.c b/src/getchar.c new file mode 100644 index 0000000..fe74dbf --- /dev/null +++ b/src/getchar.c @@ -0,0 +1,5323 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * 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 "vim.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 recordbuff = {{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 + TERMINAL)) ? (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 int read_readbuf(buffheader_T *buf, int advance); +static void init_typebuf(void); +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 int inchar(char_u *buf, int maxlen, long wait_time); +#ifdef FEAT_EVAL +static char_u *eval_map_expr(char_u *str, int c); +#endif + +/* + * 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; + vim_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 */ +{ + long_u count = 0; + char_u *p = NULL; + char_u *p2; + char_u *str; + buffblock_T *bp; + + /* compute the total length of the string */ + for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next) + count += (long_u)STRLEN(bp->b_str); + + if ((count || dozero) && (p = lalloc(count + 1, TRUE)) != NULL) + { + p2 = p; + for (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 */ +{ + buffblock_T *p; + long_u len; + + 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 */ + { + iemsg(_("E222: Add to read buffer")); + return; + } + else if (buf->bh_index != 0) + mch_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; + + if (buf->bh_space >= (int)slen) + { + len = (long_u)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; + p = (buffblock_T *)lalloc((long_u)(sizeof(buffblock_T) + len), + TRUE); + if (p == NULL) + return; /* no space, just forget it */ + 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, "%ld", 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; + } +#ifdef FEAT_GUI + else if (c == CSI) + { + /* Translate a CSI to a CSI - KS_EXTRA - KE_CSI sequence */ + temp[0] = CSI; + temp[1] = KS_EXTRA; + temp[2] = (int)KE_CSI; + temp[3] = NUL; + } +#endif + else + { + temp[0] = c; + temp[1] = NUL; + } + add_buff(buf, temp, -1L); + } +} + +/* 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}; + +/* + * 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; + vim_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); +} + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * Return TRUE if readbuf1 is empty. There may still be redo characters in + * redbuf2. + */ + int +readbuf1_empty(void) +{ + return (readbuf1.bh_first.b_next == NULL); +} +#endif + +/* + * 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(flush_buffers_T flush_typeahead) +{ + init_typebuf(); + + start_stuff(); + while (read_readbuffers(TRUE) != NUL) + ; + + if (flush_typeahead == FLUSH_MINIMAL) + { + // remove mapped characters at the start only + typebuf.tb_off += typebuf.tb_maplen; + typebuf.tb_len -= typebuf.tb_maplen; + } + else + { + // remove typeahead + if (flush_typeahead == FLUSH_INPUT) + // 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) != 0) + ; + typebuf.tb_off = MAXMAPLEN; + typebuf.tb_len = 0; +#if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL) + /* Reset the flag that text received from a client or from feedkeys() + * was inserted in the typeahead buffer. */ + typebuf_was_filled = FALSE; +#endif + } + 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. + */ + void +saveRedobuff(save_redo_T *save_redo) +{ + char_u *s; + + save_redo->sr_redobuff = redobuff; + redobuff.bh_first.b_next = NULL; + save_redo->sr_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_redo->sr_redobuff, FALSE); + if (s != NULL) + { + add_buff(&redobuff, s, -1L); + vim_free(s); + } +} + +/* + * Restore redobuff and old_redobuff from save_redobuff and save_old_redobuff. + * Used after executing autocommands and user functions. + */ + void +restoreRedobuff(save_redo_T *save_redo) +{ + free_buff(&redobuff); + redobuff = save_redo->sr_redobuff; + free_buff(&old_redobuff); + old_redobuff = save_redo->sr_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 >= ' ' +#ifndef EBCDIC + && *s < DEL /* EBCDIC: all chars above space are normal */ +#endif + && (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') +#ifdef EBCDIC + add_buff(&redobuff, (char_u *)"xf0", 3L); +#else + add_buff(&redobuff, (char_u *)"048", 3L); +#endif + 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); +} + +/* + * Append string "s" to the redo stuff buffer. + * CSI and K_SPECIAL must already have been escaped. + */ + void +stuffRedoReadbuff(char_u *s) +{ + add_buff(&readbuf2, s, -1L); +} + + void +stuffReadbuffLen(char_u *s, long len) +{ + add_buff(&readbuf1, s, len); +} + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * 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); + } + } +} +#endif + +/* + * 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; + } +#ifdef FEAT_GUI + if (c == CSI) /* escaped CSI */ + p += 2; +#endif + 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); + + /* the expression register should be re-evaluated */ + if (c == '=') + { + add_char_buff(&readbuf2, CAR); + cmd_silent = TRUE; + } + + 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 = MAXMAPLEN + 4; + 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); + + if (offset == 0 && addlen <= typebuf.tb_off) + { + /* + * Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off] + */ + typebuf.tb_off -= addlen; + mch_memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen); + } + else if (typebuf.tb_len == 0 && typebuf.tb_buflen + >= addlen + 3 * (MAXMAPLEN + 4)) + { + /* + * Buffer is empty and string fits in the existing buffer. + * Leave some space before and after, if possible. + */ + typebuf.tb_off = (typebuf.tb_buflen - addlen - 3 * (MAXMAPLEN + 4)) / 2; + mch_memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen); + } + else + { + /* + * 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. + */ + 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 */ + { + vim_free(s1); + return FAIL; + } + typebuf.tb_buflen = newlen; + + /* copy the old chars, before the insertion point */ + mch_memmove(s1 + newoff, typebuf.tb_buf + typebuf.tb_off, + (size_t)offset); + /* copy the new chars */ + mch_memmove(s1 + newoff + offset, str, (size_t)addlen); + /* copy the old chars, after the insertion point, including the NUL at + * the end */ + mch_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) + vim_free(typebuf.tb_buf); + typebuf.tb_buf = s1; + + mch_memmove(s2 + newoff, typebuf.tb_noremap + typebuf.tb_off, + (size_t)offset); + mch_memmove(s2 + newoff + offset + addlen, + typebuf.tb_noremap + typebuf.tb_off + offset, + (size_t)(typebuf.tb_len - offset)); + if (typebuf.tb_noremap != noremapbuf_init) + vim_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 +#if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL) + || typebuf_was_filled +#endif + )); +} + +/* + * 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) + { + mch_memmove(typebuf.tb_buf + MAXMAPLEN, + typebuf.tb_buf + typebuf.tb_off, (size_t)offset); + mch_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) */ + mch_memmove(typebuf.tb_buf + typebuf.tb_off + offset, + typebuf.tb_buf + i + len, + (size_t)(typebuf.tb_len - offset + 1)); + /* adjust typebuf.tb_noremap[] */ + mch_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; + } + +#if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL) + /* Reset the flag that text received from a client or from feedkeys() + * was inserted in the typeahead buffer. */ + typebuf_was_filled = FALSE; +#endif + 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 i; + static char_u buf[4]; + static int buflen = 0; + int todo = len; + + while (todo--) + { + buf[buflen++] = *s++; + + // When receiving a special key sequence, store it until we have all + // the bytes and we can decide what to do with it. + if (buflen == 1 && buf[0] == K_SPECIAL) + continue; + if (buflen == 2) + continue; + if (buflen == 3 && buf[1] == KS_EXTRA + && (buf[2] == KE_FOCUSGAINED || buf[2] == KE_FOCUSLOST)) + { + // Drop K_FOCUSGAINED and K_FOCUSLOST, they are not useful in a + // recording. + buflen = 0; + continue; + } + + /* Handle one byte at a time; no translation to be done. */ + for (i = 0; i < buflen; ++i) + updatescript(buf[i]); + + if (reg_recording != 0) + { + buf[buflen] = NUL; + add_buff(&recordbuff, buf, (long)buflen); + /* remember how many chars were last recorded */ + last_recorded_len += buflen; + } + buflen = 0; + } + may_sync_undo(); + +#ifdef FEAT_EVAL + /* output "debug mode" message next time in debug mode */ + debug_did_msg = FALSE; +#endif + + /* 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 = MAXMAPLEN + 4; /* can insert without realloc */ + 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) + internal_error("Free typebuf 1"); + else + vim_free(typebuf.tb_buf); + if (typebuf.tb_noremap == noremapbuf_init) + internal_error("Free typebuf 2"); + else + vim_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 */ +#ifdef FEAT_MOUSE +static int old_mouse_row; /* mouse_row related to old_char */ +static int old_mouse_col; /* mouse_col related to old_char */ +#endif + +/* + * 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; + } +#ifdef FEAT_EVAL + if (ignore_script) + /* Not reading from script, also don't open one. Warning message? */ + return; +#endif + + 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) + { + semsg(_(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 + +#if defined(FEAT_INS_EXPAND) || defined(PROTO) +/* + * Return TRUE when reading keys from a script file. + */ + int +using_script(void) +{ + return scriptin[curscript] != NULL; +} +#endif + +/* + * 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); +#ifdef FEAT_EVAL + if (may_garbage_collect) + garbage_collect(FALSE); +#endif +} + +/* + * 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; + +#ifdef FEAT_EVAL + /* Do garbage collection when garbagecollect() was called previously and + * we are now at the toplevel. */ + if (may_garbage_collect && want_garbage_collect) + garbage_collect(FALSE); +#endif + + /* + * 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; +#ifdef FEAT_MOUSE + mouse_row = old_mouse_row; + mouse_col = old_mouse_col; +#endif + } + else + { + mod_mask = 0x0; + last_recorded_len = 0; + for (;;) /* this is done twice if there are modifiers */ + { + int did_inc = FALSE; + + if (mod_mask +#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) + || im_is_preediting() +#endif + ) + { + /* no mapping after modifier has been read */ + ++no_mapping; + ++allow_keys; + did_inc = TRUE; /* mod_mask may change value */ + } + c = vgetorpeek(TRUE); + if (did_inc) + { + --no_mapping; + --allow_keys; + } + + /* Get two extra bytes for special keys */ + if (c == K_SPECIAL +#ifdef FEAT_GUI + || c == CSI +#endif + ) + { + 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); + +#if defined(FEAT_GUI_W32) && defined(FEAT_MENU) && defined(FEAT_TEAROFF) + /* Handle K_TEAROFF here, the caller of vgetc() doesn't need to + * know that a menu was torn off */ + if (c == K_TEAROFF) + { + char_u name[200]; + int i; + + /* get menu path, it ends with a */ + for (i = 0; (c = vgetorpeek(TRUE)) != '\r'; ) + { + name[i] = c; + if (i < 199) + ++i; + } + name[i] = NUL; + gui_make_tearoff(name); + continue; + } +#endif +#if defined(FEAT_GUI) && defined(FEAT_GUI_GTK) && defined(FEAT_MENU) + /* GTK: normally selects the menu, but it's passed until + * here to allow mapping it. Intercept and invoke the GTK + * behavior if it's not mapped. */ + if (c == K_F10 && gui.menubar != NULL) + { + gtk_menu_shell_select_first(GTK_MENU_SHELL(gui.menubar), FALSE); + continue; + } +#endif +#ifdef FEAT_GUI + /* Handle focus event here, so that the caller doesn't need to + * know about it. Return K_IGNORE so that we loop once (needed if + * 'lazyredraw' is set). */ + if (c == K_FOCUSGAINED || c == K_FOCUSLOST) + { + ui_focus_change(c == K_FOCUSGAINED); + c = K_IGNORE; + } + + /* Translate K_CSI to CSI. The special key is only used to avoid + * it being recognized as the start of a special key. */ + if (c == K_CSI) + c = CSI; +#endif + } + /* 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: +#ifdef WIN32 + // Can be either '.' or a ',', + // depending on the type of keypad. + c = MapVirtualKey(VK_DECIMAL, 2); break; +#else + c = '.'; break; +#endif + 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 +#ifdef FEAT_GUI + || buf[i] == CSI +#endif + ) + { + /* 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; + } + } + +#ifdef FEAT_EVAL + /* + * 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; +#endif +#ifdef FEAT_BEVAL_TERM + if (c != K_MOUSEMOVE && c != K_IGNORE) + { + /* Don't trigger 'balloonexpr' unless only the mouse was moved. */ + bevalexpr_due_set = FALSE; + ui_remove_balloon(); + } +#endif + + 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); + + if (c == K_PS) + /* Only handle the first pasted character. Drop the rest, since we + * don't know what to do with it. */ + c = bracketed_paste(PASTE_ONE_CHAR, FALSE, NULL); + + 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!. + * Returns NUL if no character is available. + */ + int +vpeekc(void) +{ + if (old_char != -1) + return old_char; + return vgetorpeek(FALSE); +} + +#if defined(FEAT_TERMRESPONSE) || defined(FEAT_TERMINAL) || defined(PROTO) +/* + * 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; +} +#endif + +#if defined(FEAT_INS_EXPAND) || defined(FEAT_EVAL) || defined(PROTO) +/* + * 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; +} +#endif + +/* + * Call vpeekc() without causing anything to be mapped. + * Return TRUE if a character is available, FALSE otherwise. + */ + int +char_avail(void) +{ + int retval; + +#ifdef FEAT_EVAL + /* When test_override("char_avail", 1) was called pretend there is no + * typeahead. */ + if (disable_char_avail_for_testing) + return FALSE; +#endif + ++no_mapping; + retval = vpeekc(); + --no_mapping; + return (retval != NUL); +} + +/* + * unget one character (can only be done once!) + */ + void +vungetc(int c) +{ + old_char = c; + old_mod_mask = mod_mask; +#ifdef FEAT_MOUSE + old_mouse_row = mouse_row; + old_mouse_col = mouse_col; +#endif +} + +/* + * Get a byte: + * 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. + * Return NUL if not. + * + * 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; +#ifdef FEAT_LOCALMAP + mapblock_T *mp2; +#endif + 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; +#ifdef FEAT_CMDL_INFO + int new_wcol, new_wrow; +#endif +#ifdef FEAT_GUI +# ifdef FEAT_MENU + int idx; +# endif + int shape_changed = FALSE; /* adjusted cursor shape */ +#endif + int n; +#ifdef FEAT_LANGMAP + int nolmaplen; +#endif + 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) + reg_executing = 0; + 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); + /* + * 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(FLUSH_INPUT); // 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 +#ifdef FEAT_INS_EXPAND + && !((ctrl_x_mode_not_default() + && vim_is_ctrl_x_key(c1)) + || ((compl_cont_status & CONT_LOCAL) + && (c1 == Ctrl_N || c1 == Ctrl_P))) +#endif + ) + { +#ifdef FEAT_LANGMAP + if (c1 == K_SPECIAL) + nolmaplen = 2; + else + { + LANGMAP_ADJUST(c1, + (State & (CMDLINE | INSERT)) == 0 + && get_real_state() != SELECTMODE); + nolmaplen = 0; + } +#endif +#ifdef FEAT_LOCALMAP + /* 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; + } +#else + mp = maphash[MAP_HASH(local_State, c1)]; +#endif + /* + * 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; +#ifdef FEAT_LOCALMAP + mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : +#endif + (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)) + { +#ifdef FEAT_LANGMAP + int nomap = nolmaplen; + int c2; +#endif + /* find the match length of this mapping */ + for (mlen = 1; mlen < typebuf.tb_len; ++mlen) + { +#ifdef FEAT_LANGMAP + 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) +#else + if (mp->m_keys[mlen] != + typebuf.tb_buf[typebuf.tb_off + mlen]) +#endif + 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 */ + { +#ifdef AMIGA /* check for window bounds report */ + if (typebuf.tb_maplen == 0 && (typebuf.tb_buf[ + typebuf.tb_off] & 0xff) == CSI) + { + for (s = typebuf.tb_buf + typebuf.tb_off + 1; + s < typebuf.tb_buf + typebuf.tb_off + + typebuf.tb_len + && (VIM_ISDIGIT(*s) || *s == ';' + || *s == ' '); + ++s) + ; + if (*s == 'r' || *s == '|') /* found one */ + { + del_typebuf((int)(s + 1 - + (typebuf.tb_buf + typebuf.tb_off)), 0); + /* get size and redraw screen */ + shell_resized(); + continue; + } + if (*s == NUL) /* need more characters */ + keylen = KEYLEN_PART_KEY; + } + if (keylen >= 0) +#endif + /* 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 */ + { +#if defined(FEAT_GUI) && defined(FEAT_MENU) + if (typebuf.tb_len >= 2 + && typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL + && typebuf.tb_buf[typebuf.tb_off + 1] + == KS_MENU) + { + /* + * Using a menu may cause a break in undo! + * It's like using gotchars(), but without + * recording or writing to a script file. + */ + may_sync_undo(); + del_typebuf(3, 0); + idx = get_menu_index(current_menu, local_State); + if (idx != MENU_INDEX_INVALID) + { + /* + * In Select mode and a Visual mode menu + * is used: Switch to Visual mode + * temporarily. Append K_SELECT to switch + * back to Select mode. + */ + if (VIsual_active && VIsual_select + && (current_menu->modes & VISUAL)) + { + VIsual_select = FALSE; + (void)ins_typebuf(K_SELECT_STRING, + REMAP_NONE, 0, TRUE, FALSE); + } + ins_typebuf(current_menu->strings[idx], + current_menu->noremap[idx], + 0, TRUE, + current_menu->silent[idx]); + } + } +#endif /* FEAT_GUI && FEAT_MENU */ + 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) + { +#ifdef FEAT_EVAL + int save_m_expr; + int save_m_noremap; + int save_m_silent; + char_u *save_m_keys; + char_u *save_m_str; +#else +# define save_m_noremap mp->m_noremap +# define save_m_silent mp->m_silent +#endif + + /* 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(FLUSH_MINIMAL); + 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); + } + +#ifdef FEAT_EVAL + /* 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 +#endif + 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 ( +#ifdef FEAT_EVAL + STRNCMP(s, save_m_keys != NULL + ? save_m_keys : mp->m_keys, + (size_t)keylen) +#else + STRNCMP(s, mp->m_keys, (size_t)keylen) +#endif + != 0) + noremap = REMAP_YES; + else + noremap = REMAP_SKIP; + i = ins_typebuf(s, noremap, + 0, TRUE, cmd_silent || save_m_silent); +#ifdef FEAT_EVAL + if (save_m_expr) + vim_free(s); +#endif + } +#ifdef FEAT_EVAL + vim_free(save_m_keys); + vim_free(save_m_str); +#endif + 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; +#ifdef FEAT_CMDL_INFO + new_wcol = curwin->w_wcol; + new_wrow = curwin->w_wrow; +#endif + 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)) == 0) + { + colnr_T col = 0, vcol; + char_u *ptr; + + if (mode_displayed) + { + unshowmode(TRUE); + mode_deleted = TRUE; + } +#ifdef FEAT_GUI + /* may show a different cursor shape */ + if (gui.in_use && State != NORMAL && !cmd_silent) + { + int save_State; + + save_State = State; + State = NORMAL; + gui_update_cursor(TRUE, FALSE); + State = save_State; + shape_changed = TRUE; + } +#endif + 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, 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 / curwin->w_width; + curwin->w_wcol %= curwin->w_width; + 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 = curwin->w_width - 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(); +#ifdef FEAT_CMDL_INFO + new_wcol = curwin->w_wcol; + new_wrow = curwin->w_wrow; +#endif + curwin->w_wcol = old_wcol; + curwin->w_wrow = old_wrow; + } + if (c < 0) + continue; /* end of input script reached */ + + /* Allow mapping for just typed characters. When we get here c + * is the number of extra bytes and typebuf.tb_len is 1. */ + for (n = 1; n <= c; ++n) + typebuf.tb_noremap[typebuf.tb_off + n] = RM_YES; + 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) + { +#ifdef FEAT_CMDWIN + static int tc = 0; +#endif + + /* 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; +#ifdef FEAT_TERMINAL + else if (terminal_is_active()) + c = K_CANCEL; +#endif + else if ((State & CMDLINE) +#ifdef FEAT_CMDWIN + || (cmdwin_type > 0 && tc == ESC) +#endif + ) + c = Ctrl_C; + else + c = ESC; +#ifdef FEAT_CMDWIN + tc = c; +#endif + 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. + */ +#ifdef FEAT_CMDL_INFO + i = 0; +#endif + 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; + } +#ifdef FEAT_CMDL_INFO + /* 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; +#endif + } + + /* this looks nice when typing a dead character map */ + if ((State & CMDLINE) +#if defined(FEAT_CRYPT) || defined(FEAT_EVAL) + && cmdline_star == 0 +#endif + && 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 + */ + if (typebuf.tb_len == 0) + // timedout may have been set while waiting for a mapping + // that has a RHS. + timedout = FALSE; + + 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))); + +#ifdef FEAT_CMDL_INFO + if (i != 0) + pop_showcmd(); +#endif + 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 HAVE_INPUT_METHOD + /* 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 && c != K_CANCEL) || (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(); + } + } +#ifdef FEAT_GUI + /* may unshow different cursor shape */ + if (gui.in_use && shape_changed) + gui_update_cursor(TRUE, FALSE); +#endif + + --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. + */ + static int +inchar( + char_u *buf, + int maxlen, + long wait_time) /* milli seconds */ +{ + int len = 0; /* init for GCC */ + int retesc = FALSE; /* return ESC with gotint */ + int script_char; + int tb_change_cnt = typebuf.tb_change_cnt; + + if (wait_time == -1L || wait_time > 100L) /* flush output before waiting */ + { + cursor_on(); + out_flush_cursor(FALSE, FALSE); +#if defined(FEAT_GUI) && defined(FEAT_MOUSESHAPE) + if (gui.in_use && postponed_mouseshape) + update_mouseshape(-1); +#endif + } + + /* + * 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 +#ifdef FEAT_EVAL + && !ignore_script +#endif + ) + { + +#ifdef MESSAGE_QUEUE + parse_queued_messages(); +#endif + + 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 and not just peeking. + */ + if (wait_time == -1L || wait_time > 10L) + 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 the typebuf was changed further down, it is like nothing was added by + * this call. */ + if (typebuf_changed(tb_change_cnt)) + return 0; + + /* Note the change in the typeahead buffer, this matters for when + * vgetorpeek() is called recursively, e.g. using getchar(1) in a timer + * function. */ + if (len > 0 && ++typebuf.tb_change_cnt == 0) + typebuf.tb_change_cnt = 1; + + return fix_input_buffer(buf, len); +} + +/* + * 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 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 + */ + for (i = len; --i >= 0; ++p) + { +#ifdef FEAT_GUI + /* When the GUI is used any character can come after a CSI, don't + * escape it. */ + if (gui.in_use && p[0] == CSI && i >= 2) + { + p += 2; + i -= 2; + } + /* When the GUI is not used CSI needs to be escaped. */ + else if (!gui.in_use && p[0] == CSI) + { + mch_memmove(p + 3, p + 1, (size_t)i); + *p++ = K_SPECIAL; + *p++ = KS_EXTRA; + *p = (int)KE_CSI; + len += 2; + } + else +#endif + if (p[0] == NUL || (p[0] == K_SPECIAL + /* timeout may generate K_CURSORHOLD */ + && (i < 2 || p[1] != KS_EXTRA || p[2] != (int)KE_CURSORHOLD) +#if defined(WIN3264) && !defined(FEAT_GUI) + /* Win32 console passes modifiers */ + && (i < 2 || p[1] != KS_MODIFIER) +#endif + )) + { + mch_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 the server_to_input_buf() + * or 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() +# if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL) + || typebuf_was_filled +# endif + ); +} +#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 :tmap mode is TERMINAL + * + * 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; +#ifdef FEAT_LOCALMAP + int did_local = FALSE; +#endif + 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; +#ifdef FEAT_EVAL + int expr = FALSE; +#endif + 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 , , ,