info-cvs
[Top][All Lists]
Advanced

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

End-of-line patch


From: Philippe Payant
Subject: End-of-line patch
Date: Wed, 13 Dec 2000 14:03:17 -0500

We needed this at work to suit our working environment.  Maybe you do too.

>Submitter-Id:   net
>Originator:     Philippe Payant
>Organization:   net
>Confidential:   no
>Synopsis:  client can use any end-of-line style (newline or CR-LF pairs)
>Severity:  non-critical
>Priority:  low
>Category:  cvs
>Class:     change
>Release:   cvs1-11 is the latest tag at time of patch creation
>Environment: Win32 and Solaris 8


>Description:
    The CVS client always works with text files in the platform's
    native end-of-line style.  On Win32 platforms, a line is
    ended with a CR-LF pair.  On Unix platforms, a line is
    ended with a newline character.  Users in mixed environment
    may want to use CVS from either platform to work with files
    compatible with either platform.  This patch gives this
    ability to CVS.

    Two new global options have been added: "--newline" and "--crlf".
    "--newline" makes the client treat and create files with the Unix
    style end-of-line (ended with a newline).  "--crlf" makes the client
    treat and create files with the Windows style eof-of-line (ended with
    a CR-LF pair.)  When neither option are used, the platform's natural
    EOL style is used.

    Also, you can control the EOL style with the environment variable
    "CVSEOLSTYLE".  The accepted values are "newline" and "crlf".

    Binary files are still handled correctly.

    Example: cvs --newline co ccvs

>How-To-Repeat:
    Non-applicable: this is a new feature.
>Fix:
    Non-applicable: this is a new feature.

>Licensing:
    I grant permission to distribute this patch under
    the terms of the GNU Public License

2000-Dec-11 Philippe Payant (address@hidden)

    src/cvs.h: added constant CVSEOLSTYLE, added enum EolStyle
      and global variable eol_style declaration.
      Added prototypes to write_file() and read_file().
    src/client.c: changed the way the binary option for CVS_OPEN
      is selected in update_entries() and send_modified().  Replaced
      calls to write(), fwrite() and read() to calls to write_file() or
      read_file().  Added parameter to gunzip_and_write() and
      read_and_gzip().
    src/main.c: added options to the options struct array and
      switch/case to handle the new options.  Added check for the
      environment variable "CVSEOLSTYLE".
    src/filesubr.c and windows-NT/filesubr.c: added functions
      write_file() and read_file(), according to the platform's
      peculiarities.
    src/server.c: added parameter to gunzip_and_write()
      (in function receive_file()) and read_and_gzip()
      (in function server_updated().)
    src/server.h: modified the prototypes to gunzip_and_write() and
      read_and_gzip().
    src/zlib.c: modified gunzip_and_write() and read_and_gzip() to
      forward the new parameter to write_file() and read_file(),
      which they now call respectively.

Index: src/client.c
===================================================================
RCS file: /home2/cvsroot/ccvs/src/client.c,v
retrieving revision 1.291
diff -c -u -r1.291 client.c
--- src/client.c        2000/11/28 21:23:21     1.291
+++ src/client.c        2000/12/11 21:19:53
@@ -1827,7 +1827,11 @@
            library I/O funtions.  Here we tell them whether or not to
            convert -- if this file is marked "binary" with the RCS -kb
            flag, then we don't want to convert, else we do (because
-           CVS assumes text files by default). */
+           CVS assumes text files by default).
+
+           Binary files should ALWAYS be opened in binary mode.
+           This is compounded by the issue of selected EOL style.
+           Check out write_file() in filesubr.c. */

        if (options)
            bin = !(strcmp (options, "-kb"));
@@ -1848,10 +1852,22 @@
        else
        {
            int fd;
+        int open_bin;
+
+        /* File should be opened as binary or not
+           depending on the platform AND the selected
+           EOL style AND the file's real type. */
+
+#ifdef WIN32
+/* OR OS/2, but for OS/2 I do not know nor can test what to put here... */
+        open_bin = bin || EOL_NEWLINE == eol_style;
+#else
+        open_bin = bin || EOL_CRLF == eol_style;
+#endif

            fd = CVS_OPEN (temp_filename,
                           (O_WRONLY | O_CREAT | O_TRUNC
-                           | (bin ? OPEN_BINARY : 0)),
+                           | (open_bin ? OPEN_BINARY : 0)),
                           0777);

            if (fd < 0)
