Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Patches for New Features



I have spent some time over the last several days delving into zshell (I
had jury duty! :-) ) and have made some exciting (to me, anyway)
improvements that I'd like to get some feedback on, and would love to
see in a future version of Zshell.  The patches are attached.  The
enhancements they provide have been tested both on Linux-2.0.12 (RedHat
3.0.3 w/ upgraded kernel) and on Sun Solaris 2.4 (where I need a
-ltermcap to build properly-- configure problem??)  Briefly, the
enhancements are post-prompt printing, partial word "CamelCaps" movement
and deletion in zle, rudimentary dynamic abbreviation (like emacs), and
color-ls like colorized filename completion listings.  Detailed
descriptions follow.

Please note! ALL USUAL DISCLAIMERS APPLY! I am not responsible for any
problems that this causes you, I hope that it is useful but make no
claims whatsoever about its functioning or correctness.  You may
redistribute unmodified, modify it for personal use (please send me a
copy).  I'd be happy to see the patches become part of version 3.1.

===============================================
o A postprompt mechanism which allows the user to customize a "prompt"
string (print -P format) to be output to the terminal after the most
recent foreground process has been executing for a given number of
seconds.  (An improvement over a similar patch with limited distribution
for zsh-2.5.03).  My main goal for this feature is to make Zshell output an
xterm control sequence to change the window/icon name based on the
current foreground process, totally automatically.  For example:

part of my .zshrc
----------------------
...
# InteractiveComments are nice to add an extra note to a command line
setopt interactivecomments
setopt promptsubst

# The escape sequences only work in xterm or color_xterm's, so
# guard for them
if [ "$TERM" == "xterm" -o "$TERM" == "color_xterm" ]; then
  # Output an initial string to set the window title to the hostname
  # followed by a # if you're root
  print -nP '%{\e]0;%m%(#.#.)\C-g%}'
  # %H is a new prompt character meaning the last zle line
  # I use cmd substitution w/ print here so that
  # my escape characters are cut & pastable
  POSTPROMPT=`print -n '%{\e]0;%m%(#.#.) %H\C-g%}'`
  # We only commands that run for >= 4 seconds will output the postprompt
  # This eliminates trivial commands like "ls", etc.
  PPTMOUT=4
fi
-----------------------

As you can see above, the new mechanism is controlled by:

$POSTPROMPT variable, set to the prompt to print
$PPTMOUT variable, set to the number of seconds to wait before
  outputting the post-prompt.  Or set to 0 for no post-prompt (the
  default) or set to -1 for no waiting (output it right away).
%H as a new prompt substitution character, to mean the last zle line.


Try this feature out with "sleep 6", or just man zshall, then wait four
seconds-- your window title should change, making that xterm with the
man page easier to find in your window manager's window list (I use
FVWM2, and this works great!)

===============================================
o Simple new ZLE functions, {for,back}ward-word-part and 
  {for,back}ward-delete-word-part.  These move through CamelCaps
  occurring in the zle line.  I have these bound like so:

bindkey "\eOD" backward-word-part
bindkey "\eOC" forward-word-part
bindkey "^\\" backward-delete-word-part

  Where my .Xdefaults overrides the C-S-H keystroke to give \eOD, and
the C-S-L keystroke to give \eOC.  They should work with numeric args,
etc. and may not be useful to everyone, but they give some added
flexibility, and make zle capabilities more like emacs.
===============================================
o Rudimentary (read "poor") dabbrev-complete ZLE function.  I'd love to
  see someone who knows about zsh's completion mechanism do this right.
****It is a feature of tcsh that zsh lacks******! Currently it doesn't
  complete back on the currently line, and doesn't cycle choices
properly, but since I know I'm doing this wrong I didn't want to spend
more time on it.  By default, bound to M-/, but must compile with
-DSIMPLE_DABBREV_GJB to enable it.  (See bindkey -me and your terminal
settings to see if your meta key will work).
===============================================
and last but certainly not least!

o Color-ls like colorized filename completion listings.  Analogous to
the LIST_TYPES option, my new LIST_COLORS option turns this feature on.
It uses ZLS_COLORS, ZLS_COLOURS, LS_COLORS, LS_COLOURS [first found, in
that order] to set the colors options.  See dircolors(1) from the
color-ls patch, available on sunsite and elsewhere.  If you don't have
dircolors and the color-ls patch to ls, get them, but here is my
LS_COLORS setting so you can use it with zsh:

export LS_COLORS="no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:\
bd=40;33;01:cd=40;33;01:or=01;05;31;40:ex=32;40:\
*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:\
*.tar=00;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:\
*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.rpm=34:*.jpg=01;35:\
*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:"

Be sure to only set the listcolors option when running color_xterm, or
another terminal type that supports ansi color sequences (linux console
is fine, too).  Otherwise you'll get some bizarre escape sequences
showing up.

I use:
unsetopt MENU_COMPLETE
setopt AUTO_LIST
setopt AUTO_MENU
setenv LISTMAX 0

Try ls /<TAB> or just ls <TAB> then type the beginning of a directory
name, complete it, and hit <TAB> again to list that directory.
==================================================

Please give me feedback about what you like and what you don't like.
Also, if anyone is going to pick up the dabbrevcomplete function, I'd
love to drop it from my patches.  Alternatively, hints and/or pointers
are appreciated.

Attached are separate patches, one for the post prompt feature, and a
second for everything else.  I know these work when applied in that
order. Please let me know if something doesn't work for you, with the
usual detailed description.  I can't promise to fix it, but I will when
it's important and time permits.  You may also be able to find these
patches on my web site, http://www.cs.duke.edu/~gjb, in a couple days.

Greg J. Badros
gjb@xxxxxxxxxxx
http://www.cs.duke.edu/~gjb
*** globals.h	1996/08/19 02:06:53	1.1
--- globals.h	1996/08/20 01:03:30	1.2
***************
*** 331,336 ****
--- 331,337 ----
  EXTERN char *prompt4;
  EXTERN char *rprompt;		/* $RPROMPT    */
  EXTERN char *sprompt;
+ EXTERN char *postprompt;
  
  EXTERN char *wordchars;
  EXTERN char *rstring, *Rstring;
