/* vi:set ts=4 sw=4: * * VIM - Vi IMproved * */ /* * Author: * K.Tsuchida (ken) kenichi-tsu@aix.or.jp/gdh01571@niftyserve.or.jp */ /* * Author: (base code: winnt.c jelvis version) * T.Watanabe (Tom_W) tom_w@st.rim.or.jp/GCD02235@niftyserve.or.jp */ /* Author: (base code: msdos.c) * Code Contributions By: Bram Moolenaar mool@oce.nl * Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ /* * winnt.c * * WINDOWS-NT system-dependent routines. */ #include #include #include "vim.h" #include "globals.h" #include "param.h" #include "proto.h" #include #include #ifdef FEPCTRL #include "fepctrl.h" #endif static HANDLE h_stdin, h_stdout; static int sy_fg_color; static int sy_bg_color; #define NM_COLOR (sy_fg_color|(sy_bg_color<<4)) #define SO_COLOR (sy_bg_color|(sy_fg_color<<4)) #define gotoxy(x, y) {COORD csr; csr.X = (x-1); csr.Y = (y-1); (void) SetConsoleCursorPosition(h_stdout, csr);} typedef struct filelist { char **file; int nfiles; int maxfiles; } FileList; static void addfile __ARGS((FileList *, char *, int)); static int pstrcmp(); /* __ARGS((char **, char **)); BCC does not like this */ static void strlowcpy __ARGS((char *, char *)); static int expandpath __ARGS((FileList *, char *, int, int, int)); static int ctrlc_pressed = FALSE; /* set when ctrl-C or ctrl-break detected */ static int delayed_redraw = FALSE; /* set when ctrl-C detected */ #ifdef DEBUG /* * Put two characters in the video buffer without calling BIOS or DOS. */ blink(n) int n; { /* do nothing */ } #endif void sleep(time) unsigned time; { INPUT_RECORD ir; DWORD cnt; time *= 1000; /* s -> ms */ while (time > 0) { Sleep(200); if (WaitForSingleObject(h_stdin, 0) == WAIT_OBJECT_0) { while (PeekConsoleInput(h_stdin, &ir, 1, &cnt) && cnt) { if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) { return; } ReadConsoleInput(h_stdin, &ir, 1, &cnt); } } time -= 200; } } void vim_delay() { sleep(1); } /* * this version of remove is not scared by a readonly (backup) file */ int vim_remove(name) char *name; { setperm(name, 0); /* default permissions */ return unlink(name); } static void clrscr() { DWORD cnt, dummy; COORD csr; csr.X = csr.Y = 0; cnt = Rows * Columns; (void) FillConsoleOutputCharacter(h_stdout, ' ', cnt, csr, &dummy); (void) FillConsoleOutputAttribute(h_stdout, NM_COLOR, cnt, csr, &dummy); } static void clreol() { CONSOLE_SCREEN_BUFFER_INFO pcinf; COORD csr; DWORD cnt, dummy; (void) GetConsoleScreenBufferInfo(h_stdout, &pcinf); csr.X = pcinf.dwCursorPosition.X; csr.Y = pcinf.dwCursorPosition.Y; cnt = Columns - pcinf.dwCursorPosition.X; (void) FillConsoleOutputCharacter(h_stdout, ' ', cnt, csr, &dummy); (void) FillConsoleOutputAttribute(h_stdout, NM_COLOR, cnt, csr, &dummy); } static void insline() { CONSOLE_SCREEN_BUFFER_INFO pcinf; SMALL_RECT area, clip; COORD dest; CHAR_INFO fill; (void) GetConsoleScreenBufferInfo(h_stdout, &pcinf); area.Left = 0; area.Right = Columns - 1; area.Top = pcinf.dwCursorPosition.Y; area.Bottom = Rows - 2; clip.Left = 0; clip.Right = Columns - 1; clip.Top = pcinf.dwCursorPosition.Y; clip.Bottom = Rows - 1; dest.X = 0; dest.Y = pcinf.dwCursorPosition.Y + 1; fill.Char.AsciiChar = ' '; fill.Attributes = NM_COLOR; (void) ScrollConsoleScreenBuffer(h_stdout, &area, &clip, dest, &fill); } static void delline() { CONSOLE_SCREEN_BUFFER_INFO pcinf; SMALL_RECT area, clip; COORD dest; CHAR_INFO fill; (void) GetConsoleScreenBufferInfo(h_stdout, &pcinf); area.Left = 0; area.Right = Columns - 1; area.Top = pcinf.dwCursorPosition.Y + 1; area.Bottom = Rows - 1; clip.Left = 0; clip.Right = Columns - 1; clip.Top = pcinf.dwCursorPosition.Y; clip.Bottom = Rows - 1; dest.X = 0; dest.Y = pcinf.dwCursorPosition.Y; fill.Char.AsciiChar = ' '; fill.Attributes = NM_COLOR; (void) ScrollConsoleScreenBuffer(h_stdout, &area, &clip, dest, &fill); } /* * mch_write(): write the output buffer to the screen */ void mch_write(s, len) char *s; int len; { char *p; int row, col; LPDWORD cnt; static char buffer[IOSIZE]; int pos = 0; #define SETBUF(_c) {if (pos >= sizeof(buffer)) {(void)WriteConsole(h_stdout, buffer, (unsigned)pos, &cnt, NULL); pos = 0;} buffer[pos++] = (_c);} #define PUTBUF() {if (pos != 0) (void)WriteConsole(h_stdout, buffer, (unsigned)pos, &cnt, NULL); pos = 0;} if (term_console) { /* translate ESC | sequences into bios calls */ while (len--) { if (s[0] == '\n') { PUTBUF(); (void) WriteConsole(h_stdout, "\r", (unsigned)1, &cnt, NULL); } else if ((s[0] == '\007') && p_vb) { HWND console; console = GetForegroundWindow(); if (IsWindow(console)) { FlashWindow(console, TRUE); Sleep(80); FlashWindow(console, TRUE); Sleep(80); FlashWindow(console, TRUE); Sleep(80); FlashWindow(console, TRUE); } s++; continue; } else if (s[0] == ESC && len > 1 && s[1] == '|') { switch (s[2]) { case 'J': PUTBUF(); clrscr(); goto got3; case 'K': PUTBUF(); clreol(); goto got3; case 'L': PUTBUF(); insline(); goto got3; case 'M': PUTBUF(); delline(); got3: s += 3; len -= 2; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p = s + 2; row = getdigits(&p); /* no check for length! */ if (p > s + len) break; if (*p == ';') { ++p; col = getdigits(&p);/* no check for length! */ if (p > s + len) break; if (*p == 'H') { PUTBUF(); gotoxy(col, row); len -= p - s; s = p + 1; continue; } } else if (*p == 'm') { PUTBUF(); if (row == 0) SetConsoleTextAttribute(h_stdout, NM_COLOR); else SetConsoleTextAttribute(h_stdout, SO_COLOR); len -= p - s; s = p + 1; continue; } } } SETBUF(*s++); } PUTBUF(); } else write(1, s, (unsigned)len); } /* * GetChars(): low level input funcion. * Get a characters from the keyboard. * If time == 0 do not wait for characters. * If time == n wait a short time for characters. * If time == -1 wait forever for characters. */ int GetChars(buf, maxlen, time) char *buf; int maxlen; int time; { CONSOLE_SCREEN_BUFFER_INFO pcinf; INPUT_RECORD ir; DWORD cnt; int len = 0; int update = FALSE; if (delayed_redraw) { delayed_redraw = FALSE; updateScreen(CLEAR); setcursor(); } flushbuf(); #ifdef FEPCTRL if (p_fc && p_fs) fep_disp_sync(h_stdout); #endif if (time >= 0) { if (time == 0) /* don't know if time == 0 is allowed */ time = 1; } else /* time == -1 */ { /* * If there is no character available within 2 seconds (default) * write the autoscript file to disk */ time = p_ut; update = TRUE; } if (WaitForSingleObject(h_stdin, time) != WAIT_OBJECT_0) { if (update) updatescript(0); return 0; /* no KEY data */ } while (len < maxlen-1) { if ((PeekConsoleInput(h_stdin, &ir, 1, &cnt) != TRUE) || (cnt == 0)) { return len; } if ((ReadConsoleInput(h_stdin, &ir, 1, &cnt) != TRUE) || (cnt == 0)) { return len; } if (ir.EventType == KEY_EVENT /* KEY HIT ? */ && ir.Event.KeyEvent.bKeyDown) { if (ir.Event.KeyEvent.uChar.AsciiChar) { buf[len++] = ir.Event.KeyEvent.uChar.AsciiChar; #ifdef JP #define IsSJISkanji(c) ((c)>=0x81 && (c)<=0x9f || (c)>=0xe0 && (c)<=0xfc) if (IsSJISkanji((u_char)(buf[len-1]))) { /* get 2nd kanji code */ for (;;) { if ((ReadConsoleInput(h_stdin, &ir, 1, &cnt) != TRUE) || (cnt == 0)) { continue; } if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown && ir.Event.KeyEvent.uChar.AsciiChar) { break; /* for loop */ } } buf[len++] = ir.Event.KeyEvent.uChar.AsciiChar; } #endif continue; /* while loop */ } switch (ir.Event.KeyEvent.wVirtualKeyCode) { case VK_UP: buf[len++] = 0xff; buf[len++] = 'H'; return len; case VK_DOWN: buf[len++] = 0xff; buf[len++] = 'P'; return len; case VK_LEFT: if (ir.Event.KeyEvent.dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) { buf[len++] = 0xff; buf[len++] = 's'; } else { buf[len++] = 0xff; buf[len++] = 'K'; } return len; case VK_RIGHT: if (ir.Event.KeyEvent.dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) { buf[len++] = 0xff; buf[len++] = 't'; } else { buf[len++] = 0xff; buf[len++] = 'M'; } return len; case VK_PRIOR: buf[len++] = Ctrl('B'); return len; case VK_NEXT: buf[len++] = Ctrl('F'); return len; #ifdef NT106KEY case 0xf3: case 0xf4: /* ZENKAKU / HANKAKU KEY */ buf[len++] = '[' & 0x1f; /* ESC key !! */ return len; #endif case '6': /* jkeyb.sys */ /* ken add */ case 0xde: /* ^ key */ if (ir.Event.KeyEvent.dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) { buf[len++] = '^' & 0x1f; /* CTRL+^=switch file */ return len; } break; } } } return(len); } /* * We have no job control, fake it by starting a new shell. */ void mch_suspend() { outstr("new shell started\n"); call_shell(NULL, 0, TRUE); } extern int _fmode; /* * we do not use windows, there is not much to do here */ void mch_windinit() { CONSOLE_SCREEN_BUFFER_INFO pcinf; _fmode = O_BINARY; /* we do our own CR-LF translation */ if ((h_stdin = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) { FatalAppExit(0, "stdin handle get error\n"); exit(99); } if ((h_stdout = GetStdHandle(STD_OUTPUT_HANDLE)) == INVALID_HANDLE_VALUE) { FatalAppExit(0, "stdout handle get error\n"); exit(99); } term_console = 1; flushbuf(); mch_get_winsize(); #ifdef FEPCTRL if (p_fc) fep_init(); #endif } void check_win(argc, argv) int argc; char **argv; { if (!isatty(0) || !isatty(1)) { fprintf(stderr, "VIM: no controlling terminal\n"); exit(2); } } /* * fname_case(): Set the case of the filename, if it already exists. * msdos filesystem is far to primitive for that. do nothing. */ void fname_case(name) char *name; { } /* * settitle(): set titlebar of our window. * Dos console has no title. */ void settitle(str) char *str; { if (*str) SetConsoleTitle(str); else SetConsoleTitle("-JVim-"); } void resettitle() { SetConsoleTitle(""); } /* * Get name of current directory into buffer 'buf' of length 'len' bytes. * Return non-zero for success. */ int dirname(buf, len) char *buf; int len; { return (getcwd(buf, len) != NULL); } /* * get absolute filename into buffer 'buf' of length 'len' bytes */ int FullName(fname, buf, len) char *fname, *buf; int len; { if (fname == NULL) /* always fail */ return 0; #ifdef __BORLANDC__ /* the old Turbo C does not have this */ if (_fullpath(buf, fname, len) == NULL) { strncpy(buf, fname, len); /* failed, use the relative path name */ return 0; } return 1; #else /* almost the same as FullName in unix.c */ { int l; char olddir[MAXPATHL]; char *p, *q; int c; int retval = 1; *buf = 0; /* * change to the directory for a moment, * and then do the getwd() (and get back to where we were). * This will get the correct path name with "../" things. */ p = strrchr(fname, '/'); q = strrchr(fname, '\\'); if (q && (p == NULL || q > p)) p = q; q = strrchr(fname, ':'); if (q && (p == NULL || q > p)) p = q; if (p != NULL) { if (getcwd(olddir, MAXPATHL) == NULL) { p = NULL; /* can't get current dir: don't chdir */ retval = 0; } else { if (*p == ':' || (p > fname && p[-1] == ':')) q = p + 1; else q = p; c = *q; *q = NUL; if (chdir(fname)) retval = 0; else fname = p + 1; *q = c; } } if (getcwd(buf, len) == NULL) { retval = 0; *buf = NUL; } l = strlen(buf); if (l && buf[l - 1] != '/' && buf[l - 1] != '\\') strcat(buf, "\\"); if (p) chdir(olddir); strcat(buf, fname); return retval; } #endif } /* * get file permissions for 'name' * -1 : error * else FA_attributes defined in dos.h */ long getperm(name) char *name; { return(_chmod(name, 0, 0)); /* get file mode */ } /* * set file permission for 'name' to 'perm' */ int setperm(name, perm) char *name; long perm; { perm &= ~FA_ARCH; return _chmod(name, 1, (int)perm); } /* * check if "name" is a directory */ int isdir(name) char *name; { int f; f = _chmod(name, 0, 0); if (f == -1) return -1; /* file does not exist at all */ if ((f & FA_DIREC) == 0) return 0; /* not a directory */ return 1; } /* * Careful: mch_windexit() may be called before mch_windinit()! */ void mch_windexit(r) int r; { #ifdef FEPCTRL if (p_fc) fep_term(); #endif settmode(0); stoptermcap(); flushbuf(); stopscript(); /* remove autoscript file */ exit(r); } /* * */ BOOL cbrk_handler(type) DWORD type; { switch (type) { case CTRL_BREAK_EVENT: delayed_redraw = TRUE; return(TRUE); case CTRL_C_EVENT: /* FlushConsoleInputBuffer(h_stdin); */ ctrlc_pressed = TRUE; return(TRUE); case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: if (p_wa && !p_ro) { extern int global_busy; global_busy = 1; p_bk = TRUE; if (Filename == NULL) setfname("-JVim-", NULL); docmdline(":x!"); } else { docmdline(":q!"); } getout(1); return(TRUE); } return(FALSE); } void mch_settmode(raw) int raw; { DWORD bits; GetConsoleMode(h_stdin, &bits); if (raw) { SetConsoleCtrlHandler((PHANDLER_ROUTINE)cbrk_handler, TRUE); bits &= ~(ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_ECHO_INPUT); } else { SetConsoleTextAttribute(h_stdout, NM_COLOR); bits |= (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_ECHO_INPUT); } SetConsoleMode(h_stdin, bits); } /* * Structure used by Turbo-C/Borland-C to store video parameters. */ int mch_get_winsize() { int i; CONSOLE_SCREEN_BUFFER_INFO pcinf; SMALL_RECT rect; if (GetConsoleScreenBufferInfo(h_stdout, &pcinf)) { Columns = pcinf.dwSize.X; Rows = pcinf.dwSize.Y; sy_fg_color = pcinf.wAttributes & 0x0f; sy_bg_color = (pcinf.wAttributes & 0xf0) >> 4; rect.Left = rect.Top = 0; rect.Right = Columns - 1; rect.Bottom = Rows - 1; SetConsoleWindowInfo(h_stdout, TRUE, &rect); /* COORD coord; SetConsoleScreenBufferSize(h_stdout, coord); */ } else { Columns = 25; Rows = 80; sy_fg_color = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; sy_bg_color = 0; } set_window(); if (Columns < 5 || Columns > MAX_COLUMNS || Rows < 2 || Rows > MAX_COLUMNS) { /* these values are overwritten by termcap size or default */ Columns = 80; Rows = 25; return 1; } Rows_max = Rows; /* remember physical max height */ check_winsize(); script_winsize(); return 0; } /* * Set the active window for delline/insline. */ void set_window() { /* nothing */ } void mch_set_winsize() { /* should try to set the window size to Rows and Columns */ /* may involve switching display mode.... */ } int call_shell(cmd, filter, cooked) char *cmd; int filter; /* if != 0: called by dofilter() */ int cooked; { int x; char newcmd[200]; flushbuf(); #ifdef FEPCTRL if (p_fc) fep_term(); #endif if (cooked) settmode(0); /* set to cooked mode */ if (cmd == NULL) x = spawnlp(P_WAIT, p_sh, p_sh, NULL); else { /* we use "command" to start the shell, slow but easy */ sprintf(newcmd, "%cc", get_switchar()); x = spawnlp(P_WAIT, p_sh, p_sh, newcmd, cmd, NULL); } outchar('\n'); if (cooked) settmode(1); /* set to raw mode */ if (x) { smsg("%d returned", x); outchar('\n'); } #ifdef FEPCTRL if (p_fc) fep_init(); #endif return x; } /* * check for an "interrupt signal": CTRL-break or CTRL-C */ void breakcheck() { if (ctrlc_pressed) { ctrlc_pressed = FALSE; got_int = TRUE; } } #define FL_CHUNK 512 static void addfile(fl, f, isdir) FileList *fl; char *f; int isdir; { char *p; if (!fl->file) { fl->file = (char **)alloc(sizeof(char *) * FL_CHUNK); if (!fl->file) return; fl->nfiles = 0; fl->maxfiles = FL_CHUNK; } if (fl->nfiles >= fl->maxfiles) { char **t; int i; t = (char **)lalloc(sizeof(char *) * (fl->maxfiles + FL_CHUNK), TRUE); if (!t) return; for (i = fl->nfiles - 1; i >= 0; i--) t[i] = fl->file[i]; free(fl->file); fl->file = t; fl->maxfiles += FL_CHUNK; } p = alloc((unsigned)(strlen(f) + 1 + isdir)); if (p) { strcpy(p, f); if (isdir) strcat(p, "\\"); } fl->file[fl->nfiles++] = p; } static int pstrcmp(a, b) char **a, **b; { return (strcmp(*a, *b)); } int has_wildcard(s) char *s; { if (s) for ( ; *s; ++s) if (*s == '?' || *s == '*') return 1; return 0; } static void strlowcpy(d, s) char *d, *s; { while (*s) *d++ = tolower(*s++); *d = '\0'; } static int expandpath(fl, path, fonly, donly, notf) FileList *fl; char *path; int fonly, donly, notf; { char buf[MAXPATH]; char *p, *s, *e; int lastn, c, r; struct ffblk fb; lastn = fl->nfiles; /* * Find the first part in the path name that contains a wildcard. * Copy it into buf, including the preceding characters. */ p = buf; s = NULL; e = NULL; while (*path) { if (*path == '\\' || *path == ':' || *path == '/') { if (e) break; else s = p; } if (*path == '*' || *path == '?') e = p; *p++ = *path++; } e = p; if (s) s++; else s = buf; /* now we have one wildcard component between s and e */ *e = '\0'; r = 0; /* If we are expanding wildcards we try both files and directories */ if ((c = findfirst(buf, &fb, (*path || !notf) ? FA_DIREC : 0)) != 0) { /* not found */ strcpy(e, path); if (notf) addfile(fl, buf, FALSE); return 1; /* unexpanded or empty */ } while (!c) { strlowcpy(s, fb.ff_name); if (*s != '.' || (s[1] != '\0' && (s[1] != '.' || s[2] != '\0'))) { strcat(buf, path); if (!has_wildcard(path)) addfile(fl, buf, (isdir(buf) > 0)); else r |= expandpath(fl, buf, fonly, donly, notf); } c = findnext(&fb); } qsort(fl->file + lastn, fl->nfiles - lastn, sizeof(char *), pstrcmp); return r; } /* * MSDOS rebuilt of Scott Ballantynes ExpandWildCard for amiga/arp. * jw */ int ExpandWildCards(num_pat, pat, num_file, file, files_only, list_notfound) int num_pat; char **pat; int *num_file; char ***file; int files_only, list_notfound; { int i, r = 0; FileList f; f.file = NULL; f.nfiles = 0; for (i = 0; i < num_pat; i++) { if (!has_wildcard(pat[i])) addfile(&f, pat[i], files_only ? FALSE : (isdir(pat[i]) > 0)); else r |= expandpath(&f, pat[i], files_only, 0, list_notfound); } if (r == 0) { *num_file = f.nfiles; *file = f.file; } else { *num_file = 0; *file = NULL; } return r; } void FreeWild(num, file) int num; char **file; { if (file == NULL || num <= 0) return; while (num--) free(file[num]); free(file); } /* * The normal chdir() does not change the default drive. * This one does. */ #undef chdir int vim_chdir(path) char *path; { if (path[0] == NUL) /* just checking... */ return 0; if (path[1] == ':') /* has a drive name */ { if (_chdrive(toupper(path[0]) - 'A' + 1)) return -1; /* invalid drive name */ path += 2; } if (*path == NUL) /* drive name only */ return 0; return chdir(path); /* let the normal chdir() do the rest */ } int get_switchar() { return '/'; }