@@ -1873,11 +1889,11 @@

                if (use_gzip)
                {
-                   if (gunzip_and_write (fd, short_pathname,
+                   if (gunzip_and_write (fd, bin, short_pathname,
                                          (unsigned char *) buf, size))
                        error (1, 0, "aborting due to compression error");
                }
-               else if (write (fd, buf, size) != size)
+               else if (write_file (fd, bin, buf, size) != size)
                    error (1, errno, "writing %s", short_pathname);
            }

@@ -1973,15 +1989,36 @@

                if (! patch_failed)
                {
-                   FILE *e;
+            int fd;
+            int open_bin;
+
+            /* File should be opened as binary or not
+               depending on the platform AND the selected
+               EOL style AND the file's real type. */
+
+#ifdef WIN32 /* OR OS/2, but I do not know nor can test what to put here...
*/
+            open_bin = bin || EOL_NEWLINE == eol_style;
+#else
+            open_bin = bin || EOL_CRLF == eol_style;
+#endif

-                   e = open_file (temp_filename,
-                                  bin ? FOPEN_BINARY_WRITE : "w");
-                   if (fwrite (patchedbuf, 1, patchedlen, e) != patchedlen)
-                       error (1, errno, "cannot write %s", temp_filename);
-                   if (fclose (e) == EOF)
-                       error (1, errno, "cannot close %s", temp_filename);
-                   rename_file (temp_filename, filename);
+               fd = CVS_OPEN (temp_filename,
+                              (O_WRONLY | O_CREAT | O_TRUNC
+                               | (open_bin ? OPEN_BINARY : 0)),
+                              0777);
+
+               if (fd < 0)
+               {
+                   error (0, errno, "cannot write %s", short_pathname);
+                   goto discard_file_and_return;
+               }
+
+                   if (write_file (fd, bin, patchedbuf, patchedlen) != 
patchedlen)
+               error (1, errno, "writing %s", short_pathname);
+
+               if (close (fd) < 0)
+                   error (1, errno, "writing %s", short_pathname);
+
                }

                free (patchedbuf);
@@ -4914,6 +4951,7 @@
     char *mode_string;
     size_t bufsize;
     int bin;
+    int open_bin;

     if (trace)
        (void) fprintf (stderr, " -> Sending file `%s' to server\n", file);
@@ -4957,7 +4995,15 @@
     else
        fd = CVS_OPEN (file, O_RDONLY | OPEN_BINARY);
 #else
-    fd = CVS_OPEN (file, O_RDONLY | (bin ? OPEN_BINARY : 0));
+
+#ifdef WIN32
+/* OR OS/2, but for OS/2 I do not know nor can test what to put here... */
+        open_bin = bin || EOL_NEWLINE == eol_style;
+#else
+        open_bin = bin || EOL_CRLF == eol_style;
+#endif
+
+    fd = CVS_OPEN (file, O_RDONLY | (open_bin ? OPEN_BINARY : 0));
 #endif

     if (fd < 0)
@@ -4967,7 +5013,7 @@
     {
        size_t newsize = 0;

-       if (read_and_gzip (fd, short_pathname, (unsigned char **)&buf,
+       if (read_and_gzip (fd, bin, short_pathname, (unsigned char **)&buf,
                           &bufsize, &newsize,
                           file_gzip_level))
            error (1, 0, "aborting due to compression error");
@@ -5002,12 +5048,13 @@
               Instead of this we should just be reading a block of
               data (e.g. 8192 bytes), writing it to the network, and
               so on until EOF.  */
-           while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0)
+           while ((len = read_file (fd, bin, bufp, (buf + sb.st_size) - bufp)) 
>
0)
                bufp += len;

            if (len < 0)
                error (1, errno, "reading %s", short_pathname);

+        /* Actual size read in buffer. */
            newsize = bufp - buf;
        }
        if (close (fd) < 0)
Index: src/cvs.h
===================================================================
RCS file: /home2/cvsroot/ccvs/src/cvs.h,v
retrieving revision 1.208
diff -c -u -r1.208 cvs.h
--- src/cvs.h   2000/11/15 21:21:11     1.208
+++ src/cvs.h   2000/12/11 21:19:54
@@ -267,6 +267,8 @@
 #define        CVSROOT_ENV     "CVSROOT"       /* source directory root */
 #define        CVSROOT_DFLT    NULL            /* No dflt; must set for 
checkout */

+#define EOLSTYLE_ENV "CVSEOLSTYLE"   /* eol style the client should use */
+
 #define        IGNORE_ENV      "CVSIGNORE"     /* More files to ignore */
 #define WRAPPER_ENV     "CVSWRAPPERS"   /* name of the wrapper file */

@@ -355,6 +357,9 @@
 typedef enum direnter_type Dtype;
 #endif

+/* Values for EOL style selection. */
+typedef enum { EOL_DEFAULT, EOL_NEWLINE, EOL_CRLF } EolStyle;
+
 extern char *program_name, *program_path, *command_name;
 extern char *Tmpdir, *Editor;
 extern int cvsadmin_root;
@@ -363,6 +368,7 @@
 extern int use_editor;
 extern int cvswrite;
 extern mode_t cvsumask;
+extern EolStyle eol_style;

 /* Access method specified in CVSroot. */
 typedef enum {
@@ -436,6 +442,8 @@

 DBM *open_module PROTO((void));
 FILE *open_file PROTO((const char *, const char *));
+int write_file PROTO((int fd, int bin, const void *buf, unsigned int len));
+int read_file PROTO((int fd, int bin, void *buf, unsigned int len));
 List *Find_Directories PROTO((char *repository, int which, List *entries));
 void Entries_Close PROTO((List *entries));
 List *Entries_Open PROTO ((int aflag, char *update_dir));
Index: src/filesubr.c
===================================================================
RCS file: /home2/cvsroot/ccvs/src/filesubr.c,v
retrieving revision 1.51
diff -c -u -r1.51 filesubr.c
--- src/filesubr.c      2000/11/16 23:45:54     1.51
+++ src/filesubr.c      2000/12/11 21:19:54
@@ -278,6 +278,165 @@
 }

 /*
+ * Here is what should be done to handle the EOL style.
+ *
+ * Default:
+ *   open/write/read: nothing to do
+ *
+ * Newline (--newline):
+ *   On Unix: open/write/read: nothing to do
+ *
+ * CR-LF (--crlf):
+ *   On Unix: open binary, write with conversion, read with conversion
+ *
+ */
+
+/*
+ * Write to a file according to the
+ * selected end-of-line style (default, newline or CR-LF.)
+ */
+int write_file (fd, bin, buf, len)
+    int fd;
+    int bin;
+    const void *buf;
+    unsigned int len;
+{
+
+    const void *start_of_line = NULL;
+    const void *p = NULL;
+    const char crlf[2] = { '\r', '\n' };
+    int writ = 0;
+    int e = -1;
+    int total_written = 0;
+
+
+    if (bin || EOL_CRLF != eol_style)
+    {
+
+    /* Output "as-is". */
+    return write (fd, buf, len);
+
+    }
+    else
+    {
+
+    /* Conversion from newline to CR-LF and not binary. */
+    start_of_line = buf;
+    total_written = 0;
+
+    while (start_of_line < buf+len)
+    {
+
+        /* Search for next newline. */
+        p = start_of_line;
+
+        while (p < buf+len)
+        {
+
+            if ('\n' == *((const char*)p))
+            {
+                /* Output up to newline (but not including it.) */
+                writ = p-start_of_line;
+                e = write (fd, start_of_line, writ);
+
+                if (e != writ)
+                {
+                if (-1 == e)
+                return e;
+
+                /* Written some part of it... */
+                total_written += e;
+                return total_written;
+                }
+
+                total_written += e;
+
+                /* Output CR-LF sequence. */
+                writ = sizeof (crlf);
+                e = write (fd, crlf, writ);
+
+                if (e != writ)
+                {
+                if (-1 == e)
+                return e;
+
+                /* Could not write complete CR-LF, so
+                 * this we should make it look like
+                 * the newline we are converting did not
+                 * get written... I guess it is
+                 * best like that.
+                 */
+                return total_written;
+                }
+
+                /* Increment because we are spoofing the write! */
+                total_written ++;
+
+                /* Skip LF and move start of line. */
+                p ++;
+                start_of_line = p;
+                break;
+            }
+
+            p ++;
+
+        } /* End while looking for a '\n'. */
+
+    } /* End while not end of buffer. */
+
+    return total_written;
+
+    }
+
+}
+
+/*
+ * Read from a file removing all CRs
+ * and leaving only the LFs.
+ */
+int read_file (fd, bin, buf, len)
+    int fd;
+    int bin;
+    void *buf;
+    unsigned int len;
+{
+
+    int nread;
+    int i;
+
+    /* First, read the buffer. */
+    nread = read (fd, buf, len);
+
+    if (bin || EOL_CRLF != eol_style)
+    {
+    return nread;
+    }
+
+
+    /* Conversion from CR-LF to newline. */
+
+    i = 0;
+
+    while (i < nread)
+    {
+
+        if ('\r' == ((char*)buf)[i])
+        {
+        /* Found a CR, remove it */
+        memmove (buf+i, buf+i+1, nread-i);
+        nread --;
+        }
+        else
+        {
+        i++;
+        }
+    }
+
+    return nread;
+
+}
+
+/*
  * Make a directory and die if it fails
  */
 void
Index: src/main.c
===================================================================
RCS file: /home2/cvsroot/ccvs/src/main.c,v
retrieving revision 1.155
diff -c -u -r1.155 main.c
--- src/main.c  2000/11/29 21:18:46     1.155
+++ src/main.c  2000/12/11 21:19:55
@@ -42,6 +42,7 @@
 int trace = 0;
 int noexec = 0;
 int logoff = 0;
+EolStyle eol_style = EOL_DEFAULT;

 /* Set if we should be writing CVSADM directories at top level.  At
    least for now we'll make the default be off (the CVS 1.9, not CVS
@@ -437,10 +438,12 @@
     {
         {"help", 0, NULL, 'H'},
         {"version", 0, NULL, 'v'},
-       {"help-commands", 0, NULL, 1},
-       {"help-synonyms", 0, NULL, 2},
-       {"help-options", 0, NULL, 4},
-       {"allow-root", required_argument, NULL, 3},
+        {"help-commands", 0, NULL, 1},
+        {"help-synonyms", 0, NULL, 2},
+        {"help-options", 0, NULL, 4},
+        {"allow-root", required_argument, NULL, 3},
+        {"newline", 0, NULL, 5},
+        {"crlf", 0, NULL, 6},
         {0, 0, 0, 0}
     };
     /* `getopt_long' stores the option index here, but right now we
@@ -493,9 +496,19 @@
        CVSroot = cp;
        cvs_update_env = 0;             /* it's already there */
     }
+
     if (getenv (CVSREAD_ENV) != NULL)
        cvswrite = 0;

+    if ((cp = getenv (EOLSTYLE_ENV)) != NULL)
+    {
+    if (strcmp (cp,"newline")==0)
+        eol_style = EOL_NEWLINE;
+
+    if (strcmp (cp,"crlf")==0)
+        eol_style = EOL_CRLF;
+    }
+
     /* Set this to 0 to force getopt initialization.  getopt() sets
        this to 1 internally.  */
     optind = 0;
@@ -545,6 +558,14 @@
                /* --allow-root */
                root_allow_add (optarg);
                break;
+        case 5:
+        /* --newline */
+        eol_style = EOL_NEWLINE;
+        break;
+        case 6:
+        /* --crlf */
+        eol_style = EOL_CRLF;
+        break;
            case 'Q':
                really_quiet = 1;
                /* FALL THROUGH */
Index: src/server.c
===================================================================
RCS file: /home2/cvsroot/ccvs/src/server.c,v
retrieving revision 1.246
diff -c -u -r1.246 server.c
--- src/server.c        2000/10/20 20:48:00     1.246
+++ src/server.c        2000/12/11 21:19:57
@@ -1430,7 +1430,7 @@
            goto out;
        }

