PageRenderTime 72ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/src/process.cc

https://github.com/snmsts/xyzzy
C++ | 1522 lines | 1342 code | 179 blank | 1 comment | 218 complexity | fd9816d1060f479d881e1bf8f9dfddf6 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. #include "stdafx.h"
  2. #include "ed.h"
  3. #include "dyn-handle.h"
  4. #include "sockinet.h"
  5. #include "byte-stream.h"
  6. #include "mainframe.h"
  7. class EnvStrings
  8. {
  9. char *e_env;
  10. char *e_buf;
  11. void set (char **, char **&, char *) const;
  12. char *set (char **, char **&, char *, lisp, lisp) const;
  13. static int __cdecl compare (const void *p1, const void *p2)
  14. {return stricmp (*(char **)p1, *(char **)p2);}
  15. public:
  16. EnvStrings () : e_env (0), e_buf (0) {}
  17. ~EnvStrings ()
  18. {
  19. xfree (e_buf);
  20. xfree (e_env);
  21. }
  22. void setup (lisp);
  23. const char *str () const {return e_env;}
  24. };
  25. void
  26. EnvStrings::set (char **nb, char **&ne, char *b) const
  27. {
  28. char *eq = b;
  29. if (*eq == '=')
  30. eq++;
  31. eq = strchr (eq, '=');
  32. if (!eq)
  33. return;
  34. int l = eq - b + 1;
  35. for (; nb < ne; nb++)
  36. if (!memicmp (b, *nb, l))
  37. {
  38. *nb = b[l] ? b : "";
  39. return;
  40. }
  41. if (b[l])
  42. *ne++ = b;
  43. }
  44. char *
  45. EnvStrings::set (char **nb, char **&ne, char *b, lisp var, lisp val) const
  46. {
  47. char *b0 = b;
  48. b = w2s (b, var);
  49. *b++ = '=';
  50. if (val == Qnil)
  51. *b++ = 0;
  52. else
  53. b = w2s (b, val) + 1;
  54. set (nb, ne, b0);
  55. return b;
  56. }
  57. void
  58. EnvStrings::setup (lisp lenv)
  59. {
  60. int n = 0, l = 0;
  61. for (lisp le = lenv; consp (le); le = xcdr (le), n++)
  62. {
  63. lisp x = xcar (le);
  64. check_cons (x);
  65. check_string (xcar (x));
  66. l += xstring_length (xcar (x)) * 2 + 2;
  67. if (xcdr (x) != Qnil)
  68. {
  69. check_string (xcdr (x));
  70. l += xstring_length (xcdr (x)) * 2;
  71. }
  72. }
  73. for (int d = 0; d < 26; d++)
  74. {
  75. const char *dir = get_device_dir (d);
  76. int x = strlen (dir);
  77. if (x > 3)
  78. {
  79. l += x + sizeof "=X:=X:";
  80. n++;
  81. }
  82. }
  83. for (char **e = environ; *e; e++, n++)
  84. ;
  85. l = (l + sizeof (char **) - 1) / sizeof (char **) * sizeof (char **);
  86. e_buf = (char *)xmalloc (l + sizeof (char **) * n);
  87. char **nb = (char **)(e_buf + l);
  88. char **ne = nb;
  89. for (char **e = environ; *e; e++, ne++)
  90. *ne = *e;
  91. char *b = e_buf;
  92. for (lisp le = lenv; consp (le); le = xcdr (le))
  93. {
  94. lisp x = xcar (le);
  95. b = set (nb, ne, b, xcar (x), xcdr (x));
  96. }
  97. for (int d = 0; d < 26; d++)
  98. {
  99. const char *dir = get_device_dir (d);
  100. int x = strlen (dir);
  101. if (x > 3)
  102. {
  103. char *b0 = b;
  104. b += sprintf (b, "=%c:=%c:%s", 'A' + d, 'A' + d, dir) + 1;
  105. set (nb, ne, b0);
  106. }
  107. }
  108. qsort (nb, ne - nb, sizeof *nb, compare);
  109. l = 1;
  110. for (char **np = nb; np < ne; np++)
  111. if (**np)
  112. l += strlen (*np) + 1;
  113. e_env = (char *)xmalloc (l);
  114. char *p = e_env;
  115. for (char **np = nb; np < ne; np++)
  116. if (**np)
  117. p = stpcpy (p, *np) + 1;
  118. *p = 0;
  119. }
  120. static void
  121. path_arg (int no_std_handles, lisp lpath, char *path)
  122. {
  123. if (no_std_handles)
  124. *path = 0;
  125. else if (stringp (lpath))
  126. pathname2cstr (lpath, path);
  127. else if (lpath == Qnil)
  128. strcpy (path, "nul");
  129. else
  130. *path = 0;
  131. }
  132. static void
  133. open_for_read (dyn_handle &dh, const char *path, lisp lpath,
  134. SECURITY_ATTRIBUTES *sa)
  135. {
  136. if (!*path)
  137. return;
  138. dh.fix (WINFS::CreateFile (path, GENERIC_READ,
  139. FILE_SHARE_READ | FILE_SHARE_WRITE, sa,
  140. OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0));
  141. if (!dh.valid ())
  142. file_error (GetLastError (), lpath);
  143. }
  144. static void
  145. open_for_write (dyn_handle &dh, const char *path, lisp lpath,
  146. SECURITY_ATTRIBUTES *sa)
  147. {
  148. if (!*path)
  149. return;
  150. dh.fix (WINFS::CreateFile (path, GENERIC_WRITE,
  151. FILE_SHARE_READ | FILE_SHARE_WRITE, sa, CREATE_ALWAYS,
  152. FILE_ATTRIBUTE_ARCHIVE | FILE_FLAG_SEQUENTIAL_SCAN, 0));
  153. if (dh.valid ())
  154. return;
  155. dh.fix (WINFS::CreateFile (path, GENERIC_WRITE,
  156. FILE_SHARE_READ | FILE_SHARE_WRITE, sa,
  157. OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0));
  158. if (!dh.valid ())
  159. file_error (GetLastError (), lpath);
  160. }
  161. lisp
  162. Fcall_process (lisp cmd, lisp keys)
  163. {
  164. check_string (cmd);
  165. EnvStrings env;
  166. env.setup (find_keyword (Kenviron, keys));
  167. char *cmdline = (char *)alloca (xstring_length (cmd) * 2 + 1);
  168. w2s (cmdline, cmd);
  169. int no_std_handles = find_keyword_bool (Kno_std_handles, keys);
  170. lisp lstdin = find_keyword (Kinput, keys);
  171. lisp lstdout = find_keyword (Koutput, keys);
  172. lisp lstderr = find_keyword (Kerror, keys, 0);
  173. if (!lstderr)
  174. lstderr = lstdout;
  175. char infile[PATH_MAX + 1], outfile[PATH_MAX + 1], errfile[PATH_MAX + 1];
  176. path_arg (no_std_handles, lstdin, infile);
  177. path_arg (no_std_handles, lstdout, outfile);
  178. path_arg (no_std_handles, lstderr, errfile);
  179. lisp exec_dir = find_keyword (Kexec_directory, keys);
  180. if (exec_dir == Qnil)
  181. exec_dir = selected_buffer ()->ldirectory;
  182. char dir[PATH_MAX + 1];
  183. pathname2cstr (exec_dir, dir);
  184. map_sl_to_backsl (dir);
  185. int show;
  186. lisp lshow = find_keyword (Kshow, keys);
  187. if (lshow == Kshow)
  188. show = SW_SHOWNORMAL;
  189. else if (lshow == Kno_active)
  190. show = SW_SHOWNA;
  191. else if (lshow == Kmaximize)
  192. show = SW_SHOWMAXIMIZED;
  193. else if (lshow == Khide)
  194. show = SW_HIDE;
  195. else if (lshow == Kminimize)
  196. show = SW_SHOWMINNOACTIVE;
  197. else
  198. show = SW_SHOWNORMAL;
  199. lisp wait = find_keyword (Kwait, keys);
  200. if (wait != Qnil && !realp (wait))
  201. wait = Qt;
  202. SECURITY_ATTRIBUTES sa;
  203. sa.nLength = sizeof sa;
  204. sa.lpSecurityDescriptor = 0;
  205. sa.bInheritHandle = 1;
  206. dyn_handle hin, hout, herr;
  207. open_for_read (hin, infile, lstdin, &sa);
  208. open_for_write (hout, outfile, lstdout, &sa);
  209. if (lstdout != lstderr)
  210. open_for_write (herr, errfile, lstderr, &sa);
  211. STARTUPINFO si;
  212. bzero (&si, sizeof si);
  213. si.cb = sizeof si;
  214. si.dwFlags = STARTF_USESHOWWINDOW;
  215. si.wShowWindow = show;
  216. if (!no_std_handles)
  217. {
  218. si.dwFlags |= STARTF_USESTDHANDLES;
  219. si.hStdInput = hin.valid () ? (HANDLE)hin : GetStdHandle (STD_INPUT_HANDLE);
  220. si.hStdOutput = hout.valid () ? (HANDLE)hout : GetStdHandle (STD_OUTPUT_HANDLE);
  221. si.hStdError = (lstdout != lstderr
  222. ? herr.valid () ? (HANDLE)herr : GetStdHandle (STD_ERROR_HANDLE)
  223. : hout.valid () ? (HANDLE)hout : GetStdHandle (STD_ERROR_HANDLE));
  224. }
  225. WINFS::SetCurrentDirectory (dir);
  226. PROCESS_INFORMATION pi;
  227. int result = CreateProcess (0, cmdline, 0, 0, !no_std_handles,
  228. (CREATE_DEFAULT_ERROR_MODE
  229. /*| CREATE_NEW_PROCESS_GROUP*/
  230. | NORMAL_PRIORITY_CLASS),
  231. (void *)env.str (), dir, &si, &pi);
  232. int error = GetLastError ();
  233. w2s (dir, xsymbol_value (Qdefault_dir));
  234. WINFS::SetCurrentDirectory (dir);
  235. DWORD exit_code = 0;
  236. if (!result)
  237. FEsimple_win32_error (error, cmd);
  238. CloseHandle (pi.hThread);
  239. if (wait == Qt)
  240. {
  241. Char cc = 0;
  242. temporary_string tem (&cc, 0);
  243. Fsi_minibuffer_message (tem.string (), Qt);
  244. wait_process_terminate (pi.hProcess);
  245. GetExitCodeProcess (pi.hProcess, &exit_code);
  246. Fsi_minibuffer_message (Qnil, Qnil);
  247. }
  248. else if (wait != Qnil)
  249. {
  250. double w = coerce_to_double_float (wait);
  251. DWORD d = min (DWORD (w * 1000), 300000UL);
  252. if (d > 0)
  253. WaitForInputIdle (pi.hProcess, d);
  254. }
  255. CloseHandle (pi.hProcess);
  256. return wait == Qt ? make_fixnum (exit_code) : Qt;
  257. }
  258. class Process
  259. {
  260. protected:
  261. struct read_data
  262. {
  263. const Char *data;
  264. int size;
  265. int done;
  266. };
  267. Buffer *p_bufp;
  268. lisp p_proc;
  269. lisp p_filter;
  270. lisp p_sentinel;
  271. lisp p_last_incode;
  272. lisp p_marker;
  273. xbuffered_read_stream *p_input_stream;
  274. StrBuf p_osbuf;
  275. CRITICAL_SECTION p_cri;
  276. int p_in_send_string;
  277. int p_pending;
  278. Process (Buffer *bp, lisp pl, lisp marker);
  279. virtual u_int read_process () = 0;
  280. static u_int __stdcall read_process (void *p)
  281. {return ((Process *)p)->read_process ();}
  282. void read_process_output ();
  283. void read_process_output (xbuffered_read_stream &, class process_output_stream &);
  284. void terminated (int);
  285. void notify_term () const
  286. {PostMessage (app.toplev, WM_PRIVATE_PROCESS_TERMINATE, 0, LPARAM (this));}
  287. public:
  288. virtual ~Process ();
  289. virtual void wait_terminate () = 0;
  290. virtual void signal () = 0;
  291. virtual void kill () = 0;
  292. virtual void send (const char *, int) const = 0;
  293. void insert_process_output (void *);
  294. lisp process_buffer () const {return p_bufp->lbp;}
  295. void flush_input ();
  296. void store_output (const Char *, int);
  297. virtual int readin (u_char *, int) = 0;
  298. int incode_modified_p () const
  299. {return xprocess_incode (p_proc) != p_last_incode;}
  300. eol_code eolcode () const {return xprocess_eol_code (p_proc);}
  301. lisp &filter () {return p_filter;}
  302. lisp &sentinel () {return p_sentinel;}
  303. lisp &marker () {return p_marker;}
  304. static lisp make_process_marker (Buffer *bp)
  305. {
  306. lisp marker = Fmake_marker (bp->lbp);
  307. xmarker_point (marker) = bp->b_contents.p2;
  308. return marker;
  309. }
  310. int &in_send_string_p () {return p_in_send_string;}
  311. void end_send_string ()
  312. {
  313. if (!p_pending)
  314. {
  315. EnterCriticalSection (&p_cri);
  316. int empty_p = p_osbuf.empty_p ();
  317. LeaveCriticalSection (&p_cri);
  318. if (!empty_p)
  319. {
  320. PostMessage (app.toplev, WM_PRIVATE_PROCESS_OUTPUT, 0, LPARAM (this));
  321. p_pending = 1;
  322. }
  323. }
  324. }
  325. };
  326. static lisp
  327. process_char_encoding (lisp encoding)
  328. {
  329. if (encoding == Qnil)
  330. encoding = xsymbol_value (Vdefault_process_encoding);
  331. check_char_encoding (encoding);
  332. if (xchar_encoding_type (encoding) == encoding_auto_detect)
  333. FEtype_error (encoding, Qchar_encoding);
  334. return encoding;
  335. }
  336. void
  337. process_io_encoding (lisp &incode, lisp &outcode, lisp keys)
  338. {
  339. incode = process_char_encoding (find_keyword (Kincode, keys));
  340. outcode = process_char_encoding (find_keyword (Koutcode, keys));
  341. }
  342. static eol_code
  343. process_eol_code (lisp code)
  344. {
  345. if (code == Qnil)
  346. return eol_crlf;
  347. int n = fixnum_value (code);
  348. if (!valid_eol_code_p (n) || n == eol_guess)
  349. n = eol_crlf;
  350. return eol_code (n);
  351. }
  352. Process::Process (Buffer *bp, lisp pl, lisp marker)
  353. : p_bufp (bp), p_proc (pl), p_filter (Qnil), p_sentinel (Qnil),
  354. p_marker (marker), p_input_stream (0), p_in_send_string (0),
  355. p_pending (0)
  356. {
  357. InitializeCriticalSection (&p_cri);
  358. }
  359. Process::~Process ()
  360. {
  361. DeleteCriticalSection (&p_cri);
  362. }
  363. void
  364. Process::terminated (int exit_code)
  365. {
  366. xprocess_data (p_proc) = 0;
  367. xprocess_status (p_proc) = PS_EXIT;
  368. xprocess_exit_code (p_proc) = exit_code;
  369. delq (p_proc, &xsymbol_value (Vprocess_list));
  370. if (p_sentinel != Qnil)
  371. {
  372. try
  373. {
  374. dynamic_bind d (Vinhibit_quit, Qt);
  375. funcall_1 (p_sentinel, p_proc);
  376. }
  377. catch (nonlocal_jump &)
  378. {
  379. }
  380. }
  381. p_bufp->modify_mode_line ();
  382. for (Window *wp = app.active_frame.windows; wp; wp = wp->w_next)
  383. if (wp->w_bufp == p_bufp)
  384. {
  385. refresh_screen (0);
  386. g_frame.update_ui ();
  387. break;
  388. }
  389. }
  390. void
  391. Process::insert_process_output (void *p)
  392. {
  393. lisp lstring = 0;
  394. try
  395. {
  396. read_data *r = (read_data *)p;
  397. const Char *data;
  398. int size;
  399. if (r)
  400. {
  401. if (r->done)
  402. return;
  403. r->done = 1;
  404. if (p_in_send_string || !p_osbuf.empty_p ())
  405. {
  406. p_osbuf.add (r->data, r->size);
  407. return;
  408. }
  409. data = r->data;
  410. size = r->size;
  411. }
  412. else
  413. {
  414. p_pending = 0;
  415. if (p_osbuf.empty_p ())
  416. return;
  417. EnterCriticalSection (&p_cri);
  418. try
  419. {
  420. lstring = p_osbuf.make_string ();
  421. }
  422. catch (nonlocal_jump &)
  423. {
  424. }
  425. p_osbuf.empty ();
  426. LeaveCriticalSection (&p_cri);
  427. if (!lstring)
  428. return;
  429. data = xstring_contents (lstring);
  430. size = xstring_length (lstring);
  431. }
  432. if (p_filter != Qnil)
  433. {
  434. dynamic_bind d (Vinhibit_quit, Qt);
  435. lisp s = lstring ? lstring : make_string (data, size);
  436. lstring = 0;
  437. funcall_2 (p_filter, p_proc, s);
  438. }
  439. else
  440. {
  441. Window *wp = selected_window ();
  442. if (xmarker_point (p_marker) == NO_MARK_SET)
  443. xmarker_point (p_marker) = p_bufp->b_contents.p2;
  444. int goto_tail = (wp->w_bufp == p_bufp
  445. && wp->w_point.p_point == xmarker_point (p_marker));
  446. Point point;
  447. p_bufp->set_point (point, xmarker_point (p_marker));
  448. p_bufp->check_read_only ();
  449. p_bufp->insert_chars (point, data, size);
  450. xmarker_point (p_marker) += size;
  451. if (goto_tail)
  452. p_bufp->goto_char (wp->w_point, xmarker_point (p_marker));
  453. int f = 0;
  454. for (wp = app.active_frame.windows; wp; wp = wp->w_next)
  455. if (wp->w_bufp == p_bufp)
  456. {
  457. wp->w_disp_flags |= Window::WDF_REFRAME_SCROLL;
  458. f = 1;
  459. }
  460. if (f)
  461. refresh_screen (0);
  462. }
  463. }
  464. catch (nonlocal_jump &)
  465. {
  466. }
  467. if (lstring)
  468. destruct_string (lstring);
  469. }
  470. static int
  471. good_process_p (const Process *pr)
  472. {
  473. for (lisp p = xsymbol_value (Vprocess_list); consp (p); p = xcdr (p))
  474. if (xprocess_data (xcar (p)) == pr)
  475. return 1;
  476. return 0;
  477. }
  478. void
  479. read_process_output (WPARAM wparam, LPARAM lparam)
  480. {
  481. if (good_process_p ((Process *)lparam))
  482. ((Process *)lparam)->insert_process_output ((void *)wparam);
  483. }
  484. void
  485. Process::store_output (const Char *w, int l)
  486. {
  487. if (!l)
  488. return;
  489. EnterCriticalSection (&p_cri);
  490. if (p_osbuf.empty_p ())
  491. {
  492. LeaveCriticalSection (&p_cri);
  493. read_data r;
  494. r.data = w;
  495. r.size = l;
  496. r.done = 0;
  497. DWORD result;
  498. do
  499. if (SendMessageTimeout (app.toplev, WM_PRIVATE_PROCESS_OUTPUT,
  500. WPARAM (&r), LPARAM (this),
  501. SMTO_NORMAL, 1000, &result)
  502. || !IsWindow (app.toplev)
  503. || r.done)
  504. return;
  505. while (!p_in_send_string);
  506. EnterCriticalSection (&p_cri);
  507. }
  508. try
  509. {
  510. p_osbuf.add (w, l);
  511. }
  512. catch (nonlocal_jump &)
  513. {
  514. }
  515. LeaveCriticalSection (&p_cri);
  516. }
  517. class process_output_stream: public Char_output_wstream
  518. {
  519. Process &p_proc;
  520. virtual void swrite (const Char *w, int l)
  521. {p_proc.store_output (w, l);}
  522. public:
  523. process_output_stream (Process &proc) : p_proc (proc) {}
  524. };
  525. class process_input_stream: public byte_input_stream
  526. {
  527. Process &p_proc;
  528. Char_output_stream &p_os;
  529. u_char p_buf[1024];
  530. int p_eofp;
  531. virtual int refill ()
  532. {
  533. if (p_eofp)
  534. return eof;
  535. p_os.flush (0);
  536. p_proc.flush_input ();
  537. int l;
  538. do
  539. {
  540. l = p_proc.readin (p_buf, sizeof p_buf);
  541. if (!l)
  542. {
  543. p_eofp = 1;
  544. break;
  545. }
  546. switch (p_proc.eolcode ())
  547. {
  548. case eol_crlf:
  549. {
  550. u_char *d = p_buf, *s = p_buf, *const se = s + l;
  551. for (; s < se; s++)
  552. if (*s != '\r')
  553. *d++ = *s;
  554. l = d - p_buf;
  555. break;
  556. }
  557. case eol_cr:
  558. {
  559. for (u_char *s = p_buf, *const se = s + l; s < se; s++)
  560. if (*s == '\r')
  561. *s = '\n';
  562. break;
  563. }
  564. }
  565. }
  566. while (!l);
  567. int c = setbuf (p_buf, p_buf + l);
  568. if (!p_proc.incode_modified_p ())
  569. return c;
  570. putback (c);
  571. return eof;
  572. }
  573. public:
  574. process_input_stream (Process &proc, Char_output_stream &os)
  575. : p_proc (proc), p_os (os), p_eofp (0) {}
  576. int eofp () const {return p_eofp;}
  577. };
  578. void
  579. Process::flush_input ()
  580. {
  581. if (p_input_stream)
  582. {
  583. int l;
  584. const Char *b;
  585. p_input_stream->flush (b, l);
  586. if (l)
  587. store_output (b, l);
  588. }
  589. }
  590. void
  591. Process::read_process_output (xbuffered_read_stream &is, process_output_stream &os)
  592. {
  593. p_input_stream = &is;
  594. int c;
  595. while ((c = is.get ()) != xstream::eof)
  596. os.put (c);
  597. os.flush (1);
  598. p_input_stream = 0;
  599. }
  600. void
  601. Process::read_process_output ()
  602. {
  603. process_output_stream os (*this);
  604. process_input_stream is (*this, os);
  605. while (!is.eofp ())
  606. {
  607. p_last_incode = xprocess_incode (p_proc);
  608. encoding_input_stream_helper s (p_last_incode, is);
  609. read_process_output ((xbuffered_read_stream &)s, os);
  610. }
  611. os.flush (1);
  612. }
  613. class process_output_byte_stream: public byte_output_stream
  614. {
  615. Process &p_proc;
  616. u_char p_buf[1024];
  617. protected:
  618. virtual u_char *sflush (u_char *b, u_char *be, int)
  619. {
  620. p_proc.send ((char *)b, be - b);
  621. return b;
  622. }
  623. public:
  624. process_output_byte_stream (Process &proc)
  625. : byte_output_stream (p_buf, p_buf + sizeof p_buf), p_proc (proc) {}
  626. };
  627. class NormalProcess: public Process
  628. {
  629. protected:
  630. dyn_handle p_in;
  631. dyn_handle p_out;
  632. dyn_handle p_event;
  633. dyn_handle p_process;
  634. dyn_handle p_read_thread;
  635. dyn_handle p_wait_thread;
  636. DWORD p_process_id;
  637. DWORD p_exit_code;
  638. virtual u_int read_process ();
  639. u_int wait_process ();
  640. static u_int __stdcall wait_process (void *p)
  641. {return ((NormalProcess *)p)->wait_process ();}
  642. void signal_nt ()
  643. {
  644. if (!SetEvent (p_event))
  645. FEsimple_win32_error (GetLastError ());
  646. }
  647. void signal_win95 ();
  648. struct dos_prompt
  649. {
  650. HWND hwnd;
  651. DWORD pid;
  652. };
  653. static BOOL CALLBACK find_tty (HWND, LPARAM);
  654. public:
  655. NormalProcess (Buffer *bp, lisp pl, lisp marker) : Process (bp, pl, marker) {}
  656. virtual ~NormalProcess () {}
  657. virtual void wait_terminate ();
  658. virtual void signal ()
  659. {
  660. if (sysdep.WinNTp ())
  661. signal_nt ();
  662. else
  663. signal_win95 ();
  664. }
  665. virtual void kill ()
  666. {
  667. if (!TerminateProcess (p_process, 2))
  668. FEsimple_win32_error (GetLastError ());
  669. }
  670. virtual void send (const char *s, int l) const
  671. {
  672. DWORD nwrite;
  673. if (!WriteFile (p_out, s, l, &nwrite, 0))
  674. file_error (GetLastError ());
  675. }
  676. void create (lisp, lisp, int, const char *);
  677. virtual int readin (u_char *, int);
  678. };
  679. int
  680. NormalProcess::readin (u_char *buf, int size)
  681. {
  682. u_char *b = buf, *be = buf + size;
  683. DWORD avail;
  684. int wait = 5;
  685. while (1)
  686. {
  687. if (!PeekNamedPipe (p_in, 0, 0, 0, &avail, 0))
  688. return 0;
  689. if (avail)
  690. break;
  691. if (!p_process.valid ())
  692. return 0;
  693. Sleep (wait);
  694. if (wait < 100)
  695. wait += 5;
  696. }
  697. int i = 0;
  698. while (1)
  699. {
  700. DWORD nread;
  701. if (!ReadFile (p_in, b, be - b, &nread, 0) || !nread)
  702. break;
  703. b += nread;
  704. if (++i >= 10 || b - buf >= size / 2)
  705. break;
  706. Sleep (5);
  707. if (!PeekNamedPipe (p_in, 0, 0, 0, &avail, 0) || !avail)
  708. break;
  709. }
  710. return b - buf;
  711. }
  712. u_int
  713. NormalProcess::read_process ()
  714. {
  715. if (!p_process.valid ())
  716. return 0;
  717. read_process_output ();
  718. return 0;
  719. }
  720. u_int
  721. NormalProcess::wait_process ()
  722. {
  723. if (!p_process.valid ())
  724. return 0;
  725. WaitForSingleObject (p_process, INFINITE);
  726. if (!GetExitCodeProcess (p_process, &p_exit_code))
  727. p_exit_code = DWORD (-1);
  728. p_process.close ();
  729. WaitForSingleObject (p_read_thread, INFINITE);
  730. notify_term ();
  731. return 0;
  732. }
  733. BOOL CALLBACK
  734. NormalProcess::find_tty (HWND hwnd, LPARAM arg)
  735. {
  736. DWORD pid;
  737. GetWindowThreadProcessId (hwnd, &pid);
  738. if (pid != ((dos_prompt *)arg)->pid)
  739. return 1;
  740. char name[32];
  741. if (!GetClassName (hwnd, name, sizeof name)
  742. || lstrcmp (name, "tty"))
  743. return 1;
  744. ((dos_prompt *)arg)->hwnd = hwnd;
  745. return 0;
  746. }
  747. void
  748. NormalProcess::signal_win95 ()
  749. {
  750. dos_prompt tty;
  751. tty.hwnd = 0;
  752. tty.pid = p_process_id;
  753. EnumWindows (find_tty, LPARAM (&tty));
  754. if (!tty.hwnd)
  755. return;
  756. SuspendThread (p_read_thread);
  757. UINT c = MapVirtualKey ('C', 0);
  758. UINT ctrl = MapVirtualKey (VK_CONTROL, 0);
  759. ShowWindow (tty.hwnd, SW_RESTORE);
  760. ForceSetForegroundWindow (tty.hwnd);
  761. keybd_event (VK_CONTROL, ctrl, 0, 0);
  762. keybd_event ('C', c, 0, 0);
  763. keybd_event ('C', c, KEYEVENTF_KEYUP, 0);
  764. keybd_event (VK_CONTROL, ctrl, KEYEVENTF_KEYUP, 0);
  765. ResumeThread (p_read_thread);
  766. Sleep (100);
  767. ShowWindow (tty.hwnd, SW_MINIMIZE);
  768. }
  769. void
  770. NormalProcess::create (lisp command, lisp execdir, int show, const char *env)
  771. {
  772. char dir[PATH_MAX + 1];
  773. pathname2cstr (execdir, dir);
  774. map_sl_to_backsl (dir);
  775. SECURITY_ATTRIBUTES sa;
  776. sa.nLength = sizeof sa;
  777. sa.lpSecurityDescriptor = 0;
  778. sa.bInheritHandle = 1;
  779. dyn_handle opipe_r, opipe_w;
  780. if (!pipe (opipe_r, opipe_w, &sa))
  781. file_error (GetLastError ());
  782. dyn_handle ipipe_r, ipipe_w;
  783. if (!pipe (ipipe_r, ipipe_w, &sa))
  784. file_error (GetLastError ());
  785. dyn_handle d (ipipe_w);
  786. if (!d.valid ())
  787. file_error (GetLastError ());
  788. CloseHandle (ipipe_w.unfix ());
  789. ipipe_w.fix (d.unfix ());
  790. dyn_handle event (CreateEvent (&sa, 0, 0, 0));
  791. if (!event.valid ())
  792. file_error (GetLastError ());
  793. char *cmdline = (char *)alloca (128 + xstring_length (command) * 2 + 1);
  794. sprintf (cmdline, "xyzzyenv %u ", HANDLE (event));
  795. w2s (cmdline + strlen (cmdline), command);
  796. u_int thread_id;
  797. HANDLE hread_thread = HANDLE (_beginthreadex (0, 0, Process::read_process, this,
  798. CREATE_SUSPENDED, &thread_id));
  799. if (!hread_thread)
  800. FEsimple_error (Ecreate_thread_failed);
  801. HANDLE hwait_thread = HANDLE (_beginthreadex (0, 0, wait_process, this,
  802. CREATE_SUSPENDED, &thread_id));
  803. if (!hwait_thread)
  804. {
  805. ResumeThread (hread_thread);
  806. WaitForSingleObject (hread_thread, INFINITE);
  807. FEsimple_error (Ecreate_thread_failed);
  808. }
  809. STARTUPINFO si;
  810. bzero (&si, sizeof si);
  811. si.cb = sizeof si;
  812. si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  813. si.wShowWindow = show;
  814. si.hStdInput = ipipe_r;
  815. si.hStdOutput = opipe_w;
  816. si.hStdError = opipe_w;
  817. WINFS::SetCurrentDirectory (dir);
  818. PROCESS_INFORMATION pi;
  819. int result = CreateProcess (0, cmdline, 0, 0, 1,
  820. (CREATE_NEW_PROCESS_GROUP
  821. | CREATE_DEFAULT_ERROR_MODE
  822. | NORMAL_PRIORITY_CLASS),
  823. (void *)env, dir, &si, &pi);
  824. int error = GetLastError ();
  825. w2s (dir, xsymbol_value (Qdefault_dir));
  826. WINFS::SetCurrentDirectory (dir);
  827. p_in.fix (opipe_r.unfix ());
  828. p_out.fix (ipipe_w.unfix ());
  829. p_event.fix (event.unfix ());
  830. if (result)
  831. {
  832. CloseHandle (pi.hThread);
  833. p_process.fix (pi.hProcess);
  834. p_process_id = pi.dwProcessId;
  835. }
  836. p_read_thread.fix (hread_thread);
  837. p_wait_thread.fix (hwait_thread);
  838. ResumeThread (hread_thread);
  839. ResumeThread (hwait_thread);
  840. if (!result)
  841. {
  842. WaitForSingleObject (hread_thread, INFINITE);
  843. WaitForSingleObject (hwait_thread, INFINITE);
  844. file_error (error, command);
  845. }
  846. }
  847. void
  848. NormalProcess::wait_terminate ()
  849. {
  850. WaitForSingleObject (p_wait_thread, INFINITE);
  851. terminated (p_exit_code);
  852. }
  853. void
  854. wait_process_terminate (WPARAM wparam, LPARAM lparam)
  855. {
  856. if (good_process_p ((Process *)lparam))
  857. {
  858. Process *pl = (Process *)lparam;
  859. pl->wait_terminate ();
  860. delete pl;
  861. }
  862. }
  863. lisp
  864. Fmake_process (lisp command, lisp keys)
  865. {
  866. check_string (command);
  867. int show;
  868. lisp lshow = find_keyword (Kshow, keys);
  869. if (lshow == Kshow)
  870. show = SW_SHOWNORMAL;
  871. else if (lshow == Kno_active)
  872. show = SW_SHOWNA;
  873. else if (lshow == Kmaximize)
  874. show = SW_SHOWMAXIMIZED;
  875. else if (lshow == Khide)
  876. show = SW_HIDE;
  877. else if (lshow == Kminimize)
  878. show = SW_SHOWMINNOACTIVE;
  879. else
  880. show = SW_SHOWMINNOACTIVE;
  881. lisp execdir = find_keyword (Kexec_directory, keys);
  882. if (execdir == Qnil)
  883. execdir = selected_buffer ()->ldirectory;
  884. Buffer *bp = Buffer::coerce_to_buffer (find_keyword (Koutput, keys));
  885. if (buffer_has_process (bp))
  886. FEsimple_error (Esubprocess_is_already_running);
  887. lisp incode, outcode;
  888. process_io_encoding (incode, outcode, keys);
  889. lisp x = find_keyword (Keol_code, keys, 0);
  890. if (!x)
  891. x = find_keyword (Knewline_code, keys);
  892. eol_code eol = process_eol_code (x);
  893. EnvStrings env;
  894. env.setup (find_keyword (Kenviron, keys));
  895. lisp process = make_process ();
  896. lisp pl = xcons (process, xsymbol_value (Vprocess_list));
  897. xprocess_buffer (process) = bp->lbp;
  898. xprocess_command (process) = command;
  899. xprocess_incode (process) = incode;
  900. xprocess_outcode (process) = outcode;
  901. xprocess_eol_code (process) = eol;
  902. NormalProcess *pr = new NormalProcess (bp, process, Process::make_process_marker (bp));
  903. try
  904. {
  905. pr->create (command, execdir, show, env.str ());
  906. }
  907. catch (nonlocal_jump &)
  908. {
  909. delete pr;
  910. throw;
  911. }
  912. xsymbol_value (Vprocess_list) = pl;
  913. xprocess_data (process) = pr;
  914. xprocess_status (process) = PS_RUN;
  915. bp->lprocess = process;
  916. bp->modify_mode_line ();
  917. return process;
  918. }
  919. class SocketProcess: public Process
  920. {
  921. protected:
  922. sockinet p_so;
  923. dyn_handle p_read_thread;
  924. int p_error_code;
  925. virtual u_int read_process ();
  926. public:
  927. SocketProcess (Buffer *bp, lisp pl, lisp marker) : Process (bp, pl, marker) {}
  928. virtual ~SocketProcess () {}
  929. virtual void wait_terminate ();
  930. virtual void signal ()
  931. {
  932. try {p_so.close ();}
  933. catch (sock_error &e) {FEsocket_error (e.error_code ());}
  934. }
  935. virtual void kill ()
  936. {
  937. try {p_so.close (1);}
  938. catch (sock_error &e) {FEsocket_error (e.error_code ());}
  939. }
  940. virtual void send (const char *s, int l) const
  941. {
  942. try {p_so.send (s, l);}
  943. catch (sock_error &e) {FEsocket_error (e.error_code ());}
  944. }
  945. void create (lisp, lisp);
  946. virtual int readin (u_char *, int);
  947. };
  948. void
  949. SocketProcess::wait_terminate ()
  950. {
  951. WaitForSingleObject (p_read_thread, INFINITE);
  952. terminated (p_error_code);
  953. }
  954. int
  955. SocketProcess::readin (u_char *buf, int size)
  956. {
  957. try
  958. {
  959. return p_so.recv (buf, size);
  960. }
  961. catch (sock_error &e)
  962. {
  963. p_error_code = e.error_code ();
  964. return 0;
  965. }
  966. }
  967. u_int
  968. SocketProcess::read_process ()
  969. {
  970. if (!p_read_thread.valid ())
  971. return 0;
  972. p_error_code = 0;
  973. read_process_output ();
  974. notify_term ();
  975. return 0;
  976. }
  977. void
  978. SocketProcess::create (lisp host, lisp service)
  979. {
  980. u_int thread_id;
  981. HANDLE hread_thread = HANDLE (_beginthreadex (0, 0, Process::read_process, this,
  982. CREATE_SUSPENDED, &thread_id));
  983. if (!hread_thread)
  984. FEsimple_error (Ecreate_thread_failed);
  985. try
  986. {
  987. Fbegin_wait_cursor ();
  988. sockinet::saddr addr (host, service);
  989. p_so.set_eof_error_p (0);
  990. p_so.create ();
  991. p_so.connect (addr);
  992. }
  993. catch (sock_error &e)
  994. {
  995. Fend_wait_cursor ();
  996. ResumeThread (hread_thread);
  997. WaitForSingleObject (hread_thread, INFINITE);
  998. FEsocket_error (e.error_code ());
  999. }
  1000. Fend_wait_cursor ();
  1001. p_read_thread.fix (hread_thread);
  1002. ResumeThread (hread_thread);
  1003. }
  1004. lisp
  1005. Fopen_network_stream (lisp buffer, lisp host, lisp service, lisp keys)
  1006. {
  1007. Buffer *bp = Buffer::coerce_to_buffer (buffer);
  1008. if (buffer_has_process (bp))
  1009. FEsimple_error (Esubprocess_is_already_running);
  1010. lisp incode, outcode;
  1011. process_io_encoding (incode, outcode, keys);
  1012. lisp x = find_keyword (Keol_code, keys, 0);
  1013. if (!x)
  1014. x = find_keyword (Knewline_code, keys);
  1015. eol_code eol = process_eol_code (x);
  1016. lisp process = make_process ();
  1017. lisp pl = xcons (process, xsymbol_value (Vprocess_list));
  1018. xprocess_buffer (process) = bp->lbp;
  1019. xprocess_command (process) = xcons (host, service);
  1020. xprocess_incode (process) = incode;
  1021. xprocess_outcode (process) = outcode;
  1022. xprocess_eol_code (process) = eol;
  1023. SocketProcess *pr = new SocketProcess (bp, process, Process::make_process_marker (bp));
  1024. try
  1025. {
  1026. protect_gc gcpro1 (pl);
  1027. protect_gc gcpro2 (pr->marker ());
  1028. pr->create (host, service);
  1029. }
  1030. catch (nonlocal_jump &)
  1031. {
  1032. delete pr;
  1033. throw;
  1034. }
  1035. xsymbol_value (Vprocess_list) = pl;
  1036. xprocess_data (process) = pr;
  1037. xprocess_status (process) = PS_RUN;
  1038. bp->lprocess = process;
  1039. bp->modify_mode_line ();
  1040. return process;
  1041. }
  1042. int
  1043. buffer_has_process (const Buffer *bp)
  1044. {
  1045. for (lisp p = xsymbol_value (Vprocess_list); consp (p); p = xcdr (p))
  1046. if (xprocess_data (xcar (p))
  1047. && xprocess_data (xcar (p))->process_buffer () == bp->lbp)
  1048. return 1;
  1049. return 0;
  1050. }
  1051. int
  1052. query_kill_subprocesses ()
  1053. {
  1054. if (!consp (xsymbol_value (Vprocess_list)))
  1055. return 1;
  1056. if (!yes_or_no_p (Msubprocesses_are_running))
  1057. return 0;
  1058. for (lisp p = xsymbol_value (Vprocess_list); consp (p); p = xcdr (p))
  1059. if (xprocess_data (xcar (p)))
  1060. xprocess_data (xcar (p))->signal ();
  1061. return 1;
  1062. }
  1063. void
  1064. process_gc_mark (void (*fn)(lisp))
  1065. {
  1066. for (lisp p = xsymbol_value (Vprocess_list); consp (p); p = xcdr (p))
  1067. {
  1068. Process *pr = xprocess_data (xcar (p));
  1069. if (pr)
  1070. {
  1071. (*fn)(pr->filter ());
  1072. (*fn)(pr->sentinel ());
  1073. (*fn)(pr->marker ());
  1074. }
  1075. }
  1076. }
  1077. lisp
  1078. Fbuffer_process (lisp buffer)
  1079. {
  1080. return Buffer::coerce_to_buffer (buffer)->lprocess;
  1081. }
  1082. lisp
  1083. Fprocess_buffer (lisp process)
  1084. {
  1085. check_process (process);
  1086. return xprocess_buffer (process);
  1087. }
  1088. lisp
  1089. Fprocess_command (lisp process)
  1090. {
  1091. check_process (process);
  1092. return xprocess_command (process);
  1093. }
  1094. lisp
  1095. Fprocess_status (lisp process)
  1096. {
  1097. check_process (process);
  1098. switch (xprocess_status (process))
  1099. {
  1100. case PS_RUN:
  1101. return Krun;
  1102. case PS_EXIT:
  1103. return Kexit;
  1104. default:
  1105. return Qnil;
  1106. }
  1107. }
  1108. lisp
  1109. Fprocess_exit_code (lisp process)
  1110. {
  1111. check_process (process);
  1112. return (xprocess_status (process) == PS_EXIT
  1113. ? make_fixnum (xprocess_exit_code (process)) : Qnil);
  1114. }
  1115. lisp
  1116. Fprocess_incode (lisp process)
  1117. {
  1118. check_process (process);
  1119. return xprocess_incode (process);
  1120. }
  1121. lisp
  1122. Fprocess_outcode (lisp process)
  1123. {
  1124. check_process (process);
  1125. return xprocess_outcode (process);
  1126. }
  1127. lisp
  1128. Fset_process_incode (lisp process, lisp encoding)
  1129. {
  1130. check_process (process);
  1131. xprocess_incode (process) = process_char_encoding (encoding);
  1132. return Qt;
  1133. }
  1134. lisp
  1135. Fset_process_outcode (lisp process, lisp encoding)
  1136. {
  1137. check_process (process);
  1138. xprocess_outcode (process) = process_char_encoding (encoding);
  1139. return Qt;
  1140. }
  1141. lisp
  1142. Fprocess_eol_code (lisp process)
  1143. {
  1144. check_process (process);
  1145. return make_fixnum (xprocess_eol_code (process));
  1146. }
  1147. lisp
  1148. Fset_process_eol_code (lisp process, lisp code)
  1149. {
  1150. check_process (process);
  1151. xprocess_eol_code (process) = process_eol_code (code);
  1152. return Qt;
  1153. }
  1154. lisp
  1155. Fsignal_process (lisp process)
  1156. {
  1157. check_process (process);
  1158. Process *pr = xprocess_data (process);
  1159. if (pr)
  1160. pr->signal ();
  1161. return Qt;
  1162. }
  1163. lisp
  1164. Fkill_process (lisp process)
  1165. {
  1166. check_process (process);
  1167. Process *pr = xprocess_data (process);
  1168. if (pr)
  1169. pr->kill ();
  1170. return Qt;
  1171. }
  1172. class in_process_send_string
  1173. {
  1174. Process &i_pr;
  1175. public:
  1176. in_process_send_string (Process &pr) : i_pr (pr)
  1177. {i_pr.in_send_string_p () = 1;}
  1178. ~in_process_send_string ()
  1179. {
  1180. i_pr.in_send_string_p () = 0;
  1181. i_pr.end_send_string ();
  1182. }
  1183. };
  1184. lisp
  1185. Fprocess_send_string (lisp process, lisp string)
  1186. {
  1187. check_process (process);
  1188. check_string (string);
  1189. Process *pr = xprocess_data (process);
  1190. if (!pr)
  1191. return Qnil;
  1192. Char_input_string_stream is (string);
  1193. process_output_byte_stream os (*pr);
  1194. encoding_output_stream_helper s (xprocess_outcode (process), is, eol_noconv);
  1195. in_process_send_string in (*pr);
  1196. copy_xstream (s, os);
  1197. return Qt;
  1198. }
  1199. lisp
  1200. Fset_process_filter (lisp process, lisp filter)
  1201. {
  1202. check_process (process);
  1203. Process *pr = xprocess_data (process);
  1204. if (!pr)
  1205. return Qnil;
  1206. pr->filter () = filter;
  1207. return Qt;
  1208. }
  1209. lisp
  1210. Fprocess_filter (lisp process)
  1211. {
  1212. check_process (process);
  1213. Process *pr = xprocess_data (process);
  1214. if (!pr)
  1215. return Qnil;
  1216. return pr->filter ();
  1217. }
  1218. lisp
  1219. Fset_process_sentinel (lisp process, lisp sentinel)
  1220. {
  1221. check_process (process);
  1222. Process *pr = xprocess_data (process);
  1223. if (!pr)
  1224. return Qnil;
  1225. pr->sentinel () = sentinel;
  1226. return Qt;
  1227. }
  1228. lisp
  1229. Fprocess_sentinel (lisp process)
  1230. {
  1231. check_process (process);
  1232. Process *pr = xprocess_data (process);
  1233. if (!pr)
  1234. return Qnil;
  1235. return pr->sentinel ();
  1236. }
  1237. lisp
  1238. Fprocess_marker (lisp process)
  1239. {
  1240. check_process (process);
  1241. Process *pr = xprocess_data (process);
  1242. if (!pr)
  1243. return Qnil;
  1244. return pr->marker ();
  1245. }
  1246. static void
  1247. se_error (lisp lpath, int e)
  1248. {
  1249. switch (e)
  1250. {
  1251. case SE_ERR_ASSOCINCOMPLETE:
  1252. FEsimple_error (Eassoc_incomplete, lpath);
  1253. case SE_ERR_DDEBUSY:
  1254. FEdde_busy ();
  1255. case SE_ERR_DDEFAIL:
  1256. FEdde_error (lpath);
  1257. case SE_ERR_DDETIMEOUT:
  1258. FEdde_busy ();
  1259. case SE_ERR_NOASSOC:
  1260. FEsimple_error (Eno_assoc, lpath);
  1261. case SE_ERR_SHARE:
  1262. file_error (ERROR_SHARING_VIOLATION, lpath);
  1263. case 0:
  1264. case SE_ERR_OOM:
  1265. FEstorage_error ();
  1266. case SE_ERR_ACCESSDENIED:
  1267. file_error (ERROR_ACCESS_DENIED, lpath);
  1268. case ERROR_FILE_NOT_FOUND:
  1269. case ERROR_PATH_NOT_FOUND:
  1270. case ERROR_BAD_FORMAT:
  1271. file_error (e, lpath);
  1272. default:
  1273. FEsimple_win32_error (e);
  1274. }
  1275. }
  1276. lisp
  1277. Fshell_execute (lisp lpath, lisp ldir, lisp lparam)
  1278. {
  1279. char *path, *dir, *param;
  1280. if (ldir == Qt)
  1281. {
  1282. check_string (lpath);
  1283. path = (char *)alloca (xstring_length (lpath) * 2 + 1);
  1284. w2s (path, lpath);
  1285. dir = 0;
  1286. }
  1287. else
  1288. {
  1289. path = (char *)alloca (PATH_MAX + 1);
  1290. pathname2cstr (lpath, path);
  1291. map_sl_to_backsl (path);
  1292. dir = (char *)alloca (PATH_MAX + 1);
  1293. if (ldir && ldir != Qnil)
  1294. pathname2cstr (ldir, dir);
  1295. else
  1296. pathname2cstr (Fdirectory_namestring (lpath), dir);
  1297. map_sl_to_backsl (dir);
  1298. }
  1299. if (lparam && lparam != Qnil)
  1300. {
  1301. check_string (lparam);
  1302. param = (char *)alloca (xstring_length (lparam) * 2 + 1);
  1303. w2s (param, lparam);
  1304. }
  1305. else
  1306. param = 0;
  1307. UINT omode = SetErrorMode (0);
  1308. if (dir)
  1309. WINFS::SetCurrentDirectory (dir);
  1310. if (xsymbol_value (Vshell_execute_disregards_shift_key) != Qnil)
  1311. {
  1312. BYTE b[256];
  1313. GetKeyboardState (b);
  1314. b[VK_SHIFT] = 0;
  1315. SetKeyboardState (b);
  1316. }
  1317. DWORD e;
  1318. typedef int (WINAPI *SHELLEXECUTEEX)(SHELLEXECUTEINFO *);
  1319. SHELLEXECUTEEX ex = (xsymbol_value (Vuse_shell_execute_ex) != Qnil
  1320. ? (SHELLEXECUTEEX)GetProcAddress (GetModuleHandle ("shell32.dll"),
  1321. "ShellExecuteExA")
  1322. : 0);
  1323. if (ex)
  1324. {
  1325. SHELLEXECUTEINFO sei = {sizeof sei};
  1326. sei.fMask = SEE_MASK_FLAG_NO_UI;
  1327. sei.hwnd = get_active_window ();
  1328. sei.lpFile = path;
  1329. sei.lpParameters = param;
  1330. sei.lpDirectory = dir;
  1331. sei.nShow = SW_SHOW;
  1332. e = (*ex)(&sei) ? 33 : DWORD (sei.hInstApp);
  1333. }
  1334. else
  1335. e = DWORD (ShellExecute (get_active_window (), "open",
  1336. path, param, dir, SW_SHOWNORMAL));
  1337. if (dir)
  1338. WINFS::SetCurrentDirectory (sysdep.curdir);
  1339. SetErrorMode (omode);
  1340. if (e <= 32)
  1341. se_error (lpath, e);
  1342. return Qt;
  1343. }