1 /*
2  * w32misc:  collection of unrelated, common win32 functions used by both
3  *           the console and GUI flavors of the editor.
4  *
5  * $Id: w32misc.c,v 1.65 2020/01/17 23:33:14 tom Exp $
6  */
7 
8 #include "estruct.h"
9 #include "edef.h"
10 #include "nefunc.h"
11 
12 #include <io.h>
13 #include <conio.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <process.h>
17 
18 #define CSHEXE           "csh.exe"
19 #define CSHEXE_LEN       (sizeof(CSHEXE) - 1)
20 #define HOST_95          0
21 #define HOST_NT          1
22 #define HOST_UNDEF       (-1)
23 #define SHEXE            "sh.exe"
24 #define SHEXE_LEN        (sizeof(SHEXE) - 1)
25 #define SHELL_C_LEN      (sizeof(" -c ") - 1)
26 
27 static int host_type = HOST_UNDEF;	/* nt or 95? */
28 #if !DISP_NTWIN
29 static W32_CHAR saved_title[256];
30 #endif
31 
32 /* ------------------------------------------------------------------ */
33 
34 #if DISP_NTWIN
35 int
stdin_data_available(void)36 stdin_data_available(void)
37 {
38     FILE *fp;
39     int rc = 0;
40     int fd1 = fileno(stdin);
41     int fd2;
42 
43     if (fd1 >= 0) {
44 	if ((fd2 = dup(fd1)) >= 0) {
45 	    if ((fp = fdopen(fd2, "r")) != NULL) {
46 		fclose(fp);
47 		rc = 1;
48 	    }
49 	}
50     }
51     return (rc);
52 }
53 
54 #define MAX_SIGS 6
55 
56 typedef void (*SIGFUNC_TYPE) (int);
57 
58 static SIGFUNC_TYPE saved_sigs[MAX_SIGS];
59 
60 static void
ignore_signals(void)61 ignore_signals(void)
62 {
63     saved_sigs[0] = signal(SIGILL, SIG_IGN);
64     saved_sigs[1] = signal(SIGFPE, SIG_IGN);
65     saved_sigs[2] = signal(SIGSEGV, SIG_IGN);
66     saved_sigs[3] = signal(SIGTERM, SIG_IGN);
67     saved_sigs[4] = signal(SIGBREAK, SIG_IGN);
68     saved_sigs[5] = signal(SIGABRT, SIG_IGN);
69 }
70 
71 static void
restore_signals(void)72 restore_signals(void)
73 {
74     (void) signal(SIGILL, saved_sigs[0]);
75     (void) signal(SIGFPE, saved_sigs[1]);
76     (void) signal(SIGSEGV, saved_sigs[2]);
77     (void) signal(SIGTERM, saved_sigs[3]);
78     (void) signal(SIGBREAK, saved_sigs[4]);
79     (void) signal(SIGABRT, saved_sigs[5]);
80 }
81 
82 #endif
83 
84 static void
set_host(void)85 set_host(void)
86 {
87     OSVERSIONINFO info;
88 
89     info.dwOSVersionInfoSize = sizeof(info);
90 #pragma warning(suppress: 28159)
91     GetVersionEx(&info);
92     host_type = (info.dwPlatformId == VER_PLATFORM_WIN32_NT) ?
93 	HOST_NT : HOST_95;
94 }
95 
96 int
is_winnt(void)97 is_winnt(void)
98 {
99     if (host_type == HOST_UNDEF)
100 	set_host();
101     return (host_type == HOST_NT);
102 }
103 
104 int
is_win95(void)105 is_win95(void)
106 {
107     if (host_type == HOST_UNDEF)
108 	set_host();
109     return (host_type == HOST_95);
110 }
111 
112 /*
113  * FUNCTION
114  *   mk_shell_cmd_str(char *cmd, int *allocd_mem, int prepend_shc)
115  *
116  *   cmd         - command string to be be passed to a Win32 command
117  *                 interpreter.
118  *
119  *   alloced_mem - Boolean, T -> returned string was allocated on heap.
120  *
121  *   prepend_shc - Boolean, T -> prepend "$SHELL -c" to cmd.
122  *
123  * DESCRIPTION
124  *   If the user's shell is a unix lookalike, then a command passed to
125  *   system() or CreateProcess() requires special preprocessing.
126  *   This extra processing is required because the aforementioned
127  *   functions pass a "raw", flat command line to the shell that is
128  *   _not_ broken up into the following four canonical argv components:
129  *
130  *     argv[0] = name of shell
131  *     argv[1] = name of shell
132  *     argv[2] = -c
133  *     argv[3] = cmd
134  *     argv[4] = NULL
135  *
136  *   Put another way, a true execlp() does not exist in the win32 world and,
137  *   therefore, cannot be called to effect sh -c "cmdstr".  Consequently,
138  *   when a unix shell (executing under win32) receives a "raw" command line,
139  *   the shell splits the raw command into words, performs its normal
140  *   expansions (file globbing, variable substitution, etc.) and then
141  *   removes all quote characters.  After that, the shell executes the
142  *   command.  This scenario means that if the user tries the following
143  *   command in vile:
144  *
145  *       :!echo 'word1    word2'
146  *
147  *   It is passed to the shell as:
148  *
149  *        sh -c echo 'word1    word2'
150  *
151  *   and is displayed by the shell as:
152  *
153  *        word1 word2
154  *
155  *   That's not a big deal, but consider this vile idiom:
156  *
157  *        ^X-!egrep -n 'word1 word2' *.c
158  *
159  *   Egrep receives the following command line from the shell:
160  *
161  *        egrep -n word1 word2 <glob'd file list>
162  *
163  *   Oops.  Word2 of the regular expression is now a filename.
164  *
165  * SOLUTIONS
166  *   1) If user's shell is a unix lookalike and the command contains no
167  *      single quote delimiters, enclose the entire command in single
168  *      quotes.  This forces the shell to treat the command string
169  *      as a single argument _before_ word splitting, expansions, and
170  *      quote removal.  Otherwise,
171  *
172  *   2) If user's shell is a unix lookalike, enclose the command string in
173  *      double quotes and escape every nonquoted double quote within the
174  *      original string.  This is the same principle as 1) above, but uses
175  *      a nestable delimiter.  This solution isn't foolproof.  Consider:
176  *
177  *          ^X-!echo '[#@$*]' \"special\" word
178  *
179  *      will be read into the error buffer as:
180  *
181  *          [#@$*] special word
182  *
183  *      This could be worked around by preceding a leading \" token with '
184  *      and appending ' to its closing delimiter.  But that creates its
185  *      own set of side effects.
186  *
187  * CAVEATS
188  *   The workarounds are inappropriate for csh (variants) which don't
189  *   support nested quotes.
190  *
191  * RETURNS
192  *   Pointer to possibly modified string.  If modified, the command string
193  *   was created on the heap and must be free'd by the client.  If
194  *   storage can't be allocated, NULL is returned.
195  */
196 
197 char *
mk_shell_cmd_str(const char * cmd,int * allocd_mem,int prepend_shc)198 mk_shell_cmd_str(const char *cmd, int *allocd_mem, int prepend_shc)
199 {
200     int alloc_len;
201     int bourne_shell;		/* Boolean, T if user's shell has appearances
202 				 * of a Unix lookalike bourne shell (e.g.,
203 				 * sh, ksh, bash).
204 				 */
205     size_t len;
206     char *out_str, *cp, *shell, *shell_c = "/c";
207 
208     shell = get_shell();
209     len = strlen(shell);
210     bourne_shell = (len >= 2 &&
211 		    toLower(shell[len - 2]) == 's' &&
212 		    toLower(shell[len - 1]) == 'h')
213 	||
214 	(len >= SHEXE_LEN &&
215 	 stricmp(shell + len - SHEXE_LEN, SHEXE) == 0);
216     if (bourne_shell) {
217 	shell_c = "-c";
218 
219 	/* Now check for csh lookalike. */
220 	bourne_shell = !(
221 			    (len >= 3 &&
222 			     toLower(shell[len - 3]) == 'c')
223 			    ||
224 			    (len >= CSHEXE_LEN &&
225 			     stricmp(shell + len - CSHEXE_LEN, CSHEXE) == 0)
226 	    );
227     }
228     if (!bourne_shell) {
229 	/*
230 	 * MS-DOS shell or csh.  Do not bother quoting user's command
231 	 * string, since the former is oblivious to the notion of a unix
232 	 * shell's argument quoting and the latter does not support nested
233 	 * double quotes.
234 	 */
235 
236 	if (prepend_shc) {
237 	    alloc_len = (int) (strlen(cmd) + strlen(shell) + SHELL_C_LEN + 1);
238 	    if ((out_str = typeallocn(char, alloc_len)) == NULL)
239 		  return (out_str);
240 	    *allocd_mem = TRUE;
241 	    sprintf(out_str, "%s %s %s", shell, shell_c, cmd);
242 	    return (out_str);
243 	} else {
244 	    *allocd_mem = FALSE;
245 	    return (char *) (cmd);
246 	}
247     }
248 
249     /* Else apply solutions 1-2 above. */
250     alloc_len = (int) strlen(cmd) * 2;	/* worst case -- every cmd byte quoted */
251     if (prepend_shc)
252 	alloc_len += (int) strlen(shell) + SHELL_C_LEN;
253     alloc_len += 3;		/* terminating nul + 2 quote chars     */
254     if ((out_str = typeallocn(char, alloc_len)) == NULL) {
255 	errno = ENOMEM;
256 	return (out_str);
257     }
258     *allocd_mem = TRUE;
259 
260     cp = out_str;
261     if (prepend_shc)
262 	cp += sprintf(cp, "%s %s ", shell, shell_c);
263     if (strchr(cmd, '\'') == NULL) {
264 	/* No single quotes in command string.  Solution #1. */
265 
266 	sprintf(cp, "'%s'", cmd);
267 	return (out_str);
268     }
269 
270     /* Solution #2. */
271     *cp++ = '"';
272     while (*cmd) {
273 	if (*cmd == '\\') {
274 	    *cp++ = *cmd++;
275 	    if (*cmd) {
276 		/* Any quoted char is immune to further quoting. */
277 
278 		*cp++ = *cmd++;
279 	    }
280 	} else if (*cmd != '"')
281 	    *cp++ = *cmd++;
282 	else {
283 	    /* bare '"' */
284 
285 	    *cp++ = '\\';
286 	    *cp++ = *cmd++;
287 	}
288     }
289     *cp++ = '"';
290     *cp = '\0';
291     return (out_str);
292 }
293 
294 /*
295  * Semi-generic CreateProcess(). Refer to the w32_system()
296  * DESCRIPTION below for the rationale behind "no_wait" argument.
297  */
298 int
w32_CreateProcess(char * cmd,int no_wait)299 w32_CreateProcess(char *cmd, int no_wait)
300 {
301     int rc = -1;
302     PROCESS_INFORMATION pi;
303     STARTUPINFO si;
304     W32_CHAR *actual;
305 
306     TRACE((T_CALLED "w32_CreateProcess(%s, %d)\n", cmd, no_wait));
307 
308     memset(&si, 0, sizeof(si));
309     si.cb = sizeof(si);
310     if (!no_wait) {
311 	/* command requires a shell, so hookup console I/O */
312 
313 	si.dwFlags = STARTF_USESTDHANDLES;
314 	si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
315 	si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
316 	si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
317     }
318 
319     if ((actual = w32_charstring(cmd)) != 0) {
320 	if (CreateProcess(NULL,
321 			  actual,
322 			  NULL,
323 			  NULL,
324 			  !no_wait,	/* Inherit handles */
325 			  0,
326 			  NULL,
327 			  NULL,
328 			  &si,
329 			  &pi)) {
330 	    /* Success */
331 	    if (!no_wait) {
332 		/* wait for shell process to exit */
333 
334 		(void) cwait(&rc, (CWAIT_PARAM_TYPE) pi.hProcess, 0);
335 	    }
336 	    (void) CloseHandle(pi.hProcess);
337 	    (void) CloseHandle(pi.hThread);
338 	} else {
339 	    /* Bummer */
340 
341 	    mlforce("[unable to create win32 process]");
342 	}
343 	free(actual);
344     } else {
345 	no_memory("w32_CreateProcess");
346     }
347     returnCode(rc);
348 }
349 
350 /*
351  * FUNCTION
352  *   w32_system(const char *cmd)
353  *
354  *   cmd - command string to be be passed to a Win32 command interpreter.
355  *
356  * DESCRIPTION
357  *   Executes our version of the system() call, taking care to ensure that
358  *   the user's command string is properly quoted if get_shell() points to a
359  *   bourne shell clone.  We use our version of system() rather than the
360  *   vendor's so that vile's users may redefine their shell via the editor's
361  *   $shell state variable.  If we didn't add this additional layer of
362  *   indirection, users would be at the mercy of whatever env var the C
363  *   compiler's runtime library chose to reference from within system()--in
364  *   the case of the MS CRT, that would be $COMSPEC.
365  *
366  *   As an additional feature, "cmd" may be executed without running
367  *   as a shell subprocess if the the first token of the command string
368  *   is "start " (useful for launching windows apps that don't need a shell).
369  *
370  * RETURNS
371  *   If memory allocation fails, -1.
372  *   Else, whatever system() returns.
373  */
374 
375 int
w32_system(const char * cmd)376 w32_system(const char *cmd)
377 {
378     char *cmdstr;
379     int no_shell, freestr, rc;
380 
381     TRACE(("w32_system(%s)\n", cmd));
382 
383     no_shell = W32_SKIP_SHELL(cmd);
384     if (no_shell) {
385 	/*
386 	 * Must strip off "start " prefix from command because this
387 	 * idiom is supported by Win95, but not by WinNT.
388 	 */
389 
390 	if ((cmdstr = typeallocn(char, strlen(cmd) + 1)) == NULL) {
391 	    (void) no_memory("w32_system");
392 	    return (-1);
393 	}
394 	strcpy(cmdstr, cmd + W32_START_STR_LEN);
395 	freestr = TRUE;
396     } else if ((cmdstr = mk_shell_cmd_str(cmd, &freestr, TRUE)) == NULL) {
397 	/* heap exhausted! */
398 
399 	(void) no_memory("w32_system");
400 	return (-1);
401     }
402     set_console_title(cmd);
403     rc = w32_CreateProcess(cmdstr, no_shell);
404     if (freestr)
405 	free(cmdstr);
406     restore_console_title();
407     return (rc);
408 }
409 
410 #if DISP_NTWIN
411 
412 static int
get_console_handles(STARTUPINFO * psi,SECURITY_ATTRIBUTES * psa)413 get_console_handles(STARTUPINFO * psi, SECURITY_ATTRIBUTES * psa)
414 {
415     CONSOLE_CURSOR_INFO cci;
416     char shell[NFILEN];
417 
418     psi->hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
419     psi->dwFlags = STARTF_USESTDHANDLES;
420 
421     /* If it's possible to fetch data via hStdOutput, assume all is well. */
422     if (GetConsoleCursorInfo(psi->hStdOutput, &cci)) {
423 	psi->hStdInput = GetStdHandle(STD_INPUT_HANDLE);
424 	psi->hStdError = GetStdHandle(STD_ERROR_HANDLE);
425 	return (TRUE);
426     }
427 
428     /*
429      * Else assume that winvile was started from a shell and that stdout
430      * and stdin (and maybe stderr) have been redirected to handles that
431      * can't be accessed by the parent process.  This is known to occur
432      * with the CYGWIN shell(s) on a WinNT host.  In this case, workaround
433      * these issues:
434      *
435      * Numero Uno:  the stdin and stdout handles returned by GetStdHandle()
436      *              are worthless.  Workaround:  get the real handles by
437      *              opening CONIN$ and CONOUT$.  We don't look for a stderr
438      *              handle because I couldn't find any mention of CONERR$
439      *              in MSDN.  Additionally, the CYGWIN shells will create
440      *              stderr if it doesn't exist.
441      *
442      * Numero Dos:  a dynamically created console in the context of a CYGWIN
443      *              shell sets its foreground and background colors to the
444      *              same value (usually black).  Not so easy to read the
445      *              text :-( . Workaround: force contrasting colors (white
446      *              text on black bgrnd).  Someday we ought to let the users
447      *              choose the colors via a win32-specific mode.
448      *
449      * And it should be noted that the above techniques do not work on
450      * a Win2K host using the latest version of bash.  Sigh.  So, if you
451      * use bash as your shell, invoke winvile from the command line
452      * and type:
453      *
454      *    :sh
455      *
456      * or
457      *
458      *    :!bash
459      *
460      * the spawned bash shell will hang.  No known workaround.  Does not
461      * occur if winvile is launched by windows explorer.
462      */
463     if ((psi->hStdInput = CreateFile(W32_STRING("CONIN$"),
464 				     GENERIC_READ,
465 				     FILE_SHARE_READ,
466 				     psa,
467 				     OPEN_EXISTING,
468 				     FILE_ATTRIBUTE_NORMAL,
469 				     NULL)) == INVALID_HANDLE_VALUE) {
470 	mlforce("[std input handle creation failed]");
471 	return (FALSE);
472     }
473     if ((psi->hStdOutput = CreateFile(W32_STRING("CONOUT$"),
474 				      GENERIC_WRITE,
475 				      FILE_SHARE_WRITE,
476 				      psa,
477 				      OPEN_EXISTING,
478 				      FILE_ATTRIBUTE_NORMAL,
479 				      NULL)) == INVALID_HANDLE_VALUE) {
480 	mlforce("[std output handle creation failed]");
481 	return (FALSE);
482     }
483 
484     /*
485      * set some value for stderr...this seems to be important for
486      * cygwin version circa 1.5.xx (so that error messages appear in the
487      * spawned console when winvile is invoked from bash command line).
488      *
489      * What to choose depends on the user's $shell .
490      */
491     strcpy(shell, get_shell());
492     (void) mklower(shell);
493     if (strstr(shell, "cmd") || strstr(shell, "command"))
494 	psi->hStdError = psi->hStdOutput;
495     else
496 	psi->hStdError = GetStdHandle(STD_ERROR_HANDLE);
497 
498     psi->dwFlags |= STARTF_USEFILLATTRIBUTE;
499     psi->dwFillAttribute = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
500     return (TRUE);
501 }
502 
503 /*
504  * FUNCTION
505  *   w32_system_winvile(const char *cmd, int *pressret)
506  *
507  *   cmd       - command string to be be passed to a Win32 command interpreter.
508  *
509  *   *pressret - Boolean, T -> display prompt and wait for response.  Value
510  *               usually read-only, but will be set if the user's command
511  *               is prefixed with W32_START_STR
512  *
513  * DESCRIPTION
514  *   Executes a system() call in the context of a Win32 GUI application,
515  *   taking care to ensure that the user's command string is properly
516  *   quoted if get_shell() points to a bourne shell clone.
517  *
518  *   "In the context of a Win32 GUI application" means that:
519  *
520  *   a) the GUI requires explicit console allocation prior to exec'ing
521  *      "cmd", and
522  *   b) said console stays "up" until explicitly dismissed by the user if
523  *      "*pressret" is TRUE.
524  *
525  * ACKNOWLEDGMENTS
526  *   I had no idea a Win32 GUI app could exec a console command until I
527  *   browsed the win32 gvim code.
528  *
529  * RETURNS
530  *   If memory/console allocation fails, -1.
531  *   Else whatever the executed command returns.
532  */
533 
534 int
w32_system_winvile(const char * cmd,int * pressret)535 w32_system_winvile(const char *cmd, int *pressret)
536 {
537 #define PRESS_ANY_KEY "\n[Press any key to continue]"
538 
539     char *cmdstr;
540     HWND hwnd;
541     int no_shell = W32_SKIP_SHELL(cmd);
542     int freestr;
543     int close_disabled = FALSE;
544     int rc = -1;
545     PROCESS_INFORMATION pi;
546     SECURITY_ATTRIBUTES sa;
547     STARTUPINFO si;
548 
549     W32_CHAR *w32_cmd = w32_charstring(cmd);
550     W32_CHAR *w32_cmdstr = 0;
551 
552     TRACE((T_CALLED "w32_system_winvile(%s)\n", cmd));
553 
554     memset(&si, 0, sizeof(si));
555     si.cb = sizeof(si);
556 
557     memset(&sa, 0, sizeof(sa));
558     sa.nLength = sizeof(sa);
559 
560     if (no_shell) {
561 	/*
562 	 * Must strip off "start " prefix from command because this
563 	 * idiom is supported by Win95, but not by WinNT.
564 	 */
565 
566 	if ((cmdstr = typeallocn(char, strlen(cmd) + 1)) == NULL) {
567 	    (void) no_memory("w32_system_winvile");
568 	    returnCode(rc);
569 	}
570 	strcpy(cmdstr, cmd + W32_START_STR_LEN);
571 	freestr = TRUE;
572 	*pressret = FALSE;	/* Not waiting for the launched cmd to exit. */
573     } else {
574 	sa.bInheritHandle = TRUE;
575 	if ((cmdstr = mk_shell_cmd_str(cmd, &freestr, TRUE)) == NULL) {
576 	    /* heap exhausted! */
577 
578 	    (void) no_memory("w32_system_winvile");
579 	    returnCode(rc);
580 	}
581 	if (!AllocConsole()) {
582 	    if (freestr)
583 		free(cmdstr);
584 	    mlforce("[console creation failed]");
585 	    returnCode(rc);
586 	}
587 	if (!get_console_handles(&si, &sa)) {
588 	    (void) FreeConsole();
589 	    if (freestr)
590 		free(cmdstr);
591 	    returnCode(rc);
592 	}
593 	SetConsoleTitle(w32_cmd);
594 
595 	/* don't let signal in dynamic console kill winvile */
596 	ignore_signals();
597 
598 	/*
599 	 * If the spawned console's close button is pressed, both the
600 	 * console _and_ winvile are killed.  Not good.  Try to disable
601 	 * console close button before shell process is started, while the
602 	 * console title is still intact.  Once the shell starts running,
603 	 * it is free to change the console's title, and many will do so
604 	 * (e.g., bash).
605 	 */
606 	Sleep(0);		/* yield processor so GDI can create console frame */
607 	if ((hwnd = FindWindow(NULL, w32_cmd)) != NULL) {
608 	    /*
609 	     * Disable console close button using code borrowed from
610 	     * Charles Petzold.
611 	     */
612 
613 	    (void) EnableMenuItem(GetSystemMenu(hwnd, FALSE),
614 				  SC_CLOSE,
615 				  MF_GRAYED);
616 	    close_disabled = TRUE;
617 
618 	    /*
619 	     * On a Win2K host, the console is often created as a
620 	     * background window, which means that the user must press
621 	     * ALT+TAB to bring it to the foreground to interact with the
622 	     * shell.  Ugh.  This call to SetForegroundWindow() should
623 	     * work, even on 2K / 98.
624 	     */
625 	    (void) SetForegroundWindow(hwnd);
626 	}
627     }
628     if ((w32_cmdstr = w32_charstring(cmdstr)) == 0) {
629 	(void) no_memory("w32_system_winvile");
630 	returnCode(rc);
631     }
632     if (CreateProcess(NULL,
633 		      w32_cmdstr,
634 		      &sa,
635 		      &sa,
636 		      !no_shell,	/* Inherit handles */
637 		      0,
638 		      NULL,
639 		      NULL,
640 		      &si,
641 		      &pi)) {
642 	/* Success */
643 
644 	DWORD dummy;
645 	INPUT_RECORD ir;
646 
647 	if (!close_disabled) {
648 	    int i;
649 
650 	    /*
651 	     * If the spawned console's close button is pressed, both the
652 	     * console _and_ winvile are killed.  Not good.
653 	     */
654 	    for (i = 0; i < 5; i++) {
655 		if ((hwnd = FindWindow(NULL, w32_cmd)) != NULL) {
656 		    (void) EnableMenuItem(GetSystemMenu(hwnd, FALSE),
657 					  SC_CLOSE,
658 					  MF_GRAYED);
659 		    (void) SetForegroundWindow(hwnd);
660 		    break;
661 		}
662 		Sleep(200);
663 	    }
664 	}
665 	if (!no_shell) {
666 	    (void) cwait(&rc, (CWAIT_PARAM_TYPE) pi.hProcess, 0);
667 	    restore_signals();
668 	    if (*pressret) {
669 		if (!WriteFile(si.hStdOutput,
670 			       PRESS_ANY_KEY,
671 			       sizeof(PRESS_ANY_KEY) - 1,
672 			       &dummy,
673 			       NULL)) {
674 		    mlforce("[dynamic console write failed]");
675 		    rc = -1;
676 		} else {
677 		    for_ever
678 		    {
679 			/* Wait for a single key of input from user. */
680 
681 			if (!ReadConsoleInput(si.hStdInput, &ir, 1, &dummy)) {
682 			    mlforce("[dynamic console read failed]");
683 			    rc = -1;
684 			    break;
685 			}
686 			if (ir.EventType == KEY_EVENT &&
687 			    ir.Event.KeyEvent.bKeyDown) {
688 			    break;
689 			}
690 		    }
691 		}
692 	    }
693 	} else {
694 	    rc = 0;
695 	}
696 	(void) CloseHandle(pi.hProcess);
697 	(void) CloseHandle(pi.hThread);
698     } else {
699 	/* Bummer */
700 
701 	mlforce("[unable to create Win32 process]");
702     }
703     free(w32_cmd);
704     free(w32_cmdstr);
705     if (freestr)
706 	free(cmdstr);
707     if (!no_shell)
708 	FreeConsole();
709     returnCode(rc);
710 }
711 #endif /* DISP_NTWIN */
712 
713 /*
714  * FUNCTION
715  *   w32_keybrd_reopen(int pressret)
716  *
717  *   pressret - Boolean, T -> display prompt and wait for response
718  *
719  * DESCRIPTION
720  *   This is essentially the Win32 equivalent of the pressreturn() function
721  *   in spawn.c, but differs in that it reopens the console keyboard _after_
722  *   prompting the user to press return.  Order is important IF the user has
723  *   configured his/her dos box such that the buffer size exceeds the
724  *   window size.  In that scenario, if the ntconio.c routines gained
725  *   control (via term.kopen) before the prompt, then the output of the
726  *   previous shell command (e.g., :!dir) is immediately thrown away
727  *   due to a screen context switch and the user has no chance to read the
728  *   shell output.
729  *
730  *   This function prevents that scenario from occurring.
731  *
732  * APPLIES TO
733  *   W32 console vile only.
734  *
735  * RETURNS
736  *   None
737  */
738 
739 void
w32_keybrd_reopen(int pressret)740 w32_keybrd_reopen(int pressret)
741 {
742 #if DISP_NTCONS
743     int c;
744 
745     if (pressret) {
746 	vl_fputs("[Press return to continue]", stdout);
747 	fflush(stdout);
748 
749 	/* loop for a CR, a space, or a : to do another named command */
750 	while ((c = _getch()) != '\r' &&
751 	       c != '\n' &&
752 	       c != ' ' &&
753 	       !ABORTED(c)) {
754 	    if (DefaultKeyBinding(c) == &f_namedcmd) {
755 		unkeystroke(c);
756 		break;
757 	    }
758 	}
759 	vl_putc('\n', stdout);
760     }
761     term.kopen();
762     kbd_erase_to_end(0);
763 #else
764     (void) pressret;
765 #endif
766 }
767 
768 #if DISP_NTCONS
769 void
w32_set_console_title(const char * title)770 w32_set_console_title(const char *title)
771 {
772     W32_CHAR *actual = w32_charstring(title);
773     if (actual != 0) {
774 	SetConsoleTitle(actual);
775 	free(actual);
776     }
777 }
778 #endif
779 
780 /*
781  * The code in ntconio.c that saves and restores console titles
782  * didn't work reliably for pipe or shell operations.  It's moved here
783  * now and called via the w32_system() wrapper or the w32pipe module.
784  */
785 void
set_console_title(const char * title)786 set_console_title(const char *title)
787 {
788     (void) title;
789 #if !DISP_NTWIN
790     GetConsoleTitle(saved_title, sizeof(saved_title));
791     w32_set_console_title(title);
792 #endif
793 }
794 
795 void
restore_console_title(void)796 restore_console_title(void)
797 {
798 #if !DISP_NTWIN
799     SetConsoleTitle(saved_title);
800 #endif
801 }
802 
803 /*
804  * FUNCTION
805  *   fmt_win32_error(ULONG errcode, char **buf, ULONG buflen)
806  *
807  *   errcode - win32 error code for which message applies.  If errcode is
808  *             W32_SYS_ERROR, GetLastError() will be invoked to obtain the
809  *             error code.
810  *
811  *   buf     - indirect pointer to buf to receive formatted message.  If *buf
812  *             is NULL, the buffer is allocated on behalf of the client and
813  *             must be free'd using LocalFree().
814  *
815  *   buflen  - length of buffer (specify 0 if *buf is NULL).
816  *
817  * DESCRIPTION
818  *   Format system error reported by Win32 API.
819  *
820  * RETURNS
821  *   *buf
822  */
823 
824 char *
fmt_win32_error(ULONG errcode,char ** buf,ULONG buflen)825 fmt_win32_error(ULONG errcode, char **buf, ULONG buflen)
826 {
827     W32_CHAR *buffer = 0;
828     int flags = FORMAT_MESSAGE_FROM_SYSTEM;
829 
830     if (*buf) {
831 	buffer = typeallocn(W32_CHAR, buflen);
832     } else {
833 	flags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;
834     }
835     if (buffer != NULL) {
836 	FormatMessage(flags,
837 		      NULL,
838 		      errcode == W32_SYS_ERROR ? GetLastError() : errcode,
839 		      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),	/* dflt language */
840 		      buffer,
841 		      buflen,
842 		      NULL);
843 	if (*buf) {
844 	    char *formatted = asc_charstring(buffer);
845 	    vl_strncpy(*buf, formatted, buflen);
846 	    free(formatted);
847 	    free(buffer);
848 	}
849     }
850     return (*buf);
851 }
852 
853 W32_CHAR *
w32_prognam(void)854 w32_prognam(void)
855 {
856     static W32_CHAR *result;
857 
858     if (result == 0)
859 	result = w32_charstring(prognam);
860 
861     return result;
862 }
863 
864 int
w32_message_box(HWND hwnd,const char * message,int code)865 w32_message_box(HWND hwnd, const char *message, int code)
866 {
867     int rc;
868     W32_CHAR *buf = w32_charstring(message);
869 
870     rc = MessageBox(hwnd, buf, w32_prognam(), code);
871     free(buf);
872     return (rc);
873 }
874 
875 /*
876  * FUNCTION
877  *   disp_win32_error(ULONG errcode, void *hwnd)
878  *
879  *   errcode - win32 error code for which message applies.  If errcode is
880  *             W32_SYS_ERROR, GetLastError() will be invoked to obtain the
881  *             error code.
882  *
883  *   hwnd    - specifies the window handle argument to MessageBox (can be NULL).
884  *
885  * DESCRIPTION
886  *   Format system error reported by Win32 API and display in message box.
887  *
888  * RETURNS
889  *   None
890  */
891 
892 void
disp_win32_error(ULONG errcode,void * hwnd)893 disp_win32_error(ULONG errcode, void *hwnd)
894 {
895     char *buf = NULL;
896 
897     fmt_win32_error(errcode, &buf, 0);
898     w32_message_box(hwnd, buf, MB_OK | MB_ICONSTOP);
899     LocalFree(buf);
900 }
901 
902 #if DISP_NTWIN
903 /*
904  * FUNCTION
905  *   parse_font_str(const char *fontstr, FONTSTR_OPTIONS *results)
906  *
907  *   fontstr - a font specification string, see DESCRIPTION below.
908  *
909  *   results - Pointer to structure that returns data from a successfully
910  *             parsed font string.
911  *
912  * DESCRIPTION
913  *   Turns a font specification string into a LOGFONT data structure.
914  *   Specification syntax is as follows:
915  *
916  *     <font>  :== [<face>,]<size>[,<style>]
917  *
918  *     <face>  :== font-name
919  *     <size>  :== point size (integer)
920  *     <style> :== { bold | italic | bold-italic }
921  *
922  *     ex:    Letter Gothic,8
923  *     ex:    r_ansi,8,bold
924  *
925  *     Note 1:  If <style> unspecified, "normal" is assumed.
926  *     Note 2:  if <face> contains a comma it should be escaped with '\'.
927  *     Note 3:  if <face> is omitted, the current font is modified.
928  *
929  * RETURNS
930  *   Boolean, T -> font syntax ok, else bogus syntax
931  */
932 
933 int
parse_font_str(const char * fontstr,FONTSTR_OPTIONS * results)934 parse_font_str(const char *fontstr, FONTSTR_OPTIONS * results)
935 {
936     const char *cp, *tmp;
937     char *endnum, *facep;
938     ULONG size;
939 
940     memset(results, 0, sizeof(*results));
941     size = 0;
942     cp = skip_cblanks(fontstr);
943 
944     /* Up first is either a font face or font size. */
945     if (isDigit(*cp)) {
946 	errno = 0;
947 	size = strtoul(cp, &endnum, 10);
948 	if (errno != 0)
949 	    return (FALSE);
950 	tmp = skip_cblanks(endnum);
951 	if (*tmp != '\0') {
952 	    if (*tmp != ',') {
953 		/* Not a 100% integer value, assume this is a font face. */
954 		size = 0;
955 	    } else
956 		cp = tmp;	/* Valid point size. */
957 	} else
958 	    cp = tmp;		/* Only a point size specified, nothing left. */
959     }
960     if (size == 0) {
961 	/* this must be a font face */
962 
963 	facep = results->face;
964 	while (*cp) {
965 	    if (*cp == ',') {
966 		cp++;
967 		break;
968 	    } else if (*cp == '\\' && cp[1] == ',') {
969 		*facep++ = ',';
970 		cp += 2;
971 	    } else
972 		*facep++ = *cp++;
973 	}
974 	*facep = '\0';
975 	if (results->face[0] == '\0' || *cp == '\0') {
976 	    return (FALSE);
977 	} else {
978 	    /* Now pick up non-optional font size (that follows face). */
979 
980 	    errno = 0;
981 	    size = strtoul(cp, &endnum, 10);
982 	    if (errno != 0 || size == 0)
983 		return (FALSE);
984 	    cp = endnum;
985 	}
986     }
987 
988     /* Now look for optional font style. */
989     cp = skip_cblanks(cp);
990 
991     /* At this point, there are two allowable states:  delimiter or EOS. */
992     if (*cp) {
993 	if (*cp++ == ',') {
994 	    cp = skip_cblanks(cp);
995 	    if (strncmp(cp, "bold-italic", sizeof("bold-italic") - 1) == 0)
996 		results->bold = results->italic = TRUE;
997 	    else if (strncmp(cp, "italic", sizeof("italic") - 1) == 0)
998 		results->italic = TRUE;
999 	    else if (strncmp(cp, "bold", sizeof("bold") - 1) == 0)
1000 		results->bold = TRUE;
1001 	    else
1002 		return (FALSE);
1003 	} else
1004 	    return (FALSE);
1005     }
1006     results->size = size;
1007     TRACE(("parse_font_str(face=\"%s\", size=%ld, style=\"%s%s\")\n",
1008 	   results->face,
1009 	   results->size,
1010 	   results->bold ? "Bold" : "",
1011 	   results->italic ? "Italic" : ""));
1012     return (TRUE);
1013 }
1014 #endif /* DISP_NTWIN */
1015 
1016 /* return current window title in dynamic buffer */
1017 char *
w32_wdw_title(void)1018 w32_wdw_title(void)
1019 {
1020     static char *buffer;
1021     static W32_CHAR *buf;
1022     static DWORD bufsize;
1023     int nchars = 0;
1024     char *result = 0;
1025 
1026     if (!buf) {
1027 	bufsize = 128;
1028 	buf = typeallocn(W32_CHAR, bufsize);
1029     }
1030     while (buf != 0) {
1031 #if DISP_NTWIN
1032 	nchars = GetWindowText(winvile_hwnd(), buf, bufsize);
1033 #else
1034 	nchars = GetConsoleTitle(buf, bufsize);
1035 #endif
1036 	if (nchars >= ((int) bufsize - 1)) {
1037 	    /* Enlarge buffer and try again. */
1038 
1039 	    bufsize *= 2;
1040 	    safe_typereallocn(W32_CHAR, buf, bufsize);
1041 	} else {
1042 	    break;
1043 	}
1044     }
1045     if (nchars && buf) {
1046 	FreeIfNeeded(result);
1047 	buffer = asc_charstring(buf);
1048 	result = buffer;
1049     } else {
1050 	result = error_val;
1051     }
1052     return (result);
1053 }
1054 
1055 /*
1056  * Delete current text selection and optionally copy same to Windows
1057  * clipboard.
1058  */
1059 int
w32_del_selection(int copy_to_cbrd)1060 w32_del_selection(int copy_to_cbrd)
1061 {
1062     AREGION delrp;
1063     MARK savedot;
1064     REGIONSHAPE shape;
1065     int status;
1066 
1067     /* first, go to beginning of selection, if it exists. */
1068     if (!sel_motion(FALSE, FALSE))
1069 	return (FALSE);
1070 
1071     if (!sel_yank(0))		/* copy selection to unnamed register */
1072 	return (FALSE);
1073 
1074     if (!get_selection_buffer_and_region(&delrp)) {
1075 	/* no selected region.  hmmm -- should not happen */
1076 
1077 	return (FALSE);
1078     }
1079     shape = delrp.ar_shape;
1080     savedot = DOT;
1081 
1082     /*
1083      * Region to delete is now accessible via delrp.  Mimic the code
1084      * executed by operdel().  The actual code executed depends upon
1085      * whether or not the region is rectangular.
1086      */
1087     if (shape == rgn_RECTANGLE) {
1088 	DORGNLINES dorgn;
1089 	int save;
1090 
1091 	save = FALSE;
1092 	dorgn = get_do_lines_rgn();
1093 	DOT = delrp.ar_region.r_orig;
1094 
1095 	/* setup dorgn() aka do_lines_in_region() */
1096 	haveregion = &delrp.ar_region;
1097 	regionshape = shape;
1098 	status = dorgn(kill_line, &save, FALSE);
1099 	haveregion = NULL;
1100     } else {
1101 	lines_deleted = 0;
1102 	DOT = delrp.ar_region.r_orig;
1103 	status = ldel_bytes(delrp.ar_region.r_size, FALSE);
1104 #if OPT_SELECTIONS
1105 	find_release_attr(curbp, &delrp.ar_region);
1106 #endif
1107     }
1108     restore_dot(savedot);
1109     if (status) {
1110 	if (copy_to_cbrd) {
1111 	    /*
1112 	     * cbrdcpy_unnamed() reports number of lines copied, which is
1113 	     * same as number of lines deleted.
1114 	     */
1115 	    status = cbrdcpy_unnamed(FALSE, FALSE);
1116 	} else {
1117 	    /* No data copied to clipboard, report number of lines deleted. */
1118 
1119 	    status = TRUE;
1120 	    if (shape == rgn_RECTANGLE) {
1121 		if (do_report(klines + (kchars != 0))) {
1122 		    mlwrite("[%d line%s, %d character%s killed]",
1123 			    klines,
1124 			    PLURAL(klines),
1125 			    kchars,
1126 			    PLURAL(kchars));
1127 		}
1128 	    } else {
1129 		if (do_report(lines_deleted))
1130 		    mlwrite("[%d lines deleted]", lines_deleted);
1131 	    }
1132 	}
1133     }
1134     return (status);
1135 }
1136 
1137 #ifdef UNICODE
1138 #define PASS_WCHAR 1
1139 #endif
1140 
1141 /* slam a string into the editor's input buffer */
1142 int
w32_keybrd_write(W32_CHAR * data)1143 w32_keybrd_write(W32_CHAR * data)
1144 {
1145 #if DISP_NTCONS
1146     HANDLE hstdin;
1147     INPUT_RECORD ir;
1148     DWORD unused;
1149 #else
1150     HANDLE hwvile;
1151 #endif
1152     int rc;
1153 #ifndef PASS_WCHAR
1154     int n, nbytes;
1155     UCHAR buffer[8];
1156 #endif
1157 
1158     rc = TRUE;
1159 #if DISP_NTCONS
1160     hstdin = GetStdHandle(STD_INPUT_HANDLE);
1161     memset(&ir, 0, sizeof(ir));
1162     ir.EventType = KEY_EVENT;
1163     ir.Event.KeyEvent.bKeyDown = TRUE;
1164 #else
1165     hwvile = winvile_hwnd();
1166 #endif
1167 
1168     while (*data && rc) {
1169 #ifdef PASS_WCHAR
1170 #if DISP_NTCONS
1171 	ir.Event.KeyEvent.uChar.UnicodeChar = *data;
1172 	rc = WriteConsoleInput(hstdin, &ir, 1, &unused);
1173 #else
1174 	rc = PostMessage(hwvile, WM_CHAR, *data, 0);
1175 #endif
1176 #else /* !PASS_WCHAR */
1177 #ifdef UNICODE
1178 	nbytes = vl_conv_to_utf8(buffer, *data, 8);
1179 #else
1180 	buffer[0] = *data;
1181 	nbytes = 1;
1182 #endif
1183 	for (n = 0; n < nbytes; ++n) {
1184 #if DISP_NTCONS
1185 	    ir.Event.KeyEvent.uChar.AsciiChar = buffer[n];
1186 	    rc = WriteConsoleInput(hstdin, &ir, 1, &unused);
1187 #else
1188 	    rc = PostMessage(hwvile, WM_CHAR, buffer[n], 0);
1189 #endif
1190 	}
1191 #endif /* PASS_WCHAR */
1192 	data++;
1193     }
1194     return (rc);
1195 }
1196 
1197 #if DISP_NTWIN
1198 
1199 /* Center a child window (usually a dialog box) over a parent. */
1200 void
w32_center_window(HWND child_hwnd,HWND parent_hwnd)1201 w32_center_window(HWND child_hwnd, HWND parent_hwnd)
1202 {
1203     int w, h;
1204     RECT crect;			/* child rect */
1205     RECT prect;			/* parent rect */
1206 
1207     GetWindowRect(parent_hwnd, &prect);
1208     GetWindowRect(child_hwnd, &crect);
1209     w = crect.right - crect.left;
1210     h = crect.bottom - crect.top;
1211     MoveWindow(child_hwnd,
1212 	       prect.left + ((prect.right - prect.left) / 2 - w / 2),
1213 	       prect.top + ((prect.bottom - prect.top) / 2 - h / 2),
1214 	       w,
1215 	       h,
1216 	       TRUE);
1217 }
1218 #endif
1219 
1220 /*
1221  * If necessary, add/remove World/Everyone write permission to/from a file acl.
1222  *
1223  * Much of this function's logic is cribbed from here:
1224  *
1225  *     http://www.devx.com/cplus/Article/16711/1954?pf=true
1226  *
1227  * This function is not 100% robust.  It will not handle the case of:
1228  *
1229  *     + a file that does not include World/Everyone ACE
1230  *     + a file that includes one or more DENIED World/Everyone ACEs
1231  *
1232  * However, this function provides enough logic to make an NTFS-based,
1233  * cygwin-generated, readonly file, writable.  Which is good enough for
1234  * the purpose at hand.
1235  */
1236 static int
add_remove_write_acl(const char * filename,int add_acl,DWORD * prev_access_mask)1237 add_remove_write_acl(const char *filename, int add_acl, DWORD * prev_access_mask)
1238 {
1239 #define WRITABLE_MASK (FILE_WRITE_DATA | FILE_APPEND_DATA)
1240 
1241     BOOL bDaclPresent, bDaclDefaulted;
1242     W32_CHAR *w32_bslfn = 0;
1243     char *bslfn, *msg = NULL;
1244     DWORD dwSizeNeeded;
1245     int i, rc = FALSE;
1246     PSID pAceSID, pWorldSID;
1247     PACL pacl = NULL;
1248     ACCESS_ALLOWED_ACE *pAllowed;
1249     BYTE *pSecDescriptorBuf = 0;
1250 
1251     SID_IDENTIFIER_AUTHORITY SIDAuthWorld =
1252     {
1253 	SECURITY_WORLD_SID_AUTHORITY
1254     };
1255 
1256     /* does the file exist? */
1257     bslfn = sl_to_bsl(filename);
1258     if (access(bslfn, 0) != 0)
1259 	return (rc);
1260     if ((w32_bslfn = w32_charstring(bslfn)) == 0)
1261 	return (rc);
1262 
1263     dwSizeNeeded = 0;
1264     (void) GetFileSecurity(w32_bslfn,
1265 			   DACL_SECURITY_INFORMATION,
1266 			   NULL,
1267 			   0,
1268 			   &dwSizeNeeded);
1269 
1270     if (dwSizeNeeded == 0) {
1271 	fmt_win32_error(W32_SYS_ERROR, &msg, 0);
1272 	mlforce("[GetFileSecurity: %s]", mktrimmed(msg));
1273 	LocalFree(msg);
1274     } else if ((pSecDescriptorBuf = malloc(sizeof(BYTE) * dwSizeNeeded)) == NULL) {
1275 	rc = no_memory("add_remove_write_acl");
1276     } else if (!GetFileSecurity(w32_bslfn,
1277 				DACL_SECURITY_INFORMATION,
1278 				pSecDescriptorBuf,
1279 				dwSizeNeeded,
1280 				&dwSizeNeeded)) {
1281 	fmt_win32_error(W32_SYS_ERROR, &msg, 0);
1282 	mlforce("[GetFileSecurity: %s]", mktrimmed(msg));
1283 	LocalFree(msg);
1284     }
1285 
1286     /* Get DACL from Security Descriptor */
1287     else if (!GetSecurityDescriptorDacl((SECURITY_DESCRIPTOR *) pSecDescriptorBuf,
1288 					&bDaclPresent,
1289 					&pacl,
1290 					&bDaclDefaulted)) {
1291 	fmt_win32_error(W32_SYS_ERROR, &msg, 0);
1292 	mlforce("[GetSecurityDescriptorDacl: %s]", mktrimmed(msg));
1293 	LocalFree(msg);
1294     }
1295 
1296     /* Check if DACL present in security descriptor */
1297     else if (!bDaclPresent || pacl == NULL) {
1298 	/*
1299 	 * Nothing to manipulate, perhaps a non-NTFS file.  Regardless, a
1300 	 * NULL discretionary ACL implicitly allows all access to an object
1301 	 * (sez docu for GetSecurityDescriptorDacl).
1302 	 */
1303 	;
1304     }
1305 
1306     /* Create a well-known SID for "Everyone/World" (code courtesy of MSDN). */
1307     else if (!AllocateAndInitializeSid(&SIDAuthWorld,
1308 				       1,
1309 				       SECURITY_WORLD_RID,
1310 				       0, 0, 0, 0, 0, 0, 0,
1311 				       &pWorldSID)) {
1312 	fmt_win32_error(W32_SYS_ERROR, &msg, 0);
1313 	mlforce("[AllocateAndInitializeSid: %s]", mktrimmed(msg));
1314 	LocalFree(msg);
1315     } else {
1316 	for (i = 0, pAllowed = NULL; i < pacl->AceCount; i++) {
1317 	    ACE_HEADER *phdr;
1318 
1319 	    if (GetAce(pacl, i, (LPVOID *) & phdr)) {
1320 		if (phdr->AceType == ACCESS_ALLOWED_ACE_TYPE) {
1321 		    pAllowed = (ACCESS_ALLOWED_ACE *) phdr;
1322 		    pAceSID = (SID *) & (pAllowed->SidStart);
1323 		    if (EqualSid(pWorldSID, pAceSID))
1324 			break;
1325 		}
1326 	    }
1327 	}
1328 	if (i < pacl->AceCount) {
1329 	    /* success */
1330 
1331 	    int mkchange = FALSE;
1332 
1333 	    if (add_acl) {
1334 		if ((pAllowed->Mask & WRITABLE_MASK) != WRITABLE_MASK) {
1335 		    /* world ACE does not have "write" permissions...add them */
1336 
1337 		    *prev_access_mask = pAllowed->Mask;
1338 		    mkchange = TRUE;
1339 		    pAllowed->Mask |= FILE_GENERIC_WRITE;
1340 		}
1341 	    } else {
1342 		/* restore previous world ACE mask for this file */
1343 
1344 		pAllowed->Mask = *prev_access_mask;
1345 		mkchange = TRUE;
1346 	    }
1347 	    if (mkchange) {
1348 		rc = SetFileSecurity(w32_bslfn,
1349 				     DACL_SECURITY_INFORMATION,
1350 				     pSecDescriptorBuf);
1351 
1352 		if (!rc) {
1353 		    DWORD err = GetLastError();
1354 		    if (!(add_acl && err == ERROR_ACCESS_DENIED)) {
1355 			fmt_win32_error(err, &msg, 0);
1356 			mlforce("[SetFileSecurity: %s]", mktrimmed(msg));
1357 			LocalFree(msg);
1358 		    }
1359 		    /*
1360 		     * Else tried adding write permissions and privs are
1361 		     * insufficient.  Report no error...whatever action the
1362 		     * client is attempting will soon fail and an error
1363 		     * will be reported at that time.
1364 		     */
1365 		}
1366 	    }
1367 	}
1368 	/* Else no World ACE, so add it...someday...maybe...when really bored? */
1369 
1370 	FreeSid(pWorldSID);
1371     }
1372     FreeIfNeeded(w32_bslfn);
1373     FreeIfNeeded(pSecDescriptorBuf);
1374     return (rc);
1375 
1376 #undef WRITABLE_MASK
1377 }
1378 
1379 /*
1380  * If necessary, modify a file's World/Everyone ACE to include file "write"
1381  * permissions.  This function returns TRUE if an ACE change was made.  In
1382  * addition, if the ACE is changed, "old_mask_access" is initialized with the
1383  * prior value of the file's World/Everyone ACCESS_MASK.  This value is used
1384  * by w32_remove_write_acl() to restore the file's original ACCESS_MASK.
1385  */
1386 int
w32_add_write_acl(const char * filename,ULONG * old_access_mask)1387 w32_add_write_acl(const char *filename, ULONG * old_access_mask)
1388 {
1389     if (is_win95())
1390 	return (FALSE);		/* no such win9x feature */
1391     else
1392 	return (add_remove_write_acl(filename, TRUE, old_access_mask));
1393 }
1394 
1395 /*
1396  * Bookend of w32_add_write_acl().  "orig_access_mask" is the value used to
1397  * restore the file's World/Everyone ACE ACCESS_MASK to its original value.
1398  */
1399 int
w32_remove_write_acl(const char * filename,ULONG orig_access_mask)1400 w32_remove_write_acl(const char *filename, ULONG orig_access_mask)
1401 {
1402     if (is_win95())
1403 	return (FALSE);		/* no such win9x feature */
1404     else
1405 	return (add_remove_write_acl(filename, FALSE, &orig_access_mask));
1406 }
1407 
1408 #ifdef UNICODE
1409 /*
1410  * Use this via "w32_charstring()" to convert literal ASCII strings to Unicode.
1411  */
1412 W32_CHAR *
w32_ansi_to_ucs2(const char * source,int sourcelen)1413 w32_ansi_to_ucs2(const char *source, int sourcelen)
1414 {
1415     W32_CHAR *target = 0;
1416 
1417     if (source != 0) {
1418 	ULONG len = MultiByteToWideChar(CP_ACP,
1419 					MB_USEGLYPHCHARS | MB_PRECOMPOSED,
1420 					source,
1421 					sourcelen,
1422 					0,
1423 					0);
1424 	if (len != 0) {
1425 	    target = typecallocn(W32_CHAR, len + 1);
1426 
1427 	    (void) MultiByteToWideChar(CP_ACP,
1428 				       MB_USEGLYPHCHARS | MB_PRECOMPOSED,
1429 				       source,
1430 				       sourcelen,
1431 				       target,
1432 				       len);
1433 	}
1434     }
1435 
1436     return target;
1437 }
1438 
1439 /* WINVER >= _0x0500 */
1440 #ifndef WC_NO_BEST_FIT_CHARS
1441 #define WC_NO_BEST_FIT_CHARS 0
1442 #endif
1443 
1444 /*
1445  * Use this via "asc_charstring()" to convert Unicode to ASCII strings.
1446  */
1447 char *
w32_ucs2_to_ansi(const W32_CHAR * source,int sourcelen)1448 w32_ucs2_to_ansi(const W32_CHAR * source, int sourcelen)
1449 {
1450     char *target = 0;
1451 
1452     if (source != 0) {
1453 	ULONG len = WideCharToMultiByte(CP_ACP,
1454 					WC_NO_BEST_FIT_CHARS,
1455 					source,
1456 					sourcelen,
1457 					0,
1458 					0,
1459 					NULL,
1460 					NULL);
1461 	if (len) {
1462 	    target = typecallocn(char, len + 1);
1463 
1464 	    (void) WideCharToMultiByte(CP_ACP,
1465 				       WC_NO_BEST_FIT_CHARS,
1466 				       source,
1467 				       sourcelen,
1468 				       target,
1469 				       len,
1470 				       NULL,
1471 				       NULL);
1472 	}
1473     }
1474 
1475     return target;
1476 }
1477 #endif
1478 
1479 void *
binmalloc(void * source,int length)1480 binmalloc(void *source, int length)
1481 {
1482     void *target = malloc(length);
1483     if (target != 0)
1484 	memcpy(target, source, length);
1485     return target;
1486 }
1487