X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Ftransform.c;h=fd2ed51ec70401101b0120693e28974a9463e477;hb=37edfd9e8f6a744ea037b631e999ce87ffdabc44;hp=c5eafc347e8260352fd3ed1274a5b9cc687a0425;hpb=c8aa01c80c9154b1153a4662a66ac8a5f43d7e30;p=chaz%2Ftar diff --git a/src/transform.c b/src/transform.c index c5eafc3..fd2ed51 100644 --- a/src/transform.c +++ b/src/transform.c @@ -19,13 +19,14 @@ #include #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 (®ex, 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; + } } } } @@ -342,7 +500,7 @@ _transform_name_to_obstack (char *input) bool transform_name_fp (char **pinput, char *(*fun)(char *)) { - char *str, *p; + char *str; bool ret = _transform_name_to_obstack (*pinput); if (ret) { @@ -350,6 +508,14 @@ transform_name_fp (char **pinput, char *(*fun)(char *)) assign_string (pinput, fun ? fun (str) : str); obstack_free (&stk, str); } + else if (fun) + { + str = *pinput; + *pinput = NULL; + assign_string (pinput, fun (str)); + free (str); + ret = true; + } return ret; } @@ -359,20 +525,3 @@ transform_name (char **pinput) return transform_name_fp (pinput, 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