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