<commit_before>#include "command.hh"
#include "globals.hh"
#include "eval.hh"
#include "eval-inline.hh"
#include "names.hh"
#include "get-drvs.hh"
#include "common-args.hh"
#include "json.hh"
#include "json-to-value.hh"

#include <regex>
#include <fstream>

using namespace nix;

std::string hilite(const std::string & s, const std::smatch & m)
{
    return
        m.empty()
        ? s
        : std::string(m.prefix())
          + ANSI_RED + std::string(m.str()) + ANSI_NORMAL
          + std::string(m.suffix());
}

struct CmdSearch : SourceExprCommand, MixJSON
{
    std::string re;

    bool writeCache = true;
    bool useCache = true;

    CmdSearch()
    {
        expectArg("regex", &re, true);

        mkFlag()
            .longName("update-cache")
            .shortName('u')
            .description("update the package search cache")
            .handler([&](Strings ss) { writeCache = true; useCache = false; });

        mkFlag()
            .longName("no-cache")
            .description("do not use or update the package search cache")
            .handler([&](Strings ss) { writeCache = false; useCache = false; });
    }

    std::string name() override
    {
        return "search";
    }

    std::string description() override
    {
        return "query available packages";
    }

    void run(ref<Store> store) override
    {
        settings.readOnlyMode = true;

        std::regex regex(re, std::regex::extended | std::regex::icase);

        auto state = getEvalState();

        bool first = true;

        auto jsonOut = json ? std::make_unique<JSONObject>(std::cout, true) : nullptr;

        auto sToplevel = state->symbols.create("_toplevel");
        auto sRecurse = state->symbols.create("recurseForDerivations");

        bool fromCache = false;

        std::function<void(Value *, std::string, bool, JSONObject *)> doExpr;

        doExpr = [&](Value * v, std::string attrPath, bool toplevel, JSONObject * cache) {
            debug("at attribute '%s'", attrPath);

            try {

                state->forceValue(*v);

                if (v->type == tLambda && toplevel) {
                    Value * v2 = state->allocValue();
                    state->autoCallFunction(*state->allocBindings(1), *v, *v2);
                    v = v2;
                    state->forceValue(*v);
                }

                if (state->isDerivation(*v)) {

                    DrvInfo drv(*state, attrPath, v->attrs);

                    DrvName parsed(drv.queryName());

                    std::smatch attrPathMatch;
                    std::regex_search(attrPath, attrPathMatch, regex);

                    auto name = parsed.name;
                    std::smatch nameMatch;
                    std::regex_search(name, nameMatch, regex);

                    std::string description = drv.queryMetaString("description");
                    std::replace(description.begin(), description.end(), '\n', ' ');
                    std::smatch descriptionMatch;
                    std::regex_search(description, descriptionMatch, regex);

                    if (!attrPathMatch.empty()
                        || !nameMatch.empty()
                        || !descriptionMatch.empty())
                    {
                        if (json) {

                            auto jsonElem = jsonOut->object(attrPath);

                            jsonElem.attr("pkgName", parsed.name);
                            jsonElem.attr("version", parsed.version);
                            jsonElem.attr("description", description);

                        } else {
                            if (!first) std::cout << "\n";
                            first = false;

                            std::cout << fmt(
                                "Attribute name: %s\n"
                                "Package name: %s\n"
                                "Version: %s\n"
                                "Description: %s\n",
                                hilite(attrPath, attrPathMatch),
                                hilite(name, nameMatch),
                                parsed.version,
                                hilite(description, descriptionMatch));
                        }
                    }

                    if (cache) {
                        cache->attr("type", "derivation");
                        cache->attr("name", drv.queryName());
                        cache->attr("system", drv.querySystem());
                        if (description != "") {
                            auto meta(cache->object("meta"));
                            meta.attr("description", description);
                        }
                    }
                }

                else if (v->type == tAttrs) {

                    if (!toplevel) {
                        auto attrs = v->attrs;
                        Bindings::iterator j = attrs->find(sRecurse);
                        if (j == attrs->end() || !state->forceBool(*j->value, *j->pos)) {
                            debug("skip attribute '%s'", attrPath);
                            return;
                        }
                    }

                    bool toplevel2 = false;
                    if (!fromCache) {
                        Bindings::iterator j = v->attrs->find(sToplevel);
                        toplevel2 = j != v->attrs->end() && state->forceBool(*j->value, *j->pos);
                    }

                    for (auto & i : *v->attrs) {
                        auto cache2 =
                            cache ? std::make_unique<JSONObject>(cache->object(i.name)) : nullptr;
                        doExpr(i.value,
                            attrPath == "" ? (std::string) i.name : attrPath + "." + (std::string) i.name,
                            toplevel2 || fromCache, cache2 ? cache2.get() : nullptr);
                    }
                }

            } catch (AssertionError & e) {
            } catch (Error & e) {
                if (!toplevel) {
                    e.addPrefix(fmt("While evaluating the attribute '%s':\n", attrPath));
                    throw;
                }
            }
        };

        Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json";

        if (useCache && pathExists(jsonCacheFileName)) {

            warn("using cached results; pass '-u' to update the cache");

            Value vRoot;
            parseJSON(*state, readFile(jsonCacheFileName), vRoot);

            fromCache = true;

            doExpr(&vRoot, "", true, nullptr);
        }

        else {
            Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid());

            std::ofstream jsonCacheFile(tmpFile);

            auto cache = writeCache ? std::make_unique<JSONObject>(jsonCacheFile, false) : nullptr;

            doExpr(getSourceExpr(*state), "", true, cache.get());

            if (rename(tmpFile.c_str(), jsonCacheFileName.c_str()) == -1)
                throw SysError("cannot rename '%s' to '%s'", tmpFile, jsonCacheFileName);
        }
    }
};

static RegisterCommand r1(make_ref<CmdSearch>());
<commit_msg>nix search: Add examples<commit_after>#include "command.hh"
#include "globals.hh"
#include "eval.hh"
#include "eval-inline.hh"
#include "names.hh"
#include "get-drvs.hh"
#include "common-args.hh"
#include "json.hh"
#include "json-to-value.hh"

#include <regex>
#include <fstream>

using namespace nix;

std::string hilite(const std::string & s, const std::smatch & m)
{
    return
        m.empty()
        ? s
        : std::string(m.prefix())
          + ANSI_RED + std::string(m.str()) + ANSI_NORMAL
          + std::string(m.suffix());
}

struct CmdSearch : SourceExprCommand, MixJSON
{
    std::string re;

    bool writeCache = true;
    bool useCache = true;

    CmdSearch()
    {
        expectArg("regex", &re, true);

        mkFlag()
            .longName("update-cache")
            .shortName('u')
            .description("update the package search cache")
            .handler([&](Strings ss) { writeCache = true; useCache = false; });

        mkFlag()
            .longName("no-cache")
            .description("do not use or update the package search cache")
            .handler([&](Strings ss) { writeCache = false; useCache = false; });
    }

    std::string name() override
    {
        return "search";
    }

    std::string description() override
    {
        return "query available packages";
    }

    Examples examples() override
    {
        return {
            Example{
                "To show all available packages:",
                "nix search"
            },
            Example{
                "To show any packages containing 'blender' in its name or description:",
                "nix search blender"
            },
            Example{
                "To search for Firefox and Chromium:",
                "nix search 'firefox|chromium'"
            },
        };
    }

    void run(ref<Store> store) override
    {
        settings.readOnlyMode = true;

        std::regex regex(re, std::regex::extended | std::regex::icase);

        auto state = getEvalState();

        bool first = true;

        auto jsonOut = json ? std::make_unique<JSONObject>(std::cout, true) : nullptr;

        auto sToplevel = state->symbols.create("_toplevel");
        auto sRecurse = state->symbols.create("recurseForDerivations");

        bool fromCache = false;

        std::function<void(Value *, std::string, bool, JSONObject *)> doExpr;

        doExpr = [&](Value * v, std::string attrPath, bool toplevel, JSONObject * cache) {
            debug("at attribute '%s'", attrPath);

            try {

                state->forceValue(*v);

                if (v->type == tLambda && toplevel) {
                    Value * v2 = state->allocValue();
                    state->autoCallFunction(*state->allocBindings(1), *v, *v2);
                    v = v2;
                    state->forceValue(*v);
                }

                if (state->isDerivation(*v)) {

                    DrvInfo drv(*state, attrPath, v->attrs);

                    DrvName parsed(drv.queryName());

                    std::smatch attrPathMatch;
                    std::regex_search(attrPath, attrPathMatch, regex);

                    auto name = parsed.name;
                    std::smatch nameMatch;
                    std::regex_search(name, nameMatch, regex);

                    std::string description = drv.queryMetaString("description");
                    std::replace(description.begin(), description.end(), '\n', ' ');
                    std::smatch descriptionMatch;
                    std::regex_search(description, descriptionMatch, regex);

                    if (!attrPathMatch.empty()
                        || !nameMatch.empty()
                        || !descriptionMatch.empty())
                    {
                        if (json) {

                            auto jsonElem = jsonOut->object(attrPath);

                            jsonElem.attr("pkgName", parsed.name);
                            jsonElem.attr("version", parsed.version);
                            jsonElem.attr("description", description);

                        } else {
                            if (!first) std::cout << "\n";
                            first = false;

                            std::cout << fmt(
                                "Attribute name: %s\n"
                                "Package name: %s\n"
                                "Version: %s\n"
                                "Description: %s\n",
                                hilite(attrPath, attrPathMatch),
                                hilite(name, nameMatch),
                                parsed.version,
                                hilite(description, descriptionMatch));
                        }
                    }

                    if (cache) {
                        cache->attr("type", "derivation");
                        cache->attr("name", drv.queryName());
                        cache->attr("system", drv.querySystem());
                        if (description != "") {
                            auto meta(cache->object("meta"));
                            meta.attr("description", description);
                        }
                    }
                }

                else if (v->type == tAttrs) {

                    if (!toplevel) {
                        auto attrs = v->attrs;
                        Bindings::iterator j = attrs->find(sRecurse);
                        if (j == attrs->end() || !state->forceBool(*j->value, *j->pos)) {
                            debug("skip attribute '%s'", attrPath);
                            return;
                        }
                    }

                    bool toplevel2 = false;
                    if (!fromCache) {
                        Bindings::iterator j = v->attrs->find(sToplevel);
                        toplevel2 = j != v->attrs->end() && state->forceBool(*j->value, *j->pos);
                    }

                    for (auto & i : *v->attrs) {
                        auto cache2 =
                            cache ? std::make_unique<JSONObject>(cache->object(i.name)) : nullptr;
                        doExpr(i.value,
                            attrPath == "" ? (std::string) i.name : attrPath + "." + (std::string) i.name,
                            toplevel2 || fromCache, cache2 ? cache2.get() : nullptr);
                    }
                }

            } catch (AssertionError & e) {
            } catch (Error & e) {
                if (!toplevel) {
                    e.addPrefix(fmt("While evaluating the attribute '%s':\n", attrPath));
                    throw;
                }
            }
        };

        Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json";

        if (useCache && pathExists(jsonCacheFileName)) {

            warn("using cached results; pass '-u' to update the cache");

            Value vRoot;
            parseJSON(*state, readFile(jsonCacheFileName), vRoot);

            fromCache = true;

            doExpr(&vRoot, "", true, nullptr);
        }

        else {
            Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid());

            std::ofstream jsonCacheFile(tmpFile);

            auto cache = writeCache ? std::make_unique<JSONObject>(jsonCacheFile, false) : nullptr;

            doExpr(getSourceExpr(*state), "", true, cache.get());

            if (rename(tmpFile.c_str(), jsonCacheFileName.c_str()) == -1)
                throw SysError("cannot rename '%s' to '%s'", tmpFile, jsonCacheFileName);
        }
    }
};