*** hashtable.h	1996/08/19 23:49:13	1.1
--- hashtable.h	1996/08/20 01:02:15	1.2
***************
*** 156,161 ****
--- 156,162 ----
  IPDEF7("PS4", &prompt4),
  IPDEF7("RPS1", &rprompt),
  IPDEF7("SPROMPT", &sprompt),
+ IPDEF7("POSTPROMPT", &postprompt),
  IPDEF7("0", &argzero),
  
  #define IPDEF8(A,B,C) {NULL,A,PM_SCALAR|PM_SPECIAL,NULL,IFN(colonarrsetfn),IFN(colonarrgetfn),0,(void *)B,NULL,C,NULL,0}
*** init.c	1996/08/18 20:27:14	1.1
--- init.c	1996/08/20 01:03:45	1.2
***************
*** 515,520 ****
--- 515,521 ----
  	prompt3 = ztrdup("");
  	prompt4 = ztrdup("");
      }
+     postprompt = ztrdup("");
      sprompt = ztrdup("zsh: correct '%R' to '%r' [nyae]? ");
  
      if (!(ttystrname = ztrdup(ttyname(SHTTY))))
*** signals.c	1996/08/18 20:33:52	1.1
--- signals.c	1996/08/20 01:01:55	1.2
***************
*** 309,314 ****
--- 309,315 ----
  
      sigfillset(&set);
      sigdelset(&set, sig);
+     sigdelset(&set, SIGALRM);
      sigdelset(&set, SIGHUP);  /* still don't know why we add this? */
      if (sig2)
          sigdelset(&set, sig2);
***************
*** 319,324 ****
--- 320,326 ----
  
      sigfillset(&set);
      sigdelset(&set, sig);
+     sigdelset(&set, SIGALRM);
      if (sig2)
        sigdelset(&set, sig2);
      ret = sigpause(set);
***************
*** 538,549 ****
          break;
  
      case SIGALRM:
!         if (sigtrapped[SIGALRM]) {
  	    int tmout;
!             dotrap(SIGALRM);
!             if ((tmout = getiparam("TMOUT")))
!                 alarm(tmout);           /* reset the alarm */
!         } else {
  	    int idle = ttyidlegetfn(NULL);
  	    int tmout = getiparam("TMOUT");
  	    if (idle >= 0 && idle < tmout)
--- 540,559 ----
          break;
  
      case SIGALRM:
! 	if (!zleactive) {
! 	    /* One possibility is to do the TRAPALRM fn -- however,
! 	       I choose to save that for the TMOUT mechanism-- a new prompt
! 	       is all we really need, methinks */
! 	    /*dotrap(SIGALRM);*/
! 	    putpostprompt();
! 	    alarm(0);
! 	    }
! 	else if (sigtrapped[SIGALRM]) {
  	    int tmout;
! 	    dotrap(SIGALRM);
! 	    if ((tmout = getiparam("TMOUT")))
! 		alarm(tmout);		/* reset the alarm */
! 	} else {
  	    int idle = ttyidlegetfn(NULL);
  	    int tmout = getiparam("TMOUT");
  	    if (idle >= 0 && idle < tmout)
*** version.h	1996/08/18 20:42:01	1.1
--- version.h	1996/08/20 01:04:06	1.2
***************
*** 1 ****
! #define ZSH_VERSION "3.0.0"
--- 1 ----
! #define ZSH_VERSION "3.0.0-gjb"
*** zle_main.c	1996/08/18 19:57:24	1.1
--- zle_main.c	1996/08/20 00:59:48	1.2
***************
*** 342,347 ****
--- 342,363 ----
  
  /* Read a line.  It is returned metafied. */
  
+ #define cchMaxInZleLine 64
+ char szLastZleLine[cchMaxInZleLine];
+ 
+ /**/
+ void
+ putpostprompt(void)
+ {
+     int plen = 0;
+     char *szPostPrompt = putprompt(postprompt,&plen,NULL,0);
+     /*    fprintf(stderr,"SIGALRM case. szPostPrompt = %sn",szPostPrompt); 
+     fflush(stderr); */
+     fwrite(szPostPrompt,sizeof(char),plen,stderr);
+     fflush(stderr);
+     free(szPostPrompt);
+ }
+ 
  /**/
  unsigned char *
  zleread(char *lp, char *rp)
***************
*** 417,424 ****
  	initundo();
  	if (isset(PROMPTCR))
  	    putc('\r', shout);
! 	if (tmout)
! 	    alarm(tmout);
  	genprompts();
  	zleactive = 1;
  	resetneeded = 1;
--- 433,439 ----
  	initundo();
  	if (isset(PROMPTCR))
  	    putc('\r', shout);
! 	alarm(tmout);
  	genprompts();
  	zleactive = 1;
  	resetneeded = 1;
***************
*** 489,496 ****
  	statusline = NULL;
  	invalidatelist();
  	trashzle();
  	zleactive = 0;
! 	alarm(0);
      } LASTALLOC;
      zsfree(curhistline);
      free(lastline);		/* freeundo */
--- 504,515 ----
  	statusline = NULL;
  	invalidatelist();
  	trashzle();
+ 	strncpy(szLastZleLine, line, cchMaxInZleLine);
  	zleactive = 0;
! 	if ((tmout = getiparam("PPTMOUT")) < 0)
! 	    putpostprompt();
! 	else /* if tmout == 0, we want to remove the alarm, so just call alarm */
! 	    alarm(tmout);
      } LASTALLOC;
      zsfree(curhistline);
      free(lastline);		/* freeundo */
*** zle_misc.c	1996/08/19 02:04:38	1.1
--- zle_misc.c	1996/08/20 01:00:32	1.2
***************
*** 805,810 ****
--- 805,812 ----
  static char *buf, *bp1, *bl0, *fm, *pmpt;
  static int bracepos, bufspc;
  
+ extern char szLastZleLine[];
+ 
  /**/
  char *
  putprompt(char *fmin, int *lenp, int *wp, int cnt)
***************
*** 1038,1043 ****
--- 1040,1048 ----
  		break;
  	    case 'M':
  		stradd(hostnam);
+ 		break;
+ 	    case 'H':
+ 		stradd(szLastZleLine);
  		break;
  	    case 'm':
  		if (!arg)
