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 #undef CHILD_DEBUG
21 
22 #include "gretl.h"
23 #include "gretlwin32.h"
24 #include "textutil.h"
25 #include "guiprint.h"
26 #include "gpt_control.h"
27 #include "menustate.h"
28 #include "gretl_www.h"
29 
30 #include <dirent.h>
31 
32 #include <pango/pangowin32.h>
33 #include <gdk/gdkwin32.h>
34 
35 /* extra Windows headers */
36 #include <shlobj.h>
37 #include <shellapi.h>
38 #include <shlwapi.h>
39 #include <fcntl.h>
40 
41 #define MAX_CONSOLE_LINES 500
42 
43 #define SET_FONT 0
44 
redirect_io_to_console(void)45 void redirect_io_to_console (void)
46 {
47 #if SET_FONT
48     CONSOLE_FONT_INFOEX cfie = {0};
49     int font_ok;
50 #endif
51     CONSOLE_SCREEN_BUFFER_INFO coninfo;
52     int conhandle;
53     HANDLE stdhandle;
54     FILE *fp;
55 
56     AllocConsole();
57 
58     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
59 			       &coninfo);
60 
61     coninfo.dwSize.Y = MAX_CONSOLE_LINES;
62     SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
63 			       coninfo.dwSize);
64 
65 #if SET_FONT
66     /* try to ensure TrueType font */
67     cfie.cbSize = sizeof(cfie);
68     lstrcpyW(cfie.FaceName, L"Lucida Console"); /* maybe Consolas? */
69 #endif
70 
71     /* redirect unbuffered STDOUT to the console */
72     stdhandle = GetStdHandle(STD_OUTPUT_HANDLE);
73 #if SET_FONT
74     font_ok = SetCurrentConsoleFontEx(stdhandle, 0, &cfie);
75 #endif
76     conhandle = _open_osfhandle((intptr_t) stdhandle, _O_TEXT);
77     fp = _fdopen(conhandle, "w");
78     *stdout = *fp;
79     setvbuf(stdout, NULL, _IONBF, 0);
80 
81     /* redirect unbuffered STDERR to the console */
82     stdhandle = GetStdHandle(STD_ERROR_HANDLE);
83 #if SET_FONT
84     font_ok = SetCurrentConsoleFontEx(stdhandle, 0, &cfie);
85 #endif
86     conhandle = _open_osfhandle((intptr_t) stdhandle, _O_TEXT);
87     fp = _fdopen(conhandle, "w");
88     *stderr = *fp;
89     setvbuf(stderr, NULL, _IONBF, 0);
90 
91     if (IsValidCodePage(65001) && SetConsoleOutputCP(65001)) {
92 	; /* OK */
93     }
94 }
95 
96 /* Asynchronous execution of child process. We'll be ready
97    to re-encode @arg if necessary, but in some cases (see
98    the calls below) that will already be handled. This is
99    flagged by the fact that the recoded argument is already
100    composited into @prog, and the @prog string will start
101    with a double quote.
102 */
103 
real_create_child_process(const char * prog,const char * arg,const char * opts,int showerr)104 static int real_create_child_process (const char *prog,
105 				      const char *arg,
106 				      const char *opts,
107 				      int showerr)
108 {
109     PROCESS_INFORMATION proc_info;
110     STARTUPINFO start_info;
111     gchar *cmdline = NULL;
112     gchar *pconv = NULL;
113     gchar *aconv = NULL;
114     int ret, err = 0;
115 
116     /* We assume that @opts will not need recoding */
117 
118     err = ensure_locale_encoding(&prog, &pconv, &arg, &aconv);
119     if (err) {
120 	return err;
121     }
122 
123     if (arg == NULL && opts == NULL) {
124 	if (*prog == '"') {
125 	    /* already wrapped in quotes */
126 	    cmdline = g_strdup(prog);
127 	} else {
128 	    cmdline = g_strdup_printf("\"%s\"", prog);
129 	}
130     } else if (arg != NULL && opts != NULL) {
131 	cmdline = g_strdup_printf("\"%s\" %s \"%s\"", prog, opts, arg);
132     } else if (arg != NULL) {
133 	cmdline = g_strdup_printf("\"%s\" \"%s\"", prog, arg);
134     } else {
135 	cmdline = g_strdup_printf("\"%s\" %s", prog, opts);
136     }
137 
138     ZeroMemory(&proc_info, sizeof proc_info);
139     ZeroMemory(&start_info, sizeof start_info);
140     start_info.cb = sizeof start_info;
141 
142     ret = CreateProcess(NULL,
143 			cmdline,       /* command line */
144 			NULL,          /* process security attributes  */
145 			NULL,          /* primary thread security attributes */
146 			FALSE,         /* handles are inherited?  */
147 			0,             /* creation flags  */
148 			NULL,          /* NULL => use parent's environment  */
149 			NULL,          /* use parent's current directory  */
150 			&start_info,   /* receives STARTUPINFO */
151 			&proc_info);   /* receives PROCESS_INFORMATION  */
152 
153     if (ret == 0) {
154 	if (showerr) {
155 	    win_show_last_error();
156 	}
157 	err = 1;
158     }
159 
160 #ifdef CHILD_DEBUG
161     if (err) {
162 	fprintf(stderr, "gretl: create_child_process():\n"
163 		" cmdline='%s'\n\n", cmdline);
164 	fprintf(stderr, " return from CreateProcess() = %d\n", ret);
165     }
166 #endif
167 
168     g_free(cmdline);
169     g_free(pconv);
170     g_free(aconv);
171 
172     return err;
173 }
174 
win32_run_async(const char * prog,const char * arg)175 int win32_run_async (const char *prog, const char *arg)
176 {
177     return real_create_child_process(prog, arg, NULL, 1);
178 }
179 
180 /* try registry for path to Rgui.exe */
181 
Rgui_path_from_registry(void)182 static int Rgui_path_from_registry (void)
183 {
184     char tmp[MAX_PATH];
185     int err;
186 
187     err = R_path_from_registry(tmp, RGUI);
188 
189     if (!err) {
190 	*Rcommand = '\0';
191 	strncat(Rcommand, tmp, MAXSTR - 1);
192     }
193 
194     return err;
195 }
196 
197 /* start R in asynchronous (interactive) mode */
198 
win32_start_R_async(void)199 void win32_start_R_async (void)
200 {
201     const char *opts = "--no-init-file --no-restore-data";
202     int err;
203 
204     err = real_create_child_process(Rcommand, NULL, opts, 0);
205 
206     if (err) {
207 	/* The default Rcommand (plain "Rgui.exe"), or the value
208 	   entered by the user via the GUI, didn't work, so we try
209 	   figuring this out from the registry.
210 	*/
211 	err = Rgui_path_from_registry();
212 	if (!err) {
213 	    real_create_child_process(Rcommand, NULL, opts, 1);
214 	} else {
215 	    gui_errmsg(E_EXTERNAL);
216 	}
217     }
218 }
219 
dummy_output_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)220 static void dummy_output_handler (const gchar *log_domain,
221 				  GLogLevelFlags log_level,
222 				  const gchar *message,
223 				  gpointer user_data)
224 {
225     return;
226 }
227 
stderr_output_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)228 static void stderr_output_handler (const gchar *log_domain,
229 				   GLogLevelFlags log_level,
230 				   const gchar *message,
231 				   gpointer user_data)
232 {
233     fprintf(stderr, "%s : %s\n", log_domain, message);
234 }
235 
set_g_logging(int debug)236 static void set_g_logging (int debug)
237 {
238     void (*handler) (const gchar *, GLogLevelFlags,
239 		     const gchar *, gpointer);
240      GLogLevelFlags flags = G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
241 	 G_LOG_LEVEL_DEBUG;
242 
243     if (debug) {
244 	handler = stderr_output_handler;
245     } else {
246 	handler = dummy_output_handler;
247     }
248 
249     g_log_set_handler("Gtk", flags, (GLogFunc) handler, NULL);
250     g_log_set_handler("Gdk", flags, (GLogFunc) handler, NULL);
251     g_log_set_handler("GLib", flags, (GLogFunc) handler, NULL);
252     g_log_set_handler("Pango", flags, (GLogFunc) handler, NULL);
253     g_log_set_handler("GtkSourceView", flags, (GLogFunc) handler, NULL);
254 }
255 
get_default_windows_app_font(char * target)256 void get_default_windows_app_font (char *target)
257 {
258     NONCLIENTMETRICS ncm;
259 
260     memset(&ncm, 0, sizeof ncm);
261     ncm.cbSize = sizeof ncm;
262 
263     if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0)) {
264 	HDC screen = GetDC(0);
265 	double y_scale = 72.0 / GetDeviceCaps(screen, LOGPIXELSY);
266 	int point_size = (int) (ncm.lfMenuFont.lfHeight * y_scale);
267 
268 	if (point_size < 0) {
269 	    point_size = -point_size;
270 	}
271 	sprintf(target, "%s %d", ncm.lfMenuFont.lfFaceName, point_size);
272 	ReleaseDC(0, screen);
273     } else {
274 	/* fallback */
275 	strcpy(target, "tahoma 8");
276     }
277 }
278 
279 static char *winlocale;
280 
record_win32_locale(char * s)281 void record_win32_locale (char *s)
282 {
283     if (s != NULL) {
284 	winlocale = gretl_strdup(s);
285     }
286 }
287 
gretl_win32_debug_init(int debug)288 void gretl_win32_debug_init (int debug)
289 {
290     if (debug) {
291 	char *s = getenv("OSTYPE");
292 	const char *charset = NULL;
293 	gchar *pkgdir;
294 
295 	if (s == NULL || strcmp(s, "msys")) {
296 	    /* This doesn't work if gretl is launched
297 	       from an MSYS2 terminal window, and in that
298 	       context we should have OSTYPE=msys in the
299 	       environment.
300 	    */
301 	    redirect_io_to_console();
302 	}
303 	set_windebug(1);
304 	fprintf(stderr, "Windows locale = %s\n",
305 		winlocale == NULL ? "NULL" : winlocale);
306 	g_get_charset(&charset);
307 	fprintf(stderr, "charset = %s\n", charset);
308 	pkgdir = g_win32_get_package_installation_directory_of_module(NULL);
309 	fprintf(stderr, "pkgdir = '%s'\n", pkgdir);
310 	g_free(pkgdir);
311     }
312 
313     set_g_logging(debug);
314 }
315 
316 /* Carry out some Windows-specific start-up tasks, and
317    call read_rc to get the per-user configuration info.
318 */
319 
gretl_win32_init(int debug)320 void gretl_win32_init (int debug)
321 {
322     char tmp[4] = {0};
323 
324 #if GTK_MAJOR_VERSION < 3
325     read_reg_val(HKEY_CURRENT_USER,
326 		 "Microsoft\\Windows\\CurrentVersion\\ThemeManager",
327 		 "ThemeActive", tmp);
328     set_wimp_preferred(strcmp(tmp, "1") == 0);
329 #endif
330 
331     read_win32_config(debug);
332     set_gretl_startdir();
333 }
334 
prn_to_clipboard(PRN * prn,int fmt)335 int prn_to_clipboard (PRN *prn, int fmt)
336 {
337     char *buf = gretl_print_steal_buffer(prn);
338     char *modbuf = NULL;
339     int rtf_format = 0;
340     int err = 0;
341 
342     if (buf == NULL || *buf == '\0') {
343 	errbox(_("Copy buffer was empty"));
344 	return 0;
345     }
346 
347     if (!OpenClipboard(NULL)) {
348 	errbox(_("Cannot open the clipboard"));
349 	return 1;
350     }
351 
352     if (fmt == GRETL_FORMAT_RTF || fmt == GRETL_FORMAT_RTF_TXT) {
353 	rtf_format = 1;
354     }
355 
356     EmptyClipboard();
357 
358     err = maybe_post_process_buffer(buf, fmt, W_COPY, &modbuf);
359 
360     if (!err) {
361 	HGLOBAL winclip;
362 	LPTSTR ptr;
363 	unsigned clip_format;
364 	gunichar2 *ubuf = NULL;
365 	char *winbuf;
366 	glong wrote = 0;
367 	size_t sz;
368 
369 	winbuf = modbuf != NULL ? modbuf : buf;
370 
371 	if (!rtf_format && !gretl_is_ascii(winbuf)) {
372 	    /* for Windows clipboard, recode UTF-8 to UTF-16 */
373 	    ubuf = g_utf8_to_utf16(winbuf, -1, NULL, &wrote, NULL);
374 	}
375 
376 	if (ubuf != NULL) {
377 	    sz = (wrote + 1) * sizeof(gunichar2);
378 	} else {
379 	    sz = strlen(winbuf) + 1;
380 	}
381 
382 	winclip = GlobalAlloc(GMEM_MOVEABLE, sz);
383 	ptr = GlobalLock(winclip);
384 	if (ubuf != NULL) {
385 	    memcpy(ptr, ubuf, sz);
386 	} else {
387 	    memcpy(ptr, winbuf, sz);
388 	}
389 	GlobalUnlock(winclip);
390 
391 	if (ubuf != NULL) {
392 	    clip_format = CF_UNICODETEXT;
393 	} else if (rtf_format) {
394 	    clip_format = RegisterClipboardFormat("Rich Text Format");
395 	} else if (fmt == GRETL_FORMAT_CSV) {
396 	    clip_format = RegisterClipboardFormat("CSV");
397 	} else {
398 	    clip_format = CF_TEXT;
399 	}
400 
401 	SetClipboardData(clip_format, winclip);
402 
403 	if (ubuf != NULL) {
404 	    g_free(ubuf);
405 	}
406     }
407 
408     CloseClipboard();
409 
410     free(buf);
411     free(modbuf);
412 
413     return err;
414 }
415 
416 /* copy plot to clipboard by generating an EMF file (enhanced
417    metafile), reading it into a buffer, and putting it on the
418    clipboard.
419 */
420 
emf_to_clipboard(char * emfname)421 int emf_to_clipboard (char *emfname)
422 {
423     HWND mainw;
424     HENHMETAFILE hemfclip;
425     HENHMETAFILE hemf = NULL;
426     HANDLE htest;
427     gchar *fconv = NULL;
428 
429 #if GTK_MAJOR_VERSION > 2
430     mainw = GDK_WINDOW_HWND(gtk_widget_get_window(mdata->main));
431 #else
432     mainw = GDK_WINDOW_HWND(mdata->main->window);
433 #endif
434     if (mainw == NULL) {
435 	errbox("Got NULL HWND");
436 	return 1;
437     }
438 
439     if (!OpenClipboard(mainw)) {
440 	errbox(_("Cannot open the clipboard"));
441 	return 1;
442     }
443 
444     EmptyClipboard();
445 
446     if (utf8_encoded(emfname)) {
447 	gunichar2 *fconv;
448 
449 	fconv = g_utf8_to_utf16(emfname, -1, NULL, NULL, NULL);
450 	if (fconv != NULL) {
451 	    hemf = GetEnhMetaFileW(fconv);
452 	    g_free(fconv);
453 	}
454     } else {
455 	hemf = GetEnhMetaFile(emfname);
456     }
457 
458     if (hemf == NULL) {
459 	errbox("Couldn't get handle to graphic metafile");
460 	return 1;
461     }
462 
463     hemfclip = CopyEnhMetaFile(hemf, NULL);
464     if (hemfclip == NULL) {
465 	errbox("Couldn't copy graphic metafile");
466 	return 1;
467     }
468 
469     htest = SetClipboardData(CF_ENHMETAFILE, hemfclip);
470     if (htest == NULL) {
471 	errbox("Failed to put data on clipboard");
472 	return 1;
473     }
474 
475     CloseClipboard();
476     DeleteEnhMetaFile(hemf);
477 
478     return 0;
479 }
480 
481 #if 0 /* unused, but may have use again */
482 
483 static long get_reg_key (HKEY key, char *subkey, char *retdata)
484 {
485     long err;
486     HKEY hkey;
487 
488     err = RegOpenKeyEx(key, subkey, 0, KEY_QUERY_VALUE, &hkey);
489 
490     if (err == ERROR_SUCCESS) {
491 	long datasize = MAX_PATH;
492 	char data[MAX_PATH];
493 
494 	RegQueryValue(hkey, NULL, (LPSTR) data, &datasize);
495 
496 	lstrcpy(retdata, data);
497 	RegCloseKey(hkey);
498     }
499 
500     return err;
501 }
502 
503 #endif
504 
505 static int dde_open_pdf (const char *exename,
506 			 const char *fname,
507 			 const char *dest);
508 
get_exe_for_type(const char * ext)509 static char *get_exe_for_type (const char *ext)
510 {
511     char *exe = NULL;
512     HRESULT ret;
513     DWORD len = 0;
514 
515     ret = AssocQueryString(ASSOCF_NOTRUNCATE, ASSOCSTR_EXECUTABLE,
516 			   ext, NULL, NULL, &len);
517     if (ret == S_FALSE) {
518 	exe = calloc(len + 1, 1);
519 	ret = AssocQueryString(0, ASSOCSTR_EXECUTABLE,
520 			       ext, NULL, exe, &len);
521 	if (ret != S_OK) {
522 	    fprintf(stderr, "couldn't determine exe for type %s\n", ext);
523 	    free(exe);
524 	    exe = NULL;
525 	}
526     }
527 
528     return exe;
529 }
530 
get_pdf_service_name(char * service,const char * exe)531 static int get_pdf_service_name (char *service, const char *exe)
532 {
533     const char *s;
534     char ver[4] = {0};
535     int n, err = 0;
536 
537     *service = '\0';
538 
539     /* cases include:
540          $pf\Adobe\Acrobat Reader DC\...
541          $pf\Adobe\Acrobat 10.0\...
542     */
543 
544     s = strstr(exe, "obe\\Acrobat");
545     if (s == NULL) {
546 	/* huh? */
547 	return 1;
548     }
549 
550     /* get in place to read "Reader <version>" or
551        just "<version>" for Acrobat */
552     s += 11;
553     s += strspn(s, " ");
554 
555     if (!strncmp(s, "Reader ", 7)) {
556 	s += 7;
557 	if (sscanf(s, "%3[^\\]", ver) == 1) {
558 	    if (!strcmp(ver, "DC")) {
559 		strcpy(service, "AcroViewR15");
560 	    } else if (isdigit(*ver)) {
561 		n = atoi(ver);
562 		if (n >= 10) {
563 		    sprintf(service, "AcroViewR%d", n);
564 		} else {
565 		    strcpy(service, "AcroView");
566 		}
567 	    } else if (*ver == 'X') {
568 		if (ver[1] == 'I') {
569 		    strcpy(service, "AcroViewR11");
570 		} else {
571 		    strcpy(service, "AcroViewR10");
572 		}
573 	    }
574 	}
575     } else {
576 	/* Acrobat as such */
577 	if (sscanf(s, "%3[^\\]", ver) == 1) {
578 	    if (!strcmp(ver, "DC")) {
579 		strcpy(service, "AcroViewA15");
580 	    } else if (*ver == 'X') {
581 		if (ver[1] == 'I') {
582 		    strcpy(service, "AcroViewA11");
583 		} else {
584 		    strcpy(service, "AcroViewA10");
585 		}
586 	    } else if (isdigit(*ver)) {
587 		n = atoi(ver);
588 		if (n >= 10) {
589 		    sprintf(service, "AcroViewA%d", n);
590 		} else {
591 		    strcpy(service, "AcroView");
592 		}
593 	    }
594 	}
595     }
596 
597     if (*service == '\0') {
598 	fprintf(stderr, "pdf_service_name: no service!\n");
599 	err = 1;
600     }
601 
602     return err;
603 }
604 
605 static int coinitted;
606 
607 /* If and when win32_open_arg() gets called, @arg should
608    already be re-encoded to the locale if necessary.
609 */
610 
win32_open_arg(const char * arg,char * ext)611 static int win32_open_arg (const char *arg, char *ext)
612 {
613     int err = 0;
614 
615     if (!coinitted) {
616 	CoInitialize(NULL);
617 	coinitted = 1;
618     }
619 
620     /* From the MSDN doc: "If the function succeeds, it returns a
621        value greater than 32. If the function fails, it returns an
622        error value that indicates the cause of the failure. The return
623        value is cast as an HINSTANCE for backward compatibility with
624        16-bit Windows applications. It is not a true HINSTANCE,
625        however. It can be cast only to an int and compared to either
626        32 or the following error codes below..."
627     */
628 
629     if ((int) ShellExecute(NULL, "open", arg, NULL, NULL, SW_SHOW) <= 32) {
630 	/* if the above fails, try via the registry */
631 	char *exe = get_exe_for_type(ext);
632 
633 	if (exe == NULL) {
634 	    err = 1;
635 	} else {
636 	    gchar *cmd = g_strdup_printf("\"%s\" \"%s\"", exe, arg);
637 
638 	    err = real_create_child_process(cmd, NULL, NULL, 1);
639 	    g_free(cmd);
640 	    free(exe);
641 	}
642     }
643 
644     return err;
645 }
646 
win32_open_pdf(const char * fname,const char * dest)647 int win32_open_pdf (const char *fname, const char *dest)
648 {
649     char *exe = get_exe_for_type(".pdf");
650     gchar *fconv = NULL;
651     gchar *cmd = NULL;
652     int err = 0;
653 
654     if (utf8_encoded(fname)) {
655 	/* note: @exe will be in the locale already */
656 	fconv = g_win32_locale_filename_from_utf8(fname);
657 	if (fconv != NULL) {
658 	    fname = (const char *) fconv;
659 	}
660     }
661 
662     if (exe != NULL && strstr(exe, "Acro") != NULL) {
663 	/* give DDE a whirl */
664 	err = dde_open_pdf(exe, fname, dest);
665 	if (err) {
666 	    /* but if that fails, try something a bit
667 	       less ambitious */
668 	    err = 0;
669 	    cmd = g_strdup_printf("\"%s\" /A \"nameddest=%s\" \"%s\"",
670 				  exe, dest, fname);
671 	    err = real_create_child_process(cmd, NULL, NULL, 1);
672 	}
673     } else if (exe != NULL && strstr(exe, "umatra") != NULL) {
674 	cmd = g_strdup_printf("\"%s\" -named-dest %s \"%s\"",
675 			      exe, dest, fname);
676 	err = real_create_child_process(cmd, NULL, NULL, 1);
677     } else {
678 	err = win32_open_arg(fname, ".pdf");
679     }
680 
681     free(exe);
682     g_free(fconv);
683     g_free(cmd);
684 
685     return err;
686 }
687 
browser_open(const char * url)688 int browser_open (const char *url)
689 {
690     char sfx[5] = ".htm";
691 
692     return win32_open_arg(url, sfx);
693 }
694 
win32_open_file(const char * fname)695 int win32_open_file (const char *fname)
696 {
697     char sfx[5];
698     int err = 0;
699 
700     if (has_suffix(fname, ".pdf")) {
701 	strcpy(sfx, ".pdf");
702     } else if (has_suffix(fname, ".ps") ||
703 	       has_suffix(fname, ".eps")) {
704 	strcpy(sfx, ".ps");
705     } else {
706 	*sfx = '\0';
707     }
708 
709     if (utf8_encoded(fname)) {
710 	gchar *fconv = g_win32_locale_filename_from_utf8(fname);
711 
712 	if (fconv != NULL) {
713 	    err = win32_open_arg(fconv, sfx);
714 	    g_free(fconv);
715 	} else {
716 	    err = E_FOPEN;
717 	}
718     } else {
719 	err = win32_open_arg(fname, sfx);
720     }
721 
722     return err;
723 }
724 
win32_rename_dir(const char * oldname,const char * newname)725 int win32_rename_dir (const char *oldname, const char *newname)
726 {
727     char *oldtmp = NULL, *newtmp = NULL;
728     int len, err;
729 
730     /* trim trailing slash for non-root dirs */
731 
732     len = strlen(oldname);
733     if (len > 1 && oldname[len-1] == '\\' && oldname[len-2] != ':') {
734 	oldtmp = gretl_strndup(oldname, len - 1);
735 	oldname = oldtmp;
736     }
737 
738     len = strlen(newname);
739     if (len > 1 && newname[len-1] == '\\' && newname[len-2] != ':') {
740 	newtmp = gretl_strndup(newname, len - 1);
741 	newname = newtmp;
742     }
743 
744     err = gretl_rename(oldname, newname);
745 
746     if (oldtmp != NULL || newtmp != NULL) {
747 	free(oldtmp);
748 	free(newtmp);
749     }
750 
751     return err;
752 }
753 
754 /* experimental: use DDE */
755 
756 #include <ddeml.h>
757 #include <dde.h>
758 #include <malloc.h>
759 
760 #define CONNECT_DELAY            500 /* ms */
761 #define TRANSACTION_TIMEOUT     5000 /* ms */
762 #define MAX_INPUT_IDLE_WAIT INFINITE /* ms */
763 
init_callback(UINT uType,UINT uFmt,HCONV hconv,HSZ hsz1,HSZ hsz2,HDDEDATA hdata,DWORD dwData1,DWORD dwData2)764 HDDEDATA CALLBACK init_callback (UINT uType, UINT uFmt, HCONV hconv,
765 				 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
766 				 DWORD dwData1, DWORD dwData2)
767 {
768     if (uType == XTYP_ADVDATA) {
769 	DWORD len = DdeGetData(hdata, NULL, 0, 0);
770 	char *buf = (char *)_alloca(len + 1);
771 
772 	DdeGetData(hdata, (LPBYTE) buf, len + 1, 0);
773 	return (HDDEDATA) DDE_FACK;
774     }
775 
776     return (HDDEDATA) NULL;
777 }
778 
start_dde_server(LPCTSTR prog)779 static int start_dde_server (LPCTSTR prog)
780 {
781     PROCESS_INFORMATION pi;
782     STARTUPINFO si;
783 
784     ZeroMemory(&si, sizeof si);
785     si.cb = sizeof si;
786 
787     if (!CreateProcess(NULL, (LPTSTR) prog, NULL, NULL, FALSE, 0,
788 		       NULL, NULL, &si, &pi)) {
789 	fprintf(stderr, "DDE: couldn't start process %s\n", prog);
790 	return 1;
791     }
792 
793     WaitForInputIdle(pi.hProcess, MAX_INPUT_IDLE_WAIT);
794 
795     CloseHandle(pi.hThread);
796     CloseHandle(pi.hProcess);
797 
798     return 0;
799 }
800 
open_dde_conversation(LPCTSTR topic_name,const char * exename,const char * ddename,HCONV * convp)801 static DWORD open_dde_conversation (LPCTSTR topic_name,
802 				    const char *exename,
803 				    const char *ddename,
804 				    HCONV *convp)
805 {
806     DWORD session = 0;
807     HCONV conversation = NULL;
808     HSZ service, topic;
809     UINT ret;
810     int i, err = 0;
811 
812     ret = DdeInitialize(&session, (PFNCALLBACK) init_callback,
813 			APPCLASS_STANDARD | APPCMD_CLIENTONLY, 0);
814 
815     if (ret != DMLERR_NO_ERROR) {
816 	fprintf(stderr, "DDE: couldn't initialize session\n");
817 	return 0;
818     }
819 
820     service = DdeCreateStringHandle(session, ddename, CP_WINANSI);
821     topic   = DdeCreateStringHandle(session, topic_name, CP_WINANSI);
822 
823     if (!service || !topic) {
824 	fprintf(stderr, "DDE: string creation failed\n");
825 	DdeUninitialize(session);
826 	return 0;
827     }
828 
829     conversation = DdeConnect(session, service, topic, 0);
830 
831     if (conversation == NULL) {
832 	err = start_dde_server(exename);
833 	if (!err) {
834 	    /* try to connect */
835 	    for (i=0; i<5 && !conversation; i++) {
836 		Sleep(CONNECT_DELAY);
837 		conversation = DdeConnect(session, service, topic, 0);
838 	    }
839 	}
840 	if (conversation == NULL && !err) {
841 	    fprintf(stderr, "DDE: couldn't contact server %s\n", ddename);
842 	    err = 1;
843 	}
844     }
845 
846     DdeFreeStringHandle(session, service);
847     DdeFreeStringHandle(session, topic);
848 
849     if (err) {
850 	DdeUninitialize(session);
851 	session = 0;
852     } else {
853 	*convp = conversation;
854     }
855 
856     return session;
857 }
858 
exec_dde_command(const char * buf,HCONV conversation,DWORD session)859 static int exec_dde_command (const char *buf, HCONV conversation,
860 			     DWORD session)
861 {
862     HDDEDATA ret;
863     int err;
864 
865     ret = DdeClientTransaction((LPBYTE) buf, strlen(buf) + 1,
866 			       conversation, 0, 0, XTYP_EXECUTE,
867 			       TRANSACTION_TIMEOUT, 0);
868 
869     /* MSDN: "The return value is zero for all unsuccessful
870        transactions"
871     */
872     err = (ret == 0);
873 
874     return err;
875 }
876 
dde_open_pdf(const char * exename,const char * fname,const char * dest)877 static int dde_open_pdf (const char *exename,
878 			 const char *fname,
879 			 const char *dest)
880 {
881     DWORD session = 0;
882     HCONV conversation = NULL;
883     char ddename[32];
884     char *buf = NULL;
885     int err = 0;
886 
887     /* Try to figure out the name of the DDE service
888        provided by Acrobat Reader or Acrobat (oh, Adobe!)
889     */
890     err = get_pdf_service_name(ddename, exename);
891     if (err) {
892 	return err;
893     }
894 
895     buf = calloc(strlen(fname) + strlen(dest) + 32, 1);
896 
897     session = open_dde_conversation("control", exename, ddename,
898 				    &conversation);
899     if (session == 0) {
900 	free(buf);
901 	return 1;
902     }
903 
904     /* Adobe DDE commands only work on documents opened
905        by DDE. It's therefore necessary to close the document
906        first (if it's already open) then reopen it.
907     */
908 
909     sprintf(buf, "[DocClose(\"%s\")]", fname);
910     exec_dde_command(buf, conversation, session);
911     sprintf(buf, "[DocOpen(\"%s\")]", fname);
912     exec_dde_command(buf, conversation, session);
913     if (strstr(ddename, "wR") == NULL) {
914 	/* specific to acrord32 version 8 bug */
915 	sprintf(buf, "[DocOpen(\"%s\")]", fname);
916 	exec_dde_command(buf, conversation, session);
917     }
918     sprintf(buf, "[FileOpen(\"%s\")]", fname);
919     exec_dde_command(buf, conversation, session);
920 
921     sprintf(buf, "[DocGoToNameDest(\"%s\", %s)]", fname, dest);
922     err = exec_dde_command(buf, conversation, session);
923 
924     free(buf);
925 
926     if (conversation) {
927 	DdeDisconnect(conversation);
928     }
929     if (session) {
930 	DdeUninitialize(session);
931     }
932 
933     return err;
934 }
935