-       if (gunzip_and_write (fd, file, (unsigned char *) filebuf, size))
+       if (gunzip_and_write (fd, 0, file, (unsigned char *) filebuf, size))
        {
            if (alloc_pending (80))
                sprintf (pending_error_text,
@@ -4080,7 +4080,7 @@
                fd = CVS_OPEN (finfo->file, O_RDONLY | OPEN_BINARY, 0);
                if (fd < 0)
                    error (1, errno, "reading %s", finfo->fullname);
-               if (read_and_gzip (fd, finfo->fullname, &file,
+               if (read_and_gzip (fd, 0, finfo->fullname, &file,
                                   &file_allocated, &file_used,
                                   file_gzip_level))
                    error (1, 0, "aborting due to compression error");
Index: src/server.h
===================================================================
RCS file: /home2/cvsroot/ccvs/src/server.h,v
retrieving revision 1.22
diff -c -u -r1.22 server.h
--- src/server.h        1998/10/21 17:09:43     1.22
+++ src/server.h        2000/12/11 21:19:57
@@ -173,6 +173,6 @@
 extern struct request requests[];

 /* Gzip library, see zlib.c.  */
-extern int gunzip_and_write PROTO ((int, char *, unsigned char *, size_t));
-extern int read_and_gzip PROTO ((int, char *, unsigned char **, size_t *,
+extern int gunzip_and_write PROTO ((int, int, char *, unsigned char *,
size_t));
+extern int read_and_gzip PROTO ((int, int, char *, unsigned char **, size_t
*,
                                 size_t *, int));
Index: src/zlib.c
===================================================================
RCS file: /home2/cvsroot/ccvs/src/zlib.c,v
retrieving revision 1.8
diff -c -u -r1.8 zlib.c
--- src/zlib.c  2000/07/10 19:35:32     1.8
+++ src/zlib.c  2000/12/11 21:19:57
@@ -438,8 +438,9 @@
    it is an error we can't recover from.  */

 int
-gunzip_and_write (fd, fullname, buf, size)
+gunzip_and_write (fd, bin, fullname, buf, size)
     int fd;
+    int bin;
     char *fullname;
     unsigned char *buf;
     size_t size;
@@ -502,7 +503,7 @@
            compress_error (0, zstatus, &zstr, fullname);
            return 1;
        }
-       if (write (fd, outbuf, sizeof (outbuf) - zstr.avail_out) < 0)
+       if (write_file (fd, bin, outbuf, sizeof (outbuf) - zstr.avail_out) < 0)
        {
            error (0, errno, "writing decompressed file %s", fullname);
            return 1;
@@ -542,8 +543,9 @@
    recover from it).  LEVEL is the compression level (1-9).  */

 int
-read_and_gzip (fd, fullname, buf, size, len, level)
+read_and_gzip (fd, bin, fullname, buf, size, len, level)
     int fd;
+    int bin;
     char *fullname;
     unsigned char **buf;
     size_t *size;
@@ -594,7 +596,7 @@
     {
        int finish = 0;

-       nread = read (fd, inbuf, sizeof inbuf);
+       nread = read_file (fd, bin, inbuf, sizeof inbuf);
        if (nread < 0)
        {
            error (0, errno, "cannot read %s", fullname);
Index: windows-NT/filesubr.c
===================================================================
RCS file: /home2/cvsroot/ccvs/windows-NT/filesubr.c,v
retrieving revision 1.36
diff -c -u -r1.36 filesubr.c
--- windows-NT/filesubr.c       2000/11/22 01:11:16     1.36
+++ windows-NT/filesubr.c       2000/12/11 21:19:58
@@ -242,6 +242,64 @@
 }

 /*
+ * Here is what should be done to handle the EOL style.
+ *
+ * Default:
+ *   open/write/read: nothing to do
+ *
+ * Newline (--newline):
+ *   On Windows: open binary, write no conversion, read no conversion
+ *
+ * CR-LF (--crlf):
+ *   On Windows: open/write/read: nothing to do
+ *
+ */
+
+/*
+ * Write to a file according to the
+ * selected end-of-line style (default, newline or CR-LF.)
+ */
+int write_file (fd, bin, buf, len)
+    int fd;
+    int bin;
+    const char *buf;
+    unsigned int len;
+{
+
+    bin;
+    /*
+     * Assuming the data coming from the repository
+     * is always newline EOL style, then this will
+     * always work.
+     */
+    return write (fd, buf, len);
+
+}
+
+/*
+ * Read from a file removing all CRs
+ * and leaving only the LFs.
+ */
+int read_file (fd, bin, buf, len)
+    int fd;
+    int bin;
+    char *buf;
+    unsigned int len;
+{
+
+    bin;
+    /*
+     * Maybe we could check if the dat contains CR-LF and
+     * the file is not supposed to be binary.  This would
+     * mean that the user is commiting a file with --newline
+     * assuming it has newline EOL style but in fact it does
+     * not.
+     */
+    return read (fd, buf, len);
+
+}
+
+/*
  * Make a directory and die if it fails
  */
 void



Philippe Payant ifta
<< Freshly developed software can work perfectly for weeks in test.  Freshly
tested software can fail unexpectedly during demos.  The Greek god of fear
is named Deimos. >>

Mist Wireless
<mailto:address@hidden>






reply via email to

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