Beispiel #1
0
    /* LookupTable::for_each callback */
    static void cb(VMG_ const vm_val_t *key, const vm_val_t *val, void *ctx)
    {
        /* the context is our 'this' pointer */
        metatab_saver *self = (metatab_saver *)ctx;
        CVmFile *fp = self->fp;

        /* both the key and value must be strings for us to save them */
        const char *keystr = key->get_as_string(vmg0_);
        const char *valstr = val->get_as_string(vmg0_);
        if (keystr != 0 && valstr != 0)
        {
            size_t len;
            
            /* save the key string */
            fp->write_uint2(len = vmb_get_len(keystr));
            fp->write_bytes(keystr + VMB_LEN, len);

            /* save the value string */
            fp->write_uint2(len = vmb_get_len(valstr));
            fp->write_bytes(valstr + VMB_LEN, len);

            /* count the pair */
            self->cnt += 1;
        }
    }
Beispiel #2
0
/*
 *   Get the Unicode character code of the first character of a string 
 */
void CVmBifT3Test::get_charcode(VMG_ uint argc)
{
    const char *str;

    /* one argument required */
    check_argc(vmg_ argc, 1);

    /* get the object ID as an integer */
    str = pop_str_val(vmg0_);

    /* 
     *   if the string is empty, return nil; otherwise, return the Unicode
     *   character code of the first character 
     */
    if (vmb_get_len(str) == 0)
    {
        /* empty string - return nil */
        retval_nil(vmg0_);
    }
    else
    {
        /* 
         *   get the character code of the first character and return it
         *   as an integer 
         */
        retval_int(vmg_ (int)utf8_ptr::s_getch(str + VMB_LEN));
    }
}
Beispiel #3
0
/*
 *   find a local variable in our frame by name, given a VM string value 
 */
int CVmObjFrameDesc::find_local(VMG_ const vm_val_t *nval,
                               CVmDbgFrameSymPtr *symp)
{
    /* make sure the name is a string */
    const char *name = nval->get_as_string(vmg0_);
    if (name == 0)
        err_throw(VMERR_BAD_TYPE_BIF);

    /* parse the length */
    size_t namelen = vmb_get_len(name);
    name += VMB_LEN;

    /* look up the symbol */
    return find_local(vmg_ name, namelen, symp);
}
Beispiel #4
0
/* get the storage server URL */
void CVmBifNet::get_storage_url(VMG_ uint oargc)
{
    /* check arguments */
    check_argc(vmg_ oargc, 1);

    /* set a default nil return in case we can't build the path */
    retval_nil(vmg0_);

    /* get the resource name */
    const char *page = G_stk->get(0)->get_as_string(vmg0_);
    if (page == 0)
        err_throw(VMERR_STRING_VAL_REQD);

    /* get the resource name length and buffer pointer */
    size_t pagelen = vmb_get_len(page);
    page += VMB_LEN;

    /* if there's a network configuration, build the resource path */
    const char *host = 0, *rootpath = 0;
    if (G_net_config != 0)
    {
        /* get the storage server host name and root path */
        host = G_net_config->get("storage.domain");
        rootpath = G_net_config->get("storage.rootpath", "/");
    }

    /* we must have a host name to proceed */
    if (host != 0)
    {
        /* build the full string */
        G_interpreter->push_stringf(vmg_ "http://%s%s%.*s",
                                    host, rootpath,
                                    (int)pagelen, page);

        /* pop it into R0 */
        G_stk->pop(G_interpreter->get_r0());
    }

    /* discard arguments */
    G_stk->discard();
}
Beispiel #5
0
/*
 *   Look up a named argument.  If 'mandatory' is set, we throw an error if
 *   we can't find a resolution. 
 */
