diff options
| author | Glenn Morris | 2008-06-13 08:08:20 +0000 |
|---|---|---|
| committer | Glenn Morris | 2008-06-13 08:08:20 +0000 |
| commit | d888760c951787ccc3a48d7f90bc798ee0996b9b (patch) | |
| tree | 1aabac1ac803d6d2e8614eef3dcf35874e5841de /src | |
| parent | 07d99e7544ffa51339ae1ba1a3785f49247e0c6e (diff) | |
| download | emacs-d888760c951787ccc3a48d7f90bc798ee0996b9b.tar.gz emacs-d888760c951787ccc3a48d7f90bc798ee0996b9b.zip | |
Daniel Engeler <engeler at gmail.com>
These changes add serial port access.
* process.c: Add HAVE_SERIAL.
(Fdelete_process, Fprocess_status, Fset_process_buffer)
(Fset_process_filter, Fset_process_sentinel, Fprocess_contact)
(list_processes_1, select_wrapper, Fstop_process)
(Fcontinue_process, Fprocess_send_eof, kill_buffer_processes)
(status_notify): Modify to handle serial processes.
[HAVE_SERIAL] (Fserial_process_configure)
[HAVE_SERIAL] (make_serial_process_unwind, Fmake_serial_process):
New functions.
* process.h (struct Lisp_Process): Add `type'.
* sysdep.c [HAVE_TERMIOS] (serial_open, serial_configure):
New functions.
* w32.c (_sys_read_ahead, sys_read, sys_write): Modify to handle serial ports.
(serial_open, serial_configure) New functions.
* w32.h: Add FILE_SERIAL.
(struct _child_process): Add ovl_read, ovl_write.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 21 | ||||
| -rw-r--r-- | src/process.c | 496 | ||||
| -rw-r--r-- | src/process.h | 10 | ||||
| -rw-r--r-- | src/sysdep.c | 199 | ||||
| -rw-r--r-- | src/w32.c | 315 | ||||
| -rw-r--r-- | src/w32.h | 3 |
6 files changed, 999 insertions, 45 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index a648ca5cbbf..e575375e560 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,24 @@ | |||
| 1 | 2008-06-13 Daniel Engeler <engeler@gmail.com> | ||
| 2 | |||
| 3 | These changes add serial port access. | ||
| 4 | * process.c: Add HAVE_SERIAL. | ||
| 5 | (Fdelete_process, Fprocess_status, Fset_process_buffer) | ||
| 6 | (Fset_process_filter, Fset_process_sentinel, Fprocess_contact) | ||
| 7 | (list_processes_1, select_wrapper, Fstop_process) | ||
| 8 | (Fcontinue_process, Fprocess_send_eof, kill_buffer_processes) | ||
| 9 | (status_notify): Modify to handle serial processes. | ||
| 10 | [HAVE_SERIAL] (Fserial_process_configure) | ||
| 11 | [HAVE_SERIAL] (make_serial_process_unwind, Fmake_serial_process): | ||
| 12 | New functions. | ||
| 13 | * process.h (struct Lisp_Process): Add `type'. | ||
| 14 | * sysdep.c [HAVE_TERMIOS] (serial_open, serial_configure): | ||
| 15 | New functions. | ||
| 16 | * w32.c (_sys_read_ahead, sys_read, sys_write): Modify to handle | ||
| 17 | serial ports. | ||
| 18 | (serial_open, serial_configure) New functions. | ||
| 19 | * w32.h: Add FILE_SERIAL. | ||
| 20 | (struct _child_process): Add ovl_read, ovl_write. | ||
| 21 | |||
| 1 | 2008-06-13 Kenichi Handa <handa@m17n.org> | 22 | 2008-06-13 Kenichi Handa <handa@m17n.org> |
| 2 | 23 | ||
| 3 | * dispextern.h (enum lface_attribute_index): New member | 24 | * dispextern.h (enum lface_attribute_index): New member |
diff --git a/src/process.c b/src/process.c index 413bd8522b3..1d49f8fc2b4 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -136,9 +136,13 @@ Lisp_Object Qprocessp; | |||
| 136 | Lisp_Object Qrun, Qstop, Qsignal; | 136 | Lisp_Object Qrun, Qstop, Qsignal; |
| 137 | Lisp_Object Qopen, Qclosed, Qconnect, Qfailed, Qlisten; | 137 | Lisp_Object Qopen, Qclosed, Qconnect, Qfailed, Qlisten; |
| 138 | Lisp_Object Qlocal, Qipv4, Qdatagram; | 138 | Lisp_Object Qlocal, Qipv4, Qdatagram; |
| 139 | Lisp_Object Qreal, Qnetwork, Qserial; | ||
| 139 | #ifdef AF_INET6 | 140 | #ifdef AF_INET6 |
| 140 | Lisp_Object Qipv6; | 141 | Lisp_Object Qipv6; |
| 141 | #endif | 142 | #endif |
| 143 | Lisp_Object QCport, QCspeed, QCprocess; | ||
| 144 | Lisp_Object QCbytesize, QCstopbits, QCparity, Qodd, Qeven; | ||
| 145 | Lisp_Object QCflowcontrol, Qhw, Qsw, QCsummary; | ||
| 142 | Lisp_Object QCname, QCbuffer, QChost, QCservice, QCtype; | 146 | Lisp_Object QCname, QCbuffer, QChost, QCservice, QCtype; |
| 143 | Lisp_Object QClocal, QCremote, QCcoding; | 147 | Lisp_Object QClocal, QCremote, QCcoding; |
| 144 | Lisp_Object QCserver, QCnowait, QCnoquery, QCstop; | 148 | Lisp_Object QCserver, QCnowait, QCnoquery, QCstop; |
| @@ -155,15 +159,16 @@ extern Lisp_Object QCfamily; | |||
| 155 | /* QCfilter is defined in keyboard.c. */ | 159 | /* QCfilter is defined in keyboard.c. */ |
| 156 | extern Lisp_Object QCfilter; | 160 | extern Lisp_Object QCfilter; |
| 157 | 161 | ||
| 158 | /* a process object is a network connection when its childp field is neither | ||
| 159 | Qt nor Qnil but is instead a property list (KEY VAL ...). */ | ||
| 160 | |||
| 161 | #ifdef HAVE_SOCKETS | 162 | #ifdef HAVE_SOCKETS |
| 162 | #define NETCONN_P(p) (CONSP (XPROCESS (p)->childp)) | 163 | #define NETCONN_P(p) (EQ (XPROCESS (p)->type, Qnetwork)) |
| 163 | #define NETCONN1_P(p) (CONSP ((p)->childp)) | 164 | #define NETCONN1_P(p) (EQ ((p)->type, Qnetwork)) |
| 165 | #define SERIALCONN_P(p) (EQ (XPROCESS (p)->type, Qserial)) | ||
| 166 | #define SERIALCONN1_P(p) (EQ ((p)->type, Qserial)) | ||
| 164 | #else | 167 | #else |
| 165 | #define NETCONN_P(p) 0 | 168 | #define NETCONN_P(p) 0 |
| 166 | #define NETCONN1_P(p) 0 | 169 | #define NETCONN1_P(p) 0 |
| 170 | #define SERIALCONN_P(p) 0 | ||
| 171 | #define SERIALCONN1_P(p) 0 | ||
| 167 | #endif /* HAVE_SOCKETS */ | 172 | #endif /* HAVE_SOCKETS */ |
| 168 | 173 | ||
| 169 | /* Define first descriptor number available for subprocesses. */ | 174 | /* Define first descriptor number available for subprocesses. */ |
| @@ -186,6 +191,17 @@ extern Lisp_Object QCfilter; | |||
| 186 | 191 | ||
| 187 | extern char *get_operating_system_release (); | 192 | extern char *get_operating_system_release (); |
| 188 | 193 | ||
| 194 | /* Serial processes require termios or Windows. */ | ||
| 195 | #if defined (HAVE_TERMIOS) || defined (WINDOWSNT) | ||
| 196 | #define HAVE_SERIAL | ||
| 197 | #endif | ||
| 198 | |||
| 199 | #ifdef HAVE_SERIAL | ||
| 200 | /* From sysdep.c or w32.c */ | ||
| 201 | extern int serial_open (char *port); | ||
| 202 | extern void serial_configure (struct Lisp_Process *p, Lisp_Object contact); | ||
| 203 | #endif | ||
| 204 | |||
| 189 | #ifndef USE_CRT_DLL | 205 | #ifndef USE_CRT_DLL |
| 190 | extern int errno; | 206 | extern int errno; |
| 191 | #endif | 207 | #endif |
| @@ -784,7 +800,7 @@ nil, indicating the current buffer's process. */) | |||
| 784 | p = XPROCESS (process); | 800 | p = XPROCESS (process); |
| 785 | 801 | ||
| 786 | p->raw_status_new = 0; | 802 | p->raw_status_new = 0; |
| 787 | if (NETCONN1_P (p)) | 803 | if (NETCONN1_P (p) || SERIALCONN1_P (p)) |
| 788 | { | 804 | { |
| 789 | p->status = Fcons (Qexit, Fcons (make_number (0), Qnil)); | 805 | p->status = Fcons (Qexit, Fcons (make_number (0), Qnil)); |
| 790 | p->tick = ++process_tick; | 806 | p->tick = ++process_tick; |
| @@ -861,7 +877,7 @@ nil, indicating the current buffer's process. */) | |||
| 861 | status = p->status; | 877 | status = p->status; |
| 862 | if (CONSP (status)) | 878 | if (CONSP (status)) |
| 863 | status = XCAR (status); | 879 | status = XCAR (status); |
| 864 | if (NETCONN1_P (p)) | 880 | if (NETCONN1_P (p) || SERIALCONN1_P (p)) |
| 865 | { | 881 | { |
| 866 | if (EQ (status, Qexit)) | 882 | if (EQ (status, Qexit)) |
| 867 | status = Qclosed; | 883 | status = Qclosed; |
| @@ -919,7 +935,8 @@ DEFUN ("process-command", Fprocess_command, Sprocess_command, 1, 1, 0, | |||
| 919 | doc: /* Return the command that was executed to start PROCESS. | 935 | doc: /* Return the command that was executed to start PROCESS. |
| 920 | This is a list of strings, the first string being the program executed | 936 | This is a list of strings, the first string being the program executed |
| 921 | and the rest of the strings being the arguments given to it. | 937 | and the rest of the strings being the arguments given to it. |
| 922 | For a non-child channel, this is nil. */) | 938 | For a network or serial process, this is nil (process is running) or t |
| 939 | \(process is stopped). */) | ||
| 923 | (process) | 940 | (process) |
| 924 | register Lisp_Object process; | 941 | register Lisp_Object process; |
| 925 | { | 942 | { |
| @@ -951,7 +968,7 @@ DEFUN ("set-process-buffer", Fset_process_buffer, Sset_process_buffer, | |||
| 951 | CHECK_BUFFER (buffer); | 968 | CHECK_BUFFER (buffer); |
| 952 | p = XPROCESS (process); | 969 | p = XPROCESS (process); |
| 953 | p->buffer = buffer; | 970 | p->buffer = buffer; |
| 954 | if (NETCONN1_P (p)) | 971 | if (NETCONN1_P (p) || SERIALCONN1_P (p)) |
| 955 | p->childp = Fplist_put (p->childp, QCbuffer, buffer); | 972 | p->childp = Fplist_put (p->childp, QCbuffer, buffer); |
| 956 | setup_process_coding_systems (process); | 973 | setup_process_coding_systems (process); |
| 957 | return buffer; | 974 | return buffer; |
| @@ -1018,7 +1035,8 @@ The string argument is normally a multibyte string, except: | |||
| 1018 | FD_CLR (p->infd, &non_keyboard_wait_mask); | 1035 | FD_CLR (p->infd, &non_keyboard_wait_mask); |
| 1019 | } | 1036 | } |
| 1020 | else if (EQ (p->filter, Qt) | 1037 | else if (EQ (p->filter, Qt) |
| 1021 | && !EQ (p->command, Qt)) /* Network process not stopped. */ | 1038 | /* Network or serial process not stopped: */ |
| 1039 | && !EQ (p->command, Qt)) | ||
| 1022 | { | 1040 | { |
| 1023 | FD_SET (p->infd, &input_wait_mask); | 1041 | FD_SET (p->infd, &input_wait_mask); |
| 1024 | FD_SET (p->infd, &non_keyboard_wait_mask); | 1042 | FD_SET (p->infd, &non_keyboard_wait_mask); |
| @@ -1026,7 +1044,7 @@ The string argument is normally a multibyte string, except: | |||
| 1026 | } | 1044 | } |
| 1027 | 1045 | ||
| 1028 | p->filter = filter; | 1046 | p->filter = filter; |
| 1029 | if (NETCONN1_P (p)) | 1047 | if (NETCONN1_P (p) || SERIALCONN1_P (p)) |
| 1030 | p->childp = Fplist_put (p->childp, QCfilter, filter); | 1048 | p->childp = Fplist_put (p->childp, QCfilter, filter); |
| 1031 | setup_process_coding_systems (process); | 1049 | setup_process_coding_systems (process); |
| 1032 | return filter; | 1050 | return filter; |
| @@ -1057,7 +1075,7 @@ It gets two arguments: the process, and a string describing the change. */) | |||
| 1057 | p = XPROCESS (process); | 1075 | p = XPROCESS (process); |
| 1058 | 1076 | ||
| 1059 | p->sentinel = sentinel; | 1077 | p->sentinel = sentinel; |
| 1060 | if (NETCONN1_P (p)) | 1078 | if (NETCONN1_P (p) || SERIALCONN1_P (p)) |
| 1061 | p->childp = Fplist_put (p->childp, QCsentinel, sentinel); | 1079 | p->childp = Fplist_put (p->childp, QCsentinel, sentinel); |
| 1062 | return sentinel; | 1080 | return sentinel; |
| 1063 | } | 1081 | } |
| @@ -1162,11 +1180,13 @@ Lisp_Object Fprocess_datagram_address (); | |||
| 1162 | DEFUN ("process-contact", Fprocess_contact, Sprocess_contact, | 1180 | DEFUN ("process-contact", Fprocess_contact, Sprocess_contact, |
| 1163 | 1, 2, 0, | 1181 | 1, 2, 0, |
| 1164 | doc: /* Return the contact info of PROCESS; t for a real child. | 1182 | doc: /* Return the contact info of PROCESS; t for a real child. |
| 1165 | For a net connection, the value depends on the optional KEY arg. | 1183 | For a network or serial connection, the value depends on the optional |
| 1166 | If KEY is nil, value is a cons cell of the form (HOST SERVICE), | 1184 | KEY arg. If KEY is nil, value is a cons cell of the form (HOST |
| 1167 | if KEY is t, the complete contact information for the connection is | 1185 | SERVICE) for a network connection or (PORT SPEED) for a serial |
| 1168 | returned, else the specific value for the keyword KEY is returned. | 1186 | connection. If KEY is t, the complete contact information for the |
| 1169 | See `make-network-process' for a list of keywords. */) | 1187 | connection is returned, else the specific value for the keyword KEY is |
| 1188 | returned. See `make-network-process' or `make-serial-process' for a | ||
| 1189 | list of keywords. */) | ||
| 1170 | (process, key) | 1190 | (process, key) |
| 1171 | register Lisp_Object process, key; | 1191 | register Lisp_Object process, key; |
| 1172 | { | 1192 | { |
| @@ -1182,11 +1202,14 @@ See `make-network-process' for a list of keywords. */) | |||
| 1182 | Fprocess_datagram_address (process)); | 1202 | Fprocess_datagram_address (process)); |
| 1183 | #endif | 1203 | #endif |
| 1184 | 1204 | ||
| 1185 | if (!NETCONN_P (process) || EQ (key, Qt)) | 1205 | if ((!NETCONN_P (process) && !SERIALCONN_P (process)) || EQ (key, Qt)) |
| 1186 | return contact; | 1206 | return contact; |
| 1187 | if (NILP (key)) | 1207 | if (NILP (key) && NETCONN_P (process)) |
| 1188 | return Fcons (Fplist_get (contact, QChost), | 1208 | return Fcons (Fplist_get (contact, QChost), |
| 1189 | Fcons (Fplist_get (contact, QCservice), Qnil)); | 1209 | Fcons (Fplist_get (contact, QCservice), Qnil)); |
| 1210 | if (NILP (key) && SERIALCONN_P (process)) | ||
| 1211 | return Fcons (Fplist_get (contact, QCport), | ||
| 1212 | Fcons (Fplist_get (contact, QCspeed), Qnil)); | ||
| 1190 | return Fplist_get (contact, key); | 1213 | return Fplist_get (contact, key); |
| 1191 | } | 1214 | } |
| 1192 | 1215 | ||
| @@ -1225,6 +1248,19 @@ a socket connection. */) | |||
| 1225 | return XPROCESS (process)->type; | 1248 | return XPROCESS (process)->type; |
| 1226 | } | 1249 | } |
| 1227 | #endif | 1250 | #endif |
| 1251 | |||
| 1252 | DEFUN ("process-type", Fprocess_type, Sprocess_type, 1, 1, 0, | ||
| 1253 | doc: /* Return the connection type of PROCESS. | ||
| 1254 | The value is either the symbol `real', `network', or `serial'. | ||
| 1255 | PROCESS may be a process, a buffer, the name of a process or buffer, or | ||
| 1256 | nil, indicating the current buffer's process. */) | ||
| 1257 | (process) | ||
| 1258 | Lisp_Object process; | ||
| 1259 | { | ||
| 1260 | Lisp_Object proc; | ||
| 1261 | proc = get_process (process); | ||
| 1262 | return XPROCESS (proc)->type; | ||
| 1263 | } | ||
| 1228 | 1264 | ||
| 1229 | #ifdef HAVE_SOCKETS | 1265 | #ifdef HAVE_SOCKETS |
| 1230 | DEFUN ("format-network-address", Fformat_network_address, Sformat_network_address, | 1266 | DEFUN ("format-network-address", Fformat_network_address, Sformat_network_address, |
| @@ -1325,7 +1361,7 @@ list_processes_1 (query_only) | |||
| 1325 | 1361 | ||
| 1326 | proc = Fcdr (XCAR (tail)); | 1362 | proc = Fcdr (XCAR (tail)); |
| 1327 | p = XPROCESS (proc); | 1363 | p = XPROCESS (proc); |
| 1328 | if (NILP (p->childp)) | 1364 | if (NILP (p->type)) |
| 1329 | continue; | 1365 | continue; |
| 1330 | if (!NILP (query_only) && p->kill_without_query) | 1366 | if (!NILP (query_only) && p->kill_without_query) |
| 1331 | continue; | 1367 | continue; |
| @@ -1393,7 +1429,7 @@ list_processes_1 (query_only) | |||
| 1393 | 1429 | ||
| 1394 | proc = Fcdr (XCAR (tail)); | 1430 | proc = Fcdr (XCAR (tail)); |
| 1395 | p = XPROCESS (proc); | 1431 | p = XPROCESS (proc); |
| 1396 | if (NILP (p->childp)) | 1432 | if (NILP (p->type)) |
| 1397 | continue; | 1433 | continue; |
| 1398 | if (!NILP (query_only) && p->kill_without_query) | 1434 | if (!NILP (query_only) && p->kill_without_query) |
| 1399 | continue; | 1435 | continue; |
| @@ -1418,7 +1454,7 @@ list_processes_1 (query_only) | |||
| 1418 | #endif | 1454 | #endif |
| 1419 | Fprinc (symbol, Qnil); | 1455 | Fprinc (symbol, Qnil); |
| 1420 | } | 1456 | } |
| 1421 | else if (NETCONN1_P (p)) | 1457 | else if (NETCONN1_P (p) || SERIALCONN1_P (p)) |
| 1422 | { | 1458 | { |
| 1423 | if (EQ (symbol, Qexit)) | 1459 | if (EQ (symbol, Qexit)) |
| 1424 | write_string ("closed", -1); | 1460 | write_string ("closed", -1); |
| @@ -1429,6 +1465,10 @@ list_processes_1 (query_only) | |||
| 1429 | else | 1465 | else |
| 1430 | Fprinc (symbol, Qnil); | 1466 | Fprinc (symbol, Qnil); |
| 1431 | } | 1467 | } |
| 1468 | else if (SERIALCONN1_P (p)) | ||
| 1469 | { | ||
| 1470 | write_string ("running", -1); | ||
| 1471 | } | ||
| 1432 | else | 1472 | else |
| 1433 | Fprinc (symbol, Qnil); | 1473 | Fprinc (symbol, Qnil); |
| 1434 | 1474 | ||
| @@ -1493,6 +1533,22 @@ list_processes_1 (query_only) | |||
| 1493 | (STRINGP (host) ? (char *)SDATA (host) : "?")); | 1533 | (STRINGP (host) ? (char *)SDATA (host) : "?")); |
| 1494 | insert_string (tembuf); | 1534 | insert_string (tembuf); |
| 1495 | } | 1535 | } |
| 1536 | else if (SERIALCONN1_P (p)) | ||
| 1537 | { | ||
| 1538 | Lisp_Object port = Fplist_get (p->childp, QCport); | ||
| 1539 | Lisp_Object speed = Fplist_get (p->childp, QCspeed); | ||
| 1540 | insert_string ("(serial port "); | ||
| 1541 | if (STRINGP (port)) | ||
| 1542 | insert_string (SDATA (port)); | ||
| 1543 | else | ||
| 1544 | insert_string ("?"); | ||
| 1545 | if (INTEGERP (speed)) | ||
| 1546 | { | ||
| 1547 | sprintf (tembuf, " at %d b/s", XINT (speed)); | ||
| 1548 | insert_string (tembuf); | ||
| 1549 | } | ||
| 1550 | insert_string (")\n"); | ||
| 1551 | } | ||
| 1496 | else | 1552 | else |
| 1497 | { | 1553 | { |
| 1498 | tem = p->command; | 1554 | tem = p->command; |
| @@ -1619,6 +1675,7 @@ usage: (start-process NAME BUFFER PROGRAM &rest PROGRAM-ARGS) */) | |||
| 1619 | 1675 | ||
| 1620 | XPROCESS (proc)->childp = Qt; | 1676 | XPROCESS (proc)->childp = Qt; |
| 1621 | XPROCESS (proc)->plist = Qnil; | 1677 | XPROCESS (proc)->plist = Qnil; |
| 1678 | XPROCESS (proc)->type = Qreal; | ||
| 1622 | XPROCESS (proc)->buffer = buffer; | 1679 | XPROCESS (proc)->buffer = buffer; |
| 1623 | XPROCESS (proc)->sentinel = Qnil; | 1680 | XPROCESS (proc)->sentinel = Qnil; |
| 1624 | XPROCESS (proc)->filter = Qnil; | 1681 | XPROCESS (proc)->filter = Qnil; |
| @@ -2656,6 +2713,312 @@ unwind_request_sigio (dummy) | |||
| 2656 | } | 2713 | } |
| 2657 | #endif | 2714 | #endif |
| 2658 | 2715 | ||
| 2716 | #ifdef HAVE_SERIAL | ||
| 2717 | DEFUN ("serial-process-configure", | ||
| 2718 | Fserial_process_configure, | ||
| 2719 | Sserial_process_configure, | ||
| 2720 | 0, MANY, 0, | ||
| 2721 | doc: /* Configure speed, bytesize, etc. of a serial process. | ||
| 2722 | |||
| 2723 | Arguments are specified as keyword/argument pairs. Attributes that | ||
| 2724 | are not given are re-initialized from the process's current | ||
| 2725 | configuration (available via the function `process-contact') or set to | ||
| 2726 | reasonable default values. The following arguments are defined: | ||
| 2727 | |||
| 2728 | :process PROCESS | ||
| 2729 | :name NAME | ||
| 2730 | :buffer BUFFER | ||
| 2731 | :port PORT | ||
| 2732 | -- Any of these arguments can be given to identify the process that is | ||
| 2733 | to be configured. If none of these arguments is given, the current | ||
| 2734 | buffer's process is used. | ||
| 2735 | |||
| 2736 | :speed SPEED -- SPEED is the speed of the serial port in bits per | ||
| 2737 | second, also called baud rate. Any value can be given for SPEED, but | ||
| 2738 | most serial ports work only at a few defined values between 1200 and | ||
| 2739 | 115200, with 9600 being the most common value. If SPEED is nil, the | ||
| 2740 | serial port is not configured any further, i.e., all other arguments | ||
| 2741 | are ignored. This may be useful for special serial ports such as | ||
| 2742 | Bluetooth-to-serial converters which can only be configured through AT | ||
| 2743 | commands. A value of nil for SPEED can be used only when passed | ||
| 2744 | through `make-serial-process' or `serial-term'. | ||
| 2745 | |||
| 2746 | :bytesize BYTESIZE -- BYTESIZE is the number of bits per byte, which | ||
| 2747 | can be 7 or 8. If BYTESIZE is not given or nil, a value of 8 is used. | ||
| 2748 | |||
| 2749 | :parity PARITY -- PARITY can be nil (don't use parity), the symbol | ||
| 2750 | `odd' (use odd parity), or the symbol `even' (use even parity). If | ||
| 2751 | PARITY is not given, no parity is used. | ||
| 2752 | |||
| 2753 | :stopbits STOPBITS -- STOPBITS is the number of stopbits used to | ||
| 2754 | terminate a byte transmission. STOPBITS can be 1 or 2. If STOPBITS | ||
| 2755 | is not given or nil, 1 stopbit is used. | ||
| 2756 | |||
| 2757 | :flowcontrol FLOWCONTROL -- FLOWCONTROL determines the type of | ||
| 2758 | flowcontrol to be used, which is either nil (don't use flowcontrol), | ||
| 2759 | the symbol `hw' (use RTS/CTS hardware flowcontrol), or the symbol `sw' | ||
| 2760 | \(use XON/XOFF software flowcontrol). If FLOWCONTROL is not given, no | ||
| 2761 | flowcontrol is used. | ||
| 2762 | |||
| 2763 | `serial-process-configure' is called by `make-serial-process' for the | ||
| 2764 | initial configuration of the serial port. | ||
| 2765 | |||
| 2766 | Examples: | ||
| 2767 | |||
| 2768 | \(serial-process-configure :process "/dev/ttyS0" :speed 1200) | ||
| 2769 | |||
| 2770 | \(serial-process-configure | ||
| 2771 | :buffer "COM1" :stopbits 1 :parity 'odd :flowcontrol 'hw) | ||
| 2772 | |||
| 2773 | \(serial-process-configure :port "\\\\.\\COM13" :bytesize 7) | ||
| 2774 | |||
| 2775 | usage: (serial-process-configure &rest ARGS) */) | ||
| 2776 | (nargs, args) | ||
| 2777 | int nargs; | ||
| 2778 | Lisp_Object *args; | ||
| 2779 | { | ||
| 2780 | struct Lisp_Process *p; | ||
| 2781 | Lisp_Object contact = Qnil; | ||
| 2782 | Lisp_Object proc = Qnil; | ||
| 2783 | struct gcpro gcpro1; | ||
| 2784 | |||
| 2785 | contact = Flist (nargs, args); | ||
| 2786 | GCPRO1 (contact); | ||
| 2787 | |||
| 2788 | proc = Fplist_get (contact, QCprocess); | ||
| 2789 | if (NILP (proc)) | ||
| 2790 | proc = Fplist_get (contact, QCname); | ||
| 2791 | if (NILP (proc)) | ||
| 2792 | proc = Fplist_get (contact, QCbuffer); | ||
| 2793 | if (NILP (proc)) | ||
| 2794 | proc = Fplist_get (contact, QCport); | ||
| 2795 | proc = get_process (proc); | ||
| 2796 | p = XPROCESS (proc); | ||
| 2797 | if (p->type != Qserial) | ||
| 2798 | error ("Not a serial process"); | ||
| 2799 | |||
| 2800 | if (NILP (Fplist_get (p->childp, QCspeed))) | ||
| 2801 | { | ||
| 2802 | UNGCPRO; | ||
| 2803 | return Qnil; | ||
| 2804 | } | ||
| 2805 | |||
| 2806 | serial_configure (p, contact); | ||
| 2807 | |||
| 2808 | UNGCPRO; | ||
| 2809 | return Qnil; | ||
| 2810 | } | ||
| 2811 | #endif /* HAVE_SERIAL */ | ||
| 2812 | |||
| 2813 | #ifdef HAVE_SERIAL | ||
| 2814 | /* Used by make-serial-process to recover from errors. */ | ||
| 2815 | Lisp_Object make_serial_process_unwind (Lisp_Object proc) | ||
| 2816 | { | ||
| 2817 | if (!PROCESSP (proc)) | ||
| 2818 | abort (); | ||
| 2819 | remove_process (proc); | ||
| 2820 | return Qnil; | ||
| 2821 | } | ||
| 2822 | #endif /* HAVE_SERIAL */ | ||
| 2823 | |||
| 2824 | #ifdef HAVE_SERIAL | ||
| 2825 | DEFUN ("make-serial-process", Fmake_serial_process, Smake_serial_process, | ||
| 2826 | 0, MANY, 0, | ||
| 2827 | doc: /* Create and return a serial port process. | ||
| 2828 | |||
| 2829 | In Emacs, serial port connections are represented by process objects, | ||
| 2830 | so input and output work as for subprocesses, and `delete-process' | ||
| 2831 | closes a serial port connection. However, a serial process has no | ||
| 2832 | process id, it cannot be signaled, and the status codes are different | ||
| 2833 | from normal processes. | ||
| 2834 | |||
| 2835 | `make-serial-process' creates a process and a buffer, on which you | ||
| 2836 | probably want to use `process-send-string'. Try \\[serial-term] for | ||
| 2837 | an interactive terminal. See below for examples. | ||
| 2838 | |||
| 2839 | Arguments are specified as keyword/argument pairs. The following | ||
| 2840 | arguments are defined: | ||
| 2841 | |||
| 2842 | :port PORT -- (mandatory) PORT is the path or name of the serial port. | ||
| 2843 | For example, this could be "/dev/ttyS0" on Unix. On Windows, this | ||
| 2844 | could be "COM1", or "\\\\.\\COM10" for ports higher than COM9 (double | ||
| 2845 | the backslashes in strings). | ||
| 2846 | |||
| 2847 | :speed SPEED -- (mandatory) is handled by `serial-process-configure', | ||
| 2848 | which is called by `make-serial-process'. | ||
| 2849 | |||
| 2850 | :name NAME -- NAME is the name of the process. If NAME is not given, | ||
| 2851 | the value of PORT is used. | ||
| 2852 | |||
| 2853 | :buffer BUFFER -- BUFFER is the buffer (or buffer-name) to associate | ||
| 2854 | with the process. Process output goes at the end of that buffer, | ||
| 2855 | unless you specify an output stream or filter function to handle the | ||
| 2856 | output. If BUFFER is not given, the value of NAME is used. | ||
| 2857 | |||
| 2858 | :coding CODING -- If CODING is a symbol, it specifies the coding | ||
| 2859 | system used for both reading and writing for this process. If CODING | ||
| 2860 | is a cons (DECODING . ENCODING), DECODING is used for reading, and | ||
| 2861 | ENCODING is used for writing. | ||
| 2862 | |||
| 2863 | :noquery BOOL -- When exiting Emacs, query the user if BOOL is nil and | ||
| 2864 | the process is running. If BOOL is not given, query before exiting. | ||
| 2865 | |||
| 2866 | :stop BOOL -- Start process in the `stopped' state if BOOL is non-nil. | ||
| 2867 | In the stopped state, a serial process does not accept incoming data, | ||
| 2868 | but you can send outgoing data. The stopped state is cleared by | ||
| 2869 | `continue-process' and set by `stop-process'. | ||
| 2870 | |||
| 2871 | :filter FILTER -- Install FILTER as the process filter. | ||
| 2872 | |||
| 2873 | :sentinel SENTINEL -- Install SENTINEL as the process sentinel. | ||
| 2874 | |||
| 2875 | :plist PLIST -- Install PLIST as the initial plist of the process. | ||
| 2876 | |||
| 2877 | :speed | ||
| 2878 | :bytesize | ||
| 2879 | :parity | ||
| 2880 | :stopbits | ||
| 2881 | :flowcontrol | ||
| 2882 | -- These arguments are handled by `serial-process-configure', which is | ||
| 2883 | called by `make-serial-process'. | ||
| 2884 | |||
| 2885 | The original argument list, possibly modified by later configuration, | ||
| 2886 | is available via the function `process-contact'. | ||
| 2887 | |||
| 2888 | Examples: | ||
| 2889 | |||
| 2890 | \(make-serial-process :port "/dev/ttyS0" :speed 9600) | ||
| 2891 | |||
| 2892 | \(make-serial-process :port "COM1" :speed 115200 :stopbits 2) | ||
| 2893 | |||
| 2894 | \(make-serial-process :port "\\\\.\\COM13" :speed 1200 :bytesize 7 :parity 'odd) | ||
| 2895 | |||
| 2896 | \(make-serial-process :port "/dev/tty.BlueConsole-SPP-1" :speed nil) | ||
| 2897 | |||
| 2898 | usage: (make-serial-process &rest ARGS) */) | ||
| 2899 | (nargs, args) | ||
| 2900 | int nargs; | ||
| 2901 | Lisp_Object *args; | ||
| 2902 | { | ||
| 2903 | int fd = -1; | ||
| 2904 | Lisp_Object proc, contact, port; | ||
| 2905 | struct Lisp_Process *p; | ||
| 2906 | struct gcpro gcpro1; | ||
| 2907 | Lisp_Object name, buffer; | ||
| 2908 | Lisp_Object tem, val; | ||
| 2909 | int specpdl_count = -1; | ||
| 2910 | |||
| 2911 | if (nargs == 0) | ||
| 2912 | return Qnil; | ||
| 2913 | |||
| 2914 | contact = Flist (nargs, args); | ||
| 2915 | GCPRO1 (contact); | ||
| 2916 | |||
| 2917 | port = Fplist_get (contact, QCport); | ||
| 2918 | if (NILP (port)) | ||
| 2919 | error ("No port specified"); | ||
| 2920 | CHECK_STRING (port); | ||
| 2921 | |||
| 2922 | if (NILP (Fplist_member (contact, QCspeed))) | ||
| 2923 | error (":speed not specified"); | ||
| 2924 | if (!NILP (Fplist_get (contact, QCspeed))) | ||
| 2925 | CHECK_NUMBER (Fplist_get (contact, QCspeed)); | ||
| 2926 | |||
| 2927 | name = Fplist_get (contact, QCname); | ||
| 2928 | if (NILP (name)) | ||
| 2929 | name = port; | ||
| 2930 | CHECK_STRING (name); | ||
| 2931 | proc = make_process (name); | ||
| 2932 | specpdl_count = SPECPDL_INDEX (); | ||
| 2933 | record_unwind_protect (make_serial_process_unwind, proc); | ||
| 2934 | p = XPROCESS (proc); | ||
| 2935 | |||
| 2936 | fd = serial_open ((char*) SDATA (port)); | ||
| 2937 | p->infd = fd; | ||
| 2938 | p->outfd = fd; | ||
| 2939 | if (fd > max_process_desc) | ||
| 2940 | max_process_desc = fd; | ||
| 2941 | chan_process[fd] = proc; | ||
| 2942 | |||
| 2943 | buffer = Fplist_get (contact, QCbuffer); | ||
| 2944 | if (NILP (buffer)) | ||
| 2945 | buffer = name; | ||
| 2946 | buffer = Fget_buffer_create (buffer); | ||
| 2947 | p->buffer = buffer; | ||
| 2948 | |||
| 2949 | p->childp = contact; | ||
| 2950 | p->plist = Fcopy_sequence (Fplist_get (contact, QCplist)); | ||
| 2951 | p->type = Qserial; | ||
| 2952 | p->sentinel = Fplist_get (contact, QCsentinel); | ||
| 2953 | p->filter = Fplist_get (contact, QCfilter); | ||
| 2954 | p->log = Qnil; | ||
| 2955 | if (tem = Fplist_get (contact, QCnoquery), !NILP (tem)) | ||
| 2956 | p->kill_without_query = 1; | ||
| 2957 | if (tem = Fplist_get (contact, QCstop), !NILP (tem)) | ||
| 2958 | p->command = Qt; | ||
| 2959 | p->pty_flag = 0; | ||
| 2960 | |||
| 2961 | if (!EQ (p->command, Qt)) | ||
| 2962 | { | ||
| 2963 | FD_SET (fd, &input_wait_mask); | ||
| 2964 | FD_SET (fd, &non_keyboard_wait_mask); | ||
| 2965 | } | ||
| 2966 | |||
| 2967 | if (BUFFERP (buffer)) | ||
| 2968 | { | ||
| 2969 | set_marker_both (p->mark, buffer, | ||
| 2970 | BUF_ZV (XBUFFER (buffer)), | ||
| 2971 | BUF_ZV_BYTE (XBUFFER (buffer))); | ||
| 2972 | } | ||
| 2973 | |||
| 2974 | tem = Fplist_member (contact, QCcoding); | ||
| 2975 | if (!NILP (tem) && (!CONSP (tem) || !CONSP (XCDR (tem)))) | ||
| 2976 | tem = Qnil; | ||
| 2977 | |||
| 2978 | val = Qnil; | ||
| 2979 | if (!NILP (tem)) | ||
| 2980 | { | ||
| 2981 | val = XCAR (XCDR (tem)); | ||
| 2982 | if (CONSP (val)) | ||
| 2983 | val = XCAR (val); | ||
| 2984 | } | ||
| 2985 | else if (!NILP (Vcoding_system_for_read)) | ||
| 2986 | val = Vcoding_system_for_read; | ||
| 2987 | else if ((!NILP (buffer) && NILP (XBUFFER (buffer)->enable_multibyte_characters)) | ||
| 2988 | || (NILP (buffer) && NILP (buffer_defaults.enable_multibyte_characters))) | ||
| 2989 | val = Qnil; | ||
| 2990 | p->decode_coding_system = val; | ||
| 2991 | |||
| 2992 | val = Qnil; | ||
| 2993 | if (!NILP (tem)) | ||
| 2994 | { | ||
| 2995 | val = XCAR (XCDR (tem)); | ||
| 2996 | if (CONSP (val)) | ||
| 2997 | val = XCDR (val); | ||
| 2998 | } | ||
| 2999 | else if (!NILP (Vcoding_system_for_write)) | ||
| 3000 | val = Vcoding_system_for_write; | ||
| 3001 | else if ((!NILP (buffer) && NILP (XBUFFER (buffer)->enable_multibyte_characters)) | ||
| 3002 | || (NILP (buffer) && NILP (buffer_defaults.enable_multibyte_characters))) | ||
| 3003 | val = Qnil; | ||
| 3004 | p->encode_coding_system = val; | ||
| 3005 | |||
| 3006 | setup_process_coding_systems (proc); | ||
| 3007 | p->decoding_buf = make_uninit_string (0); | ||
| 3008 | p->decoding_carryover = 0; | ||
| 3009 | p->encoding_buf = make_uninit_string (0); | ||
| 3010 | p->inherit_coding_system_flag | ||
| 3011 | = !(!NILP (tem) || NILP (buffer) || !inherit_process_coding_system); | ||
| 3012 | |||
| 3013 | Fserial_process_configure(nargs, args); | ||
| 3014 | |||
| 3015 | specpdl_ptr = specpdl + specpdl_count; | ||
| 3016 | |||
| 3017 | UNGCPRO; | ||
| 3018 | return proc; | ||
| 3019 | } | ||
| 3020 | #endif /* HAVE_SERIAL */ | ||
| 3021 | |||
| 2659 | /* Create a network stream/datagram client/server process. Treated | 3022 | /* Create a network stream/datagram client/server process. Treated |
| 2660 | exactly like a normal process when reading and writing. Primary | 3023 | exactly like a normal process when reading and writing. Primary |
| 2661 | differences are in status display and process deletion. A network | 3024 | differences are in status display and process deletion. A network |
| @@ -3395,6 +3758,7 @@ usage: (make-network-process &rest ARGS) */) | |||
| 3395 | 3758 | ||
| 3396 | p->childp = contact; | 3759 | p->childp = contact; |
| 3397 | p->plist = Fcopy_sequence (Fplist_get (contact, QCplist)); | 3760 | p->plist = Fcopy_sequence (Fplist_get (contact, QCplist)); |
| 3761 | p->type = Qnetwork; | ||
| 3398 | 3762 | ||
| 3399 | p->buffer = buffer; | 3763 | p->buffer = buffer; |
| 3400 | p->sentinel = sentinel; | 3764 | p->sentinel = sentinel; |
| @@ -4113,6 +4477,7 @@ server_accept_connection (server, channel) | |||
| 4113 | 4477 | ||
| 4114 | p->childp = contact; | 4478 | p->childp = contact; |
| 4115 | p->plist = Fcopy_sequence (ps->plist); | 4479 | p->plist = Fcopy_sequence (ps->plist); |
| 4480 | p->type = Qnetwork; | ||
| 4116 | 4481 | ||
| 4117 | p->buffer = buffer; | 4482 | p->buffer = buffer; |
| 4118 | p->sentinel = ps->sentinel; | 4483 | p->sentinel = ps->sentinel; |
| @@ -4811,7 +5176,7 @@ wait_reading_process_output (time_limit, microsecs, read_kbd, do_display, | |||
| 4811 | available now and a closed pipe. | 5176 | available now and a closed pipe. |
| 4812 | With luck, a closed pipe will be accompanied by | 5177 | With luck, a closed pipe will be accompanied by |
| 4813 | subprocess termination and SIGCHLD. */ | 5178 | subprocess termination and SIGCHLD. */ |
| 4814 | else if (nread == 0 && !NETCONN_P (proc)) | 5179 | else if (nread == 0 && !NETCONN_P (proc) && !SERIALCONN_P (proc)) |
| 4815 | ; | 5180 | ; |
| 4816 | #endif /* O_NDELAY */ | 5181 | #endif /* O_NDELAY */ |
| 4817 | #endif /* O_NONBLOCK */ | 5182 | #endif /* O_NONBLOCK */ |
| @@ -4839,7 +5204,7 @@ wait_reading_process_output (time_limit, microsecs, read_kbd, do_display, | |||
| 4839 | /* If we can detect process termination, don't consider the process | 5204 | /* If we can detect process termination, don't consider the process |
| 4840 | gone just because its pipe is closed. */ | 5205 | gone just because its pipe is closed. */ |
| 4841 | #ifdef SIGCHLD | 5206 | #ifdef SIGCHLD |
| 4842 | else if (nread == 0 && !NETCONN_P (proc)) | 5207 | else if (nread == 0 && !NETCONN_P (proc) && !SERIALCONN_P (proc)) |
| 4843 | ; | 5208 | ; |
| 4844 | #endif | 5209 | #endif |
| 4845 | else | 5210 | else |
| @@ -5628,7 +5993,7 @@ send_process (proc, buf, len, object) | |||
| 5628 | this -= rv; | 5993 | this -= rv; |
| 5629 | } | 5994 | } |
| 5630 | 5995 | ||
| 5631 | /* If we sent just part of the string, put in an EOF | 5996 | /* If we sent just part of the string, put in an EOF (C-d) |
| 5632 | to force it through, before we send the rest. */ | 5997 | to force it through, before we send the rest. */ |
| 5633 | if (len > 0) | 5998 | if (len > 0) |
| 5634 | Fprocess_send_eof (proc); | 5999 | Fprocess_send_eof (proc); |
| @@ -5748,7 +6113,7 @@ return t unconditionally. */) | |||
| 5748 | proc = get_process (process); | 6113 | proc = get_process (process); |
| 5749 | p = XPROCESS (proc); | 6114 | p = XPROCESS (proc); |
| 5750 | 6115 | ||
| 5751 | if (!EQ (p->childp, Qt)) | 6116 | if (!EQ (p->type, Qreal)) |
| 5752 | error ("Process %s is not a subprocess", | 6117 | error ("Process %s is not a subprocess", |
| 5753 | SDATA (p->name)); | 6118 | SDATA (p->name)); |
| 5754 | if (p->infd < 0) | 6119 | if (p->infd < 0) |
| @@ -5791,7 +6156,7 @@ process_send_signal (process, signo, current_group, nomsg) | |||
| 5791 | proc = get_process (process); | 6156 | proc = get_process (process); |
| 5792 | p = XPROCESS (proc); | 6157 | p = XPROCESS (proc); |
| 5793 | 6158 | ||
| 5794 | if (!EQ (p->childp, Qt)) | 6159 | if (!EQ (p->type, Qreal)) |
| 5795 | error ("Process %s is not a subprocess", | 6160 | error ("Process %s is not a subprocess", |
| 5796 | SDATA (p->name)); | 6161 | SDATA (p->name)); |
| 5797 | if (p->infd < 0) | 6162 | if (p->infd < 0) |
| @@ -6040,12 +6405,13 @@ See function `interrupt-process' for more details on usage. */) | |||
| 6040 | DEFUN ("stop-process", Fstop_process, Sstop_process, 0, 2, 0, | 6405 | DEFUN ("stop-process", Fstop_process, Sstop_process, 0, 2, 0, |
| 6041 | doc: /* Stop process PROCESS. May be process or name of one. | 6406 | doc: /* Stop process PROCESS. May be process or name of one. |
| 6042 | See function `interrupt-process' for more details on usage. | 6407 | See function `interrupt-process' for more details on usage. |
| 6043 | If PROCESS is a network process, inhibit handling of incoming traffic. */) | 6408 | If PROCESS is a network or serial process, inhibit handling of incoming |
| 6409 | traffic. */) | ||
| 6044 | (process, current_group) | 6410 | (process, current_group) |
| 6045 | Lisp_Object process, current_group; | 6411 | Lisp_Object process, current_group; |
| 6046 | { | 6412 | { |
| 6047 | #ifdef HAVE_SOCKETS | 6413 | #ifdef HAVE_SOCKETS |
| 6048 | if (PROCESSP (process) && NETCONN_P (process)) | 6414 | if (PROCESSP (process) && (NETCONN_P (process) || SERIALCONN_P (process))) |
| 6049 | { | 6415 | { |
| 6050 | struct Lisp_Process *p; | 6416 | struct Lisp_Process *p; |
| 6051 | 6417 | ||
| @@ -6071,12 +6437,13 @@ If PROCESS is a network process, inhibit handling of incoming traffic. */) | |||
| 6071 | DEFUN ("continue-process", Fcontinue_process, Scontinue_process, 0, 2, 0, | 6437 | DEFUN ("continue-process", Fcontinue_process, Scontinue_process, 0, 2, 0, |
| 6072 | doc: /* Continue process PROCESS. May be process or name of one. | 6438 | doc: /* Continue process PROCESS. May be process or name of one. |
| 6073 | See function `interrupt-process' for more details on usage. | 6439 | See function `interrupt-process' for more details on usage. |
| 6074 | If PROCESS is a network process, resume handling of incoming traffic. */) | 6440 | If PROCESS is a network or serial process, resume handling of incoming |
| 6441 | traffic. */) | ||
| 6075 | (process, current_group) | 6442 | (process, current_group) |
| 6076 | Lisp_Object process, current_group; | 6443 | Lisp_Object process, current_group; |
| 6077 | { | 6444 | { |
| 6078 | #ifdef HAVE_SOCKETS | 6445 | #ifdef HAVE_SOCKETS |
| 6079 | if (PROCESSP (process) && NETCONN_P (process)) | 6446 | if (PROCESSP (process) && (NETCONN_P (process) || SERIALCONN_P (process))) |
| 6080 | { | 6447 | { |
| 6081 | struct Lisp_Process *p; | 6448 | struct Lisp_Process *p; |
| 6082 | 6449 | ||
| @@ -6087,6 +6454,13 @@ If PROCESS is a network process, resume handling of incoming traffic. */) | |||
| 6087 | { | 6454 | { |
| 6088 | FD_SET (p->infd, &input_wait_mask); | 6455 | FD_SET (p->infd, &input_wait_mask); |
| 6089 | FD_SET (p->infd, &non_keyboard_wait_mask); | 6456 | FD_SET (p->infd, &non_keyboard_wait_mask); |
| 6457 | #ifdef WINDOWSNT | ||
| 6458 | if (fd_info[ p->infd ].flags & FILE_SERIAL) | ||
| 6459 | PurgeComm (fd_info[ p->infd ].hnd, PURGE_RXABORT | PURGE_RXCLEAR); | ||
| 6460 | #endif | ||
| 6461 | #ifdef HAVE_TERMIOS | ||
| 6462 | tcflush (p->infd, TCIFLUSH); | ||
| 6463 | #endif | ||
| 6090 | } | 6464 | } |
| 6091 | p->command = Qnil; | 6465 | p->command = Qnil; |
| 6092 | return process; | 6466 | return process; |
| @@ -6272,7 +6646,9 @@ PROCESS may be a process, a buffer, the name of a process or buffer, or | |||
| 6272 | nil, indicating the current buffer's process. | 6646 | nil, indicating the current buffer's process. |
| 6273 | If PROCESS is a network connection, or is a process communicating | 6647 | If PROCESS is a network connection, or is a process communicating |
| 6274 | through a pipe (as opposed to a pty), then you cannot send any more | 6648 | through a pipe (as opposed to a pty), then you cannot send any more |
| 6275 | text to PROCESS after you call this function. */) | 6649 | text to PROCESS after you call this function. |
| 6650 | If PROCESS is a serial process, wait until all output written to the | ||
| 6651 | process has been transmitted to the serial port. */) | ||
| 6276 | (process) | 6652 | (process) |
| 6277 | Lisp_Object process; | 6653 | Lisp_Object process; |
| 6278 | { | 6654 | { |
| @@ -6302,6 +6678,14 @@ text to PROCESS after you call this function. */) | |||
| 6302 | #else | 6678 | #else |
| 6303 | if (XPROCESS (proc)->pty_flag) | 6679 | if (XPROCESS (proc)->pty_flag) |
| 6304 | send_process (proc, "\004", 1, Qnil); | 6680 | send_process (proc, "\004", 1, Qnil); |
| 6681 | else if (XPROCESS (proc)->type == Qserial) | ||
| 6682 | { | ||
| 6683 | #ifdef HAVE_TERMIOS | ||
| 6684 | if (tcdrain (XPROCESS (proc)->outfd) != 0) | ||
| 6685 | error ("tcdrain() failed: %s", emacs_strerror (errno)); | ||
| 6686 | #endif | ||
| 6687 | /* Do nothing on Windows because writes are blocking. */ | ||
| 6688 | } | ||
| 6305 | else | 6689 | else |
| 6306 | { | 6690 | { |
| 6307 | int old_outfd, new_outfd; | 6691 | int old_outfd, new_outfd; |
| @@ -6311,7 +6695,7 @@ text to PROCESS after you call this function. */) | |||
| 6311 | for communication with the subprocess, call shutdown to cause EOF. | 6695 | for communication with the subprocess, call shutdown to cause EOF. |
| 6312 | (In some old system, shutdown to socketpair doesn't work. | 6696 | (In some old system, shutdown to socketpair doesn't work. |
| 6313 | Then we just can't win.) */ | 6697 | Then we just can't win.) */ |
| 6314 | if (XPROCESS (proc)->pid == 0 | 6698 | if (XPROCESS (proc)->type == Qnetwork |
| 6315 | || XPROCESS (proc)->outfd == XPROCESS (proc)->infd) | 6699 | || XPROCESS (proc)->outfd == XPROCESS (proc)->infd) |
| 6316 | shutdown (XPROCESS (proc)->outfd, 1); | 6700 | shutdown (XPROCESS (proc)->outfd, 1); |
| 6317 | /* In case of socketpair, outfd == infd, so don't close it. */ | 6701 | /* In case of socketpair, outfd == infd, so don't close it. */ |
| @@ -6355,7 +6739,7 @@ kill_buffer_processes (buffer) | |||
| 6355 | if (PROCESSP (proc) | 6739 | if (PROCESSP (proc) |
| 6356 | && (NILP (buffer) || EQ (XPROCESS (proc)->buffer, buffer))) | 6740 | && (NILP (buffer) || EQ (XPROCESS (proc)->buffer, buffer))) |
| 6357 | { | 6741 | { |
| 6358 | if (NETCONN_P (proc)) | 6742 | if (NETCONN_P (proc) || SERIALCONN_P (proc)) |
| 6359 | Fdelete_process (proc); | 6743 | Fdelete_process (proc); |
| 6360 | else if (XPROCESS (proc)->infd >= 0) | 6744 | else if (XPROCESS (proc)->infd >= 0) |
| 6361 | process_send_signal (proc, SIGHUP, Qnil, 1); | 6745 | process_send_signal (proc, SIGHUP, Qnil, 1); |
| @@ -6464,7 +6848,7 @@ sigchld_handler (signo) | |||
| 6464 | { | 6848 | { |
| 6465 | proc = XCDR (XCAR (tail)); | 6849 | proc = XCDR (XCAR (tail)); |
| 6466 | p = XPROCESS (proc); | 6850 | p = XPROCESS (proc); |
| 6467 | if (EQ (p->childp, Qt) && p->pid == pid) | 6851 | if (EQ (p->type, Qreal) && p->pid == pid) |
| 6468 | break; | 6852 | break; |
| 6469 | p = 0; | 6853 | p = 0; |
| 6470 | } | 6854 | } |
| @@ -6686,7 +7070,8 @@ status_notify (deleting_process) | |||
| 6686 | while (! EQ (p->filter, Qt) | 7070 | while (! EQ (p->filter, Qt) |
| 6687 | && ! EQ (p->status, Qconnect) | 7071 | && ! EQ (p->status, Qconnect) |
| 6688 | && ! EQ (p->status, Qlisten) | 7072 | && ! EQ (p->status, Qlisten) |
| 6689 | && ! EQ (p->command, Qt) /* Network process not stopped. */ | 7073 | /* Network or serial process not stopped: */ |
| 7074 | && ! EQ (p->command, Qt) | ||
| 6690 | && p->infd >= 0 | 7075 | && p->infd >= 0 |
| 6691 | && p != deleting_process | 7076 | && p != deleting_process |
| 6692 | && read_process_output (proc, p->infd) > 0); | 7077 | && read_process_output (proc, p->infd) > 0); |
| @@ -7073,6 +7458,39 @@ syms_of_process () | |||
| 7073 | Qdatagram = intern ("datagram"); | 7458 | Qdatagram = intern ("datagram"); |
| 7074 | staticpro (&Qdatagram); | 7459 | staticpro (&Qdatagram); |
| 7075 | 7460 | ||
| 7461 | QCport = intern (":port"); | ||
| 7462 | staticpro (&QCport); | ||
| 7463 | QCspeed = intern (":speed"); | ||
| 7464 | staticpro (&QCspeed); | ||
| 7465 | QCprocess = intern (":process"); | ||
| 7466 | staticpro (&QCprocess); | ||
| 7467 | |||
| 7468 | QCbytesize = intern (":bytesize"); | ||
| 7469 | staticpro (&QCbytesize); | ||
| 7470 | QCstopbits = intern (":stopbits"); | ||
| 7471 | staticpro (&QCstopbits); | ||
| 7472 | QCparity = intern (":parity"); | ||
| 7473 | staticpro (&QCparity); | ||
| 7474 | Qodd = intern ("odd"); | ||
| 7475 | staticpro (&Qodd); | ||
| 7476 | Qeven = intern ("even"); | ||
| 7477 | staticpro (&Qeven); | ||
| 7478 | QCflowcontrol = intern (":flowcontrol"); | ||
| 7479 | staticpro (&QCflowcontrol); | ||
| 7480 | Qhw = intern ("hw"); | ||
| 7481 | staticpro (&Qhw); | ||
| 7482 | Qsw = intern ("sw"); | ||
| 7483 | staticpro (&Qsw); | ||
| 7484 | QCsummary = intern (":summary"); | ||
| 7485 | staticpro (&QCsummary); | ||
| 7486 | |||
| 7487 | Qreal = intern ("real"); | ||
| 7488 | staticpro (&Qreal); | ||
| 7489 | Qnetwork = intern ("network"); | ||
| 7490 | staticpro (&Qnetwork); | ||
| 7491 | Qserial = intern ("serial"); | ||
| 7492 | staticpro (&Qserial); | ||
| 7493 | |||
| 7076 | QCname = intern (":name"); | 7494 | QCname = intern (":name"); |
| 7077 | staticpro (&QCname); | 7495 | staticpro (&QCname); |
| 7078 | QCbuffer = intern (":buffer"); | 7496 | QCbuffer = intern (":buffer"); |
| @@ -7170,6 +7588,10 @@ The variable takes effect when `start-process' is called. */); | |||
| 7170 | defsubr (&Slist_processes); | 7588 | defsubr (&Slist_processes); |
| 7171 | defsubr (&Sprocess_list); | 7589 | defsubr (&Sprocess_list); |
| 7172 | defsubr (&Sstart_process); | 7590 | defsubr (&Sstart_process); |
| 7591 | #ifdef HAVE_SERIAL | ||
| 7592 | defsubr (&Sserial_process_configure); | ||
| 7593 | defsubr (&Smake_serial_process); | ||
| 7594 | #endif /* HAVE_SERIAL */ | ||
| 7173 | #ifdef HAVE_SOCKETS | 7595 | #ifdef HAVE_SOCKETS |
| 7174 | defsubr (&Sset_network_process_option); | 7596 | defsubr (&Sset_network_process_option); |
| 7175 | defsubr (&Smake_network_process); | 7597 | defsubr (&Smake_network_process); |
| @@ -7199,7 +7621,7 @@ The variable takes effect when `start-process' is called. */); | |||
| 7199 | defsubr (&Sprocess_send_eof); | 7621 | defsubr (&Sprocess_send_eof); |
| 7200 | defsubr (&Ssignal_process); | 7622 | defsubr (&Ssignal_process); |
| 7201 | defsubr (&Swaiting_for_user_input_p); | 7623 | defsubr (&Swaiting_for_user_input_p); |
| 7202 | /* defsubr (&Sprocess_connection); */ | 7624 | defsubr (&Sprocess_type); |
| 7203 | defsubr (&Sset_process_coding_system); | 7625 | defsubr (&Sset_process_coding_system); |
| 7204 | defsubr (&Sprocess_coding_system); | 7626 | defsubr (&Sprocess_coding_system); |
| 7205 | defsubr (&Sset_process_filter_multibyte); | 7627 | defsubr (&Sset_process_filter_multibyte); |
diff --git a/src/process.h b/src/process.h index 619eb2b5e67..29cf38a2a4f 100644 --- a/src/process.h +++ b/src/process.h | |||
| @@ -51,11 +51,14 @@ struct Lisp_Process | |||
| 51 | Lisp_Object log; | 51 | Lisp_Object log; |
| 52 | /* Buffer that output is going to */ | 52 | /* Buffer that output is going to */ |
| 53 | Lisp_Object buffer; | 53 | Lisp_Object buffer; |
| 54 | /* t if this is a real child process. | 54 | /* t if this is a real child process. For a network or serial |
| 55 | For a net connection, it is a plist based on the arguments to make-network-process. */ | 55 | connection, it is a plist based on the arguments to |
| 56 | make-network-process or make-serial-process. */ | ||
| 56 | Lisp_Object childp; | 57 | Lisp_Object childp; |
| 57 | /* Plist for programs to keep per-process state information, parameters, etc. */ | 58 | /* Plist for programs to keep per-process state information, parameters, etc. */ |
| 58 | Lisp_Object plist; | 59 | Lisp_Object plist; |
| 60 | /* Symbol indicating the type of process: real, network, serial */ | ||
| 61 | Lisp_Object type; | ||
| 59 | /* Marker set to end of last buffer-inserted output from this process */ | 62 | /* Marker set to end of last buffer-inserted output from this process */ |
| 60 | Lisp_Object mark; | 63 | Lisp_Object mark; |
| 61 | /* Symbol indicating status of process. | 64 | /* Symbol indicating status of process. |
| @@ -78,7 +81,8 @@ struct Lisp_Process | |||
| 78 | 81 | ||
| 79 | /* Number of this process. | 82 | /* Number of this process. |
| 80 | allocate_process assumes this is the first non-Lisp_Object field. | 83 | allocate_process assumes this is the first non-Lisp_Object field. |
| 81 | A value 0 is used for pseudo-processes such as network connections. */ | 84 | A value 0 is used for pseudo-processes such as network or serial |
| 85 | connections. */ | ||
| 82 | pid_t pid; | 86 | pid_t pid; |
| 83 | /* Descriptor by which we read from this process */ | 87 | /* Descriptor by which we read from this process */ |
| 84 | int infd; | 88 | int infd; |
diff --git a/src/sysdep.c b/src/sysdep.c index 6c3e0e4a54e..9e1813bf71b 100644 --- a/src/sysdep.c +++ b/src/sysdep.c | |||
| @@ -166,6 +166,11 @@ extern int quit_char; | |||
| 166 | #include "process.h" | 166 | #include "process.h" |
| 167 | #include "cm.h" /* for reset_sys_modes */ | 167 | #include "cm.h" /* for reset_sys_modes */ |
| 168 | 168 | ||
| 169 | /* For serial_configure() and serial_open() */ | ||
| 170 | extern Lisp_Object QCport, QCspeed, QCprocess; | ||
| 171 | extern Lisp_Object QCbytesize, QCstopbits, QCparity, Qodd, Qeven; | ||
| 172 | extern Lisp_Object QCflowcontrol, Qhw, Qsw, QCsummary; | ||
| 173 | |||
| 169 | #ifdef WINDOWSNT | 174 | #ifdef WINDOWSNT |
| 170 | #include <direct.h> | 175 | #include <direct.h> |
| 171 | /* In process.h which conflicts with the local copy. */ | 176 | /* In process.h which conflicts with the local copy. */ |
| @@ -5379,6 +5384,200 @@ strsignal (code) | |||
| 5379 | return signame; | 5384 | return signame; |
| 5380 | } | 5385 | } |
| 5381 | #endif /* HAVE_STRSIGNAL */ | 5386 | #endif /* HAVE_STRSIGNAL */ |
| 5387 | |||
| 5388 | #ifdef HAVE_TERMIOS | ||
| 5389 | /* For make-serial-process */ | ||
| 5390 | int serial_open (char *port) | ||
| 5391 | { | ||
| 5392 | int fd = -1; | ||
| 5393 | |||
| 5394 | fd = emacs_open ((char*) port, | ||
| 5395 | O_RDWR | ||
| 5396 | #ifdef O_NONBLOCK | ||
| 5397 | | O_NONBLOCK | ||
| 5398 | #else | ||
| 5399 | | O_NDELAY | ||
| 5400 | #endif | ||
| 5401 | #ifdef O_NOCTTY | ||
| 5402 | | O_NOCTTY | ||
| 5403 | #endif | ||
| 5404 | , 0); | ||
| 5405 | if (fd < 0) | ||
| 5406 | { | ||
| 5407 | error ("Could not open %s: %s", | ||
| 5408 | port, emacs_strerror (errno)); | ||
| 5409 | } | ||
| 5410 | #ifdef TIOCEXCL | ||
| 5411 | ioctl (fd, TIOCEXCL, (char *) 0); | ||
| 5412 | #endif | ||
| 5413 | |||
| 5414 | return fd; | ||
| 5415 | } | ||
| 5416 | #endif /* TERMIOS */ | ||
| 5417 | |||
| 5418 | #ifdef HAVE_TERMIOS | ||
| 5419 | /* For serial-process-configure */ | ||
| 5420 | void | ||
| 5421 | serial_configure (struct Lisp_Process *p, | ||
| 5422 | Lisp_Object contact) | ||
| 5423 | { | ||
| 5424 | Lisp_Object childp2 = Qnil; | ||
| 5425 | Lisp_Object tem = Qnil; | ||
| 5426 | struct termios attr; | ||
| 5427 | int err = -1; | ||
| 5428 | char summary[4] = "???"; /* This usually becomes "8N1". */ | ||
| 5429 | |||
| 5430 | childp2 = Fcopy_sequence (p->childp); | ||
| 5431 | |||
| 5432 | /* Read port attributes and prepare default configuration. */ | ||
| 5433 | err = tcgetattr (p->outfd, &attr); | ||
| 5434 | if (err != 0) | ||
| 5435 | error ("tcgetattr() failed: %s", emacs_strerror (errno)); | ||
| 5436 | cfmakeraw (&attr); | ||
| 5437 | #if defined (CLOCAL) | ||
| 5438 | attr.c_cflag |= CLOCAL; | ||
| 5439 | #endif | ||
| 5440 | #if defined (CREAD) | ||
| 5441 | attr.c_cflag | CREAD; | ||
| 5442 | #endif | ||
| 5443 | |||
| 5444 | /* Configure speed. */ | ||
| 5445 | if (!NILP (Fplist_member (contact, QCspeed))) | ||
| 5446 | tem = Fplist_get (contact, QCspeed); | ||
| 5447 | else | ||
| 5448 | tem = Fplist_get (p->childp, QCspeed); | ||
| 5449 | CHECK_NUMBER (tem); | ||
| 5450 | err = cfsetspeed (&attr, XINT (tem)); | ||
| 5451 | if (err != 0) | ||
| 5452 | error ("cfsetspeed(%d) failed: %s", XINT (tem), emacs_strerror (errno)); | ||
| 5453 | childp2 = Fplist_put (childp2, QCspeed, tem); | ||
| 5454 | |||
| 5455 | /* Configure bytesize. */ | ||
| 5456 | if (!NILP (Fplist_member (contact, QCbytesize))) | ||
| 5457 | tem = Fplist_get (contact, QCbytesize); | ||
| 5458 | else | ||
| 5459 | tem = Fplist_get (p->childp, QCbytesize); | ||
| 5460 | if (NILP (tem)) | ||
| 5461 | tem = make_number (8); | ||
| 5462 | CHECK_NUMBER (tem); | ||
| 5463 | if (XINT (tem) != 7 && XINT (tem) != 8) | ||
| 5464 | error (":bytesize must be nil (8), 7, or 8"); | ||
| 5465 | summary[0] = XINT(tem) + '0'; | ||
| 5466 | #if defined (CSIZE) && defined (CS7) && defined (CS8) | ||
| 5467 | attr.c_cflag &= ~CSIZE; | ||
| 5468 | attr.c_cflag |= ((XINT (tem) == 7) ? CS7 : CS8); | ||
| 5469 | #else | ||
| 5470 | /* Don't error on bytesize 8, which should be set by cfmakeraw(). */ | ||
| 5471 | if (XINT (tem) != 8) | ||
| 5472 | error ("Bytesize cannot be changed"); | ||
| 5473 | #endif | ||
| 5474 | childp2 = Fplist_put (childp2, QCbytesize, tem); | ||
| 5475 | |||
| 5476 | /* Configure parity. */ | ||
| 5477 | if (!NILP (Fplist_member (contact, QCparity))) | ||
| 5478 | tem = Fplist_get (contact, QCparity); | ||
| 5479 | else | ||
| 5480 | tem = Fplist_get (p->childp, QCparity); | ||
| 5481 | if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd)) | ||
| 5482 | error (":parity must be nil (no parity), `even', or `odd'"); | ||
| 5483 | #if defined (PARENB) && defined (PARODD) && defined (IGNPAR) && defined (INPCK) | ||
| 5484 | attr.c_cflag &= ~(PARENB | PARODD); | ||
| 5485 | attr.c_iflag &= ~(IGNPAR | INPCK); | ||
| 5486 | if (NILP (tem)) | ||
| 5487 | { | ||
| 5488 | summary[1] = 'N'; | ||
| 5489 | } | ||
| 5490 | else if (EQ (tem, Qeven)) | ||
| 5491 | { | ||
| 5492 | summary[1] = 'E'; | ||
| 5493 | attr.c_cflag |= PARENB; | ||
| 5494 | attr.c_iflag |= (IGNPAR | INPCK); | ||
| 5495 | } | ||
| 5496 | else if (EQ (tem, Qodd)) | ||
| 5497 | { | ||
| 5498 | summary[1] = 'O'; | ||
| 5499 | attr.c_cflag |= (PARENB | PARODD); | ||
| 5500 | attr.c_iflag |= (IGNPAR | INPCK); | ||
| 5501 | } | ||
| 5502 | #else | ||
| 5503 | /* Don't error on no parity, which should be set by cfmakeraw(). */ | ||
| 5504 | if (!NILP (tem)) | ||
| 5505 | error ("Parity cannot be configured"); | ||
| 5506 | #endif | ||
| 5507 | childp2 = Fplist_put (childp2, QCparity, tem); | ||
| 5508 | |||
| 5509 | /* Configure stopbits. */ | ||
| 5510 | if (!NILP (Fplist_member (contact, QCstopbits))) | ||
| 5511 | tem = Fplist_get (contact, QCstopbits); | ||
| 5512 | else | ||
| 5513 | tem = Fplist_get (p->childp, QCstopbits); | ||
| 5514 | if (NILP (tem)) | ||
| 5515 | tem = make_number (1); | ||
| 5516 | CHECK_NUMBER (tem); | ||
| 5517 | if (XINT (tem) != 1 && XINT (tem) != 2) | ||
| 5518 | error (":stopbits must be nil (1 stopbit), 1, or 2"); | ||
| 5519 | summary[2] = XINT (tem) + '0'; | ||
| 5520 | #if defined (CSTOPB) | ||
| 5521 | attr.c_cflag &= ~CSTOPB; | ||
| 5522 | if (XINT (tem) == 2) | ||
| 5523 | attr.c_cflag |= CSTOPB; | ||
| 5524 | #else | ||
| 5525 | /* Don't error on 1 stopbit, which should be set by cfmakeraw(). */ | ||
| 5526 | if (XINT (tem) != 1) | ||
| 5527 | error ("Stopbits cannot be configured"); | ||
| 5528 | #endif | ||
| 5529 | childp2 = Fplist_put (childp2, QCstopbits, tem); | ||
| 5530 | |||
| 5531 | /* Configure flowcontrol. */ | ||
| 5532 | if (!NILP (Fplist_member (contact, QCflowcontrol))) | ||
| 5533 | tem = Fplist_get (contact, QCflowcontrol); | ||
| 5534 | else | ||
| 5535 | tem = Fplist_get (p->childp, QCflowcontrol); | ||
| 5536 | if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw)) | ||
| 5537 | error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'"); | ||
| 5538 | #if defined (CRTSCTS) | ||
| 5539 | attr.c_cflag &= ~CRTSCTS; | ||
| 5540 | #endif | ||
| 5541 | #if defined (CNEW_RTSCTS) | ||
| 5542 | attr.c_cflag &= ~CNEW_RTSCTS; | ||
| 5543 | #endif | ||
| 5544 | #if defined (IXON) && defined (IXOFF) | ||
| 5545 | attr.c_iflag &= ~(IXON | IXOFF); | ||
| 5546 | #endif | ||
| 5547 | if (NILP (tem)) | ||
| 5548 | { | ||
| 5549 | /* Already configured. */ | ||
| 5550 | } | ||
| 5551 | else if (EQ (tem, Qhw)) | ||
| 5552 | { | ||
| 5553 | #if defined (CRTSCTS) | ||
| 5554 | attr.c_cflag |= CRTSCTS; | ||
| 5555 | #elif defined (CNEW_RTSCTS) | ||
| 5556 | attr.c_cflag |= CNEW_RTSCTS; | ||
| 5557 | #else | ||
| 5558 | error ("Hardware flowcontrol (RTS/CTS) not supported"); | ||
| 5559 | #endif | ||
| 5560 | } | ||
| 5561 | else if (EQ (tem, Qsw)) | ||
| 5562 | { | ||
| 5563 | #if defined (IXON) && defined (IXOFF) | ||
| 5564 | attr.c_iflag |= (IXON | IXOFF); | ||
| 5565 | #else | ||
| 5566 | error ("Software flowcontrol (XON/XOFF) not supported"); | ||
| 5567 | #endif | ||
| 5568 | } | ||
| 5569 | childp2 = Fplist_put (childp2, QCflowcontrol, tem); | ||
| 5570 | |||
| 5571 | /* Activate configuration. */ | ||
| 5572 | err = tcsetattr (p->outfd, TCSANOW, &attr); | ||
| 5573 | if (err != 0) | ||
| 5574 | error ("tcsetattr() failed: %s", emacs_strerror (errno)); | ||
| 5575 | |||
| 5576 | childp2 = Fplist_put (childp2, QCsummary, build_string (summary)); | ||
| 5577 | p->childp = childp2; | ||
| 5578 | |||
| 5579 | } | ||
| 5580 | #endif /* TERMIOS */ | ||
| 5382 | 5581 | ||
| 5383 | /* arch-tag: edb43589-4e09-4544-b325-978b5b121dcf | 5582 | /* arch-tag: edb43589-4e09-4544-b325-978b5b121dcf |
| 5384 | (do not change this comment) */ | 5583 | (do not change this comment) */ |
| @@ -102,6 +102,13 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 102 | #include "systime.h" | 102 | #include "systime.h" |
| 103 | #include "dispextern.h" /* for xstrcasecmp */ | 103 | #include "dispextern.h" /* for xstrcasecmp */ |
| 104 | 104 | ||
| 105 | /* For serial_configure() and serial_open() */ | ||
| 106 | #include "process.h" | ||
| 107 | /* From process.c */ | ||
| 108 | extern Lisp_Object QCport, QCspeed, QCprocess; | ||
| 109 | extern Lisp_Object QCbytesize, QCstopbits, QCparity, Qodd, Qeven; | ||
| 110 | extern Lisp_Object QCflowcontrol, Qhw, Qsw, QCsummary; | ||
| 111 | |||
| 105 | typedef HRESULT (WINAPI * ShGetFolderPath_fn) | 112 | typedef HRESULT (WINAPI * ShGetFolderPath_fn) |
| 106 | (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *); | 113 | (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *); |
| 107 | 114 | ||
| @@ -4063,10 +4070,10 @@ _sys_read_ahead (int fd) | |||
| 4063 | if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY) | 4070 | if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY) |
| 4064 | return STATUS_READ_ERROR; | 4071 | return STATUS_READ_ERROR; |
| 4065 | 4072 | ||
| 4066 | if ((fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) == 0 | 4073 | if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0 |
| 4067 | || (fd_info[fd].flags & FILE_READ) == 0) | 4074 | || (fd_info[fd].flags & FILE_READ) == 0) |
| 4068 | { | 4075 | { |
| 4069 | DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe or socket!\n", fd)); | 4076 | DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd)); |
| 4070 | abort (); | 4077 | abort (); |
| 4071 | } | 4078 | } |
| 4072 | 4079 | ||
| @@ -4080,7 +4087,7 @@ _sys_read_ahead (int fd) | |||
| 4080 | reporting that input is available; we need this because Windows 95 | 4087 | reporting that input is available; we need this because Windows 95 |
| 4081 | connects DOS programs to pipes by making the pipe appear to be | 4088 | connects DOS programs to pipes by making the pipe appear to be |
| 4082 | the normal console stdout - as a result most DOS programs will | 4089 | the normal console stdout - as a result most DOS programs will |
| 4083 | write to stdout without buffering, ie. one character at a | 4090 | write to stdout without buffering, ie. one character at a |
| 4084 | time. Even some W32 programs do this - "dir" in a command | 4091 | time. Even some W32 programs do this - "dir" in a command |
| 4085 | shell on NT is very slow if we don't do this. */ | 4092 | shell on NT is very slow if we don't do this. */ |
| 4086 | if (rc > 0) | 4093 | if (rc > 0) |
| @@ -4096,6 +4103,29 @@ _sys_read_ahead (int fd) | |||
| 4096 | Sleep (0); | 4103 | Sleep (0); |
| 4097 | } | 4104 | } |
| 4098 | } | 4105 | } |
| 4106 | else if (fd_info[fd].flags & FILE_SERIAL) | ||
| 4107 | { | ||
| 4108 | HANDLE hnd = fd_info[fd].hnd; | ||
| 4109 | OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read; | ||
| 4110 | COMMTIMEOUTS ct; | ||
| 4111 | |||
| 4112 | /* Configure timeouts for blocking read. */ | ||
| 4113 | if (!GetCommTimeouts (hnd, &ct)) | ||
| 4114 | return STATUS_READ_ERROR; | ||
| 4115 | ct.ReadIntervalTimeout = 0; | ||
| 4116 | ct.ReadTotalTimeoutMultiplier = 0; | ||
| 4117 | ct.ReadTotalTimeoutConstant = 0; | ||
| 4118 | if (!SetCommTimeouts (hnd, &ct)) | ||
| 4119 | return STATUS_READ_ERROR; | ||
| 4120 | |||
| 4121 | if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl)) | ||
| 4122 | { | ||
| 4123 | if (GetLastError () != ERROR_IO_PENDING) | ||
| 4124 | return STATUS_READ_ERROR; | ||
| 4125 | if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE)) | ||
| 4126 | return STATUS_READ_ERROR; | ||
| 4127 | } | ||
| 4128 | } | ||
| 4099 | #ifdef HAVE_SOCKETS | 4129 | #ifdef HAVE_SOCKETS |
| 4100 | else if (fd_info[fd].flags & FILE_SOCKET) | 4130 | else if (fd_info[fd].flags & FILE_SOCKET) |
| 4101 | { | 4131 | { |
| @@ -4167,7 +4197,7 @@ sys_read (int fd, char * buffer, unsigned int count) | |||
| 4167 | return -1; | 4197 | return -1; |
| 4168 | } | 4198 | } |
| 4169 | 4199 | ||
| 4170 | if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) | 4200 | if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL)) |
| 4171 | { | 4201 | { |
| 4172 | child_process *cp = fd_info[fd].cp; | 4202 | child_process *cp = fd_info[fd].cp; |
| 4173 | 4203 | ||
| @@ -4238,6 +4268,52 @@ sys_read (int fd, char * buffer, unsigned int count) | |||
| 4238 | if (to_read > 0) | 4268 | if (to_read > 0) |
| 4239 | nchars += _read (fd, buffer, to_read); | 4269 | nchars += _read (fd, buffer, to_read); |
| 4240 | } | 4270 | } |
| 4271 | else if (fd_info[fd].flags & FILE_SERIAL) | ||
| 4272 | { | ||
| 4273 | HANDLE hnd = fd_info[fd].hnd; | ||
| 4274 | OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read; | ||
| 4275 | DWORD err = 0; | ||
| 4276 | int rc = 0; | ||
| 4277 | COMMTIMEOUTS ct; | ||
| 4278 | |||
| 4279 | if (count > 0) | ||
| 4280 | { | ||
| 4281 | /* Configure timeouts for non-blocking read. */ | ||
| 4282 | if (!GetCommTimeouts (hnd, &ct)) | ||
| 4283 | { | ||
| 4284 | errno = EIO; | ||
| 4285 | return -1; | ||
| 4286 | } | ||
| 4287 | ct.ReadIntervalTimeout = MAXDWORD; | ||
| 4288 | ct.ReadTotalTimeoutMultiplier = 0; | ||
| 4289 | ct.ReadTotalTimeoutConstant = 0; | ||
| 4290 | if (!SetCommTimeouts (hnd, &ct)) | ||
| 4291 | { | ||
| 4292 | errno = EIO; | ||
| 4293 | return -1; | ||
| 4294 | } | ||
| 4295 | |||
| 4296 | if (!ResetEvent (ovl->hEvent)) | ||
| 4297 | { | ||
| 4298 | errno = EIO; | ||
| 4299 | return -1; | ||
| 4300 | } | ||
| 4301 | if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl)) | ||
| 4302 | { | ||
| 4303 | if (GetLastError () != ERROR_IO_PENDING) | ||
| 4304 | { | ||
| 4305 | errno = EIO; | ||
| 4306 | return -1; | ||
| 4307 | } | ||
| 4308 | if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE)) | ||
| 4309 | { | ||
| 4310 | errno = EIO; | ||
| 4311 | return -1; | ||
| 4312 | } | ||
| 4313 | } | ||
| 4314 | nchars += rc; | ||
| 4315 | } | ||
| 4316 | } | ||
| 4241 | #ifdef HAVE_SOCKETS | 4317 | #ifdef HAVE_SOCKETS |
| 4242 | else /* FILE_SOCKET */ | 4318 | else /* FILE_SOCKET */ |
| 4243 | { | 4319 | { |
| @@ -4299,6 +4375,9 @@ sys_read (int fd, char * buffer, unsigned int count) | |||
| 4299 | return nchars; | 4375 | return nchars; |
| 4300 | } | 4376 | } |
| 4301 | 4377 | ||
| 4378 | /* From w32xfns.c */ | ||
| 4379 | extern HANDLE interrupt_handle; | ||
| 4380 | |||
| 4302 | /* For now, don't bother with a non-blocking mode */ | 4381 | /* For now, don't bother with a non-blocking mode */ |
| 4303 | int | 4382 | int |
| 4304 | sys_write (int fd, const void * buffer, unsigned int count) | 4383 | sys_write (int fd, const void * buffer, unsigned int count) |
| @@ -4311,7 +4390,7 @@ sys_write (int fd, const void * buffer, unsigned int count) | |||
| 4311 | return -1; | 4390 | return -1; |
| 4312 | } | 4391 | } |
| 4313 | 4392 | ||
| 4314 | if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) | 4393 | if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL)) |
| 4315 | { | 4394 | { |
| 4316 | if ((fd_info[fd].flags & FILE_WRITE) == 0) | 4395 | if ((fd_info[fd].flags & FILE_WRITE) == 0) |
| 4317 | { | 4396 | { |
| @@ -4352,6 +4431,42 @@ sys_write (int fd, const void * buffer, unsigned int count) | |||
| 4352 | } | 4431 | } |
| 4353 | } | 4432 | } |
| 4354 | 4433 | ||
| 4434 | if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL) | ||
| 4435 | { | ||
| 4436 | HANDLE hnd = (HANDLE) _get_osfhandle (fd); | ||
| 4437 | OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write; | ||
| 4438 | HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent }; | ||
| 4439 | DWORD active = 0; | ||
| 4440 | |||
| 4441 | if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl)) | ||
| 4442 | { | ||
| 4443 | if (GetLastError () != ERROR_IO_PENDING) | ||
| 4444 | { | ||
| 4445 | errno = EIO; | ||
| 4446 | return -1; | ||
| 4447 | } | ||
| 4448 | if (detect_input_pending ()) | ||
| 4449 | active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE, | ||
| 4450 | QS_ALLINPUT); | ||
| 4451 | else | ||
| 4452 | active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE); | ||
| 4453 | if (active == WAIT_OBJECT_0) | ||
| 4454 | { /* User pressed C-g, cancel write, then leave. Don't bother | ||
| 4455 | cleaning up as we may only get stuck in buggy drivers. */ | ||
| 4456 | PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR); | ||
| 4457 | CancelIo (hnd); | ||
| 4458 | errno = EIO; | ||
| 4459 | return -1; | ||
| 4460 | } | ||
| 4461 | if (active == WAIT_OBJECT_0 + 1 | ||
| 4462 | && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE)) | ||
| 4463 | { | ||
| 4464 | errno = EIO; | ||
| 4465 | return -1; | ||
| 4466 | } | ||
| 4467 | } | ||
| 4468 | } | ||
| 4469 | else | ||
| 4355 | #ifdef HAVE_SOCKETS | 4470 | #ifdef HAVE_SOCKETS |
| 4356 | if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET) | 4471 | if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET) |
| 4357 | { | 4472 | { |
| @@ -4612,6 +4727,196 @@ globals_of_w32 () | |||
| 4612 | strcpy (dflt_group_name, "None"); | 4727 | strcpy (dflt_group_name, "None"); |
| 4613 | } | 4728 | } |
| 4614 | 4729 | ||
| 4730 | /* For make-serial-process */ | ||
| 4731 | int serial_open (char *port) | ||
| 4732 | { | ||
| 4733 | HANDLE hnd; | ||
| 4734 | child_process *cp; | ||
| 4735 | int fd = -1; | ||
| 4736 | |||
| 4737 | hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0, | ||
| 4738 | OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); | ||
| 4739 | if (hnd == INVALID_HANDLE_VALUE) | ||
| 4740 | error ("Could not open %s", port); | ||
| 4741 | fd = (int) _open_osfhandle ((int) hnd, 0); | ||
| 4742 | if (fd == -1) | ||
| 4743 | error ("Could not open %s", port); | ||
| 4744 | |||
| 4745 | cp = new_child (); | ||
| 4746 | if (!cp) | ||
| 4747 | error ("Could not create child process"); | ||
| 4748 | cp->fd = fd; | ||
| 4749 | cp->status = STATUS_READ_ACKNOWLEDGED; | ||
| 4750 | fd_info[ fd ].hnd = hnd; | ||
| 4751 | fd_info[ fd ].flags |= | ||
| 4752 | FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL; | ||
| 4753 | if (fd_info[ fd ].cp != NULL) | ||
| 4754 | { | ||
| 4755 | error ("fd_info[fd = %d] is already in use", fd); | ||
| 4756 | } | ||
| 4757 | fd_info[ fd ].cp = cp; | ||
| 4758 | cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); | ||
| 4759 | if (cp->ovl_read.hEvent == NULL) | ||
| 4760 | error ("Could not create read event"); | ||
| 4761 | cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); | ||
| 4762 | if (cp->ovl_write.hEvent == NULL) | ||
| 4763 | error ("Could not create write event"); | ||
| 4764 | |||
| 4765 | return fd; | ||
| 4766 | } | ||
| 4767 | |||
| 4768 | /* For serial-process-configure */ | ||
| 4769 | void | ||
| 4770 | serial_configure (struct Lisp_Process *p, | ||
| 4771 | Lisp_Object contact) | ||
| 4772 | { | ||
| 4773 | Lisp_Object childp2 = Qnil; | ||
| 4774 | Lisp_Object tem = Qnil; | ||
| 4775 | HANDLE hnd; | ||
| 4776 | DCB dcb; | ||
| 4777 | COMMTIMEOUTS ct; | ||
| 4778 | char summary[4] = "???"; /* This usually becomes "8N1". */ | ||
| 4779 | |||
| 4780 | if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0) | ||
| 4781 | error ("Not a serial process"); | ||
| 4782 | hnd = fd_info[ p->outfd ].hnd; | ||
| 4783 | |||
| 4784 | childp2 = Fcopy_sequence (p->childp); | ||
| 4785 | |||
| 4786 | /* Initialize timeouts for blocking read and blocking write. */ | ||
| 4787 | if (!GetCommTimeouts (hnd, &ct)) | ||
| 4788 | error ("GetCommTimeouts() failed"); | ||
| 4789 | ct.ReadIntervalTimeout = 0; | ||
| 4790 | ct.ReadTotalTimeoutMultiplier = 0; | ||
| 4791 | ct.ReadTotalTimeoutConstant = 0; | ||
| 4792 | ct.WriteTotalTimeoutMultiplier = 0; | ||
| 4793 | ct.WriteTotalTimeoutConstant = 0; | ||
| 4794 | if (!SetCommTimeouts (hnd, &ct)) | ||
| 4795 | error ("SetCommTimeouts() failed"); | ||
| 4796 | /* Read port attributes and prepare default configuration. */ | ||
| 4797 | memset (&dcb, 0, sizeof (dcb)); | ||
| 4798 | dcb.DCBlength = sizeof (DCB); | ||
| 4799 | if (!GetCommState (hnd, &dcb)) | ||
| 4800 | error ("GetCommState() failed"); | ||
| 4801 | dcb.fBinary = TRUE; | ||
| 4802 | dcb.fNull = FALSE; | ||
| 4803 | dcb.fAbortOnError = FALSE; | ||
| 4804 | /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */ | ||
| 4805 | dcb.ErrorChar = 0; | ||
| 4806 | dcb.EofChar = 0; | ||
| 4807 | dcb.EvtChar = 0; | ||
| 4808 | |||
| 4809 | /* Configure speed. */ | ||
| 4810 | if (!NILP (Fplist_member (contact, QCspeed))) | ||
| 4811 | tem = Fplist_get (contact, QCspeed); | ||
| 4812 | else | ||
| 4813 | tem = Fplist_get (p->childp, QCspeed); | ||
| 4814 | CHECK_NUMBER (tem); | ||
| 4815 | dcb.BaudRate = XINT (tem); | ||
| 4816 | childp2 = Fplist_put (childp2, QCspeed, tem); | ||
| 4817 | |||
| 4818 | /* Configure bytesize. */ | ||
| 4819 | if (!NILP (Fplist_member (contact, QCbytesize))) | ||
| 4820 | tem = Fplist_get (contact, QCbytesize); | ||
| 4821 | else | ||
| 4822 | tem = Fplist_get (p->childp, QCbytesize); | ||
| 4823 | if (NILP (tem)) | ||
| 4824 | tem = make_number (8); | ||
| 4825 | CHECK_NUMBER (tem); | ||
| 4826 | if (XINT (tem) != 7 && XINT (tem) != 8) | ||
| 4827 | error (":bytesize must be nil (8), 7, or 8"); | ||
| 4828 | dcb.ByteSize = XINT (tem); | ||
| 4829 | summary[0] = XINT (tem) + '0'; | ||
| 4830 | childp2 = Fplist_put (childp2, QCbytesize, tem); | ||
| 4831 | |||
| 4832 | /* Configure parity. */ | ||
| 4833 | if (!NILP (Fplist_member (contact, QCparity))) | ||
| 4834 | tem = Fplist_get (contact, QCparity); | ||
| 4835 | else | ||
| 4836 | tem = Fplist_get (p->childp, QCparity); | ||
| 4837 | if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd)) | ||
| 4838 | error (":parity must be nil (no parity), `even', or `odd'"); | ||
| 4839 | dcb.fParity = FALSE; | ||
| 4840 | dcb.Parity = NOPARITY; | ||
| 4841 | dcb.fErrorChar = FALSE; | ||
| 4842 | if (NILP (tem)) | ||
| 4843 | { | ||
| 4844 | summary[1] = 'N'; | ||
| 4845 | } | ||
| 4846 | else if (EQ (tem, Qeven)) | ||
| 4847 | { | ||
| 4848 | summary[1] = 'E'; | ||
| 4849 | dcb.fParity = TRUE; | ||
| 4850 | dcb.Parity = EVENPARITY; | ||
| 4851 | dcb.fErrorChar = TRUE; | ||
| 4852 | } | ||
| 4853 | else if (EQ (tem, Qodd)) | ||
| 4854 | { | ||
| 4855 | summary[1] = 'O'; | ||
| 4856 | dcb.fParity = TRUE; | ||
| 4857 | dcb.Parity = ODDPARITY; | ||
| 4858 | dcb.fErrorChar = TRUE; | ||
| 4859 | } | ||
| 4860 | childp2 = Fplist_put (childp2, QCparity, tem); | ||
| 4861 | |||
| 4862 | /* Configure stopbits. */ | ||
| 4863 | if (!NILP (Fplist_member (contact, QCstopbits))) | ||
| 4864 | tem = Fplist_get (contact, QCstopbits); | ||
| 4865 | else | ||
| 4866 | tem = Fplist_get (p->childp, QCstopbits); | ||
| 4867 | if (NILP (tem)) | ||
| 4868 | tem = make_number (1); | ||
| 4869 | CHECK_NUMBER (tem); | ||
| 4870 | if (XINT (tem) != 1 && XINT (tem) != 2) | ||
| 4871 | error (":stopbits must be nil (1 stopbit), 1, or 2"); | ||
| 4872 | summary[2] = XINT (tem) + '0'; | ||
| 4873 | if (XINT (tem) == 1) | ||
| 4874 | dcb.StopBits = ONESTOPBIT; | ||
| 4875 | else if (XINT (tem) == 2) | ||
| 4876 | dcb.StopBits = TWOSTOPBITS; | ||
| 4877 | childp2 = Fplist_put (childp2, QCstopbits, tem); | ||
| 4878 | |||
| 4879 | /* Configure flowcontrol. */ | ||
| 4880 | if (!NILP (Fplist_member (contact, QCflowcontrol))) | ||
| 4881 | tem = Fplist_get (contact, QCflowcontrol); | ||
| 4882 | else | ||
| 4883 | tem = Fplist_get (p->childp, QCflowcontrol); | ||
| 4884 | if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw)) | ||
| 4885 | error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'"); | ||
| 4886 | dcb.fOutxCtsFlow = FALSE; | ||
| 4887 | dcb.fOutxDsrFlow = FALSE; | ||
| 4888 | dcb.fDtrControl = DTR_CONTROL_DISABLE; | ||
| 4889 | dcb.fDsrSensitivity = FALSE; | ||
| 4890 | dcb.fTXContinueOnXoff = FALSE; | ||
| 4891 | dcb.fOutX = FALSE; | ||
| 4892 | dcb.fInX = FALSE; | ||
| 4893 | dcb.fRtsControl = RTS_CONTROL_DISABLE; | ||
| 4894 | dcb.XonChar = 17; /* Control-Q */ | ||
| 4895 | dcb.XoffChar = 19; /* Control-S */ | ||
| 4896 | if (NILP (tem)) | ||
| 4897 | { | ||
| 4898 | /* Already configured. */ | ||
| 4899 | } | ||
| 4900 | else if (EQ (tem, Qhw)) | ||
| 4901 | { | ||
| 4902 | dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; | ||
| 4903 | dcb.fOutxCtsFlow = TRUE; | ||
| 4904 | } | ||
| 4905 | else if (EQ (tem, Qsw)) | ||
| 4906 | { | ||
| 4907 | dcb.fOutX = TRUE; | ||
| 4908 | dcb.fInX = TRUE; | ||
| 4909 | } | ||
| 4910 | childp2 = Fplist_put (childp2, QCflowcontrol, tem); | ||
| 4911 | |||
| 4912 | /* Activate configuration. */ | ||
| 4913 | if (!SetCommState (hnd, &dcb)) | ||
| 4914 | error ("SetCommState() failed"); | ||
| 4915 | |||
| 4916 | childp2 = Fplist_put (childp2, QCsummary, build_string (summary)); | ||
| 4917 | p->childp = childp2; | ||
| 4918 | } | ||
| 4919 | |||
| 4615 | /* end of w32.c */ | 4920 | /* end of w32.c */ |
| 4616 | 4921 | ||
| 4617 | /* arch-tag: 90442dd3-37be-482b-b272-ac752e3049f1 | 4922 | /* arch-tag: 90442dd3-37be-482b-b272-ac752e3049f1 |
| @@ -72,6 +72,8 @@ typedef struct _child_process | |||
| 72 | PROCESS_INFORMATION procinfo; | 72 | PROCESS_INFORMATION procinfo; |
| 73 | volatile int status; | 73 | volatile int status; |
| 74 | char chr; | 74 | char chr; |
| 75 | OVERLAPPED ovl_read; | ||
| 76 | OVERLAPPED ovl_write; | ||
| 75 | } child_process; | 77 | } child_process; |
| 76 | 78 | ||
| 77 | #define MAXDESC FD_SETSIZE | 79 | #define MAXDESC FD_SETSIZE |
| @@ -99,6 +101,7 @@ extern filedesc fd_info [ MAXDESC ]; | |||
| 99 | #define FILE_PIPE 0x0100 | 101 | #define FILE_PIPE 0x0100 |
| 100 | #define FILE_SOCKET 0x0200 | 102 | #define FILE_SOCKET 0x0200 |
| 101 | #define FILE_NDELAY 0x0400 | 103 | #define FILE_NDELAY 0x0400 |
| 104 | #define FILE_SERIAL 0x0800 | ||
| 102 | 105 | ||
| 103 | extern child_process * new_child (void); | 106 | extern child_process * new_child (void); |
| 104 | extern void delete_child (child_process *cp); | 107 | extern void delete_child (child_process *cp); |