bug-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

PATCH: backport of symlink fix for issue 142 to cvs 1.11.9.1


From: Mark D. Baushke
Subject: PATCH: backport of symlink fix for issue 142 to cvs 1.11.9.1
Date: Thu, 23 Oct 2003 21:05:13 -0700

Hi Folks,

The following seems to be the minimum amount of change needed to
implement a backport of the symlink bugfix that Derek put into
the feature branch.

For reference, this is Issue 142:

    http://ccvs.cvshome.org/issues/show_bug.cgi?id=142

The last entry of Issue 142 has Derek Price writing as follows:

| Just wanted to note that this issue has been fixed, along with other
| symlink related issues, on the feature branch since April.  I'm
| leaving this issue open pending the completion of discussions with
| Mark and Larry Jones on <bug-cvs@gnu.org>, but I think the needed
| overhaul to fix all the symlink related issues is really too large to
| be justifiable the stable branch and that symlink related fixes should
| be considered the a new feature on the feature branch.

Now that the patch is 'backported, it remains for folks to determine if
it is too large to be justifiable in the stable branch.

On my Redhat 7.3 GNU/Linux system, this version passed the following
tests:

    /bin/sh ../../src/sanity.sh `pwd`/cvs
    /bin/sh ../../src/sanity.sh -r `pwd`/cvs
    /bin/sh ../../src/sanity.sh -l `pwd`/cvs
    /bin/sh ../../src/sanity.sh -l -r `pwd`/cvs

The only work I have not yet done is to port the xresolvepath function
to the files:

    ./emx/filesubr.c
    ./os2/filesubr.c
    ./vms/filesubr.c

I would have backported the versions from the feature branch, but the
function does not exist for those files. 

I did make the necessary changes to the windows-NT/filesubr.c file, 
but I do not have any machines that run Windows, so I have no way
to test there.

Note: I did copy both the xresolvepath() and cvs_casecmp()
implementations from feature, so if anyone has recently been unable to
build Windows NT versions of cvs 1.11.9.1 with SERVER_SUPPORT defined,
that problem should now be resolved with this patch.

Note: There is one patch that is not really related:

        * recurse.c (do_recursion): Call Lock_Cleanup() only repository
        was set.

        @@ -736,7 +753,10 @@ do_recursion (frame)
                err += walklist (filelist, do_file_proc, &frfile);
         
                /* unlock it */
        -       if (locktype != CVS_LOCK_NONE)
        +       if (/* We only lock the repository above when repository is set 
*/
        +           repository
        +           /* and when asked for a read or write lock. */
        +           && locktype != CVS_LOCK_NONE)
                    Lock_Cleanup ();
         
                /* clean up */

It came up when I was doing the merge. I can remove it if you feel that
this patch should be 'pure' and not include that change.

        Thanks,
        -- Mark

Note: I uncovered two changes that are not in the feature branch that I
have already forwarded to Derek for consideration as fixing possible
memory leaks in the feature version.

src/ChangeLog entry:
2003-10-23  Mark D. Baushke  <mdb@cvshome.org>

        * Backport symlink bugfix from cvs 1.12.1.1 feature release
        as written by Derek Price  <derek@ximbiot.com>.

        * recurse.c (start_recursion): Accept new repository argument so
        that the working directory may be tracked by do_recursion without
        using xgetwd(), which returned a value different from the one the
        user requested when symlinks were in use. Pass repository_in to
        do_recursion() as part of the recursion frame.

        * cvs.h (xreadlink): #ifdef HAVE_READLINK proto.
        (xresolvepath): New proto.
        (start_recursion): Add repository to proto.

        * checkout.c (safe_location): Add more complete header comment.
        Add trace.  Use new xresolvepath() function.  Always return true
        in client mode since checking our destination path against the
        CVSROOT path is usually meaningless in client/server mode.
        (checkout_proc): Pass repository to do_update() for later use with
        start_recursion().

        * admin.c (admin): Use new definition of start_recursion().
        * annotate.c (rannotate_proc): Ditto.
        * client.c (send_files): Ditto.
        * commit.c (commit): Ditto.
        * diff.c (diff): Ditto.
        * edit.c (watch_onoff, send_notifications, edit, unedit, editors):
        Ditto.
        * lock.c (lock_tree_for_write): Ditto.
        * log.c (rlog_proc): Ditto.
        * patch.c (patch_proc): Ditto.
        * remove.c (cvsremove): Ditto.
        * tag.c (rtag_proc): Ditto.
        * update.c (do_update): Ditto.
        * watch.c (watch_addremove, watchers): Ditto.

        * patch.c (patch_proc): Call tag_check_valid with repository
        instead of NULL.

        * filesubr.c (xreadlink): #ifdef HAVE_READLINK this function.  Add
        more complete header comment.
        (xresolvepath): New function.

        * recurse.c (do_recursion): Call Lock_Cleanup() only repository
        was set.

        * update.c (do_update): Accept new repository argument so that the
        working directory may be tracked.
        (update): Pass NULL repository to do_update().
        * update.c (do_update): Add repository to proto.

        * checkout.c (checkout_proc): Use new definition of do_update().
        * update.c (update): Ditto.

        * sanity.sh: Add new -l option to test symlinked roots.
        (checkout_repository-1): Add server error messages about absolute
        paths since the client now skips destination validity checks.
        (check_repository-2): Test renamed to checkout_repository-2.
        (checkout_repository-2): Process client error messages about
        CVSROOT files being in the way since the client skips destination
        validity checks since it should be rare that a client is running
        in client/server mode on the server and CVS has no current way to
        check if it is running on the server. 
        (check_repository-3): Test renamed to checkout_repository-3.
        (dottedroot): New test to check that a CVSROOT with a "." in the
        name will work.

windows-NT/ChangeLog entry:
2003-10-23  Mark D. Baushke  <mdb@cvshome.org>

        * filesubr.c (cvs_casecmp, xresolvepath): New functions.