static RegisterCommand r1(make_ref<CmdSearch>());
<|endoftext|>


<commit_before>#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdexcept>
#include <map>
#include <unistd.h>

#include <xorg/gtest/xorg-gtest.h>
#include <linux/input.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include <stdexcept>
#include <fstream>
#include <xorg/gtest/xorg-gtest.h>

#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include <xorg/wacom-properties.h>
#include <xorg/xserver-properties.h>
#include <unistd.h>

#include "input-driver-test.h"
#include "device-interface.h"
#include "helpers.h"

#define NUM_ELEMS_MATRIX 9
// Intuos5 maxX=44704 maxY=27940 maxZ=2047 resX=200000 resY=200000  tilt=enabled
#define MAX_X 44704
#define MAX_Y 27940

typedef struct Matrix {
    float elem[NUM_ELEMS_MATRIX];
} Matrix;

/**
 * Wacom driver test class. This class takes a struct Tablet that defines
 * which device should be initialised.
 */
class WacomMatrixTest : public InputDriverTest,
                        public DeviceInterface {
public:
    /**
     * Initializes a standard tablet device.
     */
    virtual void SetUp() {
        SetDevice("tablets/Wacom-Intuos5-touch-M-Pen.desc");
        InputDriverTest::SetUp();
    }

    /**
     * Sets up an xorg.conf for a single mouse CorePointer device based on
     * the evemu device.
     */
    virtual void SetUpConfigAndLog(const std::string &param) {
        server.SetOption("-logfile", "/tmp/Xorg-wacom-matrix-test.log");
        server.SetOption("-config", "/tmp/wacom-matrix-test.conf");

        config.AddDefaultScreenWithDriver();
        config.AddInputSection("wacom", "Stylus",
                               "Option \"CorePointer\" \"on\"\n"
                               "Option \"Type\"        \"stylus\"\n"
                               "Option \"Device\"      \"" + dev->GetDeviceNode() + "\"\n");

        config.WriteConfig("/tmp/wacom-matrix-test.conf");
    }
protected:
    int xrandr_event, xrandr_error;
};

TEST_F(WacomMatrixTest, DevicePresent)
{
    SCOPED_TRACE("Test presence of tools as defined in the xorg.conf");

    int ndevices;
    XIDeviceInfo *info;

    info = XIQueryDevice(Display(), XIAllDevices, &ndevices);

    bool found = false;
    while(ndevices--) {
        if (strcmp(info[ndevices].name, "Stylus") == 0) {
            ASSERT_EQ(found, false) << "Duplicate device" << std::endl;
            found = true;
        }
    }
    ASSERT_EQ(found, true);
    XIFreeDeviceInfo(info);
}

bool set_input_matrix (Display *dpy, int deviceid, Matrix *matrix)
{
    Atom prop_float, prop_matrix;

    union {
        unsigned char *c;
        float *f;
    } data;
    int format_return;
    Atom type_return;
    unsigned long nitems;
    unsigned long bytes_after;
    int rc;

    prop_float = XInternAtom(dpy, "FLOAT", False);
    prop_matrix = XInternAtom(dpy, XI_PROP_TRANSFORM, False);

    if (!prop_float)
        return False;

    if (!prop_matrix)
        return False;

    rc = XIGetProperty(dpy, deviceid, prop_matrix, 0, 9, False, prop_float,
                       &type_return, &format_return, &nitems, &bytes_after,
                       &data.c);

    if (rc != Success || prop_float != type_return || format_return != 32 ||
        nitems != NUM_ELEMS_MATRIX || bytes_after != 0)
        return False;

    memcpy(data.f, matrix->elem, sizeof(matrix->elem));

    XIChangeProperty(dpy, deviceid, prop_matrix, prop_float,
                     format_return, PropModeReplace, data.c, nitems);

    XFree(data.c);

    return True;
}

/**
 * compute the input trarnsformation matrix to match the area specified
 * by the given (x,y) width x height.
 */
Bool compute_input_matrix (Display *dpy, int x, int y, int width, int height, Matrix *matrix)
{
    // Reset matrix to no transformation by default
    matrix->elem[0] = 1.0f;
    matrix->elem[1] = 0.0f;
    matrix->elem[2] = 0.0f;
    matrix->elem[3] = 0.0f;
    matrix->elem[4] = 1.0f;
    matrix->elem[5] = 0.0f;
    matrix->elem[6] = 0.0f;
    matrix->elem[7] = 0.0f;
    matrix->elem[8] = 1.0f;

    int screen_x      = 0;
    int screen_y      = 0;
    int screen_width  = WidthOfScreen (DefaultScreenOfDisplay (dpy));
    int screen_height = HeightOfScreen (DefaultScreenOfDisplay (dpy));

    if ((x < screen_x) ||
        (y < screen_y) ||
        (x + width > screen_x + screen_width) ||
        (y + height > screen_y + screen_height))
        return False;

    float x_scale      = (float) x / screen_width;
    float y_scale      = (float) y / screen_height;
    float width_scale  = (float) width / screen_width;
    float height_scale = (float) height / screen_height;

    matrix->elem[0] = width_scale;
    matrix->elem[1] = 0.0f;
    matrix->elem[2] = x_scale;

    matrix->elem[3] = 0.0f;
    matrix->elem[4] = height_scale;
    matrix->elem[5] = y_scale;

    matrix->elem[6] = 0.0f;
    matrix->elem[7] = 0.0f;
    matrix->elem[8] = 1.0f;

    return True;
}

/**
 * Moves the stylus from positon (x, y) by n steps in the
 * direction (code) ABS_X or ABS_Y.
 */
void move_stylus (xorg::testing::evemu::Device *dev,
                 int x, int y, int steps, int code)
{
    int i;

    // Move to device coord (x, y)
    dev->PlayOne(EV_ABS, ABS_X, x, True);
    dev->PlayOne(EV_ABS, ABS_Y, y, True);
    dev->PlayOne(EV_ABS, ABS_DISTANCE, 0, True);
    dev->PlayOne(EV_KEY, BTN_TOOL_PEN, 1, True);

    for (i = 0; i < steps; i++) {
        if (code == ABS_X)
            dev->PlayOne(EV_ABS, ABS_X, x + i, True);
        else
            dev->PlayOne(EV_ABS, ABS_Y, y + i, True);

        dev->PlayOne(EV_ABS, ABS_DISTANCE, i, True);
    }
    dev->PlayOne(EV_KEY, BTN_TOOL_PEN, 0, True);
}

void test_area (Display *dpy, xorg::testing::evemu::Device *dev,
                int deviceid, int x, int y, int width, int height)
{
    XEvent ev;

    Matrix matrix;
    compute_input_matrix (dpy, x, y, width, height, &matrix);
    ASSERT_TRUE (set_input_matrix (dpy, deviceid, &matrix));

    XSync(dpy, False);
    while(XPending(dpy))
        XNextEvent(dpy, &ev);

    // Simulate stylus movement for the entire tablet resolution
    move_stylus (dev, 0, 0, MAX_X, ABS_X);
    move_stylus (dev, MAX_X, 0, MAX_Y, ABS_Y);
    move_stylus (dev, 0, 0, MAX_Y, ABS_Y);
    move_stylus (dev, 0, MAX_Y, MAX_X, ABS_X);

    XSync (dpy, False);
    EXPECT_NE(XPending(dpy), 0) << "No event received??" << std::endl;

    // Capture motion events and check they remain within the mapped area
    XSync (dpy, False);
    while(XCheckMaskEvent (dpy, PointerMotionMask, &ev)) {
        EXPECT_TRUE (ev.xmotion.x_root >= x);
        EXPECT_TRUE (ev.xmotion.x_root < x + width);
        EXPECT_TRUE (ev.xmotion.y_root >= y);
        EXPECT_TRUE (ev.xmotion.y_root < y + height);
    }

    while(XPending(dpy))
        XNextEvent(dpy, &ev);
}

TEST_F(WacomMatrixTest, InputMatrix)
{
    SCOPED_TRACE("Test input transformation matrix");

    /* the server takes a while to start up but the devices may not respond
       to events yet. Add a noop call that just delays everything long
       enough for this test to work */
    XInternAtom(Display(), "foo", True);
    XFlush(Display());

    int major = 2;
    int minor = 0;
    ASSERT_EQ(Success, XIQueryVersion(Display(), &major, &minor));

    EXPECT_TRUE(InitRandRSupport(Display(), &xrandr_event, &xrandr_error));

    int monitor_x, monitor_y, monitor_width, monitor_height;
    int n_monitors = GetNMonitors (Display());

    monitor_x = 0;
    monitor_y = 0;
    monitor_width = WidthOfScreen (DefaultScreenOfDisplay (Display()));
    monitor_height = HeightOfScreen (DefaultScreenOfDisplay (Display()));

    if (n_monitors > 0)
        GetMonitorGeometry (Display(), 0, &monitor_x, &monitor_y, &monitor_width, &monitor_height);

    int deviceid;
    FindInputDeviceByName(Display(), "Stylus", &deviceid);
    ASSERT_NE (deviceid, 0);

    XSelectInput(Display(), DefaultRootWindow(Display()), PointerMotionMask |
                                                          ButtonMotionMask);
    XSync(Display(), False);

    // Check with upper right quarter
    test_area (Display(), dev.get(), deviceid, monitor_x, monitor_y, monitor_width / 2, monitor_height / 2);

    // Check with upper left quarter
    test_area (Display(), dev.get(), deviceid, monitor_x + monitor_width / 2, monitor_y, monitor_width / 2, monitor_height / 2);

    // Check with bottom right quarter
    test_area (Display(), dev.get(), deviceid, monitor_x, monitor_y + monitor_height / 2, monitor_width / 2, monitor_height / 2);

    // Check with bottom left quarter
    test_area (Display(), dev.get(), deviceid, monitor_x + monitor_width / 2, monitor_y + monitor_height / 2, monitor_width / 2, monitor_height / 2);
}
<commit_msg>input/wacom: use EXPECT_GT, etc. instead of EXPECT_TRUE<commit_after>#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdexcept>
#include <map>
#include <unistd.h>

