Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / contrib / gdb / gdb / serial.c
1 /* Generic serial interface routines
2    Copyright 1992, 1993, 1996, 1997 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 2 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, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #include "defs.h"
21 #include <ctype.h>
22 #include "serial.h"
23 #include "gdb_string.h"
24 #include "gdbcmd.h"
25
26 /* Linked list of serial I/O handlers */
27
28 static struct serial_ops *serial_ops_list = NULL;
29
30 /* This is the last serial stream opened.  Used by connect command. */
31
32 static serial_t last_serial_opened = NULL;
33
34 /* Pointer to list of scb's. */
35
36 static serial_t scb_base;
37
38 /* Non-NULL gives filename which contains a recording of the remote session,
39    suitable for playback by gdbserver. */
40
41 static char *serial_logfile = NULL;
42 static GDB_FILE *serial_logfp = NULL;
43
44 static struct serial_ops *serial_interface_lookup PARAMS ((char *));
45 static void serial_logchar PARAMS ((int, int, int));
46 static char logbase_hex[] = "hex";
47 static char logbase_octal[] = "octal";
48 static char logbase_ascii[] = "ascii";
49 static char *logbase_enums[] = {logbase_hex, logbase_octal, logbase_ascii, NULL};
50 static char *serial_logbase = logbase_ascii;
51
52 \f
53 static int serial_current_type = 0;
54
55 /* Log char CH of type CHTYPE, with TIMEOUT */
56
57 /* Define bogus char to represent a BREAK.  Should be careful to choose a value
58    that can't be confused with a normal char, or an error code.  */
59 #define SERIAL_BREAK 1235
60
61 static void
62 serial_logchar (ch_type, ch, timeout)
63      int ch_type;
64      int ch;
65      int timeout;
66 {
67   if (ch_type != serial_current_type)
68     {
69       fprintf_unfiltered (serial_logfp, "\n%c ", ch_type);
70       serial_current_type = ch_type;
71     }
72
73   if (serial_logbase != logbase_ascii)
74     fputc_unfiltered (' ', serial_logfp);
75
76   switch (ch)
77     {
78     case SERIAL_TIMEOUT:
79       fprintf_unfiltered (serial_logfp, "<Timeout: %d seconds>", timeout);
80       return;
81     case SERIAL_ERROR:
82       fprintf_unfiltered (serial_logfp, "<Error: %s>", safe_strerror (errno));
83       return;
84     case SERIAL_EOF:
85       fputs_unfiltered ("<Eof>", serial_logfp);
86       return;
87     case SERIAL_BREAK:
88       fputs_unfiltered ("<Break>", serial_logfp);
89       return;
90     default:
91       if (serial_logbase == logbase_hex)
92         fprintf_unfiltered (serial_logfp, "%02x", ch & 0xff);
93       else if (serial_logbase == logbase_octal)
94         fprintf_unfiltered (serial_logfp, "%03o", ch & 0xff);
95       else
96         switch (ch)
97           {
98           case '\\':    fputs_unfiltered ("\\\\", serial_logfp); break; 
99           case '\b':    fputs_unfiltered ("\\b", serial_logfp); break;  
100           case '\f':    fputs_unfiltered ("\\f", serial_logfp); break;  
101           case '\n':    fputs_unfiltered ("\\n", serial_logfp); break;  
102           case '\r':    fputs_unfiltered ("\\r", serial_logfp); break;  
103           case '\t':    fputs_unfiltered ("\\t", serial_logfp); break;  
104           case '\v':    fputs_unfiltered ("\\v", serial_logfp); break;  
105           default:      fprintf_unfiltered (serial_logfp, isprint (ch) ? "%c" : "\\x%02x", ch & 0xFF); break;
106           }
107     }
108 }
109
110 void
111 serial_log_command (cmd)
112      const char *cmd;
113 {
114   if (!serial_logfp)
115     return;
116
117   serial_current_type = 'c';
118
119   fputs_unfiltered ("\nc ", serial_logfp);
120   fputs_unfiltered (cmd, serial_logfp);
121
122   /* Make sure that the log file is as up-to-date as possible,
123      in case we are getting ready to dump core or something. */
124   gdb_flush (serial_logfp);
125 }
126
127 int
128 serial_write (scb, str, len)
129      serial_t scb;
130      const char *str;
131      int len;
132 {
133   if (serial_logfp != NULL)
134     {
135       int count;
136
137       for (count = 0; count < len; count++)
138         serial_logchar ('w', str[count] & 0xff, 0);
139
140       /* Make sure that the log file is as up-to-date as possible,
141          in case we are getting ready to dump core or something. */
142       gdb_flush (serial_logfp);
143     }
144
145   return (scb -> ops -> write (scb, str, len));
146 }
147
148 int
149 serial_readchar (scb, timeout)
150      serial_t scb;
151      int timeout;
152 {
153   int ch;
154
155   ch = scb -> ops -> readchar (scb, timeout);
156   if (serial_logfp != NULL)
157     {
158       serial_logchar ('r', ch, timeout);
159
160       /* Make sure that the log file is as up-to-date as possible,
161          in case we are getting ready to dump core or something. */
162       gdb_flush (serial_logfp);
163     }
164
165   return (ch);
166 }
167
168 int
169 serial_send_break (scb)
170      serial_t scb;
171 {
172   if (serial_logfp != NULL)
173     serial_logchar ('w', SERIAL_BREAK, 0);
174
175   return (scb -> ops -> send_break (scb));
176 }
177
178 static struct serial_ops *
179 serial_interface_lookup (name)
180      char *name;
181 {
182   struct serial_ops *ops;
183
184   for (ops = serial_ops_list; ops; ops = ops->next)
185     if (strcmp (name, ops->name) == 0)
186       return ops;
187
188   return NULL;
189 }
190
191 void
192 serial_add_interface(optable)
193      struct serial_ops *optable;
194 {
195   optable->next = serial_ops_list;
196   serial_ops_list = optable;
197 }
198
199 /* Open up a device or a network socket, depending upon the syntax of NAME. */
200
201 serial_t
202 serial_open (name)
203      const char *name;
204 {
205   serial_t scb;
206   struct serial_ops *ops;
207
208   for (scb = scb_base; scb; scb = scb->next)
209     if (scb->name && strcmp (scb->name, name) == 0)
210       {
211         scb->refcnt++;
212         return scb;
213       }
214
215   if (strcmp (name, "ocd") == 0)
216     ops = serial_interface_lookup ("ocd");
217   else if (strcmp (name, "pc") == 0)
218     ops = serial_interface_lookup ("pc");
219   else if (strchr (name, ':'))
220     ops = serial_interface_lookup ("tcp");
221   else if (strncmp (name, "lpt", 3) == 0)
222     ops = serial_interface_lookup ("parallel");
223   else
224     ops = serial_interface_lookup ("hardwire");
225
226   if (!ops)
227     return NULL;
228
229   scb = (serial_t)xmalloc (sizeof (struct _serial_t));
230
231   scb->ops = ops;
232
233   scb->bufcnt = 0;
234   scb->bufp = scb->buf;
235
236   if (scb->ops->open(scb, name))
237     {
238       free (scb);
239       return NULL;
240     }
241
242   scb->name = strsave (name);
243   scb->next = scb_base;
244   scb->refcnt = 1;
245   scb_base = scb;
246
247   last_serial_opened = scb;
248
249   if (serial_logfile != NULL)
250     {
251       serial_logfp = gdb_fopen (serial_logfile, "w");
252       if (serial_logfp == NULL)
253         perror_with_name (serial_logfile);
254     }
255
256   return scb;
257 }
258
259 serial_t
260 serial_fdopen (fd)
261      const int fd;
262 {
263   serial_t scb;
264   struct serial_ops *ops;
265
266   for (scb = scb_base; scb; scb = scb->next)
267     if (scb->fd == fd)
268       {
269         scb->refcnt++;
270         return scb;
271       }
272
273   ops = serial_interface_lookup ("hardwire");
274
275   if (!ops)
276     return NULL;
277
278   scb = (serial_t)xmalloc (sizeof (struct _serial_t));
279
280   scb->ops = ops;
281
282   scb->bufcnt = 0;
283   scb->bufp = scb->buf;
284
285   scb->fd = fd;
286
287   scb->name = NULL;
288   scb->next = scb_base;
289   scb->refcnt = 1;
290   scb_base = scb;
291
292   last_serial_opened = scb;
293
294   return scb;
295 }
296
297 void
298 serial_close (scb, really_close)
299      serial_t scb;
300      int really_close;
301 {
302   serial_t tmp_scb;
303
304   last_serial_opened = NULL;
305
306   if (serial_logfp)
307     {
308       fputs_unfiltered ("\nEnd of log\n", serial_logfp);
309       serial_current_type = 0;
310
311       /* XXX - What if serial_logfp == gdb_stdout or gdb_stderr? */
312       gdb_fclose (&serial_logfp); 
313       serial_logfp = NULL;
314     }
315
316 /* This is bogus.  It's not our fault if you pass us a bad scb...!  Rob, you
317    should fix your code instead.  */
318
319   if (!scb)
320     return;
321
322   scb->refcnt--;
323   if (scb->refcnt > 0)
324     return;
325
326   if (really_close)
327     scb->ops->close (scb);
328
329   if (scb->name)
330     free (scb->name);
331
332   if (scb_base == scb)
333     scb_base = scb_base->next;
334   else
335     for (tmp_scb = scb_base; tmp_scb; tmp_scb = tmp_scb->next)
336       {
337         if (tmp_scb->next != scb)
338           continue;
339
340         tmp_scb->next = tmp_scb->next->next;
341         break;
342       }
343
344   free(scb);
345 }
346
347 #if 0
348 /*
349 The connect command is #if 0 because I hadn't thought of an elegant
350 way to wait for I/O on two serial_t's simultaneously.  Two solutions
351 came to mind:
352
353         1) Fork, and have have one fork handle the to user direction,
354            and have the other hand the to target direction.  This
355            obviously won't cut it for MSDOS.
356
357         2) Use something like select.  This assumes that stdin and
358            the target side can both be waited on via the same
359            mechanism.  This may not be true for DOS, if GDB is
360            talking to the target via a TCP socket.
361 -grossman, 8 Jun 93
362 */
363
364 /* Connect the user directly to the remote system.  This command acts just like
365    the 'cu' or 'tip' command.  Use <CR>~. or <CR>~^D to break out.  */
366
367 static serial_t tty_desc;               /* Controlling terminal */
368
369 static void
370 cleanup_tty(ttystate)
371      serial_ttystate ttystate;
372 {
373   printf_unfiltered ("\r\n[Exiting connect mode]\r\n");
374   SERIAL_SET_TTY_STATE (tty_desc, ttystate);
375   free (ttystate);
376   SERIAL_CLOSE (tty_desc);
377 }
378
379 static void
380 connect_command (args, fromtty)
381      char       *args;
382      int        fromtty;
383 {
384   int c;
385   char cur_esc = 0;
386   serial_ttystate ttystate;
387   serial_t port_desc;           /* TTY port */
388
389   dont_repeat();
390
391   if (args)
392     fprintf_unfiltered(gdb_stderr, "This command takes no args.  They have been ignored.\n");
393         
394   printf_unfiltered("[Entering connect mode.  Use ~. or ~^D to escape]\n");
395
396   tty_desc = SERIAL_FDOPEN (0);
397   port_desc = last_serial_opened;
398
399   ttystate = SERIAL_GET_TTY_STATE (tty_desc);
400
401   SERIAL_RAW (tty_desc);
402   SERIAL_RAW (port_desc);
403
404   make_cleanup (cleanup_tty, ttystate);
405
406   while (1)
407     {
408       int mask;
409
410       mask = SERIAL_WAIT_2 (tty_desc, port_desc, -1);
411
412       if (mask & 2)
413         {                       /* tty input */
414           char cx;
415
416           while (1)
417             {
418               c = SERIAL_READCHAR(tty_desc, 0);
419
420               if (c == SERIAL_TIMEOUT)
421                   break;
422
423               if (c < 0)
424                 perror_with_name("connect");
425
426               cx = c;
427               SERIAL_WRITE(port_desc, &cx, 1);
428
429               switch (cur_esc)
430                 {
431                 case 0:
432                   if (c == '\r')
433                     cur_esc = c;
434                   break;
435                 case '\r':
436                   if (c == '~')
437                     cur_esc = c;
438                   else
439                     cur_esc = 0;
440                   break;
441                 case '~':
442                   if (c == '.' || c == '\004')
443                     return;
444                   else
445                     cur_esc = 0;
446                 }
447             }
448         }
449
450       if (mask & 1)
451         {                       /* Port input */
452           char cx;
453
454           while (1)
455             {
456               c = SERIAL_READCHAR(port_desc, 0);
457
458               if (c == SERIAL_TIMEOUT)
459                   break;
460
461               if (c < 0)
462                 perror_with_name("connect");
463
464               cx = c;
465
466               SERIAL_WRITE(tty_desc, &cx, 1);
467             }
468         }
469     }
470 }
471 #endif /* 0 */
472
473 /* VARARGS */
474 void
475 #ifdef ANSI_PROTOTYPES
476 serial_printf (serial_t desc, const char *format, ...)
477 #else
478 serial_printf (va_alist)
479      va_dcl
480 #endif
481 {
482   va_list args;
483   char *buf;
484 #ifdef ANSI_PROTOTYPES
485   va_start (args, format);
486 #else
487   serial_t desc;
488   char *format;
489
490   va_start (args);
491   desc = va_arg (args, serial_t);
492   format = va_arg (args, char *);
493 #endif
494
495   vasprintf (&buf, format, args);
496   SERIAL_WRITE (desc, buf, strlen (buf));
497
498   free (buf);
499   va_end (args);
500 }
501
502 void
503 _initialize_serial ()
504 {
505 #if 0
506   add_com ("connect", class_obscure, connect_command,
507            "Connect the terminal directly up to the command monitor.\n\
508 Use <CR>~. or <CR>~^D to break out.");
509 #endif /* 0 */
510
511   add_show_from_set 
512     (add_set_cmd ("remotelogfile", no_class,
513                   var_filename, (char *) &serial_logfile,
514                   "Set filename for remote session recording.\n\
515 This file is used to record the remote session for future playback\n\
516 by gdbserver.", 
517                   &setlist),
518      &showlist);
519
520   add_show_from_set 
521     (add_set_enum_cmd ("remotelogbase", no_class,
522                        logbase_enums, (char *) &serial_logbase,
523                        "Set numerical base for remote session logging",
524                        &setlist),
525      &showlist);
526 }