Index: src/admin.c
===================================================================
RCS file: /cvs/ccvs/src/admin.c,v
retrieving revision 1.80.4.3
diff -u -p -r1.80.4.3 admin.c
--- src/admin.c 18 Oct 2003 19:20:31 -0000      1.80.4.3
+++ src/admin.c 24 Oct 2003 02:09:20 -0000
@@ -520,7 +520,8 @@ admin (argc, argv)
     err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc,
                           (DIRLEAVEPROC) NULL, (void *)&admin_data,
                           argc, argv, 0,
-                          W_LOCAL, 0, CVS_LOCK_NONE, (char *) NULL, 1);
+                          W_LOCAL, 0, CVS_LOCK_NONE, (char *) NULL, 1,
+                          (char *) NULL);
     Lock_Cleanup ();
 
  return_it:
Index: src/annotate.c
===================================================================
RCS file: /cvs/ccvs/src/annotate.c,v
retrieving revision 1.7
diff -u -p -r1.7 annotate.c
--- src/annotate.c      28 Dec 2002 17:56:59 -0000      1.7
+++ src/annotate.c      24 Oct 2003 02:09:20 -0000
@@ -224,14 +224,12 @@ rannotate_proc (argc, argv, xwhere, mwhe
            free (repository);
            return (1);
        }
-       free (repository);
        /* End section which is identical to patch_proc.  */
 
        if (force_tag_match && tag != NULL)
            which = W_REPOS | W_ATTIC;
        else
            which = W_REPOS;
-       repository = NULL;
     }
     else
     {
@@ -249,7 +247,8 @@ rannotate_proc (argc, argv, xwhere, mwhe
     err = start_recursion (annotate_fileproc, (FILESDONEPROC) NULL,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
-                          where, 1);
+                          where, 1, repository);
+    if ( which & W_REPOS ) free ( repository );
     return err;
 }
 
Index: src/checkout.c
===================================================================
RCS file: /cvs/ccvs/src/checkout.c,v
retrieving revision 1.107.4.3
diff -u -p -r1.107.4.3 checkout.c
--- src/checkout.c      28 Jul 2003 12:58:11 -0000      1.107.4.3
+++ src/checkout.c      24 Oct 2003 02:09:20 -0000
@@ -391,39 +391,56 @@ checkout (argc, argv)
 /* FIXME: This is and emptydir_name are in checkout.c for historical
    reasons, probably want to move them.  */
 
+/* int
+ * safe_location ( char *where )
+ *
+ * Return true if where is a safe destination for a checkout.
+ *
+ * INPUTS
+ *  where      The requested destination directory.
+ *
+ * GLOBALS
+ *  current_parsed_root->directory
+ *  current_parsed_root->isremote
+ *             Used to locate our CVSROOT.
+ *
+ * RETURNS
+ *  true       If we are running in client mode or if where is not located
+ *             within the CVSROOT.
+ *  false      Otherwise.
+ *
+ * ERRORS
+ *  Exits with a fatal error message when various events occur, such as not
+ *  being able to resolve a path or failing ot chdir to a path.
+ */
 int
 safe_location (where)
     char *where;
 {
     char *current;
     char *where_location;
-    char hardpath[PATH_MAX+5];
+    char *hardpath;
     size_t hardpath_len;
     int  x;
     int retval;
 
-#ifdef HAVE_READLINK
-    /* FIXME-arbitrary limit: should be retrying this like xgetwd.
-       But how does readlink let us know that the buffer was too small?
-       (by returning sizeof hardpath - 1?).  */
-    x = readlink(current_parsed_root->directory, hardpath, sizeof hardpath - 
1);
-#else
-    x = -1;
-#endif
-    if (x == -1)
-    {
-        strcpy(hardpath, current_parsed_root->directory);
-    }
-    else
-    {
-        hardpath[x] = '\0';
-    }
+    if (trace)
+       (void) fprintf (stderr, "%s-> safe_location( where=%s )\n",
+                       CLIENT_SERVER_STR,
+                       where ? where : "(null)");
+
+#ifdef CLIENT_SUPPORT
+    /* Don't compare remote CVSROOTs to our destination directory. */
+    if ( current_parsed_root->isremote ) return 1;
+#endif /* CLIENT_SUPPORT */
 
     /* set current - even if where is set we'll need to cd back... */
     current = xgetwd ();
     if (current == NULL)
        error (1, errno, "could not get working directory");
 
+    hardpath = xresolvepath ( current_parsed_root->directory );
+
     /* if where is set, set current to where, where - last_component( where ),
      * or fail, depending on whether the directories exist or not.
      */
@@ -499,6 +516,7 @@ safe_location (where)
     else
        retval = 1;
     free (current);
+    free (hardpath);
     return retval;
 }
 
@@ -1080,7 +1098,8 @@ internal error: %s doesn't start with %s
                          force_tag_match, 0 /* !local */ ,
                          1 /* update -d */ , aflag, checkout_prune_dirs,
                          pipeout, which, join_rev1, join_rev2,
-                         preload_update_dir, m_type == CHECKOUT);
+                         preload_update_dir, m_type == CHECKOUT,
+                         repository);
        goto out;
     }
 
@@ -1136,7 +1155,8 @@ internal error: %s doesn't start with %s
     err += do_update (argc - 1, argv + 1, options, tag, date,
                      force_tag_match, local_specified, 1 /* update -d */,
                      aflag, checkout_prune_dirs, pipeout, which, join_rev1,
-                     join_rev2, preload_update_dir, m_type == CHECKOUT);
+                     join_rev2, preload_update_dir, m_type == CHECKOUT,
+                     repository);
 out:
     free (preload_update_dir);
     preload_update_dir = oldupdate;
Index: src/client.c
===================================================================
RCS file: /cvs/ccvs/src/client.c,v
retrieving revision 1.318.4.8
diff -u -p -r1.318.4.8 client.c
--- src/client.c        20 Jun 2003 21:44:07 -0000      1.318.4.8
+++ src/client.c        24 Oct 2003 02:09:20 -0000
@@ -5470,7 +5470,8 @@ send_files (argc, argv, local, aflag, fl
     err = start_recursion
        (send_fileproc, send_filesdoneproc,
         send_dirent_proc, send_dirleave_proc, (void *) &args,
-        argc, argv, local, W_LOCAL, aflag, CVS_LOCK_NONE, (char *)NULL, 0);
+        argc, argv, local, W_LOCAL, aflag, CVS_LOCK_NONE, (char *)NULL, 0,
+        (char *) NULL);
     if (err)
        error_exit ();
     if (toplevel_repos == NULL)
