Index: doc/info.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/info.n,v
retrieving revision 1.8
diff -u -r1.8 info.n
--- doc/info.n	11 Jun 2002 13:22:35 -0000	1.8
+++ doc/info.n	21 Mar 2003 04:47:30 -0000
@@ -75,7 +75,14 @@
 Returns \fB1\fR if the variable named \fIvarName\fR exists in the
 current context (either as a global or local variable) and has been
 defined by being given a value, returns \fB0\fR otherwise.
-.VS 8.4
+.VS 8.5
+.TP
+\fBinfo formalargs \fIprocname\fR
+Returns a list containing the formal argument specifiers to procedure
+\fIprocname\fR, in order.  Each argument specifier is either the argument
+name, or a two-element list containing the argument name and its default 
+value.  \fIProcname\fR must be the name of a Tcl command procedure.  
+.VE
 .TP
 \fBinfo functions \fR?\fIpattern\fR?
 If \fIpattern\fR isn't specified, returns a list of all the math
@@ -83,7 +90,6 @@
 If \fIpattern\fR is specified, only those functions whose name matches
 \fIpattern\fR are returned.  Matching is determined using the same
 rules as for \fBstring match\fR.
-.VE
 .TP
 \fBinfo globals \fR?\fIpattern\fR?
 If \fIpattern\fR isn't specified, returns a list of all the names
Index: generic/tclCmdIL.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclCmdIL.c,v
retrieving revision 1.47
diff -u -r1.47 tclCmdIL.c
--- generic/tclCmdIL.c	27 Feb 2003 16:01:55 -0000	1.47
+++ generic/tclCmdIL.c	21 Mar 2003 04:48:02 -0000
@@ -112,6 +112,9 @@
 static int		InfoFunctionsCmd _ANSI_ARGS_((ClientData dummy,
 			    Tcl_Interp *interp, int objc,
 			    Tcl_Obj *CONST objv[]));
+static int		InfoFormalArgsCmd _ANSI_ARGS_((ClientData dummy,
+			    Tcl_Interp *interp, int objc,
+			    Tcl_Obj *CONST objv[]));
 static int		InfoGlobalsCmd _ANSI_ARGS_((ClientData dummy,
 			    Tcl_Interp *interp, int objc,
 			    Tcl_Obj *CONST objv[]));
@@ -401,17 +404,18 @@
 {
     static CONST char *subCmds[] = {
             "args", "body", "cmdcount", "commands",
-	     "complete", "default", "exists", "functions", "globals",
-	     "hostname", "level", "library", "loaded",
-	     "locals", "nameofexecutable", "patchlevel", "procs",
-	     "script", "sharedlibextension", "tclversion", "vars",
-	     (char *) NULL};
+	    "complete", "default", "exists", "formalargs", "functions",
+	    "globals", "hostname", "level", "library", "loaded",
+	    "locals", "nameofexecutable", "patchlevel", "procs",
+	    "script", "sharedlibextension", "tclversion", "vars",
+	    (char *) NULL};
     enum ISubCmdIdx {
 	    IArgsIdx, IBodyIdx, ICmdCountIdx, ICommandsIdx,
-	    ICompleteIdx, IDefaultIdx, IExistsIdx, IFunctionsIdx, IGlobalsIdx,
-	    IHostnameIdx, ILevelIdx, ILibraryIdx, ILoadedIdx,
-	    ILocalsIdx, INameOfExecutableIdx, IPatchLevelIdx, IProcsIdx,
-	    IScriptIdx, ISharedLibExtensionIdx, ITclVersionIdx, IVarsIdx
+	    ICompleteIdx, IDefaultIdx, IExistsIdx, IFormalArgsIdx,
+	    IFunctionsIdx, IGlobalsIdx, IHostnameIdx, ILevelIdx, ILibraryIdx,
+	    ILoadedIdx, ILocalsIdx, INameOfExecutableIdx, IPatchLevelIdx,
+	    IProcsIdx, IScriptIdx, ISharedLibExtensionIdx, ITclVersionIdx,
+	    IVarsIdx
     };
     int index, result;
 
@@ -451,6 +455,9 @@
 	case IFunctionsIdx:
 	    result = InfoFunctionsCmd(clientData, interp, objc, objv);
 	    break;
+	case IFormalArgsIdx:
+	    result = InfoFormalArgsCmd(clientData, interp, objc, objv);
+	    break;
         case IGlobalsIdx:
 	    result = InfoGlobalsCmd(clientData, interp, objc, objv);
 	    break;
@@ -962,6 +969,78 @@
     } else {
         Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
     }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoFormalArgsCmd --
