Relax expected output in gdb.ada/access_tagged_param.exp test
[external/binutils.git] / gdb / inflow.c
1 /* Low level interface to ptrace, for GDB when running under Unix.
2    Copyright (C) 1986-2018 Free Software Foundation, Inc.
3
4    This file is part of GDB.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 #include "defs.h"
20 #include "frame.h"
21 #include "inferior.h"
22 #include "command.h"
23 #include "serial.h"
24 #include "terminal.h"
25 #include "target.h"
26 #include "gdbthread.h"
27 #include "observer.h"
28 #include <signal.h>
29 #include <fcntl.h>
30 #include "gdb_select.h"
31
32 #include "inflow.h"
33 #include "gdbcmd.h"
34 #ifdef HAVE_TERMIOS_H
35 #include <termios.h>
36 #endif
37 #include "job-control.h"
38
39 #ifdef HAVE_SYS_IOCTL_H
40 #include <sys/ioctl.h>
41 #endif
42
43 #ifndef O_NOCTTY
44 #define O_NOCTTY 0
45 #endif
46
47 static void pass_signal (int);
48
49 static void child_terminal_ours_1 (int);
50 \f
51 /* Record terminal status separately for debugger and inferior.  */
52
53 static struct serial *stdin_serial;
54
55 /* Terminal related info we need to keep track of.  Each inferior
56    holds an instance of this structure --- we save it whenever the
57    corresponding inferior stops, and restore it to the foreground
58    inferior when it resumes.  */
59 struct terminal_info
60 {
61   /* The name of the tty (from the `tty' command) that we gave to the
62      inferior when it was started.  */
63   char *run_terminal;
64
65   /* TTY state.  We save it whenever the inferior stops, and restore
66      it when it resumes.  */
67   serial_ttystate ttystate;
68
69 #ifdef HAVE_TERMIOS_H
70   /* Process group.  Saved and restored just like ttystate.  */
71   pid_t process_group;
72 #endif
73
74   /* fcntl flags.  Saved and restored just like ttystate.  */
75   int tflags;
76 };
77
78 /* Our own tty state, which we restore every time we need to deal with
79    the terminal.  This is set once, when GDB first starts, and then
80    whenever we enter/leave TUI mode (gdb_save_tty_state).  The
81    settings of flags which readline saves and restores are
82    unimportant.  */
83 static struct terminal_info our_terminal_info;
84
85 /* Snapshot of the initial tty state taken during initialization of
86    GDB, before readline/ncurses have had a chance to change it.  This
87    is used as the initial tty state given to each new spawned
88    inferior.  Unlike our_terminal_info, this is only ever set
89    once.  */
90 static serial_ttystate initial_gdb_ttystate;
91
92 static struct terminal_info *get_inflow_inferior_data (struct inferior *);
93
94 /* RAII class used to ignore SIGTTOU in a scope.  */
95
96 class scoped_ignore_sigttou
97 {
98 public:
99   scoped_ignore_sigttou ()
100   {
101 #ifdef SIGTTOU
102     if (job_control)
103       m_osigttou = signal (SIGTTOU, SIG_IGN);
104 #endif
105   }
106
107   ~scoped_ignore_sigttou ()
108   {
109 #ifdef SIGTTOU
110     if (job_control)
111       signal (SIGTTOU, m_osigttou);
112 #endif
113   }
114
115   DISABLE_COPY_AND_ASSIGN (scoped_ignore_sigttou);
116
117 private:
118 #ifdef SIGTTOU
119   sighandler_t m_osigttou = NULL;
120 #endif
121 };
122
123 #ifdef HAVE_TERMIOS_H
124
125 /* Return the process group of the current inferior.  */
126
127 pid_t
128 inferior_process_group (void)
129 {
130   return get_inflow_inferior_data (current_inferior ())->process_group;
131 }
132 #endif
133
134 /* While the inferior is running, we want SIGINT and SIGQUIT to go to the
135    inferior only.  If we have job control, that takes care of it.  If not,
136    we save our handlers in these two variables and set SIGINT and SIGQUIT
137    to SIG_IGN.  */
138
139 static sighandler_t sigint_ours;
140 static sighandler_t sigquit_ours;
141
142 /* The name of the tty (from the `tty' command) that we're giving to
143    the inferior when starting it up.  This is only (and should only
144    be) used as a transient global by new_tty_prefork,
145    create_tty_session, new_tty and new_tty_postfork, all called from
146    fork_inferior, while forking a new child.  */
147 static const char *inferior_thisrun_terminal;
148
149 /* Nonzero if our terminal settings are in effect.  Zero if the
150    inferior's settings are in effect.  Ignored if !gdb_has_a_terminal
151    ().  */
152
153 int terminal_is_ours;
154
155 /* See terminal.h.  */
156
157 void
158 set_initial_gdb_ttystate (void)
159 {
160   /* Note we can't do any of this in _initialize_inflow because at
161      that point stdin_serial has not been created yet.  */
162
163   initial_gdb_ttystate = serial_get_tty_state (stdin_serial);
164
165   if (initial_gdb_ttystate != NULL)
166     {
167       our_terminal_info.ttystate
168         = serial_copy_tty_state (stdin_serial, initial_gdb_ttystate);
169 #ifdef F_GETFL
170       our_terminal_info.tflags = fcntl (0, F_GETFL, 0);
171 #endif
172 #ifdef HAVE_TERMIOS_H
173       our_terminal_info.process_group = tcgetpgrp (0);
174 #endif
175     }
176 }
177
178 /* Does GDB have a terminal (on stdin)?  */
179
180 static int
181 gdb_has_a_terminal (void)
182 {
183   return initial_gdb_ttystate != NULL;
184 }
185
186 /* Macro for printing errors from ioctl operations */
187
188 #define OOPSY(what)     \
189   if (result == -1)     \
190     fprintf_unfiltered(gdb_stderr, "[%s failed in terminal_inferior: %s]\n", \
191             what, safe_strerror (errno))
192
193 /* Initialize the terminal settings we record for the inferior,
194    before we actually run the inferior.  */
195
196 void
197 child_terminal_init (struct target_ops *self)
198 {
199   struct inferior *inf = current_inferior ();
200   struct terminal_info *tinfo = get_inflow_inferior_data (inf);
201
202 #ifdef HAVE_TERMIOS_H
203   /* Store the process group even without a terminal as it is used not
204      only to reset the tty foreground process group, but also to
205      interrupt the inferior.  A child we spawn should be a process
206      group leader (PGID==PID) at this point, though that may not be
207      true if we're attaching to an existing process.  */
208   tinfo->process_group = inf->pid;
209 #endif
210
211   if (gdb_has_a_terminal ())
212     {
213       xfree (tinfo->ttystate);
214       tinfo->ttystate = serial_copy_tty_state (stdin_serial,
215                                                initial_gdb_ttystate);
216
217       /* Make sure that next time we call terminal_inferior (which will be
218          before the program runs, as it needs to be), we install the new
219          process group.  */
220       terminal_is_ours = 1;
221     }
222 }
223
224 /* Save the terminal settings again.  This is necessary for the TUI
225    when it switches to TUI or non-TUI mode;  curses changes the terminal
226    and gdb must be able to restore it correctly.  */
227
228 void
229 gdb_save_tty_state (void)
230 {
231   if (gdb_has_a_terminal ())
232     {
233       xfree (our_terminal_info.ttystate);
234       our_terminal_info.ttystate = serial_get_tty_state (stdin_serial);
235     }
236 }
237
238 /* Put the inferior's terminal settings into effect.
239    This is preparation for starting or resuming the inferior.
240
241    N.B. Targets that want to use this with async support must build that
242    support on top of this (e.g., the caller still needs to remove stdin
243    from the event loop).  E.g., see linux_nat_terminal_inferior.  */
244
245 void
246 child_terminal_inferior (struct target_ops *self)
247 {
248   struct inferior *inf;
249   struct terminal_info *tinfo;
250
251   if (!terminal_is_ours)
252     return;
253
254   inf = current_inferior ();
255   tinfo = get_inflow_inferior_data (inf);
256
257   if (gdb_has_a_terminal ()
258       && tinfo->ttystate != NULL
259       && tinfo->run_terminal == NULL)
260     {
261       int result;
262
263 #ifdef F_GETFL
264       result = fcntl (0, F_SETFL, tinfo->tflags);
265       OOPSY ("fcntl F_SETFL");
266 #endif
267
268       result = serial_set_tty_state (stdin_serial, tinfo->ttystate);
269       OOPSY ("setting tty state");
270
271       if (!job_control)
272         {
273           sigint_ours = signal (SIGINT, SIG_IGN);
274 #ifdef SIGQUIT
275           sigquit_ours = signal (SIGQUIT, SIG_IGN);
276 #endif
277         }
278
279       /* If attach_flag is set, we don't know whether we are sharing a
280          terminal with the inferior or not.  (attaching a process
281          without a terminal is one case where we do not; attaching a
282          process which we ran from the same shell as GDB via `&' is
283          one case where we do, I think (but perhaps this is not
284          `sharing' in the sense that we need to save and restore tty
285          state)).  I don't know if there is any way to tell whether we
286          are sharing a terminal.  So what we do is to go through all
287          the saving and restoring of the tty state, but ignore errors
288          setting the process group, which will happen if we are not
289          sharing a terminal).  */
290
291       if (job_control)
292         {
293 #ifdef HAVE_TERMIOS_H
294           result = tcsetpgrp (0, tinfo->process_group);
295           if (!inf->attach_flag)
296             OOPSY ("tcsetpgrp");
297 #endif
298         }
299     }
300   terminal_is_ours = 0;
301 }
302
303 /* Put some of our terminal settings into effect,
304    enough to get proper results from our output,
305    but do not change into or out of RAW mode
306    so that no input is discarded.
307
308    After doing this, either terminal_ours or terminal_inferior
309    should be called to get back to a normal state of affairs.
310
311    N.B. The implementation is (currently) no different than
312    child_terminal_ours.  See child_terminal_ours_1.  */
313
314 void
315 child_terminal_ours_for_output (struct target_ops *self)
316 {
317   child_terminal_ours_1 (1);
318 }
319
320 /* Put our terminal settings into effect.
321    First record the inferior's terminal settings
322    so they can be restored properly later.
323
324    N.B. Targets that want to use this with async support must build that
325    support on top of this (e.g., the caller still needs to add stdin to the
326    event loop).  E.g., see linux_nat_terminal_ours.  */
327
328 void
329 child_terminal_ours (struct target_ops *self)
330 {
331   child_terminal_ours_1 (0);
332 }
333
334 /* output_only is not used, and should not be used unless we introduce
335    separate terminal_is_ours and terminal_is_ours_for_output
336    flags.  */
337
338 static void
339 child_terminal_ours_1 (int output_only)
340 {
341   struct inferior *inf;
342   struct terminal_info *tinfo;
343
344   if (terminal_is_ours)
345     return;
346
347   terminal_is_ours = 1;
348
349   /* Checking inferior->run_terminal is necessary so that
350      if GDB is running in the background, it won't block trying
351      to do the ioctl()'s below.  Checking gdb_has_a_terminal
352      avoids attempting all the ioctl's when running in batch.  */
353
354   inf = current_inferior ();
355   tinfo = get_inflow_inferior_data (inf);
356
357   if (tinfo->run_terminal != NULL || gdb_has_a_terminal () == 0)
358     return;
359   else
360     {
361       int result ATTRIBUTE_UNUSED;
362
363       /* Ignore SIGTTOU since it will happen when we try to set the
364          terminal's pgrp.  */
365       scoped_ignore_sigttou ignore_sigttou;
366
367       xfree (tinfo->ttystate);
368       tinfo->ttystate = serial_get_tty_state (stdin_serial);
369
370 #ifdef HAVE_TERMIOS_H
371       if (!inf->attach_flag)
372         /* If tcsetpgrp failed in terminal_inferior, this would give us
373            our process group instead of the inferior's.  See
374            terminal_inferior for details.  */
375         tinfo->process_group = tcgetpgrp (0);
376 #endif
377
378       /* Set tty state to our_ttystate.  */
379       serial_set_tty_state (stdin_serial, our_terminal_info.ttystate);
380
381       if (job_control)
382         {
383 #ifdef HAVE_TERMIOS_H
384           result = tcsetpgrp (0, our_terminal_info.process_group);
385 #if 0
386           /* This fails on Ultrix with EINVAL if you run the testsuite
387              in the background with nohup, and then log out.  GDB never
388              used to check for an error here, so perhaps there are other
389              such situations as well.  */
390           if (result == -1)
391             fprintf_unfiltered (gdb_stderr,
392                                 "[tcsetpgrp failed in child_terminal_ours: %s]\n",
393                                 safe_strerror (errno));
394 #endif
395 #endif /* termios */
396         }
397
398       if (!job_control)
399         {
400           signal (SIGINT, sigint_ours);
401 #ifdef SIGQUIT
402           signal (SIGQUIT, sigquit_ours);
403 #endif
404         }
405
406 #ifdef F_GETFL
407       tinfo->tflags = fcntl (0, F_GETFL, 0);
408       result = fcntl (0, F_SETFL, our_terminal_info.tflags);
409 #endif
410     }
411 }
412
413 /* Per-inferior data key.  */
414 static const struct inferior_data *inflow_inferior_data;
415
416 static void
417 inflow_inferior_data_cleanup (struct inferior *inf, void *arg)
418 {
419   struct terminal_info *info = (struct terminal_info *) arg;
420
421   xfree (info->run_terminal);
422   xfree (info->ttystate);
423   xfree (info);
424 }
425
426 /* Get the current svr4 data.  If none is found yet, add it now.  This
427    function always returns a valid object.  */
428
429 static struct terminal_info *
430 get_inflow_inferior_data (struct inferior *inf)
431 {
432   struct terminal_info *info;
433
434   info = (struct terminal_info *) inferior_data (inf, inflow_inferior_data);
435   if (info == NULL)
436     {
437       info = XCNEW (struct terminal_info);
438       set_inferior_data (inf, inflow_inferior_data, info);
439     }
440
441   return info;
442 }
443
444 /* This is a "inferior_exit" observer.  Releases the TERMINAL_INFO member
445    of the inferior structure.  This field is private to inflow.c, and
446    its type is opaque to the rest of GDB.  PID is the target pid of
447    the inferior that is about to be removed from the inferior
448    list.  */
449
450 static void
451 inflow_inferior_exit (struct inferior *inf)
452 {
453   struct terminal_info *info;
454
455   info = (struct terminal_info *) inferior_data (inf, inflow_inferior_data);
456   if (info != NULL)
457     {
458       xfree (info->run_terminal);
459       xfree (info->ttystate);
460       xfree (info);
461       set_inferior_data (inf, inflow_inferior_data, NULL);
462     }
463 }
464
465 void
466 copy_terminal_info (struct inferior *to, struct inferior *from)
467 {
468   struct terminal_info *tinfo_to, *tinfo_from;
469
470   tinfo_to = get_inflow_inferior_data (to);
471   tinfo_from = get_inflow_inferior_data (from);
472
473   xfree (tinfo_to->run_terminal);
474   xfree (tinfo_to->ttystate);
475
476   *tinfo_to = *tinfo_from;
477
478   if (tinfo_from->run_terminal)
479     tinfo_to->run_terminal
480       = xstrdup (tinfo_from->run_terminal);
481
482   if (tinfo_from->ttystate)
483     tinfo_to->ttystate
484       = serial_copy_tty_state (stdin_serial, tinfo_from->ttystate);
485 }
486
487 void
488 info_terminal_command (const char *arg, int from_tty)
489 {
490   target_terminal::info (arg, from_tty);
491 }
492
493 void
494 child_terminal_info (struct target_ops *self, const char *args, int from_tty)
495 {
496   struct inferior *inf;
497   struct terminal_info *tinfo;
498
499   if (!gdb_has_a_terminal ())
500     {
501       printf_filtered (_("This GDB does not control a terminal.\n"));
502       return;
503     }
504
505   if (ptid_equal (inferior_ptid, null_ptid))
506     return;
507
508   inf = current_inferior ();
509   tinfo = get_inflow_inferior_data (inf);
510
511   printf_filtered (_("Inferior's terminal status "
512                      "(currently saved by GDB):\n"));
513
514   /* First the fcntl flags.  */
515   {
516     int flags;
517
518     flags = tinfo->tflags;
519
520     printf_filtered ("File descriptor flags = ");
521
522 #ifndef O_ACCMODE
523 #define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
524 #endif
525     /* (O_ACCMODE) parens are to avoid Ultrix header file bug.  */
526     switch (flags & (O_ACCMODE))
527       {
528       case O_RDONLY:
529         printf_filtered ("O_RDONLY");
530         break;
531       case O_WRONLY:
532         printf_filtered ("O_WRONLY");
533         break;
534       case O_RDWR:
535         printf_filtered ("O_RDWR");
536         break;
537       }
538     flags &= ~(O_ACCMODE);
539
540 #ifdef O_NONBLOCK
541     if (flags & O_NONBLOCK)
542       printf_filtered (" | O_NONBLOCK");
543     flags &= ~O_NONBLOCK;
544 #endif
545
546 #if defined (O_NDELAY)
547     /* If O_NDELAY and O_NONBLOCK are defined to the same thing, we will
548        print it as O_NONBLOCK, which is good cause that is what POSIX
549        has, and the flag will already be cleared by the time we get here.  */
550     if (flags & O_NDELAY)
551       printf_filtered (" | O_NDELAY");
552     flags &= ~O_NDELAY;
553 #endif
554
555     if (flags & O_APPEND)
556       printf_filtered (" | O_APPEND");
557     flags &= ~O_APPEND;
558
559 #if defined (O_BINARY)
560     if (flags & O_BINARY)
561       printf_filtered (" | O_BINARY");
562     flags &= ~O_BINARY;
563 #endif
564
565     if (flags)
566       printf_filtered (" | 0x%x", flags);
567     printf_filtered ("\n");
568   }
569
570 #ifdef HAVE_TERMIOS_H
571   printf_filtered ("Process group = %d\n", (int) tinfo->process_group);
572 #endif
573
574   serial_print_tty_state (stdin_serial, tinfo->ttystate, gdb_stdout);
575 }
576 \f
577 /* NEW_TTY_PREFORK is called before forking a new child process,
578    so we can record the state of ttys in the child to be formed.
579    TTYNAME is null if we are to share the terminal with gdb;
580    or points to a string containing the name of the desired tty.
581
582    NEW_TTY is called in new child processes under Unix, which will
583    become debugger target processes.  This actually switches to
584    the terminal specified in the NEW_TTY_PREFORK call.  */
585
586 void
587 new_tty_prefork (const char *ttyname)
588 {
589   /* Save the name for later, for determining whether we and the child
590      are sharing a tty.  */
591   inferior_thisrun_terminal = ttyname;
592 }
593
594 #if !defined(__GO32__) && !defined(_WIN32)
595 /* If RESULT, assumed to be the return value from a system call, is
596    negative, print the error message indicated by errno and exit.
597    MSG should identify the operation that failed.  */
598 static void
599 check_syscall (const char *msg, int result)
600 {
601   if (result < 0)
602     {
603       print_sys_errmsg (msg, errno);
604       _exit (1);
605     }
606 }
607 #endif
608
609 void
610 new_tty (void)
611 {
612   int tty;
613
614   if (inferior_thisrun_terminal == 0)
615     return;
616 #if !defined(__GO32__) && !defined(_WIN32)
617 #ifdef TIOCNOTTY
618   /* Disconnect the child process from our controlling terminal.  On some
619      systems (SVR4 for example), this may cause a SIGTTOU, so temporarily
620      ignore SIGTTOU.  */
621   tty = open ("/dev/tty", O_RDWR);
622   if (tty > 0)
623     {
624       scoped_ignore_sigttou ignore_sigttou;
625
626       ioctl (tty, TIOCNOTTY, 0);
627       close (tty);
628     }
629 #endif
630
631   /* Now open the specified new terminal.  */
632   tty = open (inferior_thisrun_terminal, O_RDWR | O_NOCTTY);
633   check_syscall (inferior_thisrun_terminal, tty);
634
635   /* Avoid use of dup2; doesn't exist on all systems.  */
636   if (tty != 0)
637     {
638       close (0);
639       check_syscall ("dup'ing tty into fd 0", dup (tty));
640     }
641   if (tty != 1)
642     {
643       close (1);
644       check_syscall ("dup'ing tty into fd 1", dup (tty));
645     }
646   if (tty != 2)
647     {
648       close (2);
649       check_syscall ("dup'ing tty into fd 2", dup (tty));
650     }
651
652 #ifdef TIOCSCTTY
653   /* Make tty our new controlling terminal.  */
654   if (ioctl (tty, TIOCSCTTY, 0) == -1)
655     /* Mention GDB in warning because it will appear in the inferior's
656        terminal instead of GDB's.  */
657     warning (_("GDB: Failed to set controlling terminal: %s"),
658              safe_strerror (errno));
659 #endif
660
661   if (tty > 2)
662     close (tty);
663 #endif /* !go32 && !win32 */
664 }
665
666 /* NEW_TTY_POSTFORK is called after forking a new child process, and
667    adding it to the inferior table, to store the TTYNAME being used by
668    the child, or null if it sharing the terminal with gdb.  */
669
670 void
671 new_tty_postfork (void)
672 {
673   /* Save the name for later, for determining whether we and the child
674      are sharing a tty.  */
675
676   if (inferior_thisrun_terminal)
677     {
678       struct inferior *inf = current_inferior ();
679       struct terminal_info *tinfo = get_inflow_inferior_data (inf);
680
681       tinfo->run_terminal = xstrdup (inferior_thisrun_terminal);
682     }
683
684   inferior_thisrun_terminal = NULL;
685 }
686
687 \f
688 /* Call set_sigint_trap when you need to pass a signal on to an attached
689    process when handling SIGINT.  */
690
691 static void
692 pass_signal (int signo)
693 {
694 #ifndef _WIN32
695   kill (ptid_get_pid (inferior_ptid), SIGINT);
696 #endif
697 }
698
699 static sighandler_t osig;
700 static int osig_set;
701
702 void
703 set_sigint_trap (void)
704 {
705   struct inferior *inf = current_inferior ();
706   struct terminal_info *tinfo = get_inflow_inferior_data (inf);
707
708   if (inf->attach_flag || tinfo->run_terminal)
709     {
710       osig = signal (SIGINT, pass_signal);
711       osig_set = 1;
712     }
713   else
714     osig_set = 0;
715 }
716
717 void
718 clear_sigint_trap (void)
719 {
720   if (osig_set)
721     {
722       signal (SIGINT, osig);
723       osig_set = 0;
724     }
725 }
726 \f
727
728 /* Create a new session if the inferior will run in a different tty.
729    A session is UNIX's way of grouping processes that share a controlling
730    terminal, so a new one is needed if the inferior terminal will be
731    different from GDB's.
732
733    Returns the session id of the new session, 0 if no session was created
734    or -1 if an error occurred.  */
735 pid_t
736 create_tty_session (void)
737 {
738 #ifdef HAVE_SETSID
739   pid_t ret;
740
741   if (!job_control || inferior_thisrun_terminal == 0)
742     return 0;
743
744   ret = setsid ();
745   if (ret == -1)
746     warning (_("Failed to create new terminal session: setsid: %s"),
747              safe_strerror (errno));
748
749   return ret;
750 #else
751   return 0;
752 #endif /* HAVE_SETSID */
753 }
754
755 /* Get all the current tty settings (including whether we have a
756    tty at all!).  We can't do this in _initialize_inflow because
757    serial_fdopen() won't work until the serial_ops_list is
758    initialized, but we don't want to do it lazily either, so
759    that we can guarantee stdin_serial is opened if there is
760    a terminal.  */
761 void
762 initialize_stdin_serial (void)
763 {
764   stdin_serial = serial_fdopen (0);
765 }
766
767 void
768 _initialize_inflow (void)
769 {
770   add_info ("terminal", info_terminal_command,
771             _("Print inferior's saved terminal status."));
772
773   terminal_is_ours = 1;
774
775   /* OK, figure out whether we have job control.  */
776   have_job_control ();
777
778   observer_attach_inferior_exit (inflow_inferior_exit);
779
780   inflow_inferior_data
781     = register_inferior_data_with_cleanup (NULL, inflow_inferior_data_cleanup);
782 }