Index: src/commit.c
===================================================================
RCS file: /cvs/ccvs/src/commit.c,v
retrieving revision 1.187.4.12
diff -u -p -r1.187.4.12 commit.c
--- src/commit.c        15 Oct 2003 18:00:50 -0000      1.187.4.12
+++ src/commit.c        24 Oct 2003 02:09:20 -0000
@@ -469,7 +469,7 @@ commit (argc, argv)
                               find_dirent_proc, (DIRLEAVEPROC) NULL,
                               (void *)&find_args,
                               argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE,
-                              (char *)NULL, 0);
+                              (char *)NULL, 0, (char *) NULL);
        if (err)
            error (1, 0, "correct above errors first!");
 
@@ -648,7 +648,7 @@ commit (argc, argv)
     err = start_recursion (check_fileproc, check_filesdoneproc,
                           check_direntproc, (DIRLEAVEPROC) NULL, NULL, argc,
                           argv, local, W_LOCAL, aflag, CVS_LOCK_NONE,
-                          (char *) NULL, 1);
+                          (char *) NULL, 1, (char *) NULL);
     if (err)
     {
        Lock_Cleanup ();
@@ -663,7 +663,7 @@ commit (argc, argv)
        err = start_recursion (commit_fileproc, commit_filesdoneproc,
                               commit_direntproc, commit_dirleaveproc, NULL,
                               argc, argv, local, W_LOCAL, aflag, CVS_LOCK_NONE,
-                              (char *) NULL, 1);
+                              (char *) NULL, 1, (char *) NULL);
 
     /*
      * Unlock all the dirs and clean up
Index: src/cvs.h
===================================================================
RCS file: /cvs/ccvs/src/cvs.h,v
retrieving revision 1.235.4.10
diff -u -p -r1.235.4.10 cvs.h
--- src/cvs.h   7 Oct 2003 18:23:01 -0000       1.235.4.10
+++ src/cvs.h   24 Oct 2003 02:09:20 -0000
@@ -481,7 +481,10 @@ int isreadable PROTO((const char *file))
 int iswritable PROTO((const char *file));
 int isaccessible PROTO((const char *file, const int mode));
 int isabsolute PROTO((const char *filename));
+#ifdef HAVE_READLINK
 char *xreadlink PROTO((const char *link));
+#endif
+char *xresolvepath PROTO((const char *path));
 char *last_component PROTO((char *path));
 char *get_homedir PROTO ((void));
 char *strcat_filename_onto_homedir PROTO ((const char *, const char *));
@@ -639,7 +642,7 @@ int start_recursion PROTO((FILEPROC file
                     void *callerdat,
                     int argc, char *argv[], int local, int which,
                     int aflag, int locktype, char *update_preload,
-                    int dosrcs));
+                    int dosrcs, char *repository));
 void SIG_beginCrSect PROTO((void));
 void SIG_endCrSect PROTO((void));
 int SIG_inCrSect PROTO((void));
Index: src/diff.c
===================================================================
RCS file: /cvs/ccvs/src/diff.c,v
retrieving revision 1.94.2.4
diff -u -p -r1.94.2.4 diff.c
--- src/diff.c  26 Sep 2003 22:34:53 -0000      1.94.2.4
+++ src/diff.c  24 Oct 2003 02:09:20 -0000
@@ -431,7 +431,8 @@ diff (argc, argv)
     /* start the recursion processor */
     err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc,
                           diff_dirleaveproc, NULL, argc, argv, local,
-                          which, 0, CVS_LOCK_READ, (char *) NULL, 1);
+                          which, 0, CVS_LOCK_READ, (char *) NULL, 1,
+                          (char *) NULL);
 
     /* clean up */
     free (options);
Index: src/edit.c
===================================================================
RCS file: /cvs/ccvs/src/edit.c,v
retrieving revision 1.57
diff -u -p -r1.57 edit.c
--- src/edit.c  28 Dec 2002 17:56:59 -0000      1.57
+++ src/edit.c  24 Oct 2003 02:09:20 -0000
@@ -104,7 +104,7 @@ watch_onoff (argc, argv)
     err = start_recursion (onoff_fileproc, onoff_filesdoneproc,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE,
-                          (char *)NULL, 0);
+                          (char *)NULL, 0, (char *) NULL);
 
     Lock_Cleanup ();
     return err;
@@ -247,7 +247,7 @@ send_notifications (argc, argv, local)
        err += start_recursion (dummy_fileproc, (FILESDONEPROC) NULL,
                                (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                                argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                               0);
+                               0, (char *) NULL);
 
        send_to_server ("noop\012", 0);
        if (strcmp (command_name, "release") == 0)
@@ -264,7 +264,7 @@ send_notifications (argc, argv, local)
        err += start_recursion (ncheck_fileproc, (FILESDONEPROC) NULL,
                                (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                                argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                               0);
+                               0, (char *) NULL);
        Lock_Cleanup ();
     }
     return err;
@@ -444,7 +444,7 @@ edit (argc, argv)
     err = start_recursion (edit_fileproc, (FILESDONEPROC) NULL,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                          0);
+                          0, (char *) NULL);
 
     err += send_notifications (argc, argv, local);
 
@@ -613,7 +613,7 @@ unedit (argc, argv)
     err = start_recursion (unedit_fileproc, (FILESDONEPROC) NULL,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                          0);
+                          0,  (char *) NULL);
 
     err += send_notifications (argc, argv, local);
 
@@ -1137,5 +1137,5 @@ editors (argc, argv)
     return start_recursion (editors_fileproc, (FILESDONEPROC) NULL,
                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                            argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
-                           0);
+                           0,  (char *) NULL);
 }
