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 ®key /* 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