--- title: C + Duktape pagination_prev: demos/bigdata/index pagination_next: solutions/input --- import current from '/version.js'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; Duktape is an embeddable JS engine written in C. It has been ported to a number of exotic architectures and operating systems. The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone) can be parsed and evaluated in a Duktape context. ## Integration Details _Initialize Duktape_ Duktape does not provide a `global` variable. It can be created in one line: ```c /* initialize */ duk_context *ctx = duk_create_heap_default(); /* duktape does not expose a standard "global" by default */ // highlight-next-line duk_eval_string_noresult(ctx, "var global = (function(){ return this; }).call(null);"); ``` _Load SheetJS Scripts_ The shim and main libraries can be loaded by reading the scripts from the file system and evaluating in the Duktape context: ```c /* simple wrapper to read the entire script file */ static duk_int_t eval_file(duk_context *ctx, const char *filename) { size_t len; /* read script from filesystem */ FILE *f = fopen(filename, "rb"); if(!f) { duk_push_undefined(ctx); perror("fopen"); return 1; } long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); } char *buf = (char *)malloc(fsize * sizeof(char)); len = fread((void *) buf, 1, fsize, f); fclose(f); if(!buf) { duk_push_undefined(ctx); perror("fread"); return 1; } // highlight-start /* load script into the context */ duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len); /* eval script */ duk_int_t retval = duk_peval(ctx); /* cleanup */ duk_pop(ctx); // highlight-end return retval; } // ... duk_int_t res = 0; if((res = eval_file(ctx, "shim.min.js")) != 0) { /* error handler */ } if((res = eval_file(ctx, "xlsx.full.min.js")) != 0) { /* error handler */ } ``` To confirm the library is loaded, `XLSX.version` can be inspected: ```c /* get version string */ duk_eval_string(ctx, "XLSX.version"); printf("SheetJS library version %s\n", duk_get_string(ctx, -1)); duk_pop(ctx); ``` ### Reading Files Duktape supports `Buffer` natively but should be sliced before processing. Assuming `buf` is a C byte array, with length `len`, this snippet parses data: ```c /* load C char array and save to a Buffer */ duk_push_external_buffer(ctx); duk_config_buffer(ctx, -1, buf, len); duk_put_global_string(ctx, "buf"); /* parse with SheetJS */ duk_eval_string_noresult("workbook = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});"); ``` `workbook` will be a variable in the JS environment that can be inspected using the various SheetJS API functions. ### Writing Files `duk_get_buffer_data` can pull `Buffer` object data into the C code: ```c /* write with SheetJS using type: "array" */ duk_eval_string(ctx, "XLSX.write(workbook, {type:'array', bookType:'xlsx'})"); /* pull result back to C */ duk_size_t sz; char *buf = (char *)duk_get_buffer_data(ctx, -1, sz); /* discard result in duktape */ duk_pop(ctx); ``` The resulting `buf` can be written to file with `fwrite`. ## Complete Example :::note This demo was tested in the following deployments: | Architecture | Version | Date | |:-------------|:--------|:-----------| | `darwin-x64` | `2.7.0` | 2023-07-24 | | `darwin-arm` | `2.7.0` | 2023-06-05 | | `win10-x64` | `2.7.0` | 2023-07-24 | | `win11-arm` | `2.7.0` | 2023-09-26 | | `linux-x64` | `2.7.0` | 2023-09-22 | | `linux-arm` | `2.7.0` | 2023-08-30 | ::: This program parses a file and prints CSV data from the first worksheet. It also generates an XLSB file and writes to the filesystem. The [flow diagram is displayed after the example steps](#flow-diagram) 0) Download and extract Duktape: ```bash mkdir sheetjs-duk cd sheetjs-duk curl -LO https://duktape.org/duktape-2.7.0.tar.xz tar -xJf duktape-2.7.0.tar.xz mv duktape-2.7.0/src/*.{c,h} . ``` 1) Download the SheetJS Standalone script, shim script and test file. Move all three files to the project directory: