Fix bug 435474 - Provide a readline function for xpcshell to read a line from stdin. r/sr=mrbkap,a191=beltzner,p=philipp,dbo
authorPhilipp Kewisch <mozilla@kewis.ch>
Thu, 27 Nov 2008 18:25:35 -0800
changeset 22052 43900a377c77ba10962f1cf7d42374d3983e82a9
parent 22051 a5e50d053bc0f84bfcb89954b619eaa4cb960d5d
child 22053 367be4e8418664ca249ef4100401a746f6f3e3d5
push idunknown
push userunknown
push dateunknown
bugs435474
milestone1.9.1b3pre
Fix bug 435474 - Provide a readline function for xpcshell to read a line from stdin. r/sr=mrbkap,a191=beltzner,p=philipp,dbo
js/src/xpconnect/shell/xpcshell.cpp
--- a/js/src/xpconnect/shell/xpcshell.cpp
+++ b/js/src/xpconnect/shell/xpcshell.cpp
@@ -97,24 +97,56 @@
 
 /***************************************************************************/
 
 #define EXITCODE_RUNTIME_ERROR 3
 #define EXITCODE_FILE_NOT_FOUND 4
 
 FILE *gOutFile = NULL;
 FILE *gErrFile = NULL;
+FILE *gInFile = NULL;
 
 int gExitCode = 0;
 JSBool gQuitting = JS_FALSE;
 static JSBool reportWarnings = JS_TRUE;
 static JSBool compileOnly = JS_FALSE;
 
 JSPrincipals *gJSPrincipals = nsnull;
 
+static JSBool
+GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
+#ifdef EDITLINE
+    /*
+     * Use readline only if file is stdin, because there's no way to specify
+     * another handle.  Are other filehandles interactive?
+     */
+    if (file == stdin) {
+        char *linep = readline(prompt);
+        if (!linep)
+            return JS_FALSE;
+        if (*linep)
+            add_history(linep);
+        strcpy(bufp, linep);
+        JS_free(cx, linep);
+        bufp += strlen(bufp);
+        *bufp++ = '\n';
+        *bufp = '\0';
+    } else
+#endif
+    {
+        char line[256];
+        fprintf(gOutFile, prompt);
+        fflush(gOutFile);
+        if (!fgets(line, sizeof line, file))
+            return JS_FALSE;
+        strcpy(bufp, line);
+    }
+    return JS_TRUE;
+}
+
 static void
 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
 {
     int i, j, k, n;
     char *prefix = NULL, *tmp;
     const char *ctmp;
     JSStackFrame * fp = nsnull;
     nsCOMPtr<nsIXPConnect> xpc;
@@ -200,16 +232,58 @@ my_ErrorReporter(JSContext *cx, const ch
     fputs("^\n", gErrFile);
  out:
     if (!JSREPORT_IS_WARNING(report->flags))
         gExitCode = EXITCODE_RUNTIME_ERROR;
     JS_free(cx, prefix);
 }
 
 static JSBool
+ReadLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+    // While 4096 might be quite arbitrary, this is something to be fixed in
+    // bug 105707. It is also the same limit as in ProcessFile.
+    char buf[4096];
+    JSString *str;
+
+    /* If a prompt was specified, construct the string */
+    if (argc > 0) {
+        str = JS_ValueToString(cx, argv[0]);
+        if (!str)
+            return JS_FALSE;
+        argv[0] = STRING_TO_JSVAL(str);
+    } else {
+        str = JSVAL_TO_STRING(JS_GetEmptyStringValue(cx));
+    }
+
+    /* Get a line from the infile */
+    if (!GetLine(cx, buf, gInFile, JS_GetStringBytes(str)))
+        return JS_FALSE;
+
+    /* Strip newline character added by GetLine() */
+    unsigned int buflen = strlen(buf);
+    if (buflen == 0) {
+        if (feof(gInFile)) {
+            *rval = JSVAL_NULL;
+            return JS_TRUE;
+        }
+    } else if (buf[buflen - 1] == '\n') {
+        --buflen;
+    }
+
+    /* Turn buf into a JSString */
+    str = JS_NewStringCopyN(cx, buf, buflen);
+    if (!str)
+        return JS_FALSE;
+
+    *rval = STRING_TO_JSVAL(str);
+    return JS_TRUE;
+}
+
+static JSBool
 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
     uintN i, n;
     JSString *str;
 
     for (i = n = 0; i < argc; i++) {
         str = JS_ValueToString(cx, argv[i]);
         if (!str)
@@ -441,16 +515,17 @@ Clear(JSContext *cx, JSObject *obj, uint
         JS_ReportError(cx, "'clear' requires an object");
         return JS_FALSE;
     }    
     return JS_TRUE;
 }
 
 static JSFunctionSpec glob_functions[] = {
     {"print",           Print,          0,0,0},
+    {"readline",        ReadLine,       1,0,0},
     {"load",            Load,           1,0,0},
     {"quit",            Quit,           0,0,0},
     {"version",         Version,        1,0,0},
     {"build",           BuildDate,      0,0,0},
     {"dumpXPC",         DumpXPC,        1,0,0},
     {"dump",            Dump,           1,0,0},
     {"gc",              GC,             0,0,0},
     {"clear",           Clear,          1,0,0},
@@ -621,46 +696,16 @@ my_GetErrorMessage(void *userRef, const 
 
 #ifdef EDITLINE
 extern "C" {
 extern char     *readline(const char *prompt);
 extern void     add_history(char *line);
 }
 #endif
 
-static JSBool
-GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
-#ifdef EDITLINE
-    /*
-     * Use readline only if file is stdin, because there's no way to specify
-     * another handle.  Are other filehandles interactive?
-     */
-    if (file == stdin) {
-        char *linep = readline(prompt);
-        if (!linep)
-            return JS_FALSE;
-        if (*linep)
-            add_history(linep);
-        strcpy(bufp, linep);
-        JS_free(cx, linep);
-        bufp += strlen(bufp);
-        *bufp++ = '\n';
-        *bufp = '\0';
-    } else
-#endif
-    {
-        char line[256];
-        fprintf(gOutFile, prompt);
-        fflush(gOutFile);
-        if (!fgets(line, sizeof line, file))
-            return JS_FALSE;
-        strcpy(bufp, line);
-    }
-    return JS_TRUE;
-}
 
 static void
 ProcessFile(JSContext *cx, JSObject *obj, const char *filename, FILE *file,
             JSBool forceTTY)
 {
     JSScript *script;
     jsval result;
     int lineno, startline;
@@ -1363,16 +1408,17 @@ main(int argc, char **argv, char **envp)
     nsresult rv;
 
     // unbuffer stdout so that output is in the correct order; note that stderr
     // is unbuffered by default
     setbuf(stdout, 0);
 
     gErrFile = stderr;
     gOutFile = stdout;
+    gInFile = stdin;
     {
         nsCOMPtr<nsIServiceManager> servMan;
         rv = NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull);
         if (NS_FAILED(rv)) {
             printf("NS_InitXPCOM failed!\n");
             return 1;
         }
         {