void CVmBifT3::get_named_arg(VMG_ uint argc)
{
    /* check arguments */
    check_argc_range(vmg_ argc, 1, 2);

    /* get the name we're looking for */
    const char *name = G_stk->get(0)->get_as_string(vmg0_);
    if (name == 0)
        err_throw(VMERR_STRING_VAL_REQD);

    /* get the length and buffer pointer */
    size_t namelen = vmb_get_len(name);
    name += VMB_LEN;

    /* 
     *   Scan the stack for named parameter tables.  A named parameter table
     *   is always in the calling frame at the stack slot just beyond the
     *   last argument.  
     */
    for (vm_val_t *fp = G_interpreter->get_frame_ptr() ; fp != 0 ;
         fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp))
    {
        /* check for a table in this frame */
        vm_val_t *argp;
        const uchar *t = CVmRun::get_named_args_from_frame(vmg_ fp, &argp);
        if (t != 0)
        {
            /* get the number of table entries */
            int n = osrp2(t);
            t += 2;

            /* scan the table for the name */
            for (int i = 0 ; n >= 0 ; --n, i += 2, ++argp)
            {
                /* get this element's offset, and figure its length */
                uint eofs = osrp2(t + i);
                uint elen = osrp2(t + i + 2) - eofs;

                /* check for a match */
                if (elen == namelen && memcmp(name, t + eofs, elen) == 0)
                {
                    /* found it - return the value */
                    retval(vmg_ argp);

                    /* discard arguments and return */
                    G_stk->discard(argc);
                    return;
                }
            }
        }
    }

    /* 
     *   The argument is undefined.  If a default value was supplied, simply
     *   return the default value.  Otherwise throw an error.  
     */
    if (argc >= 2)
    {
        /* a default value was supplied - simply return it */
        retval(vmg_ G_stk->get(1));

        /* discard arguments */
        G_stk->discard(argc);
    }
    else
    {
        /* no default value - throw an error */
        err_throw_a(VMERR_MISSING_NAMED_ARG, 1, ERR_TYPE_TEXTCHAR_LEN,
                    name, namelen);
    }
}
Beispiel #6
0
/*
 *   Get a list of our properties 
 */
void CVmObjClass::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval)
{
    vm_obj_id_t mod_obj;
    vm_meta_entry_t *entry;
    size_t my_prop_cnt;
    size_t mod_prop_cnt;
    vm_val_t mod_val;
    CVmObjList *lst;
    CVmObjList *mod_lst;

    /* presume we won't find any static properties of our own */
    my_prop_cnt = 0;

    /* get my metaclass table entry */
    entry = get_meta_entry(vmg0_);

    /* if we have an entry, count the properties */
    if (entry != 0)
        my_prop_cnt = list_class_props(vmg_ self, entry, 0, 0, FALSE);

    /* if we have a modifier object, get its property list */
    if ((mod_obj = get_mod_obj()) != VM_INVALID_OBJ)
    {
        /* get the modifier's property list - we'll add it to our own */
        vm_objp(vmg_ mod_obj)->build_prop_list(vmg_ self, &mod_val);

        /* get the result as a list object, properly cast */
        mod_lst = (CVmObjList *)vm_objp(vmg_ mod_val.val.obj);

        /*
         *   As an optimization, if we don't have any properties of our own
         *   to add to the modifier list, just return the modifier list
         *   directly (thus avoiding unnecessarily creating another copy of
         *   the list with no changes).
         */
        if (my_prop_cnt == 0)
        {
            /* the modifier list is the entire return value */
            *retval = mod_val;
            return;
        }

        /* get the size of the modifier list */
        mod_prop_cnt = vmb_get_len(mod_lst->get_as_list());
    }
    else
    {
        /* 
         *   we have no modifier object - for the result list, we simply
         *   need our own list, so set the modifier list to nil 
         */
        mod_val.set_nil();
        mod_prop_cnt = 0;
        mod_lst = 0;
    }

    /* for gc protection, push the modifier's list */
    G_stk->push(&mod_val);

    /*
     *   Allocate a list big enough to hold the modifier's list plus our own
     *   list.  
     */
    retval->set_obj(CVmObjList::
                    create(vmg_ FALSE, my_prop_cnt + mod_prop_cnt));

    /* push the return value list for gc protection */
    G_stk->push(retval);

    /* get it as a list object, properly cast */
    lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj);
    lst->cons_clear();

    /* start the list with our own properties */
    if (entry != 0)
        list_class_props(vmg_ self, entry, lst, 0, FALSE);

    /* copy the modifier list into the results, if there is a modifier list */
    if (mod_prop_cnt != 0)
        lst->cons_copy_elements(my_prop_cnt, mod_lst->get_as_list());

    /* done with the gc protection */
    G_stk->discard(2);
}
Beispiel #7
0
/*
 *   Connect to the Web UI
 */