*** globals.h	1996/08/20 01:03:30	1.2
--- globals.h	1996/08/21 19:24:00	1.3
***************
*** 732,737 ****
--- 732,738 ----
      {"listambiguous", 		0,    0,    0},
      {"listbeep", 		0,    0,    OPT_ALL},
      {"listtypes", 		'X',  0,    OPT_CSH},
+     {"listcolors", 		0,    0,    0},
      {"localoptions", 		0,    0,    OPT_EMULATE|OPT_KSH},
      {"login", 			'l',  'l',  OPT_SPECIAL},
      {"longlistjobs", 		'R',  0,    0},
*** zle_tricky.c	1996/08/20 13:59:54	1.1
--- zle_tricky.c	1996/08/21 20:07:48	1.3
***************
*** 211,219 ****
  #define COMP_COMPLETE 0
  #define COMP_LIST_COMPLETE 1
  #define COMP_SPELL 2
! #define COMP_EXPAND 3
! #define COMP_EXPAND_COMPLETE 4
! #define COMP_LIST_EXPAND 5
  #define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
  
  /**/
--- 211,220 ----
  #define COMP_COMPLETE 0
  #define COMP_LIST_COMPLETE 1
  #define COMP_SPELL 2
! #define COMP_DABBREV 3
! #define COMP_EXPAND 4
! #define COMP_EXPAND_COMPLETE 5
! #define COMP_LIST_EXPAND 6
  #define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
  
  /**/
***************
*** 257,262 ****
--- 258,364 ----
      docomplete(COMP_SPELL);
  }
  
+ #ifdef SIMPLE_DABBREV_GJB
+ /* FIX: This should really use some of the completion stuff provided
+    elsewhere */
+ /**/
+ void
+ dabbrevcomplete(void)
+ {
+     char *pchStartWord = line+cs;
+     int ich = cs;
+     int cchWord = 0;
+     int ihistSearch = curhist;
+     Comp compc = NULL;
+     char *e;
+     char *h;
+     char hpatsav;
+     Histent he;
+     static int ihistLastWhenCompleting = -1;
+     static int ihistLastSearched = -1;
+     static char *pchStartWordLast = NULL;
+     static char cchInserted = 0;
+ 
+     if (ich > 0)
+ 	{
+ 	ich--;
+ 	pchStartWord--;
+ 	}
+     while (ich && !isspace(*pchStartWord))
+ 	{
+ 	ich--;
+ 	pchStartWord--;
+ 	}
+     if (isspace(*pchStartWord)) {
+ 	ich++;
+ 	pchStartWord++;
+     }
+     cchWord = cs-ich;
+     
+     /* Check if this is our second consecutive press, to continue old search */
+     if (ihistLastWhenCompleting == curhist && pchStartWordLast == pchStartWord
+ 	&& (lastcmd & ZLE_MENUCMP)) {
+ 	/* continue old search */
+ 	backdel(cchInserted);
+ 	cchInserted = 0;
+ 	ihistSearch = ihistLastSearched - 1;
+ 	if (ihistSearch < 0) {
+ 	    ihistSearch = curhist;
+ 	}
+     }
+ 
+     pchStartWordLast = pchStartWord;
+ 
+     while (1) {
+ 	/* Parse the pattern, if it isn't the null string. */
+ 	if (*pchStartWord) {
+ 	    char *thpat = dupstring(pchStartWord);
+ 	    thpat[cchWord] = '*';
+ 	    thpat[cchWord+1] = 0;
+ 	    tokenize(thpat);
+ 	    compc = parsereg(thpat);
+ 	}
+ 
+ 	/* FIX: First check earlier on the current line (in "line") */
+ 
+ 	/* Now search the history. */
+ 	while ((he = quietgethist(ihistSearch--))) {
+ 	    int iwords;
+ 	    for (iwords = 0; iwords < he->nwords; iwords++) {
+ 		h = he->text + he->words[iwords*2];
+ 		e = he->text + he->words[iwords*2+1];
+ 		hpatsav = *e;
+ 		*e = '\0';
+ 		/* We now have a word from the history, ignore it *
+ 		 * if it begins with a quote or `$'.              */
+ 		if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' &&
+ 		    (!compc || domatch(h, compc, 0))) {
+ 		    cchInserted = strlen(h) - cchWord;
+ 		    /* Otherwise add it if it was matched. */
+ 		    inststrlen(h+cchWord,1,-1);
+ 		    goto cont;
+ 		}
+ 		if (hpatsav)
+ 		    *e = hpatsav;
+ 	    }
+ 	}
+ 	/* FIX: perhaps give it a prefix of '*' and retry */
+ 	break;
+     }
+ cont:
+     ihistLastSearched = ihistSearch;
+     ihistLastWhenCompleting = curhist;
+ }
+ #else /* ! SIMPLE_DABBREV_GJB */
+ /**/
+ void
+ dabbrevcomplete(void)
+ {
+     usemenu = useglob = 0;
+     docomplete(COMP_DABBREV);
+ }
+ #endif /* SIMPLE_DABBREV_GJB */
+ 
  /**/
  void
  deletecharorlist(void)
***************
*** 686,692 ****
  	    /* call the real spell checker, ash@xxxxxxxxxx */
  	    spckword(x, 0, lincmd, 0);
  	    inststr(*x);
! 	} else if (COMP_ISEXPAND(lst)) {
  	    /* Do expansion. */
  	    char *ol = (olst == COMP_EXPAND_COMPLETE) ?
  		dupstring((char *)line) : (char *)line;
--- 788,800 ----
  	    /* call the real spell checker, ash@xxxxxxxxxx */
  	    spckword(x, 0, lincmd, 0);
  	    inststr(*x);
! 	} 
! #ifndef SIMPLE_DABBREV_GJB
! 	else if (lst == COMP_DABBREV) {
! 	    /* FIX: What to do??? */
! 	}
! #endif
! 	else if (COMP_ISEXPAND(lst)) {
  	    /* Do expansion. */
  	    char *ol = (olst == COMP_EXPAND_COMPLETE) ?
  		dupstring((char *)line) : (char *)line;
***************
*** 3480,3485 ****
--- 3588,4085 ----
      return l + (cc / COLUMNS);
  }
  
