From: Sergey Poznyakoff Date: Wed, 7 Jun 2006 14:57:10 +0000 (+0000) Subject: (_transform_name_to_obstack,set_transform_expr): Implement case conversion operations... X-Git-Url: https://git.dogcows.com/gitweb?a=commitdiff_plain;h=8b2f4ad6705f4a561a5751f58c1e0169f5fe0270;p=chaz%2Ftar (_transform_name_to_obstack,set_transform_expr): Implement case conversion operations (GNU extension). --- diff --git a/src/transform.c b/src/transform.c index acdf552..598caac 100644 --- a/src/transform.c +++ b/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 (®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) @@ -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; + } } } }