瀏覽代碼

(_transform_name_to_obstack,set_transform_expr): Implement case conversion operations (GNU extension).

Sergey Poznyakoff 19 年之前
父節點
當前提交
8b2f4ad670
共有 1 個文件被更改,包括 153 次插入10 次删除
  1. 153 10
      src/transform.c

+ 153 - 10
src/transform.c

@@ -33,6 +33,16 @@ enum replace_segm_type
   {
     segm_literal,   /* Literal segment */
     segm_backref,   /* Back-reference segment */
+    segm_case_ctl   /* Case control segment (GNU extension) */
+  };
+
+enum case_ctl_type
+  {
+    ctl_stop,       /* Stop case conversion */ 
+    ctl_upcase_next,/* Turn the next character to uppercase */ 
+    ctl_locase_next,/* Turn the next character to lowercase */
+    ctl_upcase,     /* Turn the replacement to uppercase until ctl_stop */
+    ctl_locase      /* Turn the replacement to lowercase until ctl_stop */
   };
 
 struct replace_segm
@@ -45,13 +55,15 @@ struct replace_segm
     {
       char *ptr;
       size_t size;
-    } literal;
-    size_t ref;
+    } literal;                /* type == segm_literal */   
+    size_t ref;               /* type == segm_backref */
+    enum case_ctl_type ctl;   /* type == segm_case_ctl */ 
   } v;
 };
 
+/* Compiled replacement expression */
 static struct replace_segm *repl_head, *repl_tail;
-static segm_count;
+static segm_count; /* Number of elements in the above list */
 
 static struct replace_segm *
 add_segment (void)
@@ -101,6 +113,14 @@ add_backref_segment (size_t ref)
   segm->v.ref = ref;
 }
 
