kill.c revision 119610
121308Sache/* kill.c -- kill ring management. */ 221308Sache 321308Sache/* Copyright (C) 1994 Free Software Foundation, Inc. 421308Sache 521308Sache This file is part of the GNU Readline Library, a library for 621308Sache reading lines of text with interactive input and history editing. 721308Sache 821308Sache The GNU Readline Library is free software; you can redistribute it 921308Sache and/or modify it under the terms of the GNU General Public License 1058310Sache as published by the Free Software Foundation; either version 2, or 1121308Sache (at your option) any later version. 1221308Sache 1321308Sache The GNU Readline Library is distributed in the hope that it will be 1421308Sache useful, but WITHOUT ANY WARRANTY; without even the implied warranty 1521308Sache of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1621308Sache GNU General Public License for more details. 1721308Sache 1821308Sache The GNU General Public License is often shipped with GNU software, and 1921308Sache is generally kept in a file called COPYING or LICENSE. If you do not 2021308Sache have a copy of the license, write to the Free Software Foundation, 2158310Sache 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ 2221308Sache#define READLINE_LIBRARY 2321308Sache 2421308Sache#if defined (HAVE_CONFIG_H) 2521308Sache# include <config.h> 2621308Sache#endif 2721308Sache 2821308Sache#include <sys/types.h> 2921308Sache 3021308Sache#if defined (HAVE_UNISTD_H) 3121308Sache# include <unistd.h> /* for _POSIX_VERSION */ 3221308Sache#endif /* HAVE_UNISTD_H */ 3321308Sache 3421308Sache#if defined (HAVE_STDLIB_H) 3521308Sache# include <stdlib.h> 3621308Sache#else 3721308Sache# include "ansi_stdlib.h" 3821308Sache#endif /* HAVE_STDLIB_H */ 3921308Sache 4021308Sache#include <stdio.h> 4121308Sache 4221308Sache/* System-specific feature definitions and include files. */ 4321308Sache#include "rldefs.h" 4421308Sache 4521308Sache/* Some standard library routines. */ 4621308Sache#include "readline.h" 4721308Sache#include "history.h" 4821308Sache 4958310Sache#include "rlprivate.h" 5058310Sache#include "xmalloc.h" 5121308Sache 5221308Sache/* **************************************************************** */ 5321308Sache/* */ 5421308Sache/* Killing Mechanism */ 5521308Sache/* */ 5621308Sache/* **************************************************************** */ 5721308Sache 5821308Sache/* What we assume for a max number of kills. */ 5921308Sache#define DEFAULT_MAX_KILLS 10 6021308Sache 6121308Sache/* The real variable to look at to find out when to flush kills. */ 6221308Sachestatic int rl_max_kills = DEFAULT_MAX_KILLS; 6321308Sache 6421308Sache/* Where to store killed text. */ 6521308Sachestatic char **rl_kill_ring = (char **)NULL; 6621308Sache 6721308Sache/* Where we are in the kill ring. */ 6821308Sachestatic int rl_kill_index; 6921308Sache 7021308Sache/* How many slots we have in the kill ring. */ 7121308Sachestatic int rl_kill_ring_length; 7221308Sache 73119610Sachestatic int _rl_copy_to_kill_ring PARAMS((char *, int)); 74119610Sachestatic int region_kill_internal PARAMS((int)); 75119610Sachestatic int _rl_copy_word_as_kill PARAMS((int, int)); 76119610Sachestatic int rl_yank_nth_arg_internal PARAMS((int, int, int)); 77119610Sache 7821308Sache/* How to say that you only want to save a certain amount 7921308Sache of kill material. */ 8021308Sacheint 8121308Sacherl_set_retained_kills (num) 8221308Sache int num; 8321308Sache{ 8421308Sache return 0; 8521308Sache} 8621308Sache 8721308Sache/* Add TEXT to the kill ring, allocating a new kill ring slot as necessary. 8821308Sache This uses TEXT directly, so the caller must not free it. If APPEND is 8921308Sache non-zero, and the last command was a kill, the text is appended to the 9021308Sache current kill ring slot, otherwise prepended. */ 9121308Sachestatic int 9221308Sache_rl_copy_to_kill_ring (text, append) 9321308Sache char *text; 9421308Sache int append; 9521308Sache{ 9621308Sache char *old, *new; 9721308Sache int slot; 9821308Sache 9921308Sache /* First, find the slot to work with. */ 10021308Sache if (_rl_last_command_was_kill == 0) 10121308Sache { 10221308Sache /* Get a new slot. */ 10321308Sache if (rl_kill_ring == 0) 10421308Sache { 10521308Sache /* If we don't have any defined, then make one. */ 10621308Sache rl_kill_ring = (char **) 10721308Sache xmalloc (((rl_kill_ring_length = 1) + 1) * sizeof (char *)); 10821308Sache rl_kill_ring[slot = 0] = (char *)NULL; 10921308Sache } 11021308Sache else 11121308Sache { 11221308Sache /* We have to add a new slot on the end, unless we have 11321308Sache exceeded the max limit for remembering kills. */ 11421308Sache slot = rl_kill_ring_length; 11521308Sache if (slot == rl_max_kills) 11621308Sache { 11721308Sache register int i; 11821308Sache free (rl_kill_ring[0]); 11921308Sache for (i = 0; i < slot; i++) 12021308Sache rl_kill_ring[i] = rl_kill_ring[i + 1]; 12121308Sache } 12221308Sache else 12321308Sache { 12421308Sache slot = rl_kill_ring_length += 1; 12521308Sache rl_kill_ring = (char **)xrealloc (rl_kill_ring, slot * sizeof (char *)); 12621308Sache } 12721308Sache rl_kill_ring[--slot] = (char *)NULL; 12821308Sache } 12921308Sache } 13021308Sache else 13121308Sache slot = rl_kill_ring_length - 1; 13221308Sache 13321308Sache /* If the last command was a kill, prepend or append. */ 13421308Sache if (_rl_last_command_was_kill && rl_editing_mode != vi_mode) 13521308Sache { 13621308Sache old = rl_kill_ring[slot]; 137119610Sache new = (char *)xmalloc (1 + strlen (old) + strlen (text)); 13821308Sache 13921308Sache if (append) 14021308Sache { 14121308Sache strcpy (new, old); 14221308Sache strcat (new, text); 14321308Sache } 14421308Sache else 14521308Sache { 14621308Sache strcpy (new, text); 14721308Sache strcat (new, old); 14821308Sache } 14921308Sache free (old); 15021308Sache free (text); 15121308Sache rl_kill_ring[slot] = new; 15221308Sache } 15321308Sache else 15421308Sache rl_kill_ring[slot] = text; 15521308Sache 15621308Sache rl_kill_index = slot; 15721308Sache return 0; 15821308Sache} 15921308Sache 16021308Sache/* The way to kill something. This appends or prepends to the last 16121308Sache kill, if the last command was a kill command. if FROM is less 16221308Sache than TO, then the text is appended, otherwise prepended. If the 16321308Sache last command was not a kill command, then a new slot is made for 16421308Sache this kill. */ 16521308Sacheint 16621308Sacherl_kill_text (from, to) 16721308Sache int from, to; 16821308Sache{ 16921308Sache char *text; 17021308Sache 17121308Sache /* Is there anything to kill? */ 17221308Sache if (from == to) 17321308Sache { 17421308Sache _rl_last_command_was_kill++; 17521308Sache return 0; 17621308Sache } 17721308Sache 17821308Sache text = rl_copy_text (from, to); 17921308Sache 18021308Sache /* Delete the copied text from the line. */ 18121308Sache rl_delete_text (from, to); 18221308Sache 18321308Sache _rl_copy_to_kill_ring (text, from < to); 18421308Sache 18521308Sache _rl_last_command_was_kill++; 18621308Sache return 0; 18721308Sache} 18821308Sache 18921308Sache/* Now REMEMBER! In order to do prepending or appending correctly, kill 19021308Sache commands always make rl_point's original position be the FROM argument, 19121308Sache and rl_point's extent be the TO argument. */ 19221308Sache 19321308Sache/* **************************************************************** */ 19421308Sache/* */ 19521308Sache/* Killing Commands */ 19621308Sache/* */ 19721308Sache/* **************************************************************** */ 19821308Sache 19921308Sache/* Delete the word at point, saving the text in the kill ring. */ 20021308Sacheint 20121308Sacherl_kill_word (count, key) 20221308Sache int count, key; 20321308Sache{ 204119610Sache int orig_point; 20521308Sache 20621308Sache if (count < 0) 20721308Sache return (rl_backward_kill_word (-count, key)); 20821308Sache else 20921308Sache { 210119610Sache orig_point = rl_point; 21121308Sache rl_forward_word (count, key); 21221308Sache 21321308Sache if (rl_point != orig_point) 21421308Sache rl_kill_text (orig_point, rl_point); 21521308Sache 21621308Sache rl_point = orig_point; 217119610Sache if (rl_editing_mode == emacs_mode) 218119610Sache rl_mark = rl_point; 21921308Sache } 22021308Sache return 0; 22121308Sache} 22221308Sache 22321308Sache/* Rubout the word before point, placing it on the kill ring. */ 22421308Sacheint 22521308Sacherl_backward_kill_word (count, ignore) 22621308Sache int count, ignore; 22721308Sache{ 228119610Sache int orig_point; 22921308Sache 23021308Sache if (count < 0) 23121308Sache return (rl_kill_word (-count, ignore)); 23221308Sache else 23321308Sache { 234119610Sache orig_point = rl_point; 23521308Sache rl_backward_word (count, ignore); 23621308Sache 23721308Sache if (rl_point != orig_point) 23821308Sache rl_kill_text (orig_point, rl_point); 239119610Sache 240119610Sache if (rl_editing_mode == emacs_mode) 241119610Sache rl_mark = rl_point; 24221308Sache } 24321308Sache return 0; 24421308Sache} 24521308Sache 24621308Sache/* Kill from here to the end of the line. If DIRECTION is negative, kill 24721308Sache back to the line start instead. */ 24821308Sacheint 24921308Sacherl_kill_line (direction, ignore) 25021308Sache int direction, ignore; 25121308Sache{ 252119610Sache int orig_point; 25321308Sache 25421308Sache if (direction < 0) 25521308Sache return (rl_backward_kill_line (1, ignore)); 25621308Sache else 25721308Sache { 258119610Sache orig_point = rl_point; 25921308Sache rl_end_of_line (1, ignore); 26021308Sache if (orig_point != rl_point) 26121308Sache rl_kill_text (orig_point, rl_point); 26221308Sache rl_point = orig_point; 263119610Sache if (rl_editing_mode == emacs_mode) 264119610Sache rl_mark = rl_point; 26521308Sache } 26621308Sache return 0; 26721308Sache} 26821308Sache 26921308Sache/* Kill backwards to the start of the line. If DIRECTION is negative, kill 27021308Sache forwards to the line end instead. */ 27121308Sacheint 27221308Sacherl_backward_kill_line (direction, ignore) 27321308Sache int direction, ignore; 27421308Sache{ 275119610Sache int orig_point; 27621308Sache 27721308Sache if (direction < 0) 27821308Sache return (rl_kill_line (1, ignore)); 27921308Sache else 28021308Sache { 28121308Sache if (!rl_point) 28275406Sache rl_ding (); 28321308Sache else 28421308Sache { 285119610Sache orig_point = rl_point; 28621308Sache rl_beg_of_line (1, ignore); 287119610Sache if (rl_point != orig_point) 288119610Sache rl_kill_text (orig_point, rl_point); 289119610Sache if (rl_editing_mode == emacs_mode) 290119610Sache rl_mark = rl_point; 29121308Sache } 29221308Sache } 29321308Sache return 0; 29421308Sache} 29521308Sache 29621308Sache/* Kill the whole line, no matter where point is. */ 29721308Sacheint 29821308Sacherl_kill_full_line (count, ignore) 29921308Sache int count, ignore; 30021308Sache{ 30121308Sache rl_begin_undo_group (); 30221308Sache rl_point = 0; 30321308Sache rl_kill_text (rl_point, rl_end); 304119610Sache rl_mark = 0; 30521308Sache rl_end_undo_group (); 30621308Sache return 0; 30721308Sache} 30821308Sache 30921308Sache/* The next two functions mimic unix line editing behaviour, except they 31021308Sache save the deleted text on the kill ring. This is safer than not saving 31121308Sache it, and since we have a ring, nobody should get screwed. */ 31221308Sache 31321308Sache/* This does what C-w does in Unix. We can't prevent people from 31421308Sache using behaviour that they expect. */ 31521308Sacheint 31621308Sacherl_unix_word_rubout (count, key) 31721308Sache int count, key; 31821308Sache{ 31921308Sache int orig_point; 32021308Sache 32121308Sache if (rl_point == 0) 32275406Sache rl_ding (); 32321308Sache else 32421308Sache { 32521308Sache orig_point = rl_point; 32621308Sache if (count <= 0) 32721308Sache count = 1; 32821308Sache 32921308Sache while (count--) 33021308Sache { 33121308Sache while (rl_point && whitespace (rl_line_buffer[rl_point - 1])) 33221308Sache rl_point--; 33321308Sache 33421308Sache while (rl_point && (whitespace (rl_line_buffer[rl_point - 1]) == 0)) 33521308Sache rl_point--; 33621308Sache } 33721308Sache 33821308Sache rl_kill_text (orig_point, rl_point); 339119610Sache if (rl_editing_mode == emacs_mode) 340119610Sache rl_mark = rl_point; 34121308Sache } 34221308Sache return 0; 34321308Sache} 34421308Sache 34521308Sache/* Here is C-u doing what Unix does. You don't *have* to use these 34621308Sache key-bindings. We have a choice of killing the entire line, or 34721308Sache killing from where we are to the start of the line. We choose the 34821308Sache latter, because if you are a Unix weenie, then you haven't backspaced 34921308Sache into the line at all, and if you aren't, then you know what you are 35021308Sache doing. */ 35121308Sacheint 35221308Sacherl_unix_line_discard (count, key) 35321308Sache int count, key; 35421308Sache{ 35521308Sache if (rl_point == 0) 35675406Sache rl_ding (); 35721308Sache else 35821308Sache { 35921308Sache rl_kill_text (rl_point, 0); 36021308Sache rl_point = 0; 361119610Sache if (rl_editing_mode == emacs_mode) 362119610Sache rl_mark = rl_point; 36321308Sache } 36421308Sache return 0; 36521308Sache} 36621308Sache 36721308Sache/* Copy the text in the `region' to the kill ring. If DELETE is non-zero, 36821308Sache delete the text from the line as well. */ 36921308Sachestatic int 37021308Sacheregion_kill_internal (delete) 37121308Sache int delete; 37221308Sache{ 37321308Sache char *text; 37421308Sache 375119610Sache if (rl_mark != rl_point) 37621308Sache { 377119610Sache text = rl_copy_text (rl_point, rl_mark); 378119610Sache if (delete) 379119610Sache rl_delete_text (rl_point, rl_mark); 380119610Sache _rl_copy_to_kill_ring (text, rl_point < rl_mark); 38121308Sache } 38221308Sache 38321308Sache _rl_last_command_was_kill++; 38421308Sache return 0; 38521308Sache} 38621308Sache 38721308Sache/* Copy the text in the region to the kill ring. */ 38821308Sacheint 38921308Sacherl_copy_region_to_kill (count, ignore) 39021308Sache int count, ignore; 39121308Sache{ 39221308Sache return (region_kill_internal (0)); 39321308Sache} 39421308Sache 39521308Sache/* Kill the text between the point and mark. */ 39621308Sacheint 39721308Sacherl_kill_region (count, ignore) 39821308Sache int count, ignore; 39921308Sache{ 40058310Sache int r, npoint; 40126497Sache 40258310Sache npoint = (rl_point < rl_mark) ? rl_point : rl_mark; 40326497Sache r = region_kill_internal (1); 40426497Sache _rl_fix_point (1); 40558310Sache rl_point = npoint; 40626497Sache return r; 40721308Sache} 40821308Sache 40921308Sache/* Copy COUNT words to the kill ring. DIR says which direction we look 41021308Sache to find the words. */ 41121308Sachestatic int 41221308Sache_rl_copy_word_as_kill (count, dir) 41321308Sache int count, dir; 41421308Sache{ 41521308Sache int om, op, r; 41621308Sache 41721308Sache om = rl_mark; 41821308Sache op = rl_point; 41921308Sache 42021308Sache if (dir > 0) 42121308Sache rl_forward_word (count, 0); 42221308Sache else 42321308Sache rl_backward_word (count, 0); 42421308Sache 42521308Sache rl_mark = rl_point; 42621308Sache 42721308Sache if (dir > 0) 42821308Sache rl_backward_word (count, 0); 42921308Sache else 43021308Sache rl_forward_word (count, 0); 43121308Sache 43221308Sache r = region_kill_internal (0); 43321308Sache 43421308Sache rl_mark = om; 43521308Sache rl_point = op; 43621308Sache 43721308Sache return r; 43821308Sache} 43921308Sache 44021308Sacheint 44121308Sacherl_copy_forward_word (count, key) 44221308Sache int count, key; 44321308Sache{ 44421308Sache if (count < 0) 44521308Sache return (rl_copy_backward_word (-count, key)); 44621308Sache 44721308Sache return (_rl_copy_word_as_kill (count, 1)); 44821308Sache} 44921308Sache 45021308Sacheint 45121308Sacherl_copy_backward_word (count, key) 45221308Sache int count, key; 45321308Sache{ 45421308Sache if (count < 0) 45521308Sache return (rl_copy_forward_word (-count, key)); 45621308Sache 45721308Sache return (_rl_copy_word_as_kill (count, -1)); 45821308Sache} 45921308Sache 46021308Sache/* Yank back the last killed text. This ignores arguments. */ 46121308Sacheint 46221308Sacherl_yank (count, ignore) 46321308Sache int count, ignore; 46421308Sache{ 46521308Sache if (rl_kill_ring == 0) 46621308Sache { 46721308Sache _rl_abort_internal (); 46821308Sache return -1; 46921308Sache } 47021308Sache 47121308Sache _rl_set_mark_at_pos (rl_point); 47221308Sache rl_insert_text (rl_kill_ring[rl_kill_index]); 47321308Sache return 0; 47421308Sache} 47521308Sache 47621308Sache/* If the last command was yank, or yank_pop, and the text just 47721308Sache before point is identical to the current kill item, then 47821308Sache delete that text from the line, rotate the index down, and 47921308Sache yank back some other text. */ 48021308Sacheint 48121308Sacherl_yank_pop (count, key) 48221308Sache int count, key; 48321308Sache{ 48421308Sache int l, n; 48521308Sache 48621308Sache if (((rl_last_func != rl_yank_pop) && (rl_last_func != rl_yank)) || 48721308Sache !rl_kill_ring) 48821308Sache { 48921308Sache _rl_abort_internal (); 49021308Sache return -1; 49121308Sache } 49221308Sache 49321308Sache l = strlen (rl_kill_ring[rl_kill_index]); 49421308Sache n = rl_point - l; 49521308Sache if (n >= 0 && STREQN (rl_line_buffer + n, rl_kill_ring[rl_kill_index], l)) 49621308Sache { 49721308Sache rl_delete_text (n, rl_point); 49821308Sache rl_point = n; 49921308Sache rl_kill_index--; 50021308Sache if (rl_kill_index < 0) 50121308Sache rl_kill_index = rl_kill_ring_length - 1; 50221308Sache rl_yank (1, 0); 50321308Sache return 0; 50421308Sache } 50521308Sache else 50621308Sache { 50721308Sache _rl_abort_internal (); 50821308Sache return -1; 50921308Sache } 51021308Sache} 51121308Sache 51235486Sache/* Yank the COUNTh argument from the previous history line, skipping 51335486Sache HISTORY_SKIP lines before looking for the `previous line'. */ 51435486Sachestatic int 51535486Sacherl_yank_nth_arg_internal (count, ignore, history_skip) 51635486Sache int count, ignore, history_skip; 51721308Sache{ 51821308Sache register HIST_ENTRY *entry; 51921308Sache char *arg; 52058310Sache int i, pos; 52121308Sache 52258310Sache pos = where_history (); 52358310Sache 52435486Sache if (history_skip) 52535486Sache { 52635486Sache for (i = 0; i < history_skip; i++) 52735486Sache entry = previous_history (); 52835486Sache } 52935486Sache 53021308Sache entry = previous_history (); 53158310Sache 53258310Sache history_set_pos (pos); 53358310Sache 53458310Sache if (entry == 0) 53535486Sache { 53675406Sache rl_ding (); 53721308Sache return -1; 53821308Sache } 53921308Sache 54021308Sache arg = history_arg_extract (count, count, entry->line); 54121308Sache if (!arg || !*arg) 54221308Sache { 54375406Sache rl_ding (); 54421308Sache return -1; 54521308Sache } 54621308Sache 54721308Sache rl_begin_undo_group (); 54821308Sache 549119610Sache _rl_set_mark_at_pos (rl_point); 550119610Sache 55121308Sache#if defined (VI_MODE) 55221308Sache /* Vi mode always inserts a space before yanking the argument, and it 55321308Sache inserts it right *after* rl_point. */ 55421308Sache if (rl_editing_mode == vi_mode) 55521308Sache { 55626497Sache rl_vi_append_mode (1, ignore); 55721308Sache rl_insert_text (" "); 55821308Sache } 55921308Sache#endif /* VI_MODE */ 56021308Sache 56121308Sache rl_insert_text (arg); 56221308Sache free (arg); 56321308Sache 56421308Sache rl_end_undo_group (); 56521308Sache return 0; 56621308Sache} 56721308Sache 56835486Sache/* Yank the COUNTth argument from the previous history line. */ 56935486Sacheint 57035486Sacherl_yank_nth_arg (count, ignore) 57135486Sache int count, ignore; 57235486Sache{ 57335486Sache return (rl_yank_nth_arg_internal (count, ignore, 0)); 57435486Sache} 57535486Sache 57621308Sache/* Yank the last argument from the previous history line. This `knows' 57721308Sache how rl_yank_nth_arg treats a count of `$'. With an argument, this 57821308Sache behaves the same as rl_yank_nth_arg. */ 57921308Sacheint 58021308Sacherl_yank_last_arg (count, key) 58121308Sache int count, key; 58221308Sache{ 58335486Sache static int history_skip = 0; 58435486Sache static int explicit_arg_p = 0; 58535486Sache static int count_passed = 1; 58635486Sache static int direction = 1; 58747558Sache static int undo_needed = 0; 58847558Sache int retval; 58935486Sache 59035486Sache if (rl_last_func != rl_yank_last_arg) 59135486Sache { 59235486Sache history_skip = 0; 59335486Sache explicit_arg_p = rl_explicit_arg; 59435486Sache count_passed = count; 59535486Sache direction = 1; 59635486Sache } 59721308Sache else 59835486Sache { 59947558Sache if (undo_needed) 60047558Sache rl_do_undo (); 60135486Sache if (count < 1) 60235486Sache direction = -direction; 60335486Sache history_skip += direction; 60435486Sache if (history_skip < 0) 60535486Sache history_skip = 0; 60635486Sache } 60735486Sache 60835486Sache if (explicit_arg_p) 60947558Sache retval = rl_yank_nth_arg_internal (count_passed, key, history_skip); 61035486Sache else 61147558Sache retval = rl_yank_nth_arg_internal ('$', key, history_skip); 61247558Sache 61347558Sache undo_needed = retval == 0; 61447558Sache return retval; 61521308Sache} 61635486Sache 61735486Sache/* A special paste command for users of Cygnus's cygwin32. */ 61875406Sache#if defined (__CYGWIN__) 61935486Sache#include <windows.h> 62035486Sache 62135486Sacheint 62235486Sacherl_paste_from_clipboard (count, key) 62335486Sache int count, key; 62435486Sache{ 62535486Sache char *data, *ptr; 62635486Sache int len; 62735486Sache 62835486Sache if (OpenClipboard (NULL) == 0) 62935486Sache return (0); 63035486Sache 63135486Sache data = (char *)GetClipboardData (CF_TEXT); 63235486Sache if (data) 63335486Sache { 63435486Sache ptr = strchr (data, '\r'); 63535486Sache if (ptr) 63635486Sache { 63735486Sache len = ptr - data; 638119610Sache ptr = (char *)xmalloc (len + 1); 63935486Sache ptr[len] = '\0'; 64035486Sache strncpy (ptr, data, len); 64135486Sache } 64235486Sache else 64335486Sache ptr = data; 644119610Sache _rl_set_mark_at_pos (rl_point); 64535486Sache rl_insert_text (ptr); 64635486Sache if (ptr != data) 64735486Sache free (ptr); 64835486Sache CloseClipboard (); 64935486Sache } 65035486Sache return (0); 65135486Sache} 65275406Sache#endif /* __CYGWIN__ */ 653