#include <xorg/gtest/xorg-gtest.h>
#include <linux/input.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include <stdexcept>
#include <fstream>
#include <xorg/gtest/xorg-gtest.h>

#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include <xorg/wacom-properties.h>
#include <xorg/xserver-properties.h>
#include <unistd.h>

#include "input-driver-test.h"
#include "device-interface.h"
#include "helpers.h"

#define NUM_ELEMS_MATRIX 9
// Intuos5 maxX=44704 maxY=27940 maxZ=2047 resX=200000 resY=200000  tilt=enabled
#define MAX_X 44704
#define MAX_Y 27940

typedef struct Matrix {
    float elem[NUM_ELEMS_MATRIX];
} Matrix;

/**
 * Wacom driver test class. This class takes a struct Tablet that defines
 * which device should be initialised.
 */
class WacomMatrixTest : public InputDriverTest,
                        public DeviceInterface {
public:
    /**
     * Initializes a standard tablet device.
     */
    virtual void SetUp() {
        SetDevice("tablets/Wacom-Intuos5-touch-M-Pen.desc");
        InputDriverTest::SetUp();
    }

    /**
     * Sets up an xorg.conf for a single mouse CorePointer device based on
     * the evemu device.
     */
    virtual void SetUpConfigAndLog(const std::string &param) {
        server.SetOption("-logfile", "/tmp/Xorg-wacom-matrix-test.log");
        server.SetOption("-config", "/tmp/wacom-matrix-test.conf");

        config.AddDefaultScreenWithDriver();
        config.AddInputSection("wacom", "Stylus",
                               "Option \"CorePointer\" \"on\"\n"
                               "Option \"Type\"        \"stylus\"\n"
                               "Option \"Device\"      \"" + dev->GetDeviceNode() + "\"\n");

        config.WriteConfig("/tmp/wacom-matrix-test.conf");
    }
protected:
    int xrandr_event, xrandr_error;
};

TEST_F(WacomMatrixTest, DevicePresent)
{
    SCOPED_TRACE("Test presence of tools as defined in the xorg.conf");

    int ndevices;
    XIDeviceInfo *info;

    info = XIQueryDevice(Display(), XIAllDevices, &ndevices);

    bool found = false;
    while(ndevices--) {
        if (strcmp(info[ndevices].name, "Stylus") == 0) {
            ASSERT_EQ(found, false) << "Duplicate device" << std::endl;
            found = true;
        }
    }
    ASSERT_EQ(found, true);
    XIFreeDeviceInfo(info);
}

bool set_input_matrix (Display *dpy, int deviceid, Matrix *matrix)
{
    Atom prop_float, prop_matrix;

    union {
        unsigned char *c;
        float *f;
    } data;
    int format_return;
    Atom type_return;
    unsigned long nitems;
    unsigned long bytes_after;
    int rc;

    prop_float = XInternAtom(dpy, "FLOAT", False);
    prop_matrix = XInternAtom(dpy, XI_PROP_TRANSFORM, False);

    if (!prop_float)
        return False;

    if (!prop_matrix)
        return False;

    rc = XIGetProperty(dpy, deviceid, prop_matrix, 0, 9, False, prop_float,
                       &type_return, &format_return, &nitems, &bytes_after,
                       &data.c);

    if (rc != Success || prop_float != type_return || format_return != 32 ||
        nitems != NUM_ELEMS_MATRIX || bytes_after != 0)
        return False;

    memcpy(data.f, matrix->elem, sizeof(matrix->elem));

    XIChangeProperty(dpy, deviceid, prop_matrix, prop_float,
                     format_return, PropModeReplace, data.c, nitems);

    XFree(data.c);

    return True;
}

/**
 * compute the input trarnsformation matrix to match the area specified
 * by the given (x,y) width x height.
 */
Bool compute_input_matrix (Display *dpy, int x, int y, int width, int height, Matrix *matrix)
{
    // Reset matrix to no transformation by default
    matrix->elem[0] = 1.0f;
    matrix->elem[1] = 0.0f;
    matrix->elem[2] = 0.0f;
    matrix->elem[3] = 0.0f;
    matrix->elem[4] = 1.0f;
    matrix->elem[5] = 0.0f;
    matrix->elem[6] = 0.0f;
    matrix->elem[7] = 0.0f;
    matrix->elem[8] = 1.0f;

    int screen_x      = 0;
    int screen_y      = 0;
    int screen_width  = WidthOfScreen (DefaultScreenOfDisplay (dpy));
    int screen_height = HeightOfScreen (DefaultScreenOfDisplay (dpy));

    if ((x < screen_x) ||
        (y < screen_y) ||
        (x + width > screen_x + screen_width) ||
        (y + height > screen_y + screen_height))
        return False;

    float x_scale      = (float) x / screen_width;
    float y_scale      = (float) y / screen_height;
    float width_scale  = (float) width / screen_width;
    float height_scale = (float) height / screen_height;

    matrix->elem[0] = width_scale;
    matrix->elem[1] = 0.0f;
    matrix->elem[2] = x_scale;

    matrix->elem[3] = 0.0f;
    matrix->elem[4] = height_scale;
    matrix->elem[5] = y_scale;

    matrix->elem[6] = 0.0f;
    matrix->elem[7] = 0.0f;
    matrix->elem[8] = 1.0f;

    return True;
}

/**
 * Moves the stylus from positon (x, y) by n steps in the
 * direction (code) ABS_X or ABS_Y.
 */
void move_stylus (xorg::testing::evemu::Device *dev,
                 int x, int y, int steps, int code)
{
    int i;

    // Move to device coord (x, y)
    dev->PlayOne(EV_ABS, ABS_X, x, True);
    dev->PlayOne(EV_ABS, ABS_Y, y, True);
    dev->PlayOne(EV_ABS, ABS_DISTANCE, 0, True);
    dev->PlayOne(EV_KEY, BTN_TOOL_PEN, 1, True);

    for (i = 0; i < steps; i++) {
        if (code == ABS_X)
            dev->PlayOne(EV_ABS, ABS_X, x + i, True);
        else
            dev->PlayOne(EV_ABS, ABS_Y, y + i, True);

        dev->PlayOne(EV_ABS, ABS_DISTANCE, i, True);
    }
    dev->PlayOne(EV_KEY, BTN_TOOL_PEN, 0, True);
}

void test_area (Display *dpy, xorg::testing::evemu::Device *dev,
                int deviceid, int x, int y, int width, int height)
{
    XEvent ev;

    Matrix matrix;
    compute_input_matrix (dpy, x, y, width, height, &matrix);
    ASSERT_TRUE (set_input_matrix (dpy, deviceid, &matrix));

    XSync(dpy, False);
    while(XPending(dpy))
        XNextEvent(dpy, &ev);

    // Simulate stylus movement for the entire tablet resolution
    move_stylus (dev, 0, 0, MAX_X, ABS_X);
    move_stylus (dev, MAX_X, 0, MAX_Y, ABS_Y);
    move_stylus (dev, 0, 0, MAX_Y, ABS_Y);
    move_stylus (dev, 0, MAX_Y, MAX_X, ABS_X);

    XSync (dpy, False);
    EXPECT_NE(XPending(dpy), 0) << "No event received??" << std::endl;

    // Capture motion events and check they remain within the mapped area
    XSync (dpy, False);
    while(XCheckMaskEvent (dpy, PointerMotionMask, &ev)) {
        EXPECT_GE (ev.xmotion.x_root, x);
        EXPECT_LT (ev.xmotion.x_root, x + width);
        EXPECT_GE (ev.xmotion.y_root, y);
        EXPECT_LE (ev.xmotion.y_root, y + height);
    }

    while(XPending(dpy))
        XNextEvent(dpy, &ev);
}

