Esempio n. 1
0
static void mqtt_onConnect(
    struct mosquitto *client,
    void *data,
    int rc)
{
    /* Subscribe to topic when connected to the broker */
    mqtt_Connector this = data;
    if (rc != 0) {
        corto_error("mqtt: unable to connect to %s", this->host);
    } else {
        corto_id topic;
        strcpy(topic, this->topic);

        corto_ok("mqtt: connected to %s", this->host);

        /* Subscribe to subtree of mountpoint */
        if (*topic && strcmp(topic, "/")) {
            strcat(topic, "/#");
        } else {
            strcpy(topic, "#");
        }

        corto_trace("mqtt: subscribing to %s", topic);
        if (mosquitto_subscribe(client, 0, topic, 1)) {
            corto_error("mqtt: failed to subscribe for topic");
        }
        corto_ok("mqtt: subscribed to %s", topic);
    }
}
Esempio n. 2
0
corto_int16 json_deserReference(void* p, corto_type t, JSON_Value* v)
{
    switch(json_value_get_type(v)) {
    case JSONString: {
        const char* reference = json_value_get_string(v);
        corto_object o = corto_resolve(NULL, (corto_string)reference);
        if (!o) {
            corto_error("unresolved reference \"%s\"", reference);
            goto error;
        }

        if (!corto_instanceof(t, o)) {
            corto_error("%s is not an instance of %s", reference, corto_idof(t));
        }

        corto_setref(p, o);
        corto_release(o);
        break;
    }
    case JSONObject: {
        JSON_Object* obj = json_value_get_object(v);

        JSON_Value* type = json_object_get_value(obj, "type");
        if (json_value_get_type(type) != JSONString) {
            corto_seterr("type parameter of anonymous object must be a string");
            goto error;
        }
        corto_type cortoType = corto_resolve(NULL, (char*)json_value_get_string(type));
        if (!cortoType) {
            corto_seterr("type '%s' not found for anonymous object", json_value_get_string(type));
            goto error;
        }

        corto_object cortoObj = *(corto_object*)p;
        if (!cortoObj || (corto_typeof(cortoObj) != cortoType)) {
            cortoObj = corto_create(cortoType);
            corto_setref(p, cortoObj);
            corto_release(cortoObj);
        }
        corto_release(cortoType);

        JSON_Value* value = json_object_get_value(obj, "value");
        if (json_deserType(cortoObj, cortoType, value)) {
            goto error;
        }
        break;
    }
    case JSONNull:
        corto_setref(p, NULL);
        break;
    default:
        corto_seterr("expected string, null or object (reference), got %s", json_valueTypeToString(v));
        break;
    }

    return 0;
error:
    return -1;
}
Esempio n. 3
0
corto_int16 cortotool_language(char *language) {
    if (!generators) {
        generators = corto_llNew();
    }

    if (!attributes) {
        attributes = corto_llNew();
    }

    if (!strcmp(language, "c")) {
        corto_llAppend(generators, "c_project");
        corto_llAppend(generators, "c_type");
        corto_llAppend(generators, "c_interface");
        corto_llAppend(generators, "c_meta");
        corto_llAppend(generators, "c_api");
        corto_llAppend(attributes, "c=src");
        corto_llAppend(attributes, "h=include");

    } else if (!strcmp(language, "cpp")) {
        corto_llAppend(generators, "cpp_class");
        corto_llAppend(generators, "c_type");
        corto_llAppend(generators, "c_meta");
        corto_llAppend(attributes, "c=src");
        corto_llAppend(attributes, "h=include");

    } else {
        corto_error("corto: unknown language '%s'", language);
        goto error;
    }

    return 0;
error:
    return -1;
}
Esempio n. 4
0
/* Remove object */
void corto_llRemove(corto_ll list, void* o) {
    corto_llNode node, prev;
    corto_bool found = FALSE;

    prev = 0;
    node = list->first;

    while(node) {
        if (node->data == o) {
            found = TRUE;
            if (prev) {
                prev->next = node->next;
                if (!prev->next) {
                    list->last = prev;
                }
            } else {
                list->first = node->next;
                if (!node->next) {
                    list->last = list->first;
                }
            }
            free(node);
            break;
        }
        prev = node;
        node = node->next;
    }
    list->size--;

    if (!found) {
        corto_error("ll::remove: list does not contain %p", o);
    }
}
Esempio n. 5
0
corto_int16 corto_ser_initCollection(corto_serializer s, corto_value* v, void* userData) {
    corto_type t;
    void* o;
    
    t = corto_valueType(v);
    o = corto_valueValue(v);
    
    switch(corto_collection(t)->kind) {
        case CORTO_ARRAY:
            /* Serialize elements */
            if (corto_serializeElements(s, v, userData)) {
                goto error;
            }
            break;
        case CORTO_SEQUENCE:
            ((corto_objectseq*)o)->buffer = NULL;
            ((corto_objectseq*)o)->length = 0;
            break;
        case CORTO_LIST:
            *(corto_ll*)o = corto_llNew();
            break;
        case CORTO_MAP:
            /**(corto_rbtree*)o = corto_rbtreeNew(t);*/
            break;
        default:
            corto_error("invalid collection object!");
            goto error;
            break;
    }
    
    return 0;
error:
    return -1;
}
Esempio n. 6
0
static int cortotool_addDirToMonitor(corto_string dir, corto_ll monitorList) {
    corto_id cortoDir, srcDir;
    sprintf(cortoDir, "%s/.corto", dir);
    sprintf(srcDir, "%s/src", dir);

    corto_ll files = corto_opendir(srcDir);
    if (!files || !corto_fileTest(cortoDir)) {
        corto_error("'%s' isn't a valid project directory", dir);
        goto error;
    }

    corto_iter iter = corto_ll_iter(files);
    while (corto_iter_hasNext(&iter)) {
        corto_id srcFile;
        corto_string file = corto_iter_next(&iter);
        sprintf(srcFile, "%s/src/%s", dir, file);
        corto_fileMonitor *mon = cortotool_monitorNew(srcFile, dir);
        corto_ll_append(monitorList, mon);
    }

    corto_closedir(files);

    return 0;
error:
    return -1;
}
Esempio n. 7
0
/* Register a filetype */
int corto_load_register(corto_string ext, corto_loadAction handler, void* userData) {
    struct corto_fileHandler* h;

    /* Check if extension is already registered */
    corto_mutexLock(&corto_adminLock);
    if ((h = corto_lookupExt(ext))) {
        if (h->load != handler) {
            corto_error("corto_load_register: extension '%s' is already registered with another loader.", ext);
            abort();
            goto error;
        }
    } else {
        h = corto_alloc(sizeof(struct corto_fileHandler));
        h->ext = ext;
        h->load = handler;
        h->userData = userData;

        if (!fileHandlers) {
            fileHandlers = corto_ll_new();
        }

        corto_ll_append(fileHandlers, h);
    }
    corto_mutexUnlock(&corto_adminLock);

    return 0;
error:
    return -1;
}
Esempio n. 8
0
int objects_hierarchyMain(int argc, char *argv[]) {

    /* Create an object in the global object hierarchy */
    corto_object *parent = corto_createChild(root_o, "parent", corto_void_o);
    if (!parent) {
        goto error;
    }

    /* Show information about the parent object */
    printInfo(parent);

    /* Create an object in the scope of the parent object */
    corto_object *child = corto_createChild(parent, "child", corto_void_o);
    if (!parent) {
        goto error;
    }

    /* Show information about the child object */
    printInfo(child);

    /* Delete both the parent and child object */
    if (corto_delete(parent)) {
        goto error;
    }

    return 0;
error:
    corto_error("error: %s", corto_lasterr());
    return -1;
}
Esempio n. 9
0
/* Sleep */
void corto_sleep(unsigned int sec, unsigned int nanosec) {
    struct timespec sleepTime;

    sleepTime.tv_sec = sec;
    sleepTime.tv_nsec = nanosec;
    if (nanosleep(&sleepTime, NULL)) {
        corto_error("nanosleep failed: %s", strerror(errno));
    }
}
Esempio n. 10
0
char* corto_value_exprStr(corto_value* v, char* buffer, unsigned int length) {
    corto_member m;
    corto_value* parents[CORTO_MAX_INHERITANCE_DEPTH];
    corto_int32 parentCount, i;
    corto_value* vptr;

    *buffer = '\0';

    /* Collect parents */
    parentCount = 0;
    vptr = v;
    do{
        if (vptr->kind != CORTO_OBJECT) {
            parents[parentCount] = vptr;
            parentCount++;
        }
    }while((vptr = vptr->parent));

    /* Put name of member or branch in buffer */
    for(i=parentCount-1; i>=0; i--) {
        vptr = parents[i];
        m = NULL;
        switch(vptr->kind) {
        case CORTO_LITERAL:
        case CORTO_VALUE:
        case CORTO_MEM:
            break;
        case CORTO_BASE:
            break;
        case CORTO_MEMBER:
            m = vptr->is.member.t;
            break;
        case CORTO_ELEMENT:
            sprintf(buffer, "%s[%d]", buffer, vptr->is.element.t.index);
            m = NULL;
            break;
        default:
            corto_trace("corto_value_exprStr: unhandled '%s'", corto_valueKindString[vptr->kind]);
            m = NULL;
            goto error;
        }
        if (m) {
            if ((strlen(buffer) + strlen(corto_idof(m)) + 1) >= length) {
                corto_error("buffer passed to corto_strving is too short for member name");
            } else {
                strcat(buffer, ".");
                strcat(buffer, corto_idof(m));
            }
        }
    }

    return buffer;
error:
    return NULL;
}
Esempio n. 11
0
void corto_seterrv(char *fmt, va_list args) {
    char *err = NULL;
    if (fmt) {
        corto_vasprintf(&err, fmt, args);
    }
    corto_setLasterror(err);

    if (fmt && (CORTO_DEBUG_ENABLED || CORTO_OPERATIONAL)) {
        if (CORTO_OPERATIONAL == 1) {
            corto_error("error raised while starting up: %s", err);
        } else if (CORTO_OPERATIONAL){
            corto_error("error raised while shutting down: %s", err);
        } else {
            corto_error("debug: %s", err);
        }
        corto_backtrace(stderr);
    }

    corto_dealloc(err);
}
Esempio n. 12
0
int dynamic_structMain(int argc, char *argv[]) {

    /* Create a new struct */
    corto_struct Point = corto_declareChild(root_o, "Point", corto_struct_o);
    if (!Point) {
        goto error;
    }

    /* Create x member */
    corto_member x = corto_declareChild(Point, "x", corto_member_o);
    if (!x) {
        goto error;
    }
    if (!corto_checkState(x, CORTO_DEFINED)) {
        corto_setref(&x->type, corto_int32_o);
        if (corto_define(x)) {
            goto error;
        }
    }

    /* Create y member */
    corto_member y = corto_declareChild(Point, "y", corto_member_o);
    if (!y) {
        goto error;
    }
    if (!corto_checkState(y, CORTO_DEFINED)) {
        corto_setref(&y->type, corto_int32_o);
        if (corto_define(y)) {
            goto error;
        }
    }

    /* Finalize struct */
    if (corto_define(Point)) {
        goto error;
    }

    /* Create an instance of Point */
    corto_object p = corto_createChild(root_o, "p", Point);
    if (!p) {
        goto error;
    }

    *(corto_int32*)CORTO_OFFSET(p, x->offset) = 10;
    *(corto_int32*)CORTO_OFFSET(p, y->offset) = 20;

    printf("p = %s\n", corto_contentof(NULL, "text/corto", p));

    return 0;
error:
    corto_error("error: %s", corto_lasterr());
    return -1;
}
Esempio n. 13
0
corto_int16 cortotool_test(int argc, char *argv[]) {
    corto_string testCase = NULL;

    if (argc > 1) {
        if (corto_chdir(argv[1])) {
            corto_error("corto: can't change to directory '%s'", argv[1]);
            goto error;
        }
        if (argc > 2) {
            testCase = argv[2];
        }
    }

    if (corto_fileTest("test")) {
        corto_int8 ret = 0, sig = 0, err = 0;
        corto_pid pid = corto_procrun("corto", (char*[]){"corto", "build", "test", "--silent", NULL});
Esempio n. 14
0
corto_int16 _corto_delegate_bind(corto_function object) {
/* $begin(::corto::lang::delegate::bind) */
    corto_object parent = corto_parentof(object);

    if (corto_class_instanceof(corto_interface_o, corto_typeof(parent))) {
        corto_interface type = corto_interface(corto_typeof(parent));
        corto_id functionName;
        corto_member m = NULL;

        /* Get function name, lookup delegate, assign function */
        corto_signatureName(corto_nameof(object), functionName);
        if (corto_checkState(corto_type_o, CORTO_DEFINED) && (m = corto_interface_resolveMember(type, functionName)) &&
            (m->type->kind == CORTO_COMPOSITE) && (corto_interface(m->type)->kind == CORTO_DELEGATE)) {
            if (corto_delegate_instanceof(corto_delegate(m->type), object)) {
                /* Bind instance of function is a method */
                if (corto_procedure(corto_typeof(object))->kind == CORTO_METHOD) {
                    corto_setref(&((corto_delegatedata *) CORTO_OFFSET(parent, m->offset))->instance, parent);
                }
                /* Bind procedure */
                corto_setref(&((corto_delegatedata *) CORTO_OFFSET(parent, m->offset))->procedure, object);    
            } else {
                /* If there is a member that corresponds to a delegate but has a non matching
                 * signature, always report error */
                corto_id id1, id2;
                corto_error("member '%s' of delegate type '%s' does not match signature of '%s'",
                    corto_nameof(m), corto_fullname(m->type, id1), corto_fullname(object, id2));
                goto error;
            }
        }
    }

    return 0;
error:
    return -1;
/* $end */
}
Esempio n. 15
0
corto_int16 cortotool_publish(int argc, char *argv[]) {
    corto_ll silent, mute, notag, dirs, majorarg, minorarg, patcharg;
    corto_uint32 major = 0, minor = 0, patch = 0;

    CORTO_UNUSED(argc);

    corto_argdata *data = corto_argparse(
      argv,
      (corto_argdata[]){
        {"$0", NULL, NULL}, /* Ignore 'publish' argument */
        {"--silent", &silent, NULL},
        {"--mute", &mute, NULL},
        {"--notag", &notag, NULL},
        {"$?*", &dirs, NULL},
        {"$+major", &majorarg, NULL},
        {"$|minor", &minorarg, NULL},
        {"$|patch", &patcharg, NULL},
        {NULL}
      }
    );

    if (!data) {
        corto_error("corto: %s", corto_lasterr());
        goto error;
    }

    if (dirs) {
        corto_string dir = corto_llGet(dirs, 0);
        if (corto_chdir(dir)) {
            corto_error("corto: %s", corto_lasterr());
            goto error;
        }
    }


    if (!corto_fileTest("./.corto")) {
        corto_error("corto: invalid project directory");
        goto error;
    }

    if (!notag) {
        corto_error(
            "corto: tagging of repository not yet supported "
            "(use --notag to just increase version)");
        goto error;
    }

    corto_string version = corto_fileLoad(".corto/version.txt");

    /* Patch version */
    if (version) {
        char *v = version;

        /* Parse major version */
        char *ptr = strchr(version, '.');
        if (ptr) {
            *ptr = '\0';
            major = atoi(v);
            v = ptr + 1;
        }

        /* Parse minor version */
        ptr = strchr(v, '.');
        if (ptr) {
            *ptr = '\0';
            minor = atoi(v);
            v = ptr + 1;
        }

        /* Parse patch version */
        patch = atoi(v);
    }

    if (majorarg) {
        major++;
    }
    if (minorarg) {
        minor++;
    }
    if (patcharg) {
        patch++;
    }

    FILE *f = fopen(".corto/version.txt", "w");
    if (!f) {
        corto_error("failed to open '.corto/version.txt' (check permissions)");
        goto error;
    }

    fprintf(f, "%u.%u.%u\n", major, minor, patch);
    fclose(f);

    corto_argclean(data);
    corto_dealloc(version);

    if (!silent) {
        corto_print("corto: version updated to %u.%u.%u", major, minor, patch);
    }

    return 0;
error:
    return -1;
}
Esempio n. 16
0
int select_contentTypeMain(int argc, char *argv[]) {

    /* Create a Point type, so we have something to serialize to JSON */
    corto_struct Point = corto_declareChild(root_o, "Point", corto_struct_o);
    if (!Point) {
        goto error;
    }

    /* Create x member */
    corto_member x = corto_declareChild(Point, "x", corto_member_o);
    if (!x) {
        goto error;
    }
    if (!corto_checkState(x, CORTO_DEFINED)) {
        corto_setref(&x->type, corto_int32_o);
        if (corto_define(x)) {
            goto error;
        }
    }

    /* Create y member */
    corto_member y = corto_declareChild(Point, "y", corto_member_o);
    if (!y) {
        goto error;
    }
    if (!corto_checkState(y, CORTO_DEFINED)) {
        corto_setref(&y->type, corto_int32_o);
        if (corto_define(y)) {
            goto error;
        }
    }

    /* Finalize Point struct */
    if (corto_define(Point)) {
        goto error;
    }

    /* Create two instances of Point. */
    corto_object p1 = corto_declareChild(root_o, "p1", Point);
    if (!p1) {
        goto error;
    }

    if (!corto_checkState(p1, CORTO_DEFINED)) {
        *(corto_int32*)CORTO_OFFSET(p1, x->offset) = 10;
        *(corto_int32*)CORTO_OFFSET(p1, y->offset) = 10;
        if (corto_define(p1)) {
            goto error;
        }
    }

    corto_object p2 = corto_declareChild(root_o, "p2", Point);
    if (!p2) {
        goto error;
    }

    if (!corto_checkState(p2, CORTO_DEFINED)) {
        *(corto_int32*)CORTO_OFFSET(p2, x->offset) = 20;
        *(corto_int32*)CORTO_OFFSET(p2, y->offset) = 30;
        if (corto_define(p2)) {
            goto error;
        }
    }

    /* Select all instances of type Point, get value in JSON */
    corto_iter it;
    corto_int16 ret = corto_select("/", "*")
      .contentType("text/json")
      .type("/Point")
      .iter(&it);
    if (ret) {
        goto error;
    }

    while (corto_iterHasNext(&it)) {
        corto_result *r = corto_iterNext(&it);
        printf("id: %s, value: %s\n", r->id, corto_result_getText(r));
    }

    return 0;
error:
    corto_error("error: %s", corto_lasterr());
    return -1;
}
Esempio n. 17
0
static void mqtt_onMessage(
    struct mosquitto *client,
    void *data,
    const struct mosquitto_message *msg)
{
    corto_id nameBuffer;
    char *name = nameBuffer;
    corto_object o = NULL;
    mqtt_Connector this = data;
    corto_bool isDelete = FALSE;

    /* If the payload has been serialized as a corto string, the typename is
     * potentially prefixed to the value */
    char *valueStr = strchr(msg->payload, '{');

    /* mqtt is the owner of this thread. This ensures that all subsequent create
     * update / delete actions are performed with the right owner. Ownership
     * ensures to not trigger on own updates, and to only forward data from
     * other connectors (or the application). */
    corto_object prevOwner = corto_setOwner(this);

    /* Remove topic from name, so that name is relative to mount point. */
    strcpy(name, msg->topic);
    if (this->topic) {
        name += strlen(this->topic) + 1;
    }

    char *lastElem = strrchr(name, '/');
    if (lastElem && !strcmp(lastElem, "/_d")) {
        *lastElem = '\0';
        isDelete = TRUE;
    }

    corto_debug("mqtt: %s: received '%s'", msg->topic, msg->payload);

    /* If object doesn't yet exist in the store, create it */
    if (!(o = corto_lookup(corto_mount(this)->mount, name)) && !isDelete) {
        corto_id buffer;
        corto_debug("mqtt: creating new object for '%s'", name);

        /* If the mount has been configured with a fixed type, use that type to
         * create a new object. Otherwise, look for type in payload. */
        if (corto_observer(this)->type) {
            strcpy(buffer, corto_observer(this)->type);
        } else {
            char *typeStr = strchr(msg->payload, '{');
            memcpy(buffer, msg->payload, typeStr - (char*)msg->payload);
            buffer[typeStr - (char*)msg->payload] = '\0';
        }

        /* Resolve type. If type wasn't yet loaded in corto, corto_resolve will
         * do a lookup on the package repository. If it doesn't exist there
         * either throw an error. Currently, the MQTT connector does not align
         * types. */
        corto_type type = corto_resolve(NULL, buffer);
        if (!type) {
            corto_error("mqtt: type '%s' not found", buffer);
            goto error;
        }

        corto_debug("mqtt: creating '%s' with type '%s'", name, buffer);

        /* Create a new object under the mountpoint. The name is derived from
         * the MQTT topic name. */
        o = corto_declareChild(corto_mount(this)->mount, name, type);
        if (!o) {
            corto_error("mqtt: failed to create object '%s'", name);
            goto error;
        }
    } else {
        corto_debug("mqtt: found '%s' for '%s'", corto_fullpath(NULL, o), name);
    }

    /* Only continue updating object when it is owned by mqtt */
    if (o && corto_owned(o)) {
        if (isDelete) {
            if (corto_delete(o)) {
                corto_error("mqtt: failed to delete '%s': %s", name, corto_lasterr());
            }
            corto_release(o);
        } else {
            /* Start updating object (takes a writelock) */
            if (!corto_updateBegin(o)) {
                /* Serialize value from JSON string */
                if (corto_fromcontent(o, "text/json", valueStr)) {
                    corto_error("mqtt: failed to deserialize for %s: %s (%s)\n",
                        name,
                        corto_lasterr(),
                        msg->payload);

                    /* If deserialization fails, cancel the update. No notification
                     * will be sent. */
                    corto_updateCancel(o);
                    goto error;
                }
                /* Successful update. Send notification and unlock object */
                if (corto_updateEnd(o)) {
                    corto_error("mqtt: failed to update '%s': %s", name, corto_lasterr());
                    goto error;
                }
            } else {
                /* For some reason, couldn't start updating object */
                corto_error("mqtt: failed to start updating '%s': %s", name, corto_lasterr());
                goto error;
            }
        }
    } else if (o) {
        corto_debug("mqtt: '%s' not owned by me (%s, defined = %d), ignoring",
            corto_fullpath(NULL, o),
            corto_ownerof(o) ? corto_fullpath(NULL, o) : "local",
            corto_checkState(o, CORTO_DEFINED));
    }

error:
    /* Restore previous owner */
    corto_setOwner(prevOwner);
}
Esempio n. 18
0
int main(int argc, char* argv[]) {
    int i;
    corto_bool mute = FALSE;
    corto_bool startShell = FALSE;

    /* Parse debugging options before starting the core */
    for(i = 1; i < argc; i++) {
        if (*argv[i] == '-') {
            if (*(argv[i]+1) == 'd') {
                CORTO_DEBUG_ENABLED = TRUE;
            }else if (*(argv[i]+1) == 't') {
                CORTO_TRACE_OBJECT = argv[i + 1];
                i ++;
            }else if (*(argv[i]+1) == 'h') {
                cortotool_printUsage(FALSE);
                break;
            } else if (*(argv[i]+1) == 'v') {
                printf("%s\n", CORTO_VERSION);
            } else if (*(argv[i]+1) == '-') {
                if (!strcmp(argv[i], "--backtrace")) {
                    CORTO_BACKTRACE_ENABLED = TRUE;
                }
            }
        }
    }

    /* Start corto */
    corto_start();

    /* Parse arguments */
    if (argc == 1) {
        if (cortotool_shell(argc, argv)) {
            goto error;
        }
    } else {
        for(i=1; i<argc; i++) {
            if (*argv[i] == '-') {
                if (*(argv[i]+1) == 'd') {
                    /* Already handled */
                }else if (*(argv[i]+1) == 't') {
                    /* Already handled */
                    i ++;
                }else if (*(argv[i]+1) == 'h') {
                    /* Already handled */
                    break;
                } else if (*(argv[i]+1) == 'v') {
                    /* Already handled */
                } else if (*(argv[i]+1) == 'c') {
                    if (corto_loadComponent(argv[i + 1], 0, NULL)) {
                        corto_error("%s: %s", argv[i + 1], corto_lasterr());
                        goto error;
                    }
                    i++;
                } else if (*(argv[i]+1) == 'p') {
                    if (corto_load(argv[i + 1], 0, NULL)) {
                        corto_error("%s: %s", argv[i + 1], corto_lasterr());
                        goto error;
                    }
                    i++;
                } else if (*(argv[i]+1) == '-') {
                    if (!strcmp(argv[i] + 2, "version")) {
                        printf("corto version %s (%s) build %s\n\n",
                            CORTO_VERSION,
                            CORTO_PLATFORM_STRING,
                            corto_getBuild());
                    } else if (!strcmp(argv[i] + 2, "minor")) {
                        printf("%s.%s\n", CORTO_VERSION_MAJOR, CORTO_VERSION_MINOR);
                    } else if (!strcmp(argv[i] + 2, "help")) {
                        cortotool_printUsage(FALSE);
                    } else if (!strcmp(argv[i] + 2, "expert")) {
                        cortotool_printUsage(TRUE);
                    } else if (!strcmp(argv[i] + 2, "mute")) {
                        mute = TRUE;
                    } else if (!strcmp(argv[i] + 2, "backtrace")) {
                        /* Already handled */
                    } else {
                        corto_error("corto: unknown option '%s'", argv[i] + 2);
                        cortotool_printUsage(FALSE);
                        goto error;
                    }
                } else {
                    corto_error("corto: unknown option '%s'", argv[i] + 1);
                    cortotool_printUsage(FALSE);
                    goto error;
                }
            } else if (!strcmp(argv[i], "create")) {
                if (cortotool_create(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "add")) {
                if (cortotool_add(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "remove")) {
                if (cortotool_remove(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "list")) {
                if (cortotool_list(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "pp")) {
                if (cortotool_pp(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "publish")) {
                if (cortotool_publish(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "build")) {
                if (cortotool_build(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "rebuild")) {
                if (cortotool_rebuild(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "test")) {
                if (cortotool_test(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "clean")) {
                if (cortotool_clean(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "install")) {
                if (cortotool_install(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "uninstall")) {
                if (cortotool_uninstall(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "update")) {
                if (cortotool_update(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "locate")) {
                if (cortotool_locate(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "run")) {
                if (cortotool_run(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "debug")) {
                if (cortotool_debug(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[i], "shell")) {
                startShell = TRUE;
            } else if (!strcmp(argv[1], "tar")) {
                if (cortotool_tar(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[1], "untar")) {
                if (cortotool_untar(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else if (!strcmp(argv[1], "help")) {
                if (cortotool_help(argc-i, &argv[i])) {
                    goto error;
                }
                break;
            } else {
                if (corto_load(argv[i], argc-i, &argv[i])) {
                    if (!mute) {
                        if (corto_lasterr()) {
                            corto_error("corto: %s: %s", argv[i], corto_lasterr());
                        } else {
                            corto_error("corto: %s: input error", argv[i]);
                        }
                    }
                    goto error;
                }
                break;
            }
        }
    }

    if (startShell) {
        if (cortotool_shell(argc-i, &argv[i])) {
            goto error;
        }
    }

    /* Stop corto */
    corto_stop();
    return 0;
error:
    return -1;
}