void CVmBifNet::connect_ui(VMG_ uint oargc)
{
    /* check arguments */
    check_argc(vmg_ oargc, 2);

    /* get the server object */
    vm_val_t *srv = G_stk->get(0);
    if (srv->typ != VM_OBJ
        || !CVmObjHTTPServer::is_httpsrv_obj(vmg_ srv->val.obj))
        err_throw(VMERR_BAD_TYPE_BIF);

    /* get the object pointer properly cast */
    CVmObjHTTPServer *srvp = (CVmObjHTTPServer *)vm_objp(vmg_ srv->val.obj);

    /* get the URL path */
    const char *path = G_stk->get(1)->get_as_string(vmg0_);
    if (path == 0)
        err_throw(VMERR_BAD_TYPE_BIF);

    /* make a null-terminated copy of the path */
    char *pathb = lib_copy_str(path + VMB_LEN, vmb_get_len(path));

    /* get the server network address information */
    char *errmsg = 0;
    char *addr = 0, *ip = 0;
    int port;
    int ok = srvp->get_listener_addr(addr, ip, port);

    /* 
     *   If we don't have a network configuration yet, create one.  This
     *   notifies other subsystems that we have an active web UI; for
     *   example, the presence of a network UI disables the regular console
     *   UI, since all UI actions have to go through the web UI once it's
     *   established.
     *   
     *   The interpreter startup creates a network configuration if it
     *   receives command-line information telling it that the game was
     *   launched by a Web server in response to an incoming client request.
     *   When the user launches the game in stand-alone mode directly from
     *   the operating system shell, there's no Web server launch
     *   information, so the startup code doesn't create a net config object.
     *   So, if we get here and find we don't have this object, it means that
     *   we're running in local standalone mode.  
     */
    if (G_net_config == 0)
        G_net_config = new TadsNetConfig();

    /* connect */
    if (ok)
    {
        /* connect */
        ok = osnet_connect_webui(vmg_ addr, port, pathb, &errmsg);
    }
    else
    {
        /* couldn't get the server address */
        errmsg = lib_copy_str(
            "No address information available for HTTPServer");
    }

    /* free strings */
    lib_free_str(pathb);
    lib_free_str(addr);
    lib_free_str(ip);

    /* if there's an error, throw it */
    if (!ok)
    {
        G_interpreter->push_string(vmg_ errmsg);
        lib_free_str(errmsg);
        G_interpreter->throw_new_class(vmg_ G_predef->net_exception, 1,
                                       "Error connecting to Web UI");
    }

    /* no return value */
    retval_nil(vmg0_);
}
Beispiel #8
0
    /* 
     *   Set up the thread.  All string parameters are provided in internal
     *   string format, with VMB_LEN length prefixes.  
     */
    HttpReqThread(VMG_ const vm_val_t *id, const char *url,
                  const char *verb, int32 options,
                  const char *hdrs, vm_val_t *body, const char *body_type,
                  const vm_rcdesc *rc)
    {
        /* save the VM globals */
        vmg = VMGLOB_ADDR;

        /* add a reference on the net event queue */
        if ((queue = G_net_queue) != 0)
            queue->add_ref();

        /* create a global for the ID value */
        idg = G_obj_table->create_global_var();
        idg->val = *id;

        /* save the option flags */
        this->options = options;

        /* set up to parse the URL */
        size_t urll = vmb_get_len(url);
        url += VMB_LEN;

        /* note whether the scheme is plain http or https */
        https = (urll >= 8 && memcmp(url, "https://", 8) == 0);

        /* we don't have any URL pieces yet */
        host = 0;
        resource = 0;
        port = (https ? 443 : 80);

        /* skip the scheme prefix */
        size_t pfx = (urll >= 7 && memcmp(url, "http://", 7) == 0 ? 7 :
                      urll >= 8 && memcmp(url, "https://", 8) == 0 ? 8 : 0);
        url += pfx, urll -= pfx;

        /* the host name is the part up to the ':', '/', or end of string */
        const char *start;
        for (start = url ; urll > 0 && *url != ':' && *url != '/' ;
             ++url, --urll) ;

        /* save the host name */
        host = lib_copy_str(start, url - start);

        /* parse the port, if present */
        if (urll > 0 && *url == ':')
        {
            /* parse the port number string */
            for (--urll, start = ++url, port = 0 ; urll > 0 && *url != '/' ;
                 ++url, --urll)
            {
                if (is_digit(*url))
                    port = port*10 + value_of_digit(*url);
            }
        }

        /* 
         *   The rest (including the '/') is the resource string.  If there's
         *   nothing left, the resource is implicitly '/'. 
         */
        if (urll > 0)
            resource = lib_copy_str(url, urll);
        else
            resource = lib_copy_str("/");

        /* make null-terminated copies of the various strings */
        this->verb = lib_copy_str(verb + VMB_LEN, vmb_get_len(verb));
        this->hdrs = (hdrs == 0 ? 0 :
                      lib_copy_str(hdrs + VMB_LEN, vmb_get_len(hdrs)));

        /* if there's a content body, set up the payload */
        this->body = 0;
        if (body != 0)
        {
            /* set up an empty payload */
            this->body = new OS_HttpPayload();
            
            /* 
             *   Convert the body to a stream payload, if it's a string or
             *   ByteArray.  If it's a LookupTable, it's a set of form
             *   fields.
             */
            if (body->typ == VM_OBJ
                && CVmObjLookupTable::is_lookup_table_obj(vmg_ body->val.obj))
            {
                /* 
                 *   The body is a LookupTable, so we have a set of form
                 *   fields to encode as HTML form data. 
                 */
                
                /* cast the object value */
                CVmObjLookupTable *tab =
                    (CVmObjLookupTable *)vm_objp(vmg_ body->val.obj);
                
                /* add each key in the table as a form field */
                iter_body_table_ctx ctx(this, rc);
                tab->for_each(vmg_ iter_body_table, &ctx);
            }
            else
            {
                /* 
                 *   It's not a lookup table, so it must be bytes to upload,
                 *   as a string or ByteArray.
                 */
                const char *def_mime_type = 0;
                CVmStream *stream = val_to_stream(vmg_ body, &def_mime_type);
                
                /* presume we're going to use the default mime type */
                const char *mime_type = def_mime_type;
                size_t mime_type_len =
                    (mime_type != 0 ? strlen(mime_type) : 0);

                /* if the caller specified a mime type, use it instead */
                if (body_type != 0)
                {
                    mime_type = body_type + VMB_LEN;
                    mime_type_len = vmb_get_len(body_type);
                }
                
                /* add the stream */
                this->body->add(
                    "", 0, "", 0, mime_type, mime_type_len, stream);
            }
        }
    }
