Index: doc/open.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/open.n,v
retrieving revision 1.23
diff -u -r1.23 open.n
--- doc/open.n	6 Apr 2005 20:55:23 -0000	1.23
+++ doc/open.n	27 Apr 2005 18:07:45 -0000
@@ -61,6 +61,14 @@
 Open the file for reading and writing.  If the file doesn't exist,
 create a new empty file.
 Set the initial access position  to the end of the file.
+.VS 8.5
+.PP
+All of the legal \fIaccess\fR values above may have the character
+\fBb\fR added as the second or third character in the value to
+indicate that the opened channel should be configured with the
+\fB-translation binary\fR option, making the channel suitable for 
+reading or writing of binary data.
+.VE 8.5
 .PP
 In the second form, \fIaccess\fR consists of a list of any of the
 following flags, all of which have the standard POSIX meanings.
@@ -78,6 +86,11 @@
 \fBAPPEND\fR
 Set the file pointer to the end of the file prior to each write.
 .TP 15
+.VS 8.5
+\fBBINARY\fR
+Configure the opened channed with the \fB-translation binary\fR option.
+.VE 8.5
+.TP 15
 \fBCREAT\fR
 Create the file if it doesn't already exist (without this flag it
 is an error for the file not to exist).
@@ -106,14 +119,6 @@
 (an integer) is used to set the permissions for the new file in
 conjunction with the process's file mode creation mask.
 \fIPermissions\fR defaults to 0666.
-.PP
-Note that if you are going to be reading or writing binary data from
-the channel created by this command, you should use the
-\fBfconfigure\fR command to change the \fB-translation\fR option of
-the channel to \fBbinary\fR before transferring any binary data.  This
-is in contrast to the ``b'' character passed as part of the equivalent
-of the \fIaccess\fR parameter to some versions of the C library
-\fIfopen()\fR function.
 
 .SH "COMMAND PIPELINES"
 .PP
Index: generic/tclIOCmd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIOCmd.c,v
retrieving revision 1.22
diff -u -r1.22 tclIOCmd.c
--- generic/tclIOCmd.c	7 Oct 2004 00:24:49 -0000	1.22
+++ generic/tclIOCmd.c	27 Apr 2005 18:07:45 -0000
@@ -960,14 +960,14 @@
     if (!pipeline) {
         chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot);
     } else {
-	int mode, seekFlag, cmdObjc;
+	int mode, seekFlag, cmdObjc, binary;
 	CONST char **cmdArgv;
 
         if (Tcl_SplitList(interp, what+1, &cmdObjc, &cmdArgv) != TCL_OK) {
             return TCL_ERROR;
         }
 
-        mode = TclGetOpenMode(interp, modeString, &seekFlag);
+        mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary);
         if (mode == -1) {
 	    chan = NULL;
         } else {
@@ -987,6 +987,9 @@
 		    break;
 	    }
 	    chan = Tcl_OpenCommandChannel(interp, cmdObjc, cmdArgv, flags);
+	    if (binary) {
+		Tcl_SetChannelOption(interp, chan, "-translation", "binary");
+	    }
 	}
         ckfree((char *) cmdArgv);
     }
Index: generic/tclIOUtil.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIOUtil.c,v
retrieving revision 1.116
diff -u -r1.116 tclIOUtil.c
--- generic/tclIOUtil.c	16 Apr 2005 08:04:56 -0000	1.116
+++ generic/tclIOUtil.c	27 Apr 2005 18:07:46 -0000
@@ -1412,9 +1412,43 @@
  * TclGetOpenMode --
  *
  * Description:
+ * 	This routine is an obsolete, limited version of
+ * 	TclGetOpenModeEx() below.  It exists only to satisfy any
+ * 	extensions imprudently using it via Tcl's internal stubs table.
+ *
+ * Results:
+ * 	Same as TclGetOpenModeEx().
+ *
+ * Side effects:
+ * 	Same as TclGetOpenModeEx().
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TclGetOpenMode(interp, modeString, seekFlagPtr)
+    Tcl_Interp *interp;			/* Interpreter to use for error
+					 * reporting - may be NULL. */
+    CONST char *modeString;		/* Mode string, e.g. "r+" or
+					 * "RDONLY CREAT". */
+    int *seekFlagPtr;			/* Set this to 1 if the caller
+                                         * should seek to EOF during the
+                                         * opening of the file. */
+{
+    int binary = 0;
+    return TclGetOpenModeEx(interp, modeString, seekFlagPtr, &binary);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclGetOpenModeEx --
+ *
+ * Description:
  *	Computes a POSIX mode mask for opening a file, from a given string,
- *	and also sets a flag to indicate whether the caller should seek to
- *	EOF after opening the file.
+ *	and also sets flags to indicate whether the caller should seek to
+ *	EOF after opening the file, and whether the caller should
+ *	configure the channel for binary data.
  *
  * Results:
  *	On success, returns mode to pass to "open". If an error occurs, the
@@ -1423,7 +1457,9 @@
  *
  * Side effects:
  *	Sets the integer referenced by seekFlagPtr to 1 to tell the caller
- *	to seek to EOF after opening the file.
+ *	to seek to EOF after opening the file, or to 0 otherwise.  Sets the
+ *	integer referenced by binaryPtr to 1 to tell the caller	to seek to
+ *	configure the channel for binary data, or to 0 otherwise.
  *
  * Special note:
  *	This code is based on a prototype implementation contributed
@@ -1431,16 +1467,18 @@
  *
  *---------------------------------------------------------------------------
  */
-
 int
-TclGetOpenMode(interp, string, seekFlagPtr)
+TclGetOpenModeEx(interp, modeString, seekFlagPtr, binaryPtr)
     Tcl_Interp *interp;			/* Interpreter to use for error
 					 * reporting - may be NULL. */
-    CONST char *string;			/* Mode string, e.g. "r+" or
+    CONST char *modeString;		/* Mode string, e.g. "r+" or
 					 * "RDONLY CREAT". */
     int *seekFlagPtr;			/* Set this to 1 if the caller
                                          * should seek to EOF during the
                                          * opening of the file. */
+    int *binaryPtr;			/* Set this to 1 if the caller
+					 * should configure the opened
+					 * channel for binary operations */
 {
     int mode, modeArgc, c, i, gotRW;
     CONST char **modeArgv, *flag;
@@ -1453,6 +1491,7 @@
      */
 
     *seekFlagPtr = 0;
+    *binaryPtr = 0;
     mode = 0;
 
     /*
@@ -1460,9 +1499,9 @@
      * routines.
      */
 
-    if (!(string[0] & 0x80)
-	    && islower(UCHAR(string[0]))) { /* INTL: ISO only. */
-	switch (string[0]) {
+    if (!(modeString[0] & 0x80)
+	    && islower(UCHAR(modeString[0]))) { /* INTL: ISO only. */
+	switch (modeString[0]) {
 	    case 'r':
 		mode = O_RDONLY;
 		break;
@@ -1475,20 +1514,33 @@
 		break;
 	    default:
 		error:
+                *seekFlagPtr = 0;
+		*binaryPtr = 0;
                 if (interp != (Tcl_Interp *) NULL) {
                     Tcl_AppendResult(interp,
-                            "illegal access mode \"", string, "\"",
+                            "illegal access mode \"", modeString, "\"",
                             (char *) NULL);
                 }
 		return -1;
 	}
-	if (string[1] == '+') {
-	    mode &= ~(O_RDONLY|O_WRONLY);
-	    mode |= O_RDWR;
-	    if (string[2] != 0) {
+	i=1;
+	while (i<3 && modeString[i]) {
+	    if (modeString[i] == modeString[i-1]) {
 		goto error;
 	    }
-	} else if (string[1] != 0) {
+	    switch (modeString[i++]) {
+		case '+':
+		    mode &= ~(O_RDONLY|O_WRONLY);
+		    mode |= O_RDWR;
+		    break;
+		case 'b':
+		    *binaryPtr = 1;
+		    break;
+		default:
+		    goto error;
+	    }
+	}
+	if (modeString[i] != 0) {
 	    goto error;
 	}
         return mode;
@@ -1502,11 +1554,11 @@
      * a NULL interpreter is passed in.
      */
 
-    if (Tcl_SplitList(interp, string, &modeArgc, &modeArgv) != TCL_OK) {
+    if (Tcl_SplitList(interp, modeString, &modeArgc, &modeArgv) != TCL_OK) {
         if (interp != (Tcl_Interp *) NULL) {
             Tcl_AddErrorInfo(interp,
                     "\n    while processing open access modes \"");
-            Tcl_AddErrorInfo(interp, string);
+            Tcl_AddErrorInfo(interp, modeString);
             Tcl_AddErrorInfo(interp, "\"");
         }
         return -1;
@@ -1560,11 +1612,14 @@
 #endif
 	} else if ((c == 'T') && (strcmp(flag, "TRUNC") == 0)) {
 	    mode |= O_TRUNC;
+	} else if ((c == 'B') && (strcmp(flag, "BINARY") == 0)) {
+	    *binaryPtr = 1;
 	} else {
             if (interp != (Tcl_Interp *) NULL) {
                 Tcl_AppendResult(interp, "invalid access mode \"", flag,
-                        "\": must be RDONLY, WRONLY, RDWR, APPEND, CREAT",
-                        " EXCL, NOCTTY, NONBLOCK, or TRUNC", (char *) NULL);
+			"\": must be RDONLY, WRONLY, RDWR, APPEND, BINARY, "
+			"CREAT, EXCL, NOCTTY, NONBLOCK, or TRUNC",
+			(char *) NULL);
             }
 	    ckfree((char *) modeArgv);
 	    return -1;
@@ -2086,8 +2141,8 @@
     if (fsPtr != NULL) {
 	Tcl_FSOpenFileChannelProc *proc = fsPtr->openFileChannelProc;
 	if (proc != NULL) {
-	    int mode, seekFlag;
-	    mode = TclGetOpenMode(interp, modeString, &seekFlag);
+	    int mode, seekFlag, binary;
+	    mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary);
 	    if (mode == -1) {
 	        return NULL;
 	    }
@@ -2106,6 +2161,10 @@
 			return NULL;
 		    }
 		}
+		if (binary) {
+		    Tcl_SetChannelOption(interp, retVal,
+			    "-translation", "binary");
+		}
 	    }
 	    return retVal;
 	}
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.225
diff -u -r1.225 tclInt.h
--- generic/tclInt.h	22 Apr 2005 15:46:57 -0000	1.225
+++ generic/tclInt.h	27 Apr 2005 18:07:46 -0000
@@ -1889,7 +1889,9 @@
 MODULE_SCOPE int        TclGetNamespaceFromObj _ANSI_ARGS_((
 			    Tcl_Interp *interp, Tcl_Obj *objPtr,
 			    Tcl_Namespace **nsPtrPtr));
-
+MODULE_SCOPE int	TclGetOpenModeEx _ANSI_ARGS_((Tcl_Interp *interp,
+			    CONST char *modeString, int *seekFlagPtr,
+			    int *binaryPtr));
 MODULE_SCOPE Tcl_Obj *	TclGetProcessGlobalValue _ANSI_ARGS_ ((
 			    ProcessGlobalValue *pgvPtr));
 MODULE_SCOPE int	TclGlob _ANSI_ARGS_((Tcl_Interp *interp,
Index: tests/ioCmd.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/ioCmd.test,v
retrieving revision 1.21
diff -u -r1.21 ioCmd.test
--- tests/ioCmd.test	23 Jun 2004 15:36:57 -0000	1.21
+++ tests/ioCmd.test	27 Apr 2005 18:07:48 -0000
@@ -426,11 +426,36 @@
 \"open \$path(test3) \"FOO \\{BAR BAZ\"\""
 test iocmd-12.7 {POSIX open access modes: errors} {
   list [catch {open $path(test3) {FOO BAR BAZ}} msg] $msg
-} {1 {invalid access mode "FOO": must be RDONLY, WRONLY, RDWR, APPEND, CREAT EXCL, NOCTTY, NONBLOCK, or TRUNC}}
+} {1 {invalid access mode "FOO": must be RDONLY, WRONLY, RDWR, APPEND, BINARY, CREAT, EXCL, NOCTTY, NONBLOCK, or TRUNC}}
 test iocmd-12.8 {POSIX open access modes: errors} {
     list [catch {open $path(test3) {TRUNC CREAT}} msg] $msg
 } {1 {access mode must include either RDONLY, WRONLY, or RDWR}}
 close [open $path(test3) w]
+test iocmd-12.9 {POSIX open access modes: BINARY} {
+    list [catch {open $path(test1) BINARY} msg] $msg
+} {1 {access mode must include either RDONLY, WRONLY, or RDWR}}
+test iocmd-12.10 {POSIX open access modes: BINARY} {
+    set f [open $path(test1) {WRONLY BINARY TRUNC}]
+    puts $f a
+    puts $f b
+    puts -nonewline $f c	;# contents are now 5 bytes: a\nb\nc
+    close $f
+    set f [open $path(test1) r]
+    fconfigure $f -translation binary
+    set result [string length [read $f]]
+    close $f
+    set result
+} 5
+test iocmd-12.11 {POSIX open access modes: BINARY} {
+    set f [open $path(test1) {WRONLY BINARY TRUNC}]
+    puts $f \u0248	;# gets truncated to \u0048
+    close $f
+    set f [open $path(test1) r]
+    fconfigure $f -translation binary
+    set result [read -nonewline $f]
+    close $f
+    set result
+} \u0048
 
 test iocmd-13.1 {errors in open command} {
     list [catch {open} msg] $msg
@@ -452,6 +477,15 @@
     regsub [file join {} _non_existent_] $msg "_non_existent_" msg
 	string tolower $msg
 } {1 {couldn't open "_non_existent_": no such file or directory} {posix enoent {no such file or directory}}}
+test iocmd-13.7 {errors in open command} {
+    list [catch {open $path(test1) b} msg] $msg
+} {1 {illegal access mode "b"}}
+test iocmd-13.8 {errors in open command} {
+    list [catch {open $path(test1) rbb} msg] $msg
+} {1 {illegal access mode "rbb"}}
+test iocmd-13.9 {errors in open command} {
+    list [catch {open $path(test1) r++} msg] $msg
+} {1 {illegal access mode "r++"}}
 
 test iocmd-14.1 {file id parsing errors} {
     list [catch {eof gorp} msg] $msg $errorCode