Index: src/filesubr.c
===================================================================
RCS file: /cvs/ccvs/src/filesubr.c,v
retrieving revision 1.59.4.5
diff -u -p -r1.59.4.5 filesubr.c
--- src/filesubr.c      28 Jun 2003 03:28:29 -0000      1.59.4.5
+++ src/filesubr.c      24 Oct 2003 02:09:20 -0000
@@ -855,22 +855,33 @@ isabsolute (filename)
     return filename[0] == '/';
 }
 
-/*
- * Return a string (dynamically allocated) with the name of the file to which
- * LINK is symlinked.
+
+
+#ifdef HAVE_READLINK
+/* char *
+ * xreadlink ( const char *link )
+ *
+ * Like the X/OPEN and 4.4BSD readlink() function, but allocates and returns
+ * its own buf.
+ *
+ * INPUTS
+ *  link       The original path.
+ *
+ * RETURNS
+ *  The resolution of the final symbolic link in the path.
+ *
+ * ERRORS
+ *  This function exits with a fatal error if it fails to read the link for
+ *  any reason.
  */
 char *
 xreadlink (link)
     const char *link;
 {
     char *file = NULL;
-    char *tfile;
     int buflen = 128;
     int link_name_len;
 
-    if (!islink (link))
-       return NULL;
-
     /* Get the name of the file to which `from' is linked.
        FIXME: what portability issues arise here?  Are readlink &
        ENAMETOOLONG defined on all systems? -twp */
@@ -887,11 +898,51 @@ xreadlink (link)
 
     file[link_name_len] = '\0';
 
-    tfile = xstrdup (file);
-    free (file);
+    return file;
+}
+#endif /* HAVE_READLINK */
+
+
 
-    return tfile;
+/* char *
+ * xresolvepath ( const char *path )
+ *
+ * Like xreadlink(), but resolve all links in a path.
+ *
+ * INPUTS
+ *  path       The original path.
+ *
+ * RETURNS
+ *  The path with any symbolic links expanded.
+ *
+ * ERRORS
+ *  This function exits with a fatal error if it fails to read the link for
+ *  any reason.
+ */
+char *
+xresolvepath ( path )
+    const char *path;
+{
+    char *hardpath;
+    char *owd;
+
+    assert ( isdir ( path ) );
+
+    /* FIXME - If HAVE_READLINK is defined, we should probably walk the path
+     * bit by bit calling xreadlink().
+     */
+
+    owd = xgetwd();
+    if ( CVS_CHDIR ( path ) < 0)
+       error ( 1, errno, "cannot chdir to %s", path );
+    if ( ( hardpath = xgetwd() ) == NULL )
+       error (1, errno, "cannot readlink %s", hardpath);
+    if ( CVS_CHDIR ( owd ) < 0)
+       error ( 1, errno, "cannot chdir to %s", owd );
+    free (owd);
+    return hardpath;
 }
+
 
 
 /* Return a pointer into PATH's last component.  */
Index: src/lock.c
===================================================================
RCS file: /cvs/ccvs/src/lock.c,v
retrieving revision 1.59.4.3
diff -u -p -r1.59.4.3 lock.c
--- src/lock.c  10 Oct 2003 15:46:42 -0000      1.59.4.3
+++ src/lock.c  24 Oct 2003 02:09:20 -0000
@@ -970,7 +970,7 @@ lock_tree_for_write (argc, argv, local, 
     err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc,
                           argv, local, which, aflag, CVS_LOCK_NONE,
-                          (char *) NULL, 0);
+                          (char *) NULL, 0, (char *) NULL);
     sortlist (lock_tree_list, fsortcmp);
     if (Writer_Lock (lock_tree_list) != 0)
        error (1, 0, "lock failed - giving up");
Index: src/log.c
===================================================================
RCS file: /cvs/ccvs/src/log.c,v
retrieving revision 1.79.4.2
diff -u -p -r1.79.4.2 log.c
--- src/log.c   23 Jun 2003 15:46:18 -0000      1.79.4.2
+++ src/log.c   24 Oct 2003 02:09:20 -0000
@@ -538,7 +538,6 @@ rlog_proc (argc, argv, xwhere, mwhere, m
            free( where );
            return (1);
        }
-       free (repository);
        /* End section which is identical to patch_proc.  */
 
        which = W_REPOS | W_ATTIC;
@@ -552,8 +551,9 @@ rlog_proc (argc, argv, xwhere, mwhere, m
     err = start_recursion (log_fileproc, (FILESDONEPROC) NULL, log_dirproc,
                           (DIRLEAVEPROC) NULL, (void *) &log_data,
                           argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
-                          where, 1);
+                          where, 1, repository);
 
+    if ( ! ( which & W_LOCAL ) ) free (repository);
     if( where ) free( where );
 
     return err;
Index: src/patch.c
===================================================================
RCS file: /cvs/ccvs/src/patch.c,v
retrieving revision 1.80.4.4
diff -u -p -r1.80.4.4 patch.c
--- src/patch.c 28 Jun 2003 03:28:29 -0000      1.80.4.4
+++ src/patch.c 24 Oct 2003 02:09:20 -0000
@@ -334,7 +334,6 @@ patch_proc (argc, argv, xwhere, mwhere, 
        free (repository);
        return (1);
     }
-    free (repository);
 
     if (force_tag_match)
        which = W_REPOS | W_ATTIC;
@@ -343,12 +342,14 @@ patch_proc (argc, argv, xwhere, mwhere, 
 
     if (rev1 != NULL && !rev1_validated)
     {
-       tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0, NULL);
+       tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0,
+                        repository);
        rev1_validated = 1;
     }
     if (rev2 != NULL && !rev2_validated)
     {
-       tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0, NULL);
+       tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
+                        repository);
        rev2_validated = 1;
     }
 
@@ -356,7 +357,8 @@ patch_proc (argc, argv, xwhere, mwhere, 
     err = start_recursion (patch_fileproc, (FILESDONEPROC) NULL, patch_dirproc,
                           (DIRLEAVEPROC) NULL, NULL,
                           argc - 1, argv + 1, local_specified,
-                          which, 0, CVS_LOCK_READ, where, 1);
+                          which, 0, CVS_LOCK_READ, where, 1, repository);
+    free (repository);
     free (where);
 
     return (err);
