rpm 5.3.7

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     (void) gpsee_destroyInterpreter(js->I);
00125 #endif
00126     js->I = NULL;
00127 }
00128 
00129 /*@unchecked@*/ /*@only@*/ /*@null@*/
00130 rpmioPool _rpmjsPool;
00131 
00132 static rpmjs rpmjsGetPool(/*@null@*/ rpmioPool pool)
00133         /*@globals _rpmjsPool, fileSystem @*/
00134         /*@modifies pool, _rpmjsPool, fileSystem @*/
00135 {
00136     rpmjs js;
00137 
00138     if (_rpmjsPool == NULL) {
00139         _rpmjsPool = rpmioNewPool("js", sizeof(*js), -1, _rpmjs_debug,
00140                         NULL, NULL, rpmjsFini);
00141         pool = _rpmjsPool;
00142     }
00143     return (rpmjs) rpmioGetPool(pool, sizeof(*js));
00144 }
00145 
00146 static rpmjs rpmjsI(void)
00147         /*@globals _rpmjsI @*/
00148         /*@modifies _rpmjsI @*/
00149 {
00150     if (_rpmjsI == NULL) {
00151 #if defined(WITH_GPSEE)
00152         gpsee_verbosity(0);     /* XXX hack around syslog(3) in GPSEE */
00153 #endif
00154         _rpmjsI = rpmjsNew(NULL, 0);
00155     }
00156 if (_rpmjs_debug)
00157 fprintf(stderr, "<== %s() _rpmjsI %p\n", __FUNCTION__, _rpmjsI);
00158     return _rpmjsI;
00159 }
00160 
00161 /* XXX FIXME: Iargv/Ienviron are now associated with running. */
00162 rpmjs rpmjsNew(char ** av, uint32_t flags)
00163 {
00164     rpmjs js =
00165 #ifdef  NOTYET
00166         (flags & 0x80000000) ? rpmjsI() :
00167 #endif
00168         rpmjsGetPool(_rpmjsPool);
00169     JSI_t I = NULL;
00170 
00171 #if defined(WITH_GPSEE)
00172     if (flags == 0)
00173         flags = _rpmjs_options;
00174 
00175     if (F_ISSET(flags, NOUTF8) || getenv("GPSEE_NO_UTF8_C_STRINGS")) {
00176         JS_DestroyRuntime(JS_NewRuntime(1024));
00177         putenv((char *) "GPSEE_NO_UTF8_C_STRINGS=1");
00178     }
00179 
00180     /* XXX FIXME: js->Iargv/js->Ienviron for use by rpmjsRunFile() */
00181     I = gpsee_createInterpreter();
00182 #ifdef  NOTYET  /* FIXME: dig out where NOCACHE has moved. */
00183     if (F_ISSET(flags, NOCACHE))
00184         I->useCompilerCache = 0;
00185 #endif
00186     if (F_ISSET(flags, NOWARN)) {
00187         gpsee_runtime_t * grt = JS_GetRuntimePrivate(JS_GetRuntime(I->cx));
00188         grt->errorReport |= er_noWarnings;
00189     }
00190 
00191     JS_SetOptions(I->cx, (flags & 0xffff));
00192 #if defined(JS_GC_ZEAL)
00193     JS_SetGCZeal(I->cx, _rpmjs_zeal);
00194 #endif
00195 #endif  /* WITH_GPSEE */
00196 
00197     js->flags = flags;
00198     js->I = I;
00199 
00200     return rpmjsLink(js);
00201 }
00202 
00203 #if defined(WITH_GPSEE)
00204 static FILE * rpmjsOpenFile(rpmjs js, const char * fn, const char ** msgp)
00205         /*@modifies js @*/
00206 {
00207     FILE * fp = NULL;
00208 
00209     fp = fopen(fn, "r");
00210     if (fp == NULL || ferror(fp)) {
00211         if (fp) {
00212             if (msgp)
00213                 *msgp = xstrdup(strerror(errno));
00214             (void) fclose(fp);
00215             fp = NULL;
00216         } else {
00217             if (msgp)   /* XXX FIXME: add __FUNCTION__ identifier? */
00218                 *msgp = xstrdup("unknown error");
00219         }
00220         goto exit;
00221     }
00222 
00223     gpsee_flock(fileno(fp), GPSEE_LOCK_SH);
00224 
00225     if (F_ISSET(js->flags, SKIPSHEBANG)) {
00226         char buf[BUFSIZ];
00227         
00228         if (fgets(buf, sizeof(buf), fp)) {
00229             if (!(buf[0] == '#' && buf[1] == '!')) {
00230                 /* XXX FIXME: return through *msgp */
00231                 rpmlog(RPMLOG_WARNING, "%s: %s: no \'#!\' on 1st line\n",
00232                         __FUNCTION__, fn);
00233                 rewind(fp);
00234             } else {
00235 #ifdef  NOTYET  /* XXX FIXME */
00236 gpsee_interpreter_t * I = js->I;
00237                 I->linenoOffset += 1;
00238 #endif  /* NOTYET */
00239                 do {    /* consume entire first line, regardless of length */
00240                     if (strchr(buf, '\n'))
00241                         break;
00242                 } while (fgets(buf, sizeof(buf), fp));
00243                 /*
00244                  * Make spidermonkey think the script starts with a blank line,
00245                  * to keep line numbers in sync.
00246                  */
00247                 ungetc('\n', fp);
00248             }
00249         }
00250     }
00251 
00252 exit:
00253 
00254 if (_rpmjs_debug)
00255 fprintf(stderr, "<== %s(%p,%s,%p) fp %p\n", __FUNCTION__, js, fn, msgp, fp);
00256 
00257     return fp;
00258 }
00259 
00260 #ifdef  NOTYET  /* XXX FIXME */
00261 static void processInlineFlags(rpmjs js, FILE * fp, signed int *verbosity_p)
00262 {
00263     char buf[256];
00264     off_t offset;
00265 
00266     offset = ftello(fp);
00267 
00268     while (fgets(buf, sizeof(buf), fp)) {
00269         char *s, *e;
00270 
00271         if ((buf[0] != '/') || (buf[1] != '/'))
00272             break;
00273 
00274         for (s = buf + 2; *s == ' ' || *s == '\t'; s++);
00275         if (strncmp(s, "gpsee:", 6) != 0)
00276             continue;
00277 
00278         for (s = s + 6; *s == ' ' || *s == '\t'; s++);
00279 
00280         for (e = s; *e; e++) {
00281             switch (*e) {
00282             case '\r':
00283             case '\n':
00284             case '\t':
00285             case ' ':
00286                 *e = '\0';
00287                 break;
00288             }
00289         }
00290 
00291         if (s[0])
00292             processFlags(gsr, s, verbosity_p);
00293     }
00294 
00295     fseeko(fp, offset, SEEK_SET);
00296 }
00297 #endif  /* NOTYET */
00298 #endif  /* WITH_GPSEE */
00299 
00300 rpmRC rpmjsRunFile(rpmjs js, const char * fn,
00301                 char *const * Iargv,
00302                 const char ** resultp)
00303 {
00304     rpmRC rc = RPMRC_FAIL;
00305 
00306     if (js == NULL) js = rpmjsI();
00307 
00308     if (fn != NULL) {
00309 #if defined(WITH_GPSEE)
00310         gpsee_interpreter_t * I = js->I;
00311         FILE * fp = rpmjsOpenFile(js, fn, resultp);
00312 
00313         if (fp == NULL) {
00314             /* XXX FIXME: strerror in *reultp */
00315             goto exit;
00316         }
00317 
00318 #ifdef  NOTYET  /* XXX FIXME */
00319         processInlineFlags(js, fp, &verbosity);
00320         gpsee_setVerbosity(verbosity);
00321 #endif
00322 
00323         /* Just compile and exit? */
00324         if (F_ISSET(js->flags, NOEXEC)) {
00325             JSScript *script = NULL;
00326             JSObject *scrobj = NULL;
00327 
00328             if (!gpsee_compileScript(I->cx, fn,
00329                         fp, NULL, &script, I->realm->globalObject, &scrobj))
00330             {
00331                 /* XXX FIXME: isatty(3) */
00332                 gpsee_reportUncaughtException(I->cx, JSVAL_NULL,
00333                         (gpsee_verbosity(0) >= GSR_FORCE_STACK_DUMP_VERBOSITY)
00334                         ||
00335                         ((gpsee_verbosity(0) >= GPSEE_ERROR_OUTPUT_VERBOSITY)
00336                                 && isatty(STDERR_FILENO)));
00337             } else
00338                 rc = RPMRC_OK;
00339         } else {
00340             char *const * Ienviron = NULL;
00341 
00342             if (F_ISSET(js->flags, ALLOW)) {
00343 #if defined(__APPLE__)
00344                 Ienviron = (char *const *) _NSGetEnviron();
00345 #else
00346                 Ienviron = environ;
00347 #endif
00348             }
00349 
00350             if (!gpsee_runProgramModule(I->cx, fn,
00351                         NULL, fp, Iargv, Ienviron))
00352             {
00353                 int code = gpsee_getExceptionExitCode(I->cx);
00354                 if (code >= 0) {
00355                     /* XXX FIXME: format and return code in *resultp. */
00356                     /* XXX hack tp get code into rc -> ec by negating */
00357                     rc = -code;
00358                 } else {
00359                     gpsee_reportUncaughtException(I->cx, JSVAL_NULL,
00360                         (gpsee_verbosity(0) >= GSR_FORCE_STACK_DUMP_VERBOSITY)
00361                         ||
00362                         ((gpsee_verbosity(0) >= GPSEE_ERROR_OUTPUT_VERBOSITY)
00363                                 && isatty(STDERR_FILENO)));
00364                 }
00365             } else
00366                 rc = RPMRC_OK;
00367         }
00368         fclose(fp);
00369         fp = NULL;
00370 #endif  /* WITH_GPSEE */
00371     }
00372 
00373 #if defined(WITH_GPSEE)
00374 exit:
00375 #endif  /* WITH_GPSEE */
00376 
00377 if (_rpmjs_debug)
00378 fprintf(stderr, "<== %s(%p,%s) rc %d\n", __FUNCTION__, js, fn, rc);
00379 
00380     return rc;
00381 }
00382 
00383 rpmRC rpmjsRun(rpmjs js, const char * str, const char ** resultp)
00384 {
00385     rpmRC rc = RPMRC_FAIL;
00386 
00387     if (js == NULL) js = rpmjsI();
00388 
00389     if (str != NULL) {
00390 #if defined(WITH_GPSEE)
00391         gpsee_interpreter_t * I = js->I;
00392         jsval v = JSVAL_VOID;
00393         JSBool ok;
00394 
00395         ok = JS_EvaluateScript(I->cx, I->realm->globalObject, str, strlen(str),
00396                                         __FILE__, __LINE__, &v);
00397         if (ok) {
00398             rc = RPMRC_OK;
00399             if (resultp) {
00400                 JSString *rstr = JS_ValueToString(I->cx, v);
00401                 *resultp = JS_GetStringBytes(rstr);
00402             }
00403         }
00404         v = JSVAL_NULL;
00405 #endif  /* WITH_GPSEE */
00406     }
00407 
00408 if (_rpmjs_debug)
00409 fprintf(stderr, "<== %s(%p,%p[%u]) rc %d\n", __FUNCTION__, js, str, (unsigned)(str ? strlen(str) : 0), rc);
00410 
00411     return rc;
00412 }