summaryrefslogtreecommitdiff
path: root/gs/src/gp_gnrdl.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/src/gp_gnrdl.c')
-rw-r--r--gs/src/gp_gnrdl.c363
1 files changed, 363 insertions, 0 deletions
diff --git a/gs/src/gp_gnrdl.c b/gs/src/gp_gnrdl.c
new file mode 100644
index 000000000..e9bb66b59
--- /dev/null
+++ b/gs/src/gp_gnrdl.c
@@ -0,0 +1,363 @@
+/* Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This file is part of Aladdin Ghostscript.
+
+ Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author
+ or distributor accepts any responsibility for the consequences of using it,
+ or for whether it serves any particular purpose or works at all, unless he
+ or she says so in writing. Refer to the Aladdin Ghostscript Free Public
+ License (the "License") for full details.
+
+ Every copy of Aladdin Ghostscript must include a copy of the License,
+ normally in a plain ASCII text file named PUBLIC. The License grants you
+ the right to copy, modify and redistribute Aladdin Ghostscript, but only
+ under certain conditions described in the License. Among other things, the
+ License requires that the copyright notice and this notice be preserved on
+ all copies.
+ */
+
+
+/* GNU readline implementation */
+#include "ctype_.h"
+#include "string_.h"
+#include "malloc_.h"
+#include "memory_.h"
+#include <readline/readline.h>
+#include <readline/history.h>
+#include "ghost.h"
+#include "errors.h"
+#include "gp.h"
+#include "gsmalloc.h"
+#include "gsmemory.h"
+#include "gsstruct.h"
+#include "stream.h"
+#include "gxiodev.h"
+#include "idict.h"
+#include "iname.h"
+#include "iutil.h"
+#include "dstack.h"
+#include "ostack.h"
+
+/*
+ * Note: This code was contributed by a user: please contact
+ * Alexey Subbotin <A.Subbotin@lpi.ru> if you have questions.
+ */
+
+#define DEFAULT_BUFFER_SIZE 256
+#define BUFSIZE_INCR 128
+
+/* The file names can be set in the makefile if desired. */
+/*
+ * The prototypes for read_history and write_history are broken (they don't
+ * specify the file name strings as const), so we have to omit the const
+ * here too, or else add casts that will cause some compilers to complain.
+ */
+#ifndef GS_HISTFILE
+# define GS_HISTFILE ".gs_history"
+#endif
+private /*const*/ char *const GS_histfile = GS_HISTFILE;
+#ifndef RL_INITFILE
+# define RL_INITFILE "~/.inputrc"
+#endif
+private /*const*/ char *const RL_initfile = RL_INITFILE;
+
+/* initial key codes to make dictionary current_completion_dict
+ (may be changed to any key sequences by settings in init file) */
+#define RL_systemdict_keycode ('s' | 0x80 ) /* Alt-s */
+#define RL_userdict_keycode ('u' | 0x80 ) /* Alt-u */
+#define RL_currentdict_keycode ('c' | 0x80 ) /* Alt-c */
+#define RL_filenames_keycode ('f' | 0x80 ) /* Alt-f */
+#define RL_show_value_keycode ('i' | 0x80 ) /* Alt-i ( = info) */
+/* e.g.: "\C-x\C-u": complete-from-userdict */
+#define RL_systemdict_func "gs-complete-from-systemdict"
+#define RL_userdict_func "gs-complete-from-userdict"
+#define RL_currentdict_func "gs-complete-from-currentdict"
+#define RL_filenames_func "gs-complete-filenames"
+#define RL_show_value_func "gs-show-name-value"
+
+#define RL_init_buffsz DEFAULT_BUFFER_SIZE
+
+private const char *const ps_delimiters = " \n\t{}[]()/";
+
+#define is_regular(A) (strchr(ps_delimiters,(A)) == NULL)
+
+typedef enum {
+ compl_systemdict,
+ compl_userdict,
+ compl_currentdict,
+ compl_filenames
+} compl_t;
+
+typedef struct readline_data_s {
+ compl_t completion_type;
+ gs_memory_t *mem;
+ i_ctx_t *i_ctx_p;
+ /* current completion state */
+ int c_idx, c_len;
+} readline_data_t;
+gs_private_st_ptrs1(st_readline_data, readline_data_t, "readline_data_t",
+ readline_data_enum_ptrs, readline_data_reloc_ptrs, i_ctx_p);
+
+private readline_data_t *the_readline_data;
+
+private char *
+gs_readline_complete(char *text, int state)
+{
+ readline_data_t *const p = the_readline_data;
+ i_ctx_t *i_ctx_p = p->i_ctx_p;
+ ref *cdict;
+ ref eltp[2];
+
+ switch (p->completion_type) {
+ case compl_systemdict:
+ cdict = systemdict;
+ break;
+ case compl_userdict:
+ cdict = idict_stack.stack.bot + dstack_userdict_index;
+ break;
+ case compl_currentdict:
+ cdict = idict_stack.stack.p;
+ break;
+ case compl_filenames:
+ return (*filename_completion_function) (text, state);
+ default:
+ return NULL;
+ }
+ if (!state) {
+ p->c_len = strlen(text);
+ if (!(p->c_idx = dict_first(cdict)))
+ return NULL;
+ }
+ while ((p->c_idx = dict_next(cdict, p->c_idx, eltp)) >= 0) {
+ const byte *rname = eltp->value.refs->value.const_bytes;
+ uint rlen = eltp->value.refs->tas.rsize >> 2;
+
+ if (rname && !strncmp((const char *)rname, text, p->c_len)) {
+ char *name = (char *)malloc(rlen + 1);
+
+ memcpy(name, rname, rlen);
+ name[rlen] = 0;
+ return name;
+ }
+ }
+ return NULL;
+}
+
+private int
+rl_systemdict_compl(int count, int key)
+{
+ readline_data_t *const p = the_readline_data;
+
+ p->completion_type = compl_systemdict;
+ return 0;
+}
+private int
+rl_userdict_compl(int count, int key)
+{
+ readline_data_t *const p = the_readline_data;
+
+ p->completion_type = compl_userdict;
+ return 0;
+}
+private int
+rl_currentdict_compl(int count, int key)
+{
+ readline_data_t *const p = the_readline_data;
+
+ p->completion_type = compl_currentdict;
+ return 0;
+}
+private int
+rl_filenames_compl(int count, int key)
+{
+ readline_data_t *const p = the_readline_data;
+
+ p->completion_type = compl_filenames;
+ return 0;
+}
+
+private int
+rl_show_name_value(int count, int key)
+{
+ readline_data_t *const p = the_readline_data;
+ i_ctx_t *i_ctx_p = p->i_ctx_p;
+ int i = rl_point;
+ char *c = rl_line_buffer + rl_point;
+ ref nref;
+ ref *pvref;
+
+ while (is_regular(*c) && i < rl_end) {
+ c++;
+ i++;
+ }
+ while (!is_regular(*c) && i) {
+ c--;
+ i--;
+ }
+ if (!is_regular(*c))
+ return 0;
+ while (is_regular(*c) && c > rl_line_buffer)
+ c--;
+ if (!is_regular(*c))
+ c++;
+ i += ((rl_line_buffer - c) + 1);
+ /*
+ * Now the name to be looked up extends from c through c + i - 1.
+ */
+ if (name_ref((const byte *)c, (uint)i, &nref, -1) < 0 ||
+ (pvref = dict_find_name(&nref)) == 0
+ )
+ puts("\nnot found");
+ else {
+#define MAX_CVS 128
+ char str[MAX_CVS];
+ const byte *pchars = (const byte *)str;
+ uint len;
+ int code = obj_cvp(pvref, (byte *)str, MAX_CVS, &len, 1, 0);
+
+ putchar('\n');
+ if (code < 0) {
+ code = obj_string_data(pvref, &pchars, &len);
+ if (code >= 0)
+ switch (r_type(pvref)) {
+ case t_string:
+ putchar('(');
+ fwrite(pchars, 1, len, stdout);
+ pchars = (const byte *)")", len = 1;
+ break;
+ case t_name:
+ if (!r_has_attr(pvref, a_executable))
+ putchar('/');
+ break;
+ default:
+ code = obj_cvs(pvref, (byte *)str, MAX_CVS, &len, &pchars);
+ }
+ }
+ if (code < 0)
+ puts("-error-");
+ else {
+ fwrite(pchars, 1, len, stdout);
+ putchar('\n');
+ }
+ }
+ rl_on_new_line();
+ rl_forced_update_display();
+ return 0;
+}
+
+
+int
+gp_readline_init(void **preadline_data, gs_memory_t * mem)
+{
+ readline_data_t *p;
+
+ using_history();
+ read_history(GS_histfile);
+
+ p = (readline_data_t *)
+ gs_alloc_struct_immovable(mem, readline_data_t, &st_readline_data,
+ "gp_readline_init(readline structure)");
+ if (!p)
+ return_error(e_VMerror);
+ gs_register_struct_root(mem, NULL, (void **)&the_readline_data,
+ "the_readline_data");
+
+ p->mem = mem;
+ p->i_ctx_p = 0; /* only meaningful when reading a line */
+ p->c_idx = p->c_len = 0;
+ p->completion_type = compl_systemdict;
+
+ rl_add_defun(RL_systemdict_func, rl_systemdict_compl, RL_systemdict_keycode);
+ rl_add_defun(RL_userdict_func, rl_userdict_compl, RL_userdict_keycode);
+ rl_add_defun(RL_currentdict_func, rl_currentdict_compl, RL_currentdict_keycode);
+ rl_add_defun(RL_filenames_func, rl_filenames_compl, RL_filenames_keycode);
+ rl_add_defun(RL_show_value_func, rl_show_name_value, RL_show_value_keycode);
+
+ rl_read_init_file(RL_initfile);
+
+ /*
+ * The GNU readline API is pretty badly broken, as indicated in the
+ * following comments about the following statics that it declares.
+ * (Just to begin with, using statics at all is broken design.)
+ */
+ /*
+ * rl_completion_entry_function should be declared with the same
+ * prototype as gs_readline_complete; however, it's declared as
+ * Function *, where Function is int ()();
+ */
+ rl_completion_entry_function = (Function *)gs_readline_complete;
+ /*
+ * rl_basic_word_break_characters should declared as const char *;
+ * however, it's declared as char *.
+ */
+ rl_basic_word_break_characters = (char *)ps_delimiters;
+
+ *preadline_data = p;
+ the_readline_data = p; /* HACK */
+ return 0;
+}
+
+void
+gp_readline_finit(void *data)
+{
+ readline_data_t *dp = (readline_data_t *)data;
+
+ if (dp)
+ gs_free_object(dp->mem, dp, "gp_readline_finit(readline structure)");
+ write_history(GS_histfile);
+ clear_history();
+}
+
+int
+gp_readline(stream *ignore_s_in, stream *ignore_s_out,
+ void *readline_data,
+ gs_const_string *ignore_prompt, gs_string * buf,
+ gs_memory_t * bufmem, uint * pcount, bool *pin_eol,
+ bool (*is_stdin)(P1(const stream *)))
+{
+ /* HACK: ignore readline_data, which is currently not supplied. */
+ readline_data_t *p = the_readline_data;
+ char *c;
+ char prompt[64];
+ uint count;
+ gx_io_device *indev = gs_findiodevice((const byte *)"%stdin", 6);
+ /* HACK: get the context pointer from the IODevice. See ziodev.c. */
+ i_ctx_t *i_ctx_p = (i_ctx_t *)indev->state;
+
+ p->i_ctx_p = i_ctx_p;
+ count = ref_stack_count(&o_stack);
+ if (count > 2)
+ sprintf(prompt, "GS<%d>", count - 2);
+ else
+ strcpy(prompt, "GS>");
+
+ if ((c = readline(prompt)) == NULL) {
+ *pcount = 0;
+ *pin_eol = false;
+ return EOFC;
+ } else {
+ count = strlen(c) + 1;
+ if (*c)
+ add_history(c);
+ if (count >= buf->size) {
+ if (!bufmem)
+ return ERRC; /* no better choice */
+ {
+ uint nsize = count + BUFSIZE_INCR;
+ byte *nbuf = gs_resize_string(bufmem, buf->data, buf->size,
+ nsize,
+ "gp_readline(resize buffer)");
+
+ if (nbuf == 0)
+ return ERRC; /* no better choice */
+ buf->data = nbuf;
+ buf->size = nsize;
+ }
+ }
+ memcpy(buf->data, c, count);
+ free(c);
+ *pin_eol = true;
+ *pcount = count;
+ return 0;
+ }
+}