Index: src/recurse.c
===================================================================
RCS file: /cvs/ccvs/src/recurse.c,v
retrieving revision 1.77
diff -u -p -r1.77 recurse.c
--- src/recurse.c       28 Dec 2002 17:56:59 -0000      1.77
+++ src/recurse.c       24 Oct 2003 02:09:20 -0000
@@ -12,6 +12,7 @@
 #include "savecwd.h"
 #include "fileattr.h"
 #include "edit.h"
+#include <assert.h>
 
 static int do_dir_proc PROTO((Node * p, void *closure));
 static int do_file_proc PROTO((Node * p, void *closure));
@@ -35,6 +36,7 @@ struct recursion_frame {
     int aflag;
     int locktype;
     int dosrcs;
+    char *repository;                  /* Keep track of repository for rtag */
 };
 
 static int do_recursion PROTO ((struct recursion_frame *frame));
@@ -67,7 +69,7 @@ struct frame_and_entries {
 int
 start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat,
                 argc, argv, local, which, aflag, locktype,
-                update_preload, dosrcs)
+                update_preload, dosrcs, repository_in)
     FILEPROC fileproc;
     FILESDONEPROC filesdoneproc;
     DIRENTPROC         direntproc;
@@ -105,6 +107,15 @@ start_recursion (fileproc, filesdoneproc
     int locktype;
     char *update_preload;
     int dosrcs;
+    /* Keep track of the repository string.  This is only for the remote mode,
+     * specifically, r* commands (rtag, rdiff, co, ...) where xgetwd() was
+     * used to locate the repository.  Things would break when xgetwd() was
+     * used with a symlinked repository because xgetwd() would return the true
+     * path and in some cases this would cause the path to be printed as other
+     * than the user specified in error messages and in other cases some of
+     * CVS's security assertions would fail.
+     */
+    char *repository_in;
 {
     int i, err = 0;
 #ifdef CLIENT_SUPPORT
@@ -123,6 +134,7 @@ start_recursion (fileproc, filesdoneproc
     frame.aflag = aflag;
     frame.locktype = locktype;
     frame.dosrcs = dosrcs;
+    frame.repository = repository_in;
 
     expand_wild (argc, argv, &argc, &argv);
 
@@ -502,7 +514,7 @@ do_recursion (frame)
 {
     int err = 0;
     int dodoneproc = 1;
-    char *srepository;
+    char *srepository = NULL;
     List *entries = NULL;
     int locktype;
     int process_this_directory = 1;
@@ -613,17 +625,19 @@ do_recursion (frame)
     if (frame->which & W_LOCAL)
     {
        if (isdir (CVSADM))
+       {
            repository = Name_Repository ((char *) NULL, update_dir);
+           srepository = repository;           /* remember what to free */
+       }
        else
            repository = NULL;
     }
     else
     {
-       repository = xgetwd ();
-       if (repository == NULL)
-           error (1, errno, "could not get working directory");
+       repository = frame->repository;
+       assert ( repository != NULL );
+       assert ( strstr ( repository, "/./" ) == NULL );
     }
-    srepository = repository;          /* remember what to free */
 
     fileattr_startdir (repository);
 
@@ -662,7 +676,10 @@ do_recursion (frame)
               repository at this point.  Name_Repository will give a
               reasonable error message.  */
            if (repository == NULL)
+           {
                repository = Name_Repository ((char *) NULL, update_dir);
+               srepository = repository; /* remember what to free */
+           }
 
            /* find the files and fill in entries if appropriate */
            if (process_this_directory)
@@ -736,7 +753,10 @@ do_recursion (frame)
        err += walklist (filelist, do_file_proc, &frfile);
 
        /* unlock it */
-       if (locktype != CVS_LOCK_NONE)
+       if (/* We only lock the repository above when repository is set */
+           repository
+           /* and when asked for a read or write lock. */
+           && locktype != CVS_LOCK_NONE)
            Lock_Cleanup ();
 
        /* clean up */
@@ -778,8 +798,8 @@ do_recursion (frame)
     if (srepository)
     {
        free (srepository);
-       repository = (char *) NULL;
     }
+    repository = (char *) NULL;
 
     return (err);
 }
@@ -1093,7 +1113,30 @@ but CVS uses %s for its own purposes; sk
        /* make the recursive call */
        xframe = *frame;
        xframe.flags = dir_return;
+       /* Keep track of repository, really just for r* commands (rtag, rdiff,
+        * co, ...) to tag_check_valid, since all the other commands use
+        * CVS/Repository to figure it out per directory.
+        */
+       if ( repository )
+       {
+           if ( strcmp ( dir, "." ) == 0 )
+               xframe.repository = xstrdup ( repository );
+           else
+           {
+               xframe.repository = xmalloc ( strlen ( repository )
+                                             + strlen ( dir )
+                                             + 2 );
+               sprintf ( xframe.repository, "%s/%s", repository, dir );
+           }
+       }
+       else
+           xframe.repository = NULL;
        err += do_recursion (&xframe);
+       if ( xframe.repository )
+       {
+           free ( xframe.repository );
+           xframe.repository = NULL;
+       }
 
        /* put the `.' back if necessary */
        if (stripped_dot)
Index: src/remove.c
===================================================================
RCS file: /cvs/ccvs/src/remove.c,v
retrieving revision 1.52
diff -u -p -r1.52 remove.c
--- src/remove.c        28 Dec 2002 17:56:59 -0000      1.52
+++ src/remove.c        24 Oct 2003 02:09:20 -0000
@@ -90,7 +90,8 @@ cvsremove (argc, argv)
                start_recursion (remove_force_fileproc, (FILESDONEPROC) NULL,
                                 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
                                 (void *) NULL, argc, argv, local, W_LOCAL,
-                                0, CVS_LOCK_NONE, (char *) NULL, 0);
+                                0, CVS_LOCK_NONE, (char *) NULL, 0,
+                                (char *) NULL);
            }
            /* else FIXME should probably act as if the file doesn't exist
               in doing the following checks.  */
@@ -114,7 +115,8 @@ cvsremove (argc, argv)
     err = start_recursion (remove_fileproc, (FILESDONEPROC) NULL,
                            remove_dirproc, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv,
-                           local, W_LOCAL, 0, CVS_LOCK_READ, (char *) NULL, 1);
+                           local, W_LOCAL, 0, CVS_LOCK_READ, (char *) NULL, 1,
+                          (char *) NULL);
 
     if (removed_files && !really_quiet)
        error (0, 0, "use '%s commit' to remove %s permanently", program_name,
Index: src/sanity.sh
===================================================================
RCS file: /cvs/ccvs/src/sanity.sh,v
retrieving revision 1.752.2.48
diff -u -p -r1.752.2.48 sanity.sh
--- src/sanity.sh       24 Oct 2003 00:04:08 -0000      1.752.2.48
+++ src/sanity.sh       24 Oct 2003 02:09:20 -0000
@@ -22,7 +22,7 @@
 usage ()
 {
     echo "Usage: `basename $0` --help"
-    echo "Usage: `basename $0` [-kr] [-f FROM-TEST] CVS-TO-TEST 
[TESTS-TO-RUN...]"
+    echo "Usage: `basename $0` [-klr] [-f FROM-TEST] CVS-TO-TEST 
[TESTS-TO-RUN...]"
 }
 
 exit_usage ()
@@ -36,6 +36,8 @@ exit_help ()
     usage
     echo
     echo "-h|--help    display this text"
+    echo "-l|--link-root"
+    echo "             test CVS using a symlink to a real CVSROOT"
     echo "-r|--remote  test remote instead of local cvs"
     echo "-k|--keep    try to keep directories created by individual tests"
     echo "             around, exiting after the first test which supports"
@@ -71,8 +73,9 @@ export LC_ALL
 #
 unset fromtest
 keep=false
+linkroot=false
 remote=false
-while getopts f:Hkr-: option ; do
+while getopts f:Hklr-: option ; do
     # convert the long opts to short opts
     if test x$option = x-;  then
        case "$OPTARG" in
@@ -84,6 +87,10 @@ while getopts f:Hkr-: option ; do
                option=k;
                OPTARG=
                ;;
+           l|li|lin|link|link-|link-r]|link-ro|link-roo|link-root)
+               option=l;
+               OPTARG=
+               ;;
            
