]> Dogcows Code - chaz/tar/blobdiff - src/transform.c
Relicense under GPLv3
[chaz/tar] / src / transform.c
index acdf552cf3b88d9237152e5004a305faf3b20122..349a0d59e21c7394781a9d27b44c7df8f2a907ff 100644 (file)
@@ -1,9 +1,9 @@
 /* This file is part of GNU tar. 
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2007 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any later
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
 #include <regex.h>
 #include "common.h"
 
-enum transform_type
+static enum transform_type
   {
     transform_none,
     transform_first,
     transform_global
   }
 transform_type = transform_none;
+static unsigned match_number = 0;
 static regex_t regex;
 static struct obstack stk;
 
@@ -33,6 +34,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 +56,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 size_t segm_count; /* Number of elements in the above list */
 
 static struct replace_segm *
 add_segment (void)
@@ -90,7 +103,7 @@ add_char_segment (int chr)
   segm->v.literal.ptr = xmalloc (2);
   segm->v.literal.ptr[0] = chr;
   segm->v.literal.ptr[1] = 0;
-  segm->v.literal.size = 2;
+  segm->v.literal.size = 1;
 }
 
 static void
@@ -101,6 +114,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)
 {
@@ -156,6 +177,12 @@ set_transform_expr (const char *expr)
        cflags |= REG_EXTENDED;
        break;
        
+      case '0': case '1': case '2': case '3': case '4':
+      case '5': case '6': case '7': case '8': case '9':
+       match_number = strtoul (p, (char**) &p, 0);
+       p--;
+       break;
+       
       default:
        USAGE_ERROR ((0, 0, _("Unknown flag in transform expression")));
       }
@@ -246,7 +273,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 +332,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;
+  size_t nmatches = 0;
+  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,34 +398,82 @@ _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)
            obstack_grow (&stk, input, rmp[0].rm_so);
-         
+
+         nmatches++;
+         if (match_number && nmatches < match_number)
+           {
+             obstack_grow (&stk, input, disp);
+             input += disp;
+             continue;
+           }
+
          for (segm = repl_head; segm; segm = segm->next)
            {
              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;
+                   }
                }
            }
        }
@@ -340,21 +498,21 @@ _transform_name_to_obstack (char *input)
 }
   
 bool
-transform_name_fp (char **pinput, char *(*fun)(char *))
+transform_name_fp (char **pinput, char *(*fun)(char *, void *), void *dat)
 {
-    char *str, *p;
+    char *str;
     bool ret = _transform_name_to_obstack (*pinput);
     if (ret)
       {
        str = obstack_finish (&stk);
-       assign_string (pinput, fun ? fun (str) : str);
+       assign_string (pinput, fun ? fun (str, dat) : str);
        obstack_free (&stk, str);
       }
     else if (fun)
       {
        str = *pinput;
        *pinput = NULL;
-       assign_string (pinput, fun (str));
+       assign_string (pinput, fun (str, dat));
        free (str);
        ret = true;
       }
@@ -364,23 +522,6 @@ transform_name_fp (char **pinput, char *(*fun)(char *))
 bool
 transform_name (char **pinput)
 {
-  return transform_name_fp (pinput, NULL);
+  return transform_name_fp (pinput, NULL, NULL);
 }
 
-#if 0
-void
-read_and_transform_loop ()
-{
-  char buf[512];
-  while (fgets (buf, sizeof buf, stdin))
-    {
-      char *p = buf + strlen (buf);
-      if (p[-1] == '\n')
-       p[-1] = 0;
-      if (transform_name (buf, &p))
-       printf ("=> %s\n", p);
-      else
-       printf ("=\n");
-    }
-}
-#endif
This page took 0.029283 seconds and 4 git commands to generate.