Beispiel #9
0
/* add a form field in TADS-string (VMB_LEN prefixed) format */
void OS_HttpPayload::add_tstr(const char *name, const char *val)
{
    add(new OS_HttpPayloadItem(name + VMB_LEN, vmb_get_len(name),
                               val + VMB_LEN, vmb_get_len(val)));
}
Beispiel #10
0
/*
 *   displayText(str) - display a text string.
 */
void CVmBifSample::display_text(VMG_ uint argc)
{
    const char *strp;
    size_t len;

    /*
     *   Check to make sure we have the right number of arguments.  'argc'
     *   tells us how many arguments we received from the T3 program.
     */
    check_argc(vmg_ argc, 1);

    /*
     *   Get the first argument, which is the string to display.
     *
     *   This will give us a pointer to a string in internal format, which
     *   has a two-byte length prefix.  So, once we have the string, we must
     *   get the length from the string pointer, and then skip the length
     *   prefix to get to the real text of the string.
     *
     *   Arguments from the T3 program to a native function always appear on
     *   the VM stack, which we can access using the pop_xxx_val() functions.
     *   The arguments appear on the stack in order such that the first
     *   pop_xxx_val() gives us the first argument, the second pop_xxx_val()
     *   gives us the second argument, and so on.  The pop_xxx_val()
     *   functions REMOVE an argument from the stack - once it's removed, we
     *   can get to the next one.  We MUST remove EXACTLY the number of
     *   arguments that we receive before we return.
     */
    strp = pop_str_val(vmg0_);
    len = vmb_get_len(strp);
    strp += VMB_LEN;

    /*
     *   Okay, we have our string, but it's in UTF-8 format, which is an
     *   encoding format for Unicode.  We don't want to display Unicode; we
     *   want to display the local character set.  How do we do this?
     *   Fortunately, T3 provides a handy character mapping subsystem that
     *   will let us convert the string fairly automatically.  The VM also
     *   gives us a pre-loaded mapper for this specific kind of conversion,
     *   in the object G_cmap_to_ui.  G_cmap_to_ui will map characters from
     *   UTF-8 to the local User Interface character set.
     *
     *   To avoid the need to allocate a gigantic string buffer to convert
     *   the characters, the mapper lets us map in chunks of any size.  So,
     *   we'll simply map and display chunks until we run out of string.
     */
    while (len != 0)
    {
        char buf[128];
        size_t cur_out;
        size_t cur_in;

        /*
         *   Map as much as we can into our buffer.  This will set cur_out to
         *   the number of bytes in the local character set (the output of
         *   the conversion), and will set cur_in to the number of bytes we
         *   used from the Unicode string (the input).
         */
        cur_out = G_cmap_to_ui->map_utf8(buf, sizeof(buf),
                                         strp, len, &cur_in);

        /*
         *   Show the local characters.
         *
         *   "%.*s" is just like "%s", but the ".*" tells printf to show
         *   exactly the number of characters in the int argument before the
         *   string, instead of showing everything until it finds a null byte
         *   in the string.  This is important because map_utf8 does NOT
         *   null-terminate the result.
         */
        printf("%.*s", (int)cur_out, buf);

        /*
         *   skip the characters of input we just translated, so that on the
         *   next iteration of this loop we'll translate the next bunch of
         *   characters
         */
        strp += cur_in;
        len -= cur_in;
    }
}