[rR]|[rR][eE]|[rR][eE][mM]|[rR][eE][mM][oO]|[rR][eE][mM][oO][tT]|[rR][eE][mM][oO][tT][eE])
                option=k;
                OPTARG=
@@ -108,6 +115,9 @@ while getopts f:Hkr-: option ; do
            # uniquely named dir (use the name of the test).
            keep=:
            ;;
+       l)
+           linkroot=:
+           ;;
        r)
            remote=:
            ;;
@@ -753,7 +763,7 @@ if test x"$*" = x; then
        # Multiple root directories and low-level protocol tests.
        tests="${tests} multiroot multiroot2 multiroot3 multiroot4"
        tests="${tests} rmroot reposmv pserver server server2 client"
-       tests="${tests} fork commit-d"
+       tests="${tests} dottedroot fork commit-d"
 else
        tests="$*"
 fi
@@ -1601,6 +1611,10 @@ EOF
 }
 
 # Set up CVSROOT (the crerepos tests will test operating without CVSROOT set).
+if $linkroot; then
+    mkdir ${TESTDIR}/realcvsroot
+    ln -s realcvsroot ${TESTDIR}/cvsroot
+fi
 CVSROOT_DIRNAME=${TESTDIR}/cvsroot
 if $remote; then
        # Currently we test :fork: and :ext: (see crerepos test).
@@ -12966,11 +12980,43 @@ ${PROG} commit: Rebuilding administrativ
         checkout_repository)
           dotest_fail checkout_repository-1 \
 "${testcvs} co -d ${CVSROOT_DIRNAME} CVSROOT" \
