path.c: make get_pathname() return strbuf instead of static buffer
[git] / path.c
1 /*
2  * Utilities for paths and pathnames
3  */
4 #include "cache.h"
5 #include "strbuf.h"
6 #include "string-list.h"
7
8 static int get_st_mode_bits(const char *path, int *mode)
9 {
10         struct stat st;
11         if (lstat(path, &st) < 0)
12                 return -1;
13         *mode = st.st_mode;
14         return 0;
15 }
16
17 static char bad_path[] = "/bad-path/";
18
19 static struct strbuf *get_pathname(void)
20 {
21         static struct strbuf pathname_array[4] = {
22                 STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
23         };
24         static int index;
25         struct strbuf *sb = &pathname_array[3 & ++index];
26         strbuf_reset(sb);
27         return sb;
28 }
29
30 static char *cleanup_path(char *path)
31 {
32         /* Clean it up */
33         if (!memcmp(path, "./", 2)) {
34                 path += 2;
35                 while (*path == '/')
36                         path++;
37         }
38         return path;
39 }
40
41 static void strbuf_cleanup_path(struct strbuf *sb)
42 {
43         char *path = cleanup_path(sb->buf);
44         if (path > sb->buf)
45                 strbuf_remove(sb, 0, path - sb->buf);
46 }
47
48 char *mksnpath(char *buf, size_t n, const char *fmt, ...)
49 {
50         va_list args;
51         unsigned len;
52
53         va_start(args, fmt);
54         len = vsnprintf(buf, n, fmt, args);
55         va_end(args);
56         if (len >= n) {
57                 strlcpy(buf, bad_path, n);
58                 return buf;
59         }
60         return cleanup_path(buf);
61 }
62
63 static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
64 {
65         const char *git_dir = get_git_dir();
66         strbuf_addstr(buf, git_dir);
67         if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
68                 strbuf_addch(buf, '/');
69         strbuf_vaddf(buf, fmt, args);
70         strbuf_cleanup_path(buf);
71 }
72
73 char *git_snpath(char *buf, size_t n, const char *fmt, ...)
74 {
75         struct strbuf sb = STRBUF_INIT;
76         va_list args;
77         va_start(args, fmt);
78         vsnpath(&sb, fmt, args);
79         va_end(args);
80         if (sb.len >= n)
81                 strlcpy(buf, bad_path, n);
82         else
83                 memcpy(buf, sb.buf, sb.len + 1);
84         strbuf_release(&sb);
85         return buf;
86 }
87
88 char *git_pathdup(const char *fmt, ...)
89 {
90         struct strbuf path = STRBUF_INIT;
91         va_list args;
92         va_start(args, fmt);
93         vsnpath(&path, fmt, args);
94         va_end(args);
95         return strbuf_detach(&path, NULL);
96 }
97
98 char *mkpathdup(const char *fmt, ...)
99 {
100         struct strbuf sb = STRBUF_INIT;
101         va_list args;
102         va_start(args, fmt);
103         strbuf_vaddf(&sb, fmt, args);
104         va_end(args);
105         strbuf_cleanup_path(&sb);
106         return strbuf_detach(&sb, NULL);
107 }
108
109 char *mkpath(const char *fmt, ...)
110 {
111         va_list args;
112         struct strbuf *pathname = get_pathname();
113         va_start(args, fmt);
114         strbuf_vaddf(pathname, fmt, args);
115         va_end(args);
116         return cleanup_path(pathname->buf);
117 }
118
119 char *git_path(const char *fmt, ...)
120 {
121         struct strbuf *pathname = get_pathname();
122         va_list args;
123         va_start(args, fmt);
124         vsnpath(pathname, fmt, args);
125         va_end(args);
126         return pathname->buf;
127 }
128
129 void home_config_paths(char **global, char **xdg, char *file)
130 {
131         char *xdg_home = getenv("XDG_CONFIG_HOME");
132         char *home = getenv("HOME");
133         char *to_free = NULL;
134
135         if (!home) {
136                 if (global)
137                         *global = NULL;
138         } else {
139                 if (!xdg_home) {
140                         to_free = mkpathdup("%s/.config", home);
141                         xdg_home = to_free;
142                 }
143                 if (global)
144                         *global = mkpathdup("%s/.gitconfig", home);
145         }
146
147         if (xdg) {
148                 if (!xdg_home)
149                         *xdg = NULL;
150                 else
151                         *xdg = mkpathdup("%s/git/%s", xdg_home, file);
152         }
153
154         free(to_free);
155 }
156
157 char *git_path_submodule(const char *path, const char *fmt, ...)
158 {
159         struct strbuf *buf = get_pathname();
160         const char *git_dir;
161         va_list args;
162
163         strbuf_addstr(buf, path);
164         if (buf->len && buf->buf[buf->len - 1] != '/')
165                 strbuf_addch(buf, '/');
166         strbuf_addstr(buf, ".git");
167
168         git_dir = read_gitfile(buf->buf);
169         if (git_dir) {
170                 strbuf_reset(buf);
171                 strbuf_addstr(buf, git_dir);
172         }
173         strbuf_addch(buf, '/');
174
175         va_start(args, fmt);
176         strbuf_vaddf(buf, fmt, args);
177         va_end(args);
178         strbuf_cleanup_path(buf);
179         return buf->buf;
180 }
181
182 int validate_headref(const char *path)
183 {
184         struct stat st;
185         char *buf, buffer[256];
186         unsigned char sha1[20];
187         int fd;
188         ssize_t len;
189
190         if (lstat(path, &st) < 0)
191                 return -1;
192
193         /* Make sure it is a "refs/.." symlink */
194         if (S_ISLNK(st.st_mode)) {
195                 len = readlink(path, buffer, sizeof(buffer)-1);
196                 if (len >= 5 && !memcmp("refs/", buffer, 5))
197                         return 0;
198                 return -1;
199         }
200
201         /*
202          * Anything else, just open it and try to see if it is a symbolic ref.
203          */
204         fd = open(path, O_RDONLY);
205         if (fd < 0)
206                 return -1;
207         len = read_in_full(fd, buffer, sizeof(buffer)-1);
208         close(fd);
209
210         /*
211          * Is it a symbolic ref?
212          */
213         if (len < 4)
214                 return -1;
215         if (!memcmp("ref:", buffer, 4)) {
216                 buf = buffer + 4;
217                 len -= 4;
218                 while (len && isspace(*buf))
219                         buf++, len--;
220                 if (len >= 5 && !memcmp("refs/", buf, 5))
221                         return 0;
222         }
223
224         /*
225          * Is this a detached HEAD?
226          */
227         if (!get_sha1_hex(buffer, sha1))
228                 return 0;
229
230         return -1;
231 }
232
233 static struct passwd *getpw_str(const char *username, size_t len)
234 {
235         struct passwd *pw;
236         char *username_z = xmemdupz(username, len);
237         pw = getpwnam(username_z);
238         free(username_z);
239         return pw;
240 }
241
242 /*
243  * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,
244  * then it is a newly allocated string. Returns NULL on getpw failure or
245  * if path is NULL.
246  */
247 char *expand_user_path(const char *path)
248 {
249         struct strbuf user_path = STRBUF_INIT;
250         const char *to_copy = path;
251
252         if (path == NULL)
253                 goto return_null;
254         if (path[0] == '~') {
255                 const char *first_slash = strchrnul(path, '/');
256                 const char *username = path + 1;
257                 size_t username_len = first_slash - username;
258                 if (username_len == 0) {
259                         const char *home = getenv("HOME");
260                         if (!home)
261                                 goto return_null;
262                         strbuf_addstr(&user_path, home);
263                 } else {
264                         struct passwd *pw = getpw_str(username, username_len);
265                         if (!pw)
266                                 goto return_null;
267                         strbuf_addstr(&user_path, pw->pw_dir);
268                 }
269                 to_copy = first_slash;
270         }
271         strbuf_addstr(&user_path, to_copy);
272         return strbuf_detach(&user_path, NULL);
273 return_null:
274         strbuf_release(&user_path);
275         return NULL;
276 }
277
278 /*
279  * First, one directory to try is determined by the following algorithm.
280  *
281  * (0) If "strict" is given, the path is used as given and no DWIM is
282  *     done. Otherwise:
283  * (1) "~/path" to mean path under the running user's home directory;
284  * (2) "~user/path" to mean path under named user's home directory;
285  * (3) "relative/path" to mean cwd relative directory; or
286  * (4) "/absolute/path" to mean absolute directory.
287  *
288  * Unless "strict" is given, we try access() for existence of "%s.git/.git",
289  * "%s/.git", "%s.git", "%s" in this order.  The first one that exists is
290  * what we try.
291  *
292  * Second, we try chdir() to that.  Upon failure, we return NULL.
293  *
294  * Then, we try if the current directory is a valid git repository.
295  * Upon failure, we return NULL.
296  *
297  * If all goes well, we return the directory we used to chdir() (but
298  * before ~user is expanded), avoiding getcwd() resolving symbolic
299  * links.  User relative paths are also returned as they are given,
300  * except DWIM suffixing.
301  */
302 const char *enter_repo(const char *path, int strict)
303 {
304         static char used_path[PATH_MAX];
305         static char validated_path[PATH_MAX];
306
307         if (!path)
308                 return NULL;
309
310         if (!strict) {
311                 static const char *suffix[] = {
312                         "/.git", "", ".git/.git", ".git", NULL,
313                 };
314                 const char *gitfile;
315                 int len = strlen(path);
316                 int i;
317                 while ((1 < len) && (path[len-1] == '/'))
318                         len--;
319
320                 if (PATH_MAX <= len)
321                         return NULL;
322                 strncpy(used_path, path, len); used_path[len] = 0 ;
323                 strcpy(validated_path, used_path);
324
325                 if (used_path[0] == '~') {
326                         char *newpath = expand_user_path(used_path);
327                         if (!newpath || (PATH_MAX - 10 < strlen(newpath))) {
328                                 free(newpath);
329                                 return NULL;
330                         }
331                         /*
332                          * Copy back into the static buffer. A pity
333                          * since newpath was not bounded, but other
334                          * branches of the if are limited by PATH_MAX
335                          * anyway.
336                          */
337                         strcpy(used_path, newpath); free(newpath);
338                 }
339                 else if (PATH_MAX - 10 < len)
340                         return NULL;
341                 len = strlen(used_path);
342                 for (i = 0; suffix[i]; i++) {
343                         struct stat st;
344                         strcpy(used_path + len, suffix[i]);
345                         if (!stat(used_path, &st) &&
346                             (S_ISREG(st.st_mode) ||
347                             (S_ISDIR(st.st_mode) && is_git_directory(used_path)))) {
348                                 strcat(validated_path, suffix[i]);
349                                 break;
350                         }
351                 }
352                 if (!suffix[i])
353                         return NULL;
354                 gitfile = read_gitfile(used_path) ;
355                 if (gitfile)
356                         strcpy(used_path, gitfile);
357                 if (chdir(used_path))
358                         return NULL;
359                 path = validated_path;
360         }
361         else if (chdir(path))
362                 return NULL;
363
364         if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
365             validate_headref("HEAD") == 0) {
366                 set_git_dir(".");
367                 check_repository_format();
368                 return path;
369         }
370
371         return NULL;
372 }
373
374 static int calc_shared_perm(int mode)
375 {
376         int tweak;
377
378         if (shared_repository < 0)
379                 tweak = -shared_repository;
380         else
381                 tweak = shared_repository;
382
383         if (!(mode & S_IWUSR))
384                 tweak &= ~0222;
385         if (mode & S_IXUSR)
386                 /* Copy read bits to execute bits */
387                 tweak |= (tweak & 0444) >> 2;
388         if (shared_repository < 0)
389                 mode = (mode & ~0777) | tweak;
390         else
391                 mode |= tweak;
392
393         return mode;
394 }
395
396
397 int adjust_shared_perm(const char *path)
398 {
399         int old_mode, new_mode;
400
401         if (!shared_repository)
402                 return 0;
403         if (get_st_mode_bits(path, &old_mode) < 0)
404                 return -1;
405
406         new_mode = calc_shared_perm(old_mode);
407         if (S_ISDIR(old_mode)) {
408                 /* Copy read bits to execute bits */
409                 new_mode |= (new_mode & 0444) >> 2;
410                 new_mode |= FORCE_DIR_SET_GID;
411         }
412
413         if (((old_mode ^ new_mode) & ~S_IFMT) &&
414                         chmod(path, (new_mode & ~S_IFMT)) < 0)
415                 return -2;
416         return 0;
417 }
418
419 static int have_same_root(const char *path1, const char *path2)
420 {
421         int is_abs1, is_abs2;
422
423         is_abs1 = is_absolute_path(path1);
424         is_abs2 = is_absolute_path(path2);
425         return (is_abs1 && is_abs2 && tolower(path1[0]) == tolower(path2[0])) ||
426                (!is_abs1 && !is_abs2);
427 }
428
429 /*
430  * Give path as relative to prefix.
431  *
432  * The strbuf may or may not be used, so do not assume it contains the
433  * returned path.
434  */
435 const char *relative_path(const char *in, const char *prefix,
436                           struct strbuf *sb)
437 {
438         int in_len = in ? strlen(in) : 0;
439         int prefix_len = prefix ? strlen(prefix) : 0;
440         int in_off = 0;
441         int prefix_off = 0;
442         int i = 0, j = 0;
443
444         if (!in_len)
445                 return "./";
446         else if (!prefix_len)
447                 return in;
448
449         if (have_same_root(in, prefix)) {
450                 /* bypass dos_drive, for "c:" is identical to "C:" */
451                 if (has_dos_drive_prefix(in)) {
452                         i = 2;
453                         j = 2;
454                 }
455         } else {
456                 return in;
457         }
458
459         while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
460                 if (is_dir_sep(prefix[i])) {
461                         while (is_dir_sep(prefix[i]))
462                                 i++;
463                         while (is_dir_sep(in[j]))
464                                 j++;
465                         prefix_off = i;
466                         in_off = j;
467                 } else {
468                         i++;
469                         j++;
470                 }
471         }
472
473         if (
474             /* "prefix" seems like prefix of "in" */
475             i >= prefix_len &&
476             /*
477              * but "/foo" is not a prefix of "/foobar"
478              * (i.e. prefix not end with '/')
479              */
480             prefix_off < prefix_len) {
481                 if (j >= in_len) {
482                         /* in="/a/b", prefix="/a/b" */
483                         in_off = in_len;
484                 } else if (is_dir_sep(in[j])) {
485                         /* in="/a/b/c", prefix="/a/b" */
486                         while (is_dir_sep(in[j]))
487                                 j++;
488                         in_off = j;
489                 } else {
490                         /* in="/a/bbb/c", prefix="/a/b" */
491                         i = prefix_off;
492                 }
493         } else if (
494                    /* "in" is short than "prefix" */
495                    j >= in_len &&
496                    /* "in" not end with '/' */
497                    in_off < in_len) {
498                 if (is_dir_sep(prefix[i])) {
499                         /* in="/a/b", prefix="/a/b/c/" */
500                         while (is_dir_sep(prefix[i]))
501                                 i++;
502                         in_off = in_len;
503                 }
504         }
505         in += in_off;
506         in_len -= in_off;
507
508         if (i >= prefix_len) {
509                 if (!in_len)
510                         return "./";
511                 else
512                         return in;
513         }
514
515         strbuf_reset(sb);
516         strbuf_grow(sb, in_len);
517
518         while (i < prefix_len) {
519                 if (is_dir_sep(prefix[i])) {
520                         strbuf_addstr(sb, "../");
521                         while (is_dir_sep(prefix[i]))
522                                 i++;
523                         continue;
524                 }
525                 i++;
526         }
527         if (!is_dir_sep(prefix[prefix_len - 1]))
528                 strbuf_addstr(sb, "../");
529
530         strbuf_addstr(sb, in);
531
532         return sb->buf;
533 }
534
535 /*
536  * A simpler implementation of relative_path
537  *
538  * Get relative path by removing "prefix" from "in". This function
539  * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter
540  * to increase performance when traversing the path to work_tree.
541  */
542 const char *remove_leading_path(const char *in, const char *prefix)
543 {
544         static char buf[PATH_MAX + 1];
545         int i = 0, j = 0;
546
547         if (!prefix || !prefix[0])
548                 return in;
549         while (prefix[i]) {
550                 if (is_dir_sep(prefix[i])) {
551                         if (!is_dir_sep(in[j]))
552                                 return in;
553                         while (is_dir_sep(prefix[i]))
554                                 i++;
555                         while (is_dir_sep(in[j]))
556                                 j++;
557                         continue;
558                 } else if (in[j] != prefix[i]) {
559                         return in;
560                 }
561                 i++;
562                 j++;
563         }
564         if (
565             /* "/foo" is a prefix of "/foo" */
566             in[j] &&
567             /* "/foo" is not a prefix of "/foobar" */
568             !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j])
569            )
570                 return in;
571         while (is_dir_sep(in[j]))
572                 j++;
573         if (!in[j])
574                 strcpy(buf, ".");
575         else
576                 strcpy(buf, in + j);
577         return buf;
578 }
579
580 /*
581  * It is okay if dst == src, but they should not overlap otherwise.
582  *
583  * Performs the following normalizations on src, storing the result in dst:
584  * - Ensures that components are separated by '/' (Windows only)
585  * - Squashes sequences of '/'.
586  * - Removes "." components.
587  * - Removes ".." components, and the components the precede them.
588  * Returns failure (non-zero) if a ".." component appears as first path
589  * component anytime during the normalization. Otherwise, returns success (0).
590  *
591  * Note that this function is purely textual.  It does not follow symlinks,
592  * verify the existence of the path, or make any system calls.
593  *
594  * prefix_len != NULL is for a specific case of prefix_pathspec():
595  * assume that src == dst and src[0..prefix_len-1] is already
596  * normalized, any time "../" eats up to the prefix_len part,
597  * prefix_len is reduced. In the end prefix_len is the remaining
598  * prefix that has not been overridden by user pathspec.
599  */
600 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
601 {
602         char *dst0;
603
604         if (has_dos_drive_prefix(src)) {
605                 *dst++ = *src++;
606                 *dst++ = *src++;
607         }
608         dst0 = dst;
609
610         if (is_dir_sep(*src)) {
611                 *dst++ = '/';
612                 while (is_dir_sep(*src))
613                         src++;
614         }
615
616         for (;;) {
617                 char c = *src;
618
619                 /*
620                  * A path component that begins with . could be
621                  * special:
622                  * (1) "." and ends   -- ignore and terminate.
623                  * (2) "./"           -- ignore them, eat slash and continue.
624                  * (3) ".." and ends  -- strip one and terminate.
625                  * (4) "../"          -- strip one, eat slash and continue.
626                  */
627                 if (c == '.') {
628                         if (!src[1]) {
629                                 /* (1) */
630                                 src++;
631                         } else if (is_dir_sep(src[1])) {
632                                 /* (2) */
633                                 src += 2;
634                                 while (is_dir_sep(*src))
635                                         src++;
636                                 continue;
637                         } else if (src[1] == '.') {
638                                 if (!src[2]) {
639                                         /* (3) */
640                                         src += 2;
641                                         goto up_one;
642                                 } else if (is_dir_sep(src[2])) {
643                                         /* (4) */
644                                         src += 3;
645                                         while (is_dir_sep(*src))
646                                                 src++;
647                                         goto up_one;
648                                 }
649                         }
650                 }
651
652                 /* copy up to the next '/', and eat all '/' */
653                 while ((c = *src++) != '\0' && !is_dir_sep(c))
654                         *dst++ = c;
655                 if (is_dir_sep(c)) {
656                         *dst++ = '/';
657                         while (is_dir_sep(c))
658                                 c = *src++;
659                         src--;
660                 } else if (!c)
661                         break;
662                 continue;
663
664         up_one:
665                 /*
666                  * dst0..dst is prefix portion, and dst[-1] is '/';
667                  * go up one level.
668                  */
669                 dst--;  /* go to trailing '/' */
670                 if (dst <= dst0)
671                         return -1;
672                 /* Windows: dst[-1] cannot be backslash anymore */
673                 while (dst0 < dst && dst[-1] != '/')
674                         dst--;
675                 if (prefix_len && *prefix_len > dst - dst0)
676                         *prefix_len = dst - dst0;
677         }
678         *dst = '\0';
679         return 0;
680 }
681
682 int normalize_path_copy(char *dst, const char *src)
683 {
684         return normalize_path_copy_len(dst, src, NULL);
685 }
686
687 /*
688  * path = Canonical absolute path
689  * prefixes = string_list containing normalized, absolute paths without
690  * trailing slashes (except for the root directory, which is denoted by "/").
691  *
692  * Determines, for each path in prefixes, whether the "prefix"
693  * is an ancestor directory of path.  Returns the length of the longest
694  * ancestor directory, excluding any trailing slashes, or -1 if no prefix
695  * is an ancestor.  (Note that this means 0 is returned if prefixes is
696  * ["/"].) "/foo" is not considered an ancestor of "/foobar".  Directories
697  * are not considered to be their own ancestors.  path must be in a
698  * canonical form: empty components, or "." or ".." components are not
699  * allowed.
700  */
701 int longest_ancestor_length(const char *path, struct string_list *prefixes)
702 {
703         int i, max_len = -1;
704
705         if (!strcmp(path, "/"))
706                 return -1;
707
708         for (i = 0; i < prefixes->nr; i++) {
709                 const char *ceil = prefixes->items[i].string;
710                 int len = strlen(ceil);
711
712                 if (len == 1 && ceil[0] == '/')
713                         len = 0; /* root matches anything, with length 0 */
714                 else if (!strncmp(path, ceil, len) && path[len] == '/')
715                         ; /* match of length len */
716                 else
717                         continue; /* no match */
718
719                 if (len > max_len)
720                         max_len = len;
721         }
722
723         return max_len;
724 }
725
726 /* strip arbitrary amount of directory separators at end of path */
727 static inline int chomp_trailing_dir_sep(const char *path, int len)
728 {
729         while (len && is_dir_sep(path[len - 1]))
730                 len--;
731         return len;
732 }
733
734 /*
735  * If path ends with suffix (complete path components), returns the
736  * part before suffix (sans trailing directory separators).
737  * Otherwise returns NULL.
738  */
739 char *strip_path_suffix(const char *path, const char *suffix)
740 {
741         int path_len = strlen(path), suffix_len = strlen(suffix);
742
743         while (suffix_len) {
744                 if (!path_len)
745                         return NULL;
746
747                 if (is_dir_sep(path[path_len - 1])) {
748                         if (!is_dir_sep(suffix[suffix_len - 1]))
749                                 return NULL;
750                         path_len = chomp_trailing_dir_sep(path, path_len);
751                         suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
752                 }
753                 else if (path[--path_len] != suffix[--suffix_len])
754                         return NULL;
755         }
756
757         if (path_len && !is_dir_sep(path[path_len - 1]))
758                 return NULL;
759         return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
760 }
761
762 int daemon_avoid_alias(const char *p)
763 {
764         int sl, ndot;
765
766         /*
767          * This resurrects the belts and suspenders paranoia check by HPA
768          * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
769          * does not do getcwd() based path canonicalization.
770          *
771          * sl becomes true immediately after seeing '/' and continues to
772          * be true as long as dots continue after that without intervening
773          * non-dot character.
774          */
775         if (!p || (*p != '/' && *p != '~'))
776                 return -1;
777         sl = 1; ndot = 0;
778         p++;
779
780         while (1) {
781                 char ch = *p++;
782                 if (sl) {
783                         if (ch == '.')
784                                 ndot++;
785                         else if (ch == '/') {
786                                 if (ndot < 3)
787                                         /* reject //, /./ and /../ */
788                                         return -1;
789                                 ndot = 0;
790                         }
791                         else if (ch == 0) {
792                                 if (0 < ndot && ndot < 3)
793                                         /* reject /.$ and /..$ */
794                                         return -1;
795                                 return 0;
796                         }
797                         else
798                                 sl = ndot = 0;
799                 }
800                 else if (ch == 0)
801                         return 0;
802                 else if (ch == '/') {
803                         sl = 1;
804                         ndot = 0;
805                 }
806         }
807 }