Added the human_readable variable (for linking with util.o).
[rsync/rsync.git] / util.c
CommitLineData
1960e228 1/* -*- c-file-style: "linux" -*-
5cb37436
WD
2 *
3 * Copyright (C) 1996-2000 by Andrew Tridgell
0ecfbf27
MP
4 * Copyright (C) Paul Mackerras 1996
5 * Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
5cb37436 6 *
0ecfbf27
MP
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
5cb37436 11 *
0ecfbf27
MP
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
5cb37436 16 *
0ecfbf27
MP
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
c627d613 21
ac13ad10 22/**
0ecfbf27 23 * @file
ac13ad10 24 *
5cb37436 25 * Utilities used in rsync
ac13ad10 26 **/
c627d613 27
c627d613
AT
28#include "rsync.h"
29
c7c11a0d 30extern int verbose;
bc6ebcd2
WD
31extern int dry_run;
32extern int module_id;
33extern int modify_window;
7e43da81 34extern int relative_paths;
a7260c40 35extern char *partial_dir;
7842418b 36extern struct filter_list_struct server_filter_list;
c7c11a0d 37
0ecfbf27
MP
38int sanitize_paths = 0;
39
40
f0359dd0 41
ac13ad10 42/**
0ecfbf27
MP
43 * Set a fd into nonblocking mode
44 **/
f0359dd0
AT
45void set_nonblocking(int fd)
46{
47 int val;
48
0ecfbf27 49 if ((val = fcntl(fd, F_GETFL, 0)) == -1)
f0359dd0
AT
50 return;
51 if (!(val & NONBLOCK_FLAG)) {
52 val |= NONBLOCK_FLAG;
53 fcntl(fd, F_SETFL, val);
54 }
55}
56
ac13ad10 57/**
0ecfbf27
MP
58 * Set a fd into blocking mode
59 **/
36349ea0
AT
60void set_blocking(int fd)
61{
62 int val;
63
0ecfbf27 64 if ((val = fcntl(fd, F_GETFL, 0)) == -1)
36349ea0
AT
65 return;
66 if (val & NONBLOCK_FLAG) {
67 val &= ~NONBLOCK_FLAG;
68 fcntl(fd, F_SETFL, val);
69 }
70}
71
f0359dd0 72
ac13ad10 73/**
0ecfbf27
MP
74 * Create a file descriptor pair - like pipe() but use socketpair if
75 * possible (because of blocking issues on pipes).
5cb37436 76 *
0ecfbf27 77 * Always set non-blocking.
f0359dd0 78 */
08f15335
AT
79int fd_pair(int fd[2])
80{
f0359dd0
AT
81 int ret;
82
4f5b0756 83#ifdef HAVE_SOCKETPAIR
f0359dd0 84 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
08f15335 85#else
f0359dd0 86 ret = pipe(fd);
08f15335 87#endif
f0359dd0
AT
88
89 if (ret == 0) {
90 set_nonblocking(fd[0]);
91 set_nonblocking(fd[1]);
92 }
0ecfbf27 93
f0359dd0 94 return ret;
08f15335
AT
95}
96
97
0ecfbf27 98void print_child_argv(char **cmd)
5ad0e46f 99{
1bbd10fe 100 rprintf(FINFO, "opening connection using ");
5ad0e46f
MP
101 for (; *cmd; cmd++) {
102 /* Look for characters that ought to be quoted. This
103 * is not a great quoting algorithm, but it's
104 * sufficient for a log message. */
105 if (strspn(*cmd, "abcdefghijklmnopqrstuvwxyz"
106 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
107 "0123456789"
108 ",.-_=+@/") != strlen(*cmd)) {
f8cd88db 109 rprintf(FINFO, "\"%s\" ", safe_fname(*cmd));
5ad0e46f 110 } else {
f8cd88db 111 rprintf(FINFO, "%s ", safe_fname(*cmd));
5ad0e46f
MP
112 }
113 }
114 rprintf(FINFO, "\n");
115}
116
117
c627d613
AT
118void out_of_memory(char *str)
119{
c284f34a
WD
120 rprintf(FERROR, "ERROR: out of memory in %s\n", str);
121 exit_cleanup(RERR_MALLOC);
575f2fca
AT
122}
123
a1f99493 124void overflow_exit(char *str)
575f2fca 125{
c284f34a
WD
126 rprintf(FERROR, "ERROR: buffer overflow in %s\n", str);
127 exit_cleanup(RERR_MALLOC);
c627d613
AT
128}
129
130
c627d613 131
25007999 132int set_modtime(char *fname, time_t modtime, mode_t mode)
c627d613 133{
25007999
WD
134#if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
135 if (S_ISLNK(mode))
136 return 1;
137#endif
138
404e813c
MP
139 if (verbose > 2) {
140 rprintf(FINFO, "set modtime of %s to (%ld) %s",
f8cd88db 141 safe_fname(fname), (long)modtime,
404e813c
MP
142 asctime(localtime(&modtime)));
143 }
5cb37436 144
15778afb
WD
145 if (dry_run)
146 return 0;
147
31e12522 148 {
25007999
WD
149#ifdef HAVE_UTIMES
150 struct timeval t[2];
151 t[0].tv_sec = time(NULL);
152 t[0].tv_usec = 0;
153 t[1].tv_sec = modtime;
154 t[1].tv_usec = 0;
155# ifdef HAVE_LUTIMES
156 if (S_ISLNK(mode))
157 return lutimes(fname, t);
158# endif
159 return utimes(fname, t);
160#elif defined HAVE_UTIMBUF
5cb37436 161 struct utimbuf tbuf;
31e12522
AT
162 tbuf.actime = time(NULL);
163 tbuf.modtime = modtime;
164 return utime(fname,&tbuf);
4f5b0756 165#elif defined HAVE_UTIME
31e12522
AT
166 time_t t[2];
167 t[0] = time(NULL);
168 t[1] = modtime;
169 return utime(fname,t);
c627d613 170#else
25007999 171#error No file-time-modification routine found!
c627d613 172#endif
31e12522 173 }
c627d613 174}
94481d91 175
720b47f2 176
ac13ad10
MP
177/**
178 Create any necessary directories in fname. Unfortunately we don't know
179 what perms to give the directory when this is called so we need to rely
180 on the umask
181**/
0ecfbf27 182int create_directory_path(char *fname, int base_umask)
6574b4f7 183{
6574b4f7
AT
184 char *p;
185
c284f34a
WD
186 while (*fname == '/')
187 fname++;
188 while (strncmp(fname, "./", 2) == 0)
189 fname += 2;
6574b4f7
AT
190
191 p = fname;
c284f34a 192 while ((p = strchr(p,'/')) != NULL) {
6574b4f7 193 *p = 0;
5cb37436 194 do_mkdir(fname, 0777 & ~base_umask);
6574b4f7
AT
195 *p = '/';
196 p++;
197 }
198 return 0;
199}
950ab32d
AT
200
201
ac13ad10
MP
202/**
203 * Write @p len bytes at @p ptr to descriptor @p desc, retrying if
204 * interrupted.
205 *
206 * @retval len upon success
207 *
208 * @retval <0 write's (negative) error code
209 *
210 * Derived from GNU C's cccp.c.
211 */
6566d205 212int full_write(int desc, char *ptr, size_t len)
950ab32d
AT
213{
214 int total_written;
5cb37436 215
950ab32d
AT
216 total_written = 0;
217 while (len > 0) {
5c1b7bfd 218 int written = write(desc, ptr, len);
950ab32d 219 if (written < 0) {
950ab32d
AT
220 if (errno == EINTR)
221 continue;
950ab32d
AT
222 return written;
223 }
224 total_written += written;
225 ptr += written;
226 len -= written;
227 }
228 return total_written;
229}
230
950ab32d 231
ac13ad10
MP
232/**
233 * Read @p len bytes at @p ptr from descriptor @p desc, retrying if
234 * interrupted.
235 *
236 * @retval >0 the actual number of bytes read
237 *
238 * @retval 0 for EOF
239 *
240 * @retval <0 for an error.
241 *
242 * Derived from GNU C's cccp.c. */
9dd891bb 243static int safe_read(int desc, char *ptr, size_t len)
950ab32d
AT
244{
245 int n_chars;
5cb37436 246
9dd891bb 247 if (len == 0)
950ab32d 248 return len;
5cb37436 249
950ab32d
AT
250 do {
251 n_chars = read(desc, ptr, len);
252 } while (n_chars < 0 && errno == EINTR);
5cb37436 253
950ab32d
AT
254 return n_chars;
255}
256
257
ac13ad10
MP
258/** Copy a file.
259 *
3e13004b
WD
260 * This is used in conjunction with the --temp-dir, --backup, and
261 * --copy-dest options. */
950ab32d
AT
262int copy_file(char *source, char *dest, mode_t mode)
263{
264 int ifd;
265 int ofd;
266 char buf[1024 * 8];
267 int len; /* Number of bytes read into `buf'. */
268
8c9fd200 269 ifd = do_open(source, O_RDONLY, 0);
950ab32d 270 if (ifd == -1) {
d62bcc17 271 rsyserr(FERROR, errno, "open %s", full_fname(source));
950ab32d
AT
272 return -1;
273 }
274
c7c11a0d 275 if (robust_unlink(dest) && errno != ENOENT) {
d62bcc17 276 rsyserr(FERROR, errno, "unlink %s", full_fname(dest));
950ab32d
AT
277 return -1;
278 }
279
31e12522 280 ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);
c46ded46 281 if (ofd == -1) {
d62bcc17 282 rsyserr(FERROR, errno, "open %s", full_fname(dest));
950ab32d
AT
283 close(ifd);
284 return -1;
285 }
286
5cb37436 287 while ((len = safe_read(ifd, buf, sizeof buf)) > 0) {
950ab32d 288 if (full_write(ofd, buf, len) < 0) {
d62bcc17 289 rsyserr(FERROR, errno, "write %s", full_fname(dest));
950ab32d
AT
290 close(ifd);
291 close(ofd);
292 return -1;
293 }
294 }
295
8b602edd 296 if (len < 0) {
d62bcc17 297 rsyserr(FERROR, errno, "read %s", full_fname(source));
8b602edd
WD
298 close(ifd);
299 close(ofd);
300 return -1;
301 }
302
9f27cd8c 303 if (close(ifd) < 0) {
d62bcc17
WD
304 rsyserr(FINFO, errno, "close failed on %s",
305 full_fname(source));
9f27cd8c
WD
306 }
307
308 if (close(ofd) < 0) {
d62bcc17
WD
309 rsyserr(FERROR, errno, "close failed on %s",
310 full_fname(dest));
9f27cd8c
WD
311 return -1;
312 }
950ab32d 313
950ab32d
AT
314 return 0;
315}
feaa89c4 316
c7c11a0d
DD
317/* MAX_RENAMES should be 10**MAX_RENAMES_DIGITS */
318#define MAX_RENAMES_DIGITS 3
319#define MAX_RENAMES 1000
320
ac13ad10 321/**
b4235b31
MP
322 * Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so
323 * rename to <path>/.rsyncNNN instead.
324 *
325 * Note that successive rsync runs will shuffle the filenames around a
326 * bit as long as the file is still busy; this is because this function
327 * does not know if the unlink call is due to a new file coming in, or
328 * --delete trying to remove old .rsyncNNN files, hence it renames it
329 * each time.
330 **/
c7c11a0d
DD
331int robust_unlink(char *fname)
332{
333#ifndef ETXTBSY
334 return do_unlink(fname);
335#else
336 static int counter = 1;
337 int rc, pos, start;
338 char path[MAXPATHLEN];
339
340 rc = do_unlink(fname);
c284f34a 341 if (rc == 0 || errno != ETXTBSY)
c7c11a0d
DD
342 return rc;
343
c284f34a
WD
344 if ((pos = strlcpy(path, fname, MAXPATHLEN)) >= MAXPATHLEN)
345 pos = MAXPATHLEN - 1;
c7c11a0d 346
c284f34a
WD
347 while (pos > 0 && path[pos-1] != '/')
348 pos--;
5cb37436 349 pos += strlcpy(path+pos, ".rsync", MAXPATHLEN-pos);
c7c11a0d
DD
350
351 if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) {
352 errno = ETXTBSY;
353 return -1;
354 }
355
356 /* start where the last one left off to reduce chance of clashes */
357 start = counter;
358 do {
359 sprintf(&path[pos], "%03d", counter);
360 if (++counter >= MAX_RENAMES)
361 counter = 1;
c284f34a 362 } while ((rc = access(path, 0)) == 0 && counter != start);
c7c11a0d 363
4791825d 364 if (verbose > 0) {
c7c11a0d 365 rprintf(FINFO,"renaming %s to %s because of text busy\n",
f8cd88db 366 safe_fname(fname), safe_fname(path));
4791825d 367 }
c7c11a0d
DD
368
369 /* maybe we should return rename()'s exit status? Nah. */
370 if (do_rename(fname, path) != 0) {
371 errno = ETXTBSY;
372 return -1;
373 }
374 return 0;
375#endif
376}
377
630f548f
WD
378/* Returns 0 on successful rename, 1 if we successfully copied the file
379 * across filesystems, -2 if copy_file() failed, and -1 on other errors. */
62c9e6b3 380int robust_rename(char *from, char *to, int mode)
c7c11a0d 381{
62c9e6b3
WD
382 int tries = 4;
383
384 while (tries--) {
385 if (do_rename(from, to) == 0)
386 return 0;
387
388 switch (errno) {
389#ifdef ETXTBSY
390 case ETXTBSY:
391 if (robust_unlink(to) != 0)
392 return -1;
393 break;
c7c11a0d 394#endif
62c9e6b3
WD
395 case EXDEV:
396 if (copy_file(from, to, mode) != 0)
397 return -2;
398 do_unlink(from);
630f548f 399 return 1;
62c9e6b3
WD
400 default:
401 return -1;
402 }
403 }
404 return -1;
feaa89c4 405}
3ba62a83
AT
406
407
408static pid_t all_pids[10];
409static int num_pids;
410
4cf64834 411/** Fork and record the pid of the child. **/
3ba62a83
AT
412pid_t do_fork(void)
413{
414 pid_t newpid = fork();
5cb37436 415
4cf64834 416 if (newpid != 0 && newpid != -1) {
3ba62a83
AT
417 all_pids[num_pids++] = newpid;
418 }
419 return newpid;
420}
421
4cf64834
MP
422/**
423 * Kill all children.
424 *
425 * @todo It would be kind of nice to make sure that they are actually
426 * all our children before we kill them, because their pids may have
427 * been recycled by some other process. Perhaps when we wait for a
428 * child, we should remove it from this array. Alternatively we could
429 * perhaps use process groups, but I think that would not work on
430 * ancient Unix versions that don't support them.
431 **/
3ba62a83
AT
432void kill_all(int sig)
433{
434 int i;
4cf64834
MP
435
436 for (i = 0; i < num_pids; i++) {
437 /* Let's just be a little careful where we
438 * point that gun, hey? See kill(2) for the
439 * magic caused by negative values. */
440 pid_t p = all_pids[i];
441
442 if (p == getpid())
443 continue;
444 if (p <= 0)
445 continue;
446
447 kill(p, sig);
3ba62a83
AT
448 }
449}
9486289c 450
4cf64834 451
ac13ad10 452/** Turn a user name into a uid */
8ef4ffd6
AT
453int name_to_uid(char *name, uid_t *uid)
454{
455 struct passwd *pass;
b5bd5542
WD
456 if (!name || !*name)
457 return 0;
8ef4ffd6
AT
458 pass = getpwnam(name);
459 if (pass) {
460 *uid = pass->pw_uid;
461 return 1;
462 }
463 return 0;
464}
465
ac13ad10 466/** Turn a group name into a gid */
8ef4ffd6
AT
467int name_to_gid(char *name, gid_t *gid)
468{
469 struct group *grp;
b5bd5542
WD
470 if (!name || !*name)
471 return 0;
8ef4ffd6
AT
472 grp = getgrnam(name);
473 if (grp) {
474 *gid = grp->gr_gid;
475 return 1;
476 }
477 return 0;
478}
479
ff8b29b8 480
ac13ad10 481/** Lock a byte range in a open file */
31593dd6 482int lock_range(int fd, int offset, int len)
0c515f17 483{
31593dd6 484 struct flock lock;
0c515f17 485
31593dd6
AT
486 lock.l_type = F_WRLCK;
487 lock.l_whence = SEEK_SET;
488 lock.l_start = offset;
489 lock.l_len = len;
490 lock.l_pid = 0;
5cb37436 491
31593dd6 492 return fcntl(fd,F_SETLK,&lock) == 0;
0c515f17 493}
874895d5 494
7842418b 495static int filter_server_path(char *arg)
4791825d
WD
496{
497 char *s;
4791825d 498
7842418b 499 if (server_filter_list.head) {
4791825d
WD
500 for (s = arg; (s = strchr(s, '/')) != NULL; ) {
501 *s = '\0';
7842418b 502 if (check_filter(&server_filter_list, arg, 1) < 0) {
4791825d
WD
503 /* We must leave arg truncated! */
504 return 1;
505 }
506 *s++ = '/';
507 }
508 }
509 return 0;
510}
874895d5 511
b7061c82
WD
512static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr,
513 int *maxargs_ptr)
874895d5 514{
b7061c82 515 char **argv = *argv_ptr;
b5bd5542 516 int argc = *argc_ptr;
b7061c82 517 int maxargs = *maxargs_ptr;
4f5b0756 518#if !defined HAVE_GLOB || !defined HAVE_GLOB_H
b7061c82
WD
519 if (argc == maxargs) {
520 maxargs += MAX_ARGS;
521 if (!(argv = realloc_array(argv, char *, maxargs)))
522 out_of_memory("glob_expand_one");
523 *argv_ptr = argv;
524 *maxargs_ptr = maxargs;
525 }
4135d091
WD
526 if (!*s)
527 s = ".";
b5bd5542 528 s = argv[argc++] = strdup(s);
7842418b 529 filter_server_path(s);
874895d5
AT
530#else
531 glob_t globbuf;
874895d5 532
b5bd5542
WD
533 if (maxargs <= argc)
534 return;
4135d091
WD
535 if (!*s)
536 s = ".";
e42c9458 537
4135d091 538 if (sanitize_paths)
1d6b8f9a 539 s = sanitize_path(NULL, s, "", 0);
84a63795
WD
540 else
541 s = strdup(s);
087bf010 542
5cb37436 543 memset(&globbuf, 0, sizeof globbuf);
7842418b 544 if (!filter_server_path(s))
4791825d 545 glob(s, 0, NULL, &globbuf);
b7061c82
WD
546 if (MAX((int)globbuf.gl_pathc, 1) > maxargs - argc) {
547 maxargs += globbuf.gl_pathc + MAX_ARGS;
548 if (!(argv = realloc_array(argv, char *, maxargs)))
549 out_of_memory("glob_expand_one");
550 *argv_ptr = argv;
551 *maxargs_ptr = maxargs;
552 }
b5bd5542
WD
553 if (globbuf.gl_pathc == 0)
554 argv[argc++] = s;
555 else {
c8933031 556 int i;
b5bd5542 557 free(s);
c8933031 558 for (i = 0; i < (int)globbuf.gl_pathc; i++) {
b5bd5542
WD
559 if (!(argv[argc++] = strdup(globbuf.gl_pathv[i])))
560 out_of_memory("glob_expand_one");
561 }
874895d5
AT
562 }
563 globfree(&globbuf);
874895d5 564#endif
b5bd5542 565 *argc_ptr = argc;
874895d5 566}
5a96ee05 567
4791825d 568/* This routine is only used in daemon mode. */
b7061c82 569void glob_expand(char *base1, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr)
087bf010 570{
b7061c82 571 char *s = (*argv_ptr)[*argc_ptr];
087bf010 572 char *p, *q;
ba5e128d 573 char *base = base1;
4791825d 574 int base_len = strlen(base);
087bf010 575
b5bd5542
WD
576 if (!s || !*s)
577 return;
087bf010 578
4791825d
WD
579 if (strncmp(s, base, base_len) == 0)
580 s += base_len;
e42c9458 581
b5bd5542
WD
582 if (!(s = strdup(s)))
583 out_of_memory("glob_expand");
087bf010 584
b5bd5542
WD
585 if (asprintf(&base," %s/", base1) <= 0)
586 out_of_memory("glob_expand");
4791825d 587 base_len++;
ba5e128d 588
b5bd5542
WD
589 for (q = s; *q; q = p + base_len) {
590 if ((p = strstr(q, base)) != NULL)
591 *p = '\0'; /* split it at this point */
b7061c82 592 glob_expand_one(q, argv_ptr, argc_ptr, maxargs_ptr);
b5bd5542
WD
593 if (!p)
594 break;
087bf010
AT
595 }
596
087bf010 597 free(s);
ba5e128d 598 free(base);
087bf010 599}
5a96ee05 600
ac13ad10
MP
601/**
602 * Convert a string to lower case
603 **/
5a96ee05
AT
604void strlower(char *s)
605{
606 while (*s) {
b5bd5542
WD
607 if (isupper(*(unsigned char *)s))
608 *s = tolower(*(unsigned char *)s);
5a96ee05
AT
609 s++;
610 }
611}
e42c9458 612
368ad70e
WD
613/* Join strings p1 & p2 into "dest" with a guaranteed '/' between them. (If
614 * p1 ends with a '/', no extra '/' is inserted.) Returns the length of both
a8f7e4b8
WD
615 * strings + 1 (if '/' was inserted), regardless of whether the null-terminated
616 * string fits into destsize. */
368ad70e
WD
617size_t pathjoin(char *dest, size_t destsize, const char *p1, const char *p2)
618{
619 size_t len = strlcpy(dest, p1, destsize);
620 if (len < destsize - 1) {
621 if (!len || dest[len-1] != '/')
622 dest[len++] = '/';
623 if (len < destsize - 1)
624 len += strlcpy(dest + len, p2, destsize - len);
625 else {
626 dest[len] = '\0';
627 len += strlen(p2);
628 }
629 }
630 else
631 len += strlen(p2) + 1; /* Assume we'd insert a '/'. */
632 return len;
633}
634
635/* Join any number of strings together, putting them in "dest". The return
a8f7e4b8
WD
636 * value is the length of all the strings, regardless of whether the null-
637 * terminated whole fits in destsize. Your list of string pointers must end
638 * with a NULL to indicate the end of the list. */
368ad70e
WD
639size_t stringjoin(char *dest, size_t destsize, ...)
640{
5cb37436 641 va_list ap;
368ad70e
WD
642 size_t len, ret = 0;
643 const char *src;
644
645 va_start(ap, destsize);
646 while (1) {
647 if (!(src = va_arg(ap, const char *)))
648 break;
649 len = strlen(src);
650 ret += len;
651 if (destsize > 1) {
652 if (len >= destsize)
653 len = destsize - 1;
654 memcpy(dest, src, len);
655 destsize -= len;
656 dest += len;
657 }
658 }
659 *dest = '\0';
660 va_end(ap);
661
662 return ret;
663}
664
1d6b8f9a
WD
665int count_dir_elements(const char *p)
666{
667 int cnt = 0, new_component = 1;
668 while (*p) {
669 if (*p++ == '/')
670 new_component = 1;
671 else if (new_component) {
672 new_component = 0;
673 cnt++;
674 }
675 }
676 return cnt;
677}
678
b92693da
WD
679/* Turns multiple adjacent slashes into a single slash, gets rid of "./"
680 * elements (but not a trailing dot dir), removes a trailing slash, and
681 * optionally collapses ".." elements (except for those at the start of the
682 * string). If the resulting name would be empty, change it into a ".". */
683unsigned int clean_fname(char *name, BOOL collapse_dot_dot)
5243c216 684{
e012b94f 685 char *limit = name - 1, *t = name, *f = name;
ebdd24d6 686 int anchored;
5243c216 687
b5bd5542 688 if (!name)
3104620c 689 return 0;
5243c216 690
ebdd24d6
WD
691 if ((anchored = *f == '/') != 0)
692 *t++ = *f++;
693 while (*f) {
694 /* discard extra slashes */
695 if (*f == '/') {
696 f++;
697 continue;
5243c216 698 }
ebdd24d6
WD
699 if (*f == '.') {
700 /* discard "." dirs (but NOT a trailing '.'!) */
701 if (f[1] == '/') {
e012b94f 702 f += 2;
ebdd24d6
WD
703 continue;
704 }
705 /* collapse ".." dirs */
b92693da
WD
706 if (collapse_dot_dot
707 && f[1] == '.' && (f[2] == '/' || !f[2])) {
ebdd24d6
WD
708 char *s = t - 1;
709 if (s == name && anchored) {
710 f += 2;
711 continue;
712 }
713 while (s > limit && *--s != '/') {}
e012b94f 714 if (s != t - 1 && (s < name || *s == '/')) {
ebdd24d6
WD
715 t = s + 1;
716 f += 2;
717 continue;
718 }
f55c2dfc 719 limit = t + 2;
5243c216
AT
720 }
721 }
ebdd24d6 722 while (*f && (*t++ = *f++) != '/') {}
5243c216 723 }
ebdd24d6
WD
724
725 if (t > name+anchored && t[-1] == '/')
726 t--;
727 if (t == name)
728 *t++ = '.';
729 *t = '\0';
3104620c
WD
730
731 return t - name;
5243c216
AT
732}
733
84a63795
WD
734/* Make path appear as if a chroot had occurred. This handles a leading
735 * "/" (either removing it or expanding it) and any leading or embedded
736 * ".." components that attempt to escape past the module's top dir.
b4235b31 737 *
1d6b8f9a
WD
738 * If dest is NULL, a buffer is allocated to hold the result. It is legal
739 * to call with the dest and the path (p) pointing to the same buffer, but
740 * rootdir will be ignored to avoid expansion of the string.
b4235b31 741 *
1d6b8f9a
WD
742 * The rootdir string contains a value to use in place of a leading slash.
743 * Specify NULL to get the default of lp_path(module_id).
ac13ad10 744 *
5886edfa
WD
745 * If depth is >= 0, it is a count of how many '..'s to allow at the start
746 * of the path. Use -1 to allow unlimited depth.
ac13ad10 747 *
b92693da
WD
748 * We also clean the path in a manner similar to clean_fname() but with a
749 * few differences:
750 *
751 * Turns multiple adjacent slashes into a single slash, gets rid of "." dir
752 * elements (INCLUDING a trailing dot dir), PRESERVES a trailing slash, and
753 * ALWAYS collapses ".." elements (except for those at the start of the
754 * string up to "depth" deep). If the resulting name would be empty,
755 * change it into a ".". */
1d6b8f9a 756char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth)
1b8e662a 757{
44e2e578 758 char *start, *sanp;
7e43da81 759 int rlen = 0, leave_one_dotdir = relative_paths;
84a63795
WD
760
761 if (dest != p) {
762 int plen = strlen(p);
1d6b8f9a
WD
763 if (*p == '/') {
764 if (!rootdir)
765 rootdir = lp_path(module_id);
766 rlen = strlen(rootdir);
767 depth = 0;
84a63795
WD
768 p++;
769 }
770 if (dest) {
771 if (rlen + plen + 1 >= MAXPATHLEN)
772 return NULL;
773 } else if (!(dest = new_array(char, rlen + plen + 1)))
774 out_of_memory("sanitize_path");
775 if (rlen) {
1d6b8f9a 776 memcpy(dest, rootdir, rlen);
84a63795
WD
777 if (rlen > 1)
778 dest[rlen++] = '/';
779 }
780 }
cb13abfe 781
84a63795 782 start = sanp = dest + rlen;
1b8e662a 783 while (*p != '\0') {
2d41264e
WD
784 /* discard leading or extra slashes */
785 if (*p == '/') {
786 p++;
787 continue;
788 }
b5f9e67d 789 /* this loop iterates once per filename component in p.
44e2e578 790 * both p (and sanp if the original had a slash) should
b5f9e67d
DD
791 * always be left pointing after a slash
792 */
c284f34a 793 if (*p == '.' && (p[1] == '/' || p[1] == '\0')) {
35812ea1 794 if (leave_one_dotdir && p[1])
7e43da81
WD
795 leave_one_dotdir = 0;
796 else {
797 /* skip "." component */
798 p++;
799 continue;
800 }
cb13abfe 801 }
c284f34a 802 if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) {
cb13abfe 803 /* ".." component followed by slash or end */
8e5f029e
WD
804 if (depth <= 0 || sanp != start) {
805 p += 2;
806 if (sanp != start) {
807 /* back up sanp one level */
808 --sanp; /* now pointing at slash */
809 while (sanp > start && sanp[-1] != '/') {
810 /* skip back up to slash */
811 sanp--;
812 }
b5f9e67d 813 }
8e5f029e 814 continue;
1b8e662a 815 }
8e5f029e
WD
816 /* allow depth levels of .. at the beginning */
817 depth--;
818 /* move the virtual beginning to leave the .. alone */
819 start = sanp + 3;
1b8e662a 820 }
2d41264e
WD
821 /* copy one component through next slash */
822 while (*p && (*sanp++ = *p++) != '/') {}
1b8e662a 823 }
84a63795 824 if (sanp == dest) {
b5f9e67d 825 /* ended up with nothing, so put in "." component */
44e2e578 826 *sanp++ = '.';
b5f9e67d 827 }
44e2e578 828 *sanp = '\0';
1b8e662a 829
84a63795 830 return dest;
14b61c63 831}
5243c216 832
4791825d 833char curr_dir[MAXPATHLEN];
4af8fe4e 834unsigned int curr_dir_len;
5243c216 835
4e5db0ad 836/**
a16d8f2b
WD
837 * Like chdir(), but it keeps track of the current directory (in the
838 * global "curr_dir"), and ensures that the path size doesn't overflow.
839 * Also cleans the path using the clean_fname() function.
4e5db0ad 840 **/
4af8fe4e 841int push_dir(char *dir)
5243c216 842{
5243c216 843 static int initialised;
4af8fe4e 844 unsigned int len;
5243c216
AT
845
846 if (!initialised) {
847 initialised = 1;
5cb37436 848 getcwd(curr_dir, sizeof curr_dir - 1);
4af8fe4e 849 curr_dir_len = strlen(curr_dir);
5243c216
AT
850 }
851
4af8fe4e
WD
852 if (!dir) /* this call was probably just to initialize */
853 return 0;
c226b7c2 854
4af8fe4e
WD
855 len = strlen(dir);
856 if (len == 1 && *dir == '.')
857 return 1;
5243c216 858
4af8fe4e
WD
859 if ((*dir == '/' ? len : curr_dir_len + 1 + len) >= sizeof curr_dir)
860 return 0;
861
862 if (chdir(dir))
863 return 0;
5243c216
AT
864
865 if (*dir == '/') {
4af8fe4e
WD
866 memcpy(curr_dir, dir, len + 1);
867 curr_dir_len = len;
868 } else {
869 curr_dir[curr_dir_len++] = '/';
870 memcpy(curr_dir + curr_dir_len, dir, len + 1);
871 curr_dir_len += len;
5243c216
AT
872 }
873
b92693da 874 curr_dir_len = clean_fname(curr_dir, 1);
5243c216 875
4af8fe4e 876 return 1;
5243c216
AT
877}
878
a16d8f2b
WD
879/**
880 * Reverse a push_dir() call. You must pass in an absolute path
881 * that was copied from a prior value of "curr_dir".
882 **/
5243c216
AT
883int pop_dir(char *dir)
884{
4af8fe4e
WD
885 if (chdir(dir))
886 return 0;
5243c216 887
4af8fe4e
WD
888 curr_dir_len = strlcpy(curr_dir, dir, sizeof curr_dir);
889 if (curr_dir_len >= sizeof curr_dir)
890 curr_dir_len = sizeof curr_dir - 1;
5243c216 891
4af8fe4e 892 return 1;
5243c216 893}
aa9b77a5 894
89d26123
WD
895/* Return the filename, turning any non-printable characters into escaped
896 * characters (e.g. \n -> \012, \ -> \\). This ensures that outputting it
897 * cannot generate an empty line nor corrupt the screen. This function can
898 * return only MAX_SAFE_NAMES values at a time! The returned value can be
899 * longer than MAXPATHLEN (because we may be trying to output an error about
900 * a too-long filename)! */
af9f56a0 901char *safe_fname(const char *fname)
820b6c9a 902{
f8cd88db
WD
903#define MAX_SAFE_NAMES 4
904 static char fbuf[MAX_SAFE_NAMES][MAXPATHLEN*2];
905 static int ndx = 0;
906 int limit = sizeof fbuf / MAX_SAFE_NAMES - 1;
907 char *t;
908
909 ndx = (ndx + 1) % MAX_SAFE_NAMES;
6f2245c8 910 for (t = fbuf[ndx]; *fname; fname++) {
89d26123
WD
911 if (*fname == '\\') {
912 if ((limit -= 2) < 0)
913 break;
914 *t++ = '\\';
915 *t++ = '\\';
916 } else if (!isprint(*(uchar*)fname)) {
917 if ((limit -= 4) < 0)
918 break;
919 sprintf(t, "\\%03o", *(uchar*)fname);
920 t += 4;
921 } else {
6f2245c8
WD
922 if (--limit < 0)
923 break;
f8cd88db 924 *t++ = *fname;
89d26123 925 }
f8cd88db
WD
926 }
927 *t = '\0';
820b6c9a 928
f8cd88db 929 return fbuf[ndx];
820b6c9a
WD
930}
931
eb61be19
WD
932/**
933 * Return a quoted string with the full pathname of the indicated filename.
934 * The string " (in MODNAME)" may also be appended. The returned pointer
935 * remains valid until the next time full_fname() is called.
936 **/
9a5ade18 937char *full_fname(const char *fn)
eb61be19 938{
eb61be19
WD
939 static char *result = NULL;
940 char *m1, *m2, *m3;
941 char *p1, *p2;
942
943 if (result)
944 free(result);
945
af1a3f9b 946 fn = safe_fname(fn);
eb61be19
WD
947 if (*fn == '/')
948 p1 = p2 = "";
949 else {
950 p1 = curr_dir;
bc83274a
WD
951 for (p2 = p1; *p2 == '/'; p2++) {}
952 if (*p2)
953 p2 = "/";
eb61be19
WD
954 }
955 if (module_id >= 0) {
956 m1 = " (in ";
957 m2 = lp_name(module_id);
958 m3 = ")";
bc83274a 959 if (p1 == curr_dir) {
eb61be19
WD
960 if (!lp_use_chroot(module_id)) {
961 char *p = lp_path(module_id);
962 if (*p != '/' || p[1])
963 p1 += strlen(p);
964 }
eb61be19 965 }
eb61be19
WD
966 } else
967 m1 = m2 = m3 = "";
968
969 asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3);
970
971 return result;
972}
973
a7260c40
WD
974static char partial_fname[MAXPATHLEN];
975
976char *partial_dir_fname(const char *fname)
977{
978 char *t = partial_fname;
979 int sz = sizeof partial_fname;
980 const char *fn;
981
982 if ((fn = strrchr(fname, '/')) != NULL) {
983 fn++;
984 if (*partial_dir != '/') {
985 int len = fn - fname;
986 strncpy(t, fname, len); /* safe */
987 t += len;
988 sz -= len;
989 }
990 } else
991 fn = fname;
992 if ((int)pathjoin(t, sz, partial_dir, fn) >= sz)
993 return NULL;
5aa7b20a
WD
994 if (server_filter_list.head) {
995 static int len;
996 if (!len)
997 len = strlen(partial_dir);
998 t[len] = '\0';
999 if (check_filter(&server_filter_list, partial_fname, 1) < 0)
1000 return NULL;
1001 t[len] = '/';
1002 if (check_filter(&server_filter_list, partial_fname, 0) < 0)
1003 return NULL;
1004 }
a7260c40
WD
1005
1006 return partial_fname;
1007}
1008
1009/* If no --partial-dir option was specified, we don't need to do anything
1010 * (the partial-dir is essentially '.'), so just return success. */
1011int handle_partial_dir(const char *fname, int create)
1012{
1013 char *fn, *dir;
1014
1015 if (fname != partial_fname)
1016 return 1;
1017 if (!create && *partial_dir == '/')
1018 return 1;
1019 if (!(fn = strrchr(partial_fname, '/')))
1020 return 1;
1021
1022 *fn = '\0';
1023 dir = partial_fname;
1024 if (create) {
1025 STRUCT_STAT st;
a7260c40 1026 int statret = do_lstat(dir, &st);
a7260c40
WD
1027 if (statret == 0 && !S_ISDIR(st.st_mode)) {
1028 if (do_unlink(dir) < 0)
1029 return 0;
1030 statret = -1;
1031 }
1032 if (statret < 0 && do_mkdir(dir, 0700) < 0)
1033 return 0;
1034 } else
1035 do_rmdir(dir);
1036 *fn = '/';
1037
1038 return 1;
1039}
1040
ac13ad10 1041/** We need to supply our own strcmp function for file list comparisons
aa9b77a5
AT
1042 to ensure that signed/unsigned usage is consistent between machines. */
1043int u_strcmp(const char *cs1, const char *cs2)
1044{
5a788ade
AT
1045 const uchar *s1 = (const uchar *)cs1;
1046 const uchar *s2 = (const uchar *)cs2;
aa9b77a5
AT
1047
1048 while (*s1 && *s2 && (*s1 == *s2)) {
1049 s1++; s2++;
1050 }
5cb37436 1051
aa9b77a5
AT
1052 return (int)*s1 - (int)*s2;
1053}
eb86d661 1054
4b957c22 1055
ac13ad10
MP
1056
1057/**
1058 * Determine if a symlink points outside the current directory tree.
036e70b0
MP
1059 * This is considered "unsafe" because e.g. when mirroring somebody
1060 * else's machine it might allow them to establish a symlink to
1061 * /etc/passwd, and then read it through a web server.
1062 *
4e5db0ad
MP
1063 * Null symlinks and absolute symlinks are always unsafe.
1064 *
1065 * Basically here we are concerned with symlinks whose target contains
1066 * "..", because this might cause us to walk back up out of the
1067 * transferred directory. We are not allowed to go back up and
1068 * reenter.
1069 *
036e70b0
MP
1070 * @param dest Target of the symlink in question.
1071 *
25d34a5c 1072 * @param src Top source directory currently applicable. Basically this
036e70b0 1073 * is the first parameter to rsync in a simple invocation, but it's
25d34a5c 1074 * modified by flist.c in slightly complex ways.
036e70b0
MP
1075 *
1076 * @retval True if unsafe
1077 * @retval False is unsafe
4e5db0ad
MP
1078 *
1079 * @sa t_unsafe.c
ac13ad10 1080 **/
7afa3a4a 1081int unsafe_symlink(const char *dest, const char *src)
4b957c22 1082{
7afa3a4a 1083 const char *name, *slash;
4b957c22
AT
1084 int depth = 0;
1085
1086 /* all absolute and null symlinks are unsafe */
b5bd5542
WD
1087 if (!dest || !*dest || *dest == '/')
1088 return 1;
4b957c22
AT
1089
1090 /* find out what our safety margin is */
7afa3a4a
WD
1091 for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) {
1092 if (strncmp(name, "../", 3) == 0) {
c284f34a 1093 depth = 0;
7afa3a4a 1094 } else if (strncmp(name, "./", 2) == 0) {
4b957c22
AT
1095 /* nothing */
1096 } else {
1097 depth++;
1098 }
1099 }
7afa3a4a
WD
1100 if (strcmp(name, "..") == 0)
1101 depth = 0;
4b957c22 1102
7afa3a4a
WD
1103 for (name = dest; (slash = strchr(name, '/')) != 0; name = slash+1) {
1104 if (strncmp(name, "../", 3) == 0) {
1105 /* if at any point we go outside the current directory
1106 then stop - it is unsafe */
1107 if (--depth < 0)
1108 return 1;
1109 } else if (strncmp(name, "./", 2) == 0) {
4b957c22
AT
1110 /* nothing */
1111 } else {
1112 depth++;
1113 }
4b957c22 1114 }
7afa3a4a
WD
1115 if (strcmp(name, "..") == 0)
1116 depth--;
4b957c22 1117
4b957c22
AT
1118 return (depth < 0);
1119}
375a4556 1120
f7632fc6 1121
ac13ad10 1122/**
b4235b31
MP
1123 * Return the date and time as a string
1124 **/
f7632fc6
AT
1125char *timestring(time_t t)
1126{
1127 static char TimeBuf[200];
1128 struct tm *tm = localtime(&t);
1129
4f5b0756 1130#ifdef HAVE_STRFTIME
5cb37436 1131 strftime(TimeBuf, sizeof TimeBuf - 1, "%Y/%m/%d %H:%M:%S", tm);
f7632fc6 1132#else
5cb37436 1133 strlcpy(TimeBuf, asctime(tm), sizeof TimeBuf);
f7632fc6
AT
1134#endif
1135
1136 if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
1137 TimeBuf[strlen(TimeBuf)-1] = 0;
1138 }
1139
1140 return(TimeBuf);
1141}
1142
9ec16c83 1143
e1bd49d6
MP
1144/**
1145 * Sleep for a specified number of milliseconds.
1146 *
1147 * Always returns TRUE. (In the future it might return FALSE if
1148 * interrupted.)
1149 **/
1150int msleep(int t)
9ec16c83 1151{
c284f34a
WD
1152 int tdiff = 0;
1153 struct timeval tval, t1, t2;
9ec16c83
AT
1154
1155 gettimeofday(&t1, NULL);
5cb37436 1156
9ec16c83
AT
1157 while (tdiff < t) {
1158 tval.tv_sec = (t-tdiff)/1000;
1159 tval.tv_usec = 1000*((t-tdiff)%1000);
5cb37436 1160
9ec16c83
AT
1161 errno = 0;
1162 select(0,NULL,NULL, NULL, &tval);
1163
1164 gettimeofday(&t2, NULL);
5cb37436 1165 tdiff = (t2.tv_sec - t1.tv_sec)*1000 +
9ec16c83
AT
1166 (t2.tv_usec - t1.tv_usec)/1000;
1167 }
e1bd49d6
MP
1168
1169 return True;
9ec16c83
AT
1170}
1171
1172
ac13ad10
MP
1173/**
1174 * Determine if two file modification times are equivalent (either
1175 * exact or in the modification timestamp window established by
1176 * --modify-window).
1177 *
1178 * @retval 0 if the times should be treated as the same
1179 *
1180 * @retval +1 if the first is later
1181 *
1182 * @retval -1 if the 2nd is later
1183 **/
5b56cc19
AT
1184int cmp_modtime(time_t file1, time_t file2)
1185{
5b56cc19 1186 if (file2 > file1) {
bc6ebcd2
WD
1187 if (file2 - file1 <= modify_window)
1188 return 0;
5b56cc19
AT
1189 return -1;
1190 }
bc6ebcd2
WD
1191 if (file1 - file2 <= modify_window)
1192 return 0;
5b56cc19
AT
1193 return 1;
1194}
1195
1196
1197#ifdef __INSURE__XX
0f8f98c8
AT
1198#include <dlfcn.h>
1199
ac13ad10
MP
1200/**
1201 This routine is a trick to immediately catch errors when debugging
1202 with insure. A xterm with a gdb is popped up when insure catches
1203 a error. It is Linux specific.
1204**/
0f8f98c8
AT
1205int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
1206{
1207 static int (*fn)();
1208 int ret;
8950ac03 1209 char *cmd;
0f8f98c8 1210
5cb37436 1211 asprintf(&cmd, "/usr/X11R6/bin/xterm -display :0 -T Panic -n Panic -e /bin/sh -c 'cat /tmp/ierrs.*.%d ; gdb /proc/%d/exe %d'",
0f8f98c8
AT
1212 getpid(), getpid(), getpid());
1213
1214 if (!fn) {
1215 static void *h;
1216 h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
1217 fn = dlsym(h, "_Insure_trap_error");
1218 }
1219
1220 ret = fn(a1, a2, a3, a4, a5, a6);
1221
1222 system(cmd);
1223
8950ac03
AT
1224 free(cmd);
1225
0f8f98c8
AT
1226 return ret;
1227}
1228#endif
58cadc86
WD
1229
1230
1231#define MALLOC_MAX 0x40000000
1232
1233void *_new_array(unsigned int size, unsigned long num)
1234{
1235 if (num >= MALLOC_MAX/size)
1236 return NULL;
1237 return malloc(size * num);
1238}
1239
1240void *_realloc_array(void *ptr, unsigned int size, unsigned long num)
1241{
1242 if (num >= MALLOC_MAX/size)
1243 return NULL;
1244 /* No realloc should need this, but just in case... */
1245 if (!ptr)
1246 return malloc(size * num);
1247 return realloc(ptr, size * num);
1248}
e64ae6d7
WD
1249
1250/* Take a filename and filename length and return the most significant
1251 * filename suffix we can find. This ignores suffixes such as "~",
1252 * ".bak", ".orig", ".~1~", etc. */
1253const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr)
1254{
1255 const char *suf, *s;
1256 BOOL had_tilde;
1257 int s_len;
1258
1259 /* One or more dots at the start aren't a suffix. */
1260 while (fn_len && *fn == '.') fn++, fn_len--;
1261
1262 /* Ignore the ~ in a "foo~" filename. */
1263 if (fn_len > 1 && fn[fn_len-1] == '~')
1264 fn_len--, had_tilde = True;
1265 else
1266 had_tilde = False;
1267
1268 /* Assume we don't find an suffix. */
1269 suf = "";
1270 *len_ptr = 0;
1271
1272 /* Find the last significant suffix. */
1273 for (s = fn + fn_len; fn_len > 1; ) {
1274 while (*--s != '.' && s != fn) {}
1275 if (s == fn)
1276 break;
1277 s_len = fn_len - (s - fn);
1278 fn_len = s - fn;
6012eaa1 1279 if (s_len == 4) {
e64ae6d7
WD
1280 if (strcmp(s+1, "bak") == 0
1281 || strcmp(s+1, "old") == 0)
1282 continue;
6012eaa1 1283 } else if (s_len == 5) {
e64ae6d7
WD
1284 if (strcmp(s+1, "orig") == 0)
1285 continue;
1286 } else if (s_len > 2 && had_tilde
73253721 1287 && s[1] == '~' && isdigit(*(uchar*)(s+2)))
e64ae6d7
WD
1288 continue;
1289 *len_ptr = s_len;
1290 suf = s;
1291 if (s_len == 1)
1292 break;
1293 /* Determine if the suffix is all digits. */
1294 for (s++, s_len--; s_len > 0; s++, s_len--) {
73253721 1295 if (!isdigit(*(uchar*)s))
e64ae6d7
WD
1296 return suf;
1297 }
1298 /* An all-digit suffix may not be that signficant. */
1299 s = suf;
1300 }
1301
1302 return suf;
1303}
1304
1305/* This is an implementation of the Levenshtein distance algorithm. It
1306 * was implemented to avoid needing a two-dimensional matrix (to save
1307 * memory). It was also tweaked to try to factor in the ASCII distance
1308 * between changed characters as a minor distance quantity. The normal
1309 * Levenshtein units of distance (each signifying a single change between
1310 * the two strings) are defined as a "UNIT". */
1311
1312#define UNIT (1 << 16)
1313
1314uint32 fuzzy_distance(const char *s1, int len1, const char *s2, int len2)
1315{
1316 uint32 a[MAXPATHLEN], diag, above, left, diag_inc, above_inc, left_inc;
1317 int32 cost;
1318 int i1, i2;
1319
1320 if (!len1 || !len2) {
1321 if (!len1) {
1322 s1 = s2;
1323 len1 = len2;
1324 }
1325 for (i1 = 0, cost = 0; i1 < len1; i1++)
1326 cost += s1[i1];
1327 return (int32)len1 * UNIT + cost;
1328 }
1329
1330 for (i2 = 0; i2 < len2; i2++)
1331 a[i2] = (i2+1) * UNIT;
1332
1333 for (i1 = 0; i1 < len1; i1++) {
1334 diag = i1 * UNIT;
1335 above = (i1+1) * UNIT;
1336 for (i2 = 0; i2 < len2; i2++) {
1337 left = a[i2];
1338 if ((cost = *((uchar*)s1+i1) - *((uchar*)s2+i2)) != 0) {
1339 if (cost < 0)
1340 cost = UNIT - cost;
1341 else
1342 cost = UNIT + cost;
1343 }
1344 diag_inc = diag + cost;
1345 left_inc = left + UNIT + *((uchar*)s1+i1);
1346 above_inc = above + UNIT + *((uchar*)s2+i2);
1347 a[i2] = above = left < above
1348 ? (left_inc < diag_inc ? left_inc : diag_inc)
1349 : (above_inc < diag_inc ? above_inc : diag_inc);
1350 diag = left;
1351 }
1352 }
1353
1354 return a[len2-1];
1355}