/** * [File] read * * Reads contents of a file. Send this message to a file to read the entire contents in * one go. For big files you might want to prefer a streaming approach to avoid * memory exhaustion (see readBytes etc). * * Usage: * * data := File new: '/path/to/mydata.csv', read. * * In the example above we read the contents of the entire CSV file callled mydata.csv * in the variable called data. */ ctr_object* ctr_file_read(ctr_object* myself, ctr_argument* argumentList) { ctr_object* path = ctr_internal_object_find_property(myself, ctr_build_string_from_cstring( "path" ), 0); ctr_object* str; ctr_size vlen, fileLen; char* pathString; char *buffer; FILE* f; if (path == NULL) return CtrStdNil; vlen = path->value.svalue->vlen; pathString = ctr_heap_allocate( sizeof(char) * ( vlen + 1 ) ); memcpy(pathString, path->value.svalue->value, vlen); memcpy(pathString+vlen,"\0",1); f = fopen(pathString, "rb"); ctr_heap_free( pathString ); if (!f) { CtrStdFlow = ctr_build_string_from_cstring( "Unable to open file." ); CtrStdFlow->info.sticky = 1; return CtrStdNil; } fseek(f, 0, SEEK_END); fileLen=ftell(f); fseek(f, 0, SEEK_SET); buffer=(char *)ctr_heap_allocate(fileLen+1); if (!buffer){ printf("Out of memory\n"); fclose(f);exit(1); } fread(buffer, fileLen, 1, f); fclose(f); str = ctr_build_string(buffer, fileLen); ctr_heap_free( buffer ); return str; }
ctr_object* ctr_json_create_object(json_t* root, ctr_object* gt) { switch(json_typeof(root)) { case JSON_OBJECT: { ctr_object* sub = ctr_internal_create_object(CTR_OBJECT_TYPE_OTOBJECT); ctr_set_link_all(sub, gt); // size_t size; const char *key; json_t *value; ctr_argument* argl = ctr_heap_allocate(sizeof(*argl)); argl->next = ctr_heap_allocate(sizeof(*argl)); // size = json_object_size(root); json_object_foreach(root, key, value) { char* k = (char*)key; ctr_object* ko = ctr_build_string_from_cstring(k); ctr_object* vo = ctr_json_create_object(value, gt); argl->object = vo; argl->next->object = ko; sub = ctr_map_put(sub, argl); } ctr_heap_free(argl->next); ctr_heap_free(argl); return sub; } case JSON_ARRAY: { ctr_object* arr = ctr_array_new(CtrStdArray, NULL); ctr_argument* arg = ctr_heap_allocate(sizeof(ctr_argument)); size_t i; size_t size = json_array_size(root); for (i = 0; i < size; i++) { arg->object = ctr_json_create_object(json_array_get(root, i), gt); ctr_array_push(arr, arg); } ctr_heap_free(arg); return arr; } case JSON_STRING: { ctr_object* str = ctr_build_string((char*)json_string_value(root), json_string_length(root)); return str; } case JSON_INTEGER: { return ctr_build_number_from_float(json_integer_value(root)); } case JSON_REAL: { return ctr_build_number_from_float(json_real_value(root)); } case JSON_FALSE: { return ctr_build_bool(0); } case JSON_TRUE: { return ctr_build_bool(1); } case JSON_NULL: { return ctr_build_nil(); } default: { CtrStdFlow = ctr_build_string_from_cstring("Unrecognized JSON type"); return CtrStdNil; } }
ctr_object* ctr_fiber_spawn(ctr_object* myself, ctr_argument* argumentList) { ctr_object* fiberObj = ctr_fiber_create(myself, NULL); ctr_object* blk = argumentList->object; if(blk == NULL || blk->info.type != CTR_OBJECT_TYPE_OTBLOCK) {CtrStdFlow = ctr_build_string_from_cstring("Fiber's spawning requires a block."); return myself;} int fiber = spawnFiber(argumentList->object); ctr_internal_object_add_property(fiberObj, ctr_build_string_from_cstring("fiberId"), ctr_build_number_from_float(fiber), CTR_CATEGORY_PRIVATE_PROPERTY); return fiberObj; }
/** * C-constructor function, as declared above. * * This function gets called when the plugin is loaded into memory. * Here you have a chance to add the new object(s) to the World. * * In our case, we are going to add the Percolator object to the * world. */ void begin(){ /* Create the Coffee Percolator Object - Use new, because its a prototype, not a class !*/ ctr_object* percolatorObject = ctr_percolator_new(CtrStdObject, NULL); /* Set the prototype */ percolatorObject->link = CtrStdObject; /* Add the method 'new' so people can create their percolators */ ctr_internal_create_func(percolatorObject, ctr_build_string_from_cstring( "new" ), &ctr_percolator_new ); ctr_internal_create_func(percolatorObject, ctr_build_string_from_cstring( "brew" ), &ctr_percolator_brew ); ctr_internal_create_func(percolatorObject, ctr_build_string_from_cstring( "coffee:water:" ), &ctr_percolator_add_coffee_water ); /* Make the Percolator accessible to the world */ ctr_internal_object_add_property(CtrStdWorld, ctr_build_string_from_cstring( "Percolator" ), percolatorObject, CTR_CATEGORY_PUBLIC_PROPERTY); }
/** * @internal * * Returns an array from the request, either for GET, POST or COOKIE. */ ctr_object* ctr_request_array(ctr_object* myself, ctr_argument* argumentList, CGI_varlist* varlist) { ctr_object* cgiVarObject; ctr_object* list; char* cgiVar; const CGI_value* value; char* val; ctr_argument* arg; int i = 0; list = ctr_array_new(CtrStdArray, NULL); cgiVarObject = ctr_internal_cast2string(argumentList->object); cgiVar = ctr_heap_allocate_cstring( cgiVarObject ); value = CGI_lookup_all(varlist, (const char*)cgiVar); ctr_heap_free( cgiVar ); if (value == NULL) { return list; } for (i = 0; value[i] != 0; i++) { arg = (ctr_argument*) ctr_heap_allocate( sizeof( ctr_argument ) ); val = (char*) value[i]; arg->object = ctr_build_string_from_cstring(val); ctr_array_push(list, arg); ctr_heap_free( arg ); } return list; }
/** * @internal * * Reads a server option set by user. */ ctr_object* ctr_request_internal_option(ctr_object* myself, char* optName) { ctr_object* key; ctr_object* val; key = ctr_build_string_from_cstring(optName); val = ctr_internal_object_find_property(myself, key, CTR_CATEGORY_PRIVATE_PROPERTY); return val; }
/** * [Map] each: [Block] * * Iterates over the map, passing key-value pairs to the specified block. * Note that within an each/map block, '⛏' and '⚿' refer to the collection. * * In other languages: * Dutch: [Lijst] elk: [Codeblok] | Past het blok code toe op elk paar uit de lijst. */ ctr_object* ctr_map_each(ctr_object* myself, ctr_argument* argumentList) { ctr_object* block = argumentList->object; ctr_mapitem* m; if (block->info.type != CTR_OBJECT_TYPE_OTBLOCK) { CtrStdFlow = ctr_build_string_from_cstring( CTR_ERR_EXP_BLK ); CtrStdFlow->info.sticky = 1; } block->info.sticky = 1; m = myself->properties->head; while(m && !CtrStdFlow) { ctr_argument* arguments = (ctr_argument*) ctr_heap_allocate( sizeof( ctr_argument ) ); ctr_argument* argument2 = (ctr_argument*) ctr_heap_allocate( sizeof( ctr_argument ) ); ctr_argument* argument3 = (ctr_argument*) ctr_heap_allocate( sizeof( ctr_argument ) ); arguments->object = m->key; argument2->object = m->value; argument3->object = myself; arguments->next = argument2; argument2->next = argument3; ctr_block_run(block, arguments, myself); if (CtrStdFlow == CtrStdContinue) CtrStdFlow = NULL; m = m->next; ctr_heap_free( arguments ); ctr_heap_free( argument2 ); ctr_heap_free( argument3 ); } if (CtrStdFlow == CtrStdBreak) CtrStdFlow = NULL; block->info.mark = 0; block->info.sticky = 0; return myself; }
/** * [List] map: [Block]. * * Iterates over the array. Passing each element as a key-value pair to the * specified block. * The map message will pass the following arguments to the block, the key, * the value and a reference to the array itself. The last argument might seem * redundant but allows for a more functional programming style. Instead of map, * you can also use each:. * * Usage: * * files map: showName. * files map: { * :key :filename :files * ✎ write: filename. * }. * * files each: { * :key :filename :files * ✎ write: filename. * }. * * In other languages: * Dutch: [Reeks] lijst: [Codeblok] | Maakt van een reeks */ ctr_object* ctr_array_map(ctr_object* myself, ctr_argument* argumentList) { ctr_object* block = argumentList->object; int i = 0; if (block->info.type != CTR_OBJECT_TYPE_OTBLOCK) { CtrStdFlow = ctr_build_string_from_cstring( CTR_ERR_EXP_BLK ); CtrStdFlow->info.sticky = 1; } for(i = myself->value.avalue->tail; i < myself->value.avalue->head; i++) { ctr_argument* arguments = (ctr_argument*) ctr_heap_allocate( sizeof( ctr_argument ) ); ctr_argument* argument2 = (ctr_argument*) ctr_heap_allocate( sizeof( ctr_argument ) ); ctr_argument* argument3 = (ctr_argument*) ctr_heap_allocate( sizeof( ctr_argument ) ); arguments->object = ctr_build_number_from_float((double) i); argument2->object = *(myself->value.avalue->elements + i); argument3->object = myself; arguments->next = argument2; argument2->next = argument3; /* keep receiver in block object otherwise, GC will destroy it */ ctr_gc_internal_pin(block); ctr_gc_internal_pin(myself); ctr_gc_internal_pin(argument2->object); ctr_block_run(block, arguments, myself); ctr_heap_free( arguments ); ctr_heap_free( argument2 ); ctr_heap_free( argument3 ); if (CtrStdFlow == CtrStdContinue) CtrStdFlow = NULL; if (CtrStdFlow) break; } if (CtrStdFlow == CtrStdBreak) CtrStdFlow = NULL; /* consume break */ return myself; }
/** * OnMessageDoAction * * Makes the object respond to a new kind of message. * * Usage: * object on: 'greet' do {\ ... }. */ ctr_object* ctr_object_on_do(ctr_object* myself, ctr_argument* argumentList) { ctr_argument* nextArgument; ctr_object* methodBlock; ctr_object* methodName = argumentList->object; if (methodName->info.type != CTR_OBJECT_TYPE_OTSTRING) { CtrStdError = ctr_build_string_from_cstring("Expected on: argument to be of type string."); return myself; } nextArgument = argumentList->next; methodBlock = nextArgument->object; if (methodBlock->info.type != CTR_OBJECT_TYPE_OTBLOCK) { CtrStdError = ctr_build_string_from_cstring("Expected argument do: to be of type block."); return myself; } ctr_internal_object_add_property(myself, methodName, methodBlock, 1); return myself; }
/** * [List] position: [Number] * * Returns the element in the list at the specified position. * Note that the first position of the list is index 0. * If you attempt to retrieve an element of the list * using a an index that is something other than a number * a catchable error will be triggered. An error will * also be triggered if your position is out of bounds * (i.e. outside the list). Instead of the message * 'position:' you can also send the message '?'. * * Usage: * * ☞ fruits := List ← 'apples' ; 'oranges' ; 'bananas'. * ☞ oranges := fruits position: 1. * ☞ oranges := fruits ? 1. * * In other languages: * Dutch: [Reeks] positie: [Getal] | Geeft het object op de aangegeven plek in de reeks. */ ctr_object* ctr_array_get(ctr_object* myself, ctr_argument* argumentList) { ctr_object* getIndex = argumentList->object; int i; if (getIndex->info.type != CTR_OBJECT_TYPE_OTNUMBER) { CtrStdFlow = ctr_build_string_from_cstring( CTR_ERR_EXP_NUM ); CtrStdFlow->info.sticky = 1; return CtrStdNil; } i = (int) getIndex->value.nvalue; if (myself->value.avalue->head <= (i + myself->value.avalue->tail) || i < 0) { CtrStdFlow = ctr_build_string_from_cstring( CTR_ERR_BOUNDS ); CtrStdFlow->info.sticky = 1; return CtrStdNil; } ctr_object* q = *(myself->value.avalue->elements + myself->value.avalue->tail + i); return q; }
/** * @internal * * Adds the Request object to the World. */ void begin(){ ctr_object* requestObject = ctr_internal_create_object(CTR_OBJECT_TYPE_OTOBJECT); requestObject->link = CtrStdObject; ctr_internal_create_func(requestObject, ctr_build_string_from_cstring( "get:" ), &ctr_request_get_string ); ctr_internal_create_func(requestObject, ctr_build_string_from_cstring( "getArray:" ), &ctr_request_get_array ); ctr_internal_create_func(requestObject, ctr_build_string_from_cstring( "cookie:" ), &ctr_request_cookie_string ); ctr_internal_create_func(requestObject, ctr_build_string_from_cstring( "cookieArray:" ), &ctr_request_cookie_array ); ctr_internal_create_func(requestObject, ctr_build_string_from_cstring( "post:" ), &ctr_request_post_string ); ctr_internal_create_func(requestObject, ctr_build_string_from_cstring( "file:" ), &ctr_request_file ); ctr_internal_create_func(requestObject, ctr_build_string_from_cstring( "postArray:" ), &ctr_request_post_array ); ctr_internal_create_func(requestObject, ctr_build_string_from_cstring( "serverOption:is:" ), &ctr_request_server_option ); ctr_internal_create_func(requestObject, ctr_build_string_from_cstring( "host:listen:pid:callback:" ), &ctr_request_serve ); ctr_internal_object_add_property(CtrStdWorld, ctr_build_string_from_cstring( "Request" ), requestObject, 0); varlistGet = CGI_get_query(NULL); varlistPost = CGI_get_post(NULL,0); varlistCookie = CGI_get_cookie(NULL); }
/** * CTRWalkerAssignment * * Processes an assignment operation. */ ctr_object * ctr_cwlk_assignment (ctr_tnode * node) { char wasReturn = 0; ctr_tlistitem *assignmentItems = node->nodes; ctr_tnode *assignee = assignmentItems->node; ctr_tlistitem *valueListItem = assignmentItems->next; ctr_tnode *value = valueListItem->node; ctr_object *x; ctr_object *result; char ret; if (CtrStdFlow == NULL) { ctr_callstack[ctr_callstack_index++] = assignee; } x = ctr_cwlk_expr (value, &wasReturn); if (!x) { CtrStdFlow = ctr_build_string_from_cstring ("NULL expression"); return NULL; } if (assignee->type == CTR_AST_NODE_REFERENCE) { if (assignee->modifier == 1) { result = ctr_assign_value_to_my (ctr_build_string (assignee->value, assignee->vlen), x); } else if (assignee->modifier == 2) { result = ctr_assign_value_to_local (ctr_build_string (assignee->value, assignee->vlen), x); } else if (assignee->modifier == 3) { result = ctr_assign_value (ctr_build_string (assignee->value, assignee->vlen), x); //Handle lexical scoping } else { result = ctr_assign_value (ctr_build_string (assignee->value, assignee->vlen), x); } } else { int old_replace = ctr_cwlk_replace_refs; ctr_cwlk_replace_refs = 1; ctr_cwlk_last_msg_level = ctr_cwlk_msg_level; ctr_object *y = ctr_cwlk_expr (assignee, &ret); ctr_cwlk_replace_refs = old_replace; //set back in case we didn't reset result = ctr_send_message_variadic (x, "unpack:", 7, 1, y); ctr_object *old_result = NULL; while (old_result != result && result->info.type == CTR_OBJECT_TYPE_OTBLOCK) result = ctr_block_run_here (result, NULL, (old_result = result)); } if (CtrStdFlow == NULL) { ctr_callstack_index--; } return result; }
/** * [Percolator] coffee: [Number] water: [Number] * * Adds coffee and water to the perculator. * * myPercolator := Percolator new. * cupOfCoffee := myPercolator coffee: 1 water: 2, brew. * */ ctr_object* ctr_percolator_add_coffee_water(ctr_object* myself, ctr_argument* argumentList) { ctr_internal_object_set_property( myself, ctr_build_string_from_cstring( "coffee" ), ctr_internal_cast2number(argumentList->object), CTR_CATEGORY_PRIVATE_PROPERTY ); ctr_internal_object_set_property( myself, ctr_build_string_from_cstring( "water" ), ctr_internal_cast2number(argumentList->next->object), CTR_CATEGORY_PRIVATE_PROPERTY ); return myself; }
ctr_object* ctr_number_div(ctr_object* myself, ctr_argument* argumentList) { ctr_object* otherNum = ctr_internal_cast2number(argumentList->object); if (otherNum->value.nvalue == 0) { CtrStdError = ctr_build_string_from_cstring("Division by zero."); return myself; } myself->value.nvalue /= otherNum->value.nvalue; return myself; }
/** * [Array] sort: [Block] * * Sorts the contents of an array using a sort block. * Uses qsort. */ ctr_object* ctr_array_sort(ctr_object* myself, ctr_argument* argumentList) { ctr_object* sorter = argumentList->object; if (sorter->info.type != CTR_OBJECT_TYPE_OTBLOCK) { CtrStdError = ctr_build_string_from_cstring("Expected block.\0"); return myself; } temp_sorter = sorter; qsort(myself->value.avalue->elements, myself->value.avalue->head, sizeof(ctr_object*), ctr_sort_cmp); return myself; }
ctr_object* ctr_number_modulo(ctr_object* myself, ctr_argument* argumentList) { ctr_object* otherNum = ctr_internal_cast2number(argumentList->object); ctr_number a = myself->value.nvalue; ctr_number b = otherNum->value.nvalue; if (b == 0) { CtrStdError = ctr_build_string_from_cstring("Division by zero."); return myself; } return ctr_build_number_from_float(fmod(a,b)); }
/** * [File] delete * * Deletes the file. */ ctr_object* ctr_file_delete(ctr_object* myself, ctr_argument* argumentList) { ctr_object* path = ctr_internal_object_find_property(myself, ctr_build_string_from_cstring( "path" ), 0); ctr_size vlen; char* pathString; int r; if (path == NULL) return myself; vlen = path->value.svalue->vlen; pathString = ctr_heap_allocate( sizeof( char ) * ( vlen + 1 ) ); memcpy(pathString, path->value.svalue->value, vlen); memcpy(pathString+vlen,"\0",1); r = remove(pathString); ctr_heap_free( pathString ); if (r!=0) { CtrStdFlow = ctr_build_string_from_cstring( "Unable to delete file." ); CtrStdFlow->info.sticky = 1; return CtrStdNil; } return myself; }
/** * File * * Represents a File object. * Creates a new file object based on the specified path. * * Usage: * * File new: '/example/path/to/file.txt'. */ ctr_object* ctr_file_new(ctr_object* myself, ctr_argument* argumentList) { ctr_object* s = ctr_object_make(myself, argumentList); ctr_object* pathObject; s->info.type = CTR_OBJECT_TYPE_OTEX; /* indicates resource for GC */ s->link = myself; s->value.rvalue = NULL; pathObject = ctr_build_string( argumentList->object->value.svalue->value, argumentList->object->value.svalue->vlen ); ctr_internal_object_add_property( s, ctr_build_string_from_cstring( "path" ), pathObject, 0 ); return s; }
/** * Percolator new * * Creates a new instance of the percolator object. * * Usage: * * myPercolator := Percolator new. * cupOfCoffee := myPercolator coffee: 1 water: 2, brew. * */ ctr_object* ctr_percolator_new(ctr_object* myself, ctr_argument* argumentList) { ctr_object* percolatorInstance = ctr_internal_create_object(CTR_OBJECT_TYPE_OTOBJECT); percolatorInstance->link = myself; ctr_internal_object_set_property( percolatorInstance, ctr_build_string_from_cstring( "coffee" ), ctr_build_number_from_float(0), CTR_CATEGORY_PRIVATE_PROPERTY ); ctr_internal_object_set_property( percolatorInstance, ctr_build_string_from_cstring( "water" ), ctr_build_number_from_float(0), CTR_CATEGORY_PRIVATE_PROPERTY ); return percolatorInstance; }
/** * [File] readBytes: [Number]. * * Reads a number of bytes from the file. * * Usage: * * f := File new: '/path/to/file.txt'. * f open: 'r+'. * x := f readBytes: 10. * f close. * * The example above reads 10 bytes from the file represented by f * and puts them in buffer x. */ ctr_object* ctr_file_read_bytes(ctr_object* myself, ctr_argument* argumentList) { int bytes; char* buffer; ctr_object* result; if (myself->value.rvalue == NULL) return myself; if (myself->value.rvalue->type != 1) return myself; bytes = ctr_internal_cast2number(argumentList->object)->value.nvalue; if (bytes < 0) return ctr_build_string_from_cstring(""); buffer = (char*) ctr_heap_allocate(bytes); if (buffer == NULL) { CtrStdFlow = ctr_build_string_from_cstring("Cannot allocate memory for file buffer."); CtrStdFlow->info.sticky = 1; return ctr_build_string_from_cstring(""); } fread(buffer, sizeof(char), (int)bytes, (FILE*)myself->value.rvalue->ptr); result = ctr_build_string(buffer, bytes); ctr_heap_free( buffer ); return result; }
/** * CTRMessageSend * * Sends a message to a receiver object. */ ctr_object* ctr_send_message(ctr_object* receiverObject, char* message, long vlen, ctr_argument* argumentList) { char toParent = 0; ctr_object* me; ctr_object* methodObject; ctr_object* searchObject; ctr_argument* argCounter; ctr_argument* mesgArgument; ctr_object* result; ctr_object* (*funct)(ctr_object* receiverObject, ctr_argument* argumentList); int argCount; if (CtrStdError != NULL) return NULL; /* Error mode, ignore subsequent messages until resolved. */ methodObject = NULL; searchObject = receiverObject; if (vlen > 1 && message[0] == '`') { me = ctr_internal_object_find_property(ctr_contexts[ctr_context_id], ctr_build_string_from_cstring("me\0"), 0); if (searchObject == me) { toParent = 1; message = message + 1; vlen--; } } while(!methodObject) { methodObject = ctr_internal_object_find_property(searchObject, ctr_build_string(message, vlen), 1); if (methodObject && toParent) { toParent = 0; methodObject = NULL; } if (methodObject) break; if (!searchObject->link) break; searchObject = searchObject->link; } if (!methodObject) { argCounter = argumentList; argCount = 0; while(argCounter->next && argCount < 4) { argCounter = argCounter->next; argCount ++; } mesgArgument = CTR_CREATE_ARGUMENT(); mesgArgument->object = ctr_build_string(message, vlen); mesgArgument->next = argumentList; if (argCount == 0 || argCount > 2) { return ctr_send_message(receiverObject, "respondTo:", 10, mesgArgument); } else if (argCount == 1) { return ctr_send_message(receiverObject, "respondTo:with:", 15, mesgArgument); } else if (argCount == 2) { return ctr_send_message(receiverObject, "respondTo:with:and:", 19, mesgArgument); } } if (methodObject->info.type == CTR_OBJECT_TYPE_OTNATFUNC) { funct = methodObject->value.fvalue; result = funct(receiverObject, argumentList); } if (methodObject->info.type == CTR_OBJECT_TYPE_OTBLOCK) { result = ctr_block_run(methodObject, argumentList, receiverObject); } return result; }
/** * [File] end. * * Moves the file pointer to the end of the file. Use this in combination with * negative seek operations. * * Usage: * * file open: 'r'. * file end. * x := file seek: -10, readBytes: 10. * * The example above will read the last 10 bytes of the file. This is * accomplished by first moving the file pointer to the end of the file, * then putting it back 10 bytes (negative number), and then reading 10 * bytes. */ ctr_object* ctr_file_seek_end(ctr_object* myself, ctr_argument* argumentList) { int error; if (myself->value.rvalue == NULL) return myself; if (myself->value.rvalue->type != 1) return myself; error = fseek((FILE*)myself->value.rvalue->ptr, 0, SEEK_END); if (error) { CtrStdFlow = ctr_build_string_from_cstring("Seek end failed."); CtrStdFlow->info.sticky = 1; } return myself; }
/** * @internal * * Returns a string from the request, either for GET, POST or COOKIE. */ ctr_object* ctr_request_string(ctr_object* myself, ctr_argument* argumentList, CGI_varlist* varlist) { ctr_object* cgiVarObject; char* cgiVar; char* value; cgiVarObject = ctr_internal_cast2string(argumentList->object); cgiVar = ctr_heap_allocate_cstring( cgiVarObject ); value = (char*) CGI_lookup(varlist, (const char*)cgiVar); ctr_heap_free( cgiVar ); if (value == NULL) return CtrStdNil; return ctr_build_string_from_cstring(value); }
/** * [List] sort: [Block] * * Sorts the contents of an list using a sort block. * Uses qsort. * * In other languages: * Dutch: [Reeks] sorteer: [Codeblok] * Sorteert de reeks door de elementen door het codeblok te halen. */ ctr_object* ctr_array_sort(ctr_object* myself, ctr_argument* argumentList) { ctr_object* sorter = argumentList->object; if (sorter->info.type != CTR_OBJECT_TYPE_OTBLOCK) { CtrStdFlow = ctr_build_string_from_cstring( CTR_ERR_EXP_BLK ); CtrStdFlow->info.sticky = 1; return myself; } temp_sorter = sorter; temp_self = myself; qsort((myself->value.avalue->elements+myself->value.avalue->tail), myself->value.avalue->head-myself->value.avalue->tail, sizeof(ctr_object*), ctr_sort_cmp); return myself; }
void begin() { initFibers(); CTR_FIBER_CONTROLLER = ctr_internal_create_object(CTR_OBJECT_TYPE_OTEX); ctr_internal_create_func(CTR_FIBER_CONTROLLER, ctr_build_string_from_cstring("toString"), &ctr_fiber_tostring); ctr_internal_create_func(CTR_FIBER_CONTROLLER, ctr_build_string_from_cstring("new:"), &ctr_fiber_spawn); ctr_internal_create_func(CTR_FIBER_CONTROLLER, ctr_build_string_from_cstring("yield:"), &ctr_fiber_yield); //with value ctr_internal_create_func(CTR_FIBER_CONTROLLER, ctr_build_string_from_cstring("yield"), &ctr_fiber_yield); //without value ctr_internal_create_func(CTR_FIBER_CONTROLLER, ctr_build_string_from_cstring("yielded"), &ctr_fiber_yielded); ctr_internal_create_func(CTR_FIBER_CONTROLLER, ctr_build_string_from_cstring("waitForAll"), &ctr_fiber_join_all); ctr_internal_object_add_property(CTR_FIBER_CONTROLLER, ctr_build_string_from_cstring("fiberId"), ctr_build_number_from_float(-1), CTR_CATEGORY_PRIVATE_PROPERTY); ctr_internal_object_add_property(CtrStdWorld, ctr_build_string_from_cstring("Fiber"), CTR_FIBER_CONTROLLER, 0); }
/** * [File] append: [String] * * Appends content to a file. The file object responds to this message like it * responds to the write-message, however in this case the contents of the string * will be appended to the existing content inside the file. */ ctr_object* ctr_file_append(ctr_object* myself, ctr_argument* argumentList) { ctr_object* str = ctr_internal_cast2string(argumentList->object); ctr_object* path = ctr_internal_object_find_property(myself, ctr_build_string_from_cstring( "path" ), 0); ctr_size vlen; char* pathString; FILE* f; if (path == NULL) return myself; vlen = path->value.svalue->vlen; pathString = ctr_heap_allocate(vlen + 1); memcpy(pathString, path->value.svalue->value, vlen); memcpy(pathString+vlen,"\0",1); f = fopen(pathString, "ab+"); ctr_heap_free( pathString ); if (!f) { CtrStdFlow = ctr_build_string_from_cstring("Unable to open file.\0"); CtrStdFlow->info.sticky = 1; return CtrStdNil; } fwrite(str->value.svalue->value, sizeof(char), str->value.svalue->vlen, f); fclose(f); return myself; }
/** * [File] seek: [Number]. * * Moves the file pointer to the specified position in the file * (relative to the current position). * * Usage: * * file open: 'r', seek: 10. * * The example above opens a file for reading and moves the * pointer to position 10 (meaning 10 bytes from the beginning of the file). * The seek value may be negative. */ ctr_object* ctr_file_seek(ctr_object* myself, ctr_argument* argumentList) { int offset; int error; if (myself->value.rvalue == NULL) return myself; if (myself->value.rvalue->type != 1) return myself; offset = (long int) ctr_internal_cast2number(argumentList->object)->value.nvalue; error = fseek((FILE*)myself->value.rvalue->ptr, offset, SEEK_CUR); if (error) { CtrStdFlow = ctr_build_string_from_cstring("Seek failed."); CtrStdFlow->info.sticky = 1; } return myself; }
/** * [Array] at: [Index] * * Returns the element in the array at the specified index. * Note that the fisrt index of the array is index 0. * * Usage: * * fruits := Array <- 'apples' ; 'oranges' ; 'bananas'. * fruits at: 1. #returns 'oranges' */ ctr_object* ctr_array_get(ctr_object* myself, ctr_argument* argumentList) { ctr_object* getIndex = argumentList->object; int i; if (getIndex->info.type != CTR_OBJECT_TYPE_OTNUMBER) { printf("Index must be number.\n"); exit(1); } i = (int) getIndex->value.nvalue; if (myself->value.avalue->head <= i || i < 0) { CtrStdError = ctr_build_string_from_cstring("Index out of bounds.\0"); return CtrStdNil; } return *(myself->value.avalue->elements + i); }
/** * @internal * * Adds the Curl object into the world **/ void begin(){ ctr_object* CtrStdHandle = ctr_internal_create_object(CTR_OBJECT_TYPE_OTOBJECT); ctr_internal_create_func(CtrStdHandle, ctr_build_string_from_cstring( "new" ), &ctr_curl_new ); ctr_internal_create_func(CtrStdHandle, ctr_build_string_from_cstring( "nieuw" ), &ctr_curl_new ); ctr_internal_create_func(CtrStdHandle, ctr_build_string_from_cstring( "type" ), &ctr_curl_type ); ctr_internal_create_func(CtrStdHandle, ctr_build_string_from_cstring( "cleanup" ), &ctr_curl_cleanup ); ctr_internal_create_func(CtrStdHandle, ctr_build_string_from_cstring( "perform" ), &ctr_curl_perform ); ctr_internal_create_func(CtrStdHandle, ctr_build_string_from_cstring( "respondTo:and:"), &ctr_curl_respondto ); ctr_internal_object_add_property(CtrStdWorld, ctr_build_string_from_cstring( "Curl" ), CtrStdHandle, CTR_CATEGORY_PUBLIC_PROPERTY); CtrStdHandle->link = CtrStdObject; CtrStdHandle->info.sticky = 1; }
/** * [Program] env: [String] * * Returns the value of an environment variable. * * Usage: * * x := Command env: 'MY_PATH_VAR'. */ ctr_object* ctr_command_get_env(ctr_object* myself, ctr_argument* argumentList) { ctr_object* envVarNameObj; char* envVarNameStr; char* envVal; envVarNameObj = ctr_internal_cast2string(argumentList->object); envVarNameStr = malloc((envVarNameObj->value.svalue->vlen+1)*sizeof(char)); strncpy(envVarNameStr, envVarNameObj->value.svalue->value, envVarNameObj->value.svalue->vlen); *(envVarNameStr + (envVarNameObj->value.svalue->vlen)) = '\0'; envVal = getenv(envVarNameStr); if (envVal == NULL) { return CtrStdNil; } return ctr_build_string_from_cstring(envVal); }