+ /* Greg J. Badros's color matching feature adapted from color-ls patch, to fileutils-3.12
+    (color-ls by Dennis Flaherty <dennisf@xxxxxxxxxxxxxxxxxxx>
+    and Peter Anvin <Peter.Anvin@xxxxxxxxx> based on original patches by Greg Lee
+    <lee@xxxxxxxxxxxxxxxxxxxxxx>) */
+ 
+ /* These defn's were selectively lifted from the color-ls patch */
+  
+ /* Nonzero means use colors to mark types.  Also define the different
+    colors as well as the stuff for the LS_COLORS environment variable.
+    The LS_COLORS variable is now in a termcap-like format.  -o */
+ 
+ /* Semantics changed by GJB-- isset(LISTCOLORS) tests whether user wants
+    color listing, this says whether the (Z)LS_COLO(U)RS vars were parsed
+    so she can have it */
+ int print_with_color;
+ 
+ /* Note that color_no and color_yes equals boolean values; they will
+    be assigned to print_with_color which is a boolean variable */
+ 
+ enum indicator_no
+ { C_LEFT, C_RIGHT, C_END, C_NORM, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK, 
+   C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC };
+ 
+ char *indicator_name[]=
+ {
+   "lc","rc","ec","no","fi","di","ln","pi","so","bd","cd","mi","or","ex",0
+ };
+ 
+ /* Null is a valid character in a color indicator (think about Epson
+    printers, for example) so we have to use a length/buffer string
+    type. */
+ 
+ struct bin_str
+ {
+   unsigned int len;		/* Number of bytes */
+   char *string;			/* Pointer to the same */
+ };
+ 
+ struct bin_str color_indicator[] =
+ {
+   { 2, "\033[" },		/* lc: Left of color sequence */
+   { 1, "m" },			/* rc: Right of color sequence */
+   { 0, NULL },			/* ec: End color (replaces lc+no+rc) */
+   { 1, "0" },			/* no: Normal */
+   { 1, "0" },			/* fi: File: default */
+   { 2, "32" },			/* di: Directory: green */
+   { 2, "36" },			/* ln: Symlink: cyan */
+   { 2, "31" },			/* pi: Pipe: red */
+   { 2, "33" },			/* so: Socket: yellow/brown */
+   { 5, "44;37" },		/* bd: Block device: white on blue */
+   { 5, "44;37" },		/* cd: Char device: white on blue */
+   { 0, NULL },			/* mi: Missing file: undefined */
+   { 0, NULL },			/* or: Orphanned symlink: undefined */
+   { 2, "35" }			/* ex: Executable: purple */
+ };
+ 
+ struct col_ext_type
+ {
+   struct bin_str ext;		/* The extension we're looking for */
+   struct bin_str seq;		/* The sequence to output when we do */
+   struct col_ext_type *next;	/* Next in list */
+ };
+ 
+ struct col_ext_type *col_ext_list = NULL;
+ char *color_buf;		/* Buffer for color sequences */
+ 
+ /* prototypes for color list-matching fns, added by GJB */
+ static void parse_ls_color ();
+ static int get_funky_string (char **dest, char **src, int equals_end);
+ static void print_color_indicator (FILE *stream, char *name, unsigned int mode, int linkok);
+ static void put_indicator(FILE *stream, struct bin_str *ind);
+ 
+ /* Parse the (Z)LS_COLORS/(Z)LS_COLOURS variable */
+ 
+ /* Function parse_ls_color from color-ls patch, now uses getsparam instead
+    of getenv, and checks for ZLS_COLORS and ZLS_COLOURS before the names w/o Z */
+ 
+ static void
+ parse_ls_color ()
+ {
+   char *p;			/* Pointer to character being parsed */
+   char *whichvar;		/* LS_COLORS or LS_COLOURS? */
+   char *buf;			/* color_buf buffer pointer */
+   int state;			/* State of parser */
+   int ind_no;			/* Indicator number */
+   char label[3] = "??";		/* Indicator label */
+   struct col_ext_type *ext = 0;	/* Extension we are working on */
+   struct col_ext_type *ext2 = 0;/* Extra pointer */
+ 
+   /* Let ZLS_COLO(U)RS take precedence of LS_COLO(U)RS in case
+      the user wants different colors for zsh completion listing and color-ls */
+   if ( (p = getsparam(whichvar = "ZLS_COLORS")) ||
+        (p = getsparam(whichvar = "ZLS_COLOURS")) ||
+        (p = getsparam(whichvar = "LS_COLORS")) ||
+        (p = getsparam(whichvar = "LS_COLOURS")) ) {
+       /* print_with_color's semantics have changed from color_ls --
+ 	 isset(LISTCOLORS) is used to tell if the user *wants* color,
+ 	 while print_with_color is set true if we parsed a color variable ok
+ 	 so she may see the color */
+       print_with_color = 1;
+       buf = color_buf = (char *) zalloc(strlen(p));
+       /* This is an overly conservative estimate, but any possible
+          LS_COLORS string will *not* generate a color_buf longer than
+ 	 itself, so it is a safe way of allocating a buffer in
+ 	 advance. */
+       
+       state = 1;
+       while ( state > 0 ) 
+ 	{
+ 	  switch(state)
+ 	    {
+ 	    case 1:		/* First label character */
+ 	      switch ( *p )
+ 		{
+ 		case ':':
+ 		  p++;
+ 		  break;
+ 		  
+ 		case '*':
+ 		  /* Allocate new extension block and add to head
+ 		     of linked list (this way a later definition will
+ 		     override an earlier one, which can be useful for
+ 		     having terminal-specific defs override global) */
+ 		  
+ 		  ext = (struct col_ext_type *)
+ 		    zalloc(sizeof(struct col_ext_type));
+ 		  ext->next = col_ext_list;
+ 		  col_ext_list = ext;
+ 
+ 		  p++;
+ 		  ext->ext.string = buf;
+ 		  
+ 		  state = (ext->ext.len =
+ 			   get_funky_string(&buf,&p,1)) < 0 ? -1 : 4;
+ 		  break;
+ 		  
+ 		case '\0':
+ 		  state = 0;	/* Done! */
+ 		  break;
+ 		  
+ 		default:	/* Assume it is file type label */
+ 		  label[0] = *(p++);
+ 		  state = 2;
+ 		  break;
+ 		}
+ 	      break;
+ 	      
+ 	    case 2:		/* Second label character */
+ 	      if ( *p )
+ 		{
+ 		  label[1] = *(p++);
+ 		  state = 3;
+ 		}
+ 	      else
+ 		state = -1;	/* Error */
+ 	      break;
+ 
+ 	    case 3:		/* Equal sign after indicator label */
+ 	      state = -1;	/* Assume failure... */
+ 	      if ( *(p++) == '=' )	/* It *should* be... */
+ 		{
+ 		  for ( ind_no = 0 ; indicator_name[ind_no] != NULL ;
+ 		       ind_no++ )
+ 		    {
+ 		      if ( strcmp(label,indicator_name[ind_no]) == 0 )
+ 			{
+ 			  color_indicator[ind_no].string = buf;
+ 			  state = (color_indicator[ind_no].len =
+ 				   get_funky_string(&buf,&p,0)) < 0 ?
+ 				     -1 : 1;
+ 			  break;
+ 			}
+ 		    }
+ 		  if ( state == -1 )
+ 		    fprintf(stderr, "Unknown prefix: %s\n", label);
+ 		}
+ 	      break;
+ 
+ 	    case 4:		/* Equal sign after *.ext */
+ 	      if ( *(p++) == '=' )
+ 		{
+ 		  ext->seq.string = buf;
+ 		  state = (ext->seq.len =
+ 			   get_funky_string(&buf,&p,0)) < 0 ?
+ 			     -1 : 1;
+ 		}
+ 	      else
+ 		state = -1;
+ 	      break;
+ 	    }
+ 	}
+ 
+       if ( state < 0 )
+ 	{
+ 	  fprintf(stderr, "Bad %s variable\n", whichvar);
+ 	  free(color_buf);
+ 	  for ( ext = col_ext_list ; ext != NULL ; )
+ 	    {
+ 	      ext2 = ext;
+ 	      ext = ext->next;
+ 	      free (ext2);
+ 	    }
+ 	  print_with_color = 0;
+ 	}
+     }
+ }
+ 
+ /* Function get_funky_string directly from color-ls patch,
+  coding style changed to match zsh's */
+ 
+ /* Parse a string as part of the LS_COLO(U)RS variable; this may involve
+    decoding all kinds of escape characters.  If equals_end is set an
+    unescaped equal sign ends the string, otherwise only a : or \0
+    does.  Returns the number of characters output, or -1 on failure.
+ 
+    The resulting string is *not* null-terminated, but may contain
+    embedded nulls.
+ 
+    Note that both dest and src are char **; on return they point to
+    the first free byte after the array and the character that ended
+    the input string, respectively. */
+ 
+ static int
+ get_funky_string (char **dest, char **src, int equals_end)
+ {
+     int num = 0;		/* For numerical codes */
+     int count;			/* Something to count with */
+     enum { st_gnd, st_backslash, st_octal, st_hex, st_caret,
+ 	   st_end, st_error } 
+     state;
+     char *p, *q;
+ 
+     p = *src;  q = *dest;		/* We don't want to double-indirect
+ 					   the whole darn time */
+     
+     count = 0;			/* No characters counted in yet */
+ 
+     state = st_gnd;		/* Start in ground state */
+     while ( state < st_end ) {
+ 	switch ( state ) {
+ 	case st_gnd:		/* Ground state (no escapes) */
+ 	    switch ( *p )
+ 		{
+ 		case ':':
+ 		case '\0':
+ 		    state = st_end;	/* End of string */
+ 		    break;
+ 		case '\\':
+ 		    state = st_backslash; /* Backslash scape sequence */
+ 		    p++;
+ 		    break;
+ 		case '^':
+ 		    state = st_caret;	/* Caret escape */
+ 		    p++;
+ 		    break;
+ 		case '=':
+ 		    if ( equals_end )
+ 			{
+ 			    state = st_end; /* End */
+ 			    break;
+ 			}
+ 		    /* else fall through */
+ 		default:
+ 		    *(q++) = *(p++);
+ 		    count++;
+ 		    break;
+ 		}
+ 	    break;
+ 
+ 	case st_backslash:	/* Backslash escaped character */
+ 	    switch ( *p ) {
+ 	    case '0':
+ 	    case '1':
+ 	    case '2':
+ 	    case '3':
+ 	    case '4':
+ 	    case '5':
+ 	    case '6':
+ 	    case '7':
+ 		state = st_octal;	/* Octal sequence */
+ 		num = *p - '0';
+ 		break;
+ 	    case 'x':
+ 	    case 'X':
+ 		state = st_hex;	/* Hex sequence */
+ 		num = 0;
+ 		break;
+ 	    case 'a':		/* Bell */
+ 		num = 7;		/* Not all C compilers know what \a means */
+ 		break;
+ 	    case 'b':		/* Backspace */
+ 		num = '\b';
+ 		break;
+ 	    case 'e':		/* Escape */
+ 		num = 27;
+ 		break;
+ 	    case 'f':		/* Form feed */
+ 		num = '\f';
+ 		break;
+ 	    case 'n':		/* Newline */
+ 		num = '\n';
+ 		break;
+ 	    case 'r':		/* Carriage return */
+ 		num = '\r';
+ 		break;
+ 	    case 't':		/* Tab */
+ 		num = '\t';
+ 		break;
+ 	    case 'v':		/* Vtab */
+ 		num = '\v';
+ 		break;
+ 	    case '?':		/* Delete */
+ 		num = 127;
+ 		break;
+ 	    case '_':		/* Space */
+ 		num = ' ';
+ 		break;
+ 	    case '\0':		/* End of string */
+ 		state = st_error;	/* Error! */
+ 		break;
+ 	    default:		/* Escaped character like \ ^ : = */
+ 		num = *p;
+ 		break;
+ 	    }
+ 	    if ( state == st_backslash ) {
+ 		*(q++) = num;
+ 		count++;
+ 		state = st_gnd;
+ 	    }
+ 	    p++;
+ 	    break;
+ 
+ 	case st_octal:		/* Octal sequence */
+ 	    if ( *p < '0' || *p > '7' ) {
+ 		*(q++) = num;
+ 		count++;
+ 		state = st_gnd;
+ 	    }
+ 	    else
+ 		num = ( num << 3 ) + ( *(p++) - '0' );
+ 	    break;
+ 
+ 	case st_hex:		/* Hex sequence */
+ 	    switch ( *p ) {
+ 	    case '0':
+ 	    case '1':
+ 	    case '2':
+ 	    case '3':
+ 	    case '4':
+ 	    case '5':
+ 	    case '6':
+ 	    case '7':
+ 	    case '8':
+ 	    case '9':
+ 		num = ( num << 4 ) + ( *(p++) - '0' );
+ 		break;
+ 	    case 'a':
+ 	    case 'b':
+ 	    case 'c':
+ 	    case 'd':
+ 	    case 'e':
+ 	    case 'f':
+ 		num = ( num << 4 ) + ( *(p++) - 'a' ) + 10;
+ 		break;
+ 	    case 'A':
+ 	    case 'B':
+ 	    case 'C':
+ 	    case 'D':
+ 	    case 'E':
+ 	    case 'F':
+ 		num = ( num << 4 ) + ( *(p++) - 'A' ) + 10;
+ 		break;
+ 	    default:
+ 		*(q++) = num;
+ 		count++;
+ 		state = st_gnd;
+ 		break;
+ 	    }
+ 	    break;
+ 
+ 	case st_caret:		/* Caret escape */
+ 	    state = st_gnd;	/* Should be the next state... */
+ 	    if ( *p >= '@' && *p <= '~' ) {
+ 		*(q++) = *(p++) & 037;
+ 		count++;
+ 	    }
+ 	    else if ( *p == '?' ) {
+ 		*(q++) = 127;
+ 		count++;
+ 	    }
+ 	    else
+ 		state = st_error;
+ 	    break;
+ 	default:
+ 	    break;
+ 	}
+     }
+ 
+     *dest = q;  *src = p;
+ 
+     return ( state == st_error ) ? -1 : count;
+ }
+ 
+ /* GJB modified this fn from color-ls patch to use zsh's file_type fn
+    instead of the macros that color-ls used.  Also a leading stream argument
+    is needed since zsh uses shout for its output stream */
+ 
+ static void
+ print_color_indicator (FILE *stream, char *name, unsigned int mode, int linkok)
+ {
+     int type = C_FILE;
+     struct col_ext_type *ext;	/* Color extension */
+     int len;			/* Length of name */
+     
+     /* Is this a nonexistent file?  If so, linkok == -1 */
+ 
+     if ( linkok == -1 && color_indicator[C_MISSING].string ) {
+ 	ext = NULL;
+ 	type = C_MISSING;
+     }
+     else {
+ 	/* Test if is is a recognized extension */
+ 	len = strlen(name);
+ 	name += len;			/* Pointer to final \0 */
+ 	for ( ext = col_ext_list ; ext != NULL ; ext = ext->next ) {
+ 	    if ( ext->ext.len <= len &&
+ 		 strncmp(name-ext->ext.len,ext->ext.string,ext->ext.len)  == 0 )
+ 		break;
+ 	}
+       
+ 	/* gjb uses glob.c's file_type to replace similar handling in color_ls */
+ 	if ( !ext ) {
+ 	    char chFileType = file_type(mode);
+ 	    
+ 	    if (chFileType == '/')
+ 		type = C_DIR;
+ 	    else if (chFileType == '@')
+ 		type = (!linkok && color_indicator[C_ORPHAN].string) ?
+ 		    C_ORPHAN : C_LINK;
+ 	    else if (chFileType == '|')
+ 		type = C_FIFO;
+ 	    else if (chFileType == '=')
+ 		type = C_SOCK;
+ 	    else if (chFileType == '#')
+ 		type = C_BLK;
+ 	    else if (chFileType == '%')
+ 		type = C_CHR;
+ 	    else if ( chFileType == '*' )
+ 		type = C_EXEC;
+ 	}
+     }
+ 
+   put_indicator(stream,&color_indicator[C_LEFT]);
+   put_indicator(stream,ext ? &(ext->seq) : &color_indicator[type]);
+   put_indicator(stream,&color_indicator[C_RIGHT]);
+ }
+ 
+ 
+ /* GJB: added a leading stream argument, since zsh needs to output to shout 
+    fn put_indicator is otherwise from color_ls patch */
+ 
+ /* Output a color indicator (which may contain nulls) */
+ static void
+ put_indicator(FILE *stream, struct bin_str *ind)
+ {
+   register int i;
+   register char *p;
+ 
+   p = ind->string;
+ 
+   for ( i = ind->len ; i ; i-- )
+     if (fputc(*(p++),stream) < 0)
+        break;
+ }
+ 
+ 
+ /* FIX: need to write fSymLinkOk, or replace the call further down if this
+    functionality exists elsewhere */
+ /* Return true (non-zero) iff szPathname's symlink exists
+  (i.e. it is not an orphaned symlink */
+ 
+ /**/
+ int
+ fSymLinkOk(char *szPathname)
+ {
+     /* Currently, this behaviour lets symlink's colors be decided
+        by the file that they point to-- this is nice w/LISTTYPES, since a symlink
+        to a directory might look like "symtodir@" in blue, a regular
+        directory might look like "regdir/" in blue */
+     return !(access(szPathname, F_OK) == -1);
+ }
+ 
  /* List the matches.  Note that the list entries are metafied. */
  
  /**/
