132 lines
3.8 KiB
C
132 lines
3.8 KiB
C
|
#include "stplugin.h"
|
||
|
#include "duktape.h"
|
||
|
|
||
|
#define DOIT(cmd) duk_eval_string_noresult(ctx, cmd);
|
||
|
|
||
|
#define FAIL_DUK(cmd) { \
|
||
|
const char *errmsg = duk_safe_to_string(ctx, -1); \
|
||
|
duk_destroy_heap(ctx); \
|
||
|
snprintf(failbuf, 255, "error in %s: %s", cmd, errmsg); \
|
||
|
SF_error(failbuf); \
|
||
|
return NULL; \
|
||
|
}
|
||
|
|
||
|
#define FAIL_LOAD { \
|
||
|
duk_push_undefined(ctx); \
|
||
|
SF_error("Error in load_file"); \
|
||
|
return 1; \
|
||
|
}
|
||
|
|
||
|
static char *read_file(const char *filename, size_t *sz) {
|
||
|
FILE *f = fopen(filename, "rb");
|
||
|
if(!f) return NULL;
|
||
|
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
|
||
|
char *buf = (char *)malloc(fsize * sizeof(char));
|
||
|
*sz = fread((void *) buf, 1, fsize, f);
|
||
|
fclose(f);
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
static duk_int_t eval_file(duk_context *ctx, const char *filename) {
|
||
|
size_t len; char *buf = read_file(filename, &len);
|
||
|
if(!buf) FAIL_LOAD
|
||
|
|
||
|
duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len);
|
||
|
duk_int_t retval = duk_peval(ctx);
|
||
|
duk_pop(ctx);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
static duk_int_t load_file(duk_context *ctx, const char *filename, const char *var) {
|
||
|
size_t len; char *buf = read_file(filename, &len);
|
||
|
if(!buf) FAIL_LOAD
|
||
|
|
||
|
duk_push_external_buffer(ctx);
|
||
|
duk_config_buffer(ctx, -1, buf, len);
|
||
|
duk_put_global_string(ctx, var);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static duk_int_t save_file(duk_context *ctx, const char *filename, const char *var) {
|
||
|
duk_get_global_string(ctx, var);
|
||
|
duk_size_t sz;
|
||
|
char *buf = (char *)duk_get_buffer_data(ctx, -1, &sz);
|
||
|
|
||
|
if(!buf) return 1;
|
||
|
FILE *f = fopen(filename, "wb"); fwrite(buf, 1, sz, f); fclose(f);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
STDLL stata_call(int argc, char *argv[])
|
||
|
{
|
||
|
duk_int_t res = 0;
|
||
|
char failbuf[255];
|
||
|
|
||
|
/* initialize duktape */
|
||
|
duk_context *ctx = duk_create_heap_default();
|
||
|
/* duktape does not expose a standard "global" by default */
|
||
|
DOIT("var global = (function(){ return this; }).call(null);")
|
||
|
|
||
|
/* load SheetJS library */
|
||
|
res = eval_file(ctx, "shim.min.js");
|
||
|
if(res != 0) FAIL_DUK("shim load")
|
||
|
res = eval_file(ctx, "xlsx.full.min.js");
|
||
|
if(res != 0) FAIL_DUK("library load")
|
||
|
|
||
|
/* print SheetJS version number */
|
||
|
//duk_eval_string(ctx, "XLSX.version");
|
||
|
//char verbuf[255];
|
||
|
//snprintf(verbuf, 255, "SheetJS library version: %s\n", duk_get_string(ctx, -1));
|
||
|
//SF_display(verbuf);
|
||
|
|
||
|
/* read file */
|
||
|
res = load_file(ctx, argv[0], "buf");
|
||
|
if(res != 0) FAIL_DUK("file load")
|
||
|
|
||
|
/* parse workbook */
|
||
|
DOIT("wb = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});");
|
||
|
|
||
|
/* print CSV */
|
||
|
duk_eval_string(ctx, "wb.SheetNames.length");
|
||
|
duk_uint_t wscnt = duk_get_uint(ctx, -1);
|
||
|
duk_pop(ctx);
|
||
|
|
||
|
/* if argument 2 is "verbose", print CSV contents */
|
||
|
if(argc>1 && !strncmp(argv[1], "verbose", 7)) for(uint32_t wsidx = 0; wsidx < wscnt; ++wsidx) {
|
||
|
/* select n-th worksheet */
|
||
|
char wsbuf[80];
|
||
|
snprintf(wsbuf, 80, "ws = wb.Sheets[wsname = wb.SheetNames[%d]]", wsidx); \
|
||
|
DOIT(wsbuf);
|
||
|
|
||
|
duk_eval_string(ctx, "wsname");
|
||
|
char namebuf[60];
|
||
|
snprintf(namebuf, 60, "Worksheet %d Name: %s\n", wsidx, duk_get_string(ctx, -1));
|
||
|
duk_pop(ctx);
|
||
|
SF_display(namebuf);
|
||
|
|
||
|
/* convert to CSV */
|
||
|
duk_eval_string(ctx, "XLSX.utils.sheet_to_csv(ws)");
|
||
|
const char *csv = duk_get_string(ctx, -1);
|
||
|
|
||
|
/* print each row in a separate line */
|
||
|
char *tok = strtok(csv, "\n");
|
||
|
while(tok != NULL) {
|
||
|
SF_display(tok);
|
||
|
SF_display("\n");
|
||
|
tok = strtok(NULL, "\n");
|
||
|
}
|
||
|
duk_pop(ctx);
|
||
|
}
|
||
|
|
||
|
/* write to sheetjs.tmp.xlsx */
|
||
|
DOIT("newbuf = (XLSX.write(wb, {type:'array', bookType:'xlsx'}));");\
|
||
|
res = save_file(ctx, "sheetjs.tmp.xlsx", "newbuf");\
|
||
|
if(res != 0) FAIL_DUK("save sheetjsw.xlsx")
|
||
|
|
||
|
SF_display("\n");
|
||
|
SF_display("Saved to `sheetjs.tmp.xlsx`\n");
|
||
|
SF_display("{stata import excel \"sheetjs.tmp.xlsx\", firstrow} will read the first sheet and use headers\n");
|
||
|
SF_display("for more help, see {help import excel}\n");
|
||
|
return(0) ;
|
||
|
}
|