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