Imported Upstream version 2.5.2p1
[debian/amanda] / common-src / util.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: util.c,v 1.42 2006/08/24 01:57:15 paddy_s Exp $
28  */
29
30 #include "amanda.h"
31 #include "util.h"
32 #include "arglist.h"
33 #include "clock.h"
34
35 /*#define NET_READ_DEBUG*/
36
37 #ifdef NET_READ_DEBUG
38 #define netprintf(x)    dbprintf(x)
39 #else
40 #define netprintf(x)
41 #endif
42
43 static int make_socket(sa_family_t family);
44 static int connect_port(struct sockaddr_storage *addrp, in_port_t port, char *proto,
45                         struct sockaddr_storage *svaddr, int nonblock);
46
47 /*
48  * Keep calling read() until we've read buflen's worth of data, or EOF,
49  * or we get an error.
50  *
51  * Returns the number of bytes read, 0 on EOF, or negative on error.
52  */
53 ssize_t
54 fullread(
55     int         fd,
56     void *      vbuf,
57     size_t      buflen)
58 {
59     ssize_t nread, tot = 0;
60     char *buf = vbuf;   /* cast to char so we can ++ it */
61
62     while (buflen > 0) {
63         nread = read(fd, buf, buflen);
64         if (nread < 0) {
65             if ((errno == EINTR) || (errno == EAGAIN))
66                 continue;
67             return ((tot > 0) ? tot : -1);
68         }
69
70         if (nread == 0)
71             break;
72
73         tot += nread;
74         buf += nread;
75         buflen -= nread;
76     }
77     return (tot);
78 }
79
80 /*
81  * Keep calling write() until we've written buflen's worth of data,
82  * or we get an error.
83  *
84  * Returns the number of bytes written, or negative on error.
85  */
86 ssize_t
87 fullwrite(
88     int         fd,
89     const void *vbuf,
90     size_t      buflen)
91 {
92     ssize_t nwritten, tot = 0;
93     const char *buf = vbuf;     /* cast to char so we can ++ it */
94
95     while (buflen > 0) {
96         nwritten = write(fd, buf, buflen);
97         if (nwritten < 0) {
98             if ((errno == EINTR) || (errno == EAGAIN))
99                 continue;
100             return ((tot > 0) ? tot : -1);
101         }
102         tot += nwritten;
103         buf += nwritten;
104         buflen -= nwritten;
105     }
106     return (tot);
107 }
108
109 static int
110 make_socket(
111     sa_family_t family)
112 {
113     int s;
114     int save_errno;
115 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
116     int on=1;
117     int r;
118 #endif
119
120     s = socket(family, SOCK_STREAM, 0);
121     if (s == -1) {
122         save_errno = errno;
123         dbprintf(("%s: make_socket: socket() failed: %s\n",
124                   debug_prefix_time(NULL),
125                   strerror(save_errno)));
126         errno = save_errno;
127         return -1;
128     }
129     if (s < 0 || s >= (int)FD_SETSIZE) {
130         aclose(s);
131         errno = EMFILE;                         /* out of range */
132         return -1;
133     }
134
135 #ifdef USE_REUSEADDR
136     r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
137     if (r < 0) {
138         save_errno = errno;
139         dbprintf(("%s: stream_server: setsockopt(SO_REUSEADDR) failed: %s\n",
140                   debug_prefix_time(NULL),
141                   strerror(errno)));
142         errno = save_errno;
143     }
144 #endif
145
146 #ifdef SO_KEEPALIVE
147     r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
148                    (void *)&on, SIZEOF(on));
149     if (r == -1) {
150         save_errno = errno;
151         dbprintf(("%s: make_socket: setsockopt() failed: %s\n",
152                   debug_prefix_time(NULL),
153                   strerror(save_errno)));
154         aclose(s);
155         errno = save_errno;
156         return -1;
157     }
158 #endif
159
160     return s;
161 }
162
163 /* addrp is my address */
164 /* svaddr is the address of the remote machine */
165 /* return socket on success */
166 /* return -1     on failure */
167 int
168 connect_portrange(
169     struct sockaddr_storage *addrp,
170     in_port_t           first_port,
171     in_port_t           last_port,
172     char *              proto,
173     struct sockaddr_storage *svaddr,
174     int                 nonblock)
175 {
176     int                 s;
177     in_port_t           port;
178     static in_port_t    port_in_use[1024];
179     static int          nb_port_in_use = 0;
180     int                 i;
181
182     assert(first_port <= last_port);
183     /* Try a port already used */
184     for(i=0; i < nb_port_in_use; i++) {
185         port = port_in_use[i];
186         if(port >= first_port && port <= last_port) {
187             s = connect_port(addrp, port, proto, svaddr, nonblock);
188             if(s == -2) return -1;
189             if(s > 0) {
190                 return s;
191             }
192         }
193     }
194
195     /* Try a port in the range */
196     for (port = first_port; port <= last_port; port++) {
197         s = connect_port(addrp, port, proto, svaddr, nonblock);
198         if(s == -2) return -1;
199         if(s > 0) {
200             port_in_use[nb_port_in_use++] = port;
201             return s;
202         }
203     }
204
205     dbprintf(("%s: connect_portrange: all ports between %d and %d busy\n",
206               debug_prefix_time(NULL),
207               first_port,
208               last_port));
209     errno = EAGAIN;
210     return -1;
211 }
212
213 /* addrp is my address */
214 /* svaddr is the address of the remote machine */
215 /* return -2: Don't try again */
216 /* return -1: Try with another port */
217 /* return >0: this is the connected socket */
218 int
219 connect_port(
220     struct sockaddr_storage *addrp,
221     in_port_t           port,
222     char *              proto,
223     struct sockaddr_storage *svaddr,
224     int                 nonblock)
225 {
226     int                 save_errno;
227     struct servent *    servPort;
228     socklen_t           len;
229     socklen_t           socklen;
230     int                 s;
231
232     servPort = getservbyport((int)htons(port), proto);
233     if (servPort != NULL && !strstr(servPort->s_name, "amanda")) {
234         dbprintf(("%s: connect_port: Skip port %d: Owned by %s.\n",
235                   debug_prefix_time(NULL), port, servPort->s_name));
236         return -1;
237     }
238
239     if(servPort == NULL)
240         dbprintf(("%s: connect_port: Try  port %d: Available   - \n",
241                   debug_prefix_time(NULL), port));
242     else {
243         dbprintf(("%s: connect_port: Try  port %d: Owned by %s - \n",
244                   debug_prefix_time(NULL), port, servPort->s_name));
245     }
246
247     if ((s = make_socket(addrp->ss_family)) == -1) return -2;
248
249     SS_SET_PORT(addrp, port);
250     socklen = SS_LEN(addrp);
251     if (bind(s, (struct sockaddr *)addrp, socklen) != 0) {
252         save_errno = errno;
253         aclose(s);
254         if (save_errno != EADDRINUSE) {
255             dbprintf(("errno %d strerror %s\n",
256                       errno, strerror(errno)));
257             errno = save_errno;
258             return -2;
259         }
260         errno = save_errno;
261         return -1;
262     }
263
264     /* find out what port was actually used */
265
266     len = sizeof(*addrp);
267     if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) {
268         save_errno = errno;
269         dbprintf(("%s: connect_port: getsockname() failed: %s\n",
270                   debug_prefix_time(NULL),
271                   strerror(save_errno)));
272         aclose(s);
273         errno = save_errno;
274         return -1;
275     }
276
277     if (nonblock)
278         fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
279     if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) {
280         save_errno = errno;
281         dbprintf(("%s: connect_portrange: connect from %s failed: %s\n",
282                   debug_prefix_time(NULL),
283                   str_sockaddr(addrp),
284                   strerror(save_errno)));
285         dbprintf(("%s: connect_portrange: connect to %s failed: %s\n",
286                   debug_prefix_time(NULL),
287                   str_sockaddr(svaddr),
288                   strerror(save_errno)));
289         aclose(s);
290         errno = save_errno;
291         if (save_errno == ECONNREFUSED ||
292             save_errno == EHOSTUNREACH ||
293             save_errno == ENETUNREACH ||
294             save_errno == ETIMEDOUT)  {
295             return -2   ;
296         }
297         return -1;
298     }
299
300     dbprintf(("%s: connected to %s\n",
301               debug_prefix_time(NULL),
302               str_sockaddr(svaddr)));
303     dbprintf(("%s: our side is %s\n",
304               debug_prefix_time(NULL),
305               str_sockaddr(addrp)));
306     return s;
307 }
308
309
310 /*
311  * Bind to a port in the given range.  Takes a begin,end pair of port numbers.
312  *
313  * Returns negative on error (EGAIN if all ports are in use).  Returns 0
314  * on success.
315  */
316 int
317 bind_portrange(
318     int                 s,
319     struct sockaddr_storage *addrp,
320     in_port_t           first_port,
321     in_port_t           last_port,
322     char *              proto)
323 {
324     in_port_t port;
325     in_port_t cnt;
326     socklen_t socklen;
327     struct servent *servPort;
328     const in_port_t num_ports = (in_port_t)(last_port - first_port + 1);
329
330     assert(first_port <= last_port);
331
332     /*
333      * We pick a different starting port based on our pid and the current
334      * time to avoid always picking the same reserved port twice.
335      */
336     port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port);
337
338     /*
339      * Scan through the range, trying all available ports that are either 
340      * not taken in /etc/services or registered for *amanda*.  Wrap around
341      * if we don't happen to start at the beginning.
342      */
343     for (cnt = 0; cnt < num_ports; cnt++) {
344         servPort = getservbyport((int)htons(port), proto);
345         if ((servPort == NULL) || strstr(servPort->s_name, "amanda")) {
346             if (servPort == NULL) {
347                 dbprintf(("%s: bind_portrange2: Try  port %d: Available   - ",
348                       debug_prefix_time(NULL), port));
349             } else {
350                 dbprintf(("%s: bind_portrange2: Try  port %d: Owned by %s - ",
351                       debug_prefix_time(NULL), port, servPort->s_name));
352             }
353             SS_SET_PORT(addrp, port);
354             socklen = SS_LEN(addrp);
355             if (bind(s, (struct sockaddr *)addrp, socklen) >= 0) {
356                 dbprintf(("Success\n"));
357                 return 0;
358             }
359             dbprintf(("%s\n", strerror(errno)));
360         } else {
361                 dbprintf(("%s: bind_portrange2: Skip port %d: Owned by %s.\n",
362                       debug_prefix_time(NULL), port, servPort->s_name));
363         }
364         if (++port > last_port)
365             port = first_port;
366     }
367     dbprintf(("%s: bind_portrange: all ports between %d and %d busy\n",
368                   debug_prefix_time(NULL),
369                   first_port,
370                   last_port));
371     errno = EAGAIN;
372     return -1;
373 }
374
375 /*
376  * Construct a datestamp (YYYYMMDD) from a time_t.
377  */
378 char *
379 construct_datestamp(
380     time_t *t)
381 {
382     struct tm *tm;
383     char datestamp[3 * NUM_STR_SIZE];
384     time_t when;
385
386     if (t == NULL) {
387         when = time((time_t *)NULL);
388     } else {
389         when = *t;
390     }
391     tm = localtime(&when);
392     if (!tm)
393         return stralloc("19000101");
394
395     snprintf(datestamp, SIZEOF(datestamp),
396              "%04d%02d%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
397     return stralloc(datestamp);
398 }
399
400 /*
401  * Construct a timestamp (YYYYMMDDHHMMSS) from a time_t.
402  */
403 char *
404 construct_timestamp(
405     time_t *t)
406 {
407     struct tm *tm;
408     char timestamp[6 * NUM_STR_SIZE];
409     time_t when;
410
411     if (t == NULL) {
412         when = time((time_t *)NULL);
413     } else {
414         when = *t;
415     }
416     tm = localtime(&when);
417     if (!tm)
418         return stralloc("19000101000000");
419
420     snprintf(timestamp, SIZEOF(timestamp),
421              "%04d%02d%02d%02d%02d%02d",
422              tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
423              tm->tm_hour, tm->tm_min, tm->tm_sec);
424     return stralloc(timestamp);
425 }
426
427
428 int
429 needs_quotes(
430     const char * str)
431 {
432     return (match("[ \t\f\r\n\"]", str) != 0);
433 }
434
435
436 /*
437  * For backward compatibility we are trying for minimal quoting.
438  * We only quote a string if it contains whitespace or is misquoted...
439  */
440
441 char *
442 quote_string(
443     const char *str)
444 {
445     char *  s;
446     char *  ret;
447
448     if ((str == NULL) || (*str == '\0')) {
449         ret = stralloc("\"\"");
450     } else if ((match("[\\\"[:space:][:cntrl:]]", str)) == 0) {
451         /*
452          * String does not need to be quoted since it contains
453          * neither whitespace, control or quote characters.
454          */
455         ret = stralloc(str);
456     } else {
457         /*
458          * Allocate maximum possible string length.
459          * (a string of all quotes plus room for leading ", trailing " and NULL)
460          */
461         ret = s = alloc((strlen(str) * 2) + 2 + 1);
462         *(s++) = '"';
463         while (*str != '\0') {
464             if (*str == '\t') {
465                 *(s++) = '\\';
466                 *(s++) = 't';
467                 str++;
468                 continue;
469             } else if (*str == '\n') {
470                 *(s++) = '\\';
471                 *(s++) = 'n';
472                 str++;
473                 continue;
474             } else if (*str == '\r') {
475                 *(s++) = '\\';
476                 *(s++) = 'r';
477                 str++;
478                 continue;
479             } else if (*str == '\f') {
480                 *(s++) = '\\';
481                 *(s++) = 'f';
482                 str++;
483                 continue;
484             } else if (*str == '\\') {
485                 *(s++) = '\\';
486                 *(s++) = '\\';
487                 str++;
488                 continue;
489             }
490             if (*str == '"')
491                 *(s++) = '\\';
492             *(s++) = *(str++);
493         }
494         *(s++) = '"';
495         *s = '\0';
496     }
497     return (ret);
498 }
499
500
501 char *
502 unquote_string(
503     const char *str)
504 {
505     char * ret;
506
507     if ((str == NULL) || (*str == '\0')) {
508         ret = stralloc("");
509     } else {
510         char * in;
511         char * out;
512
513         ret = in = out = stralloc(str);
514         while (*in != '\0') {
515             if (*in == '"') {
516                 in++;
517                 continue;
518             }
519
520             if (*in == '\\') {
521                 in++;
522                 if (*in == 'n') {
523                     in++;
524                     *(out++) = '\n';
525                     continue;
526                 } else if (*in == 't') {
527                     in++;
528                     *(out++) = '\t';
529                     continue;
530                 } else if (*in == 'r') {
531                     in++;
532                     *(out++) = '\r';
533                     continue;
534                 } else if (*in == 'f') {
535                     in++;
536                     *(out++) = '\f';
537                     continue;
538                 }
539             }
540             *(out++) = *(in++);
541         }
542         *out = '\0';
543     }
544     return (ret);
545 }
546
547 char *
548 sanitize_string(
549     const char *str)
550 {
551     char * s;
552     char * ret;
553
554     if ((str == NULL) || (*str == '\0')) {
555         ret = stralloc("");
556     } else {
557         ret = stralloc(str);
558         for (s = ret; *s != '\0'; s++) {
559             if (iscntrl(*s))
560                 *s = '?';
561         }
562     }
563     return (ret);
564 }
565
566 char *
567 strquotedstr(void)
568 {
569     char *  tok = strtok(NULL, " ");
570
571     if ((tok != NULL) && (tok[0] == '"')) {
572         char *  t;
573         size_t  len;
574
575         len = strlen(tok);
576         do {
577             t = strtok(NULL, " ");
578             tok[len] = ' ';
579             len = strlen(tok);
580         } while ((t != NULL) &&
581                  (tok[len - 1] != '"') && (tok[len - 2] != '\\'));
582     }
583     return tok;
584 }
585
586 ssize_t
587 hexdump(
588     const char *buffer,
589     size_t      len)
590 {
591     ssize_t rc = -1;
592
593     FILE *stream = popen("od -c -x -", "w");
594         
595     if (stream != NULL) {
596         fflush(stdout);
597         rc = (ssize_t)fwrite(buffer, len, 1, stream);
598         if (ferror(stream))
599             rc = -1;
600         pclose(stream);
601     }
602     return rc;
603 }
604
605 /*
606    Return 0 if the following characters are present
607    * ( ) < > [ ] , ; : ! $ \ / "
608    else returns 1
609 */
610
611 int
612 validate_mailto(
613     const char *mailto)
614 {
615     return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
616 }
617
618
619 void
620 dump_sockaddr(
621     struct sockaddr_storage *sa)
622 {
623 #ifdef WORKING_IPV6
624     char ipstr[INET6_ADDRSTRLEN];
625 #else
626     char ipstr[INET_ADDRSTRLEN];
627 #endif
628     int port;
629
630     port = SS_GET_PORT(sa);
631 #ifdef WORKING_IPV6
632     if ( sa->ss_family == (sa_family_t)AF_INET6) {
633         inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr,
634                   ipstr, sizeof(ipstr));
635         dbprintf(("%s: (sockaddr_in6 *)%p = { %d, %d, %s }\n",
636                   debug_prefix_time(NULL), sa,
637                   ((struct sockaddr_in6 *)sa)->sin6_family,
638                   port,
639                   ipstr));
640     } else
641 #endif
642     {
643         inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, ipstr,
644                   sizeof(ipstr));
645         dbprintf(("%s: (sockaddr_in *)%p = { %d, %d, %s }\n",
646                   debug_prefix_time(NULL), sa,
647                   ((struct sockaddr_in *)sa)->sin_family,
648                   port,
649                   ipstr));
650     }
651 }
652
653
654 #ifdef WORKING_IPV6
655 static char mystr_sockaddr[INET6_ADDRSTRLEN + 20];
656 #else
657 static char mystr_sockaddr[INET_ADDRSTRLEN + 20];
658 #endif
659
660 char *
661 str_sockaddr(
662     struct sockaddr_storage *sa)
663 {
664 #ifdef WORKING_IPV6
665     char ipstr[INET6_ADDRSTRLEN];
666 #else
667     char ipstr[INET_ADDRSTRLEN];
668 #endif
669     int port;
670
671     port = SS_GET_PORT(sa);
672 #ifdef WORKING_IPV6
673     if ( sa->ss_family == (sa_family_t)AF_INET6) {
674         inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr,
675                   ipstr, sizeof(ipstr));
676     } else
677 #endif
678     {
679         inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, ipstr,
680                   sizeof(ipstr));
681     }
682     snprintf(mystr_sockaddr,sizeof(mystr_sockaddr),"%s.%d", ipstr, port);
683     return mystr_sockaddr;
684 }
685
686
687 int
688 cmp_sockaddr(
689     struct sockaddr_storage *ss1,
690     struct sockaddr_storage *ss2,
691     int addr_only)
692 {
693     /* if addresses are v4mapped, "unmap" them */
694 #ifdef WORKING_IPV6
695 #ifdef IN6_IS_ADDR_V4MAPPED
696     struct sockaddr_in ss1_v4;
697     struct sockaddr_in ss2_v4;
698
699     if (ss1->ss_family == AF_INET6 &&
700         IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss1)->sin6_addr)) {
701         memset(&ss1_v4, 0, sizeof(struct sockaddr_in));
702         memcpy(&ss1_v4.sin_addr.s_addr,
703                &(((struct sockaddr_in6 *)ss1)->sin6_addr.s6_addr[12]),
704                sizeof(struct in_addr));
705         ss1_v4.sin_family = AF_INET;
706         SS_SET_PORT((struct sockaddr_storage *)&ss1_v4, SS_GET_PORT(ss1));
707         ss1 = (struct sockaddr_storage *)&ss1_v4;
708     }
709
710     if (ss2->ss_family == AF_INET6 &&
711         IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss2)->sin6_addr)) {
712         memset(&ss2_v4, 0, sizeof(struct sockaddr_in));
713         memcpy(&ss2_v4.sin_addr.s_addr,
714                &(((struct sockaddr_in6 *)ss2)->sin6_addr.s6_addr[12]),
715                sizeof(struct in_addr));
716         ss2_v4.sin_family = AF_INET;
717         SS_SET_PORT((struct sockaddr_storage *)&ss2_v4, SS_GET_PORT(ss2));
718         ss2 = (struct sockaddr_storage *)&ss2_v4;
719     }
720 #endif
721 #endif
722
723     if (ss1->ss_family == ss2->ss_family) {
724         if (addr_only) {
725 #ifdef WORKING_IPV6
726             if(ss1->ss_family == (sa_family_t)AF_INET6)
727                 return memcmp(
728                     &((struct sockaddr_in6 *)ss1)->sin6_addr,
729                     &((struct sockaddr_in6 *)ss2)->sin6_addr,
730                     sizeof(((struct sockaddr_in6 *)ss1)->sin6_addr));
731             else
732 #endif
733                 return memcmp(
734                     &((struct sockaddr_in *)ss1)->sin_addr,
735                     &((struct sockaddr_in *)ss2)->sin_addr,
736                     sizeof(((struct sockaddr_in *)ss1)->sin_addr));
737         } else {
738             return memcmp(ss1, ss2, SS_LEN(ss1));
739         }
740     } else {
741         /* compare families to give a total order */
742         if (ss1->ss_family < ss2->ss_family)
743             return -1;
744         else
745             return 1;
746     }
747 }
748
749
750 int copy_file(
751     char  *dst,
752     char  *src,
753     char **errmsg)
754 {
755     int     infd, outfd;
756     int     save_errno;
757     ssize_t nb;
758     char    buf[32768];
759     char   *quoted;
760
761     if ((infd = open(src, O_RDONLY)) == -1) {
762         save_errno = errno;
763         quoted = quote_string(src);
764         *errmsg = vstralloc("Can't open file ", quoted, " for reading: %s",
765                             strerror(save_errno), NULL);
766         amfree(quoted);
767         return -1;
768     }
769
770     if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
771         save_errno = errno;
772         quoted = quote_string(dst);
773         *errmsg = vstralloc("Can't open file ", quoted, " for writting: %s",
774                             strerror(save_errno), NULL);
775         amfree(quoted);
776         close(infd);
777         return -1;
778     }
779
780     while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
781         if(fullwrite(outfd,&buf,(size_t)nb) < nb) {
782             save_errno = errno;
783             quoted = quote_string(dst);
784             *errmsg = vstralloc("Error writing to \"", quoted, "\":",
785                                 strerror(save_errno), NULL);
786             amfree(quoted);
787             close(infd);
788             close(outfd);
789             return -1;
790         }
791     }
792
793     if (nb < 0) {
794         save_errno = errno;
795         quoted = quote_string(src);
796         *errmsg = vstralloc("Error reading from \"", quoted, "\":",
797                             strerror(save_errno), NULL);
798         amfree(quoted);
799         close(infd);
800         close(outfd);
801         return -1;
802     }
803
804     close(infd);
805     close(outfd);
806     return 0;
807 }
808 #ifndef HAVE_LIBREADLINE
809 /*
810  * simple readline() replacements
811  */
812
813 char *
814 readline(
815     const char *prompt)
816 {
817     printf("%s", prompt);
818     fflush(stdout);
819     fflush(stderr);
820     return agets(stdin);
821 }
822
823 void 
824 add_history(
825     const char *line)
826 {
827     (void)line;         /* Quite unused parameter warning */
828 }
829 #endif