sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

tr.c (6209B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <stdlib.h>
      3 
      4 #include "utf.h"
      5 #include "util.h"
      6 
      7 static int cflag = 0;
      8 static int dflag = 0;
      9 static int sflag = 0;
     10 
     11 struct range {
     12 	Rune   start;
     13 	Rune   end;
     14 	size_t quant;
     15 };
     16 
     17 static struct {
     18 	char    *name;
     19 	int    (*check)(Rune);
     20 } classes[] = {
     21 	{ "alnum",  isalnumrune  },
     22 	{ "alpha",  isalpharune  },
     23 	{ "blank",  isblankrune  },
     24 	{ "cntrl",  iscntrlrune  },
     25 	{ "digit",  isdigitrune  },
     26 	{ "graph",  isgraphrune  },
     27 	{ "lower",  islowerrune  },
     28 	{ "print",  isprintrune  },
     29 	{ "punct",  ispunctrune  },
     30 	{ "space",  isspacerune  },
     31 	{ "upper",  isupperrune  },
     32 	{ "xdigit", isxdigitrune },
     33 };
     34 
     35 static struct range *set1        = NULL;
     36 static size_t set1ranges         = 0;
     37 static int    (*set1check)(Rune) = NULL;
     38 static struct range *set2        = NULL;
     39 static size_t set2ranges         = 0;
     40 static int    (*set2check)(Rune) = NULL;
     41 
     42 static size_t
     43 rangelen(struct range r)
     44 {
     45 	return (r.end - r.start + 1) * r.quant;
     46 }
     47 
     48 static size_t
     49 setlen(struct range *set, size_t setranges)
     50 {
     51 	size_t len = 0, i;
     52 
     53 	for (i = 0; i < setranges; i++)
     54 		len += rangelen(set[i]);
     55 
     56 	return len;
     57 }
     58 
     59 static int
     60 rstrmatch(Rune *r, char *s, size_t n)
     61 {
     62 	size_t i;
     63 
     64 	for (i = 0; i < n; i++)
     65 		if (r[i] != s[i])
     66 			return 0;
     67 	return 1;
     68 }
     69 
     70 static size_t
     71 makeset(char *str, struct range **set, int (**check)(Rune))
     72 {
     73 	Rune  *rstr;
     74 	size_t len, i, j, m, n;
     75 	size_t q, setranges = 0;
     76 	int    factor, base;
     77 
     78 	/* rstr defines at most len ranges */
     79 	unescape(str);
     80 	rstr = ereallocarray(NULL, utflen(str) + 1, sizeof(*rstr));
     81 	len = utftorunestr(str, rstr);
     82 	*set = ereallocarray(NULL, len, sizeof(**set));
     83 
     84 	for (i = 0; i < len; i++) {
     85 		if (rstr[i] == '[') {
     86 			j = i;
     87 nextbrack:
     88 			if (j >= len)
     89 				goto literal;
     90 			for (m = j; m < len; m++)
     91 				if (rstr[m] == ']') {
     92 					j = m;
     93 					break;
     94 				}
     95 			if (j == i)
     96 				goto literal;
     97 
     98 			/* CLASSES [=EQUIV=] (skip) */
     99 			if (j - i > 3 && rstr[i + 1] == '=' && rstr[m - 1] == '=') {
    100 				if (j - i != 4)
    101 					goto literal;
    102 				(*set)[setranges].start = rstr[i + 2];
    103 				(*set)[setranges].end   = rstr[i + 2];
    104 				(*set)[setranges].quant = 1;
    105 				setranges++;
    106 				i = j;
    107 				continue;
    108 			}
    109 
    110 			/* CLASSES [:CLASS:] */
    111 			if (j - i > 3 && rstr[i + 1] == ':' && rstr[m - 1] == ':') {
    112 				for (n = 0; n < LEN(classes); n++) {
    113 					if (rstrmatch(rstr + i + 2, classes[n].name, j - i - 3)) {
    114 						*check = classes[n].check;
    115 						return 0;
    116 					}
    117 				}
    118 				eprintf("Invalid character class.\n");
    119 			}
    120 
    121 			/* REPEAT  [_*n] (only allowed in set2) */
    122 			if (j - i > 2 && rstr[i + 2] == '*') {
    123 				/* check if right side of '*' is a number */
    124 				q = 0;
    125 				factor = 1;
    126 				base = (rstr[i + 3] == '0') ? 8 : 10;
    127 				for (n = j - 1; n > i + 2; n--) {
    128 					if (rstr[n] < '0' || rstr[n] > '9') {
    129 						n = 0;
    130 						break;
    131 					}
    132 					q += (rstr[n] - '0') * factor;
    133 					factor *= base;
    134 				}
    135 				if (n == 0) {
    136 					j = m + 1;
    137 					goto nextbrack;
    138 				}
    139 				(*set)[setranges].start = rstr[i + 1];
    140 				(*set)[setranges].end   = rstr[i + 1];
    141 				(*set)[setranges].quant = q ? q : setlen(set1, MAX(set1ranges, 1));
    142 				setranges++;
    143 				i = j;
    144 				continue;
    145 			}
    146 
    147 			j = m + 1;
    148 			goto nextbrack;
    149 		}
    150 literal:
    151 		/* RANGES [_-__-_], _-__-_ */
    152 		/* LITERALS _______ */
    153 		(*set)[setranges].start = rstr[i];
    154 
    155 		if (i < len - 2 && rstr[i + 1] == '-' && rstr[i + 2] >= rstr[i])
    156 			i += 2;
    157 		(*set)[setranges].end = rstr[i];
    158 		(*set)[setranges].quant = 1;
    159 		setranges++;
    160 	}
    161 
    162 	free(rstr);
    163 	return setranges;
    164 }
    165 
    166 static void
    167 usage(void)
    168 {
    169 	eprintf("usage: %s [-cCds] set1 [set2]\n", argv0);
    170 }
    171 
    172 int
    173 main(int argc, char *argv[])
    174 {
    175 	Rune r, lastrune = 0;
    176 	size_t off1, off2, i, m;
    177 	int ret = 0;
    178 
    179 	ARGBEGIN {
    180 	case 'c':
    181 	case 'C':
    182 		cflag = 1;
    183 		break;
    184 	case 'd':
    185 		dflag = 1;
    186 		break;
    187 	case 's':
    188 		sflag = 1;
    189 		break;
    190 	default:
    191 		usage();
    192 	} ARGEND
    193 
    194 	if (!argc || argc > 2 || (argc == 1 && dflag == sflag))
    195 		usage();
    196 	set1ranges = makeset(argv[0], &set1, &set1check);
    197 	if (argc == 2)
    198 		set2ranges = makeset(argv[1], &set2, &set2check);
    199 
    200 	if (!dflag || (argc == 2 && sflag)) {
    201 		/* sanity checks as we are translating */
    202 		if (!sflag && !set2ranges && !set2check)
    203 			eprintf("cannot map to an empty set.\n");
    204 		if (set2check && set2check != islowerrune &&
    205 		    set2check != isupperrune) {
    206 			eprintf("can only map to 'lower' and 'upper' class.\n");
    207 		}
    208 	}
    209 read:
    210 	if (!efgetrune(&r, stdin, "<stdin>")) {
    211 		ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
    212 		return ret;
    213 	}
    214 	if (argc == 1 && sflag)
    215 		goto write;
    216 	for (i = 0, off1 = 0; i < set1ranges; off1 += rangelen(set1[i]), i++) {
    217 		if (set1[i].start <= r && r <= set1[i].end) {
    218 			if (dflag) {
    219 				if (cflag)
    220 					goto write;
    221 				else
    222 					goto read;
    223 			}
    224 			if (cflag)
    225 				goto write;
    226 
    227 			/* map r to set2 */
    228 			if (set2check) {
    229 				if (set2check == islowerrune)
    230 					r = tolowerrune(r);
    231 				else
    232 					r = toupperrune(r);
    233 			} else {
    234 				off1 += r - set1[i].start;
    235 				if (off1 > setlen(set2, set2ranges) - 1) {
    236 					r = set2[set2ranges - 1].end;
    237 					goto write;
    238 				}
    239 				for (m = 0, off2 = 0; m < set2ranges; m++) {
    240 					if (off2 + rangelen(set2[m]) > off1) {
    241 						m++;
    242 						break;
    243 					}
    244 					off2 += rangelen(set2[m]);
    245 				}
    246 				m--;
    247 				r = set2[m].start + (off1 - off2) / set2[m].quant;
    248 			}
    249 			goto write;
    250 		}
    251 	}
    252 	if (set1check && set1check(r)) {
    253 		if (cflag)
    254 			goto write;
    255 		if (dflag)
    256 			goto read;
    257 		if (set2check) {
    258 			if (set2check == islowerrune)
    259 				r = tolowerrune(r);
    260 			else
    261 				r = toupperrune(r);
    262 		} else {
    263 			r = set2[set2ranges - 1].end;
    264 		}
    265 		goto write;
    266 	}
    267 	if (!dflag && cflag) {
    268 		if (set2check) {
    269 			if (set2check == islowerrune)
    270 				r = tolowerrune(r);
    271 			else
    272 				r = toupperrune(r);
    273 		} else {
    274 			r = set2[set2ranges - 1].end;
    275 		}
    276 		goto write;
    277 	}
    278 	if (dflag && cflag)
    279 		goto read;
    280 write:
    281 	if (argc == 1 && sflag && r == lastrune) {
    282 		if (set1check && set1check(r))
    283 			goto read;
    284 		for (i = 0; i < set1ranges; i++) {
    285 			if (set1[i].start <= r && r <= set1[i].end)
    286 				goto read;
    287 		}
    288 	}
    289 	if (argc == 2 && sflag && r == lastrune) {
    290 		if (set2check && set2check(r))
    291 			goto read;
    292 		for (i = 0; i < set2ranges; i++) {
    293 			if (set2[i].start <= r && r <= set2[i].end)
    294 				goto read;
    295 		}
    296 	}
    297 	efputrune(&r, stdout, "<stdout>");
    298 	lastrune = r;
    299 	goto read;
    300 }