1 /*
2  *  gretl -- Gnu Regression, Econometrics and Time-series Library
3  *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 /* gretl_win32.c for gretl */
21 
22 #include "libgretl.h"
23 #include "libset.h"
24 #include "gretl_www.h"
25 #include "addons_utils.h"
26 
27 #include "gretl_win32.h"
28 #include <shlobj.h>
29 #include <aclapi.h>
30 
31 static int windebug;
32 static FILE *fdb;
33 
set_windebug(int s)34 void set_windebug (int s)
35 {
36     if (s == 2) {
37 	/* we're getting this from gretlcli.exe */
38 	fdb = stdout;
39     }
40     windebug = s;
41 }
42 
win_print_last_error(void)43 void win_print_last_error (void)
44 {
45     DWORD dw = GetLastError();
46     LPVOID buf = NULL;
47 
48     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
49 		  FORMAT_MESSAGE_FROM_SYSTEM |
50 		  FORMAT_MESSAGE_IGNORE_INSERTS,
51 		  NULL,
52 		  dw,
53 		  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
54 		  (LPTSTR) &buf,
55 		  0,
56 		  NULL);
57 
58     if (buf != NULL) {
59 	if (fdb != NULL) {
60 	    fprintf(fdb, "Windows says: %s\n", (char *) buf);
61 	} else {
62 	    fprintf(stderr, "Windows says: %s\n", (char *) buf);
63 	}
64 	LocalFree(buf);
65     }
66 }
67 
68 /* returns 0 on success */
69 
read_reg_val(HKEY tree,const char * base,char * keyname,char * keyval)70 int read_reg_val (HKEY tree, const char *base,
71 		  char *keyname, char *keyval)
72 {
73     unsigned long datalen = MAXLEN;
74     gchar *regpath;
75     LSTATUS ret;
76     HKEY regkey;
77     int enc_err = 0;
78 
79     regpath = g_strdup_printf("Software\\%s", base);
80 
81     ret = RegOpenKeyEx(tree,      /* handle to open key */
82 		       regpath,   /* subkey name */
83 		       0,         /* reserved */
84 		       KEY_READ,  /* access mask */
85 		       &regkey    /* key handle */
86 		       );
87 
88     if (ret == ERROR_SUCCESS) {
89 	gunichar2 *wkeyname;
90 
91 	wkeyname = g_utf8_to_utf16(keyname, -1, NULL, NULL, NULL);
92 
93 	if (wkeyname == NULL) {
94 	    enc_err = 1;
95 	} else {
96 	    gunichar2 wval[MAXLEN/2] = {0};
97 
98 	    ret = RegQueryValueExW(regkey,
99 				   wkeyname,
100 				   NULL,
101 				   NULL,
102 				   (LPBYTE) wval,
103 				   &datalen);
104 
105 	    if (ret == ERROR_SUCCESS) {
106 		gchar *result;
107 
108 		result = g_utf16_to_utf8(wval, -1, NULL, NULL, NULL);
109 		if (result != NULL) {
110 		    strcpy(keyval, result);
111 		    g_free(result);
112 		} else {
113 		    enc_err = 1;
114 		}
115 	    }
116 	    g_free(wkeyname);
117 	}
118 
119 	RegCloseKey(regkey);
120     }
121 
122     g_free(regpath);
123 
124     if (ret != ERROR_SUCCESS || enc_err) {
125 	if (ret != ERROR_SUCCESS) {
126 	    win_print_last_error();
127 	}
128 	*keyval = '\0';
129 	return 1;
130     }
131 
132     return 0;
133 }
134 
135 static char netfile[FILENAME_MAX];
136 
get_gretlnet_filename(void)137 const char *get_gretlnet_filename (void)
138 {
139     return (netfile[0] == '\0')? NULL : netfile;
140 }
141 
set_gretlnet_filename(const char * pkgdir)142 int set_gretlnet_filename (const char *pkgdir)
143 {
144     netfile[0] = '\0';
145     strncat(netfile, pkgdir, FILENAME_MAX-1);
146     strcat(netfile, "gretlnet.txt");
147 
148     return 0;
149 }
150 
cli_gretlnet_open(void)151 static FILE *cli_gretlnet_open (void)
152 {
153     FILE *fp = NULL;
154 
155     if (*netfile != '\0') {
156 	fp = gretl_fopen(netfile, "r");
157     }
158 
159     return fp;
160 }
161 
cli_rcfile_open(void)162 static FILE *cli_rcfile_open (void)
163 {
164     gchar *rcfile = NULL;
165     FILE *fp = NULL;
166 
167 #ifndef PKGBUILD
168     /* try "HOME" first */
169     char *home = getenv("HOME");
170 
171     if (home != NULL) {
172 	rcfile = g_build_filename(home, ".gretl2rc", NULL);
173     }
174 #endif
175 
176     if (rcfile == NULL) {
177 	char *appdata = appdata_path();
178 
179 	if (appdata != NULL) {
180 	    rcfile = g_build_filename(appdata, "gretl", ".gretl2rc", NULL);
181 	    free(appdata);
182 	}
183     }
184 
185     if (rcfile != NULL) {
186 	fp = gretl_fopen(rcfile, "r");
187 	g_free(rcfile);
188     }
189 
190     return fp;
191 }
192 
193 /* called from gretlcli.exe and gretlmpi.exe */
194 
win32_cli_read_rc(void)195 void win32_cli_read_rc (void)
196 {
197     ConfigPaths cpaths = {0};
198     char dbproxy[64] = {0};
199     gchar *gptheme = NULL;
200     int use_proxy = 0;
201     int updated = 0;
202     FILE *fp;
203 
204     /* try for a per-user rc file first */
205     fp = cli_rcfile_open();
206     if (fp != NULL) {
207 	get_gretl_config_from_file(fp, &cpaths, dbproxy,
208 				   &use_proxy, &updated,
209 				   &gptheme);
210 	fclose(fp);
211     }
212 
213     /* read the "gretlnet" file, if present: any settings from this
214        file will override those from the per-user rc file.
215     */
216     fp = cli_gretlnet_open();
217     if (fp != NULL) {
218 	get_gretl_config_from_file(fp, &cpaths, dbproxy,
219 				   &use_proxy, &updated,
220 				   &gptheme);
221 	fclose(fp);
222     }
223 
224     /* for a short list of items, if they're (still) missing, maybe we
225        can get them from the registry
226     */
227 
228     if (cpaths.gretldir[0] == '\0') {
229 	read_reg_val(HKEY_LOCAL_MACHINE, "gretl", "gretldir",
230 		     cpaths.gretldir);
231     }
232 
233     if (cpaths.x12a[0] == '\0') {
234 	read_reg_val(HKEY_LOCAL_MACHINE, "x12arima", "x12a",
235 		     cpaths.x12a);
236     }
237 
238     if (cpaths.tramo[0] == '\0') {
239 	read_reg_val(HKEY_LOCAL_MACHINE, "tramo", "tramo",
240 		     cpaths.tramo);
241     }
242 
243     gretl_set_paths(&cpaths);
244     gretl_www_init(dbproxy, use_proxy);
245 
246     if (gptheme != NULL) {
247 	set_plotstyle(gptheme);
248 	g_free(gptheme);
249     }
250 
251     if (updated) {
252 	update_addons_index(NULL);
253     }
254 }
255 
win_show_last_error(void)256 void win_show_last_error (void)
257 {
258     DWORD dw = GetLastError();
259     LPVOID buf;
260 
261     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
262 		  FORMAT_MESSAGE_FROM_SYSTEM |
263 		  FORMAT_MESSAGE_IGNORE_INSERTS,
264 		  NULL,
265 		  dw,
266 		  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
267 		  (LPTSTR) &buf,
268 		  0,
269 		  NULL);
270 
271     MessageBox(NULL, (LPCTSTR) buf, "Error", MB_OK | MB_ICONERROR);
272     LocalFree(buf);
273 }
274 
win_copy_last_error(void)275 void win_copy_last_error (void)
276 {
277     gint err = GetLastError();
278     gchar *buf = g_win32_error_message(err);
279 
280     gretl_errmsg_set(buf);
281     g_free(buf);
282 }
283 
ensure_utf16(const char * s1,gunichar2 ** pmod1,const char * s2,gunichar2 ** pmod2)284 static int ensure_utf16 (const char *s1, gunichar2 **pmod1,
285 			 const char *s2, gunichar2 **pmod2)
286 {
287     GError *gerr = NULL;
288     int err = 0;
289 
290     if (s1 != NULL && *s1 != '\0') {
291 	if (windebug) {
292 	    fprintf(stderr, "recoding s1 to UTF-16\n");
293 	}
294 	*pmod1 = g_utf8_to_utf16(s1, -1, NULL, NULL, &gerr);
295 	if (*pmod1 == NULL || gerr != NULL) {
296 	    err = 1;
297 	}
298     }
299 
300     if (!err && s2 != NULL && *s2 != '\0') {
301 	if (windebug) {
302 	    fprintf(stderr, "recoding s2 to UTF-16\n");
303 	}
304 	*pmod2 = g_utf8_to_utf16(s2, -1, NULL, NULL, &gerr);
305 	if (*pmod2 == NULL || gerr != NULL) {
306 	    err = 1;
307 	}
308     }
309 
310     if (gerr != NULL) {
311 	fprintf(stderr, "ensure_utf16: got GLib error:\n");
312 	fprintf(stderr, " '%s'\n", gerr->message);
313 	gretl_errmsg_set(gerr->message);
314 	g_error_free(gerr);
315     }
316 
317     return err;
318 }
319 
320 /* If the command-line (*ps1) and/or current directory
321    (*ps2) are UTF-8, convert them to locale encoding
322    ("system codepage") in *ls1 and *ls2 respectively,
323    and make *ps1, *ps2 point to the converted values.
324 
325    This is exclusively for the benefit of third-party
326    software that expects "ANSI" filenames.
327 
328    2020-10-28: We get out of here immediately if it turns
329    out that the Windows charset is UTF-8; but it remains to
330    be seen if it actually works to pass UTF-8 filenames
331    to third-party programs in that case.
332 */
333 
ensure_locale_encoding(const char ** ps1,gchar ** ls1,const char ** ps2,gchar ** ls2)334 int ensure_locale_encoding (const char **ps1, gchar **ls1,
335 			    const char **ps2, gchar **ls2)
336 {
337     GError *gerr = NULL;
338     int err = 0;
339 
340     if (g_get_charset(NULL)) {
341 	/* the Windows charset is UTF-8 */
342 	return 0;
343     }
344 
345     if (ps1 != NULL && *ps1 != NULL && utf8_encoded(*ps1)) {
346 	/* *ps1 will be the command line */
347 	if (windebug) {
348 	    fprintf(stderr, "ensure_locale_encoding: recoding cmdline to locale\n");
349 	}
350 	*ls1 = g_locale_from_utf8(*ps1, -1, NULL, NULL, &gerr);
351 	if (*ls1 == NULL || gerr != NULL) {
352 	    err = 1;
353 	} else {
354 	    *ps1 = (const char *) *ls1;
355 	    if (windebug) {
356 		fprintf(stderr, "recoded cmdline: '%s'\n", *ps1);
357 	    }
358 	}
359     }
360 
361     if (!err && ps2 != NULL && *ps2 != NULL && utf8_encoded(*ps2)) {
362 	/* *ps2, if present, will be the current directory */
363 	*ls2 = g_win32_locale_filename_from_utf8(*ps2);
364 	if (*ls2 == NULL) {
365 	    err = 1;
366 	} else {
367 	    *ps2 = (const char *) *ls2;
368 	}
369     }
370 
371     if (gerr != NULL) {
372 	fprintf(stderr, "ensure_locale_encoding: got GLib error:\n");
373 	fprintf(stderr, " '%s'\n", gerr->message);
374 	gretl_errmsg_set(gerr->message);
375 	g_error_free(gerr);
376     }
377 
378     return err;
379 }
380 
381 /* covers the cases of (a) exec'ing a console application
382    as "slave" (without opening a console window) and (b)
383    exec'ing a GUI app (in fact, just wgnuplot.exe) as slave
384 */
385 
real_win_run_sync(char * cmdline,const char * currdir,int console_app)386 static int real_win_run_sync (char *cmdline,
387 			      const char *currdir,
388 			      int console_app)
389 {
390     STARTUPINFO sinfo;
391     PROCESS_INFORMATION pinfo;
392     DWORD exitcode;
393     DWORD flags;
394     gchar *ls1 = NULL;
395     gchar *ls2 = NULL;
396     int ok, err = 0;
397 
398     err = ensure_locale_encoding((const char **) &cmdline, &ls1,
399 				 &currdir, &ls2);
400     if (err) {
401 	return err;
402     }
403 
404     ZeroMemory(&sinfo, sizeof sinfo);
405     ZeroMemory(&pinfo, sizeof pinfo);
406     sinfo.cb = sizeof sinfo;
407 
408     if (console_app) {
409 	flags = CREATE_NO_WINDOW | HIGH_PRIORITY_CLASS;
410     } else {
411 	sinfo.dwFlags = STARTF_USESHOWWINDOW;
412 	sinfo.wShowWindow = SW_SHOWMINIMIZED;
413 	flags = HIGH_PRIORITY_CLASS;
414     }
415 
416     /* zero return means failure */
417     ok = CreateProcess(NULL,
418 		       cmdline,
419 		       NULL,
420 		       NULL,
421 		       FALSE,
422 		       flags,
423 		       NULL,
424 		       currdir,
425 		       &sinfo,
426 		       &pinfo);
427 
428     if (!ok) {
429 	fprintf(stderr, "win_run_sync: failed command:\n%s\n", cmdline);
430 	win_copy_last_error();
431 	err = 1;
432     } else {
433 	WaitForSingleObject(pinfo.hProcess, INFINITE);
434 	if (GetExitCodeProcess(pinfo.hProcess, &exitcode)) {
435 	    /* the call "succeeded" */
436 	    if (exitcode != 0) {
437 		fprintf(stderr, "%s: exit code %u\n", cmdline, exitcode);
438 		gretl_errmsg_sprintf("%s: exit code %u", cmdline,
439 				     exitcode);
440 		err = 1;
441 	    }
442 	} else {
443 	    fprintf(stderr, "win_run_sync: no exit code:\n%s\n", cmdline);
444 	    win_copy_last_error();
445 	    err = 1;
446 	}
447     }
448 
449     CloseHandle(pinfo.hProcess);
450     CloseHandle(pinfo.hThread);
451 
452     g_free(ls1);
453     g_free(ls2);
454 
455     return err;
456 }
457 
win_run_sync_unicode(char * cmdline,const char * currdir,int console_app)458 static int win_run_sync_unicode (char *cmdline,
459 				 const char *currdir,
460 				 int console_app)
461 {
462     STARTUPINFOW sinfo;
463     PROCESS_INFORMATION pinfo;
464     DWORD exitcode;
465     DWORD flags;
466     gunichar2 *cl16 = NULL;
467     gunichar2 *cd16 = NULL;
468     int ok, err = 0;
469 
470     err = ensure_utf16(cmdline, &cl16, currdir, &cd16);
471     if (err) {
472 	return err;
473     }
474 
475     ZeroMemory(&sinfo, sizeof sinfo);
476     ZeroMemory(&pinfo, sizeof pinfo);
477     sinfo.cb = sizeof sinfo;
478 
479     if (console_app) {
480 	flags = CREATE_NO_WINDOW | HIGH_PRIORITY_CLASS;
481     } else {
482 	sinfo.dwFlags = STARTF_USESHOWWINDOW;
483 	sinfo.wShowWindow = SW_SHOWMINIMIZED;
484 	flags = HIGH_PRIORITY_CLASS;
485     }
486 
487     /* zero return means failure */
488     ok = CreateProcessW(NULL,
489 			cl16,
490 			NULL,
491 			NULL,
492 			FALSE,
493 			flags,
494 			NULL,
495 			cd16,
496 			&sinfo,
497 			&pinfo);
498 
499     if (!ok) {
500 	fprintf(stderr, "win_run_sync: failed command:\n%s\n", cmdline);
501 	win_copy_last_error();
502 	err = 1;
503     } else {
504 	WaitForSingleObject(pinfo.hProcess, INFINITE);
505 	if (GetExitCodeProcess(pinfo.hProcess, &exitcode)) {
506 	    /* the call "succeeded" */
507 	    if (exitcode != 0) {
508 		fprintf(stderr, "%s: exit code %u\n", cmdline, exitcode);
509 		gretl_errmsg_sprintf("%s: exit code %u", cmdline,
510 				     exitcode);
511 		err = 1;
512 	    }
513 	} else {
514 	    fprintf(stderr, "win_run_sync: no exit code:\n%s\n", cmdline);
515 	    win_copy_last_error();
516 	    err = 1;
517 	}
518     }
519 
520     CloseHandle(pinfo.hProcess);
521     CloseHandle(pinfo.hThread);
522 
523     g_free(cl16);
524     g_free(cd16);
525 
526     return err;
527 }
528 
529 /**
530  * win_run_sync:
531  * @cmdline: command line to execute.
532  * @currdir: current directory for child process (or NULL to
533  * inherit from parent)
534  *
535  * Run a command synchronously (i.e. block until it is
536  * completed) under MS Windows. This is intended for use
537  * with "slave" console applications such a latex, dvips,
538  * tramo, x12a and so on.
539  *
540  * Returns: 0 on success, non-zero on failure.
541  */
542 
win_run_sync(char * cmdline,const char * currdir)543 int win_run_sync (char *cmdline, const char *currdir)
544 {
545 #if 1
546     return win_run_sync_unicode(cmdline, currdir, 1);
547 #else
548     return real_win_run_sync(cmdline, currdir, 1);
549 #endif
550 }
551 
gretl_spawn(char * cmdline)552 int gretl_spawn (char *cmdline)
553 {
554 #if 1
555     return win_run_sync_unicode(cmdline, NULL, 0);
556 #else
557     return real_win_run_sync(cmdline, NULL, 0);
558 #endif
559 }
560 
561 /* Retrieve various special paths from the bowels of MS
562    Windows in UTF-16 form, and convert to UTF-8.
563 */
564 
win_special_path(int folder)565 static char *win_special_path (int folder)
566 {
567     gunichar2 wpath[MAX_PATH] = {0};
568     char *ret = NULL;
569 
570     if (SHGetFolderPathW(NULL, folder | CSIDL_FLAG_CREATE,
571 			 NULL, 0, wpath) == S_OK) {
572 	gchar *upath;
573 
574 	upath = g_utf16_to_utf8(wpath, -1, NULL, NULL, NULL);
575 	if (upath != NULL) {
576 	    ret = gretl_strdup(upath);
577 	    g_free(upath);
578 	}
579     }
580 
581     return ret;
582 }
583 
desktop_path(void)584 char *desktop_path (void)
585 {
586     return win_special_path(CSIDL_DESKTOPDIRECTORY);
587 }
588 
appdata_path(void)589 char *appdata_path (void)
590 {
591 #if 0 /* testing */
592     if (!strcmp(g_get_user_name(), "cottrell")) {
593 	/* fake up a non-ASCII dotdir, in UTF-8 */
594 	const char *s = "c:\\users\\cottrell\\desktop\\Дмитрий";
595 
596 	return gretl_strdup(s);
597     }
598 #else
599     return win_special_path(CSIDL_APPDATA);
600 #endif
601 }
602 
mydocs_path(void)603 char *mydocs_path (void)
604 {
605     return win_special_path(CSIDL_PERSONAL);
606 }
607 
program_files_path(void)608 char *program_files_path (void)
609 {
610     return win_special_path(CSIDL_PROGRAM_FILES);
611 }
612 
program_files_x86_path(void)613 char *program_files_x86_path (void)
614 {
615     return win_special_path(CSIDL_PROGRAM_FILESX86);
616 }
617 
compose_command_line(const char * arg)618 static gchar *compose_command_line (const char *arg)
619 {
620     CHAR cmddir[MAX_PATH];
621     char *cmdline = NULL;
622 
623     GetSystemDirectory(cmddir, sizeof cmddir);
624 
625     if (getenv("SHELLDEBUG")) {
626 	cmdline = g_strdup_printf("%s\\cmd.exe /k %s", cmddir, arg);
627     } else {
628 	cmdline = g_strdup_printf("%s\\cmd.exe /s /c \"%s\"", cmddir, arg);
629     }
630 
631     return cmdline;
632 }
633 
634 #define BUFSIZE 4096
635 
read_from_pipe(HANDLE hwrite,HANDLE hread,char ** sout,PRN * inprn)636 static int read_from_pipe (HANDLE hwrite, HANDLE hread,
637 			   char **sout, PRN *inprn)
638 {
639     PRN *prn;
640     int ok;
641 
642     if (sout != NULL) {
643 	prn = gretl_print_new(GRETL_PRINT_BUFFER, NULL);
644     } else {
645 	prn = inprn;
646     }
647 
648     /* close the write end of the pipe */
649     ok = CloseHandle(hwrite);
650 
651     if (!ok) {
652 	fputs("Closing handle failed\n", stderr);
653     } else {
654 	/* read output from the child process: note that the
655 	   buffer must be NUL-terminated for use with pputs()
656 	*/
657 	CHAR buf[BUFSIZE];
658 	DWORD dwread;
659 
660 	while (1) {
661 	    memset(buf, '\0', BUFSIZE);
662 	    ok = ReadFile(hread, buf, BUFSIZE-1, &dwread, NULL);
663 	    if (!ok || dwread == 0) {
664 		break;
665 	    }
666 	    pputs(prn, buf);
667 	}
668     }
669 
670     if (sout != NULL) {
671 	*sout = gretl_print_steal_buffer(prn);
672 	gretl_print_destroy(prn);
673     }
674 
675     return ok;
676 }
677 
relay_mpi_output(HANDLE hread,char * buf,PRN * prn)678 static int relay_mpi_output (HANDLE hread, char *buf, PRN *prn)
679 {
680     DWORD dwread;
681     int done = 0;
682 
683     memset(buf, 0, BUFSIZE);
684     ReadFile(hread, buf, BUFSIZE - 1, &dwread, NULL);
685 
686     if (dwread > 0) {
687 	char *s = strstr(buf, "__GRETLMPI_EXIT__");
688 
689 	if (s != NULL) {
690 	    done = 1;
691 	    *s = '\0';
692 	}
693 	pputs(prn, buf);
694 	gretl_flush(prn);
695     }
696 
697     return done;
698 }
699 
700 /* Option: OPT_S for shell mode, as opposed to running an
701    executable directly. If @prn is non-NULL we try to pass
702    back output in real time.
703 */
704 
705 static int
run_child_with_pipe(const char * arg,const char * currdir,HANDLE hwrite,HANDLE hread,gretlopt opt,PRN * prn)706 run_child_with_pipe (const char *arg, const char *currdir,
707 		     HANDLE hwrite, HANDLE hread,
708 		     gretlopt opt, PRN *prn)
709 {
710     PROCESS_INFORMATION pinfo;
711     STARTUPINFO sinfo;
712     gchar *cmdline = NULL;
713     gchar *targdir = NULL;
714     gchar *ls1 = NULL;
715     gchar *ls2 = NULL;
716     int ok, err;
717 
718     /* FIXME? */
719     err = ensure_locale_encoding(&arg, &ls1, &currdir, &ls2);
720     if (err) {
721 	return err;
722     }
723 
724     if (opt & OPT_S) {
725 	/* shell mode */
726 	cmdline = compose_command_line(arg);
727     } else {
728 	cmdline = g_strdup(arg);
729     }
730 
731     if (currdir != NULL) {
732 	int n;
733 
734 	targdir = g_strdup(currdir);
735 	n = strlen(targdir);
736 	if (targdir[n-1] == '\\') {
737 	    targdir[n-1] = '\0';
738 	}
739     }
740 
741     ZeroMemory(&pinfo, sizeof pinfo);
742     ZeroMemory(&sinfo, sizeof sinfo);
743 
744     sinfo.cb = sizeof sinfo;
745     sinfo.hStdError = hwrite;
746     sinfo.hStdOutput = hwrite;
747     sinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
748     sinfo.dwFlags |= (STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW);
749     sinfo.wShowWindow = SW_SHOWMINIMIZED;
750 
751     ok = CreateProcess(NULL,
752 		       cmdline,
753 		       NULL,          /* process security attributes */
754 		       NULL,          /* primary thread security attributes */
755 		       TRUE,          /* handles are inherited */
756 		       CREATE_NO_WINDOW,
757 		       NULL,          /* use parent's environment */
758 		       targdir,
759 		       &sinfo,
760 		       &pinfo);
761 
762     if (!ok) {
763 	win_show_last_error();
764     } else {
765 	if (prn != NULL) {
766 	    /* try reading output in real time */
767 	    char buf[BUFSIZE];
768 	    DWORD excode;
769 	    int ok, got_all;
770 
771 	    fprintf(stderr, "Entering MPI real-time read loop\n");
772 	    while (GetExitCodeProcess(pinfo.hProcess, &excode)
773 		   && excode == STILL_ACTIVE) {
774 		got_all = relay_mpi_output(hread, buf, prn);
775 		if (got_all) {
776 		    break;
777 		}
778 	    }
779 	    fprintf(stderr, "HERE got_all = %d\n", got_all);
780 	    CloseHandle(hwrite);
781 	}
782 	CloseHandle(pinfo.hProcess);
783 	CloseHandle(pinfo.hThread);
784     }
785 
786     g_free(cmdline);
787     g_free(targdir);
788     g_free(ls1);
789     g_free(ls2);
790 
791     return ok;
792 }
793 
run_cmd_with_pipes(const char * arg,const char * currdir,char ** sout,PRN * prn,gretlopt opt)794 static int run_cmd_with_pipes (const char *arg, const char *currdir,
795 			       char **sout, PRN *prn, gretlopt opt)
796 {
797     HANDLE hread, hwrite;
798     SECURITY_ATTRIBUTES sattr;
799     int ok;
800 
801     /* set the bInheritHandle flag so pipe handles are inherited */
802     sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
803     sattr.bInheritHandle = TRUE;
804     sattr.lpSecurityDescriptor = NULL;
805 
806     /* create pipe for the child process's STDOUT */
807     ok = CreatePipe(&hread, &hwrite, &sattr, 0);
808 
809     if (!ok) {
810 	win_show_last_error();
811     } else {
812 	/* ensure that the read handle to the child process's pipe for
813 	   STDOUT is not inherited */
814 	SetHandleInformation(hread, HANDLE_FLAG_INHERIT, 0);
815 	if (prn != NULL && (opt & OPT_R)) {
816 	    /* OPT_R is intended to support real time output */
817 	    ok = run_child_with_pipe(arg, currdir, hwrite, hread, opt, prn);
818 	} else {
819 	    ok = run_child_with_pipe(arg, currdir, hwrite, hread, opt, NULL);
820 	    if (ok) {
821 		/* read from child's output pipe on termination */
822 		read_from_pipe(hwrite, hread, sout, prn);
823 	    }
824 	}
825     }
826 
827     return !ok; /* error return */
828 }
829 
830 /* used only by gretl_shell() below */
831 
run_shell_cmd_wait(const char * cmd,PRN * prn)832 static int run_shell_cmd_wait (const char *cmd, PRN *prn)
833 {
834     STARTUPINFO sinfo;
835     PROCESS_INFORMATION pinfo;
836     const char *currdir;
837     gchar *cmdline = NULL;
838     gchar *ls1 = NULL;
839     gchar *ls2 = NULL;
840     int ok, err = 0;
841 
842     currdir = gretl_workdir();
843     err = ensure_locale_encoding(&cmd, &ls1, &currdir, &ls2);
844     if (err) {
845 	return err;
846     }
847 
848     ZeroMemory(&sinfo, sizeof sinfo);
849     ZeroMemory(&pinfo, sizeof pinfo);
850 
851     sinfo.cb = sizeof sinfo;
852     sinfo.dwFlags = STARTF_USESHOWWINDOW;
853     sinfo.wShowWindow = SW_SHOWMINIMIZED;
854 
855     /* includes getting path to cmd.exe */
856     cmdline = compose_command_line(cmd);
857 #if 0
858     fprintf(stderr, "run_shell_cmd_wait: cmd='%s'\n", cmd);
859     fprintf(stderr, "  cmdline='%s'\n", cmdline);
860 #endif
861 
862     ok = CreateProcess(NULL,
863 		       cmdline,
864 		       NULL,
865 		       NULL,
866 		       FALSE,
867 		       CREATE_NEW_CONSOLE | HIGH_PRIORITY_CLASS,
868 		       NULL,
869 		       currdir,
870 		       &sinfo,
871 		       &pinfo);
872 
873     if (!ok) {
874 	win_show_last_error();
875 	err = 1;
876     } else {
877 	WaitForSingleObject(pinfo.hProcess, INFINITE);
878 	CloseHandle(pinfo.hProcess);
879 	CloseHandle(pinfo.hThread);
880     }
881 
882     g_free(cmdline);
883     g_free(ls1);
884     g_free(ls2);
885 
886     return err;
887 }
888 
run_shell_cmd_async(const char * cmd)889 static int run_shell_cmd_async (const char *cmd)
890 {
891     STARTUPINFO sinfo;
892     PROCESS_INFORMATION pinfo;
893     const char *currdir;
894     gchar *cmdcpy;
895     gchar *ls1 = NULL;
896     gchar *ls2 = NULL;
897     int ok, err = 0;
898 
899     currdir = gretl_workdir();
900     err = ensure_locale_encoding(&cmd, &ls1, &currdir, &ls2);
901     if (err) {
902 	return err;
903     }
904 
905     ZeroMemory(&sinfo, sizeof sinfo);
906     ZeroMemory(&pinfo, sizeof pinfo);
907 
908     sinfo.cb = sizeof sinfo;
909 
910     cmdcpy = g_strdup(cmd); /* not supposed to be const */
911     ok = CreateProcess(NULL,
912 		       cmdcpy,
913 		       NULL,
914 		       NULL,
915 		       FALSE,
916 		       0,
917 		       NULL,
918 		       currdir,
919 		       &sinfo,
920 		       &pinfo);
921     g_free(cmdcpy);
922     g_free(ls1);
923     g_free(ls2);
924 
925     if (!ok) {
926 	win_show_last_error();
927 	err = 1;
928     } else {
929 	CloseHandle(pinfo.hProcess);
930 	CloseHandle(pinfo.hThread);
931     }
932 
933     return err;
934 }
935 
gretl_win32_grab_output(const char * cmdline,const char * currdir,char ** sout)936 int gretl_win32_grab_output (const char *cmdline,
937 			     const char *currdir,
938 			     char **sout)
939 {
940     return run_cmd_with_pipes(cmdline, currdir, sout, NULL, OPT_NONE);
941 }
942 
gretl_win32_pipe_output(const char * cmdline,const char * currdir,gretlopt opt,PRN * prn)943 int gretl_win32_pipe_output (const char *cmdline,
944 			     const char *currdir,
945 			     gretlopt opt,
946 			     PRN *prn)
947 {
948     return run_cmd_with_pipes(cmdline, currdir, NULL, prn, opt);
949 }
950 
951 /* note: gretl_shell_grab() is declared in interact.h,
952    and the non-Windows implementation is defined in
953    interact.c
954 */
955 
gretl_shell_grab(const char * arg,char ** sout)956 int gretl_shell_grab (const char *arg, char **sout)
957 {
958     return run_cmd_with_pipes(arg, NULL, sout, NULL, OPT_S);
959 }
960 
gretl_shell(const char * arg,gretlopt opt,PRN * prn)961 int gretl_shell (const char *arg, gretlopt opt, PRN *prn)
962 {
963     int err = 0;
964 
965     if (arg == NULL || *arg == '\0') {
966 	return 0;
967     }
968 
969     if (!libset_get_bool(SHELL_OK)) {
970 	gretl_errmsg_set(_("The shell command is not activated."));
971 	return 1;
972     }
973 
974     arg += strspn(arg, " \t");
975 
976     if (opt & OPT_A) {
977 	err = run_shell_cmd_async(arg);
978     } else if (getenv("GRETL_SHELL_NEW")) {
979 	err = run_cmd_with_pipes(arg, NULL, NULL, prn, OPT_S);
980     } else {
981 	err = run_shell_cmd_wait(arg, prn);
982     }
983 
984     return err;
985 }
986 
987 #define ACCESS_DEBUG 0
988 
989 /* win32_access_init: get security info related to the
990    current user, but independent of the file to be tested
991    for access. We can cache this info and avoid looking
992    it up repeatedly.
993 */
994 
win32_access_init(TRUSTEE_W * pt,SID ** psid)995 static int win32_access_init (TRUSTEE_W *pt, SID **psid)
996 {
997     LPWSTR domain = NULL;
998     SID *sid = NULL;
999     DWORD sidsize = 0, dlen = 0;
1000     SID_NAME_USE stype;
1001     const gchar *username;
1002     gunichar2 *acname = NULL;
1003     int ok, err = 0;
1004 
1005     /* note: the following always returns UTF-8 */
1006     username = g_get_user_name();
1007     acname = g_utf8_to_utf16(username, -1, NULL, NULL, NULL);
1008 
1009     /* get the sizes of the SID and domain */
1010     ok = LookupAccountNameW(NULL, acname, NULL, &sidsize,
1011 			    NULL, &dlen, &stype);
1012     if (!ok && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
1013 	fprintf(stderr, "LookupAccountNameW failed (username '%s')\n", username);
1014 	err = 1;
1015     } else {
1016 	*psid = sid = LocalAlloc(0, sidsize);
1017 	domain = LocalAlloc(0, dlen * sizeof *domain);
1018 	if (sid == NULL || domain == NULL) {
1019 	    err = 1;
1020 	}
1021     }
1022 
1023 #if ACCESS_DEBUG
1024     fprintf(stderr, "win32_access_init (1): err = %d\n", err);
1025 #endif
1026 
1027     if (!err) {
1028 	/* call the Lookup function for real */
1029 	ok = LookupAccountNameW(NULL, acname, sid, &sidsize,
1030 				domain, &dlen, &stype);
1031 	err = !ok;
1032     }
1033 
1034 #if ACCESS_DEBUG
1035     fprintf(stderr, "win32_access_init (2): err = %d\n", err);
1036 #endif
1037 
1038     if (!err) {
1039 	/* build a trustee; note that @sid gets stuck onto @pt */
1040 	BuildTrusteeWithSidW(pt, sid);
1041     }
1042 
1043     g_free(acname);
1044 
1045     if (domain != NULL) {
1046 	/* we won't need this again */
1047 	LocalFree(domain);
1048     }
1049 
1050     return err;
1051 }
1052 
1053 /* Note: the return values from the underlying win32 API
1054    functions are translated to return 0 on success.
1055    @path must be given as UTF-8.
1056 */
1057 
win32_write_access(const char * path)1058 int win32_write_access (const char *path)
1059 {
1060     static SID *sid = NULL;
1061     static TRUSTEE_W t;
1062     ACL *dacl = NULL;
1063     SECURITY_DESCRIPTOR *sd = NULL;
1064     ACCESS_MASK amask;
1065     GError *gerr = NULL;
1066     gunichar2 *wpath;
1067     int ret, ok = 0;
1068     int err = 0;
1069 
1070     if (path == NULL || *path == '\0') {
1071 	return -1;
1072     }
1073 
1074     wpath = g_utf8_to_utf16(path, -1, NULL, NULL, &gerr);
1075     if (gerr != NULL) {
1076         gretl_errmsg_set(gerr->message);
1077         g_error_free(gerr);
1078 	return -1;
1079     }
1080 
1081     /* basic check for the read-write attribute first */
1082     err = _waccess(wpath, 06);
1083 
1084 #if ACCESS_DEBUG
1085     fprintf(stderr, "win32_write_access (%s): first check: err = %d\n",
1086 	    path, err);
1087 #endif
1088 
1089     if (err) {
1090 	g_free(wpath);
1091 	return -1;
1092     }
1093 
1094     if (sid == NULL) {
1095 	/* get user info and cache it in static variables */
1096 	err = win32_access_init(&t, &sid);
1097     }
1098 
1099 #if ACCESS_DEBUG
1100     fprintf(stderr, " win32_access_init returned %d\n", err);
1101 #endif
1102 
1103     if (!err) {
1104 	/* note: @dacl will be a pointer into @sd */
1105 	ret = GetNamedSecurityInfoW(wpath, SE_FILE_OBJECT,
1106 				    DACL_SECURITY_INFORMATION,
1107 				    NULL, NULL, &dacl, NULL,
1108 				    (PSECURITY_DESCRIPTOR) &sd);
1109 	err = (ret != ERROR_SUCCESS);
1110 #if ACCESS_DEBUG
1111 	fprintf(stderr, " GetNamedSecurityInfoW: err = %d\n", err);
1112 #endif
1113     }
1114 
1115     if (!err) {
1116 	/* get the access mask for this trustee */
1117 	ret = GetEffectiveRightsFromAclW(dacl, &t, &amask);
1118         if (ret != ERROR_SUCCESS) {
1119             fprintf(stderr, "GetEffectiveRights...: ret=%d\n", ret);
1120             if (ret != RPC_S_SERVER_UNAVAILABLE && ret != ERROR_NO_SUCH_DOMAIN) {
1121                 err = 1;
1122             }
1123         } else if (amask & STANDARD_RIGHTS_WRITE) {
1124 	    ok = 1;
1125 	}
1126 #if ACCESS_DEBUG
1127 	fprintf(stderr, " GetEffectiveRights: err = %d, ok = %d\n", err, ok);
1128 #endif
1129     }
1130 
1131     g_free(wpath);
1132     if (sd != NULL) {
1133 	LocalFree(sd);
1134     }
1135     if (err) {
1136 	win_copy_last_error();
1137     }
1138 
1139     return ok ? 0 : -1;
1140 }
1141 
slash_convert(char * str,int which)1142 char *slash_convert (char *str, int which)
1143 {
1144     char *p;
1145 
1146     if (str == NULL) {
1147 	return NULL;
1148     }
1149 
1150     p = str;
1151     while (*p) {
1152 	if (which == FROM_BACKSLASH) {
1153 	    if (*p == '\\') *p = '/';
1154 	} else if (which == TO_BACKSLASH) {
1155 	    if (*p == '/') *p = '\\';
1156 	}
1157 	p++;
1158     }
1159 
1160     return str;
1161 }
1162 
try_for_R_path(HKEY tree,char * s)1163 static int try_for_R_path (HKEY tree, char *s)
1164 {
1165     int err = 0;
1166 
1167     /* this used to work with R 2.9.1 */
1168     err = read_reg_val(tree, "R-core\\R", "InstallPath", s);
1169 
1170     if (err) {
1171 	char version[8], path[32];
1172 
1173 	/* new-style: path contains R version number */
1174 	err = read_reg_val(tree, "R-core\\R", "Current Version",
1175 			   version);
1176 	if (!err) {
1177 	    sprintf(path, "R-core\\R\\%s", version);
1178 	    err = read_reg_val(tree, path, "InstallPath", s);
1179 	}
1180     }
1181 
1182     if (err) {
1183 	/* did this variant work at one time? */
1184 	err = read_reg_val(tree, "R", "InstallPath", s);
1185     }
1186 
1187     return err;
1188 }
1189 
append_R_filename(char * s,int which)1190 static void append_R_filename (char *s, int which)
1191 {
1192     if (which == REXE) {
1193 	strcat(s, "R.exe");
1194     } else if (which == RGUI) {
1195 	strcat(s, "Rgui.exe");
1196     } else if (which == RTERM) {
1197 	strcat(s, "Rterm.exe");
1198     } else if (which == RLIB) {
1199 	strcat(s, "R.dll");
1200     }
1201 }
1202 
1203 /* See if we can get the R installation path from the Windows
1204    registry. This is not a sure thing, since at least as of R
1205    2.11.1 recording the path in the registry on installation
1206    is optional.
1207 
1208    To complicate matters, the path within the registry where
1209    we might find this information changed somewhere between
1210    R 2.9.1 and R 2.11.1.
1211 */
1212 
R_path_from_registry(char * s,int which)1213 int R_path_from_registry (char *s, int which)
1214 {
1215     int err;
1216 
1217     *s = '\0';
1218 
1219     err = try_for_R_path(HKEY_LOCAL_MACHINE, s);
1220 
1221     if (err) {
1222 	/* maybe user is not an admin? */
1223 	err = try_for_R_path(HKEY_CURRENT_USER, s);
1224     }
1225 
1226     if (!err && which != RBASE) {
1227 	int openerr = 0;
1228 
1229 	strcat(s, "\\bin\\");
1230 	append_R_filename(s, which);
1231 	openerr = gretl_test_fopen(s, "rb");
1232 	if (openerr) {
1233 #ifdef _WIN64
1234 	    const char *arch[] = {
1235 		"x64\\",
1236 		"i386\\"
1237 	    };
1238 #else
1239 	    const char *arch[] = {
1240 		"i386\\",
1241 		"x64\\"
1242 	    };
1243 #endif
1244 	    char *p = strrchr(s, 'R');
1245 
1246 	    *p = '\0';
1247 	    strcat(s, arch[0]);
1248 	    append_R_filename(s, which);
1249 	    openerr = gretl_test_fopen(s, "rb");
1250 	    if (openerr) {
1251 		/* try for alternate arch */
1252 		*p = '\0';
1253 		strcat(s, arch[1]);
1254 		append_R_filename(s, which);
1255 		openerr = gretl_test_fopen(s, "rb");
1256 		if (openerr) {
1257 		    err = E_FOPEN;
1258 		}
1259 	    }
1260 	}
1261     }
1262 
1263     if (err) {
1264 	/* scrub invalid path */
1265 	*s = '\0';
1266     }
1267 
1268     if (windebug) {
1269 	fprintf(stderr, "R_path_from_registry: '%s'\n", s);
1270     }
1271 
1272     return err;
1273 }
1274 
win32_check_for_program(const char * prog)1275 int win32_check_for_program (const char *prog)
1276 {
1277     char tmp[MAXLEN];
1278     WIN32_FIND_DATA find_data;
1279     HANDLE hfind;
1280     int ret = 1;
1281 
1282     if (prog == NULL || *prog == '\0') {
1283 	return 0;
1284     }
1285 
1286     hfind = FindFirstFile(prog, &find_data);
1287     if (hfind == INVALID_HANDLE_VALUE) {
1288 	ret = 0;
1289     }
1290     FindClose(hfind);
1291 
1292     if (ret == 0) {
1293 	char *p;
1294 
1295 	ret = SearchPath(NULL, prog, NULL, MAXLEN, tmp, &p);
1296     }
1297 
1298     return ret != 0;
1299 }
1300 
1301 /* the following needed since mingw does not include strptime */
1302 
1303 /*
1304  * Copyright (c) 1999 Kungliga Tekniska Högskolan
1305  * (Royal Institute of Technology, Stockholm, Sweden).
1306  * All rights reserved.
1307  *
1308  * Redistribution and use in source and binary forms, with or without
1309  * modification, are permitted provided that the following conditions
1310  * are met:
1311  *
1312  * 1. Redistributions of source code must retain the above copyright
1313  *    notice, this list of conditions and the following disclaimer.
1314  *
1315  * 2. Redistributions in binary form must reproduce the above copyright
1316  *    notice, this list of conditions and the following disclaimer in the
1317  *    documentation and/or other materials provided with the distribution.
1318  *
1319  * 3. Neither the name of KTH nor the names of its contributors may be
1320  *    used to endorse or promote products derived from this software without
1321  *    specific prior written permission.
1322  *
1323  * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
1324  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1325  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1326  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
1327  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1328  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1329  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
1330  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
1331  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
1332  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1333  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
1334 
1335 #include <stddef.h>
1336 #include <stdio.h>
1337 #include <time.h>
1338 #include <string.h>
1339 
1340 static const char *abb_weekdays[] = {
1341     "Sun",
1342     "Mon",
1343     "Tue",
1344     "Wed",
1345     "Thu",
1346     "Fri",
1347     "Sat",
1348     NULL
1349 };
1350 
1351 static const char *full_weekdays[] = {
1352     "Sunday",
1353     "Monday",
1354     "Tuesday",
1355     "Wednesday",
1356     "Thursday",
1357     "Friday",
1358     "Saturday",
1359     NULL
1360 };
1361 
1362 static const char *abb_month[] = {
1363     "Jan",
1364     "Feb",
1365     "Mar",
1366     "Apr",
1367     "May",
1368     "Jun",
1369     "Jul",
1370     "Aug",
1371     "Sep",
1372     "Oct",
1373     "Nov",
1374     "Dec",
1375     NULL
1376 };
1377 
1378 static const char *full_month[] = {
1379     "January",
1380     "February",
1381     "March",
1382     "April",
1383     "May",
1384     "June",
1385     "July",
1386     "August",
1387     "September",
1388     "October",
1389     "November",
1390     "December",
1391     NULL,
1392 };
1393 
1394 static const char *ampm[] = {
1395     "am",
1396     "pm",
1397     NULL
1398 };
1399 
1400 /*
1401  * tm_year is relative this year
1402  */
1403 const int tm_year_base = 1900;
1404 
1405 /*
1406  * Return TRUE iff `year' was a leap year.
1407  * Needed for strptime.
1408  */
1409 static int
is_leap_year(int year)1410 is_leap_year (int year)
1411 {
1412     return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
1413 }
1414 
match_string(const char ** buf,const char ** strs)1415 static int match_string (const char **buf, const char **strs)
1416 {
1417     int i = 0;
1418 
1419     for (i = 0; strs[i] != NULL; ++i) {
1420 	int len = strlen (strs[i]);
1421 
1422 	if (strncasecmp (*buf, strs[i], len) == 0) {
1423 	    *buf += len;
1424 	    return i;
1425 	}
1426     }
1427     return -1;
1428 }
1429 
first_day(int year)1430 static int first_day (int year)
1431 {
1432     int ret = 4;
1433 
1434     for (; year > 1970; --year)
1435 	ret = (ret + 365 + is_leap_year (year) ? 1 : 0) % 7;
1436     return ret;
1437 }
1438 
1439 /*
1440  * Set `timeptr' given `wnum' (week number [0, 53])
1441  * Needed for strptime
1442  */
1443 
1444 static void
set_week_number_sun(struct tm * timeptr,int wnum)1445 set_week_number_sun (struct tm *timeptr, int wnum)
1446 {
1447     int fday = first_day (timeptr->tm_year + tm_year_base);
1448 
1449     timeptr->tm_yday = wnum * 7 + timeptr->tm_wday - fday;
1450     if (timeptr->tm_yday < 0) {
1451 	timeptr->tm_wday = fday;
1452 	timeptr->tm_yday = 0;
1453     }
1454 }
1455 
1456 /*
1457  * Set `timeptr' given `wnum' (week number [0, 53])
1458  * Needed for strptime
1459  */
1460 
1461 static void
set_week_number_mon(struct tm * timeptr,int wnum)1462 set_week_number_mon (struct tm *timeptr, int wnum)
1463 {
1464     int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
1465 
1466     timeptr->tm_yday = wnum * 7 + (timeptr->tm_wday + 6) % 7 - fday;
1467     if (timeptr->tm_yday < 0) {
1468 	timeptr->tm_wday = (fday + 1) % 7;
1469 	timeptr->tm_yday = 0;
1470     }
1471 }
1472 
1473 /*
1474  * Set `timeptr' given `wnum' (week number [0, 53])
1475  * Needed for strptime
1476  */
set_week_number_mon4(struct tm * timeptr,int wnum)1477 static void set_week_number_mon4 (struct tm *timeptr, int wnum)
1478 {
1479     int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
1480     int offset = 0;
1481 
1482     if (fday < 4)
1483 	offset += 7;
1484 
1485     timeptr->tm_yday = offset + (wnum - 1) * 7 + timeptr->tm_wday - fday;
1486     if (timeptr->tm_yday < 0) {
1487 	timeptr->tm_wday = fday;
1488 	timeptr->tm_yday = 0;
1489     }
1490 }
1491 
1492 /* tailor-made for handling YYYYMMDD */
1493 
parse_iso_basic(const char * buf,struct tm * timeptr)1494 static char *parse_iso_basic (const char *buf, struct tm *timeptr)
1495 {
1496     if (strlen(buf) == 8) {
1497 	char *s;
1498 	double x;
1499 
1500 	errno = 0;
1501 	x = strtod(buf, &s);
1502 
1503 	if (errno == 0 && *s == '\0') {
1504 	    /* successful conversion */
1505 	    int y = (int) floor(x / 10000);
1506 	    int m = (int) floor((x - 10000*y) / 100);
1507 	    int d = (int) (x - 10000*y - 100*m);
1508 	    guint32 ed = epoch_day_from_ymd(y, m, d);
1509 
1510 	    if (ed > 0) {
1511 		memset(timeptr, 0, sizeof *timeptr);
1512 		timeptr->tm_year = y - tm_year_base;
1513 		timeptr->tm_mon = m - 1;
1514 		timeptr->tm_mday = d;
1515 		buf += 8;
1516 	    }
1517 	}
1518     }
1519 
1520     return (char *) buf;
1521 }
1522 
my_strtoi(const char * s,char ** endptr,int dmax)1523 static int my_strtoi (const char *s, char **endptr, int dmax)
1524 {
1525     int i, k, d = 0;
1526 
1527     for (i=0; s[i]; i++) {
1528 	if (isdigit(s[i])) d++;
1529 	else break;
1530     }
1531 
1532     if (d > dmax) {
1533 	char tmp[6];
1534 
1535 	*tmp = '\0';
1536 	strncat(tmp, s, dmax);
1537 	k = (int) strtol(tmp, NULL, 10);
1538 	*endptr = (char *) s + dmax;
1539     } else {
1540 	k = (int) strtol(s, endptr, 10);
1541     }
1542 
1543     return k;
1544 }
1545 
strptime(const char * buf,const char * format,struct tm * timeptr)1546 char *strptime (const char *buf, const char *format, struct tm *timeptr)
1547 {
1548     char c;
1549 
1550     if (strcmp(format, "%Y%m%d") == 0) {
1551 	/* the case where the format contains no punctuation
1552 	   is not handled correctly below
1553 	*/
1554 	return parse_iso_basic(buf, timeptr);
1555     }
1556 
1557     for (; (c = *format) != '\0'; ++format) {
1558 	char *s;
1559 	int ret;
1560 
1561 	if (isspace(c)) {
1562 	    while (isspace (*buf))
1563 		++buf;
1564 	} else if (c == '%' && format[1] != '\0') {
1565 	    c = *++format;
1566 	    if (c == 'E' || c == 'O')
1567 		c = *++format;
1568 	    switch (c) {
1569 	    case 'A' :
1570 		ret = match_string(&buf, full_weekdays);
1571 		if (ret < 0)
1572 		    return NULL;
1573 		timeptr->tm_wday = ret;
1574 		break;
1575 	    case 'a' :
1576 		ret = match_string(&buf, abb_weekdays);
1577 		if (ret < 0)
1578 		    return NULL;
1579 		timeptr->tm_wday = ret;
1580 		break;
1581 	    case 'B' :
1582 		ret = match_string(&buf, full_month);
1583 		if (ret < 0)
1584 		    return NULL;
1585 		timeptr->tm_mon = ret;
1586 		break;
1587 	    case 'b' :
1588 	    case 'h' :
1589 		ret = match_string(&buf, abb_month);
1590 		if (ret < 0)
1591 		    return NULL;
1592 		timeptr->tm_mon = ret;
1593 		break;
1594 	    case 'C' :
1595 		ret = my_strtoi(buf, &s, 2);
1596 		if (s == buf)
1597 		    return NULL;
1598 		timeptr->tm_year = (ret * 100) - tm_year_base;
1599 		buf = s;
1600 		break;
1601 	    case 'c' :
1602 		abort ();
1603 	    case 'D' :		/* %m/%d/%y */
1604 		s = strptime(buf, "%m/%d/%y", timeptr);
1605 		if (s == NULL)
1606 		    return NULL;
1607 		buf = s;
1608 		break;
1609 	    case 'd' :
1610 	    case 'e' :
1611 		ret = my_strtoi(buf, &s, 2);
1612 		if (s == buf)
1613 		    return NULL;
1614 		timeptr->tm_mday = ret;
1615 		buf = s;
1616 		break;
1617 	    case 'H' :
1618 	    case 'k' :
1619 		ret = my_strtoi(buf, &s, 2);
1620 		if (s == buf)
1621 		    return NULL;
1622 		timeptr->tm_hour = ret;
1623 		buf = s;
1624 		break;
1625 	    case 'I' :
1626 	    case 'l' :
1627 		ret = my_strtoi(buf, &s, 2);
1628 		if (s == buf)
1629 		    return NULL;
1630 		if (ret == 12)
1631 		    timeptr->tm_hour = 0;
1632 		else
1633 		    timeptr->tm_hour = ret;
1634 		buf = s;
1635 		break;
1636 	    case 'j' :
1637 		ret = my_strtoi(buf, &s, 3);
1638 		if (s == buf)
1639 		    return NULL;
1640 		timeptr->tm_yday = ret - 1;
1641 		buf = s;
1642 		break;
1643 	    case 'm' :
1644 		ret = my_strtoi(buf, &s, 2);
1645 		if (s == buf)
1646 		    return NULL;
1647 		timeptr->tm_mon = ret - 1;
1648 		buf = s;
1649 		break;
1650 	    case 'M' :
1651 		ret = my_strtoi(buf, &s, 2);
1652 		if (s == buf)
1653 		    return NULL;
1654 		timeptr->tm_min = ret;
1655 		buf = s;
1656 		break;
1657 	    case 'n' :
1658 		if (*buf == '\n')
1659 		    ++buf;
1660 		else
1661 		    return NULL;
1662 		break;
1663 	    case 'p' :
1664 		ret = match_string(&buf, ampm);
1665 		if (ret < 0)
1666 		    return NULL;
1667 		if (timeptr->tm_hour == 0) {
1668 		    if (ret == 1)
1669 			timeptr->tm_hour = 12;
1670 		} else
1671 		    timeptr->tm_hour += 12;
1672 		break;
1673 	    case 'r' :		/* %I:%M:%S %p */
1674 		s = strptime(buf, "%I:%M:%S %p", timeptr);
1675 		if (s == NULL)
1676 		    return NULL;
1677 		buf = s;
1678 		break;
1679 	    case 'R' :		/* %H:%M */
1680 		s = strptime(buf, "%H:%M", timeptr);
1681 		if (s == NULL)
1682 		    return NULL;
1683 		buf = s;
1684 		break;
1685 	    case 'S' :
1686 		ret = my_strtoi(buf, &s, 2);
1687 		if (s == buf)
1688 		    return NULL;
1689 		timeptr->tm_sec = ret;
1690 		buf = s;
1691 		break;
1692 	    case 't' :
1693 		if (*buf == '\t')
1694 		    ++buf;
1695 		else
1696 		    return NULL;
1697 		break;
1698 	    case 'T' :		/* %H:%M:%S */
1699 	    case 'X' :
1700 		s = strptime(buf, "%H:%M:%S", timeptr);
1701 		if (s == NULL)
1702 		    return NULL;
1703 		buf = s;
1704 		break;
1705 	    case 'u' :
1706 		ret = my_strtoi(buf, &s, 1);
1707 		if (s == buf)
1708 		    return NULL;
1709 		timeptr->tm_wday = ret - 1;
1710 		buf = s;
1711 		break;
1712 	    case 'w' :
1713 		ret = my_strtoi(buf, &s, 1);
1714 		if (s == buf)
1715 		    return NULL;
1716 		timeptr->tm_wday = ret;
1717 		buf = s;
1718 		break;
1719 	    case 'U' :
1720 		ret = my_strtoi(buf, &s, 2);
1721 		if (s == buf)
1722 		    return NULL;
1723 		set_week_number_sun(timeptr, ret);
1724 		buf = s;
1725 		break;
1726 	    case 'V' :
1727 		ret = my_strtoi(buf, &s, 2);
1728 		if (s == buf)
1729 		    return NULL;
1730 		set_week_number_mon4(timeptr, ret);
1731 		buf = s;
1732 		break;
1733 	    case 'W' :
1734 		ret = my_strtoi(buf, &s, 2);
1735 		if (s == buf)
1736 		    return NULL;
1737 		set_week_number_mon(timeptr, ret);
1738 		buf = s;
1739 		break;
1740 	    case 'x' :
1741 		s = strptime(buf, "%Y:%m:%d", timeptr);
1742 		if (s == NULL)
1743 		    return NULL;
1744 		buf = s;
1745 		break;
1746 	    case 'y' :
1747 		ret = my_strtoi(buf, &s, 2);
1748 		if (s == buf)
1749 		    return NULL;
1750 		if (ret < 70)
1751 		    timeptr->tm_year = 100 + ret;
1752 		else
1753 		    timeptr->tm_year = ret;
1754 		buf = s;
1755 		break;
1756 	    case 'Y' :
1757 		ret = my_strtoi(buf, &s, 4);
1758 		if (s == buf)
1759 		    return NULL;
1760 		timeptr->tm_year = ret - tm_year_base;
1761 		buf = s;
1762 		break;
1763 	    case '\0' :
1764 		--format;
1765 		/* FALLTHROUGH */
1766 	    case '%' :
1767 		if (*buf == '%')
1768 		    ++buf;
1769 		else
1770 		    return NULL;
1771 		break;
1772 	    default :
1773 		if (*buf == '%' || *++buf == c)
1774 		    ++buf;
1775 		else
1776 		    return NULL;
1777 		break;
1778 	    }
1779 	} else {
1780 	    if (*buf == c)
1781 		++buf;
1782 	    else
1783 		return NULL;
1784 	}
1785     }
1786 
1787     return (char *) buf;
1788 }
1789 
win32_fscan_nonfinite(FILE * fp,int * err)1790 double win32_fscan_nonfinite (FILE *fp, int *err)
1791 {
1792     char test[5] = {0};
1793 
1794     fscanf(fp, "%4s", test);
1795 
1796     if (!g_ascii_strcasecmp(test, "nan") ||
1797 	!g_ascii_strcasecmp(test, "-nan") ||
1798 	!strcmp(test, ".")) {
1799 	return NADBL;
1800     } else if (!g_ascii_strcasecmp(test, "inf")) {
1801 	return 1.0 / 0.0;
1802     } else if (!g_ascii_strcasecmp(test, "-inf")) {
1803 	return -1.0 / 0.0;
1804     } else {
1805 	gretl_errmsg_sprintf(_("got invalid field '%s'"), test);
1806 	*err = E_DATA;
1807 	return 0;
1808     }
1809 }
1810 
win32_sscan_nonfinite(const char * s,int * err)1811 double win32_sscan_nonfinite (const char *s, int *err)
1812 {
1813     char test[5];
1814 
1815     sscanf(s, "%4s", test);
1816 
1817     if (!strncmp(test, "nan", 3) ||
1818 	!strncmp(test, "-nan", 4)) {
1819 	return NADBL;
1820     } else if (!strncmp(test, "inf", 3)) {
1821 	return 1.0 / 0.0;
1822     } else if (!strncmp(test, "-inf", 4)) {
1823 	return -1.0 / 0.0;
1824     } else if (!strncmp(test, "NA", 2)) {
1825 	return NADBL;
1826     } else {
1827 	*err = E_DATA;
1828 	return 0;
1829     }
1830 }
1831 
win32_pprint_nonfinite(PRN * prn,double x,char c)1832 void win32_pprint_nonfinite (PRN *prn, double x, char c)
1833 {
1834     if (isnan(x)) {
1835 	pputs(prn, "nan");
1836     } else if (x < 0) {
1837 	pputs(prn, "-inf");
1838     } else {
1839 	pputs(prn, "inf");
1840     }
1841 
1842     if (c != 0) {
1843 	pputc(prn, c);
1844     }
1845 }
1846 
win32_get_time(void)1847 double win32_get_time (void)
1848 {
1849     static LARGE_INTEGER timer_freq;
1850     LARGE_INTEGER wt;
1851     double xt;
1852 
1853     if (timer_freq.QuadPart == 0) {
1854 	QueryPerformanceFrequency(&timer_freq);
1855     }
1856 
1857     QueryPerformanceCounter(&wt);
1858     xt = wt.QuadPart / (double) timer_freq.QuadPart;
1859 
1860     return xt;
1861 }
1862 
vista_or_higher(void)1863 static int vista_or_higher (void)
1864 {
1865     OSVERSIONINFO osv = {0};
1866     int ret = 0;
1867 
1868     osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1869 
1870     if (GetVersionEx(&osv)) {
1871 	if (osv.dwMajorVersion >= 6) {
1872 	    ret = 1;
1873 	}
1874     }
1875 
1876     return ret;
1877 }
1878 
1879 /* Try to ensure that UTF-8 will be handled correctly in the
1880    Windows console. Return 0 if it looks OK, non-zero if not.
1881 */
1882 
try_for_CP_65001(void)1883 int try_for_CP_65001 (void)
1884 {
1885     int ttfont = 0;
1886     int gotinfo = 0;
1887     HANDLE h;
1888     int h_ok;
1889     int ret = -1;
1890 
1891     h = GetStdHandle(STD_OUTPUT_HANDLE);
1892     h_ok = (h != NULL && h != INVALID_HANDLE_VALUE);
1893 
1894     if (windebug) {
1895 	fprintf(fdb, "STD_OUTPUT_HANDLE: h = %s\n",
1896 		h == NULL ? "NULL" : h == INVALID_HANDLE_VALUE ?
1897 		"INVALID_HANDLE_VALUE" : "OK?");
1898 	fprintf(fdb, "vista or higher ? %s\n", vista_or_higher() ?
1899 		"yes" : "no");
1900     }
1901 
1902     if (h_ok && vista_or_higher()) {
1903 	CONSOLE_FONT_INFOEX finfo = {0};
1904 
1905 	finfo.cbSize = sizeof(CONSOLE_FONT_INFOEX);
1906 	if (GetCurrentConsoleFontEx(h, FALSE, &finfo)) {
1907 	    if (finfo.FontFamily & TMPF_TRUETYPE) {
1908 		if (windebug) {
1909 		    fprintf(fdb, "got ttfont from Family\n");
1910 		}
1911 		ttfont = 1;
1912 	    } else {
1913 		if (windebug) {
1914 		    fprintf(fdb, "got nFont = %d\n", finfo.nFont);
1915 		}
1916 		ttfont = finfo.nFont >= 8;
1917 	    }
1918 	    gotinfo = 1;
1919 	} else if (windebug) {
1920 	    fprintf(fdb, "GetCurrentConsoleFontEx failed\n");
1921 	    win_print_last_error();
1922 	}
1923     }
1924 
1925     if (h_ok && !gotinfo) {
1926 	CONSOLE_FONT_INFO finfo = {0};
1927 
1928 	if (GetCurrentConsoleFont(h, FALSE, &finfo)) {
1929 	    /* a shot in the dark here, based on what I found
1930 	       on Windows 8 at one time */
1931 	    ttfont = finfo.nFont >= 8;
1932 	    if (windebug) {
1933 		fprintf(fdb, "got nFont = %d\n", finfo.nFont);
1934 	    }
1935 	} else if (windebug) {
1936 	    fprintf(fdb, "GetCurrentConsoleFont failed\n");
1937 	    win_print_last_error();
1938 	}
1939     }
1940 
1941     /* The first option below seems to work OK if the Windows console
1942        is using a TrueType font (e.g. Lucida Console or Consolas)
1943     */
1944 
1945     if (ttfont && IsValidCodePage(65001)) {
1946 	SetConsoleOutputCP(65001);
1947 	ret = 0; /* OK signal */
1948 	if (windebug) {
1949 	    fprintf(fdb, "set console to UTF-8\n");
1950 	}
1951     }
1952 
1953     return ret;
1954 }
1955 
windows_is_xp(void)1956 int windows_is_xp (void)
1957 {
1958     OSVERSIONINFO osv = {0};
1959     int ret = 0;
1960 
1961     osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1962 
1963     if (GetVersionEx(&osv)) {
1964 	if (osv.dwMajorVersion == 5 && osv.dwMinorVersion == 1) {
1965 	    ret = 1;
1966 	}
1967     }
1968 
1969     return ret;
1970 }
1971 
1972 typedef BOOL (WINAPI *LPFN_GLPI) (
1973     PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
1974     PDWORD);
1975 
count_set_bits(ULONG_PTR bit_mask)1976 static DWORD count_set_bits (ULONG_PTR bit_mask)
1977 {
1978     DWORD LSHIFT = sizeof(ULONG_PTR)*8 - 1;
1979     DWORD set_count = 0;
1980     ULONG_PTR bit_test = (ULONG_PTR)1 << LSHIFT;
1981     int i;
1982 
1983     for (i=0; i<=LSHIFT; i++) {
1984         set_count += (bit_mask & bit_test)? 1 : 0;
1985         bit_test /= 2;
1986     }
1987 
1988     return set_count;
1989 }
1990 
win32_get_core_count(void)1991 int win32_get_core_count (void)
1992 {
1993     LPFN_GLPI glpi;
1994     PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buf = NULL;
1995     PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
1996     DWORD rc, retlen = 0;
1997     int n_procs = 0;
1998     int n_cores = 0;
1999     int bufsize = 0;
2000     int offset = 0;
2001 
2002     glpi = (LPFN_GLPI) GetProcAddress(GetModuleHandle(TEXT("kernel32")),
2003 				      "GetLogicalProcessorInformation");
2004     if (glpi == NULL) {
2005 	return 0;
2006     }
2007 
2008     /* get required buffer size */
2009     rc = glpi(buf, &retlen);
2010     if (!rc && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
2011 	/* failed */
2012 	return 0;
2013     }
2014 
2015     buf = malloc(retlen);
2016     if (buf != NULL) {
2017 	rc = glpi(buf, &retlen);
2018     }
2019 
2020     if (!rc) {
2021 	/* failed */
2022 	free(buf);
2023 	return 0;
2024     }
2025 
2026     bufsize = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
2027     ptr = buf;
2028 
2029     while (offset + bufsize <= retlen) {
2030         if (ptr->Relationship == RelationProcessorCore) {
2031             n_cores++;
2032             /* hyper-threaded cores supply more than one logical processor */
2033             n_procs += count_set_bits(ptr->ProcessorMask);
2034 	}
2035         offset += bufsize;
2036         ptr++;
2037     }
2038 
2039 #if 0
2040     fprintf(stderr, "\nGetLogicalProcessorInformation results:\n");
2041     fprintf(stderr, " Number of processor cores: %d\n", n_cores);
2042     fprintf(stderr, " Number of logical processors: %d\n", n_procs);
2043 #endif
2044 
2045     free(buf);
2046 
2047     return n_cores;
2048 }
2049