TEST_F(WacomMatrixTest, InputMatrix)
{
    SCOPED_TRACE("Test input transformation matrix");

    /* the server takes a while to start up but the devices may not respond
       to events yet. Add a noop call that just delays everything long
       enough for this test to work */
    XInternAtom(Display(), "foo", True);
    XFlush(Display());

    int major = 2;
    int minor = 0;
    ASSERT_EQ(Success, XIQueryVersion(Display(), &major, &minor));

    EXPECT_TRUE(InitRandRSupport(Display(), &xrandr_event, &xrandr_error));

    int monitor_x, monitor_y, monitor_width, monitor_height;
    int n_monitors = GetNMonitors (Display());

    monitor_x = 0;
    monitor_y = 0;
    monitor_width = WidthOfScreen (DefaultScreenOfDisplay (Display()));
    monitor_height = HeightOfScreen (DefaultScreenOfDisplay (Display()));

    if (n_monitors > 0)
        GetMonitorGeometry (Display(), 0, &monitor_x, &monitor_y, &monitor_width, &monitor_height);

    int deviceid;
    FindInputDeviceByName(Display(), "Stylus", &deviceid);
    ASSERT_NE (deviceid, 0);

    XSelectInput(Display(), DefaultRootWindow(Display()), PointerMotionMask |
                                                          ButtonMotionMask);
    XSync(Display(), False);

    // Check with upper right quarter
    test_area (Display(), dev.get(), deviceid, monitor_x, monitor_y, monitor_width / 2, monitor_height / 2);

    // Check with upper left quarter
    test_area (Display(), dev.get(), deviceid, monitor_x + monitor_width / 2, monitor_y, monitor_width / 2, monitor_height / 2);

    // Check with bottom right quarter
    test_area (Display(), dev.get(), deviceid, monitor_x, monitor_y + monitor_height / 2, monitor_width / 2, monitor_height / 2);

    // Check with bottom left quarter
    test_area (Display(), dev.get(), deviceid, monitor_x + monitor_width / 2, monitor_y + monitor_height / 2, monitor_width / 2, monitor_height / 2);
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * VLC backend for the Phonon library                                        *
 * Copyright (C) 2007-2008 Tanguy Krotoff <tkrotoff@gmail.com>               *
 * Copyright (C) 2008 Lukas Durfina <lukas.durfina@gmail.com>                *
 * Copyright (C) 2009 Fathi Boudra <fabo@kde.org>                            *
 *                                                                           *
 * This program is free software; you can redistribute it and/or             *
 * modify it under the terms of the GNU Lesser General Public                *
 * License as published by the Free Software Foundation; either              *
 * version 3 of the License, or (at your option) any later version.          *
 *                                                                           *
 * This program is distributed in the hope that it will be useful,           *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
 * Lesser General Public License for more details.                           *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public          *
 * License along with this package; if not, write to the Free Software       *
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA *
 *****************************************************************************/

#include "audiooutput.h"
#include "devicemanager.h"
#include "backend.h"

#include "mediaobject.h"
#include "vlcmediaobject.h"

#include "vlcloader.h"

#ifdef PHONON_PULSESUPPORT
#  include <phonon/pulsesupport.h>
#endif

namespace Phonon
{
namespace VLC {

AudioOutput::AudioOutput(Backend *p_back, QObject * p_parent)
        : SinkNode(p_parent),
        f_volume(1.0),
        i_device(0),
        p_backend(p_back)
{
    p_media_object = 0;
}

AudioOutput::~AudioOutput()
{
}

qreal AudioOutput::volume() const
{
    return f_volume;
}

void AudioOutput::setVolume(qreal volume)
{
    if (vlc_current_media_player) {
        libvlc_audio_set_volume(vlc_current_media_player, (int)(f_volume * 100));
        f_volume = volume;
        emit volumeChanged(f_volume);
    }
}

int AudioOutput::outputDevice() const
{
    return i_device;
}

bool AudioOutput::setOutputDevice(int device)
{
    if (i_device == device)
        return true;

#ifdef PHONON_PULSESUPPORT
    if (PulseSupport::getInstance()->isActive()) {
        i_device = device;
        libvlc_audio_output_set(vlc_current_media_player, "pulse");
        qDebug() << "set aout " << "pulse";
        return true;
    }
#endif

    const QList<AudioDevice> deviceList = p_backend->deviceManager()->audioOutputDevices();
    if (device >= 0 && device < deviceList.size()) {

        i_device = device;
        const QByteArray deviceName = deviceList.at(device).vlcId;
        libvlc_audio_output_set(vlc_current_media_player, (char *) deviceList.at(device).vlcId.data());
        qDebug() << "set aout " << deviceList.at(device).vlcId.data();
//         if (deviceName == DEFAULT_ID) {
//             libvlc_audio_device_set(p_vlc_instance, DEFAULT, vlc_exception);
//             vlcExceptionRaised();
//         } else if (deviceName.startsWith(ALSA_ID)) {
//             qDebug() << "setting ALSA " << deviceList.at(device).hwId.data();
//             libvlc_audio_device_set(p_vlc_instance, ALSA, vlc_exception);
//             vlcExceptionRaised();
//             libvlc_audio_alsa_device_set(p_vlc_instance,
//                                          deviceList.at(device).hwId,
//                                          vlc_exception);
//             vlcExceptionRaised();
    }

    return true;
}

#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0))
bool AudioOutput::setOutputDevice(const Phonon::AudioOutputDevice & device)
{
    return true;
}
#endif

}
} // Namespace Phonon::VLC
<commit_msg>Workaround: Do not crash on NULL mediaplayer.<commit_after>/*****************************************************************************
 * VLC backend for the Phonon library                                        *
 * Copyright (C) 2007-2008 Tanguy Krotoff <tkrotoff@gmail.com>               *
 * Copyright (C) 2008 Lukas Durfina <lukas.durfina@gmail.com>                *
 * Copyright (C) 2009 Fathi Boudra <fabo@kde.org>                            *
 *                                                                           *
 * This program is free software; you can redistribute it and/or             *
 * modify it under the terms of the GNU Lesser General Public                *
 * License as published by the Free Software Foundation; either              *
 * version 3 of the License, or (at your option) any later version.          *
 *                                                                           *
 * This program is distributed in the hope that it will be useful,           *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
 * Lesser General Public License for more details.                           *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public          *
 * License along with this package; if not, write to the Free Software       *
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA *
 *****************************************************************************/

#include "audiooutput.h"
#include "devicemanager.h"
#include "backend.h"

#include "mediaobject.h"
#include "vlcmediaobject.h"

#include "vlcloader.h"

#ifdef PHONON_PULSESUPPORT
#  include <phonon/pulsesupport.h>
#endif

namespace Phonon
{
namespace VLC {

AudioOutput::AudioOutput(Backend *p_back, QObject * p_parent)
        : SinkNode(p_parent),
        f_volume(1.0),
        i_device(0),
        p_backend(p_back)
{
    p_media_object = 0;
}

AudioOutput::~AudioOutput()
{
}

qreal AudioOutput::volume() const
{
    return f_volume;
}

void AudioOutput::setVolume(qreal volume)
{
    if (vlc_current_media_player) {
        libvlc_audio_set_volume(vlc_current_media_player, (int)(f_volume * 100));
        f_volume = volume;
        emit volumeChanged(f_volume);
    }
}

int AudioOutput::outputDevice() const
{
    return i_device;
}

bool AudioOutput::setOutputDevice(int device)
{
    if (i_device == device)
        return true;

#ifdef PHONON_PULSESUPPORT
    if (PulseSupport::getInstance()->isActive()) {
        i_device = device;
        libvlc_audio_output_set(vlc_current_media_player, "pulse");
        qDebug() << "set aout " << "pulse";
        return true;
    }
#endif

    const QList<AudioDevice> deviceList = p_backend->deviceManager()->audioOutputDevices();
    if (device >= 0 && device < deviceList.size()) {

        if (!vlc_current_media_player)
            return false;
        i_device = device;
        const QByteArray deviceName = deviceList.at(device).vlcId;
        libvlc_audio_output_set(vlc_current_media_player, (char *) deviceList.at(device).vlcId.data());
        qDebug() << "set aout " << deviceList.at(device).vlcId.data();
//         if (deviceName == DEFAULT_ID) {
//             libvlc_audio_device_set(p_vlc_instance, DEFAULT, vlc_exception);
//             vlcExceptionRaised();
//         } else if (deviceName.startsWith(ALSA_ID)) {
//             qDebug() << "setting ALSA " << deviceList.at(device).hwId.data();
//             libvlc_audio_device_set(p_vlc_instance, ALSA, vlc_exception);
//             vlcExceptionRaised();
//             libvlc_audio_alsa_device_set(p_vlc_instance,
//                                          deviceList.at(device).hwId,
//                                          vlc_exception);
//             vlcExceptionRaised();
    }

    return true;
}

#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0))
bool AudioOutput::setOutputDevice(const Phonon::AudioOutputDevice & device)
{
    return true;
}
#endif

}
} // Namespace Phonon::VLC
<|endoftext|>


<commit_before>#include "config.h"

#include "vm.hpp"
#include "vm/object_utils.hpp"
#include "objectmemory.hpp"
#include "primitives.hpp"

#include "builtin/array.hpp"
#include "builtin/class.hpp"
#include "builtin/exception.hpp"
#include "builtin/integer.hpp"
#include "builtin/string.hpp"
#include "builtin/time.hpp"

#include "ontology.hpp"

#include "util/time.h"

#include <sys/time.h>
#include <time.h>

#include "windows_compat.h"
#include "configuration.hpp"

namespace rubinius {
  void Time::init(STATE) {
    GO(time_class).set(ontology::new_class(state, "Time", G(object)));
    G(time_class)->set_object_type(state, TimeType);
  }

  Time* Time::now(STATE, Object* self) {
    Time* tm = state->new_object<Time>(as<Class>(self));

#ifdef HAVE_CLOCK_GETTIME
    struct timespec ts;

    ::clock_gettime(CLOCK_REALTIME, &ts);

    tm->seconds_ = ts.tv_sec;
    tm->nanoseconds_ = ts.tv_nsec;
#else
    struct timeval tv;

    /* don't fill in the 2nd argument here. getting the timezone here
     * this way is not portable and broken anyway.
     */
    ::gettimeofday(&tv, NULL);

    tm->seconds_ = tv.tv_sec;
    tm->nanoseconds_ = tv.tv_usec * 1000;
#endif

    tm->is_gmt(state, cFalse);

    return tm;
  }

  // Taken from MRI
#define NDIV(x,y) (-(-((x)+1)/(y))-1)
#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)

  Time* Time::specific(STATE, Object* self, Integer* sec, Integer* nsec,
                       Object* gmt, Object* offset)
  {
    Time* tm = state->new_object<Time>(as<Class>(self));

    if(sizeof(time_t) == sizeof(long long)) {
      tm->seconds_ = sec->to_long_long();
      tm->nanoseconds_ = nsec->to_long_long();
    } else {
      tm->seconds_ = sec->to_native();
      tm->nanoseconds_ = nsec->to_native();
    }

    // Do a little overflow cleanup.
    if(tm->nanoseconds_ >= 1000000000) {
      tm->seconds_ += tm->nanoseconds_ / 1000000000;
      tm->nanoseconds_ %= 1000000000;
    }

    if(tm->nanoseconds_ < 0) {
      tm->seconds_ += NDIV(tm->nanoseconds_, 1000000000);
      tm->nanoseconds_ = NMOD(tm->nanoseconds_, 1000000000);
    }

    if(LANGUAGE_18_ENABLED(state)) {
      tm->nanoseconds_ -= (tm->nanoseconds_ % 1000);
    }

    tm->is_gmt(state, CBOOL(gmt) ? cTrue : cFalse);
    tm->offset(state, offset);

    return tm;
  }

  Time* Time::from_array(STATE, Object* self,
                         Fixnum* sec, Fixnum* min, Fixnum* hour,
                         Fixnum* mday, Fixnum* mon, Fixnum* year, Fixnum* nsec,
                         Fixnum* isdst, Object* from_gmt, Object* offset) {
    struct tm tm;

    tm.tm_sec = sec->to_native();
    if(tm.tm_sec < 0 || tm.tm_sec > 60) {
      Exception::argument_error(state, "sec must be in 0..60");
    }

    tm.tm_min = min->to_native();
    if(tm.tm_min < 0 || tm.tm_min > 60) {
      Exception::argument_error(state, "min must be in 0..60");
    }

    tm.tm_hour = hour->to_native();
    if(tm.tm_hour < 0 || tm.tm_hour > 24) {
      Exception::argument_error(state, "hour must be in 0..24");
    }

    tm.tm_mday = mday->to_native();
    if(tm.tm_mday < 1 || tm.tm_mday > 31) {
      Exception::argument_error(state, "mday must be in 1..31");
    }

    tm.tm_mon = mon->to_native() - 1;
    if(tm.tm_mon < 0 || tm.tm_mon > 11) {
      Exception::argument_error(state, "mon must be in 0..11");
    }

    tm.tm_wday = -1;
#ifdef HAVE_TM_GMTOFF
    tm.tm_gmtoff = 0;
#endif
#ifdef HAVE_TM_ZONE
    tm.tm_zone = 0;
#endif
    tm.tm_year = year->to_native() - 1900;

    tm.tm_isdst = isdst->to_native();

    time_t seconds = -1;

    if(CBOOL(from_gmt) || !offset->nil_p()) {
      seconds = ::timegm(&tm);
    } else {
      tzset();
      seconds = ::mktime(&tm);
    }

    int err = 0;

    if(seconds == -1) {
      int utc_p = (CBOOL(from_gmt) || !offset->nil_p()) ? 1 : 0;
      seconds = mktime_extended(&tm, utc_p, &err);
    }

    if(err) Exception::argument_error(state, "time out of range");

    Time* obj = state->new_object<Time>(as<Class>(self));
    obj->seconds_ = seconds;
    obj->nanoseconds_ = nsec->to_native();
    obj->is_gmt(state, CBOOL(from_gmt) ? cTrue : cFalse);

    if(Fixnum* off = try_as<Fixnum>(offset)) {
      obj->seconds_ -= off->to_native();
      obj->offset(state, offset);
    }

    return obj;
  }

  Time* Time::dup(STATE, Object* self, Time* other) {
    Time* tm = state->new_object<Time>(as<Class>(self));
    tm->seconds_ = other->seconds_;
    tm->nanoseconds_ = other->nanoseconds_;
    tm->is_gmt(state, other->is_gmt_);
    return tm;
  }

  struct tm Time::get_tm() {
    time_t seconds = seconds_;
    struct tm tm = {0};

    if(Fixnum* off = try_as<Fixnum>(offset_)) {
      seconds += off->to_native();
      gmtime_r(&seconds, &tm);
    } else if(CBOOL(is_gmt_)) {
      gmtime_r(&seconds, &tm);
    } else {
      tzset();
      localtime_r(&seconds, &tm);
    }

    return tm;
  }

  Object* Time::utc_offset(STATE) {
    if(CBOOL(is_gmt_)) {
      return Fixnum::from(0);
    } else if(!offset_->nil_p()) {
      return offset_;
    }

    native_int off;

#ifdef HAVE_TM_NAME
    struct tm tm = get_tm();
    off = -tm.tm_tzadj;
#else /* !HAVE_TM_NAME */
#ifdef HAVE_TM_ZONE
#ifdef HAVE_TM_GMTOFF
    struct tm tm = get_tm();
    off = tm.tm_gmtoff;
#else
    off = _timezone;
#endif
#else /* !HAVE_TM_ZONE */
#if HAVE_VAR_TIMEZONE
#if HAVE_VAR_ALTZONE
    off = -(daylight ? timezone : altzone);
#else
    off = -timezone;
#endif
#else /* !HAVE_VAR_TIMEZONE */
#ifdef HAVE_GETTIMEOFDAY
    gettimeofday(&tv, &zone);
    off = -zone.tz_minuteswest * 60;
#else
    /* no timezone info, then calc by myself */
    {
      struct tm utc;
      time_t now;
      time(&now);
      utc = *gmtime(&now);
      off = (now - mktime(&utc));
    }
#endif
#endif /* !HAVE_VAR_TIMEZONE */
#endif /* !HAVE_TM_ZONE */
#endif /* !HAVE_TM_NAME */

    return Fixnum::from(off);
  }

  Array* Time::calculate_decompose(STATE) {
    if(!decomposed_->nil_p()) return decomposed_;

    struct tm tm = get_tm();

    /* update Time::TM_FIELDS when changing order of fields */
    Array* ary = Array::create(state, 11);
    ary->set(state, 0, Integer::from(state, tm.tm_sec));
    ary->set(state, 1, Integer::from(state, tm.tm_min));
    ary->set(state, 2, Integer::from(state, tm.tm_hour));
    ary->set(state, 3, Integer::from(state, tm.tm_mday));
    ary->set(state, 4, Integer::from(state, tm.tm_mon + 1));
    ary->set(state, 5, Integer::from(state, tm.tm_year + 1900));
    ary->set(state, 6, Integer::from(state, tm.tm_wday));
    ary->set(state, 7, Integer::from(state, tm.tm_yday + 1));
    ary->set(state, 8, tm.tm_isdst ? cTrue : cFalse);

    const char* tmzone;
    if(offset_->nil_p() && (tmzone = timezone_extended(&tm))) {
      ary->set(state, 9, String::create(state, tmzone));
    } else {
      ary->set(state, 9, cNil);
    }

    // Cache it.
    decomposed(state, ary);
    return ary;
  }

#define STRFTIME_STACK_BUF 128

  String* Time::strftime(STATE, String* format) {
    struct tm tm = get_tm();

    struct timespec ts;
    ts.tv_sec = seconds_;
    ts.tv_nsec = nanoseconds_;

    int off = 0;
    if(Fixnum* offset = try_as<Fixnum>(utc_offset(state))) {
      off = offset->to_int();
    }

    char stack_str[STRFTIME_STACK_BUF];
    char* malloc_str = 0;

    size_t chars = ::strftime_extended(stack_str, STRFTIME_STACK_BUF,
                       format->c_str(state), &tm, &ts, CBOOL(is_gmt_) ? 1 : 0,
                       off);

    size_t buf_size = format->byte_size();

    String* result = 0;

    if (chars == 0 && format->byte_size() > 0) {
      malloc_str = (char*)malloc(buf_size);

      chars = ::strftime_extended(malloc_str, buf_size,
                  format->c_str(state), &tm, &ts, CBOOL(is_gmt_) ? 1 : 0,
                  off);

      while (chars == 0 && format->byte_size() > 0) {
        buf_size *= 2;
        malloc_str = (char*)realloc(malloc_str, buf_size);

        chars = ::strftime_extended(malloc_str, buf_size,
                    format->c_str(state), &tm, &ts, CBOOL(is_gmt_) ? 1 : 0,
                    off);
      }

      result = String::create(state, malloc_str, chars);

      free(malloc_str);
    } else {
      result = String::create(state, stack_str, chars);
    }

    return result;
  }
}
<commit_msg>Double starting size of dynamic buffer in Time#strftime<commit_after>#include "config.h"

#include "vm.hpp"
#include "vm/object_utils.hpp"
#include "objectmemory.hpp"
#include "primitives.hpp"

#include "builtin/array.hpp"
#include "builtin/class.hpp"
#include "builtin/exception.hpp"
#include "builtin/integer.hpp"
#include "builtin/string.hpp"
#include "builtin/time.hpp"

#include "ontology.hpp"

#include "util/time.h"

#include <sys/time.h>
#include <time.h>

#include "windows_compat.h"
#include "configuration.hpp"

namespace rubinius {
  void Time::init(STATE) {
    GO(time_class).set(ontology::new_class(state, "Time", G(object)));
    G(time_class)->set_object_type(state, TimeType);
  }

  Time* Time::now(STATE, Object* self) {
    Time* tm = state->new_object<Time>(as<Class>(self));

#ifdef HAVE_CLOCK_GETTIME
    struct timespec ts;

    ::clock_gettime(CLOCK_REALTIME, &ts);

    tm->seconds_ = ts.tv_sec;
    tm->nanoseconds_ = ts.tv_nsec;
#else
    struct timeval tv;

    /* don't fill in the 2nd argument here. getting the timezone here
     * this way is not portable and broken anyway.
     */
    ::gettimeofday(&tv, NULL);

    tm->seconds_ = tv.tv_sec;
    tm->nanoseconds_ = tv.tv_usec * 1000;
#endif

    tm->is_gmt(state, cFalse);

    return tm;
  }

  // Taken from MRI
#define NDIV(x,y) (-(-((x)+1)/(y))-1)
#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)

  Time* Time::specific(STATE, Object* self, Integer* sec, Integer* nsec,
                       Object* gmt, Object* offset)
  {
    Time* tm = state->new_object<Time>(as<Class>(self));

    if(sizeof(time_t) == sizeof(long long)) {
      tm->seconds_ = sec->to_long_long();
      tm->nanoseconds_ = nsec->to_long_long();
    } else {
      tm->seconds_ = sec->to_native();
      tm->nanoseconds_ = nsec->to_native();
    }

    // Do a little overflow cleanup.
    if(tm->nanoseconds_ >= 1000000000) {
      tm->seconds_ += tm->nanoseconds_ / 1000000000;
      tm->nanoseconds_ %= 1000000000;
    }

    if(tm->nanoseconds_ < 0) {
      tm->seconds_ += NDIV(tm->nanoseconds_, 1000000000);
      tm->nanoseconds_ = NMOD(tm->nanoseconds_, 1000000000);
    }

    if(LANGUAGE_18_ENABLED(state)) {
      tm->nanoseconds_ -= (tm->nanoseconds_ % 1000);
    }

    tm->is_gmt(state, CBOOL(gmt) ? cTrue : cFalse);
    tm->offset(state, offset);

    return tm;
  }

  Time* Time::from_array(STATE, Object* self,
                         Fixnum* sec, Fixnum* min, Fixnum* hour,
                         Fixnum* mday, Fixnum* mon, Fixnum* year, Fixnum* nsec,
                         Fixnum* isdst, Object* from_gmt, Object* offset) {
    struct tm tm;

    tm.tm_sec = sec->to_native();
    if(tm.tm_sec < 0 || tm.tm_sec > 60) {
      Exception::argument_error(state, "sec must be in 0..60");
    }

    tm.tm_min = min->to_native();
    if(tm.tm_min < 0 || tm.tm_min > 60) {
      Exception::argument_error(state, "min must be in 0..60");
    }

    tm.tm_hour = hour->to_native();
    if(tm.tm_hour < 0 || tm.tm_hour > 24) {
      Exception::argument_error(state, "hour must be in 0..24");
    }

    tm.tm_mday = mday->to_native();
    if(tm.tm_mday < 1 || tm.tm_mday > 31) {
      Exception::argument_error(state, "mday must be in 1..31");
    }

    tm.tm_mon = mon->to_native() - 1;
    if(tm.tm_mon < 0 || tm.tm_mon > 11) {
      Exception::argument_error(state, "mon must be in 0..11");
    }

    tm.tm_wday = -1;
#ifdef HAVE_TM_GMTOFF
    tm.tm_gmtoff = 0;
#endif
#ifdef HAVE_TM_ZONE
    tm.tm_zone = 0;
#endif
    tm.tm_year = year->to_native() - 1900;

    tm.tm_isdst = isdst->to_native();

    time_t seconds = -1;

    if(CBOOL(from_gmt) || !offset->nil_p()) {
      seconds = ::timegm(&tm);
    } else {
      tzset();
      seconds = ::mktime(&tm);
    }

    int err = 0;

    if(seconds == -1) {
      int utc_p = (CBOOL(from_gmt) || !offset->nil_p()) ? 1 : 0;
      seconds = mktime_extended(&tm, utc_p, &err);
    }

    if(err) Exception::argument_error(state, "time out of range");

    Time* obj = state->new_object<Time>(as<Class>(self));
    obj->seconds_ = seconds;
    obj->nanoseconds_ = nsec->to_native();
    obj->is_gmt(state, CBOOL(from_gmt) ? cTrue : cFalse);

    if(Fixnum* off = try_as<Fixnum>(offset)) {
      obj->seconds_ -= off->to_native();
      obj->offset(state, offset);
    }

    return obj;
  }

  Time* Time::dup(STATE, Object* self, Time* other) {
    Time* tm = state->new_object<Time>(as<Class>(self));
    tm->seconds_ = other->seconds_;
    tm->nanoseconds_ = other->nanoseconds_;
    tm->is_gmt(state, other->is_gmt_);
    return tm;
  }

  struct tm Time::get_tm() {
    time_t seconds = seconds_;
    struct tm tm = {0};

    if(Fixnum* off = try_as<Fixnum>(offset_)) {
      seconds += off->to_native();
      gmtime_r(&seconds, &tm);
    } else if(CBOOL(is_gmt_)) {
      gmtime_r(&seconds, &tm);
    } else {
      tzset();
      localtime_r(&seconds, &tm);
    }

    return tm;
  }

  Object* Time::utc_offset(STATE) {
    if(CBOOL(is_gmt_)) {
      return Fixnum::from(0);
    } else if(!offset_->nil_p()) {
      return offset_;
    }

    native_int off;

#ifdef HAVE_TM_NAME
    struct tm tm = get_tm();
    off = -tm.tm_tzadj;
#else /* !HAVE_TM_NAME */
#ifdef HAVE_TM_ZONE
#ifdef HAVE_TM_GMTOFF
    struct tm tm = get_tm();
    off = tm.tm_gmtoff;
#else
    off = _timezone;
#endif
#else /* !HAVE_TM_ZONE */
#if HAVE_VAR_TIMEZONE
#if HAVE_VAR_ALTZONE
    off = -(daylight ? timezone : altzone);
#else
    off = -timezone;
#endif
#else /* !HAVE_VAR_TIMEZONE */
#ifdef HAVE_GETTIMEOFDAY
    gettimeofday(&tv, &zone);
    off = -zone.tz_minuteswest * 60;
#else
    /* no timezone info, then calc by myself */
    {
      struct tm utc;
      time_t now;
      time(&now);
      utc = *gmtime(&now);
      off = (now - mktime(&utc));
    }
#endif
#endif /* !HAVE_VAR_TIMEZONE */
#endif /* !HAVE_TM_ZONE */
#endif /* !HAVE_TM_NAME */

    return Fixnum::from(off);
  }

  Array* Time::calculate_decompose(STATE) {
    if(!decomposed_->nil_p()) return decomposed_;

    struct tm tm = get_tm();

    /* update Time::TM_FIELDS when changing order of fields */
    Array* ary = Array::create(state, 11);
    ary->set(state, 0, Integer::from(state, tm.tm_sec));
    ary->set(state, 1, Integer::from(state, tm.tm_min));
    ary->set(state, 2, Integer::from(state, tm.tm_hour));
    ary->set(state, 3, Integer::from(state, tm.tm_mday));
    ary->set(state, 4, Integer::from(state, tm.tm_mon + 1));
    ary->set(state, 5, Integer::from(state, tm.tm_year + 1900));
    ary->set(state, 6, Integer::from(state, tm.tm_wday));
    ary->set(state, 7, Integer::from(state, tm.tm_yday + 1));
    ary->set(state, 8, tm.tm_isdst ? cTrue : cFalse);

    const char* tmzone;
    if(offset_->nil_p() && (tmzone = timezone_extended(&tm))) {
      ary->set(state, 9, String::create(state, tmzone));
    } else {
      ary->set(state, 9, cNil);
    }

    // Cache it.
    decomposed(state, ary);
    return ary;
  }

#define STRFTIME_STACK_BUF 128

  String* Time::strftime(STATE, String* format) {
    struct tm tm = get_tm();

    struct timespec ts;
    ts.tv_sec = seconds_;
    ts.tv_nsec = nanoseconds_;

    int off = 0;
    if(Fixnum* offset = try_as<Fixnum>(utc_offset(state))) {
      off = offset->to_int();
    }

    char stack_str[STRFTIME_STACK_BUF];
    char* malloc_str = 0;

    size_t chars = ::strftime_extended(stack_str, STRFTIME_STACK_BUF,
                       format->c_str(state), &tm, &ts, CBOOL(is_gmt_) ? 1 : 0,
                       off);

    size_t buf_size = format->byte_size() * 2;

    String* result = 0;

    if (chars == 0 && format->byte_size() > 0) {
      malloc_str = (char*)malloc(buf_size);

      chars = ::strftime_extended(malloc_str, buf_size,
                  format->c_str(state), &tm, &ts, CBOOL(is_gmt_) ? 1 : 0,
                  off);

      while (chars == 0 && format->byte_size() > 0) {
        buf_size *= 2;
        malloc_str = (char*)realloc(malloc_str, buf_size);

        chars = ::strftime_extended(malloc_str, buf_size,
                    format->c_str(state), &tm, &ts, CBOOL(is_gmt_) ? 1 : 0,
                    off);
      }

      result = String::create(state, malloc_str, chars);

      free(malloc_str);
    } else {
      result = String::create(state, stack_str, chars);
    }

    return result;
  }
}
<|endoftext|>


<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2004-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "vmbase.h"

namespace vmbase {

    AtomicCounter32 VMThread::m_nextNameSuffix(0);
    VMThreadLocal<VMThread*> VMThread::m_currentThread;

    Runnable::~Runnable()
    {
    }

    WaitNotifyMonitor::WaitNotifyMonitor()
    {
    }

    WaitNotifyMonitor::~WaitNotifyMonitor()
    {
    }

    void WaitNotifyMonitor::wait()
    {
        m_condVar.wait(*this);
    }

    bool WaitNotifyMonitor::wait(int32_t timeoutMillis)
    {
        return m_condVar.wait(*this, timeoutMillis);
    }

    void VMThread::setNameFrom(const char* name)
    {
        size_t nameLength = VMPI_strlen(name);
        char* buf = new char[nameLength + 1];
        VMPI_strncpy(buf, name, nameLength + 1);
        m_name = buf;
    }

    VMThread::VMThread()
        : m_state(NOT_STARTED)
    {
        m_runnable = this;
        int suffix = m_nextNameSuffix.incAndGet();
        char buf[64];
        VMPI_snprintf(buf, sizeof(buf), "Thread-%d", suffix);
        setNameFrom(buf);
    }

    VMThread::VMThread(const char* name)
        : m_state(NOT_STARTED)
        , m_joinerQty(0)
    {
        m_runnable = this;
        setNameFrom(name);
    }

    VMThread::VMThread(Runnable* runnable)
        : m_runnable(runnable)
        , m_state(NOT_STARTED)
        , m_joinerQty(0)
    {
        int suffix = m_nextNameSuffix.incAndGet();
        char buf[64];
        VMPI_snprintf(buf, sizeof(buf), "Thread-%d", suffix);
        setNameFrom(buf);
    }

    VMThread::VMThread(const char* name, Runnable* runnable)
        : m_runnable(runnable)
        , m_state(NOT_STARTED)
        , m_joinerQty(0)
    {
        setNameFrom(name);
    }

    VMThread::~VMThread()
    {
        // Before a VMThread is freed the freeing thread must ensure
        // that the VMThread has notified all of its joiners, and that
        // all of the joiners have released the VMThread's
        // m_joinMonitor after being notified. To accomplish this, just
        // having the freeing thread join the VMThread isn't enough,
        // the VMThread dtor must explicitly wait for the joiners to
        // release the mutex. Note that the VMThread's m_status cannot
        // be used as a wait-guard as the freeing thread may see the
        // update to TERMINATED before one of the joiners. So we have
        // to count joining threads in and out, and have the VMThread
        // dtor wait until there are zero joiners.
        SCOPE_LOCK_NAMED(locker, m_joinMonitor) {
            if (m_state != NOT_STARTED) {
                while (m_state == RUNNABLE || m_joinerQty != 0) {
                    locker.wait();
                }
            }
        }
        delete [] m_name;
    }

    bool VMThread::start()
    {
        return start(NULL);
    }

    bool VMThread::start(ThreadPriority priority)
    {
        vmpi_thread_attr_t attr;
        VMPI_threadAttrInit(&attr);
        if (priority == HIGH_PRIORITY) {
            VMPI_threadAttrSetPriorityHigh(&attr);
        } else if (priority == LOW_PRIORITY) {
            VMPI_threadAttrSetPriorityLow(&attr);
        }
        return start(&attr);
    }

    bool VMThread::start(ThreadPriority priority, size_t stackSize, size_t guardSize)
    {
        vmpi_thread_attr_t attr;
        VMPI_threadAttrInit(&attr);
        if (priority == HIGH_PRIORITY) {
            VMPI_threadAttrSetPriorityHigh(&attr);
        } else if (priority == LOW_PRIORITY) {
            VMPI_threadAttrSetPriorityLow(&attr);
        }
        if (!VMPI_threadAttrSetStackSize(&attr, stackSize)) {
            return false;
        }
        if (!VMPI_threadAttrSetGuardSize(&attr, guardSize)) {
            return false;
        }
        return start(&attr);
    }

    bool VMThread::start(vmpi_thread_attr_t* attr)
    {
        SCOPE_LOCK(m_joinMonitor) {
            AvmAssert(m_state == NOT_STARTED);
            if (m_state == NOT_STARTED) {
                if (VMPI_threadCreate(&m_threadID, attr, VMThread::startInternal, (vmpi_thread_arg_t) (this))) {
                    m_state = RUNNABLE;
                    return true;
                }
            }
        }
        return false;
    }

    void VMThread::join()
    {
        SCOPE_LOCK_NAMED(locker, m_joinMonitor) {
            AvmAssert(m_state != NOT_STARTED);
            if (m_state != TERMINATED) {
                m_joinerQty++;
                do {
                    locker.wait();
                } while (m_state != TERMINATED);
                m_joinerQty--;
            }
        }
    }

    /* static */ vmpi_thread_rtn_t VMPI_THREAD_START_CC VMThread::startInternal(vmpi_thread_arg_t args)
    {
        VMThread* thread = (VMThread*) (args);
        VMThread::m_currentThread.set(thread);

        // Block until we know our parent thread has finished its side of the setup
        SCOPE_LOCK(thread->m_joinMonitor) {
            AvmAssert(thread->m_state == RUNNABLE);
        }

        thread->m_runnable->run();

        // VMThread has logically terminated. Cleanup and notify all joiners.
        SCOPE_LOCK_NAMED(locker, thread->m_joinMonitor) {
            VMPI_threadDetach(thread->m_threadID);
            thread->m_state = TERMINATED;
            locker.notifyAll();
        }
        return 0;
    }

    const char* VMThread::getName() const
    {
        return m_name;
    }

    void VMThread::run()
    {
        // Override me.
    }

    /* static */ void VMThread::sleep(int32_t timeout)
    {
        VMPI_threadSleep(timeout);
    }

}
<commit_msg>Bug 634810 - Zero parameter VMThread constructor does not initialize m_joinerQty (r+swilkin)<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2004-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "vmbase.h"

namespace vmbase {

    AtomicCounter32 VMThread::m_nextNameSuffix(0);
    VMThreadLocal<VMThread*> VMThread::m_currentThread;

    Runnable::~Runnable()
    {
    }

    WaitNotifyMonitor::WaitNotifyMonitor()
    {
    }

    WaitNotifyMonitor::~WaitNotifyMonitor()
    {
    }

    void WaitNotifyMonitor::wait()
    {
        m_condVar.wait(*this);
    }

    bool WaitNotifyMonitor::wait(int32_t timeoutMillis)
    {
        return m_condVar.wait(*this, timeoutMillis);
    }

    void VMThread::setNameFrom(const char* name)
    {
        size_t nameLength = VMPI_strlen(name);
        char* buf = new char[nameLength + 1];
        VMPI_strncpy(buf, name, nameLength + 1);
        m_name = buf;
    }

    VMThread::VMThread()
        : m_state(NOT_STARTED)
        , m_joinerQty(0)
    {
        m_runnable = this;
        int suffix = m_nextNameSuffix.incAndGet();
        char buf[64];
        VMPI_snprintf(buf, sizeof(buf), "Thread-%d", suffix);
        setNameFrom(buf);
    }

    VMThread::VMThread(const char* name)
        : m_state(NOT_STARTED)
        , m_joinerQty(0)
    {
        m_runnable = this;
        setNameFrom(name);
    }

    VMThread::VMThread(Runnable* runnable)
        : m_runnable(runnable)
        , m_state(NOT_STARTED)
        , m_joinerQty(0)
    {
        int suffix = m_nextNameSuffix.incAndGet();
        char buf[64];
        VMPI_snprintf(buf, sizeof(buf), "Thread-%d", suffix);
        setNameFrom(buf);
    }

    VMThread::VMThread(const char* name, Runnable* runnable)
        : m_runnable(runnable)
        , m_state(NOT_STARTED)
        , m_joinerQty(0)
    {
        setNameFrom(name);
    }

    VMThread::~VMThread()
    {
        // Before a VMThread is freed the freeing thread must ensure
        // that the VMThread has notified all of its joiners, and that
        // all of the joiners have released the VMThread's
        // m_joinMonitor after being notified. To accomplish this, just
        // having the freeing thread join the VMThread isn't enough,
        // the VMThread dtor must explicitly wait for the joiners to
        // release the mutex. Note that the VMThread's m_status cannot
        // be used as a wait-guard as the freeing thread may see the
        // update to TERMINATED before one of the joiners. So we have
        // to count joining threads in and out, and have the VMThread
        // dtor wait until there are zero joiners.
        SCOPE_LOCK_NAMED(locker, m_joinMonitor) {
            if (m_state != NOT_STARTED) {
                while (m_state == RUNNABLE || m_joinerQty != 0) {
                    locker.wait();
                }
            }
        }
        delete [] m_name;
    }

    bool VMThread::start()
    {
        return start(NULL);
    }

    bool VMThread::start(ThreadPriority priority)
    {
        vmpi_thread_attr_t attr;
        VMPI_threadAttrInit(&attr);
        if (priority == HIGH_PRIORITY) {
            VMPI_threadAttrSetPriorityHigh(&attr);
        } else if (priority == LOW_PRIORITY) {
            VMPI_threadAttrSetPriorityLow(&attr);
        }
        return start(&attr);
    }

    bool VMThread::start(ThreadPriority priority, size_t stackSize, size_t guardSize)
    {
        vmpi_thread_attr_t attr;
        VMPI_threadAttrInit(&attr);
        if (priority == HIGH_PRIORITY) {
            VMPI_threadAttrSetPriorityHigh(&attr);
        } else if (priority == LOW_PRIORITY) {
            VMPI_threadAttrSetPriorityLow(&attr);
        }
        if (!VMPI_threadAttrSetStackSize(&attr, stackSize)) {
            return false;
        }
        if (!VMPI_threadAttrSetGuardSize(&attr, guardSize)) {
            return false;
        }
        return start(&attr);
    }

    bool VMThread::start(vmpi_thread_attr_t* attr)
    {
        SCOPE_LOCK(m_joinMonitor) {
            AvmAssert(m_state == NOT_STARTED);
            if (m_state == NOT_STARTED) {
                if (VMPI_threadCreate(&m_threadID, attr, VMThread::startInternal, (vmpi_thread_arg_t) (this))) {
                    m_state = RUNNABLE;
                    return true;
                }
            }
        }
        return false;
    }

    void VMThread::join()
    {
        SCOPE_LOCK_NAMED(locker, m_joinMonitor) {
            AvmAssert(m_state != NOT_STARTED);
            if (m_state != TERMINATED) {
                m_joinerQty++;
                do {
                    locker.wait();
                } while (m_state != TERMINATED);
                m_joinerQty--;
            }
        }
    }

    /* static */ vmpi_thread_rtn_t VMPI_THREAD_START_CC VMThread::startInternal(vmpi_thread_arg_t args)
    {
        VMThread* thread = (VMThread*) (args);
        VMThread::m_currentThread.set(thread);

        // Block until we know our parent thread has finished its side of the setup
        SCOPE_LOCK(thread->m_joinMonitor) {
            AvmAssert(thread->m_state == RUNNABLE);
        }

        thread->m_runnable->run();

        // VMThread has logically terminated. Cleanup and notify all joiners.
        SCOPE_LOCK_NAMED(locker, thread->m_joinMonitor) {
            VMPI_threadDetach(thread->m_threadID);
            thread->m_state = TERMINATED;
            locker.notifyAll();
        }
        return 0;
    }

    const char* VMThread::getName() const
    {
        return m_name;
    }

    void VMThread::run()
    {
        // Override me.
    }

    /* static */ void VMThread::sleep(int32_t timeout)
    {
        VMPI_threadSleep(timeout);
    }

}
<|endoftext|>


<commit_before>/*
* Daemon.node: A node.JS addon that allows creating Unix/Linux Daemons in pure Javascript.
 *
* Copyright 2010 (c) <arthur@norgic.com>
* Modified By: Pedro Teixeira  2010
* Modified By: James Haliday   2010
* Modified By: Charlie Robbins 2010
* Modified By: Zak Taylor      2010
* Modified By: Daniel Bartlett 2011
* Modified By: Charlie Robbins 2011
* Brought to its final resting place by: Charlie McConnell 2012
*
* Under MIT License. See LICENSE file.
*
*/

#include <v8.h>
#include <node.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <pwd.h>

#define PID_MAXLEN 10

using namespace v8;
using namespace node;

const char* ToCString(const v8::String::Utf8Value& value) {
  return *value ? *value : "<string conversion failed>";
}

//
// Go through special routines to become a daemon.
// if successful, returns daemon pid
//
static Handle<Value> Start(const Arguments& args) {
  HandleScope scope;
  pid_t sid, pid;
  int new_fd = -1, new_fd_stderr, length;

  pid = fork();
  if (pid < 0) {
    return ThrowException(ErrnoException(errno, "fork()"));
  }
  else if (pid > 0) exit(0);

  if (pid == 0) {
    // Child process:

    sid = setsid();
    if(sid < 0) {
      return ThrowException(ErrnoException(errno, "setsid()"));
    }

    // Close stdin
    freopen("/dev/null", "r", stdin);

    length = args.Length();

    //
    // Attempt to set STDOUT_FILENO if we have been
    // passed an argument for it, otherwise point
    // to /dev/null
    //
    if (length > 0 && args[0]->IsString()) {
      String::Utf8Value outfile(args[0]->ToString());
      new_fd = open(ToCString(outfile), O_WRONLY | O_APPEND | O_CREAT,
                S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP);
      if (new_fd < 0) {
        return ThrowException(ErrnoException(errno, "open(), stdout"));
      }
      if (dup2(new_fd, STDOUT_FILENO) < 0) {
        return ThrowException(ErrnoException(errno, "dup2(), stdout"));
      }
    }
    else {
      freopen("/dev/null", "w", stdout);
    }

    //
    // Get the STDERR fd if it has been passed
    // as an argument
    //
    if (length > 1 && args[1]->IsString()) {
      String::Utf8Value errfile(args[1]->ToString());
      new_fd_stderr = open(ToCString(errfile), O_WRONLY | O_APPEND | O_CREAT,
                            S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP);
      if (new_fd_stderr < 0) {
        return ThrowException(ErrnoException(errno, "open(), stderr"));
      }
    }
    else {
      new_fd_stderr = new_fd;
    }

    //
    // Attempt to set STDERR_FILENO if we have
    // a valid file descriptor, otherwise point
    // to /dev/null
    //
    if (new_fd_stderr != -1) {
      if (dup2(new_fd_stderr, STDERR_FILENO) < 0) {
        return ThrowException(ErrnoException(errno, "dup2(), stderr"));
      }
    }
    else {
      freopen("/dev/null", "w", stderr);
    }
  }

  return scope.Close(Integer::New(getpid()));
}

//
// Close stdin by redirecting it to /dev/null
//
Handle<Value> CloseStdin(const Arguments& args) {
  freopen("/dev/null", "r", stdin);
}

//
// Close stderr by redirecting to /dev/null
//
Handle<Value> CloseStderr(const Arguments& args) {
  freopen("/dev/null", "w", stderr);
}

//
// Close stdout by redirecting to /dev/null
//
Handle<Value> CloseStdout(const Arguments& args) {
  freopen("/dev/null", "w", stdout);
}

//
// Closes all stdio by redirecting to /dev/null
//
Handle<Value> CloseStdio(const Arguments& args) {
  freopen("/dev/null", "r", stdin);
  freopen("/dev/null", "w", stderr);
  freopen("/dev/null", "w", stdout);
}

//
// File-lock to make sure that only one instance of daemon is running, also for storing pid
//   lock (filename)
//   @filename: a path to a lock-file.
//
//   Note: if filename doesn't exist, it will be created when function is called.
//
Handle<Value> LockD(const Arguments& args) {
  if (!args[0]->IsString())
    return Boolean::New(false);

  String::Utf8Value data(args[0]->ToString());
  char pid_str[PID_MAXLEN+1];

  int lfp = open(*data, O_RDWR | O_CREAT | O_TRUNC, 0640);
  if(lfp < 0) exit(1);
  if(lockf(lfp, F_TLOCK, 0) < 0) return Boolean::New(false);

  int len = snprintf(pid_str, PID_MAXLEN, "%d", getpid());
  write(lfp, pid_str, len);
  fsync(lfp);

  return Boolean::New(true);
}

//
// Set the chroot of this process. You probably want to be sure stuff is in here.
//   chroot (folder)
//   @folder {string}: The new root
//
Handle<Value> Chroot(const Arguments& args) {
  if (args.Length() < 1) {
    return ThrowException(Exception::TypeError(
      String::New("Must have one argument; a string of the folder to chroot to.")
    ));
  }
  uid_t uid;
  int rv;

  String::Utf8Value folderUtf8(args[0]->ToString());
  const char *folder = ToCString(folderUtf8);
  rv = chroot(folder);
  if (rv != 0) {
    return ThrowException(ErrnoException(errno, "chroot"));
  }
  chdir("/");

  return Boolean::New(true);
}

//
// Allow changing the real and effective user ID of this process
// so a root process can become unprivileged
//
Handle<Value> SetReuid(const Arguments& args) {
  if (args.Length() == 0 || (!args[0]->IsString() && !args[0]->IsInt32()))
    return ThrowException(Exception::Error(
      String::New("Must give a uid or username to become")
    ));

  if (args[0]->IsString()) {
    String::AsciiValue username(args[0]);

    struct passwd* pwd_entry = getpwnam(*username);

    if (pwd_entry) {
      setreuid(pwd_entry->pw_uid, pwd_entry->pw_uid);
      return Boolean::New(true);
    }
    else {
      return ThrowException(Exception::Error(
        String::New("User not found")
      ));
    }
  }
  else if (args[0]->IsInt32()) {
    uid_t uid;
    uid = args[0]->Int32Value();
    setreuid(uid, uid);
    return Boolean::New(true);
  }
}

//
// Initialize this add-on
//
extern "C" void init(Handle<Object> target) {
  HandleScope scope;

  NODE_SET_METHOD(target, "start", Start);
  NODE_SET_METHOD(target, "lock", LockD);
  NODE_SET_METHOD(target, "chroot", Chroot);
  NODE_SET_METHOD(target, "setreuid", SetReuid);
  NODE_SET_METHOD(target, "closeStderr", CloseStderr);
  NODE_SET_METHOD(target, "closeStdout", CloseStdout);
  NODE_SET_METHOD(target, "closeStdin", CloseStdin);
  NODE_SET_METHOD(target, "closeStdio", CloseStdio);
}
<commit_msg>[fix] ev_default_fork() was necessary for mac os x.<commit_after>/*
* Daemon.node: A node.JS addon that allows creating Unix/Linux Daemons in pure Javascript.
 *
* Copyright 2010 (c) <arthur@norgic.com>
* Modified By: Pedro Teixeira  2010
* Modified By: James Haliday   2010
* Modified By: Charlie Robbins 2010
* Modified By: Zak Taylor      2010
* Modified By: Daniel Bartlett 2011
* Modified By: Charlie Robbins 2011
*
* Under MIT License. See LICENSE file.
*
*/

#include <v8.h>
#include <node.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <pwd.h>

#define PID_MAXLEN 10

using namespace v8;
using namespace node;

const char* ToCString(const v8::String::Utf8Value& value) {
  return *value ? *value : "<string conversion failed>";
}

//
// Go through special routines to become a daemon.
// if successful, returns daemon pid
//
static Handle<Value> Start(const Arguments& args) {
  HandleScope scope;
  pid_t sid, pid;
  int new_fd = -1, new_fd_stderr, length;

  pid = fork();
  if (pid < 0) {
    return ThrowException(ErrnoException(errno, "fork()"));
  }
  else if (pid > 0) exit(0);

  if (pid == 0) {
    // Child process:
    ev_default_fork();

    sid = setsid();
    if(sid < 0) {
      return ThrowException(ErrnoException(errno, "setsid()"));
    }

    // Close stdin
    freopen("/dev/null", "r", stdin);

    length = args.Length();

    //
    // Attempt to set STDOUT_FILENO if we have been
    // passed an argument for it, otherwise point
    // to /dev/null
    //
    if (length > 0 && args[0]->IsString()) {
      String::Utf8Value outfile(args[0]->ToString());
      new_fd = open(ToCString(outfile), O_WRONLY | O_APPEND | O_CREAT,
                S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP);
      if (new_fd < 0) {
        return ThrowException(ErrnoException(errno, "open(), stdout"));
      }
      if (dup2(new_fd, STDOUT_FILENO) < 0) {
        return ThrowException(ErrnoException(errno, "dup2(), stdout"));
      }
    }
    else {
      freopen("/dev/null", "w", stdout);
    }

    //
    // Get the STDERR fd if it has been passed
    // as an argument
    //
    if (length > 1 && args[1]->IsString()) {
      String::Utf8Value errfile(args[1]->ToString());
      new_fd_stderr = open(ToCString(errfile), O_WRONLY | O_APPEND | O_CREAT,
                            S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP);
      if (new_fd_stderr < 0) {
        return ThrowException(ErrnoException(errno, "open(), stderr"));
      }
    }
    else {
      new_fd_stderr = new_fd;
    }

    //
    // Attempt to set STDERR_FILENO if we have
    // a valid file descriptor, otherwise point
    // to /dev/null
    //
    if (new_fd_stderr != -1) {
      if (dup2(new_fd_stderr, STDERR_FILENO) < 0) {
        return ThrowException(ErrnoException(errno, "dup2(), stderr"));
      }
    }
    else {
      freopen("/dev/null", "w", stderr);
    }
  }

  return scope.Close(Integer::New(getpid()));
}

//
// Close stdin by redirecting it to /dev/null
//
Handle<Value> CloseStdin(const Arguments& args) {
  freopen("/dev/null", "r", stdin);
}

//
// Close stderr by redirecting to /dev/null
//
Handle<Value> CloseStderr(const Arguments& args) {
  freopen("/dev/null", "w", stderr);
}

//
// Close stdout by redirecting to /dev/null
//
Handle<Value> CloseStdout(const Arguments& args) {
  freopen("/dev/null", "w", stdout);
}

//
// Closes all stdio by redirecting to /dev/null
//
Handle<Value> CloseStdio(const Arguments& args) {
  freopen("/dev/null", "r", stdin);
  freopen("/dev/null", "w", stderr);
  freopen("/dev/null", "w", stdout);
}

//
// File-lock to make sure that only one instance of daemon is running, also for storing pid
//   lock (filename)
//   @filename: a path to a lock-file.
//
//   Note: if filename doesn't exist, it will be created when function is called.
//
Handle<Value> LockD(const Arguments& args) {
  if (!args[0]->IsString())
    return Boolean::New(false);

  String::Utf8Value data(args[0]->ToString());
  char pid_str[PID_MAXLEN+1];

  int lfp = open(*data, O_RDWR | O_CREAT | O_TRUNC, 0640);
  if(lfp < 0) exit(1);
  if(lockf(lfp, F_TLOCK, 0) < 0) return Boolean::New(false);

  int len = snprintf(pid_str, PID_MAXLEN, "%d", getpid());
  write(lfp, pid_str, len);
  fsync(lfp);

  return Boolean::New(true);
}

//
// Set the chroot of this process. You probably want to be sure stuff is in here.
//   chroot (folder)
//   @folder {string}: The new root
//
Handle<Value> Chroot(const Arguments& args) {
  if (args.Length() < 1) {
    return ThrowException(Exception::TypeError(
      String::New("Must have one argument; a string of the folder to chroot to.")
    ));
  }
  uid_t uid;
  int rv;

  String::Utf8Value folderUtf8(args[0]->ToString());
  const char *folder = ToCString(folderUtf8);
  rv = chroot(folder);
  if (rv != 0) {
    return ThrowException(ErrnoException(errno, "chroot"));
  }
  chdir("/");

  return Boolean::New(true);
}

//
// Allow changing the real and effective user ID of this process
// so a root process can become unprivileged
//
Handle<Value> SetReuid(const Arguments& args) {
  if (args.Length() == 0 || (!args[0]->IsString() && !args[0]->IsInt32()))
    return ThrowException(Exception::Error(
      String::New("Must give a uid or username to become")
    ));

  if (args[0]->IsString()) {
    String::AsciiValue username(args[0]);

    struct passwd* pwd_entry = getpwnam(*username);

    if (pwd_entry) {
      setreuid(pwd_entry->pw_uid, pwd_entry->pw_uid);
      return Boolean::New(true);
    }
    else {
      return ThrowException(Exception::Error(
        String::New("User not found")
      ));
    }
  }
  else if (args[0]->IsInt32()) {
    uid_t uid;
    uid = args[0]->Int32Value();
    setreuid(uid, uid);
    return Boolean::New(true);
  }
}

//
// Initialize this add-on
//
extern "C" void init(Handle<Object> target) {
  HandleScope scope;

  NODE_SET_METHOD(target, "start", Start);
  NODE_SET_METHOD(target, "lock", LockD);
  NODE_SET_METHOD(target, "chroot", Chroot);
  NODE_SET_METHOD(target, "setreuid", SetReuid);
  NODE_SET_METHOD(target, "closeStderr", CloseStderr);
  NODE_SET_METHOD(target, "closeStdout", CloseStdout);
  NODE_SET_METHOD(target, "closeStdin", CloseStdin);
  NODE_SET_METHOD(target, "closeStdio", CloseStdio);
}
<|endoftext|>