don't interpret %h and %a when using --log-format locally
[rsync/rsync.git] / util.c
CommitLineData
c627d613
AT
1/*
2 Copyright (C) Andrew Tridgell 1996
3 Copyright (C) Paul Mackerras 1996
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18*/
19
20/*
21 Utilities used in rsync
22
23 tridge, June 1996
24 */
25#include "rsync.h"
26
a5343e76
AT
27/****************************************************************************
28Set a fd into nonblocking mode. Uses POSIX O_NONBLOCK if available,
29else
30if SYSV use O_NDELAY
31if BSD use FNDELAY
32****************************************************************************/
33int set_nonblocking(int fd)
94481d91 34{
a5343e76
AT
35 int val;
36#ifdef O_NONBLOCK
37#define FLAG_TO_SET O_NONBLOCK
38#else
39#ifdef SYSV
40#define FLAG_TO_SET O_NDELAY
41#else /* BSD */
42#define FLAG_TO_SET FNDELAY
43#endif
44#endif
45
46 if((val = fcntl(fd, F_GETFL, 0)) == -1)
47 return -1;
48 val |= FLAG_TO_SET;
49 return fcntl( fd, F_SETFL, val);
50#undef FLAG_TO_SET
94481d91
AT
51}
52
d9bea2dd 53
c627d613
AT
54/* this is taken from CVS */
55int piped_child(char **command,int *f_in,int *f_out)
56{
57 int pid;
58 int to_child_pipe[2];
59 int from_child_pipe[2];
60
61 if (pipe(to_child_pipe) < 0 ||
62 pipe(from_child_pipe) < 0) {
9486289c 63 rprintf(FERROR,"pipe: %s\n",strerror(errno));
65417579 64 exit_cleanup(RERR_IPC);
c627d613
AT
65 }
66
67
3ba62a83 68 pid = do_fork();
c627d613 69 if (pid < 0) {
9486289c 70 rprintf(FERROR,"fork: %s\n",strerror(errno));
65417579 71 exit_cleanup(RERR_IPC);
c627d613
AT
72 }
73
74 if (pid == 0)
75 {
6574b4f7 76 extern int orig_umask;
c627d613
AT
77 if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
78 close(to_child_pipe[1]) < 0 ||
79 close(from_child_pipe[0]) < 0 ||
80 dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
9486289c 81 rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
65417579 82 exit_cleanup(RERR_IPC);
c627d613 83 }
773f2bd4
AT
84 if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]);
85 if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]);
6574b4f7 86 umask(orig_umask);
c627d613 87 execvp(command[0], command);
9486289c 88 rprintf(FERROR,"Failed to exec %s : %s\n",
c627d613 89 command[0],strerror(errno));
65417579 90 exit_cleanup(RERR_IPC);
c627d613
AT
91 }
92
93 if (close(from_child_pipe[1]) < 0 ||
94 close(to_child_pipe[0]) < 0) {
9486289c 95 rprintf(FERROR,"Failed to close : %s\n",strerror(errno));
65417579 96 exit_cleanup(RERR_IPC);
c627d613
AT
97 }
98
99 *f_in = from_child_pipe[0];
100 *f_out = to_child_pipe[1];
3eb38818
AT
101
102 set_nonblocking(*f_in);
103 set_nonblocking(*f_out);
c627d613
AT
104
105 return pid;
106}
107
366345fe
AT
108int local_child(int argc, char **argv,int *f_in,int *f_out)
109{
110 int pid;
111 int to_child_pipe[2];
112 int from_child_pipe[2];
113
114 if (pipe(to_child_pipe) < 0 ||
115 pipe(from_child_pipe) < 0) {
9486289c 116 rprintf(FERROR,"pipe: %s\n",strerror(errno));
65417579 117 exit_cleanup(RERR_IPC);
366345fe
AT
118 }
119
120
121 pid = do_fork();
122 if (pid < 0) {
9486289c 123 rprintf(FERROR,"fork: %s\n",strerror(errno));
65417579 124 exit_cleanup(RERR_IPC);
366345fe
AT
125 }
126
127 if (pid == 0) {
128 extern int am_sender;
129 extern int am_server;
130
131 am_sender = !am_sender;
132 am_server = 1;
133
134 if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
135 close(to_child_pipe[1]) < 0 ||
136 close(from_child_pipe[0]) < 0 ||
137 dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
9486289c 138 rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
65417579 139 exit_cleanup(RERR_IPC);
366345fe
AT
140 }
141 if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]);
142 if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]);
9486289c 143 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
366345fe
AT
144 }
145
146 if (close(from_child_pipe[1]) < 0 ||
147 close(to_child_pipe[0]) < 0) {
9486289c 148 rprintf(FERROR,"Failed to close : %s\n",strerror(errno));
65417579 149 exit_cleanup(RERR_IPC);
366345fe
AT
150 }
151
152 *f_in = from_child_pipe[0];
153 *f_out = to_child_pipe[1];
154
155 return pid;
156}
157
158
c627d613
AT
159
160void out_of_memory(char *str)
161{
9486289c 162 rprintf(FERROR,"ERROR: out of memory in %s\n",str);
65417579 163 exit_cleanup(RERR_MALLOC);
575f2fca
AT
164}
165
166void overflow(char *str)
167{
9486289c 168 rprintf(FERROR,"ERROR: buffer overflow in %s\n",str);
65417579 169 exit_cleanup(RERR_MALLOC);
c627d613
AT
170}
171
172
c627d613
AT
173
174int set_modtime(char *fname,time_t modtime)
175{
31e12522
AT
176 extern int dry_run;
177 if (dry_run) return 0;
178 {
1e9f155a 179#ifdef HAVE_UTIMBUF
31e12522
AT
180 struct utimbuf tbuf;
181 tbuf.actime = time(NULL);
182 tbuf.modtime = modtime;
183 return utime(fname,&tbuf);
c627d613 184#elif defined(HAVE_UTIME)
31e12522
AT
185 time_t t[2];
186 t[0] = time(NULL);
187 t[1] = modtime;
188 return utime(fname,t);
c627d613 189#else
31e12522
AT
190 struct timeval t[2];
191 t[0].tv_sec = time(NULL);
192 t[0].tv_usec = 0;
193 t[1].tv_sec = modtime;
194 t[1].tv_usec = 0;
195 return utimes(fname,t);
c627d613 196#endif
31e12522 197 }
c627d613 198}
94481d91 199
720b47f2 200
6574b4f7
AT
201/****************************************************************************
202create any necessary directories in fname. Unfortunately we don't know
203what perms to give the directory when this is called so we need to rely
204on the umask
205****************************************************************************/
206int create_directory_path(char *fname)
207{
208 extern int orig_umask;
209 char *p;
210
211 while (*fname == '/') fname++;
212 while (strncmp(fname,"./",2)==0) fname += 2;
213
214 p = fname;
215 while ((p=strchr(p,'/'))) {
216 *p = 0;
1b2d733a 217 do_mkdir(fname,0777 & ~orig_umask);
6574b4f7
AT
218 *p = '/';
219 p++;
220 }
221 return 0;
222}
950ab32d
AT
223
224
225/* Write LEN bytes at PTR to descriptor DESC, retrying if interrupted.
226 Return LEN upon success, write's (negative) error code otherwise.
227
228 derived from GNU C's cccp.c.
229*/
a5343e76 230static int full_write(int desc, char *ptr, int len)
950ab32d
AT
231{
232 int total_written;
233
234 total_written = 0;
235 while (len > 0) {
236 int written = write (desc, ptr, len);
237 if (written < 0) {
238#ifdef EINTR
239 if (errno == EINTR)
240 continue;
241#endif
242 return written;
243 }
244 total_written += written;
245 ptr += written;
246 len -= written;
247 }
248 return total_written;
249}
250
251/* Read LEN bytes at PTR from descriptor DESC, retrying if interrupted.
252 Return the actual number of bytes read, zero for EOF, or negative
253 for an error.
254
255 derived from GNU C's cccp.c. */
6e4fb64e 256static int safe_read(int desc, char *ptr, int len)
950ab32d
AT
257{
258 int n_chars;
259
260 if (len <= 0)
261 return len;
262
263#ifdef EINTR
264 do {
265 n_chars = read(desc, ptr, len);
266 } while (n_chars < 0 && errno == EINTR);
267#else
268 n_chars = read(desc, ptr, len);
269#endif
270
271 return n_chars;
272}
273
274
275/* copy a file - this is used in conjunction with the --temp-dir option */
276int copy_file(char *source, char *dest, mode_t mode)
277{
278 int ifd;
279 int ofd;
280 char buf[1024 * 8];
281 int len; /* Number of bytes read into `buf'. */
282
283 ifd = open(source, O_RDONLY);
284 if (ifd == -1) {
9486289c 285 rprintf(FERROR,"open %s: %s\n",
950ab32d
AT
286 source,strerror(errno));
287 return -1;
288 }
289
31e12522 290 if (do_unlink(dest) && errno != ENOENT) {
9486289c 291 rprintf(FERROR,"unlink %s: %s\n",
950ab32d
AT
292 dest,strerror(errno));
293 return -1;
294 }
295
31e12522 296 ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);
c46ded46 297 if (ofd == -1) {
9486289c 298 rprintf(FERROR,"open %s: %s\n",
950ab32d
AT
299 dest,strerror(errno));
300 close(ifd);
301 return -1;
302 }
303
304 while ((len = safe_read(ifd, buf, sizeof(buf))) > 0) {
305 if (full_write(ofd, buf, len) < 0) {
9486289c 306 rprintf(FERROR,"write %s: %s\n",
950ab32d
AT
307 dest,strerror(errno));
308 close(ifd);
309 close(ofd);
310 return -1;
311 }
312 }
313
314 close(ifd);
315 close(ofd);
316
317 if (len < 0) {
9486289c 318 rprintf(FERROR,"read %s: %s\n",
950ab32d
AT
319 source,strerror(errno));
320 return -1;
321 }
322
323 return 0;
324}
feaa89c4
AT
325
326/* sleep for a while via select */
327void u_sleep(int usec)
328{
329 struct timeval tv;
330
331 tv.tv_sec = 0;
332 tv.tv_usec = usec;
333 select(0, NULL, NULL, NULL, &tv);
334}
3ba62a83
AT
335
336
337static pid_t all_pids[10];
338static int num_pids;
339
340/* fork and record the pid of the child */
341pid_t do_fork(void)
342{
343 pid_t newpid = fork();
344
345 if (newpid) {
346 all_pids[num_pids++] = newpid;
347 }
348 return newpid;
349}
350
351/* kill all children */
352void kill_all(int sig)
353{
354 int i;
355 for (i=0;i<num_pids;i++) {
356 if (all_pids[i] != getpid())
357 kill(all_pids[i], sig);
358 }
359}
9486289c 360
8ef4ffd6
AT
361/* turn a user name into a uid */
362int name_to_uid(char *name, uid_t *uid)
363{
364 struct passwd *pass;
365 if (!name || !*name) return 0;
366 pass = getpwnam(name);
367 if (pass) {
368 *uid = pass->pw_uid;
369 return 1;
370 }
371 return 0;
372}
373
374/* turn a group name into a gid */
375int name_to_gid(char *name, gid_t *gid)
376{
377 struct group *grp;
378 if (!name || !*name) return 0;
379 grp = getgrnam(name);
380 if (grp) {
381 *gid = grp->gr_gid;
382 return 1;
383 }
384 return 0;
385}
386
ff8b29b8 387
31593dd6
AT
388/* lock a byte range in a open file */
389int lock_range(int fd, int offset, int len)
0c515f17 390{
31593dd6 391 struct flock lock;
0c515f17 392
31593dd6
AT
393 lock.l_type = F_WRLCK;
394 lock.l_whence = SEEK_SET;
395 lock.l_start = offset;
396 lock.l_len = len;
397 lock.l_pid = 0;
398
399 return fcntl(fd,F_SETLK,&lock) == 0;
0c515f17 400}
874895d5
AT
401
402
087bf010 403static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
874895d5 404{
932be9aa 405#if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
e42c9458 406 if (!*s) s = ".";
087bf010 407 argv[*argc] = strdup(s);
874895d5
AT
408 (*argc)++;
409 return;
410#else
411 glob_t globbuf;
412 int i;
413
e42c9458
AT
414 if (!*s) s = ".";
415
087bf010
AT
416 argv[*argc] = strdup(s);
417
874895d5
AT
418 memset(&globbuf, 0, sizeof(globbuf));
419 glob(argv[*argc], 0, NULL, &globbuf);
420 if (globbuf.gl_pathc == 0) {
421 (*argc)++;
422 globfree(&globbuf);
423 return;
424 }
425 for (i=0; i<(maxargs - (*argc)) && i<globbuf.gl_pathc;i++) {
426 if (i == 0) free(argv[*argc]);
427 argv[(*argc) + i] = strdup(globbuf.gl_pathv[i]);
428 if (!argv[(*argc) + i]) out_of_memory("glob_expand");
429 }
430 globfree(&globbuf);
431 (*argc) += i;
432#endif
433}
5a96ee05 434
ba5e128d 435void glob_expand(char *base1, char **argv, int *argc, int maxargs)
087bf010
AT
436{
437 char *s = argv[*argc];
438 char *p, *q;
ba5e128d 439 char *base = base1;
087bf010
AT
440
441 if (!s || !*s) return;
442
e42c9458
AT
443 if (strncmp(s, base, strlen(base)) == 0) {
444 s += strlen(base);
445 }
446
087bf010
AT
447 s = strdup(s);
448 if (!s) out_of_memory("glob_expand");
449
ba5e128d
AT
450 base = (char *)malloc(strlen(base1)+3);
451 if (!base) out_of_memory("glob_expand");
452
453 sprintf(base," %s/", base1);
454
087bf010
AT
455 q = s;
456 while ((p = strstr(q,base)) && ((*argc) < maxargs)) {
ba5e128d
AT
457 /* split it at this point */
458 *p = 0;
459 glob_expand_one(q, argv, argc, maxargs);
460 q = p+strlen(base);
087bf010
AT
461 }
462
463 if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs);
464
465 free(s);
ba5e128d 466 free(base);
087bf010 467}
5a96ee05
AT
468
469/*******************************************************************
470 convert a string to lower case
471********************************************************************/
472void strlower(char *s)
473{
474 while (*s) {
475 if (isupper(*s)) *s = tolower(*s);
476 s++;
477 }
478}
e42c9458 479
37f9805d
AT
480/* this is like vsnprintf but it always null terminates, so you
481 can fit at most n-1 chars in */
e42c9458
AT
482int vslprintf(char *str, int n, const char *format, va_list ap)
483{
e42c9458 484 int ret = vsnprintf(str, n, format, ap);
37f9805d
AT
485 if (ret >= n || ret < 0) {
486 str[n-1] = 0;
e42c9458
AT
487 return -1;
488 }
489 str[ret] = 0;
490 return ret;
e42c9458
AT
491}
492
493
494/* like snprintf but always null terminates */
495int slprintf(char *str, int n, char *format, ...)
496{
497 va_list ap;
498 int ret;
499
500 va_start(ap, format);
501 ret = vslprintf(str,n,format,ap);
502 va_end(ap);
503 return ret;
504}
8d9dc9f9 505
fe8c0a98
AT
506
507void *Realloc(void *p, int size)
508{
509 if (!p) return (void *)malloc(size);
510 return (void *)realloc(p, size);
511}
5243c216
AT
512
513
514void clean_fname(char *name)
515{
516 char *p;
517 int l;
518 int modified = 1;
519
520 if (!name) return;
521
522 while (modified) {
523 modified = 0;
524
525 if ((p=strstr(name,"/./"))) {
526 modified = 1;
527 while (*p) {
528 p[0] = p[2];
529 p++;
530 }
531 }
532
533 if ((p=strstr(name,"//"))) {
534 modified = 1;
535 while (*p) {
536 p[0] = p[1];
537 p++;
538 }
539 }
540
541 if (strncmp(p=name,"./",2) == 0) {
542 modified = 1;
543 do {
544 p[0] = p[2];
545 } while (*p++);
546 }
547
548 l = strlen(p=name);
549 if (l > 1 && p[l-1] == '/') {
550 modified = 1;
551 p[l-1] = 0;
552 }
553 }
554}
555
1b8e662a
DD
556/*
557 * Make path appear as if a chroot had occurred:
1b8e662a
DD
558 * 1. remove leading "/" (or replace with "." if at end)
559 * 2. remove leading ".." components
560 * 3. delete any other "<dir>/.." (recursively)
b5f9e67d
DD
561 * While we're at it, remove double slashes and "." components like
562 * clean_fname does(), but DON'T remove a trailing slash because that
563 * is sometimes significant on command line arguments.
1b8e662a
DD
564 * Return a malloc'ed copy.
565 * Contributed by Dave Dykstra <dwd@bell-labs.com>
566 */
567
568char *sanitize_path(char *p)
569{
570 char *copy, *copyp;
571
1b8e662a
DD
572 copy = (char *) malloc(strlen(p)+1);
573 copyp = copy;
b5f9e67d
DD
574 while (*p == '/') {
575 /* remove leading slashes */
576 p++;
577 }
1b8e662a 578 while (*p != '\0') {
b5f9e67d
DD
579 /* this loop iterates once per filename component in p.
580 * both p (and copyp if the original had a slash) should
581 * always be left pointing after a slash
582 */
583 if ((*p == '.') && ((*(p+1) == '/') || (*(p+1) == '\0'))) {
584 /* skip "." component */
585 while (*++p == '/') {
586 /* skip following slashes */
587 ;
588 }
589 } else if ((*p == '.') && (*(p+1) == '.') &&
1b8e662a 590 ((*(p+2) == '/') || (*(p+2) == '\0'))) {
b5f9e67d 591 /* skip ".." component followed by slash or end */
1b8e662a 592 p += 2;
b5f9e67d
DD
593 if (*p == '/')
594 p++;
1b8e662a 595 if (copyp != copy) {
b5f9e67d
DD
596 /* back up the copy one level */
597 --copyp; /* now pointing at slash */
598 while ((copyp > copy) && (*(copyp - 1) != '/')) {
599 /* skip back up to slash */
1b8e662a 600 copyp--;
b5f9e67d 601 }
1b8e662a
DD
602 }
603 } else {
1b8e662a 604 while (1) {
b5f9e67d 605 /* copy one component through next slash */
1b8e662a 606 *copyp++ = *p++;
b5f9e67d
DD
607 if ((*p == '\0') || (*(p-1) == '/')) {
608 while (*p == '/') {
609 /* skip multiple slashes */
610 p++;
611 }
1b8e662a 612 break;
b5f9e67d 613 }
1b8e662a
DD
614 }
615 }
616 }
b5f9e67d
DD
617 if (copyp == copy) {
618 /* ended up with nothing, so put in "." component */
619 *copyp++ = '.';
620 }
1b8e662a
DD
621 *copyp = '\0';
622 return(copy);
623}
624
5243c216
AT
625
626static char curr_dir[MAXPATHLEN];
627
628/* like chdir() but can be reversed with pop_dir() if save is set. It
629 is also much faster as it remembers where we have been */
630char *push_dir(char *dir, int save)
631{
632 char *ret = curr_dir;
633 static int initialised;
634
635 if (!initialised) {
636 initialised = 1;
637 getcwd(curr_dir, sizeof(curr_dir)-1);
638 }
639
640 if (chdir(dir)) return NULL;
641
642 if (save) {
643 ret = strdup(curr_dir);
644 }
645
646 if (*dir == '/') {
37f9805d 647 strlcpy(curr_dir, dir, sizeof(curr_dir));
5243c216 648 } else {
37f9805d
AT
649 strlcat(curr_dir,"/", sizeof(curr_dir));
650 strlcat(curr_dir,dir, sizeof(curr_dir));
5243c216
AT
651 }
652
653 clean_fname(curr_dir);
654
655 return ret;
656}
657
658/* reverse a push_dir call */
659int pop_dir(char *dir)
660{
661 int ret;
662
663 ret = chdir(dir);
664 if (ret) {
665 free(dir);
666 return ret;
667 }
668
37f9805d 669 strlcpy(curr_dir, dir, sizeof(curr_dir));
5243c216
AT
670
671 free(dir);
672
673 return 0;
674}
aa9b77a5
AT
675
676/* we need to supply our own strcmp function for file list comparisons
677 to ensure that signed/unsigned usage is consistent between machines. */
678int u_strcmp(const char *cs1, const char *cs2)
679{
5a788ade
AT
680 const uchar *s1 = (const uchar *)cs1;
681 const uchar *s2 = (const uchar *)cs2;
aa9b77a5
AT
682
683 while (*s1 && *s2 && (*s1 == *s2)) {
684 s1++; s2++;
685 }
686
687 return (int)*s1 - (int)*s2;
688}
eb86d661 689
43b06eea 690static OFF_T last_ofs;
eb86d661
AT
691
692void end_progress(void)
693{
694 extern int do_progress, am_server;
695
696 if (do_progress && !am_server) {
697 rprintf(FINFO,"\n");
698 }
43b06eea 699 last_ofs = 0;
eb86d661
AT
700}
701
702void show_progress(OFF_T ofs, OFF_T size)
703{
704 extern int do_progress, am_server;
705
706 if (do_progress && !am_server) {
43b06eea
AT
707 if (ofs > last_ofs + 1000) {
708 int pct = (int)((100.0*ofs)/size);
eb86d661 709 rprintf(FINFO,"%.0f (%d%%)\r", (double)ofs, pct);
43b06eea 710 last_ofs = ofs;
eb86d661
AT
711 }
712 }
713}
4b957c22
AT
714
715/* determine if a symlink points outside the current directory tree */
716int unsafe_symlink(char *dest, char *src)
717{
718 char *tok;
719 int depth = 0;
720
721 /* all absolute and null symlinks are unsafe */
722 if (!dest || !(*dest) || (*dest == '/')) return 1;
723
724 src = strdup(src);
725 if (!src) out_of_memory("unsafe_symlink");
726
727 /* find out what our safety margin is */
728 for (tok=strtok(src,"/"); tok; tok=strtok(NULL,"/")) {
729 if (strcmp(tok,"..") == 0) {
730 depth=0;
731 } else if (strcmp(tok,".") == 0) {
732 /* nothing */
733 } else {
734 depth++;
735 }
736 }
737 free(src);
738
739 /* drop by one to account for the filename portion */
740 depth--;
741
742 dest = strdup(dest);
743 if (!dest) out_of_memory("unsafe_symlink");
744
745 for (tok=strtok(dest,"/"); tok; tok=strtok(NULL,"/")) {
746 if (strcmp(tok,"..") == 0) {
747 depth--;
748 } else if (strcmp(tok,".") == 0) {
749 /* nothing */
750 } else {
751 depth++;
752 }
753 /* if at any point we go outside the current directory then
754 stop - it is unsafe */
755 if (depth < 0) break;
756 }
757
758 free(dest);
759 return (depth < 0);
760}
375a4556 761
f7632fc6
AT
762
763/****************************************************************************
764 return the date and time as a string
765****************************************************************************/
766char *timestring(time_t t)
767{
768 static char TimeBuf[200];
769 struct tm *tm = localtime(&t);
770
771#ifdef HAVE_STRFTIME
772 strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %T",tm);
773#else
37f9805d 774 strlcpy(TimeBuf, asctime(tm), sizeof(TimeBuf));
f7632fc6
AT
775#endif
776
777 if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
778 TimeBuf[strlen(TimeBuf)-1] = 0;
779 }
780
781 return(TimeBuf);
782}
783