***************
*** 3493,3498 ****
--- 4093,4104 ----
      int nfpl, nfsl, nlpl, nlsl;
      int listmax = getiparam("LISTMAX");
  
+     if (isset(LISTCOLORS)) {
+ 	/* FIX: Should track when (Z)LS_COLO(U)RS variable changes and only
+ 	   parse the variable then, or when LISTCOLORS option gets set */
+ 	parse_ls_color();
+     }
+ 
  #ifdef DEBUG
      /* Sanity check */
      if(!validlist) {
***************
*** 3608,3615 ****
  		int t2;
  		char *pb;
  		struct stat buf;
  
! 		/* Build the path name for the stat. */
  		if (ispattern) {
  		    int cut = strlen(*ap) - boff;
  
--- 4214,4224 ----
  		int t2;
  		char *pb;
  		struct stat buf;
+ 		int ztat_return;
  
! 		/* Build the path name for the stat.  GJB: Print it further down
! 		 since we need to ztat (stat) the file before calling print_color_indicator
! 		 and that needs to be output before the file name */
  		if (ispattern) {
  		    int cut = strlen(*ap) - boff;
  
***************
*** 3620,3639 ****
  		    ap[0][cut] = sav;
  		    pb = *ap;
  		} else {
- 		    nicezputs(fpre, shout);
- 		    nicezputs(*ap, shout);
- 		    nicezputs(fsuf, shout);
  		    t2 = nfpl + niceztrlen(*ap) + nfsl;
  		    pb = (char *) ncalloc((prpre ? strlen(prpre) : 0) + 3 +
  					  strlen(fpre) + strlen(*ap) + strlen(fsuf));
  		    sprintf(pb, "%s%s%s%s",
  			    (prpre && *prpre) ? prpre : "./", fpre, *ap, fsuf);
  		}
! 		if (ztat(pb, &buf, 1))
  		    putc(' ', shout);
  		else
  		    /* Print the file type character. */
  		    putc(file_type(buf.st_mode), shout);
  		for (t0 = colsz; t0 && *ap; t0--, ap++);
  		if (*ap)
  		    /* And add spaces to make the columns aligned. */
--- 4229,4280 ----
  		    ap[0][cut] = sav;
  		    pb = *ap;
  		} else {
  		    t2 = nfpl + niceztrlen(*ap) + nfsl;
  		    pb = (char *) ncalloc((prpre ? strlen(prpre) : 0) + 3 +
  					  strlen(fpre) + strlen(*ap) + strlen(fsuf));
  		    sprintf(pb, "%s%s%s%s",
  			    (prpre && *prpre) ? prpre : "./", fpre, *ap, fsuf);
  		}
! 
! 		/* GJB: do the stat */
! 		ztat_return = ztat(pb,&buf,1);
! 
! 		/* GJB: now print the color escape sequence, if options say to */
! 		if (isset(LISTCOLORS) && print_with_color) {
! 		    print_color_indicator(shout,*ap,buf.st_mode,fSymLinkOk(pb));
! 		}
! 
! 		/* GJB: now print the name-- this code was in the analogous block above
! 		   which now just builds the path name.  It needed to be printed *after*
! 		   the color escape sequence */
! 
! 		if (ispattern) {
! 		    nicezputs(*ap + off, shout);
! 		}
! 		else {
! 		    nicezputs(fpre, shout);
! 		    nicezputs(*ap, shout);
! 		    nicezputs(fsuf, shout);
! 		}
! 
! 		/* GJB: Now turn off the color change [if any] before doing space alignment */
! 		if (isset(LISTCOLORS) && print_with_color) {
! 		    if ( color_indicator[C_END].string ) {
! 			put_indicator(shout,&color_indicator[C_END]);
! 		    }
! 		    else {
! 			put_indicator(shout,&color_indicator[C_LEFT]);
! 			put_indicator(shout,&color_indicator[C_NORM]);
! 			put_indicator(shout,&color_indicator[C_RIGHT]);
! 		    }
! 		}
! 
! 		if (ztat_return)
  		    putc(' ', shout);
  		else
  		    /* Print the file type character. */
  		    putc(file_type(buf.st_mode), shout);
+ 
  		for (t0 = colsz; t0 && *ap; t0--, ap++);
  		if (*ap)
  		    /* And add spaces to make the columns aligned. */
***************
*** 3679,3684 ****
--- 4320,4337 ----
  	    showinglist = -1;
  	} else
  	    clearflag = 0, putc('\n', shout);