+static void
+add_case_ctl_segment (enum case_ctl_type ctl)
+{
+  struct replace_segm *segm = add_segment ();
+  segm->type = segm_case_ctl;
+  segm->v.ctl = ctl;
+}
+
 void
 set_transform_expr (const char *expr)
 {
@@ -246,7 +266,39 @@ set_transform_expr (const char *expr)
 	      add_char_segment ('&');
 	      cur++;
 	      break;
-
+	      
+	    case 'L':
+	      /* Turn the replacement to lowercase until a `\U' or `\E'
+		 is found, */
+	      add_case_ctl_segment (ctl_locase);
+	      cur++;
+	      break;
+ 
+	    case 'l':
+	      /* Turn the next character to lowercase, */
+	      add_case_ctl_segment (ctl_locase_next);
+	      cur++;
+	      break;
+	      
+	    case 'U':
+	      /* Turn the replacement to uppercase until a `\L' or `\E'
+		 is found, */
+	      add_case_ctl_segment (ctl_upcase);
+	      cur++;
+	      break;
+	      
+	    case 'u':
+	      /* Turn the next character to uppercase, */
+	      add_case_ctl_segment (ctl_upcase_next);
+	      cur++;
+	      break;
+	      
+	    case 'E':
+	      /* Stop case conversion started by `\L' or `\U'. */
+	      add_case_ctl_segment (ctl_stop);
+	      cur++;
+	      break;
+  
 	    default:
 	      /* Try to be nice */
 	      {
@@ -273,12 +325,63 @@ set_transform_expr (const char *expr)
   
 }
 
+/* Run case conversion specified by CASE_CTL on array PTR of SIZE
+   characters. Returns pointer to statically allocated storage. */
+static char *
+run_case_conv (enum case_ctl_type case_ctl, char *ptr, size_t size)
+{
+  static char *case_ctl_buffer;
+  static size_t case_ctl_bufsize;
+  char *p;
+  
+  if (case_ctl_bufsize < size)
+    {
+      case_ctl_bufsize = size;
+      case_ctl_buffer = xrealloc (case_ctl_buffer, case_ctl_bufsize);
+    }
+  memcpy (case_ctl_buffer, ptr, size);
+  switch (case_ctl)
+    {
+    case ctl_upcase_next:
+      case_ctl_buffer[0] = toupper (case_ctl_buffer[0]);
+      break;
+      
+    case ctl_locase_next:
+      case_ctl_buffer[0] = tolower (case_ctl_buffer[0]);
+      break;
+      
+    case ctl_upcase:
+      for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
+	*p = toupper (*p);
+      break;
+      
+    case ctl_locase:
+      for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
+	*p = tolower (*p);
+      break;
+
+    case ctl_stop:
+      break;
+    }
+  return case_ctl_buffer;
+}
+
 bool
 _transform_name_to_obstack (char *input)
 {
   regmatch_t *rmp;
   char *p;
   int rc;
+  enum case_ctl_type case_ctl = ctl_stop,  /* Current case conversion op */
+                     save_ctl = ctl_stop;  /* Saved case_ctl for \u and \l */
+
+  /* Reset case conversion after a single-char operation */
+#define CASE_CTL_RESET()  if (case_ctl == ctl_upcase_next     \
+			      || case_ctl == ctl_locase_next) \
+                            {                                 \
+                              case_ctl = save_ctl;            \
+                              save_ctl = ctl_stop;            \
+			    }
   
   if (transform_type == transform_none)
     return false;
@@ -288,13 +391,14 @@ _transform_name_to_obstack (char *input)
   while (*input)
     {
       size_t disp;
+      char *ptr;
       
       rc = regexec (&regex, input, regex.re_nsub + 1, rmp, 0);
       
       if (rc == 0)
 	{
 	  struct replace_segm *segm;
-
+	  
 	  disp = rmp[0].rm_eo;
 
 	  if (rmp[0].rm_so)
@@ -305,17 +409,56 @@ _transform_name_to_obstack (char *input)
 	      switch (segm->type)
 		{
 		case segm_literal:    /* Literal segment */
-		  obstack_grow (&stk, segm->v.literal.ptr,
-				segm->v.literal.size);
+		  if (case_ctl == ctl_stop)
+		    ptr = segm->v.literal.ptr;
+		  else
+		    {
+		      ptr = run_case_conv (case_ctl,
+					   segm->v.literal.ptr,
+					   segm->v.literal.size);
+		      CASE_CTL_RESET();
+		    }
+		  obstack_grow (&stk, ptr, segm->v.literal.size);
 		  break;
 	      
 		case segm_backref:    /* Back-reference segment */
 		  if (rmp[segm->v.ref].rm_so != -1
 		      && rmp[segm->v.ref].rm_eo != -1)
-		    obstack_grow (&stk,
-				  input + rmp[segm->v.ref].rm_so,
-			      rmp[segm->v.ref].rm_eo - rmp[segm->v.ref].rm_so);
+		    {
+		      size_t size = rmp[segm->v.ref].rm_eo
+			              - rmp[segm->v.ref].rm_so;
+		      ptr = input + rmp[segm->v.ref].rm_so;
+		      if (case_ctl != ctl_stop)
+			{
+			  ptr = run_case_conv (case_ctl, ptr, size);
+			  CASE_CTL_RESET();
+			}
+		      
+		      obstack_grow (&stk, ptr, size);
+		    }
 		  break;
+
+		case segm_case_ctl:
+		  switch (segm->v.ctl)
+		    {
+		    case ctl_upcase_next:
+		    case ctl_locase_next:
+		      switch (save_ctl)
+			{
+			case ctl_stop:
+			case ctl_upcase:
+			case ctl_locase:
+			  save_ctl = case_ctl;
+			default:
+			  break;
+			}
+		      /*FALL THROUGH*/
+		      
+		    case ctl_upcase:
+		    case ctl_locase:
+		    case ctl_stop:
+		      case_ctl = segm->v.ctl;
+		    }
 		}
 	    }
 	}