Fix Xcode warnings, part I
[texmacs.git] / src / src / System / Link / socket_link.cpp
blobad5451a5e58691060cf5beb687945db841a6f569
2 /******************************************************************************
3 * MODULE : socket_link.cpp
4 * DESCRIPTION: TeXmacs links by sockets
5 * COPYRIGHT : (C) 2003 Joris van der Hoeven
6 * THANKS : Beej's Guide to Network Programming has been helpful
7 *******************************************************************************
8 * This software falls under the GNU general public license version 3 or later.
9 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
10 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ******************************************************************************/
13 #include "socket_link.hpp"
14 #include "sys_utils.hpp"
15 #include "hashset.hpp"
16 #include "iterator.hpp"
17 #include "timer.hpp"
18 #include "Scheme/object.hpp"
19 #include <stdio.h>
20 #include <string.h>
21 #ifndef __MINGW32__
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/wait.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <netdb.h>
29 #endif
30 #include <errno.h>
32 hashset<pointer> socket_link_set;
33 void socket_callback (void *obj, void* info);
35 /******************************************************************************
36 * Constructors and destructors for socket_links
37 ******************************************************************************/
39 socket_link_rep::socket_link_rep (string host2, int port2, int type2, int fd):
40 host (host2), port (port2), type (type2)
42 socket_link_set->insert ((pointer) this);
43 io = fd;
44 outbuf = "";
45 alive = (fd != -1);
46 if (type == SOCKET_SERVER) call ("server-add", object (io));
47 else if (type == SOCKET_CLIENT) call ("client-add");
50 socket_link_rep::~socket_link_rep () {
51 stop ();
52 socket_link_set->remove ((pointer) this);
55 tm_link
56 make_socket_link (string host, int port, int type, int fd) {
57 return tm_new<socket_link_rep> (host, port, type, fd);
60 tm_link
61 find_socket_link (int fd) {
62 iterator<pointer> it= iterate (socket_link_set);
63 while (it->busy()) {
64 socket_link_rep* con= (socket_link_rep*) it->next();
65 if (con->io == fd && con->alive) return tm_link (con);
67 return tm_link ();
70 void
71 close_all_sockets () {
72 #ifndef __MINGW32__
73 iterator<pointer> it= iterate (socket_link_set);
74 while (it->busy()) {
75 socket_link_rep* con= (socket_link_rep*) it->next();
76 if (con->alive) {
77 // FIXME: cleanly close the connection to the socket here
78 con->alive= false;
81 #endif
84 /******************************************************************************
85 * Routines for socket_links
86 ******************************************************************************/
88 string
89 socket_link_rep::start () {
90 #ifndef __MINGW32__
91 if (alive) return "busy";
92 if (DEBUG_AUTO)
93 cout << "TeXmacs] Connecting to '" << host << ":" << port << "'\n";
95 // getting host
96 char* _host= as_charp (host);
97 struct hostent *hp = gethostbyname (_host);
98 tm_delete_array (_host);
99 if (hp == NULL) return "Error: no connection for '" * host * "'";
101 // creating socket
102 io= socket (AF_INET, SOCK_STREAM, 0);
103 if (io < 0) return "Error: socket could not be created";
105 // connecting to socket
106 struct sockaddr_in insock;
107 string where= host * ":" * as_string (port);
108 memset ((char*) &insock, 0, sizeof (insock));
109 insock.sin_family = AF_INET;
110 insock.sin_port = htons ((unsigned short) port);
111 memcpy ((char*) &insock.sin_addr, hp->h_addr, hp->h_length);
112 if (connect (io, (struct sockaddr*) &insock, sizeof (insock)) < 0)
113 return "Error: refused connection to '" * where * "'";
115 // testing whether it works
116 int flags = O_NONBLOCK;
117 if (fcntl (io, F_SETFL, flags) < 0)
118 return "Error: non working connection to '" * where * "'";
119 alive = true;
120 sn = socket_notifier (io, &socket_callback, this, NULL);
121 return "ok";
122 #else
123 return "Error: sockets not implemented";
124 #endif
127 static string
128 debug_io_string (string s) {
129 int i, n= N(s);
130 string r;
131 for (i=0; i<n; i++) {
132 unsigned char c= (unsigned char) s[i];
133 if (c == DATA_BEGIN) r << "[BEGIN]";
134 else if (c == DATA_END) r << "[END]";
135 else if (c == DATA_COMMAND) r << "[COMMAND]";
136 else if (c == DATA_ESCAPE) r << "[ESCAPE]";
137 else r << s[i];
139 return r;
142 static int
143 send_all (int s, char *buf, int *len) {
144 #ifndef __MINGW32__
145 int total= 0; // how many bytes we've sent
146 int bytes_left= *len; // how many we have left to send
147 int n= 0;
149 while (total < *len) {
150 n= send (s, buf + total, bytes_left, 0);
151 if (n == -1) break;
152 total += n;
153 bytes_left -= n;
156 *len= total;
157 return n==-1? -1: 0;
158 #else
159 return 0;
160 #endif
164 void
165 socket_link_rep::write (string s, int channel) {
166 if ((!alive) || (channel != LINK_IN)) return;
167 if (DEBUG_IO) cout << "---> " << debug_io_string (s) << "\n";
168 int len= N(s);
169 if (send_all (io, &(s[0]), &len) == -1) {
170 cerr << "TeXmacs] write to '" << host << ":" << port << "' failed\n";
171 stop ();
175 void
176 socket_link_rep::feed (int channel) {
177 #ifndef __MINGW32__
178 if ((!alive) || (channel != LINK_OUT)) return;
179 char tempout[1024];
180 int r= recv (io, tempout, 1024, 0);
181 if (r <= 0) {
182 if (r == 0) cout << "TeXmacs] '" << host << ":" << port << "' hung up\n";
183 else cerr << "TeXmacs] read failed from '" << host << ":" << port << "'\n";
184 stop ();
186 else if (r != 0) {
187 if (DEBUG_IO) cout << debug_io_string (string (tempout, r));
188 outbuf << string (tempout, r);
190 #endif
193 string&
194 socket_link_rep::watch (int channel) {
195 static string empty_string= "";
196 if (channel == LINK_OUT) return outbuf;
197 else return empty_string;
200 string
201 socket_link_rep::read (int channel) {
202 if (channel == LINK_OUT) {
203 string r= outbuf;
204 outbuf= "";
205 return r;
207 else return "";
210 void
211 socket_link_rep::listen (int msecs) {
212 #ifndef __MINGW32__
213 if (!alive) return;
214 fd_set rfds;
215 FD_ZERO (&rfds);
216 FD_SET (io, &rfds);
217 struct timeval tv;
218 tv.tv_sec = msecs / 1000;
219 tv.tv_usec = 1000 * (msecs % 1000);
220 int nr= select (io+1, &rfds, NULL, NULL, &tv);
221 if (nr != 0 && FD_ISSET (io, &rfds)) feed (LINK_OUT);
222 #endif
225 void
226 socket_link_rep::interrupt () {
229 void
230 socket_link_rep::stop () {
231 #ifndef __MINGW32__
232 if (!alive) return;
233 if (type == SOCKET_SERVER) call ("server-remove", object (io));
234 else if (type == SOCKET_CLIENT) call ("client-remove");
235 close (io);
236 io= -1;
237 alive= false;
238 sn = socket_notifier ();
239 wait (NULL);
240 #endif
243 /******************************************************************************
244 * Call back for new information on socket
245 ******************************************************************************/
247 void
248 socket_callback (void *obj, void* info) {
249 #ifndef __MINGW32__
250 (void) info;
251 socket_link_rep* con= (socket_link_rep*) obj;
252 bool busy= true;
253 bool news= false;
254 while (busy) {
255 fd_set rfds;
256 FD_ZERO (&rfds);
257 int max_fd= con->io + 1;
258 FD_SET (con->io, &rfds);
260 struct timeval tv;
261 tv.tv_sec = 0;
262 tv.tv_usec = 0;
263 select (max_fd, &rfds, NULL, NULL, &tv);
265 busy= false;
266 if (con->alive && FD_ISSET (con->io, &rfds)) {
267 //cout << "socket_callback OUT" << LF;
268 con->feed (LINK_OUT);
269 busy= news= true;
272 if (!is_nil (con->feed_cmd) && news)
273 con->feed_cmd->apply ();
274 #endif