1 /* $Header: /home/jcb/MahJong/newmj/RCS/sysdep.c,v 12.3 2020/05/26 09:34:06 jcb Exp $
2 * sysdep.c
3 * By intention, this file contains all functions that might need
4 * fiddling with to cope with different variants of Unix.
5 * In particular, all the networking code is in here.
6 * Some parts of the other code assume POSIXish functions for file
7 * descriptors and the like; this file is responsible for providing them
8 * if they're not native. All such cases that occurred to me as I was
9 * writing them can be found by searching for sysdep.c in the other
10 * files.
11 */
12 /****************** COPYRIGHT STATEMENT **********************
13 * This file is Copyright (c) 2000 by J. C. Bradfield. *
14 * Distribution and use is governed by the LICENCE file that *
15 * accompanies this file. *
16 * The moral rights of the author are asserted. *
17 * *
18 ***************** DISCLAIMER OF WARRANTY ********************
19 * This code is not warranted fit for any purpose. See the *
20 * LICENCE file for further information. *
21 * *
22 *************************************************************/
23
24 static const char rcs_id[] = "$Header: /home/jcb/MahJong/newmj/RCS/sysdep.c,v 12.3 2020/05/26 09:34:06 jcb Exp $";
25
26 #include "sysdep.h"
27 #include <string.h>
28 /* This is missing in my windows mingw */
29 #ifdef WIN32
index(const char * s,int c)30 char *index(const char *s, int c) {
31 while ( 1 ) {
32 if (*s == 0) return NULL;
33 else if (*s == c) return (char *)s;
34 s++;
35 }
36 }
37 #endif
38
39 #include <fcntl.h>
40 #include <errno.h>
41
42 #ifndef WIN32
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <netinet/tcp.h>
46 /* needed for HP-UX 11 with _XOPEN_SOURCE_EXTENDED */
47 #include <arpa/inet.h>
48 #include <sys/un.h>
49 /* this is modern, I think. */
50 #include <netdb.h>
51 #include <pwd.h>
52 #endif /* not WIN32 */
53
54 #include <stdlib.h>
55 #include <stdarg.h>
56 #include <time.h>
57 #include <signal.h>
58
59 #ifdef WIN32
60 #include <io.h>
61 #include <fcntl.h>
62 #endif
63
64 /* warn: = fprintf(stderr,...), with automatic new line */
65 /* log_msg is a generalized version with a syslog like warning
66 level. */
67
68
69 int (*log_msg_hook)(LogLevel l,char *);
70
71 char *(*log_msg_add_note_hook)(LogLevel l);
72
73 static char *log_prefixes[] = { "" , "-I- ", "+W+ ", "*E* " };
74
75 /* internal function */
vlog_msg(LogLevel l,char * format,va_list args)76 static int vlog_msg(LogLevel l, char *format,va_list args) {
77 char buf[1024];
78 char *note = NULL;
79 int ret;
80 int n;
81
82 if ( l > LogError ) l = LogError;
83 strcpy(buf,log_prefixes[l]);
84 n = strlen(log_prefixes[l]);
85 /* reserve 1 char for extra '\n' */
86 ret = vsnprintf(buf+n,sizeof(buf)-n-1,format,args);
87 buf[1023] = 0;
88 n = strlen(buf);
89 /* do quick check for terminators */
90 #ifdef WIN32
91 if ( buf[n-2] == '\r' ) {
92 /* OK */
93 ;
94 } else if ( buf[n-1] == '\n' ) {
95 /* already have unix terminator */
96 buf[n-1] = '\r' ; buf[n] = '\n' ; buf[n+1] = 0 ;
97 } else {
98 /* no terminator */
99 strcat(buf,"\r\n");
100 }
101 #else
102 if ( buf[n-1] != '\n' ) strcat(buf,"\n");
103 #endif
104 /* add program supplied note? */
105 n = 0;
106 if ( log_msg_add_note_hook ) {
107 note = (*log_msg_add_note_hook)(l);
108 if ( note ) n = strlen(note);
109 }
110 {
111 char nbuf[1024+n+1];
112 strcpy(nbuf,buf);
113 if ( n > 0 ) { strcat(nbuf,note); }
114 /* call the hook */
115 if ( log_msg_hook == NULL || log_msg_hook(l,nbuf) == 0 ) {
116 fprintf(stderr,"%s",nbuf);
117 }
118 }
119 return ret;
120 }
121
log_msg(LogLevel l,char * format,...)122 int log_msg(LogLevel l,char *format,...) {
123 va_list args;
124 va_start(args,format);
125 return vlog_msg(l,format,args);
126 }
127
log_error(const char * function,const char * file,const int line,const char * format,...)128 int log_error(const char *function, const char *file, const int line, const char *format,...) {
129 char nformat[1024];
130 va_list args;
131 sprintf(nformat,"Error occurred in %s(), at %s:%d\n",function,file,line);
132 strmcat(nformat,format,1023-(strlen(nformat)+strlen(format)));
133 va_start(args,format);
134 return vlog_msg(LogError,nformat,args);
135 }
136
warn(char * format,...)137 int warn(char *format,...) {
138 va_list args;
139 va_start(args,format);
140 return vlog_msg(LogWarning,format,args);
141 }
142
info(char * format,...)143 int info(char *format,...) {
144 va_list args;
145 va_start(args,format);
146 return vlog_msg(LogInfo,format,args);
147 }
148
149 /* ignore the SIGPIPE signal. Return 0 on success, -1 on error */
ignore_sigpipe(void)150 int ignore_sigpipe(void) {
151 #ifdef WIN32
152 return 0;
153 #else
154 /* this is just posix at present */
155 struct sigaction act;
156
157 act.sa_handler = SIG_IGN;
158 sigemptyset(&act.sa_mask);
159 act.sa_flags = 0;
160 return sigaction(SIGPIPE,&act,NULL);
161 #endif /* WIN32 */
162 }
163
164 /* functions to be applied by the socket routines */
165 static void *(*skt_open_transform)(SOCKET) = NULL;
166 static int (*skt_closer)(void *) = NULL;
167 static int (*skt_reader)(void *, void *, size_t) = NULL;
168 static int (*skt_writer)(void *, const void *,size_t) = NULL;
169 static int sockets_initialized = 0;
170
171 #ifdef WIN32
shutdown_sockets(void)172 static void shutdown_sockets(void) {
173 WSACleanup();
174 /* I don't care what it returns */
175 }
176 #endif /* WIN32 */
177
178 /* initialize sockets */
initialize_sockets(void * (* open_transform)(SOCKET),int (* closer)(void *),int (* reader)(void *,void *,size_t),int (* writer)(void *,const void *,size_t))179 int initialize_sockets(void *(*open_transform)(SOCKET),
180 int (*closer)(void *),
181 int (*reader)(void *, void *, size_t),
182 int (*writer)(void *, const void *,size_t)) {
183 skt_open_transform = open_transform;
184 skt_closer = closer;
185 skt_reader = reader;
186 skt_writer = writer;
187 #ifdef WIN32
188
189 if ( ! sockets_initialized ) {
190 WORD version;
191 WSADATA data;
192 int err;
193
194 version = MAKEWORD( 2, 2 );
195
196 err = WSAStartup( version, &data );
197 if (err != 0) {
198 return 0;
199 }
200 if ((LOBYTE( data.wVersion ) != 2) || (HIBYTE( data.wVersion ) != 2)) {
201 WSACleanup();
202 return 0;
203 }
204 if ( atexit(shutdown_sockets) != 0 ) {
205 warn("couldn't register socket shutdown routine");
206 }
207 }
208 #endif /* WIN32 */
209
210 sockets_initialized = 1;
211 return 1;
212 }
213
214 /* this function takes a string and parses it as an address.
215 It returns 0 for an Internet address, 1 for a Unix address.
216 For Internet addresses, the host name and port are placed
217 in the given variables (the string had better be long enough);
218 for Unix, the file name is copied to the given variable.
219 */
parse_address(const char * address,char * ret_host,unsigned short * ret_port,char * ret_file,size_t n)220 static int parse_address(const char *address,
221 char *ret_host,
222 unsigned short *ret_port,
223 char *ret_file, size_t n) {
224 if ( strchr(address,':') ) {
225 /* grrr */
226 if ( address[0] == ':' ) {
227 strcpy(ret_host,"localhost");
228 sscanf(address,":%hu",ret_port);
229 } else {
230 sscanf(address,"%[^:]:%hu",ret_host,ret_port);
231 }
232 return 0;
233 } else {
234 if ( ret_file) strmcpy(ret_file,address,n);
235 return 1;
236 }
237 }
238
239 /* set_up_listening_socket:
240 Set up a socket listening on the given address.
241 Return its fd.
242 */
set_up_listening_socket(const char * address)243 SOCKET set_up_listening_socket(const char *address) {
244 SOCKET sfd;
245 struct protoent *prstruc = NULL;
246 struct sockaddr_in inaddr;
247 #ifndef WIN32
248 struct sockaddr_un unaddr;
249 #endif
250 int unixsockets;
251 unsigned short port;
252 char name[256];
253 int sockopt;
254
255 if ( ! sockets_initialized ) initialize_sockets(NULL,NULL,NULL,NULL);
256
257 unixsockets =
258 #ifdef WIN32
259 parse_address(address,name,&port,NULL,0);
260 #else
261 parse_address(address,name,&port,unaddr.sun_path,sizeof(unaddr.sun_path));
262 #endif /* WIN32 */
263
264 #ifdef WIN32
265 if ( unixsockets ) {
266 warn("unix sockets not available on Windows");
267 return INVALID_SOCKET;
268 }
269 #endif /* WINDOWS */
270
271 if ( !unixsockets ) {
272 prstruc = getprotobyname("tcp");
273 if ( prstruc == NULL ) {
274 perror("getprotobyname failed");
275 return INVALID_SOCKET;
276 }
277 }
278 sfd = socket(unixsockets ? AF_UNIX : AF_INET,
279 SOCK_STREAM,
280 unixsockets ? 0 : prstruc->p_proto);
281 if ( sfd == INVALID_SOCKET ) {
282 perror("socket failed");
283 return INVALID_SOCKET;
284 }
285
286 sockopt=1;
287 setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,(void *)&sockopt,sizeof(int));
288
289 #ifndef WIN32
290 if ( unixsockets ) {
291 unaddr.sun_family = AF_UNIX;
292 unlink(unaddr.sun_path); /* need to check success FIXME */
293 if ( bind(sfd,(struct sockaddr *)&unaddr,sizeof(struct sockaddr_un)) < 0 ) {
294 perror("bind failed");
295 return INVALID_SOCKET;
296 }
297 } else {
298 #else
299 if ( 1 ) {
300 #endif /* WIN32 */
301 inaddr.sin_family = AF_INET;
302 inaddr.sin_port = htons(port);
303 inaddr.sin_addr.s_addr = INADDR_ANY;
304 if ( bind(sfd,(struct sockaddr *)&inaddr,sizeof(struct sockaddr_in)) < 0 ) {
305 perror("bind failed");
306 return INVALID_SOCKET;
307 }
308 }
309
310 if ( listen(sfd,5) < 0 ) {
311 perror("listen failed");
312 return INVALID_SOCKET;
313 }
314
315 if ( skt_open_transform ) {
316 return (SOCKET) skt_open_transform(sfd);
317 } else {
318 return sfd;
319 }
320 }
321
322
323
324 /* accept_new_connection:
325 A connection has arrived on the socket fd.
326 Accept the connection, and return the new fd,
327 or INVALID_SOCKET on error.
328 */
329
330 SOCKET accept_new_connection(SOCKET fd) {
331 SOCKET newfd;
332 struct sockaddr saddr;
333 struct linger l = { 1, 1000 } ; /* linger on close for 10 seconds */
334 socklen_t saddrlen = sizeof(saddr);
335
336 if ( ! sockets_initialized ) initialize_sockets(NULL,NULL,NULL,NULL);
337
338 newfd = accept(fd,&saddr,&saddrlen);
339 if ( newfd == INVALID_SOCKET ) { perror("accept failed"); }
340 /* we should make sure that data is sent when this socket is closed */
341 if ( setsockopt(newfd,SOL_SOCKET,SO_LINGER,(void *)&l,sizeof(l)) < 0 ) {
342 warn("setsockopt failed");
343 }
344 if ( skt_open_transform ) {
345 return (SOCKET) skt_open_transform(newfd);
346 } else {
347 return newfd;
348 }
349 }
350
351 /* connect_to_host:
352 Establish a connection to the given address.
353 Return the file descriptor or INVALID_SOCKET on error.
354 If unixsockets, only the port number is used in making the name
355 The returned socket is marked close-on-exec .
356 */
357
358 static SOCKET _connect_to_host(int plain, const char *address) {
359 SOCKET fd;
360 struct sockaddr_in inaddr;
361 #ifndef WIN32
362 struct sockaddr_un unaddr;
363 #endif
364 char name[512];
365 int unixsockets;
366 unsigned short port;
367 struct hostent *hent = NULL;
368 struct protoent *prstruc = NULL;
369
370 if ( ! sockets_initialized ) initialize_sockets(NULL,NULL,NULL,NULL);
371
372 #ifdef WIN32
373 unixsockets = parse_address(address,name,&port,NULL,0);
374 if ( unixsockets ) {
375 warn("Unix sockets not supported on Windows");
376 return INVALID_SOCKET;
377 }
378 #else
379 unixsockets = parse_address(address,name,&port,unaddr.sun_path,sizeof(unaddr.sun_path));
380 #endif /* WIN32 */
381
382 if ( !unixsockets ) {
383 hent = gethostbyname(name);
384
385 if ( ! hent ) {
386 perror("connect_to_host: gethostbyname failed");
387 return INVALID_SOCKET;
388 }
389
390 prstruc = getprotobyname("tcp");
391 if ( prstruc == NULL ) {
392 perror("connect_to_host: getprotobyname failed");
393 return INVALID_SOCKET;
394 }
395 }
396
397 fd = socket(unixsockets ? AF_UNIX : AF_INET,
398 SOCK_STREAM,
399 unixsockets ? 0 : prstruc->p_proto);
400 if ( fd == INVALID_SOCKET ) {
401 perror("connect_to_host: socket failed");
402 return INVALID_SOCKET;
403 }
404
405 #ifndef WIN32
406 if ( unixsockets ) {
407 unaddr.sun_family = AF_UNIX;
408 if ( connect(fd,(struct sockaddr *)&unaddr,sizeof(struct sockaddr_un)) < 0 ) {
409 perror("connect_to_host: connect failed");
410 return INVALID_SOCKET;
411 }
412 } else {
413 #else
414 if ( 1 ) {
415 #endif /* WIN32 */
416 inaddr.sin_family = AF_INET;
417 inaddr.sin_port = htons(port);
418 inaddr.sin_addr = *((struct in_addr *)hent->h_addr);
419 if ( connect(fd,(struct sockaddr *)&inaddr,sizeof(struct sockaddr_in)) < 0 ) {
420 perror("connect_to_host: connect failed");
421 return INVALID_SOCKET;
422 }
423 }
424
425 #ifndef WIN32
426 /* mark close on exec */
427 fcntl(fd,F_SETFD,1);
428 #endif /* WIN32 */
429
430 /* Set a five-minute keepalive in order to dissuade natting routers
431 from dropping the connection */
432 { int data = 1;
433 int n;
434 n = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&data, sizeof(int));
435 if ( n < 0 ) {
436 perror("connect_to_host: setsockopt keepalive failed");
437 } else {
438 #ifdef TCP_KEEPIDLE
439 /* this call exists on modern Linuxes, but not everywhere */
440 data = 300;
441 n = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &data, sizeof(int));
442 if ( n < 0 ) {
443 /* of course, this is not supported for unix sockets. We could
444 have checked the address family, but it's probably simpler
445 just to ignore errors */
446 if ( errno != ENOTSUP ) {
447 perror("connect_to_host: setsockopt keepidle failed");
448 }
449 }
450 #endif
451 }
452 }
453
454 if ( !plain && skt_open_transform ) {
455 return (SOCKET) skt_open_transform(fd);
456 } else {
457 return fd;
458 }
459 }
460
461 SOCKET connect_to_host(const char *address) {
462 return _connect_to_host(0,address);
463 }
464
465 SOCKET plain_connect_to_host(const char *address) {
466 return _connect_to_host(1,address);
467 }
468
469
470 /* close a network connection */
471 static int _close_socket(int plain, SOCKET s) {
472 if ( !plain && skt_closer ) {
473 return skt_closer((void *)s);
474 } else {
475 return closesocket(s);
476 }
477 }
478
479 int close_socket(SOCKET s) {
480 return _close_socket(0,s);
481 }
482
483 int plain_close_socket(SOCKET s) {
484 return _close_socket(1,s);
485 }
486
487 /* read a line, up to lf/crlf terminator, from the socket,
488 and return it.
489 Internal backslashes are removed, unless doubled.
490 If the line ends with an unescaped backslash, replace by newline,
491 and append the next line.
492 If there is an error or end of file before seeing a terminator,
493 NULL is returned. At present, error includes reading a partial line.
494 The returned string is valid until the next call of get_line.
495 This is the internal function implementing the next two;
496 the second arg says whether the first is int or SOCKET.
497 */
498 static char *_get_line(SOCKET fd,int is_socket) {
499 int length,i,j;
500 int offset=0;
501 static char *buffer = NULL;
502 static size_t buffer_size = 0;
503
504 while ( 1 ) {
505 while ( 1 ) {
506 while (offset+1 >= (int)buffer_size) { /* +1 to keep some space for '\0' */
507 buffer_size = (buffer_size == 0) ? 1024 : (2 * buffer_size);
508 buffer = realloc(buffer, buffer_size);
509 if (!buffer) {
510 perror("get_line");
511 exit(-1);
512 }
513 }
514 length = (is_socket ?
515 (skt_reader ? skt_reader((void *)fd,buffer+offset,1)
516 :
517 #ifdef WIN32
518 recv(fd,buffer+offset,1,0)
519 #else
520 read(fd,buffer+offset,1)
521 #endif
522 ) : read(fd,buffer+offset,1));
523 if ((length <= 0) || (buffer[offset] == '\n'))
524 break;
525 offset += length;
526 }
527 if ( offset == 0 ) return NULL;
528 if ( length <= 0 ) {
529 fprintf(stderr,"get_line read partial line");
530 return NULL;
531 }
532 offset += length;
533 buffer[offset] = '\0';
534 /* now check for newline */
535 if ( buffer[offset-1] == '\n' ) {
536 if ( buffer[offset-2] == '\r' && buffer[offset-3] == '\\' ) {
537 /* zap the CR */
538 buffer[offset-2] = '\n';
539 buffer[offset-1] = '\0';
540 offset -= 1;
541 /* and go onto next clause */
542 }
543 if ( buffer[offset-2] == '\\' ) {
544 /* we have to decide whether this is an escaped backslash */
545 int j = 1,k=offset-3;
546 while ( k >= 0 && buffer[k--] == '\\' ) j++;
547 if ( j & 1 ) {
548 /* odd number, not escaped */
549 buffer[offset-2] = '\n' ;
550 buffer[--offset] = '\0' ;
551 /* read another line */
552 } else break; /* return this line */
553 } else break; /* return this line */
554 } /* no newline, keep going */
555 }
556 /* now remove internal backslashes */
557 for ( i = j = 0; 1; i++, j++ ) {
558 if ( buffer[j] == '\\' ) {
559 j++;
560 }
561 buffer[i] = buffer[j];
562 if ( !buffer[i] ) break;
563 }
564 return buffer;
565 }
566
567 char *get_line(SOCKET fd) {
568 /* are we actually being asked to read from stdout? Then read
569 from stdin */
570 if ( fd == STDOUT_FILENO ) return _get_line(STDIN_FILENO,0);
571 return _get_line((int)fd,1);
572 }
573
574 char *fd_get_line(int fd) {
575 return _get_line(fd,0);
576 }
577
578 static int _put_line_aux(SOCKET fd, char* line, int length, int is_socket) {
579 int n;
580 if ( is_socket ) {
581 n = skt_writer ? skt_writer((void *)fd,line,length)
582 :
583 #ifdef WIN32
584 send(fd,line,length,0)
585 #else
586 write(fd,line,length)
587 #endif
588 ;
589 } else {
590 n = write(fd,line,length);
591 }
592 if ( n < length ) {
593 perror("put_line: write failed");
594 return -1;
595 }
596 return n;
597 }
598
599 /* write a line (which is assumed to have any terminator included)
600 to the given socket. Return -1 on error, length of line on success.
601 Replace internal newlines by backslash CRLF and write as lines.
602 Double all internal backslashes.
603 Third arg says whether first arg is really an int, or a SOCKET.
604 This is the internal function implemented the next two.
605 */
606 static int _put_line(int fd,char *line,int is_socket) {
607 int length,n;
608 static char *buffer = NULL;
609 static size_t buf_size = 0;
610 int i,j;
611
612 /* the backslash/newline escaping can at worst treble the size
613 of the input */
614 length = strlen(line) + 1; /* include null */
615 while ( (int)buf_size <= 3*length ) {
616 buf_size = buf_size ? 2*buf_size : 1024;
617 buffer = realloc(buffer,buf_size);
618 }
619
620 /* FIXME: should retry if only write partial line */
621 i = j = 0;
622 while ( *line ) {
623 if ( *line == '\\' ) { buffer[i++] = '\\' ; }
624 if ( *line == '\n' && *(line+1) ) {
625 buffer[i++] = '\\' ;
626 buffer[i++] = '\r' ;
627 buffer[i++] = *(line++) ;
628 n = _put_line_aux(fd,buffer,i,is_socket);
629 if ( n < 0 ) { return -1 ; }
630 i = 0;
631 } else {
632 buffer[i++] = *(line++);
633 }
634 }
635 return _put_line_aux(fd,buffer,i,is_socket);
636 }
637
638 int put_line(SOCKET fd,char *line) {
639 /* are we asked to write to stdout? */
640 if ( fd == STDOUT_FILENO ) return _put_line(STDOUT_FILENO,line,0);
641 return _put_line((int)fd,line,1);
642 }
643
644 int fd_put_line(int fd,char *line) {
645 return _put_line(fd,line,0);
646 }
647
648 int put_data(SOCKET fd, char *data, int len) {
649 int n;
650
651 if ( len == 0 ) {
652 # ifdef WIN32
653 # ifdef XCOMPILE
654 n = shutdown(fd, SHUT_WR);
655 # else
656 n = shutdown(fd, SD_SEND);
657 # endif
658 # else
659 n = shutdown(fd, SHUT_WR);
660 # endif
661 if ( n < 0 ) {
662 warn("put_data failed to shutdown: %s",strerror(errno));
663 }
664 return n;
665 }
666 # ifdef WIN32
667 n = send(fd,data,len,0);
668 # else
669 n = write(fd,data,len);
670 # endif
671 if ( n < 0 ) {
672 warn("put_data failed: %s",strerror(errno));
673 }
674 return n;
675 }
676
677 int get_data(SOCKET fd, char *data, int len) {
678 int n;
679 # ifdef WIN32
680 n = recv(fd,data,len,0);
681 # else
682 n = read(fd,data,len);
683 # endif
684 if ( n < 0 ) {
685 warn("put_data failed: %s",strerror(errno));
686 }
687 return n;
688 }
689
690
691 /* random integer from 0 to top inclusive */
692 static int seed = 0;
693
694 unsigned int rand_index(int top) {
695
696 if ( seed == 0 ) {
697 /* seed the generator */
698 rand_seed(0);
699 }
700
701 return (unsigned int)((1.0+top)*rand()/(RAND_MAX+1.0));
702 }
703
704 /* and seed it */
705 void rand_seed(int s) {
706 if ( s == 0 ) s = time(NULL);
707 srand(seed = s);
708 }
709
710 /* This is like strmcat, but it also quotes the string (in a system
711 dependent way) for adding it to a shell command line.
712 */
713 char *qstrmcat(char *dest, const char *src, int len) {
714 char *d = dest;
715 char *s = (char *)src;
716 while ( *d ) d++;
717 if ( len == 0 ) return dest;
718 #ifdef WIN32
719 *d = '"'; d++; len--;
720 #else
721 *d = '\''; d++; len--;
722 #endif
723 while ( *s && len > 0 ) {
724 #ifdef WIN32
725 if ( *s == '"' ) {
726 *d++ = '"'; len--;
727 if ( len > 0 ) {
728 *d++ = *s++; len--;
729 }
730 } else {
731 *d++ = *s++; len--;
732 }
733 #else
734 if ( *s == '\'' || *s == '\\' ) {
735 *d++ = '\''; len--;
736 if ( len > 2 ) {
737 *d++ = '\\'; len--;
738 *d++ = *s++; len--;
739 *d++ = '\''; len--;
740 }
741 } else {
742 *d++ = *s++; len--;
743 }
744 #endif
745 }
746 if ( len > 0 ) {
747 #ifdef WIN32
748 *d++ = '"'; len--;
749 #else
750 *d++ = '\''; len--;
751 #endif
752 }
753 if ( len == 0 ) d--;
754 *d = 0;
755 return dest;
756 }
757
758
759 /* utility function for following: return "NULL" for null string */
760 char *nullprotect(char *s) { return s ? s : "NULL"; }
761
762 /* feed a command to the system to be started in background.
763 No fancy argument processing, just pass to shell or equiv.
764 Return 1 on success, 0 on failure. */
765 int start_background_program(const char *cmd) {
766 char buf[1024];
767 # ifdef WIN32
768 int res;
769 STARTUPINFO si;
770 PROCESS_INFORMATION pi;
771
772 strmcpy(buf,cmd,1023); buf[1023] = 0;
773 memset((void *)&si,0,sizeof(STARTUPINFO));
774 si.cb = sizeof(STARTUPINFO);
775 res = CreateProcess(NULL,buf,NULL,NULL,
776 0,DETACHED_PROCESS,NULL,NULL,&si,&pi);
777 if ( res ) {
778 CloseHandle(pi.hProcess);
779 CloseHandle(pi.hThread);
780 } else {
781 warn("Failed to start process %s",cmd);
782 }
783 return res;
784 # else
785 strmcpy(buf,cmd,sizeof(buf)-strlen(buf)-1);
786 strcat(buf," &");
787 # endif
788 return ! system(buf);
789 }
790
791 /* unfortunately we need a slightly more sophisticated one where
792 we can get at the output. This passes out a write handle for the
793 new process's stdin, and a read handle for its stdout */
794 #ifndef WIN32
795 int start_background_program_with_io(const char *cmd,int *childin,
796 int *childout)
797 {
798 int tochild[2], fromchild[2];
799 int cpid;
800
801 if ( pipe(tochild) < 0 ) {
802 perror("Unable to create pipe");
803 return 0;
804 }
805 if ( pipe(fromchild) < 0 ) {
806 perror("Unable to create pipe: %s");
807 return 0;
808 }
809 cpid = fork();
810 if ( cpid < 0 ) {
811 perror("Unable to fork: %s");
812 }
813 if ( cpid ) {
814 /* parent */
815 close(tochild[0]);
816 close(fromchild[1]);
817 *childin = tochild[1];
818 *childout = fromchild[0];
819 return 1;
820 } else {
821 close(tochild[1]);
822 close(fromchild[0]);
823 dup2(tochild[0],0);
824 close(tochild[0]);
825 dup2(fromchild[1],1);
826 close(fromchild[1]);
827 execl("/bin/sh","sh","-c",cmd,NULL);
828 /* what can we do if we get here? Not a lot... */
829 perror("Exec of child failed");
830 exit(1);
831 }
832 return 0;
833 }
834 #else
835 /* This windows version is loosely based on the example
836 Creating a Child Process with Redirected Input and Output
837 from the MSDN Library. See that example for all the comments
838 explaining what's going on.
839 In fact it's (a) easier than that, since instead of saving, setting
840 and inheriting handles, we can put the pipes into the startup
841 info of the CreateProcess call. (Thanks to somebody on Usenet,
842 no thanks to Microsoft!); (b) harder, because we need to pass
843 back C-style file descriptors, not Windows handles. Hence the mystic
844 call to _open_osfhandle. */
845 int start_background_program_with_io(const char *cmd,int *childin,
846 int *childout)
847 {
848 int res;
849 char buf[1024];
850 STARTUPINFO si;
851 PROCESS_INFORMATION pi;
852 SECURITY_ATTRIBUTES sa;
853 HANDLE tochildr, tochildw, tochildw2,
854 fromchildr, fromchildr2, fromchildw;
855
856 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
857 sa.bInheritHandle = TRUE;
858 sa.lpSecurityDescriptor = NULL;
859 if ( ! CreatePipe(&fromchildr,&fromchildw,&sa,0 ) ) {
860 perror("couldn't create pipe");
861 return 0;
862 }
863 DuplicateHandle(GetCurrentProcess(),
864 fromchildr,GetCurrentProcess(),&fromchildr2,0,
865 FALSE, DUPLICATE_SAME_ACCESS);
866 CloseHandle(fromchildr);
867
868 if ( ! CreatePipe(&tochildr,&tochildw,&sa,0 ) ) {
869 perror("couldn't create pipe");
870 return 0;
871 }
872 DuplicateHandle(GetCurrentProcess(),
873 tochildw,GetCurrentProcess(),&tochildw2,0,
874 FALSE, DUPLICATE_SAME_ACCESS);
875 CloseHandle(tochildw);
876
877 strmcpy(buf,cmd,1023); buf[1023] = 0;
878 memset((void *)&si,0,sizeof(STARTUPINFO));
879 si.cb = sizeof(STARTUPINFO);
880 si.dwFlags = STARTF_USESTDHANDLES;
881 si.hStdOutput = fromchildw;
882 si.hStdInput = tochildr;
883 si.hStdError = 0;
884 res = CreateProcess(NULL,buf,NULL,NULL,
885 TRUE,DETACHED_PROCESS,NULL,NULL,&si,&pi);
886 if ( res ) {
887 CloseHandle(pi.hProcess);
888 CloseHandle(pi.hThread);
889 } else {
890 warn("Failed to start process %s",cmd);
891 }
892 /* I think the mingw has the wrong type for _open_osfhandle ? */
893 *childin = _open_osfhandle((int)tochildw2,_O_WRONLY);
894 *childout = _open_osfhandle((int)fromchildr2,_O_RDONLY);
895 /* do we, as in unix, close our versions of the handles we don't want? */
896 if ( 1 ) {
897 CloseHandle(tochildr);
898 CloseHandle(fromchildw);
899 }
900 return res;
901 }
902 #endif
903
904 /* return a home directory */
905 char *get_homedir(void) {
906 char *p;
907 # ifdef WIN32
908 static char buf[512];
909 buf[0] = 0;
910 if ( (p = getenv("HOMEDRIVE")) ) strcpy(buf,p);
911 if ( (p = getenv("HOMEPATH")) ) strcat(buf,p);
912 if ( buf[0] ) return buf;
913 return NULL;
914 # else
915 struct passwd *pw;
916 if ( (p = getenv("HOME")) ) return p;
917 if ( (pw = getpwuid(getuid())) ) return pw->pw_dir;
918 return NULL;
919 # endif
920 }
921
922 /* stuff for Windows */
923 #ifdef WIN32
924 int gettimeofday(struct timeval *tv, void *tz UNUSED) {
925 long sec, usec;
926 LONGLONG ll;
927 FILETIME ft;
928
929 GetSystemTimeAsFileTime(&ft);
930 ll = ft.dwHighDateTime;
931 ll <<= 32;
932 ll |= ft.dwLowDateTime;
933 /* this magic number comes from Microsoft's knowledge base ' */
934 ll -= 116447360000000000LL; /* 100 nanoseconds since 1970 */
935 ll /= 10; /* microseconds since 1970 */
936 sec = (long)(ll / 1000000);
937 usec = (long)(ll - 1000000*((LONGLONG) sec));
938 if ( tv ) {
939 tv->tv_sec = sec;
940 tv->tv_usec = usec;
941 }
942 return 0;
943 }
944
945 char *getlogin(void) {
946 static char buf[128];
947 DWORD len = 128;
948
949 buf[0] = 0;
950 GetUserName(buf,&len);
951 return buf;
952 }
953
954 int getpid(void) {
955 return GetCurrentProcessId();
956 }
957
958 int unlink(const char *f) {
959 return DeleteFile(f);
960 }
961
962 unsigned int sleep(unsigned int t) { Sleep(1000*t); return 0; }
963
964 #ifndef XCOMPILE
965 /* I don't think we really need submillisecond resolution ... */
966 unsigned int usleep(unsigned int t) { Sleep(t/1000); return 0; }
967 #endif
968
969 #endif /* WIN32 */
970