PageRenderTime 62ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/src/xyzzycli.cc

https://github.com/snmsts/xyzzy
C++ | 533 lines | 521 code | 12 blank | 0 comment | 16 complexity | fa0e19b1241303f11b9212343b52006c MD5 | raw file
Possible License(s): BSD-3-Clause
  1. #include <windows.h>
  2. #include <malloc.h>
  3. #include "xyzzycli.h"
  4. #include "listen.h"
  5. #pragma data_seg (".text")
  6. #define PACK_VERSION(MAJ, MIN) MAKELONG ((MIN), (MAJ))
  7. #ifndef SPI_GETFOREGROUNDLOCKTIMEOUT
  8. #define SPI_GETFOREGROUNDLOCKTIMEOUT 0x2000
  9. #define SPI_SETFOREGROUNDLOCKTIMEOUT 0x2001
  10. #endif
  11. void
  12. ForceSetForegroundWindow (HWND hwnd)
  13. {
  14. OSVERSIONINFO os;
  15. os.dwOSVersionInfoSize = sizeof os;
  16. GetVersionEx (&os);
  17. DWORD timeout;
  18. if (PACK_VERSION (os.dwMajorVersion, os.dwMinorVersion) >= PACK_VERSION (4, 10)
  19. && SystemParametersInfo (SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &timeout, 0))
  20. {
  21. SystemParametersInfo (SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, 0);
  22. SetForegroundWindow (hwnd);
  23. SystemParametersInfo (SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (void *)timeout, 0);
  24. }
  25. else
  26. SetForegroundWindow (hwnd);
  27. }
  28. static int
  29. error (int id)
  30. {
  31. char buf[256];
  32. LoadString (GetModuleHandle (0), id, buf, sizeof buf);
  33. MessageBox (0, buf, 0, MB_SYSTEMMODAL | MB_ICONHAND);
  34. return 2;
  35. }
  36. static char *
  37. stpcpy (char *d, const char *s)
  38. {
  39. while (*d++ = *s++)
  40. ;
  41. return d - 1;
  42. }
  43. static char *
  44. store (char *d, const char *s)
  45. {
  46. *d++ = '"';
  47. while (*s)
  48. {
  49. if (IsDBCSLeadByte (*s) && s[1])
  50. {
  51. *d++ = *s++;
  52. *d++ = *s++;
  53. }
  54. else if (*s == '\\' || *s == '"')
  55. {
  56. *d++ = '\\';
  57. *d++ = *s++;
  58. }
  59. else
  60. *d++ = *s++;
  61. }
  62. *d++ = '"';
  63. return d;
  64. }
  65. class xyzzysrv
  66. {
  67. HANDLE m_hmap;
  68. void *m_base;
  69. public:
  70. xyzzysrv () : m_hmap (0), m_base (0) {}
  71. ~xyzzysrv ()
  72. {
  73. if (m_base)
  74. UnmapViewOfFile (m_base);
  75. if (m_hmap)
  76. CloseHandle (m_hmap);
  77. }
  78. int alloc (int size)
  79. {
  80. size += sizeof (xyzzysrv_param);
  81. m_hmap = CreateFileMapping (HANDLE (-1), 0, PAGE_READWRITE, 0, size, 0);
  82. if (!m_hmap)
  83. return 0;
  84. m_base = MapViewOfFile (m_hmap, FILE_MAP_WRITE, 0, 0, 0);
  85. if (!m_base)
  86. return 0;
  87. param ()->size = size;
  88. param ()->pid = 0;
  89. param ()->hevent = 0;
  90. param ()->hwnd = 0;
  91. param ()->kill_ok = 0;
  92. return 1;
  93. }
  94. xyzzysrv_param *param () const {return (xyzzysrv_param *)m_base;}
  95. char *data () const {return param ()->data;}
  96. HANDLE handle () const {return m_hmap;}
  97. };
  98. static int
  99. create_sexp (xyzzysrv &sv, int ac, char **av)
  100. {
  101. char curdir[MAX_PATH + 1];
  102. GetCurrentDirectory (sizeof curdir, curdir);
  103. int l = 256 + lstrlen (curdir) * 2;
  104. for (int i = 0; i < ac; i++)
  105. l += lstrlen (av[i]) * 2 + 3;
  106. if (!sv.alloc (l))
  107. return 0;
  108. char *d = stpcpy (sv.data (), "(ed::*xyzzycli-helper ");
  109. d = store (d, curdir);
  110. *d++ = ' ';
  111. *d++ = '\'';
  112. *d++ = '(';
  113. for (int i = 0; i < ac; i++)
  114. d = store (d, av[i]);
  115. *d++ = ')';
  116. *d++ = ')';
  117. *d = 0;
  118. return 1;
  119. }
  120. static HANDLE
  121. dup_handle (HANDLE hsrc, DWORD pid)
  122. {
  123. HANDLE hproc = OpenProcess (PROCESS_DUP_HANDLE, 0, pid);
  124. if (!hproc)
  125. return 0;
  126. HANDLE hdst;
  127. if (!DuplicateHandle (hproc, hsrc, GetCurrentProcess (), &hdst,
  128. 0, 0, DUPLICATE_SAME_ACCESS))
  129. hdst = 0;
  130. CloseHandle (hproc);
  131. return hdst;
  132. }
  133. struct lookup_server
  134. {
  135. HWND hwnd;
  136. HANDLE hevent;
  137. };
  138. static BOOL CALLBACK
  139. enum_proc (HWND hwnd, LPARAM param)
  140. {
  141. lookup_server *ls = (lookup_server *)param;
  142. HANDLE h = GetProp (hwnd, xyzzysrv_name);
  143. if (!h)
  144. return 1;
  145. DWORD pid = 0;
  146. GetWindowThreadProcessId (hwnd, &pid);
  147. if (!pid)
  148. return 1;
  149. ls->hevent = dup_handle (h, pid);
  150. if (!ls->hevent)
  151. return 1;
  152. ls->hwnd = hwnd;
  153. return 0;
  154. }
  155. static HWND
  156. find_server (lookup_server &ls)
  157. {
  158. ls.hwnd = 0;
  159. ls.hevent = 0;
  160. EnumWindows (enum_proc, LPARAM (&ls));
  161. return ls.hwnd;
  162. }
  163. static int
  164. run_xyzzy (int argc, char **argv, const char *xyzzy)
  165. {
  166. int l = lstrlen (xyzzy) + 1;
  167. for (int i = 1; i < argc; l += lstrlen (argv[i]) + 1, i++)
  168. ;
  169. char *const cl = (char *)_alloca (l);
  170. char *p = stpcpy (cl, xyzzy);
  171. for (int i = 1; i < argc; i++)
  172. {
  173. *p++ = ' ';
  174. p = stpcpy (p, argv[i]);
  175. }
  176. PROCESS_INFORMATION pi;
  177. STARTUPINFO si;
  178. memset (&si, 0, sizeof si);
  179. si.cb = sizeof si;
  180. if (!CreateProcess (0, cl, 0, 0, 0, 0, 0, 0, &si, &pi))
  181. return 0;
  182. WaitForInputIdle (pi.hProcess, 60000);
  183. CloseHandle (pi.hProcess);
  184. CloseHandle (pi.hThread);
  185. return 1;
  186. }
  187. static void
  188. wait_term (xyzzysrv &sv)
  189. {
  190. if (!sv.param ()->hwnd)
  191. return;
  192. HANDLE hevent = dup_handle (sv.param ()->hevent, sv.param ()->pid);
  193. if (hevent)
  194. {
  195. WaitForSingleObject (hevent, INFINITE);
  196. CloseHandle (hevent);
  197. }
  198. }
  199. static int
  200. skip_args (int argc, char **argv)
  201. {
  202. int ac;
  203. for (ac = 1; ac < argc - 1; ac += 2)
  204. if (lstrcmp (argv[ac], "-image")
  205. && lstrcmp (argv[ac], "-config")
  206. && lstrcmp (argv[ac], "-ini"))
  207. break;
  208. if (ac < argc && (!lstrcmp (argv[ac], "-q")
  209. || !lstrcmp (argv[ac], "-no-init-file")))
  210. ac++;
  211. return ac;
  212. }
  213. class synchronize
  214. {
  215. HANDLE h;
  216. public:
  217. synchronize (const char *name)
  218. {
  219. h = CreateMutex (0, 1, name);
  220. if (h && GetLastError () == ERROR_ALREADY_EXISTS)
  221. WaitForSingleObject (h, INFINITE);
  222. }
  223. ~synchronize ()
  224. {
  225. if (h)
  226. {
  227. ReleaseMutex (h);
  228. CloseHandle (h);
  229. }
  230. }
  231. };
  232. static int
  233. xmain (int argc, char **argv, const char *xyzzy, int multi_instance)
  234. {
  235. MSG msg;
  236. PostQuitMessage (0);
  237. GetMessage (&msg, 0, 0, 0);
  238. int ac = skip_args (argc, argv);
  239. xyzzysrv sv;
  240. if (!create_sexp (sv, argc - ac, argv + ac))
  241. return error (IDS_NO_MEMORY);
  242. lookup_server ls;
  243. {
  244. synchronize sync ("{FDFB3F8E-65AC-11D4-ADA0-0040053444B8}");
  245. if (multi_instance || !find_server (ls))
  246. {
  247. sv.param ()->kill_ok = 1;
  248. if (!run_xyzzy (ac, argv, xyzzy))
  249. return error (IDS_CALL_PROCESS);
  250. #define RETRY_MAX 30
  251. int i;
  252. for (i = 0; i < RETRY_MAX; i++)
  253. {
  254. if (find_server (ls))
  255. break;
  256. Sleep (100);
  257. }
  258. if (i == RETRY_MAX)
  259. return error (IDS_CONNECT_FAILED);
  260. }
  261. }
  262. int wait_ok = ls.hevent && WaitForSingleObject (ls.hevent, 60000) == WAIT_OBJECT_0;
  263. CloseHandle (ls.hevent);
  264. if (!wait_ok)
  265. return error (IDS_CONNECT_FAILED);
  266. ForceSetForegroundWindow (ls.hwnd);
  267. int r = SendMessage (ls.hwnd, RegisterWindowMessage (xyzzysrv_name),
  268. GetCurrentProcessId (), LPARAM (sv.handle ()));
  269. if (!r)
  270. return error (IDS_READ_FAILED);
  271. if (r > 0)
  272. wait_term (sv);
  273. return 0;
  274. }
  275. static const char *
  276. skip_white (const char *p)
  277. {
  278. for (; *p == ' ' || *p == '\t'; p++)
  279. ;
  280. return p;
  281. }
  282. #define COPYCHAR(C) (nchars++, (b ? *b++ = (C) : 0))
  283. #define COPYARGV(X) (ac++, (av ? *av++ = (X) : 0))
  284. static int
  285. parse_cmdline1 (const char *p, char *&b0, int &ac, char **&av0, int nchars)
  286. {
  287. char *b = b0;
  288. char **av = av0;
  289. while (1)
  290. {
  291. p = skip_white (p);
  292. if (!*p)
  293. break;
  294. COPYARGV (b);
  295. int dq = 0;
  296. while (1)
  297. {
  298. int nbacksl;
  299. for (nbacksl = 0; *p == '\\'; nbacksl++, p++)
  300. ;
  301. int ignore = 0;
  302. if (*p == '"')
  303. {
  304. if (!(nbacksl & 1))
  305. {
  306. if (dq && p[1] == '"')
  307. p++;
  308. else
  309. ignore = 1;
  310. dq = !dq;
  311. }
  312. nbacksl >>= 1;
  313. }
  314. while (nbacksl-- > 0)
  315. COPYCHAR ('\\');
  316. if (!*p || (!dq && (*p == ' ' || *p == '\t')))
  317. break;
  318. if (!ignore)
  319. {
  320. if (IsDBCSLeadByte (*p) && p[1])
  321. {
  322. COPYCHAR (*p);
  323. p++;
  324. }
  325. COPYCHAR (*p);
  326. }
  327. p++;
  328. }
  329. COPYCHAR (0);
  330. }
  331. b0 = b;
  332. av0 = av;
  333. return nchars;
  334. }
  335. static int
  336. notepad_parse_cmdline (const char *p, char *&b0, int &ac, char **&av0, int nchars)
  337. {
  338. char *b = b0;
  339. char **av = av0;
  340. p = skip_white (p);
  341. if (*p == '/' && (p[1] == 'p' || p[1] == 'P')
  342. && (p[2] == ' ' || p[2] == '\t' || !p[2]))
  343. {
  344. COPYARGV ("-p");
  345. p = skip_white (p + 2);
  346. }
  347. if (*p)
  348. {
  349. COPYARGV (b);
  350. do
  351. {
  352. if (*p != '"')
  353. {
  354. if (IsDBCSLeadByte (*p) && p[1])
  355. {
  356. COPYCHAR (*p);
  357. p++;
  358. }
  359. COPYCHAR (*p);
  360. }
  361. p++;
  362. }
  363. while (*p);
  364. COPYCHAR (0);
  365. }
  366. b0 = b;
  367. av0 = av;
  368. return nchars;
  369. }
  370. struct config
  371. {
  372. char xyzzy[MAX_PATH];
  373. char pre_opt[1024];
  374. char post_opt[1024];
  375. int notepad;
  376. int multi_instance;
  377. };
  378. static int
  379. parse_cmdline (const char *p, char *b, int &ac, char **av, const config &cf)
  380. {
  381. int nchars = 0;
  382. ac = -1;
  383. COPYARGV (b);
  384. if (*p == '"')
  385. {
  386. for (p++; *p && *p != '"'; p++)
  387. {
  388. if (IsDBCSLeadByte (*p) && p[1])
  389. {
  390. COPYCHAR (*p);
  391. p++;
  392. }
  393. COPYCHAR (*p);
  394. }
  395. COPYCHAR (0);
  396. if (*p == '"')
  397. p++;
  398. }
  399. else
  400. {
  401. for (; *p && *p != ' ' && *p != '\t'; p++)
  402. {
  403. if (IsDBCSLeadByte (*p) && p[1])
  404. {
  405. COPYCHAR (*p);
  406. p++;
  407. }
  408. COPYCHAR (*p);
  409. }
  410. COPYCHAR (0);
  411. }
  412. if (!cf.notepad)
  413. {
  414. nchars = parse_cmdline1 (cf.pre_opt, b, ac, av, nchars);
  415. nchars = parse_cmdline1 (p, b, ac, av, nchars);
  416. }
  417. else
  418. {
  419. COPYARGV ("-wait");
  420. nchars = parse_cmdline1 (cf.pre_opt, b, ac, av, nchars);
  421. nchars = notepad_parse_cmdline (p, b, ac, av, nchars);
  422. }
  423. nchars = parse_cmdline1 (cf.post_opt, b, ac, av, nchars);
  424. COPYARGV (0);
  425. return nchars;
  426. #undef COPYCHAR
  427. #undef COPYARGV
  428. }
  429. static char *
  430. basename (char *path)
  431. {
  432. char *base = 0;
  433. char *p = path;
  434. while (*p)
  435. {
  436. if (IsDBCSLeadByte (*p) && p[1])
  437. p += 2;
  438. else
  439. {
  440. if (*p == '\\')
  441. base = p + 1;
  442. p++;
  443. }
  444. }
  445. return base ? base : path;
  446. }
  447. static void
  448. read_config (config &cf)
  449. {
  450. char path[MAX_PATH + 16];
  451. GetModuleFileName (0, path, MAX_PATH);
  452. cf.notepad = !lstrcmpi (basename (path), "notepad.exe");
  453. int l = lstrlen (path);
  454. if (l > 4 && !lstrcmpi (&path[l - 4], ".exe"))
  455. lstrcpy (&path[l - 3], "ini");
  456. else
  457. lstrcpy (path + l, ".ini");
  458. GetPrivateProfileString ("xyzzy", "path", "xyzzy.exe",
  459. cf.xyzzy, sizeof cf.xyzzy, path);
  460. if (!cf.notepad)
  461. cf.notepad = GetPrivateProfileInt ("xyzzy", "compatNotepad", 0, path);
  462. cf.multi_instance = GetPrivateProfileInt ("xyzzy", "multipleInstances", 0, path);
  463. GetPrivateProfileString ("xyzzy", "precedingOptions", "",
  464. cf.pre_opt, sizeof cf.pre_opt, path);
  465. GetPrivateProfileString ("xyzzy", "followingOptions", "",
  466. cf.post_opt, sizeof cf.post_opt, path);
  467. }
  468. int WINAPI
  469. WinMain (HINSTANCE hinst, HINSTANCE, LPSTR, int cmdshow)
  470. {
  471. config cf;
  472. read_config (cf);
  473. const char *const cl = GetCommandLine ();
  474. int ac;
  475. int nchars = parse_cmdline (cl, 0, ac, 0, cf);
  476. char **av = (char **)_alloca (sizeof *av * (ac + 1) + nchars);
  477. parse_cmdline (cl, (char *)(av + ac + 1), ac, av, cf);
  478. ExitProcess (xmain (ac, av, cf.xyzzy, cf.multi_instance));
  479. }