+     /* FIX: Do this only when listcolor option gets unset, assuming the parsing
+      is done once when option is set or when variable changes, as described in
+      other fix comment above */
+     if (print_with_color) {
+ 	struct col_ext_type *ext;
+ 	free(color_buf);
+ 	while ( col_ext_list ) {
+ 	    ext = col_ext_list;
+ 	    col_ext_list = col_ext_list->next;
+ 	    free(ext);
+ 	}
+     }
  }
  
  /* This is used to print expansions. */
*** zle_word.c	1996/08/20 13:53:53	1.1
--- zle_word.c	1996/08/21 19:25:32	1.2
***************
*** 53,58 ****
--- 53,111 ----
  
  /**/
  void
+ forwardwordpart(void)
+ {
+     if (zmult < 0) {
+ 	zmult = -zmult;
+ 	backwardwordpart();
+ 	return;
+     }
+     while (zmult--) {
+ 	/* skip to next uppercase character after current character */
+ 	if (cs != ll)
+ 	    cs++;
+ 	while (cs != ll && !iword(line[cs]))
+ 	    cs++;
+ 	while (cs != ll && iword(line[cs]) && !isupper(line[cs]))
+ 	    cs++;
+ 	if (wordflag && !zmult) {
+ 	    return;
+ 	}
+ 	while (cs != ll && !iword(line[cs]))
+ 	    cs++;
+     }
+ }
+ 
+ /**/
+ void
+ forwarddeletewordpart(void)
+ {
+     int x = cs;
+ 
+     if (zmult < 0) {
+ 	zmult = -zmult;
+ 	backwarddeletewordpart();
+ 	return;
+     }
+     while (zmult--) {
+ 	/* delete to next uppercase character after current character */
+ 	if (x != ll)
+ 	    x++;
+ 	while (x != ll && !iword(line[x]))
+ 	    x++;
+ 	while (x != ll && iword(line[x]) && !isupper(line[x]))
+ 	    x++;
+ 	if (wordflag && !zmult) {
+ 	    break;
+ 	}
+ 	while (x != ll && !iword(line[cs]))
+ 	    x++;
+     }
+     foredel(x-cs);
+ }
+ 
+ /**/
+ void
  viforwardword(void)
  {
      if (zmult < 0) {
***************
*** 167,172 ****
--- 220,280 ----
  	while (cs && iword(line[cs - 1]))
  	    cs--;
      }
+ }
+ 
+ /**/
+ void
+ backwardwordpart(void)
+ {
+     if (zmult < 0) {
+ 	zmult = -zmult;
+ 	forwardwordpart();
+ 	return;
+     }
+     while (zmult--) {
+ 	/* skip to char after first lowercase after any uppercase character
+ 	   (do *not* look at current char) */
+ 	if (cs > 0)
+ 	    cs--;
+ 	while (cs && !iword(line[cs]))
+ 	    cs--;
+ 	while (cs && iword(line[cs]) && !isupper(line[cs]))
+ 	    cs--;
+ 	if (wordflag && !zmult) {
+ 	    return;
+ 	}
+ 	if (cs && !iword(line[cs]))
+ 	    cs--;
+     } 
+ }
+ 
+ /**/
+ void
+ backwarddeletewordpart(void)
+ {
+     int x = cs;
+ 
+     if (zmult < 0) {
+ 	zmult = -zmult;
+ 	forwarddeletewordpart();
+ 	return;
+     }
+     while (zmult--) {
+ 	/* skip to char after first lowercase after any uppercase character
+ 	   (do *not* look at current char) */
+ 	if (x > 0)
+ 	    x--;
+ 	while (x && !iword(line[x]))
+ 	    x--;
+ 	while (x && iword(line[x]) && !isupper(line[x]))
+ 	    x--;
+ 	if (wordflag && !zmult) {
+ 	    break;
+ 	}
+ 	if (x && !iword(line[x]))
+ 	    x--;
+     }
+     backdel(cs-x);
  }
  
  /**/
