rpm  5.4.4
rpmio/rpmjs.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Initial Developer of the Original Code is PageMail, Inc.
00015  *
00016  * Portions created by the Initial Developer are 
00017  * Copyright (c) 2007-2009, PageMail, Inc. All Rights Reserved.
00018  *
00019  * Contributor(s): 
00020  * 
00021  * Alternatively, the contents of this file may be used under the terms of
00022  * either of the GNU General Public License Version 2 or later (the "GPL"),
00023  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00024  * in which case the provisions of the GPL or the LGPL are applicable instead
00025  * of those above. If you wish to allow use of your version of this file only
00026  * under the terms of either the GPL or the LGPL, and not to allow others to
00027  * use your version of this file under the terms of the MPL, indicate your
00028  * decision by deleting the provisions above and replace them with the notice
00029  * and other provisions required by the GPL or the LGPL. If you do not delete
00030  * the provisions above, a recipient may use your version of this file under
00031  * the terms of any one of the MPL, the GPL or the LGPL.
00032  *
00033  * ***** END LICENSE BLOCK ***** 
00034  */
00035 
00036 #include "system.h"
00037 
00038 #include "rpmio_internal.h"
00039 #include <argv.h>
00040 #include <popt.h>
00041 
00042 #if defined(__APPLE__)
00043 #include <crt_externs.h>
00044 #else
00045 extern char ** environ;
00046 #endif
00047 
00048 #if defined(WITH_GPSEE)
00049 #define XP_UNIX 1
00050 #include "jsprf.h"
00051 #include "jsapi.h"
00052 
00053 #include <gpsee.h>
00054 typedef gpsee_interpreter_t * JSI_t;
00055 #define _RPMJS_OPTIONS  \
00056     (JSOPTION_STRICT | JSOPTION_RELIMIT | JSOPTION_ANONFUNFIX | JSOPTION_JIT)
00057 #else   /* WITH_GPSEE */
00058 typedef void * JSI_t;
00059 #define _RPMJS_OPTIONS  0
00060 #endif  /* WITH_GPSEE */
00061 
00062 #define _RPMJS_INTERNAL
00063 #include "rpmjs.h"
00064 
00065 #include "debug.h"
00066 
00067 #define F_ISSET(_flags, _FLAG) ((_flags) & RPMJS_FLAGS_##_FLAG)
00068 
00069 /*@unchecked@*/
00070 int _rpmjs_debug = 0;
00071 
00072 /*@unchecked@*/ /*@relnull@*/
00073 rpmjs _rpmjsI = NULL;
00074 
00075 /*@unchecked@*/
00076 uint32_t _rpmjs_options = _RPMJS_OPTIONS;
00077 
00078 /*@unchecked@*/
00079 int _rpmjs_zeal = 2;
00080 
00081 struct rpmjs_s _rpmjs;
00082 
00083 struct poptOption rpmjsIPoptTable[] = {
00084   { "allow", 'a', POPT_BIT_SET,         &_rpmjs.flags, RPMJS_FLAGS_ALLOW,
00085         N_("Allow (read-only) access to caller's environmen"), NULL },
00086   { "nocache", 'C', POPT_BIT_SET,       &_rpmjs.flags, RPMJS_FLAGS_NOCACHE,
00087         N_("Disables compiler caching via JSScript XDR serialization"), NULL },
00088   { "loadrc", 'R', POPT_BIT_SET,        &_rpmjs.flags, RPMJS_FLAGS_LOADRC,
00089         N_("Load RC file for interpreter based on script filename."), NULL },
00090   { "nowarn", 'W', POPT_BIT_SET,        &_rpmjs.flags, RPMJS_FLAGS_NOWARN,
00091         N_("Do not report warnings"), NULL },
00092 
00093   { "norelimit", 'e', POPT_BIT_CLR,     &_rpmjs.flags, RPMJS_FLAGS_RELIMIT,
00094         N_("Do not limit regexps to n^3 levels of backtracking"), NULL },
00095   { "nojit", 'J', POPT_BIT_CLR,         &_rpmjs.flags, RPMJS_FLAGS_JIT,
00096         N_("Disable nanojit"), NULL },
00097   { "nostrict", 'S', POPT_BIT_CLR,      &_rpmjs.flags, RPMJS_FLAGS_STRICT,
00098         N_("Disable Strict mode"), NULL },
00099   { "noutf8", 'U', POPT_BIT_SET,        &_rpmjs.flags, RPMJS_FLAGS_NOUTF8,
00100         N_("Disable UTF-8 C string processing"), NULL },
00101   { "xml", 'x', POPT_BIT_SET,           &_rpmjs.flags, RPMJS_FLAGS_XML,
00102         N_("Parse <!-- comments --> as E4X tokens"), NULL },
00103 
00104   { "anonfunfix", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,   &_rpmjs.flags, RPMJS_FLAGS_ANONFUNFIX,
00105         N_("Parse //@line number [\"filename\"] for XUL"), NULL },
00106   { "atline", 'A', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,        &_rpmjs.flags, RPMJS_FLAGS_ATLINE,
00107         N_("Parse //@line number [\"filename\"] for XUL"), NULL },
00108   { "werror", 'w', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,        &_rpmjs.flags, RPMJS_FLAGS_WERROR,
00109         N_("Convert warnings to errors"), NULL },
00110 
00111   POPT_TABLEEND
00112 };
00113 
00114 static void rpmjsFini(void * _js)
00115         /*@globals fileSystem @*/
00116         /*@modifies *_js, fileSystem @*/
00117 {
00118     rpmjs js = _js;
00119 
00120 if (_rpmjs_debug)
00121 fprintf(stderr, "==> %s(%p) I %p\n", __FUNCTION__, js, js->I);
00122 
00123 #if defined(WITH_GPSEE)
00124 #if defined(XXX_GPSEE_DEBUGGER)
00125     gpsee_finiDebugger(js->jsdc);
00126     js->jsdc = NULL;
00127 #endif
00128 
00129     (void) gpsee_destroyInterpreter(js->I);
00130 #endif
00131     js->I = NULL;
00132 }
00133 
00134 /*@unchecked@*/ /*@only@*/ /*@null@*/
00135 rpmioPool _rpmjsPool;
00136 
00137 static rpmjs rpmjsGetPool(/*@null@*/ rpmioPool pool)
00138         /*@globals _rpmjsPool, fileSystem @*/
00139         /*@modifies pool, _rpmjsPool, fileSystem @*/
00140 {
00141     rpmjs js;
00142 
00143     if (_rpmjsPool == NULL) {
00144         _rpmjsPool = rpmioNewPool("js", sizeof(*js), -1, _rpmjs_debug,
00145                         NULL, NULL, rpmjsFini);
00146         pool = _rpmjsPool;
00147     }
00148     return (rpmjs) rpmioGetPool(pool, sizeof(*js));
00149 }
00150 
00151 static rpmjs rpmjsI(void)
00152         /*@globals _rpmjsI @*/
00153         /*@modifies _rpmjsI @*/
00154 {
00155     if (_rpmjsI == NULL) {
00156 #if defined(WITH_GPSEE)
00157         gpsee_verbosity(0);     /* XXX hack around syslog(3) in GPSEE */
00158 #endif
00159         _rpmjsI = rpmjsNew(NULL, 0);
00160     }
00161 if (_rpmjs_debug)
00162 fprintf(stderr, "<== %s() _rpmjsI %p\n", __FUNCTION__, _rpmjsI);
00163     return _rpmjsI;
00164 }
00165 
00166 /* XXX FIXME: Iargv/Ienviron are now associated with running. */
00167 rpmjs rpmjsNew(char ** av, uint32_t flags)
00168 {
00169     rpmjs js =
00170 #ifdef  NOTYET
00171         (flags & 0x80000000) ? rpmjsI() :
00172 #endif
00173         rpmjsGetPool(_rpmjsPool);
00174     JSI_t I = NULL;
00175 
00176 #if defined(WITH_GPSEE)
00177 
00178 #if defined(XXX_GPSEE_DEBUGGER) /* XXX js->jsdc? */
00179     JSDContext *jsdc;
00180 #endif
00181 
00182     if (flags == 0)
00183         flags = _rpmjs_options;
00184 
00185     if (F_ISSET(flags, NOUTF8) || getenv("GPSEE_NO_UTF8_C_STRINGS")) {
00186         JS_DestroyRuntime(JS_NewRuntime(1024));
00187         putenv((char *) "GPSEE_NO_UTF8_C_STRINGS=1");
00188     }
00189 
00190     /* XXX FIXME: js->Iargv/js->Ienviron for use by rpmjsRunFile() */
00191     I = gpsee_createInterpreter();
00192 #if defined(XXX_GPSEE_DEBUGGER)
00193     js->jsdc = gpsee_initDebugger(I->cx, I->realm, DEBUGGER_JS);
00194 #endif
00195 
00196 #ifdef  NOTYET  /* FIXME: dig out where NOCACHE has moved. */
00197     if (F_ISSET(flags, NOCACHE))
00198         I->useCompilerCache = 0;
00199 #endif
00200     if (F_ISSET(flags, NOWARN)) {
00201         gpsee_runtime_t * grt = JS_GetRuntimePrivate(JS_GetRuntime(I->cx));
00202         grt->errorReport |= er_noWarnings;
00203     }
00204 
00205     JS_SetOptions(I->cx, (flags & 0xffff));
00206 #if defined(JS_GC_ZEAL)
00207     JS_SetGCZeal(I->cx, _rpmjs_zeal);
00208 #endif
00209 #endif  /* WITH_GPSEE */
00210 
00211     js->flags = flags;
00212     js->I = I;
00213 
00214     return rpmjsLink(js);
00215 }
00216 
00217 #if defined(WITH_GPSEE)
00218 static FILE * rpmjsOpenFile(rpmjs js, const char * fn, const char ** msgp)
00219         /*@modifies js @*/
00220 {
00221     FILE * fp = NULL;
00222 
00223     fp = fopen(fn, "r");
00224     if (fp == NULL || ferror(fp)) {
00225         if (fp) {
00226             if (msgp)
00227                 *msgp = xstrdup(strerror(errno));
00228             (void) fclose(fp);
00229             fp = NULL;
00230         } else {
00231             if (msgp)   /* XXX FIXME: add __FUNCTION__ identifier? */
00232                 *msgp = xstrdup("unknown error");
00233         }
00234         goto exit;
00235     }
00236 
00237     gpsee_flock(fileno(fp), GPSEE_LOCK_SH);
00238 
00239     if (F_ISSET(js->flags, SKIPSHEBANG)) {
00240         char buf[BUFSIZ];
00241         
00242         if (fgets(buf, sizeof(buf), fp)) {
00243             if (!(buf[0] == '#' && buf[1] == '!')) {
00244                 /* XXX FIXME: return through *msgp */
00245                 rpmlog(RPMLOG_WARNING, "%s: %s: no \'#!\' on 1st line\n",
00246                         __FUNCTION__, fn);
00247                 rewind(fp);
00248             } else {
00249 #ifdef  NOTYET  /* XXX FIXME */
00250 gpsee_interpreter_t * I = js->I;
00251                 I->linenoOffset += 1;
00252 #endif  /* NOTYET */
00253                 do {    /* consume entire first line, regardless of length */
00254                     if (strchr(buf, '\n'))
00255                         break;
00256                 } while (fgets(buf, sizeof(buf), fp));
00257                 /*
00258                  * Make spidermonkey think the script starts with a blank line,
00259                  * to keep line numbers in sync.
00260                  */
00261                 ungetc('\n', fp);
00262             }
00263         }
00264     }
00265 
00266 exit:
00267 
00268 if (_rpmjs_debug)
00269 fprintf(stderr, "<== %s(%p,%s,%p) fp %p\n", __FUNCTION__, js, fn, msgp, fp);
00270 
00271     return fp;
00272 }
00273 
00274 #ifdef  NOTYET  /* XXX FIXME */
00275 static void processInlineFlags(rpmjs js, FILE * fp, signed int *verbosity_p)
00276 {
00277     char buf[256];
00278     off_t offset;
00279 
00280     offset = ftello(fp);
00281 
00282     while (fgets(buf, sizeof(buf), fp)) {
00283         char *s, *e;
00284 
00285         if ((buf[0] != '/') || (buf[1] != '/'))
00286             break;
00287 
00288         for (s = buf + 2; *s == ' ' || *s == '\t'; s++);
00289         if (strncmp(s, "gpsee:", 6) != 0)
00290             continue;
00291 
00292         for (s = s + 6; *s == ' ' || *s == '\t'; s++);
00293 
00294         for (e = s; *e; e++) {
00295             switch (*e) {
00296             case '\r':
00297             case '\n':
00298             case '\t':
00299             case ' ':
00300                 *e = '\0';
00301                 break;
00302             }
00303         }
00304 
00305         if (s[0])
00306             processFlags(gsr, s, verbosity_p);
00307     }
00308 
00309     fseeko(fp, offset, SEEK_SET);
00310 }
00311 #endif  /* NOTYET */
00312 #endif  /* WITH_GPSEE */
00313 
00314 rpmRC rpmjsRunFile(rpmjs js, const char * fn,
00315                 char *const * Iargv,
00316                 const char ** resultp)
00317 {
00318     rpmRC rc = RPMRC_FAIL;
00319 
00320     if (js == NULL) js = rpmjsI();
00321 
00322     if (fn != NULL) {
00323 #if defined(WITH_GPSEE)
00324         gpsee_interpreter_t * I = js->I;
00325         FILE * fp = rpmjsOpenFile(js, fn, resultp);
00326 
00327         if (fp == NULL) {
00328             /* XXX FIXME: strerror in *reultp */
00329             goto exit;
00330         }
00331 
00332 #ifdef  NOTYET  /* XXX FIXME */
00333         processInlineFlags(js, fp, &verbosity);
00334         gpsee_setVerbosity(verbosity);
00335 #endif
00336 
00337         /* Just compile and exit? */
00338         if (F_ISSET(js->flags, NOEXEC)) {
00339             JSScript *script = NULL;
00340             JSObject *scrobj = NULL;
00341 
00342             if (!gpsee_compileScript(I->cx, fn,
00343                         fp, NULL, &script, I->realm->globalObject, &scrobj))
00344             {
00345                 /* XXX FIXME: isatty(3) */
00346                 gpsee_reportUncaughtException(I->cx, JSVAL_NULL,
00347                         (gpsee_verbosity(0) >= GSR_FORCE_STACK_DUMP_VERBOSITY)
00348                         ||
00349                         ((gpsee_verbosity(0) >= GPSEE_ERROR_OUTPUT_VERBOSITY)
00350                                 && isatty(STDERR_FILENO)));
00351             } else
00352                 rc = RPMRC_OK;
00353         } else {
00354             char *const * Ienviron = NULL;
00355 
00356             if (F_ISSET(js->flags, ALLOW)) {
00357 #if defined(__APPLE__)
00358                 Ienviron = (char *const *) _NSGetEnviron();
00359 #else
00360                 Ienviron = environ;
00361 #endif
00362             }
00363 
00364             if (!gpsee_runProgramModule(I->cx, fn,
00365                         NULL, fp, Iargv, Ienviron))
00366             {
00367                 int code = gpsee_getExceptionExitCode(I->cx);
00368                 if (code >= 0) {
00369                     /* XXX FIXME: format and return code in *resultp. */
00370                     /* XXX hack tp get code into rc -> ec by negating */
00371                     rc = -code;
00372                 } else {
00373                     gpsee_reportUncaughtException(I->cx, JSVAL_NULL,
00374                         (gpsee_verbosity(0) >= GSR_FORCE_STACK_DUMP_VERBOSITY)
00375                         ||
00376                         ((gpsee_verbosity(0) >= GPSEE_ERROR_OUTPUT_VERBOSITY)
00377                                 && isatty(STDERR_FILENO)));
00378                 }
00379             } else
00380                 rc = RPMRC_OK;
00381         }
00382         fclose(fp);
00383         fp = NULL;
00384 #endif  /* WITH_GPSEE */
00385     }
00386 
00387 #if defined(WITH_GPSEE)
00388 exit:
00389 #endif  /* WITH_GPSEE */
00390 
00391 if (_rpmjs_debug)
00392 fprintf(stderr, "<== %s(%p,%s) rc %d\n", __FUNCTION__, js, fn, rc);
00393 
00394     return rc;
00395 }
00396 
00397 rpmRC rpmjsRun(rpmjs js, const char * str, const char ** resultp)
00398 {
00399     rpmRC rc = RPMRC_FAIL;
00400 
00401     if (js == NULL) js = rpmjsI();
00402 
00403     if (str != NULL) {
00404 #if defined(WITH_GPSEE)
00405         gpsee_interpreter_t * I = js->I;
00406         jsval v = JSVAL_VOID;
00407         JSBool ok;
00408 
00409         ok = JS_EvaluateScript(I->cx, I->realm->globalObject, str, strlen(str),
00410                                         __FILE__, __LINE__, &v);
00411         if (ok) {
00412             rc = RPMRC_OK;
00413             if (resultp) {
00414                 JSString *rstr = JS_ValueToString(I->cx, v);
00415                 *resultp = gpsee_getStringBytes(I->cx, rstr);
00416             }
00417         }
00418         v = JSVAL_NULL;
00419 #endif  /* WITH_GPSEE */
00420     }
00421 
00422 if (_rpmjs_debug)
00423 fprintf(stderr, "<== %s(%p,%p[%u]) rc %d\n", __FUNCTION__, js, str, (unsigned)(str ? strlen(str) : 0), rc);
00424 
00425     return rc;
00426 }