/* -*- mode: C; mode: fold; -*- */ /* Copyright (c) 1992, 1998, 2000, 2002, 2003, 2004, 2005, 2006 John E. Davis * This file is part of JED editor library source. * * You may distribute this file under the terms the GNU General Public * License. See the file COPYING for more information. */ #include "config.h" #include "jed-feat.h" /*{{{ Include Files */ #ifdef MSWINDOWS # ifndef __WIN32__ # include # else # include # endif #endif #include #ifdef HAVE_SYS_WAIT_H # include # include #endif #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif #include #include "jdmacros.h" #include #ifdef IBMPC_SYSTEM # include #endif #include #include "buffer.h" #include "keymap.h" #include "file.h" #include "ins.h" #include "ledit.h" #include "screen.h" #include "window.h" #include "display.h" #include "search.h" #include "misc.h" #include "replace.h" #include "paste.h" #include "sysdep.h" #include "cmds.h" #include "text.h" #include "hooks.h" #include "undo.h" #include "menu.h" #include "userinfo.h" #include "indent.h" #ifdef __WIN32__ # include "win32.h" #endif #define JED_PROMPT "S-Lang>" #if JED_HAS_SUBPROCESSES # include "jprocess.h" #endif /*}}}*/ Buffer *MiniBuffer; Buffer *The_MiniBuffer; /* this never gets deleted since we may need it */ Window_Type *The_MiniWindow; static Buffer *Last_Buffer; /* extern char *get_cwd(void); */ int Ignore_Beep = 0; int MiniBuffer_Active = 0; char *Jed_Library; /* Do not change this value without changing file_findfirst/next. */ #define JED_MAX_COMPLETION_LEN JED_MAX_PATH_LEN static int (*complete_open)(char *); static int (*complete_next)(char *); static int Buf_List_Len; Buffer *Buf_Context; static char *Expect_File_Error = "Expecting filename."; static int next_bufflist(char *buf) /*{{{*/ { Buffer *tthis; while (1) { tthis = Buf_Context; if (tthis == MiniBuffer) return(0); Buf_Context = Buf_Context->next; if ((Buf_List_Len == 0) #if JED_FILE_PRESERVE_CASE || (0 == strncmp (buf, tthis->name, Buf_List_Len)) #else || (0 == jed_case_strncmp (buf, tthis->name, Buf_List_Len)) #endif ) { if (*tthis->name == ' ') continue; /* internal buffers */ safe_strcpy (buf, tthis->name, JED_MAX_COMPLETION_LEN); return 1; } } } /*}}}*/ static int open_bufflist(char *buf) /*{{{*/ { if ((Buf_Context = MiniBuffer) == NULL) return(0); Buf_Context = Buf_Context->next; Buf_List_Len = strlen(buf); return next_bufflist(buf); } /*}}}*/ char *what_buffer() /*{{{*/ { return (CBuf->name); } /*}}}*/ int bufferp(char *name) /*{{{*/ { if (NULL == find_buffer(name)) return(0); else return(1); } /*}}}*/ int insert_buffer_name(char *name) /*{{{*/ { Buffer *buf; if (NULL != (buf = find_buffer(name))) { insert_buffer(buf); return(1); } else msg_error("Unable to find buffer."); return(0); } /*}}}*/ static Buffer *find_scratch_buffer (void) /*{{{*/ { char *sname = "*scratch*"; Buffer *scratch; scratch = find_buffer (sname); if (NULL == scratch) scratch = make_buffer (sname, CBuf->dir, NULL); return scratch; } /*}}}*/ /* Try to find any other buffer but kill_buf such that the buffer is not * visible, not scratch and not buried. */ static Buffer *find_non_visible_buffer (Buffer *kill_buf) /*{{{*/ { Buffer *buf, *scratch; buf = kill_buf->next; scratch = find_scratch_buffer (); while ((buf != kill_buf) && ((*buf->name == ' ') || (buf->flags & BURIED_BUFFER) || (buf == scratch) || (buffer_visible(buf)))) { buf = buf->next; } if ((buf == kill_buf) || (buf->flags & BURIED_BUFFER)) buf = scratch; return buf; } /*}}}*/ int kill_buffer_cmd(char *name) /*{{{*/ { Buffer *this_buf, *kill_buf, *scratch, *buf; Window_Type *w; int do_kill; if (NULL == (kill_buf = find_buffer(name))) { msg_error("Buffer does not exist."); return(0); } /* Do not allow the minibuffer to be deleted. Otherwise, the whole * minibuffer creation/selection needs to be cleaned up. */ if (kill_buf == The_MiniBuffer) return 0; this_buf = CBuf; switch_to_buffer(kill_buf); #if JED_HAS_SUBPROCESSES if (kill_buf->locked) { erase_buffer (); switch_to_buffer (this_buf); return 0; } #endif do_kill = 1; if ((*name != ' ') && (kill_buf->flags & BUFFER_MODIFIED)) { jed_vmessage (1, "Buffer %s modified. (K)ill, (S)ave first, (A)bort:", name); /* This does not go through keyboard macro routines * on purpose! */ switch (my_getkey()) { case 'k': case 'K': do_kill = 1; break; case 's': case 'S': do_kill = 2; break; default: msg_error("Aborted."); return(0); } clear_message (); } if (do_kill == 2) { jed_save_buffer_as_cmd (); if (SLang_get_error ()) { switch_to_buffer(this_buf); return(0); } } /* if it is the scratch buffer, just erase it since we are going to recreate it anyway. */ scratch = find_scratch_buffer (); if (kill_buf == scratch) { erase_buffer(); #if JED_HAS_SUBPROCESSES if (kill_buf->subprocess) jed_kill_process (kill_buf->subprocess - 1); kill_buf->subprocess = 0; #endif switch_to_buffer(this_buf); return 1; } buf = find_non_visible_buffer (kill_buf); /* search through windows looking for the buffer and replace it */ w = JWindow; do { if (kill_buf == JWindow->buffer) { touch_window_hard (JWindow, 0); window_buffer(buf); buf = find_non_visible_buffer (kill_buf); } JWindow = JWindow->next; } while (w != JWindow); if (kill_buf == Last_Buffer) Last_Buffer = NULL; if (kill_buf == this_buf) this_buf = buf; switch_to_buffer(this_buf); delete_buffer(kill_buf); return 1; } /*}}}*/ int jed_save_buffer_to_file (char *dir, char *file) { int status; char *dirfile; if ((file == NULL) || (*file == 0)) { file = CBuf->file; if (*file == 0) { msg_error ("Filename Required."); return -1; } } if (NULL == (dirfile = jed_dir_file_merge (dir, file))) return -1; if (file_changed_on_disk (CBuf, dirfile) > 0) { if (0 == jed_get_yes_no("File changed on disk. Save anyway?")) { SLfree (dirfile); return -1; } } if (-1 == jed_va_run_hooks ("_jed_save_buffer_before_hooks", JED_HOOKS_RUN_ALL, 1, dirfile)) { auto_save_buffer (CBuf); SLfree (dirfile); return -1; } if (((status = write_file_with_backup (dirfile)) >= 0) && (Batch != 2)) jed_vmessage (0, "Wrote %d lines to %s", status, dirfile); if (status < 0) { jed_verror ("Error writing file %s", dirfile); SLfree (dirfile); return -1; } CBuf->flags |= AUTO_SAVE_BUFFER; CBuf->hits = 0; #ifdef UNDO_HAS_REDO update_undo_unchanged (); #endif visit_file (dir, file); if (-1 == jed_va_run_hooks ("_jed_save_buffer_after_hooks", JED_HOOKS_RUN_ALL, 1, dirfile)) { SLfree (dirfile); return -1; } SLfree (dirfile); return 0; } int jed_save_buffer_as (char *filestr) { char *dir, *file; int status; filestr = jed_standardize_filename (filestr); if (-1 == jed_dirfile_to_dir_file (filestr, &dir, &file)) { SLfree (filestr); return -1; } status = jed_save_buffer_to_file (dir, file); SLfree (filestr); SLfree (dir); SLfree (file); if (status == -1) return status; /* This function is an intrinsic and is documented to return the * number of lines written out. * */ return 0; } int jed_save_buffer_cmd (void) { if ((CBuf->file == NULL) || (CBuf->file[0] == 0)) return jed_save_buffer_as_cmd (); return jed_save_buffer_to_file (CBuf->dir, CBuf->file); } #if defined(__BORLANDC__) && !defined(MSWINDOWS) int show_memory() /*{{{*/ { struct farheapinfo hi; char *c; unsigned long total = 0, core, used = 0; unsigned long max = 0; int i = 0; hi.ptr = NULL; if (farheapcheck() == _HEAPCORRUPT) c = "corrupt"; else c = "ok"; while (farheapwalk(&hi) == _HEAPOK) { if (hi.in_use) used += hi.size; else { total += hi.size; if (hi.size > max) max = hi.size; i++; } } core = farcoreleft(); jed_vmessage (0, "used:%lu, core:%lu, free:%lu, grand:%lu, max:%lu, frag:%d (%s)", used, core, total, core + total, max, i, c); return 0; } /*}}}*/ #endif #ifdef MSWINDOWS int show_memory (void) /*{{{*/ { #ifndef __WIN32__ MEMMANINFO mmi; if (MemManInfo(&mmi)) { jed_vmessage (0, "tot pages: %lu, free pages: %lu", mmi.dwTotalLinearSpace, mmi.dwFreeLinearSpace); } #else MEMORYSTATUS mst; GlobalMemoryStatus(&mst); jed_vmessage (0, "avail space: %lu, tot phys space: %lu, avail phys space: %lu", mst.dwAvailPhys + mst.dwAvailPageFile, mst.dwTotalPhys, mst.dwAvailPhys); #endif return 0; } /*}}}*/ #endif #ifdef __WATCOMC__ #include int show_memory() /*{{{*/ { jed_vmessage (0, "avail mem: %lu, tot phys pgs: ???, free lin space: %lu", _memavl (), _memmax ()); return 0; } /*}}}*/ #endif #ifdef __GO32__ #include int show_memory() /*{{{*/ { unsigned long mem; _go32_dpmi_meminfo info; _go32_dpmi_get_free_memory_information(&info); if ((long)info.available_physical_pages != -1L) mem = info.available_physical_pages * 4096UL; else mem = info.available_memory; jed_vmessage (0, "avail mem: %lu, tot phys pgs: %lu, free lin space: %lu", mem, info.total_physical_pages, info.free_linear_space); return 0; } /*}}}*/ #endif int set_buffer(char *name) /*{{{*/ { Buffer *buf; if ((name == NULL) || (*name == 0)) { msg_error("set_buffer: Buffer name is NULL"); return (0); } /* Last_Buffer = CBuf; */ if (NULL == (buf = find_buffer(name))) buf = make_buffer (name, CBuf->dir, NULL); switch_to_buffer(buf); return 1; } /*}}}*/ int jed_get_mini_response (char *s) { int ch; int len; if (Batch) { if (EOF == fputs (s, stdout)) exit_error ("Failed to write to stdout", 0); return jed_getkey (); } len = strlen (s); if (len > Jed_Num_Screen_Cols) s += (len - Jed_Num_Screen_Cols); MiniBuf_Get_Response_String = s; update (NULL, 0, 0, 0); ch = jed_getkey (); MiniBuf_Get_Response_String = NULL; return ch; } static char *strcat_malloc (char *a, char *b) { unsigned int len; char *c; len = strlen (a); if (NULL == (c = SLmalloc (len + strlen (b) + 1))) return NULL; strcpy (c, a); strcpy (c + len, b); return c; } int jed_get_y_n (char *question) { int ans; char *yn_quest; if (NULL == (yn_quest = strcat_malloc (question, "? (y/n)"))) return -1; ans = jed_get_mini_response (yn_quest); SLfree (yn_quest); if ((ans == 'y') || (ans == 'Y')) return 1; if ((ans == 'n') || (ans == 'N')) return 0; if (SLKeyBoard_Quit || (SLang_get_error () == SL_USER_BREAK)) return -1; jed_beep (); flush_input (); return jed_get_yes_no (question); } int jed_vget_y_n (char *fmt, char *arg) { char msg [1024]; if (arg == NULL) { arg = fmt; fmt = "%s"; } SLsnprintf (msg, sizeof (msg), fmt, arg); return jed_get_y_n (msg); } int jed_get_yes_no (char *question) /*{{{*/ { char *yn_quest; char *tmp; int n; if (NULL == (yn_quest = strcat_malloc (question, "? (yes or no)"))) return -1; while (1) { if (NULL == (tmp = read_from_minibuffer(yn_quest, 0, NULL, &n))) { SLfree (yn_quest); return -1; } if (!strcmp(tmp, "yes")) { SLfree(tmp); SLfree (yn_quest); return 1; } if (!strcmp(tmp, "no")) { SLfree(tmp); SLfree (yn_quest); return 0; } msg_error("Answer `yes' or `no'."); SLfree(tmp); } } /*}}}*/ int find_file_cmd (char *filestr) /* filestr is const ! */ /*{{{*/ { Buffer *buf; int status; char *dirfile, *dir, *file; char *name; #ifdef REAL_UNIX_SYSTEM char *dir1; /* Handle the case where /foo/bar/../baz may not be the same as /foo/baz */ if (-1 == jed_dirfile_to_dir_file (filestr, &dir, &file)) return -1; if (NULL == (dir1 = jed_expand_link (dir))) { SLfree (dir); SLfree (file); return -1; } SLfree (dir); filestr = jed_dir_file_merge (dir1, file); SLfree (dir1); SLfree (file); #else filestr = jed_standardize_filename (filestr); #endif if (filestr == NULL) return -1; /* If this hook returns 1, then the file is considered to be found. */ if (1 == jed_va_run_hooks ("_jed_find_file_before_hooks", JED_HOOKS_RUN_UNTIL_NON_0, 1, filestr)) { SLfree (filestr); return 0; } dirfile = jed_expand_link(filestr); if (dirfile == NULL) { SLfree (filestr); return -1; } if (-1 == jed_dirfile_to_dir_file (filestr, &dir, &file)) { SLfree (filestr); SLfree (dirfile); return -1; } status = -1; if (*file == 0) { jed_verror (Expect_File_Error); goto free_and_return; } check_buffers (); /* search for the file in current buffer list */ if (NULL != (buf = find_file_buffer (dirfile))) { if ((file_changed_on_disk (buf, dirfile) > 0) && (1 == jed_get_yes_no("File changed on disk. Read from disk"))) { int n = (int) buf->linenum; mark_buffer_modified (buf, 0, 1); kill_buffer_cmd (buf->name); status = find_file_cmd (filestr); goto_line (&n); goto free_and_return; } if (SLang_get_error ()) goto free_and_return; status = 1; switch_to_buffer (buf); goto free_and_return; } /* Make a buffer but do not attach it to a file yet. */ name = extract_file (filestr); buf = make_buffer (name, dir, NULL); switch_to_buffer(buf); status = read_file (dirfile); switch (status) { case -2: jed_verror ("File %s not readable.", dirfile); status = -1; break; case -1: if (Batch == 0) message ("New file."); status = 0; break; default: if (Batch == 0) jed_vmessage (0, "%d lines read", status); status = 1; } if (buf == CBuf) /* check this in case the hook switches buffers */ { mark_buffer_modified (CBuf, 0, 0); /* Now attach it to a file */ visit_file (dir, file); uniquely_name_buffer (CBuf, name); (void) bob (); set_file_modes(); CBuf->flags |= UNDO_ENABLED; } if (-1 == jed_va_run_hooks ("_jed_find_file_after_hooks", JED_HOOKS_RUN_ALL, 0)) status = -1; free_and_return: SLfree (dirfile); SLfree (filestr); SLfree (dir); SLfree (file); return status; } /*}}}*/ int find_file_in_window(char *file) /*{{{*/ { int ret; Buffer *b = CBuf; ret = find_file_cmd (file); if ((b != CBuf) && (*CBuf->name != ' ')) Last_Buffer = CBuf; window_buffer(CBuf); return(ret); } /*}}}*/ /* create a minibuffer with window and switch to it. */ static void create_minibuffer(void) /*{{{*/ { Window_Type *w; MiniBuffer = The_MiniBuffer; /* We may get called before the keymap has been set up. Make sure that * we have a keymap. */ if ((The_MiniBuffer->keymap == NULL) || (The_MiniBuffer->keymap == Global_Map)) jed_setup_minibuffer_keymap (); /* I want to make Mini_Window->next = Current Window so that scroll other window routines will scroll it. */ w = JWindow; do other_window(); while (JWindow->next != w); JWindow->next = The_MiniWindow; The_MiniWindow->next = w; The_MiniWindow->hscroll_column = 1; Mini_Info.action_window = w; other_window(); /* now in minibuffer window */ window_buffer(MiniBuffer); switch_to_buffer(MiniBuffer); MiniBuffer_Active = 1; erase_buffer (); #if JED_HAS_TTY_MENUS jed_notify_menu_buffer_changed (); #endif /* allow kill region to kill to beginning of minibuffer. * Suggested by stefen@uni-paderborn.de */ jed_push_mark (); } /*}}}*/ char *Completion_Buffer = "*Completions*"; static char *Last_Completion_Buffer; static int Last_Completion_Windows; /* evaluate command in minibuffer and leave */ int exit_minibuffer() /*{{{*/ { if (IS_MINIBUFFER) { if (Last_Completion_Buffer != NULL) { pop_to_buffer (Completion_Buffer); mark_buffer_modified (CBuf, 0, 0); switch_to_buffer_cmd (Last_Completion_Buffer); kill_buffer_cmd (Completion_Buffer); touch_window_hard (JWindow, 0); if (Last_Completion_Windows == 1) one_window (); } select_minibuffer (); Exit_From_MiniBuffer = 1; } Last_Completion_Buffer = NULL; return(0); } /*}}}*/ /* return 1 if minibuffer already exists otherwise returns 0 */ int select_minibuffer() /*{{{*/ { Window_Type *w; /* Try to find active minibuffer and go there */ w = JWindow; while (MiniBuffer != NULL) { if (JWindow->sy+1 == Jed_Num_Screen_Rows) return(1); other_window(); if (w == JWindow) exit_error("Internal Error: no window!", 1); } /* switchs to minibuffer too */ create_minibuffer(); return(0); } /*}}}*/ /* if cmd != NULL, insert it into minibuffer and then send the result to the appropriate routine. */ static int ledit(void) /*{{{*/ { int n; char *tmp; if (MiniBuffer == NULL) complete_open = NULL; if (NULL == (tmp = read_from_minibuffer(JED_PROMPT, 0, NULL, &n))) return(0); /* SLang_Error = 0; ??? Why? 0.99-16 and earlier */ Suspend_Screen_Update = 1; SLang_load_string(tmp); SLfree(tmp); if (SLang_get_error () == SL_USER_BREAK) msg_error("Quit!"); /* SLang_set_error (0); */ return(1); } /*}}}*/ static int File_Expansion_Happened = 0; static int file_findfirst (char *buf) { char *file; File_Expansion_Happened = 1; if (NULL == (file = jed_expand_filename (buf))) return -1; safe_strcpy (buf, file, JED_MAX_COMPLETION_LEN); SLfree (file); return sys_findfirst (buf); } static int file_findnext (char *file) { return sys_findnext (file); } static char *read_file_from_minibuffer(char *prompt, char *def, char *stuff) /*{{{*/ { int n; char *file, *file1; File_Expansion_Happened = 0; complete_open = file_findfirst; complete_next = file_findnext; if ((stuff == NULL) || (*stuff == 0)) { if (*CBuf->dir == 0) buffer_filename (CBuf, NULL, CBuf->file); stuff = CBuf->dir; } if (NULL == (file = read_from_minibuffer (prompt, def, stuff, &n))) return NULL; if (File_Expansion_Happened) file1 = jed_standardize_filename (file); else file1 = jed_expand_filename (file); SLfree (file); return file1; } /*}}}*/ static char *String_Completion_Str; static char *String_Completion_Str_Next; static int String_Completion_Str_Len; static int next_string_list (char *buf) /*{{{*/ { register char *s = String_Completion_Str_Next; int len; while (*s) { while (*s && (*s != ',')) s++; len = (int) (s - String_Completion_Str_Next); if (*s == ',') s++; if (!len || strncmp (buf, String_Completion_Str_Next, String_Completion_Str_Len)) { String_Completion_Str_Next = s; continue; } if (len >= JED_MAX_PATH_LEN) len = JED_MAX_PATH_LEN - 1; strncpy (buf, String_Completion_Str_Next, len); buf[len] = 0; String_Completion_Str_Next = s; return 1; } String_Completion_Str_Next = s; return 0; } /*}}}*/ static int open_string_list (char *buf) /*{{{*/ { String_Completion_Str_Next = String_Completion_Str; String_Completion_Str_Len = strlen (buf); return next_string_list (buf); } /*}}}*/ void read_object_with_completion(char *prompt, char *dflt, char *stuff, int *typep) /*{{{*/ { int type = *typep, n; char buf[JED_MAX_COMPLETION_LEN], *tmp; char *str = NULL; *buf = 0; if (type == 'f') /* file */ { char *file = read_file_from_minibuffer (prompt, dflt, stuff); if (file != NULL) (void) SLang_push_malloced_string (file); return; } else if (type == 'b') /* buffer */ { complete_open = open_bufflist; complete_next = next_bufflist; } else if (type == 'F') /* function */ { complete_open = open_function_list; complete_next = next_function_list; } else if (type == 's') { complete_open = open_string_list; complete_next = next_string_list; if (SLpop_string (&str)) return; String_Completion_Str = str; } else { complete_open = NULL; } safe_strcat (buf, stuff, sizeof (buf)); if (NULL != (tmp = read_from_minibuffer(prompt, dflt, buf, &n))) { (void) SLang_push_string(tmp); SLfree(tmp); if (str != NULL) SLfree (str); } } /*}}}*/ int insert_file_cmd (void) /*{{{*/ { char *f, *file; CHECK_READ_ONLY if (NULL == (file = read_file_from_minibuffer("Insert file:", NULL, NULL))) return -1; f = extract_file(file); if ((*file == 0) || (*f == 0)) { SLfree (file); msg_error(Expect_File_Error); return(1); } if (insert_file(file) < 0) msg_error("Error inserting file."); SLfree (file); return(1); } /*}}}*/ int find_file (void) /*{{{*/ { char *file, *f; int status; if (NULL == (file = read_file_from_minibuffer("Find file:", NULL, NULL))) return 0; f = extract_file (file); if (*f == 0) { char *dirfile = jed_dir_file_merge (file, CBuf->file); SLfree (file); if (dirfile == NULL) return -1; file = dirfile; } status = find_file_in_window (file); SLfree (file); return status; } /*}}}*/ int jed_save_buffer_as_cmd (void) /*{{{*/ { char *tmp; if (NULL == (tmp = read_file_from_minibuffer("Save as:", NULL, NULL))) return(0); (void) jed_save_buffer_as (tmp); SLfree(tmp); return(1); } /*}}}*/ void switch_to_buffer_cmd (char *name) /*{{{*/ { Buffer *tthis = CBuf; set_buffer(name); window_buffer(CBuf); if ((CBuf != tthis) && (*CBuf->name != ' ')) Last_Buffer = tthis; } /*}}}*/ static void get_last_buffer(void) /*{{{*/ { if ((Last_Buffer == CBuf) || (Last_Buffer == NULL) || (*Last_Buffer->name == ' ') || (Last_Buffer->flags & BURIED_BUFFER)) { Last_Buffer = find_non_visible_buffer (CBuf); } } /*}}}*/ int get_buffer() /*{{{*/ { char *tmp; int n; complete_open = open_bufflist; complete_next = next_bufflist; get_last_buffer (); if (NULL == (tmp = read_from_minibuffer("Switch to buffer:", Last_Buffer->name, NULL, &n))) return(0); switch_to_buffer_cmd(tmp); SLfree(tmp); return(1); } /*}}}*/ int kill_buffer (void) /*{{{*/ { char *tmp; int n; complete_open = open_bufflist; complete_next = next_bufflist; tmp = read_from_minibuffer("Kill buffer:", (char *) CBuf->name, NULL, &n); if (tmp != NULL) { #if JED_HAS_SUBPROCESSES Buffer *b = find_buffer(tmp); if ((b != NULL) && (b->subprocess)) { if (0 == jed_get_yes_no("Buffer has a subprocess attached. Delete anyway")) { SLfree(tmp); return 0; } } #endif kill_buffer_cmd(tmp); SLfree(tmp); return(1); } return 0; } /*}}}*/ int evaluate_cmd() /*{{{*/ { return(!ledit()); } /*}}}*/ char *pop_to_buffer(char *name) /*{{{*/ { Window_Type *w, *action, *use_this; char *bname; Line *line, *oldline; int p, oldp, lnum, oldlnum; Buffer *b, *oldb; if (!strcmp(name, " ")) { select_minibuffer (); return CBuf->name; } /* save position so we can pop back to it if buffer already exists in window */ oldb = CBuf; oldline = CLine; oldp = Point; oldlnum = LineNum; set_buffer(name); line = CLine; p = Point; lnum = LineNum; use_this = NULL; if (MiniBuffer != NULL) { action = Mini_Info.action_window; } else action = NULL; if (Batch) return CBuf->name; w = JWindow; /* find a window to use */ do { if (w->sy + 1 != Jed_Num_Screen_Rows) { if (action != NULL) { if (w != action) use_this = w; } else if (w != JWindow) use_this = w; if (w->buffer == CBuf) { use_this = w; break; } } w = w->next; } while (w != JWindow); b = CBuf; if (use_this != NULL) { while(JWindow != use_this) other_window(); /* This is a good reason for haveing otherwindow avoid switching buffers */ if (CBuf == oldb) { CLine = oldline; Point = oldp; LineNum = oldlnum; } } else { if (action != NULL) while(JWindow != action) other_window(); split_window(); /* * doing it this way makes screen update look better */ w = JWindow; do { other_window(); } while (JWindow->buffer != w->buffer); JWindow->hscroll_column = 1; } bname = CBuf->name; switch_to_buffer(b); b->line = CLine = line; b->point = Point = p; b->linenum = LineNum = lnum; if (b != JWindow->buffer) window_buffer(b); return bname; } /*}}}*/ #if defined (REAL_UNIX_SYSTEM) || defined(__WIN32__) || (defined (__os2__) && !defined(__WATCOMC__)) # if defined (__WIN32__) /* defined (__BORLANDC__) || defined (_MSC_VER) */ # undef popen # undef pclose # define popen w32_popen # define pclose w32_pclose # endif # ifdef __IBMC__ extern FILE *popen(char *, char *); extern int pclose(FILE *); # endif # if !JED_HAS_SUBPROCESSES # define jed_popen popen # define jed_pclose pclose # endif static char *Process_Error = "Unable to open process."; int shell_command(char *cmd) /*{{{*/ { FILE *pp; VFILE *vp; int status; if (Jed_Secure_Mode) { msg_error ("Access denied."); return -1; } if (NULL == (pp = jed_popen(cmd, "r"))) { msg_error(Process_Error); return -1; } if (NULL != (vp = vstream(fileno(pp), 0, VFILE_TEXT))) { (void) insert_file_pointer(vp); SLfree(vp->buf); SLfree ((char *)vp); } else msg_error("Malloc Error."); status = jed_pclose (pp); #if defined(WIFEXITED) && defined(WEXITSTATUS) if ((status != -1) && WIFEXITED(status)) { status = WEXITSTATUS(status); } #endif return status; } /*}}}*/ int pipe_region(char *cmd) /*{{{*/ { FILE *pp; int n; if (Jed_Secure_Mode) { msg_error ("Access denied."); return -1; } if (NULL == (pp = jed_popen (cmd, "w"))) { msg_error(Process_Error); return(-1); } n = write_region_to_fp(fileno(pp)); if (n == -1) msg_error ("pipe_region: write failed"); return jed_pclose (pp); } /*}}}*/ #endif /* * Try to open a .slc then a .sl */ #ifdef SIXTEEN_BIT_SYSTEM #define VFILE_BUF_SIZE 1024 #else #define VFILE_BUF_SIZE 4096 #endif static VFILE *jed_open_lib_file (char *file, char **dirfile) /*{{{*/ { char libfsl[JED_MAX_PATH_LEN], libfslc[JED_MAX_PATH_LEN]; char *lib, *type, *libf; unsigned int n; VFILE *vp = NULL; libf = file; lib = Jed_Library; /* If file begins with ./,then read it from the current directory */ if ((0 == strncmp (file, "./", 2)) #ifdef IBMPC_SYSTEM || (0 == strncmp (file, ".\\", 2)) || (0 == strncmp (file, "..\\", 3)) #endif || (0 == strncmp (file, "../", 3))) { if (NULL == (lib = jed_get_cwd ())) lib = ""; } else if (SLpath_is_absolute_path (file)) lib = ""; else if ((lib == NULL) || (*lib == 0)) exit_error("JED_ROOT environment variable needs set.", 0); if ((NULL != (type = file_type(file))) && ((*type == 0) || ((0 != strcmp (type, "sl")) && (0 != strcmp (type, "slc"))))) type = NULL; n = 0; while (0 == SLextract_list_element (lib, n, ',', libfsl, sizeof(libfsl))) { n++; fixup_dir(libfsl); safe_strcat (libfsl, file, sizeof (libfsl)); safe_strcpy (libfsl, jed_standardize_filename_static(libfsl), sizeof (libfsl)); libf = libfsl; if ((1 == file_status (libf)) && (NULL != (vp = vopen(libf, VFILE_BUF_SIZE, VFILE_TEXT)))) break; if (type == NULL) { #ifdef VMS int vmsn; /* remove trailing '.' */ if (0 != (vmsn = strlen(libfsl))) { vmsn--; if (libfsl[vmsn] == '.') libfsl[vmsn] = 0; } #endif /* Try .sl and .slc */ safe_strcat (libfsl, ".sl", sizeof (libfsl)); safe_strcpy (libfslc, libfsl, sizeof (libfslc)); safe_strcat (libfslc, "c", sizeof (libfslc)); if (file_time_cmp(libfslc, libfsl) >= 0) libf = libfslc; if ((1 == file_status (libf)) && (NULL != (vp = vopen(libf, VFILE_BUF_SIZE, VFILE_TEXT)))) break; } } if (NULL == (*dirfile = SLang_create_slstring (libf))) { vclose (vp); return NULL; } if (0 == Jed_Load_Quietly) jed_vmessage (1, "loading %s", libf); return (vp); } /*}}}*/ static char *jed_read_from_file(SLang_Load_Type *x) /*{{{*/ { char *s; unsigned int n; if ((s = vgets((VFILE *) x->client_data, &n)) != NULL) { if (s[n - 1] != '\n') s[n] = 0; } return s; } /*}}}*/ int jed_ns_load_file (char *file, char *ns) { VFILE *vp; SLang_Load_Type *x; int ret; if (NULL == (vp = jed_open_lib_file (file, &file))) { SLang_verror (SL_OBJ_NOPEN, "Unable to load %s", file); return -1; } #if SLANG_VERSION >= 10409 x = SLns_allocate_load_type (file, ns); #else if ((ns != NULL) && (0 != strcmp (ns, "Global"))) { SLang_verror (SL_NOT_IMPLEMENTED, "loading a file into a namespace is not supported"); x = NULL; } else x = SLallocate_load_type (file); #endif SLang_free_slstring (file); if (x == NULL) { vclose (vp); return -1; } x->client_data = (VOID_STAR) vp; x->read = jed_read_from_file; ret = SLang_load_object (x); SLdeallocate_load_type (x); vclose (vp); return ret; } #if SLANG_VERSION < 10409 static int jed_load_file (char *file) { return jed_ns_load_file (file, NULL); } #endif typedef struct { Line *l; char buf[256]; } Buffer_Client_Type; static char *jed_read_from_buffer (SLang_Load_Type *x) /*{{{*/ { Buffer_Client_Type *b; char *buf; Line *l; unsigned int len; b = (Buffer_Client_Type *)x->client_data; if (NULL == (l = b->l)) return NULL; len = (unsigned int) l->len; if (len > 255) { #if SLANG_VERSION >= 20000 SLang_verror (SL_BUILTIN_LIMIT_EXCEEDED, "Line too long"); #else SLang_verror (0, "Line too long"); #endif return NULL; } buf = b->buf; SLMEMCPY(buf, (char *) l->data, len); buf [len] = 0; b->l = l->next; return buf; } /*}}}*/ void load_buffer (void) /*{{{*/ { SLang_Load_Type *x; Buffer_Client_Type client_data; Buffer *cbuf = CBuf; int flags = CBuf->flags; Line *l, *lwant; char *file; if (cbuf->file[0] == 0) file = cbuf->name; else file = cbuf->file; file = jed_dir_file_merge (CBuf->dir, file); if (file == NULL) return; x = SLallocate_load_type (file); SLfree (file); if (x == NULL) return; x->read = jed_read_from_buffer; x->client_data = (VOID_STAR) &client_data; client_data.l = CBuf->beg; cbuf->flags |= READ_ONLY; SLang_load_object(x); SLdeallocate_load_type (x); if (buffer_exists (cbuf)) cbuf->flags = flags; else cbuf = NULL; if (SLang_get_error () == 0) return; if (cbuf == NULL) return; pop_to_buffer (cbuf->name); lwant = client_data.l; if (lwant == NULL) { eob(); return; } bob(); while (1) { l = CLine->next; if ((l == NULL) || (l == lwant)) break; (void) jed_down(1); } (void) jed_skip_whitespace(); } /*}}}*/ static SLang_Name_Type *Expand_File_Hook; void set_expansion_hook (char *s) /*{{{*/ { if (NULL == (Expand_File_Hook = SLang_get_function (s))) { msg_error ("The expansion hook has not been defined."); } } /*}}}*/ int mini_complete (void) /*{{{*/ { char *pl, *pb; char last[JED_MAX_PATH_LEN], buf[JED_MAX_PATH_LEN], *tmp; static char prev[JED_MAX_PATH_LEN]; int n, last_key_char = SLang_Last_Key_Char; static int flag = 0; /* when flag goes 0, we call open */ if (complete_open == NULL) return ins_char_cmd(); bol (); jed_push_mark(); eob(); if (NULL == (tmp = make_buffer_substring(&n))) return(1); safe_strcpy(buf, tmp, sizeof (buf)); SLfree(tmp); if ((last_key_char == ' ') && ((long) Last_Key_Function == (long) mini_complete)) { if (flag) flag = (*complete_next)(buf); if (flag == 0) { safe_strcpy(buf, prev, sizeof (buf)); flag = (*complete_open)(buf); } safe_strcpy(last, buf, sizeof (last)); n = -1; } else { n = 0; safe_strcpy (prev, buf, sizeof (prev)); /* save this search context */ } if (!n) { if ((Repeat_Factor != NULL) || (complete_open != file_findfirst) || (Expand_File_Hook == NULL)) flag = (*complete_open)(buf); else { int do_free; SLang_push_string (buf); SLexecute_function (Expand_File_Hook); if (SLang_get_error () == 0) SLang_pop_integer (&do_free); if (SLang_get_error () == 0) { if (do_free == 0) { flag = (*complete_open) (buf); goto start_loop; } } if (SLang_get_error () || SLang_pop_slstring (&tmp)) { msg_error ("Error encounter during expansion. Disabling expansion hook."); Expand_File_Hook = NULL; return 1; } safe_strcpy (last, tmp, sizeof (last)); safe_strcpy (prev, last, sizeof (prev)); SLang_free_slstring (tmp); flag = 0; /* So we do not call complete_next */ n = -1; } } start_loop: if (!n && flag) { safe_strcpy(last, buf, sizeof (last)); /* This do loop tests all values from complete_next and returns the smallest length match of initial characters of buf */ do { if ((n == 0) && (last_key_char == '\t')) { set_buffer (Completion_Buffer); erase_buffer (); CBuf->flags |= BURIED_BUFFER; (void) jed_insert_string ("!!! Use Page Up/Down keys to scroll this window. !!!\n"); } n++; pl = last; pb = buf; #if !JED_FILE_PRESERVE_CASE if ((complete_open == open_bufflist) #if !JED_FILENAME_CASE_SENSITIVE || (complete_open == file_findfirst) #endif ) while (*pl && (UPPER_CASE(*pl) == UPPER_CASE(*pb))) { pl++; pb++; } else /* next statement */ #endif while (*pl && (*pl == *pb)) { pl++; pb++; } *pl = 0; if (last_key_char == '\t') { while (*pb) pb++; (void) jed_quick_insert ((unsigned char *)buf, (int) (pb - buf)); newline (); } } while(0 != (flag = (*complete_next)(buf))); #if JED_FILE_PRESERVE_CASE /* OS/2 uses case-insensitive search on buffer-names. Set the * flag if there is an exact match, so that completion will * cycle without repeats through all the matches. */ if (complete_open == open_bufflist) { safe_strcpy(buf, last, sizeof (buf)); (*complete_open)(buf); do { if (!strcmp(buf, last)) { flag = 1; break; } } while ((*complete_next)(buf)); } #endif } if ((n > 1) && (last_key_char == '\t') && (Last_Completion_Buffer == NULL)) { Last_Completion_Windows = jed_num_windows () - 1; /* not including mini */ Last_Completion_Buffer = pop_to_buffer (Completion_Buffer); bob (); } while ((CBuf != MiniBuffer) || !IS_MINIBUFFER) other_window (); if (n) { erase_buffer(); (void) jed_insert_string(last); if ((n == 1) && ((long) Last_Key_Function == (long) mini_complete)) message("[Sole Completion.]"); } else msg_error("No Match!"); return(1); } /*}}}*/ void copy_region_cmd(char *name) /*{{{*/ { Buffer *buf; if (NULL != (buf = find_buffer(name))) { copy_region_to_buffer(buf); } else msg_error("Unable to find buffer."); } /*}}}*/ #ifndef IBMPC_SYSTEM void screen_w80 (void) /*{{{*/ { tt_narrow_width (); jed_resize_display (); } /*}}}*/ void screen_w132 (void) /*{{{*/ { tt_wide_width(); jed_resize_display (); } /*}}}*/ #endif char *make_line_string(char *string, unsigned int buflen) /*{{{*/ { unsigned char *tmp, *p1, *p2; unsigned int n; if (CBuf->marks == NULL) { p1 = CLine->data + Point; p2 = CLine->data + CLine->len; } else { p1 = CLine->data + CBuf->marks->point; p2 = CLine->data + Point; if (p2 < p1) { tmp = p1; p1 = p2; p2 = tmp; } jed_pop_mark(0); } n = (unsigned int) (p2 - p1); if (n >= buflen) n = buflen - 1; SLMEMCPY(string, (char *) p1, n); string[n] = 0; return(string); } /*}}}*/ char *make_buffer_substring(int *np) /*{{{*/ { Line *tthis, *beg; int n = 1, dn, thisp; unsigned char *buf; if (!check_region(&n)) return (NULL); /* spot pushed */ /* Point now at end of the region */ beg = tthis = CBuf->marks->line; thisp = CBuf->marks->point; n = 0; jed_pop_mark(0); while (tthis != CLine) { n += tthis->len; tthis = tthis->next; } n -= thisp; n += Point; if (NULL == (buf = (unsigned char *) SLmalloc (n + 1))) { pop_spot(); return (NULL); } if (CLine == (tthis = beg)) { SLMEMCPY((char *)buf, (char *) (tthis->data + thisp), n); } else { n = 0; while (tthis != CLine) { dn = tthis->len - thisp; SLMEMCPY((char *)(buf + n), (char *) (tthis->data + thisp), dn); tthis = tthis->next; thisp = 0; n += dn; } SLMEMCPY((char *)(buf + n), (char *) tthis->data, Point); n += Point; } buf[n] = 0; *np = n; pop_spot(); return ((char *) buf); } /*}}}*/ void buffer_substring() /*{{{*/ { char *buf; int n; if (NULL == (buf = make_buffer_substring(&n))) return; SLang_push_malloced_string((char *)buf); } /*}}}*/ int markp(void) /*{{{*/ { return (CBuf->marks != NULL); } /*}}}*/ int dup_mark(void) /*{{{*/ { if (CBuf->marks == NULL) return(0); push_spot(); jed_goto_mark(CBuf->marks); jed_push_mark(); pop_spot(); return(1); } /*}}}*/ void mini_read(char *prompt, char *def, char *stuff) /*{{{*/ { char *buf; int n; complete_open = NULL; if (NULL == (buf = read_from_minibuffer(prompt, def, stuff, &n))) SLang_push_string (""); else SLang_push_malloced_string(buf); } /*}}}*/ void make_buffer_list(void) /*{{{*/ { int n = 0; Buffer *b; b = CBuf; do { SLang_push_string(b->name); b = b->next; n++; } while (b != CBuf); SLang_push_integer(n); } /*}}}*/ static void jed_traceback(char *s) /*{{{*/ { char *n; if (Batch) { (void) fputs (s, stderr); return; } n = CBuf->name; set_buffer("*traceback*"); CBuf->flags |= BURIED_BUFFER; eob(); (void) jed_insert_string(s); set_buffer(n); } /*}}}*/ #if 0 static struct /*{{{*/ { int depth = 0; char *name[20]; int marks[20]; } /*}}}*/ FName_Stack; void enter_function(char *name) /*{{{*/ { if (depth > 20) { msg_error("Function Stack too deep."); return; } FName_Stack->name[depth] = name; FName_Stack->marks[depth] = 0; } /*}}}*/ void exit_function(char *name) /*{{{*/ { int n = FName_Stack->marks[depth]; } /*}}}*/ #endif static void jed_clear_error(void) /*{{{*/ { *Error_Buffer = 0; SLKeyBoard_Quit = 0; } /*}}}*/ typedef struct _Init_SLang_Hook_Type { int (*hook)(void); struct _Init_SLang_Hook_Type *next; } Init_SLang_Hook_Type; static Init_SLang_Hook_Type *Init_SLang_Hooks; int jed_add_init_slang_hook (int (*hook)(void)) { Init_SLang_Hook_Type *h; if (hook == NULL) return 0; h = (Init_SLang_Hook_Type *)SLmalloc (sizeof (Init_SLang_Hook_Type)); if (h == NULL) return -1; h->hook = hook; h->next = Init_SLang_Hooks; Init_SLang_Hooks = h; return 0; } static int run_init_slang_hooks (void) { Init_SLang_Hook_Type *h; h = Init_SLang_Hooks; while (h != NULL) { if (-1 == (*h->hook)()) return -1; h = h->next; } while (Init_SLang_Hooks != NULL) { h = Init_SLang_Hooks->next; SLfree ((char *) Init_SLang_Hooks); Init_SLang_Hooks = h; } return 0; } static void slang_exit_error_handler (char *fmt, va_list ap) { char buf [2048]; SLvsnprintf (buf, sizeof (buf), fmt, ap); exit_error (buf, 0); } static void vmsg_hook (char *fmt, va_list ap) { char buf [2048]; SLvsnprintf (buf, sizeof (buf), fmt, ap); message (buf); } void jed_setup_minibuffer_keymap (void) { SLKeyMap_List_Type *m; if (NULL == (m = SLang_find_keymap ("Mini_Map"))) { if (NULL == (m = SLang_create_keymap("Mini_Map", Global_Map))) exit_error("Unable to create minibuffer keymap", 0); SLang_undefine_key("\r", m); SLkm_define_key ("\r", (FVOID_STAR) exit_minibuffer, m); SLkm_define_key ("\t", (FVOID_STAR) mini_complete, m); SLkm_define_key (" ", (FVOID_STAR) mini_complete, m); } The_MiniBuffer->keymap = m; } #if SLANG_VERSION < 10405 /* Work around a slang bug triggered by jed. Apps which use slang's * load_file function do not have this problem. */ extern int (*_SLprep_eval_hook) (char *); static int bug_fixed_prep_eval_expr (char *expr) { int ret; char *end; end = strchr (expr, '\n'); if (end == NULL) end = expr + strlen (expr); expr = SLmake_nstring (expr, (unsigned int) (end - expr)); if (expr == NULL) return -1; if ((0 != SLang_load_string (expr)) || (-1 == SLang_pop_integer (&ret))) ret = -1; else ret = (ret != 0); SLfree (expr); return ret; } #endif void init_minibuffer() /*{{{*/ { Buffer *tmp; tmp = CBuf; The_MiniBuffer = make_buffer (" ", NULL, NULL); The_MiniBuffer->modes = 0; /* do some initializing */ switch_to_buffer(The_MiniBuffer); remake_line(132); The_MiniWindow = create_window(Jed_Num_Screen_Rows-1, 0, 1, 1, Jed_Num_Screen_Cols); The_MiniWindow->buffer = CBuf; Buffer_Local.tab = 0; switch_to_buffer(tmp); SLang_Dump_Routine = jed_traceback; SLang_Exit_Error_Hook = slang_exit_error_handler; #ifdef __GO32__ SLang_Interrupt = i386_check_kbd; #endif #if 0 SLang_Enter_Function = enter_function; SLang_Exit_Function = exit_function; #endif #if SLANG_VERSION < 10405 _SLprep_eval_hook = bug_fixed_prep_eval_expr; #endif if ((-1 == SLang_init_slang ()) #ifndef SIXTEEN_BIT_SYSTEM || (-1 == SLang_init_slmath ()) #endif || (-1 == SLang_init_posix_process ()) || (-1 == SLang_init_posix_dir ()) || (-1 == SLang_init_slassoc ()) || (-1 == SLang_init_stdio ()) || (-1 == SLang_init_posix_io ()) || (-1 == SLang_init_ospath ()) #if JED_HAS_IMPORT || (-1 == SLang_init_import ()) #endif || (-1 == init_jed_intrinsics ()) || (-1 == jed_init_userinfo ()) #if JED_HAS_TTY_MENUS || (-1 == jed_init_menus ()) #endif || (-1 == jed_init_syntax ()) || (-1 == register_jed_classes ()) || (-1 == jed_init_user_hooks ()) || (-1 == run_init_slang_hooks ())) { exit_error("Unable to initialize S-Lang!", 0); } /* use jed rouotines instead of default slang ones */ SLang_Error_Hook = msg_error; SLang_VMessage_Hook = vmsg_hook; SLang_User_Clear_Error = jed_clear_error; #if SLANG_VERSION < 10409 SLang_Load_File_Hook = jed_load_file; #else SLns_Load_File_Hook = jed_ns_load_file; #endif } /*}}}*/