+ *
+ *      Called to implement the "info formalargs" command that returns the
+ *      formal argument list, including default values, for a procedure.
+ *      Handles the following syntax:
+ *
+ *          info formalargs procName 
+ *
+ * Results:
+ *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
+ *
+ * Side effects:
+ *      Returns a result in the interpreter's result object. If there is
+ *      an error, the result is an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoFormalArgsCmd(dummy, interp, objc, objv)
+    ClientData dummy;		/* Not used. */
+    Tcl_Interp *interp;		/* Current interpreter. */
+    int objc;			/* Number of arguments. */
+    Tcl_Obj *CONST objv[];	/* Argument objects. */
+{
+    Interp *iPtr = (Interp *) interp;
+    char *procName;
+    Proc *procPtr;
+    CompiledLocal *localPtr;
+    Tcl_Obj *listObjPtr, *sublistObjPtr;
+
+    if (objc != 3) {
+        Tcl_WrongNumArgs(interp, 2, objv, "procname");
+        return TCL_ERROR;
+    }
+
+    procName = Tcl_GetString(objv[2]);
+    procPtr = TclFindProc(iPtr, procName);
+    if (procPtr == NULL) {
+        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+                "\"", procName, "\" isn't a procedure", (char *) NULL);
+        return TCL_ERROR;
+    }
+
+    /*
+     * Build a return list containing the arguments, including defaults.
+     */
+
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    for (localPtr = procPtr->firstLocalPtr;  localPtr != NULL;
+            localPtr = localPtr->nextPtr) {
+        if (TclIsVarArgument(localPtr)) {
+            if (localPtr->defValuePtr == NULL) {
+                Tcl_ListObjAppendElement(interp, listObjPtr,
+                        Tcl_NewStringObj(localPtr->name, -1));
+            } else {
+                sublistObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+                Tcl_ListObjAppendElement(interp, sublistObjPtr,
+                        Tcl_NewStringObj(localPtr->name, -1));
+                Tcl_ListObjAppendElement(interp, sublistObjPtr,
+                        Tcl_NewStringObj(Tcl_GetString(localPtr->defValuePtr), -1));
+                Tcl_ListObjAppendElement(interp, listObjPtr, sublistObjPtr);
+            }
+        }
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
     return TCL_OK;
 }
 
Index: tests/info.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/info.test,v
retrieving revision 1.24
diff -u -r1.24 info.test
--- tests/info.test	1 Jul 2002 07:52:03 -0000	1.24
+++ tests/info.test	21 Mar 2003 04:48:11 -0000
@@ -628,16 +628,50 @@
 } {1 {wrong # args: should be "info option ?arg arg ...?"}}
 test info-21.2 {miscellaneous error conditions} {
     list [catch {info gorp} msg] $msg
-} {1 {bad option "gorp": must be args, body, cmdcount, commands, complete, default, exists, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, patchlevel, procs, script, sharedlibextension, tclversion, or vars}}
+} {1 {bad option "gorp": must be args, body, cmdcount, commands, complete, default, exists, formalargs, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, patchlevel, procs, script, sharedlibextension, tclversion, or vars}}
 test info-21.3 {miscellaneous error conditions} {
     list [catch {info c} msg] $msg
-} {1 {ambiguous option "c": must be args, body, cmdcount, commands, complete, default, exists, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, patchlevel, procs, script, sharedlibextension, tclversion, or vars}}
+} {1 {ambiguous option "c": must be args, body, cmdcount, commands, complete, default, exists, formalargs, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, patchlevel, procs, script, sharedlibextension, tclversion, or vars}}
 test info-21.4 {miscellaneous error conditions} {
     list [catch {info l} msg] $msg
-} {1 {ambiguous option "l": must be args, body, cmdcount, commands, complete, default, exists, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, patchlevel, procs, script, sharedlibextension, tclversion, or vars}}
+} {1 {ambiguous option "l": must be args, body, cmdcount, commands, complete, default, exists, formalargs, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, patchlevel, procs, script, sharedlibextension, tclversion, or vars}}
 test info-21.5 {miscellaneous error conditions} {
     list [catch {info s} msg] $msg
-} {1 {ambiguous option "s": must be args, body, cmdcount, commands, complete, default, exists, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, patchlevel, procs, script, sharedlibextension, tclversion, or vars}}
+} {1 {ambiguous option "s": must be args, body, cmdcount, commands, complete, default, exists, formalargs, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, patchlevel, procs, script, sharedlibextension, tclversion, or vars}}
+
+# Tests 22.* : [info formalargs] from TIP 65
+#
+test info-22.1 {info formalargs option} {
+    proc t1 {a bbb c} {return foo}
+    info formalargs t1
+} {a bbb c}
+test info-22.2 {info formalargs option} {
+    proc t1 {{a default1} {bbb default2} {c default3} args} {return foo}
+    info formalargs t1
+} {{a default1} {bbb default2} {c default3} args}
+test info-22.3 {info formalargs option} {
+    proc t1 "" {return foo}
+    info formalargs t1
+} {}
+test info-22.4 {info formalargs option} {
+    catch {rename t1 {}}
+    list [catch {info formalargs t1} msg] $msg
+} {1 {"t1" isn't a procedure}}
+test info-22.5 {info formalargs option} {
+    list [catch {info formalargs set} msg] $msg
+} {1 {"set" isn't a procedure}}
+test info-22.6 {info formalargs option} {
+    proc t1 {a b} {set c 123; set d $c}
+    t1 1 2
+    info formalargs t1
+} {a b}
+test info-22.7 {info formalargs option} {
+    catch {namespace delete test_ns_info2}
+    namespace eval test_ns_info2 {
+        namespace import ::test_ns_info1::*
+        list [info formalargs p] [info formalargs q]
+    }
+} {x {{y 27} {z {}}}}
 
 # cleanup
 catch {namespace delete test_ns_info1 test_ns_info2}
