c075b0c0b499fb92a039554e059cc22a52c94b92
[deliverable/binutils-gdb.git] / gdb / mi / mi-interp.c
1 /* MI Interpreter Definitions and Commands for GDB, the GNU debugger.
2
3 Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
4 Free Software Foundation, Inc.
5
6 This file is part of GDB.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20
21 #include "defs.h"
22 #include "gdb_string.h"
23 #include "interps.h"
24 #include "event-top.h"
25 #include "event-loop.h"
26 #include "inferior.h"
27 #include "ui-out.h"
28 #include "top.h"
29 #include "exceptions.h"
30 #include "mi-main.h"
31 #include "mi-cmds.h"
32 #include "mi-out.h"
33 #include "mi-console.h"
34 #include "mi-common.h"
35 #include "observer.h"
36 #include "gdbthread.h"
37 #include "solist.h"
38 #include "gdb.h"
39
40 /* These are the interpreter setup, etc. functions for the MI interpreter */
41 static void mi_execute_command_wrapper (char *cmd);
42 static void mi_command_loop (int mi_version);
43
44 /* These are hooks that we put in place while doing interpreter_exec
45 so we can report interesting things that happened "behind the mi's
46 back" in this command */
47 static int mi_interp_query_hook (const char *ctlstr, va_list ap)
48 ATTRIBUTE_PRINTF (1, 0);
49
50 static void mi3_command_loop (void);
51 static void mi2_command_loop (void);
52 static void mi1_command_loop (void);
53
54 static void mi_insert_notify_hooks (void);
55 static void mi_remove_notify_hooks (void);
56 static void mi_on_normal_stop (struct bpstats *bs, int print_frame);
57
58 static void mi_new_thread (struct thread_info *t);
59 static void mi_thread_exit (struct thread_info *t, int silent);
60 static void mi_inferior_added (struct inferior *inf);
61 static void mi_inferior_appeared (struct inferior *inf);
62 static void mi_inferior_exit (struct inferior *inf);
63 static void mi_inferior_removed (struct inferior *inf);
64 static void mi_on_resume (ptid_t ptid);
65 static void mi_solib_loaded (struct so_list *solib);
66 static void mi_solib_unloaded (struct so_list *solib);
67 static void mi_about_to_proceed (void);
68 static void mi_breakpoint_created (struct breakpoint *b);
69 static void mi_breakpoint_deleted (struct breakpoint *b);
70 static void mi_breakpoint_modified (struct breakpoint *b);
71
72 static int report_initial_inferior (struct inferior *inf, void *closure);
73
74 static void *
75 mi_interpreter_init (int top_level)
76 {
77 struct mi_interp *mi = XMALLOC (struct mi_interp);
78
79 /* HACK: We need to force stdout/stderr to point at the console. This avoids
80 any potential side effects caused by legacy code that is still
81 using the TUI / fputs_unfiltered_hook. So we set up output channels for
82 this now, and swap them in when we are run. */
83
84 raw_stdout = stdio_fileopen (stdout);
85
86 /* Create MI channels */
87 mi->out = mi_console_file_new (raw_stdout, "~", '"');
88 mi->err = mi_console_file_new (raw_stdout, "&", '"');
89 mi->log = mi->err;
90 mi->targ = mi_console_file_new (raw_stdout, "@", '"');
91 mi->event_channel = mi_console_file_new (raw_stdout, "=", 0);
92
93 if (top_level)
94 {
95 observer_attach_new_thread (mi_new_thread);
96 observer_attach_thread_exit (mi_thread_exit);
97 observer_attach_inferior_added (mi_inferior_added);
98 observer_attach_inferior_appeared (mi_inferior_appeared);
99 observer_attach_inferior_exit (mi_inferior_exit);
100 observer_attach_inferior_removed (mi_inferior_removed);
101 observer_attach_normal_stop (mi_on_normal_stop);
102 observer_attach_target_resumed (mi_on_resume);
103 observer_attach_solib_loaded (mi_solib_loaded);
104 observer_attach_solib_unloaded (mi_solib_unloaded);
105 observer_attach_about_to_proceed (mi_about_to_proceed);
106 observer_attach_breakpoint_created (mi_breakpoint_created);
107 observer_attach_breakpoint_deleted (mi_breakpoint_deleted);
108 observer_attach_breakpoint_modified (mi_breakpoint_modified);
109
110 /* The initial inferior is created before this function is called, so we
111 need to report it explicitly. Use iteration in case future version
112 of GDB creates more than one inferior up-front. */
113 iterate_over_inferiors (report_initial_inferior, mi);
114 }
115
116 return mi;
117 }
118
119 static int
120 mi_interpreter_resume (void *data)
121 {
122 struct mi_interp *mi = data;
123
124 /* As per hack note in mi_interpreter_init, swap in the output channels... */
125 gdb_setup_readline ();
126
127 /* These overwrite some of the initialization done in
128 _intialize_event_loop. */
129 call_readline = gdb_readline2;
130 input_handler = mi_execute_command_wrapper;
131 add_file_handler (input_fd, stdin_event_handler, 0);
132 async_command_editing_p = 0;
133 /* FIXME: This is a total hack for now. PB's use of the MI
134 implicitly relies on a bug in the async support which allows
135 asynchronous commands to leak through the commmand loop. The bug
136 involves (but is not limited to) the fact that sync_execution was
137 erroneously initialized to 0. Duplicate by initializing it thus
138 here... */
139 sync_execution = 0;
140
141 gdb_stdout = mi->out;
142 /* Route error and log output through the MI */
143 gdb_stderr = mi->err;
144 gdb_stdlog = mi->log;
145 /* Route target output through the MI. */
146 gdb_stdtarg = mi->targ;
147 /* Route target error through the MI as well. */
148 gdb_stdtargerr = mi->targ;
149
150 /* Replace all the hooks that we know about. There really needs to
151 be a better way of doing this... */
152 clear_interpreter_hooks ();
153
154 deprecated_show_load_progress = mi_load_progress;
155
156 /* If we're _the_ interpreter, take control. */
157 if (current_interp_named_p (INTERP_MI1))
158 deprecated_command_loop_hook = mi1_command_loop;
159 else if (current_interp_named_p (INTERP_MI2))
160 deprecated_command_loop_hook = mi2_command_loop;
161 else if (current_interp_named_p (INTERP_MI3))
162 deprecated_command_loop_hook = mi3_command_loop;
163 else
164 deprecated_command_loop_hook = mi2_command_loop;
165
166 return 1;
167 }
168
169 static int
170 mi_interpreter_suspend (void *data)
171 {
172 gdb_disable_readline ();
173 return 1;
174 }
175
176 static struct gdb_exception
177 mi_interpreter_exec (void *data, const char *command)
178 {
179 char *tmp = alloca (strlen (command) + 1);
180
181 strcpy (tmp, command);
182 mi_execute_command_wrapper (tmp);
183 return exception_none;
184 }
185
186 /* Never display the default gdb prompt in mi case. */
187 static int
188 mi_interpreter_prompt_p (void *data)
189 {
190 return 0;
191 }
192
193 void
194 mi_cmd_interpreter_exec (char *command, char **argv, int argc)
195 {
196 struct interp *interp_to_use;
197 int i;
198 char *mi_error_message = NULL;
199 struct cleanup *old_chain;
200
201 if (argc < 2)
202 error (_("-interpreter-exec: "
203 "Usage: -interpreter-exec interp command"));
204
205 interp_to_use = interp_lookup (argv[0]);
206 if (interp_to_use == NULL)
207 error (_("-interpreter-exec: could not find interpreter \"%s\""),
208 argv[0]);
209
210 if (!interp_exec_p (interp_to_use))
211 error (_("-interpreter-exec: interpreter \"%s\" "
212 "does not support command execution"),
213 argv[0]);
214
215 /* Insert the MI out hooks, making sure to also call the interpreter's hooks
216 if it has any. */
217 /* KRS: We shouldn't need this... Events should be installed and they should
218 just ALWAYS fire something out down the MI channel... */
219 mi_insert_notify_hooks ();
220
221 /* Now run the code... */
222
223 old_chain = make_cleanup (null_cleanup, 0);
224 for (i = 1; i < argc; i++)
225 {
226 struct gdb_exception e = interp_exec (interp_to_use, argv[i]);
227
228 if (e.reason < 0)
229 {
230 mi_error_message = xstrdup (e.message);
231 make_cleanup (xfree, mi_error_message);
232 break;
233 }
234 }
235
236 mi_remove_notify_hooks ();
237
238 if (mi_error_message != NULL)
239 error ("%s", mi_error_message);
240 do_cleanups (old_chain);
241 }
242
243 /*
244 * mi_insert_notify_hooks - This inserts a number of hooks that are
245 * meant to produce async-notify ("=") MI messages while running
246 * commands in another interpreter using mi_interpreter_exec. The
247 * canonical use for this is to allow access to the gdb CLI
248 * interpreter from within the MI, while still producing MI style
249 * output when actions in the CLI command change gdb's state.
250 */
251
252 static void
253 mi_insert_notify_hooks (void)
254 {
255 deprecated_query_hook = mi_interp_query_hook;
256 }
257
258 static void
259 mi_remove_notify_hooks (void)
260 {
261 deprecated_query_hook = NULL;
262 }
263
264 static int
265 mi_interp_query_hook (const char *ctlstr, va_list ap)
266 {
267 return 1;
268 }
269
270 static void
271 mi_execute_command_wrapper (char *cmd)
272 {
273 mi_execute_command (cmd, stdin == instream);
274 }
275
276 static void
277 mi1_command_loop (void)
278 {
279 mi_command_loop (1);
280 }
281
282 static void
283 mi2_command_loop (void)
284 {
285 mi_command_loop (2);
286 }
287
288 static void
289 mi3_command_loop (void)
290 {
291 mi_command_loop (3);
292 }
293
294 static void
295 mi_command_loop (int mi_version)
296 {
297 /* Turn off 8 bit strings in quoted output. Any character with the
298 high bit set is printed using C's octal format. */
299 sevenbit_strings = 1;
300 /* Tell the world that we're alive */
301 fputs_unfiltered ("(gdb) \n", raw_stdout);
302 gdb_flush (raw_stdout);
303 start_event_loop ();
304 }
305
306 static void
307 mi_new_thread (struct thread_info *t)
308 {
309 struct mi_interp *mi = top_level_interpreter_data ();
310 struct inferior *inf = find_inferior_pid (ptid_get_pid (t->ptid));
311
312 gdb_assert (inf);
313
314 fprintf_unfiltered (mi->event_channel,
315 "thread-created,id=\"%d\",group-id=\"i%d\"",
316 t->num, inf->num);
317 gdb_flush (mi->event_channel);
318 }
319
320 static void
321 mi_thread_exit (struct thread_info *t, int silent)
322 {
323 struct mi_interp *mi;
324 struct inferior *inf;
325
326 if (silent)
327 return;
328
329 inf = find_inferior_pid (ptid_get_pid (t->ptid));
330
331 mi = top_level_interpreter_data ();
332 target_terminal_ours ();
333 fprintf_unfiltered (mi->event_channel,
334 "thread-exited,id=\"%d\",group-id=\"i%d\"",
335 t->num, inf->num);
336 gdb_flush (mi->event_channel);
337 }
338
339 static void
340 mi_inferior_added (struct inferior *inf)
341 {
342 struct mi_interp *mi = top_level_interpreter_data ();
343
344 target_terminal_ours ();
345 fprintf_unfiltered (mi->event_channel,
346 "thread-group-added,id=\"i%d\"",
347 inf->num);
348 gdb_flush (mi->event_channel);
349 }
350
351 static void
352 mi_inferior_appeared (struct inferior *inf)
353 {
354 struct mi_interp *mi = top_level_interpreter_data ();
355
356 target_terminal_ours ();
357 fprintf_unfiltered (mi->event_channel,
358 "thread-group-started,id=\"i%d\",pid=\"%d\"",
359 inf->num, inf->pid);
360 gdb_flush (mi->event_channel);
361 }
362
363 static void
364 mi_inferior_exit (struct inferior *inf)
365 {
366 struct mi_interp *mi = top_level_interpreter_data ();
367
368 target_terminal_ours ();
369 fprintf_unfiltered (mi->event_channel, "thread-group-exited,id=\"i%d\"",
370 inf->num);
371 gdb_flush (mi->event_channel);
372 }
373
374 static void
375 mi_inferior_removed (struct inferior *inf)
376 {
377 struct mi_interp *mi = top_level_interpreter_data ();
378
379 target_terminal_ours ();
380 fprintf_unfiltered (mi->event_channel,
381 "thread-group-removed,id=\"i%d\"",
382 inf->num);
383 gdb_flush (mi->event_channel);
384 }
385
386 static void
387 mi_on_normal_stop (struct bpstats *bs, int print_frame)
388 {
389 /* Since this can be called when CLI command is executing,
390 using cli interpreter, be sure to use MI uiout for output,
391 not the current one. */
392 struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ());
393
394 if (print_frame)
395 {
396 int core;
397
398 if (uiout != mi_uiout)
399 {
400 /* The normal_stop function has printed frame information into
401 CLI uiout, or some other non-MI uiout. There's no way we
402 can extract proper fields from random uiout object, so we print
403 the frame again. In practice, this can only happen when running
404 a CLI command in MI. */
405 struct ui_out *saved_uiout = uiout;
406
407 uiout = mi_uiout;
408 print_stack_frame (get_selected_frame (NULL), 0, SRC_AND_LOC);
409 uiout = saved_uiout;
410 }
411
412 ui_out_field_int (mi_uiout, "thread-id",
413 pid_to_thread_id (inferior_ptid));
414 if (non_stop)
415 {
416 struct cleanup *back_to = make_cleanup_ui_out_list_begin_end
417 (mi_uiout, "stopped-threads");
418
419 ui_out_field_int (mi_uiout, NULL,
420 pid_to_thread_id (inferior_ptid));
421 do_cleanups (back_to);
422 }
423 else
424 ui_out_field_string (mi_uiout, "stopped-threads", "all");
425
426 core = target_core_of_thread (inferior_ptid);
427 if (core != -1)
428 ui_out_field_int (mi_uiout, "core", core);
429 }
430
431 fputs_unfiltered ("*stopped", raw_stdout);
432 mi_out_put (mi_uiout, raw_stdout);
433 mi_out_rewind (mi_uiout);
434 mi_print_timing_maybe ();
435 fputs_unfiltered ("\n", raw_stdout);
436 gdb_flush (raw_stdout);
437 }
438
439 static void
440 mi_about_to_proceed (void)
441 {
442 /* Suppress output while calling an inferior function. */
443
444 if (!ptid_equal (inferior_ptid, null_ptid))
445 {
446 struct thread_info *tp = inferior_thread ();
447
448 if (tp->control.in_infcall)
449 return;
450 }
451
452 mi_proceeded = 1;
453 }
454
455 /* When non-zero, no MI notifications will be emitted in
456 response to breakpoint change observers. */
457 int mi_suppress_breakpoint_notifications = 0;
458
459 /* Emit notification about a created breakpoint. */
460 static void
461 mi_breakpoint_created (struct breakpoint *b)
462 {
463 struct mi_interp *mi = top_level_interpreter_data ();
464 struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ());
465 struct gdb_exception e;
466
467 if (mi_suppress_breakpoint_notifications)
468 return;
469
470 if (b->number <= 0)
471 return;
472
473 target_terminal_ours ();
474 fprintf_unfiltered (mi->event_channel,
475 "breakpoint-created");
476 /* We want the output from gdb_breakpoint_query to go to
477 mi->event_channel. One approach would be to just
478 call gdb_breakpoint_query, and then use mi_out_put to
479 send the current content of mi_outout into mi->event_channel.
480 However, that will break if anything is output to mi_uiout
481 prior the calling the breakpoint_created notifications.
482 So, we use ui_out_redirect. */
483 ui_out_redirect (mi_uiout, mi->event_channel);
484 TRY_CATCH (e, RETURN_MASK_ERROR)
485 gdb_breakpoint_query (mi_uiout, b->number, NULL);
486 ui_out_redirect (mi_uiout, NULL);
487
488 gdb_flush (mi->event_channel);
489 }
490
491 /* Emit notification about deleted breakpoint. */
492 static void
493 mi_breakpoint_deleted (struct breakpoint *b)
494 {
495 struct mi_interp *mi = top_level_interpreter_data ();
496
497 if (mi_suppress_breakpoint_notifications)
498 return;
499
500 if (b->number <= 0)
501 return;
502
503 target_terminal_ours ();
504
505 fprintf_unfiltered (mi->event_channel, "breakpoint-deleted,id=\"%d\"",
506 b->number);
507
508 gdb_flush (mi->event_channel);
509 }
510
511 /* Emit notification about modified breakpoint. */
512 static void
513 mi_breakpoint_modified (struct breakpoint *b)
514 {
515 struct mi_interp *mi = top_level_interpreter_data ();
516 struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ());
517 struct gdb_exception e;
518
519 if (mi_suppress_breakpoint_notifications)
520 return;
521
522 if (b->number <= 0)
523 return;
524
525 target_terminal_ours ();
526 fprintf_unfiltered (mi->event_channel,
527 "breakpoint-modified");
528 /* We want the output from gdb_breakpoint_query to go to
529 mi->event_channel. One approach would be to just
530 call gdb_breakpoint_query, and then use mi_out_put to
531 send the current content of mi_outout into mi->event_channel.
532 However, that will break if anything is output to mi_uiout
533 prior the calling the breakpoint_created notifications.
534 So, we use ui_out_redirect. */
535 ui_out_redirect (mi_uiout, mi->event_channel);
536 TRY_CATCH (e, RETURN_MASK_ERROR)
537 gdb_breakpoint_query (mi_uiout, b->number, NULL);
538 ui_out_redirect (mi_uiout, NULL);
539
540 gdb_flush (mi->event_channel);
541 }
542
543
544 static int
545 mi_output_running_pid (struct thread_info *info, void *arg)
546 {
547 ptid_t *ptid = arg;
548
549 if (ptid_get_pid (*ptid) == ptid_get_pid (info->ptid))
550 fprintf_unfiltered (raw_stdout,
551 "*running,thread-id=\"%d\"\n",
552 info->num);
553
554 return 0;
555 }
556
557 static int
558 mi_inferior_count (struct inferior *inf, void *arg)
559 {
560 if (inf->pid != 0)
561 {
562 int *count_p = arg;
563 (*count_p)++;
564 }
565
566 return 0;
567 }
568
569 static void
570 mi_on_resume (ptid_t ptid)
571 {
572 struct thread_info *tp = NULL;
573
574 if (ptid_equal (ptid, minus_one_ptid) || ptid_is_pid (ptid))
575 tp = inferior_thread ();
576 else
577 tp = find_thread_ptid (ptid);
578
579 /* Suppress output while calling an inferior function. */
580 if (tp->control.in_infcall)
581 return;
582
583 /* To cater for older frontends, emit ^running, but do it only once
584 per each command. We do it here, since at this point we know
585 that the target was successfully resumed, and in non-async mode,
586 we won't return back to MI interpreter code until the target
587 is done running, so delaying the output of "^running" until then
588 will make it impossible for frontend to know what's going on.
589
590 In future (MI3), we'll be outputting "^done" here. */
591 if (!running_result_record_printed && mi_proceeded)
592 {
593 fprintf_unfiltered (raw_stdout, "%s^running\n",
594 current_token ? current_token : "");
595 }
596
597 if (PIDGET (ptid) == -1)
598 fprintf_unfiltered (raw_stdout, "*running,thread-id=\"all\"\n");
599 else if (ptid_is_pid (ptid))
600 {
601 int count = 0;
602
603 /* Backwards compatibility. If there's only one inferior,
604 output "all", otherwise, output each resumed thread
605 individually. */
606 iterate_over_inferiors (mi_inferior_count, &count);
607
608 if (count == 1)
609 fprintf_unfiltered (raw_stdout, "*running,thread-id=\"all\"\n");
610 else
611 iterate_over_threads (mi_output_running_pid, &ptid);
612 }
613 else
614 {
615 struct thread_info *ti = find_thread_ptid (ptid);
616
617 gdb_assert (ti);
618 fprintf_unfiltered (raw_stdout, "*running,thread-id=\"%d\"\n", ti->num);
619 }
620
621 if (!running_result_record_printed && mi_proceeded)
622 {
623 running_result_record_printed = 1;
624 /* This is what gdb used to do historically -- printing prompt even if
625 it cannot actually accept any input. This will be surely removed
626 for MI3, and may be removed even earler. */
627 /* FIXME: review the use of target_is_async_p here -- is that
628 what we want? */
629 if (!target_is_async_p ())
630 fputs_unfiltered ("(gdb) \n", raw_stdout);
631 }
632 gdb_flush (raw_stdout);
633 }
634
635 static void
636 mi_solib_loaded (struct so_list *solib)
637 {
638 struct mi_interp *mi = top_level_interpreter_data ();
639
640 target_terminal_ours ();
641 if (gdbarch_has_global_solist (target_gdbarch))
642 fprintf_unfiltered (mi->event_channel,
643 "library-loaded,id=\"%s\",target-name=\"%s\","
644 "host-name=\"%s\",symbols-loaded=\"%d\"",
645 solib->so_original_name, solib->so_original_name,
646 solib->so_name, solib->symbols_loaded);
647 else
648 fprintf_unfiltered (mi->event_channel,
649 "library-loaded,id=\"%s\",target-name=\"%s\","
650 "host-name=\"%s\",symbols-loaded=\"%d\","
651 "thread-group=\"i%d\"",
652 solib->so_original_name, solib->so_original_name,
653 solib->so_name, solib->symbols_loaded,
654 current_inferior ()->num);
655
656 gdb_flush (mi->event_channel);
657 }
658
659 static void
660 mi_solib_unloaded (struct so_list *solib)
661 {
662 struct mi_interp *mi = top_level_interpreter_data ();
663
664 target_terminal_ours ();
665 if (gdbarch_has_global_solist (target_gdbarch))
666 fprintf_unfiltered (mi->event_channel,
667 "library-unloaded,id=\"%s\",target-name=\"%s\","
668 "host-name=\"%s\"",
669 solib->so_original_name, solib->so_original_name,
670 solib->so_name);
671 else
672 fprintf_unfiltered (mi->event_channel,
673 "library-unloaded,id=\"%s\",target-name=\"%s\","
674 "host-name=\"%s\",thread-group=\"i%d\"",
675 solib->so_original_name, solib->so_original_name,
676 solib->so_name, current_inferior ()->num);
677
678 gdb_flush (mi->event_channel);
679 }
680
681 static int
682 report_initial_inferior (struct inferior *inf, void *closure)
683 {
684 /* This function is called from mi_intepreter_init, and since
685 mi_inferior_added assumes that inferior is fully initialized
686 and top_level_interpreter_data is set, we cannot call
687 it here. */
688 struct mi_interp *mi = closure;
689
690 target_terminal_ours ();
691 fprintf_unfiltered (mi->event_channel,
692 "thread-group-added,id=\"i%d\"",
693 inf->num);
694 gdb_flush (mi->event_channel);
695 return 0;
696 }
697
698 extern initialize_file_ftype _initialize_mi_interp; /* -Wmissing-prototypes */
699
700 void
701 _initialize_mi_interp (void)
702 {
703 static const struct interp_procs procs =
704 {
705 mi_interpreter_init, /* init_proc */
706 mi_interpreter_resume, /* resume_proc */
707 mi_interpreter_suspend, /* suspend_proc */
708 mi_interpreter_exec, /* exec_proc */
709 mi_interpreter_prompt_p /* prompt_proc_p */
710 };
711
712 /* The various interpreter levels. */
713 interp_add (interp_new (INTERP_MI1, NULL, mi_out_new (1), &procs));
714 interp_add (interp_new (INTERP_MI2, NULL, mi_out_new (2), &procs));
715 interp_add (interp_new (INTERP_MI3, NULL, mi_out_new (3), &procs));
716
717 /* "mi" selects the most recent released version. "mi2" was
718 released as part of GDB 6.0. */
719 interp_add (interp_new (INTERP_MI, NULL, mi_out_new (2), &procs));
720 }
This page took 0.078729 seconds and 3 git commands to generate.