*** zle_bindings.c	1996/08/20 01:17:54	1.1
--- zle_bindings.c	1996/08/21 19:27:47	1.2
***************
*** 42,50 ****
--- 42,52 ----
      {"backward-char", backwardchar, ZLE_MOVEMENT},
      {"backward-delete-char", backwarddeletechar, ZLE_DELETE},
      {"backward-delete-word", backwarddeleteword, ZLE_DELETE},
+     {"backward-delete-word-part", backwarddeletewordpart, ZLE_DELETE},
      {"backward-kill-line", backwardkillline, ZLE_KILL},
      {"backward-kill-word", backwardkillword, ZLE_KILL | ZLE_DELETE},
      {"backward-word", backwardword, ZLE_MOVEMENT},
+     {"backward-word-part", backwardwordpart, ZLE_MOVEMENT},
      {"beginning-of-buffer-or-history", beginningofbufferorhistory, ZLE_MOVEMENT},
      {"beginning-of-history", beginningofhistory, 0},
      {"beginning-of-line", beginningofline, ZLE_MOVEMENT},
***************
*** 54,59 ****
--- 56,62 ----
      {"complete-word", completeword, ZLE_MENUCMP},
      {"copy-prev-word", copyprevword, 0},
      {"copy-region-as-kill", copyregionaskill, ZLE_KILL},
