| 
									
										
										
										
											2023-10-10 08:40:16 +00:00
										 |  |  | #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); \ | 
					
						
							| 
									
										
										
										
											2023-11-07 03:08:55 +00:00
										 |  |  |   return 1; \ | 
					
						
							| 
									
										
										
										
											2023-10-10 08:40:16 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #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) ; | 
					
						
							|  |  |  | } |