summaryrefslogtreecommitdiffstats
path: root/src/dosinst.h
blob: f1d3dc6eb5991c02a4e2d62d3c5512541f060243 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
2
![Vim Logo](https://github.com/vim/vim/blob/master/runtime/vimlogo.gif)

# 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`](http://vimdoc.sourceforge.net/htmldoc/develop.html#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):

File name       | Description
--------------- | -----------
alloc.c		| memory management
arglist.c	| handling argument list
autocmd.c	| autocommands
blob.c		| blob data type
buffer.c	| manipulating buffers (loaded files)
bufwrite.c	| writing a buffer to file
change.c	| handling changes to text
cindent.c	| C and Lisp indentation
clientserver.c	| client server functionality
clipboard.c	| handling the clipboard
cmdexpand.c	| command-line completion
cmdhist.c	| command-line history
debugger.c	| vim script debugger
diff.c		| diff mode (vimdiff)
drawline.c	| drawing a window line
drawscreen.c	| drawing the windows
eval.c		| expression evaluation
evalbuffer.c	| buffer related built-in functions
evalfunc.c	| built-in functions
evalvars.c	| vim variables
evalwindow.c	| window related built-in functions
fileio.c	| reading and writing files
filepath.c	| dealing with file names and paths
findfile.c	| search for files in 'path'
fold.c		| folding
getchar.c	| getting characters and key mapping
gc.c	    | garbage collection
help.c		| vim help related functions
highlight.c	| syntax highlighting
indent.c	| text indentation
insexpand.c	| Insert mode completion
locale.c	| locale/language handling
map.c		| mapping and abbreviations
mark.c		| marks
match.c		| highlight matching
float.c		| floating point functions
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
mouse.c		| handling the mouse
ops.c		| handling operators ("d", "y", "p")
option.c	| options
optionstr.c	| handling string options
popupmenu.c	| popup menu
popupwin.c	| popup window
profiler.c	| vim script profiler
quickfix.c	| quickfix commands (":make", ":cn")
regexp.c	| pattern matching
register.c	| handling registers
scriptfile.c	| runtime directory handling and sourcing scripts
screen.c	| lower level screen functions
search.c	| pattern searching
session.c	| sessions and views
sign.c		| signs
spell.c		| spell checking core
spellfile.c	| spell file handling
spellsuggest.c	| spell correction suggestions
strings.c	| string manipulation functions
syntax.c	| syntax and other highlighting
tag.c		| tags
term.c		| terminal handling, termcap codes
testing.c	| testing: assert and test functions
textformat.c	| text formatting
textobject.c	| text objects
textprop.c	| text properties
time.c		| time and timer functions
typval.c	| vim script type/value functions
undo.c		| undo and redo
usercmd.c	| user defined commands
userfunc.c	| user defined functions
viminfo.c	| viminfo handling
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:

	:
/* 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 uninstall.c
 */

#include <sys/stat.h>
#include <fcntl.h>

#ifndef UNIX_LINT
# include <io.h>
# include <ctype.h>

# include <direct.h>

# include <windows.h>
# include <shlobj.h>
#endif

#ifdef UNIX_LINT
// Running lint on Unix: Some things are missing.
char *searchpath(char *name);
#endif

#if defined(UNIX_LINT)
# include <unistd.h>
# include <errno.h>
#endif

#include "version.h"

#if defined(UNIX_LINT)
# define vim_mkdir(x, y) mkdir((char *)(x), y)
#else
# define vim_mkdir(x, y) _mkdir((char *)(x))
#endif

#define sleep(n) Sleep((n) * 1000)

// ----------------------------------------


#define BUFSIZE (MAX_PATH*2)		// 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

#ifdef __MINGW32__
# define UNUSED __attribute__((unused))
#else
# define UNUSED
#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)
{
    void *p;

    p = malloc(len);
    if (p == NULL)
    {
	printf("ERROR: out of memory\n");
	exit(1);
    }
    return p;
}

/*
 * 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
}

    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;
}

/*
 * 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] =
{
    {"