-"${PROG} \[checkout aborted\]: Cannot check out files into the repository 
itself"
+"${PROG} \[checkout aborted\]: Cannot check out files into the repository 
itself" \
+"${PROG} \[checkout aborted\]: absolute pathname \`${CVSROOT_DIRNAME}' illegal 
for server"
+
+         # The behavior of the client/server test below should be correct.
+         # The CVS client currently has no way of knowing that the client and
+         # server are the same machine and thus skips the $CVSROOT checks.
+         # I think checking for this case in CVS would be bloat since this
+         # should be a fairly rare occurance.
          cd ${CVSROOT_DIRNAME}
-          dotest_fail check_repository-2 "${testcvs} co CVSROOT" \
-"${PROG} \[checkout aborted\]: Cannot check out files into the repository 
itself"
-          dotest check_repository-3 "${testcvs} co -p CVSROOT/modules 
>/dev/null" \
+          dotest_fail checkout_repository-2 "${testcvs} co CVSROOT" \
+"${PROG} \[checkout aborted\]: Cannot check out files into the repository 
itself" \
+"${PROG} checkout: Updating CVSROOT
+${PROG} checkout: move away CVSROOT/checkoutlist; it is in the way
+C CVSROOT/checkoutlist
+${PROG} checkout: move away CVSROOT/commitinfo; it is in the way
+C CVSROOT/commitinfo
+${PROG} checkout: move away CVSROOT/config; it is in the way
+C CVSROOT/config
+${PROG} checkout: move away CVSROOT/cvswrappers; it is in the way
+C CVSROOT/cvswrappers
+${PROG} checkout: move away CVSROOT/editinfo; it is in the way
+C CVSROOT/editinfo
+${PROG} checkout: move away CVSROOT/loginfo; it is in the way
+C CVSROOT/loginfo
+${PROG} checkout: move away CVSROOT/modules; it is in the way
+C CVSROOT/modules
+${PROG} checkout: move away CVSROOT/notify; it is in the way
+C CVSROOT/notify
+${PROG} checkout: move away CVSROOT/rcsinfo; it is in the way
+C CVSROOT/rcsinfo
+${PROG} checkout: move away CVSROOT/taginfo; it is in the way
+C CVSROOT/taginfo
+${PROG} checkout: move away CVSROOT/verifymsg; it is in the way
+C CVSROOT/verifymsg"
+
+          dotest checkout_repository-3 \
+"${testcvs} co -p CVSROOT/modules >/dev/null" \
 "===================================================================
 Checking out CVSROOT/modules
 RCS:  ${CVSROOT_DIRNAME}/CVSROOT/modules,v
@@ -25471,6 +25517,47 @@ update"
          fi # skip the whole thing for local
          ;;
 
+       dottedroot)
+         # Check that a CVSROOT with a "." in the name will work.
+         CVSROOT_save=${CVSROOT}
+         CVSROOT_DIRNAME_save=${CVSROOT_DIRNAME}
+         CVSROOT_DIRNAME=${TESTDIR}/cvs.root
+         if $remote; then
+             CVSROOT=:fork:${CVSROOT_DIRNAME}
+         else
+             CVSROOT=${CVSROOT_DIRNAME}
+         fi
+
+         dotest dottedroot-init-1 "${testcvs} init" ""
+         mkdir dir1
+         mkdir dir1/dir2
+         echo version1 >dir1/dir2/file1
+         cd dir1
+         dotest dottedroot-1 "${testcvs} import -m '' module1 AUTHOR INITIAL" \
+"${PROG} [a-z]*: Importing ${CVSROOT_DIRNAME}/module1/dir2
+N module1/dir2/file1
+
+No conflicts created by this import"
+         cd ..
+
+         # This is the test that used to cause an assertion failure
+         # in recurse.c:do_recursion().
+         dotest dottedroot-2 "${testcvs} co -rINITIAL module1" \
+"${PROG} [a-z]*: Updating module1
+${PROG} [a-z]*: Updating module1/dir2
+U module1/dir2/file1"
+
+           if $keep; then
+             echo Keeping ${TESTDIR} and exiting due to --keep
+             exit 0
+           fi
+
+         rm -rf ${CVSROOT_DIRNAME}
+         rm -r dir1 module1
+         CVSROOT_DIRNAME=${CVSROOT_DIRNAME_save}
+         CVSROOT=${CVSROOT_save}
+         ;;
+ 
        fork)
          # Test that the server defaults to the correct executable in :fork:
          # mode.  See the note in the TODO at the end of this file about this.
Index: src/status.c
===================================================================
RCS file: /cvs/ccvs/src/status.c,v
retrieving revision 1.50.4.1
diff -u -p -r1.50.4.1 status.c
--- src/status.c        7 Mar 2003 06:46:29 -0000       1.50.4.1
+++ src/status.c        24 Oct 2003 02:09:20 -0000
@@ -107,7 +107,8 @@ cvsstatus (argc, argv)
     err = start_recursion (status_fileproc, (FILESDONEPROC) NULL,
                           status_dirproc, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local,
-                          W_LOCAL, 0, CVS_LOCK_READ, (char *) NULL, 1);
+                          W_LOCAL, 0, CVS_LOCK_READ, (char *) NULL, 1,
+                          (char *) NULL);
 
     return (err);
 }
Index: src/tag.c
===================================================================
RCS file: /cvs/ccvs/src/tag.c,v
retrieving revision 1.100
diff -u -p -r1.100 tag.c
--- src/tag.c   28 Dec 2002 17:56:59 -0000      1.100
+++ src/tag.c   24 Oct 2003 02:09:20 -0000
@@ -353,14 +353,12 @@ rtag_proc (argc, argv, xwhere, mwhere, m
            free (repository);
            return (1);
        }
-       free (repository);
        /* End section which is identical to patch_proc.  */
 
        if (delete_flag || attic_too || (force_tag_match && numtag))
            which = W_REPOS | W_ATTIC;
        else
            which = W_REPOS;
-       repository = NULL;
     }
     else
     {
@@ -382,7 +380,7 @@ rtag_proc (argc, argv, xwhere, mwhere, m
     err = start_recursion (check_fileproc, check_filesdoneproc,
                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc - 1, argv + 1, local_specified, which, 0,
-                          CVS_LOCK_READ, where, 1);
+                          CVS_LOCK_READ, where, 1, repository);
     
     if (err)
     {
@@ -397,7 +395,9 @@ rtag_proc (argc, argv, xwhere, mwhere, m
     err = start_recursion (is_rtag ? rtag_fileproc : tag_fileproc,
                           (FILESDONEPROC) NULL, tag_dirproc,
                           (DIRLEAVEPROC) NULL, NULL, argc - 1, argv + 1,
-                          local_specified, which, 0, CVS_LOCK_WRITE, where, 1);
+                          local_specified, which, 0, CVS_LOCK_WRITE, where, 1,
+                          repository);
+    if ( which & W_REPOS ) free ( repository );
     dellist (&mtlist);
     if (where != NULL)
        free (where);
@@ -1285,7 +1285,7 @@ Numeric tag %s contains characters other
                           val_direntproc, (DIRLEAVEPROC) NULL,
                           (void *)&the_val_args,
                           argc, argv, local, which, aflag,
-                          CVS_LOCK_READ, NULL, 1);
+                          CVS_LOCK_READ, NULL, 1, repository);
     if (repository != NULL && repository[0] != '\0')
     {
        if (restore_cwd (&cwd, NULL))
Index: src/update.c
===================================================================
RCS file: /cvs/ccvs/src/update.c,v
retrieving revision 1.202.4.8
diff -u -p -r1.202.4.8 update.c
--- src/update.c        24 Oct 2003 00:04:10 -0000      1.202.4.8
+++ src/update.c        24 Oct 2003 02:09:20 -0000
@@ -414,7 +414,8 @@ update (argc, argv)
     /* call the command line interface */
     err = do_update (argc, argv, options, tag, date, force_tag_match,
                     local, update_build_dirs, aflag, update_prune_dirs,
-                    pipeout, which, join_rev1, join_rev2, (char *) NULL, 1);
+                    pipeout, which, join_rev1, join_rev2, (char *) NULL, 1,
+                    (char *) NULL);
 
     /* free the space Make_Date allocated if necessary */
     if (date != NULL)
@@ -429,7 +430,7 @@ update (argc, argv)
 int
 do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
           xprune, xpipeout, which, xjoin_rev1, xjoin_rev2, preload_update_dir,
-          xdotemplate)
+          xdotemplate, repository)
     int argc;
     char **argv;
     char *xoptions;
@@ -446,6 +447,7 @@ do_update (argc, argv, xoptions, xtag, x
     char *xjoin_rev2;
     char *preload_update_dir;
     int xdotemplate;
+    char *repository;
 {
     int err = 0;
     char *cp;
@@ -493,7 +495,7 @@ do_update (argc, argv, xoptions, xtag, x
        err = start_recursion (get_linkinfo_proc, (FILESDONEPROC) NULL,
                               (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                               argc, argv, local, which, aflag, CVS_LOCK_READ,
-                              preload_update_dir, 1);
+                              preload_update_dir, 1, (char *) NULL);
        if (err)
            return (err);
 
@@ -509,7 +511,7 @@ do_update (argc, argv, xoptions, xtag, x
     err = start_recursion (update_fileproc, update_filesdone_proc,
                           update_dirent_proc, update_dirleave_proc, NULL,
                           argc, argv, local, which, aflag, CVS_LOCK_READ,
-                          preload_update_dir, 1);
+                          preload_update_dir, 1, repository);
 
 #ifdef SERVER_SUPPORT
     if (server_active)
Index: src/update.h
===================================================================
RCS file: /cvs/ccvs/src/update.h,v
retrieving revision 1.5
diff -u -p -r1.5 update.h
--- src/update.h        14 Nov 2000 21:45:30 -0000      1.5
+++ src/update.h        24 Oct 2003 02:09:20 -0000
@@ -14,6 +14,6 @@ int do_update PROTO((int argc, char *arg
               char *xdate, int xforce, int local, int xbuild,
               int xaflag, int xprune, int xpipeout, int which,
               char *xjoin_rev1, char *xjoin_rev2, char *preload_update_dir,
-              int xdotemplate));
+              int xdotemplate, char *repository));
 int joining PROTO((void));
 extern int isemptydir PROTO ((char *dir, int might_not_exist));
Index: src/watch.c
===================================================================
RCS file: /cvs/ccvs/src/watch.c,v
retrieving revision 1.32.4.2
diff -u -p -r1.32.4.2 watch.c
--- src/watch.c 25 Feb 2003 16:17:57 -0000      1.32.4.2
+++ src/watch.c 24 Oct 2003 02:09:20 -0000
@@ -343,7 +343,7 @@ watch_addremove (argc, argv)
     err = start_recursion (addremove_fileproc, addremove_filesdoneproc,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE,
-                          (char *)NULL, 1);
+                          (char *)NULL, 1, (char *) NULL);
 
     Lock_Cleanup ();
     return err;
@@ -517,5 +517,5 @@ watchers (argc, argv)
     return start_recursion (watchers_fileproc, (FILESDONEPROC) NULL,
                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                            argc, argv, local, W_LOCAL, 0, CVS_LOCK_READ,
-                           (char *)NULL, 1);
+                           (char *)NULL, 1, (char *) NULL);
 }
Index: windows-NT/filesubr.c
===================================================================
RCS file: /cvs/ccvs/windows-NT/filesubr.c,v
retrieving revision 1.37
diff -u -p -r1.37 filesubr.c
--- windows-NT/filesubr.c       28 Dec 2002 17:58:07 -0000      1.37
+++ windows-NT/filesubr.c       24 Oct 2003 03:40:03 -0000
@@ -741,6 +741,45 @@ isabsolute (filename)
                 && ISDIRSEP (filename[2])));
 }
 
+/* char *
+ * xresolvepath ( const char *path )
+ *
+ * Like xreadlink(), but resolve all links in a path.
+ *
+ * INPUTS
+ *  path       The original path.
+ *
+ * RETURNS
+ *  The path with any symbolic links expanded.
+ *
+ * ERRORS
+ *  This function exits with a fatal error if it fails to read the link for
+ *  any reason.
+ */
+char *
+xresolvepath ( path )
+    const char *path;
+{
+    char *hardpath;
+    char *owd;
+
+    assert ( isdir ( path ) );
+
+    /* FIXME - If HAVE_READLINK is defined, we should probably walk the path
+     * bit by bit calling xreadlink().
+     */
+
+    owd = xgetwd();
+    if ( CVS_CHDIR ( path ) < 0)
+       error ( 1, errno, "cannot chdir to %s", path );
+    if ( ( hardpath = xgetwd() ) == NULL )
+       error (1, errno, "cannot readlink %s", hardpath);
+    if ( CVS_CHDIR ( owd ) < 0)
+       error ( 1, errno, "cannot chdir to %s", owd );
+    free (owd);
+    return hardpath;
+}
+
 /* Return a pointer into PATH's last component.  */
 char *
 last_component (char *path)
@@ -1032,3 +1071,31 @@ wnt_lstat (const char *file, struct stat
     check_statbuf (file, sb);
     return retval;
 }
+
+#ifdef SERVER_SUPPORT
+/* Case-insensitive string compare.  I know that some systems
+   have such a routine, but I'm not sure I see any reasons for
+   dealing with the hair of figuring out whether they do (I haven't
+   looked into whether this is a performance bottleneck; I would guess
+   not).  */
+int
+cvs_casecmp (str1, str2)
+    char *str1;
+    char *str2;
+{
+    char *p;
+    char *q;
+    int pqdiff;
+
+    p = str1;
+    q = str2;
+    while ((pqdiff = tolower (*p) - tolower (*q)) == 0)
+    {
+       if (*p == '\0')
+           return 0;
+       ++p;
+       ++q;
+    }
+    return pqdiff;
+}
+#endif /* SERVER_SUPPORT */




reply via email to

[Prev in Thread] Current Thread [Next in Thread]