1 module duk_extras.print_alert;
2 
3 /*
4  *  Duktape 1.x compatible print() and alert() bindings.
5  */
6 
7 import core.stdc.stdio;
8 import core.stdc..string;
9 
10 import duktape;
11 
12 enum DUK_PRINT_ALERT_FLUSH = true;  /* Flush after stdout/stderr write (Duktape 1.x: yes) */
13 enum DUK_PRINT_ALERT_SMALL = false; /* Prefer smaller footprint (but slower and more memory churn) */
14 
15 private duk_ret_t duk__print_alert_helper_small(duk_context *ctx, FILE *fh) {
16     duk_idx_t nargs;
17     const(duk_uint8_t) *buf;
18     duk_size_t sz_buf;
19 
20     nargs = duk_get_top(ctx);
21 
22     /* If argument count is 1 and first argument is a buffer, write the buffer
23     * as raw data into the file without a newline; this allows exact control
24     * over stdout/stderr without an additional entrypoint (useful for now).
25     * Otherwise current print/alert semantics are to ToString() coerce
26     * arguments, join them with a single space, and append a newline.
27     */
28 
29     if (nargs == 1 && duk_is_buffer(ctx, 0)) {
30         buf = cast(const(duk_uint8_t) *) duk_get_buffer(ctx, 0, &sz_buf);
31         fwrite(cast(const void *) buf, 1, cast(size_t) sz_buf, fh);
32     } else {
33         duk_push_string(ctx, " ");
34         duk_insert(ctx, 0);
35         duk_concat(ctx, nargs);
36         fprintf(fh, "%s\n", duk_require_string(ctx, -1));
37     }
38 
39     static if (DUK_PRINT_ALERT_FLUSH) {
40         fflush(fh);
41     }
42 
43     return 0;
44 }
45 
46 /* Faster, less churn, higher footprint option. */
47 private duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) {
48     duk_idx_t nargs;
49     const(duk_uint8_t) *buf;
50     duk_size_t sz_buf;
51     const char nl = cast(const char) '\n';
52     duk_uint8_t[256] buf_stack;
53 
54     nargs = duk_get_top(ctx);
55 
56     /* If argument count is 1 and first argument is a buffer, write the buffer
57     * as raw data into the file without a newline; this allows exact control
58     * over stdout/stderr without an additional entrypoint (useful for now).
59     * Otherwise current print/alert semantics are to ToString() coerce
60     * arguments, join them with a single space, and append a newline.
61     */
62 
63     if (nargs == 1 && duk_is_buffer(ctx, 0)) {
64         buf = cast(const(duk_uint8_t) *) duk_get_buffer(ctx, 0, &sz_buf);
65     } else if (nargs > 0) {
66         duk_idx_t i;
67         duk_size_t sz_str;
68         const(duk_uint8_t) *p_str;
69         duk_uint8_t *p;
70 
71         sz_buf = cast(duk_size_t) nargs;  /* spaces (nargs - 1) + newline */
72         for (i = 0; i < nargs; i++) {
73             duk_to_lstring(ctx, i, &sz_str);
74             sz_buf += sz_str;
75         }
76 
77         if (sz_buf <= buf_stack.sizeof) {
78             p = cast(duk_uint8_t *) buf_stack;
79         } else {
80             p = cast(duk_uint8_t *) duk_push_fixed_buffer(ctx, sz_buf);
81         }
82 
83         buf = cast(const(duk_uint8_t) *) p;
84         for (i = 0; i < nargs; i++) {
85             p_str = cast(const(duk_uint8_t) *) duk_get_lstring(ctx, i, &sz_str);
86             memcpy(cast(void *) p, cast(const void *) p_str, sz_str);
87             p += sz_str;
88             *p++ = cast(duk_uint8_t) (i == nargs - 1 ? '\n' : ' ');
89         }
90     } else {
91         buf = cast(const(duk_uint8_t) *) &nl;
92         sz_buf = 1;
93     }
94 
95     /* 'buf' contains the string to write, 'sz_buf' contains the length
96     * (which may be zero).
97     */
98 
99     if (sz_buf > 0) {
100         fwrite(cast(const void *) buf, 1, cast(size_t) sz_buf, fh);
101         static if (DUK_PRINT_ALERT_FLUSH) {
102             fflush(fh);
103         }
104     }
105 
106     return 0;
107 }
108 
109 extern(C) private duk_ret_t duk__print(duk_context *ctx) {
110     if (DUK_PRINT_ALERT_SMALL)
111         return duk__print_alert_helper_small(ctx, stdout);
112     else
113 	    return duk__print_alert_helper(ctx, stdout);
114 }
115 
116 extern(C) private duk_ret_t duk__alert(duk_context *ctx) {
117     if (DUK_PRINT_ALERT_SMALL)
118 	    return duk__print_alert_helper_small(ctx, stderr);
119     else
120         return duk__print_alert_helper(ctx, stderr);
121 }
122 
123 void duk_print_alert_init(duk_context *ctx, duk_uint_t flags) {
124 	//(void) flags;  /* unused at the moment */
125 
126 	/* XXX: use duk_def_prop_list(). */
127 	duk_push_global_object(ctx);
128 	duk_push_string(ctx, "print");
129 	duk_push_c_function(ctx, &duk__print, DUK_VARARGS);
130 	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE);
131 	duk_push_string(ctx, "alert");
132 	duk_push_c_function(ctx, &duk__alert, DUK_VARARGS);
133 	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE);
134 	duk_pop(ctx);
135 }