+     {"dabbrev-complete", dabbrevcomplete, ZLE_MENUCMP},
      {"delete-char", deletechar, ZLE_DELETE},
      {"delete-char-or-list", deletecharorlist, ZLE_MENUCMP},
      {"delete-word", deleteword, ZLE_DELETE},
***************
*** 79,84 ****
--- 82,89 ----
      {"expand-word", expandword, 0},
      {"forward-char", forwardchar, ZLE_MOVEMENT},
      {"forward-word", forwardword, ZLE_MOVEMENT},
+     {"forward-word-part", forwardwordpart, ZLE_MOVEMENT},
+     {"forward-delete-word-part", forwarddeletewordpart, ZLE_DELETE},
      {"get-line", getline, 0},
      {"gosmacs-transpose-chars", gosmacstransposechars, 0},
      {"history-beginning-search-backward", historybeginningsearchbackward, ZLE_HISTSEARCH},
***************
*** 229,235 ****
      /* ^Y */ z_yank,
      /* ^Z */ z_undefinedkey,
      /* ^[ */ z_prefix,
!     /* ^\ */ z_undefinedkey,
      /* ^] */ z_undefinedkey,
      /* ^^ */ z_undefinedkey,
      /* ^_ */ z_undo,
--- 234,240 ----
      /* ^Y */ z_yank,
      /* ^Z */ z_undefinedkey,
      /* ^[ */ z_prefix,
!     /* ^\ */ z_backwarddeletewordpart,
      /* ^] */ z_undefinedkey,
      /* ^^ */ z_undefinedkey,
      /* ^_ */ z_undo,
***************
*** 376,382 ****
      /* M-, */ z_undefinedkey,
      /* M-- */ z_negargument,
      /* M-. */ z_insertlastword,
!     /* M-/ */ z_undefinedkey,
      /* M-0 */ z_digitargument,
      /* M-1 */ z_digitargument,
      /* M-2 */ z_digitargument,
--- 381,387 ----
      /* M-, */ z_undefinedkey,
      /* M-- */ z_negargument,
      /* M-. */ z_insertlastword,
!     /* M-/ */ z_dabbrevcomplete,
      /* M-0 */ z_digitargument,
      /* M-1 */ z_digitargument,
      /* M-2 */ z_digitargument,
*** zle.h	1996/08/20 23:45:46	1.1
--- zle.h	1996/08/21 19:27:26	1.2
***************
*** 279,287 ****
--- 279,289 ----
      z_backwardchar,
      z_backwarddeletechar,
      z_backwarddeleteword,
+     z_backwarddeletewordpart,
      z_backwardkillline,
      z_backwardkillword,
      z_backwardword,
+     z_backwardwordpart,
      z_beginningofbufferorhistory,
      z_beginningofhistory,
      z_beginningofline,
***************
*** 291,296 ****
--- 293,299 ----
      z_completeword,
      z_copyprevword,
      z_copyregionaskill,
+     z_dabbrevcomplete,
      z_deletechar,
      z_deletecharorlist,
      z_deleteword,
***************
*** 316,321 ****
--- 319,326 ----
      z_expandword,
      z_forwardchar,
      z_forwardword,
+     z_forwarddeletewordpart,
+     z_forwardwordpart,
      z_getline,
      z_gosmacstransposechars,
      z_historybeginningsearchbackward,
*** zsh.h	1996/08/21 20:37:01	1.1
--- zsh.h	1996/08/21 20:38:05	1.2
***************
*** 1114,1119 ****
--- 1114,1120 ----
      LISTAMBIGUOUS,
      LISTBEEP,
      LISTTYPES,
+     LISTCOLORS,
      LOCALOPTIONS,
      LOGINSHELL,
      LONGLISTJOBS,


Messages sorted by: Reverse Date, Date, Thread, Author