#line 1026 "../../src/builtin/snarf.m4"
/* -*- buffer-read-only: t -*- vi: set ro:
   THIS FILE IS GENERATED AUTOMATICALLY.  PLEASE DO NOT EDIT.
*/
#line 1026
#ifdef HAVE_CONFIG_H
#line 1026
# include <config.h>
#line 1026
#endif
#line 1026
#include <sys/types.h>
#line 1026

#line 1026
#include "mailfromd.h"
#line 1026
#include "prog.h"
#line 1026
#include "builtin.h"
#line 1026

#line 1026

#line 1066 "../../src/builtin/snarf.m4"

/* End of snarf.m4 */
#line 1 "tr.bi"
/* This file is part of Mailfromd.             -*- c -*-
   Copyright (C) 2006-2025 Sergey Poznyakoff

   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 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */



#line 23 "tr.bi"
static int match_alnum(int c) { return mu_isalnum(c); }
#line 24 "tr.bi"
static int match_alpha(int c) { return mu_isalpha(c); }
#line 25 "tr.bi"
static int match_blank(int c) { return mu_isblank(c); }
#line 26 "tr.bi"
static int match_cntrl(int c) { return mu_iscntrl(c); }
#line 27 "tr.bi"
static int match_digit(int c) { return mu_isdigit(c); }
#line 28 "tr.bi"
static int match_graph(int c) { return mu_isgraph(c); }
#line 29 "tr.bi"
static int match_lower(int c) { return mu_islower(c); }
#line 30 "tr.bi"
static int match_print(int c) { return mu_isprint(c); }
#line 31 "tr.bi"
static int match_punct(int c) { return mu_ispunct(c); }
#line 32 "tr.bi"
static int match_space(int c) { return mu_isspace(c); }
#line 33 "tr.bi"
static int match_upper(int c) { return mu_isupper(c); }
#line 34 "tr.bi"
static int match_xdigit(int c) { return mu_isxdigit(c); }


typedef int (*class_matcher_fn)(int c);
struct cclass_fun {
	char const *name;
	int len;
	class_matcher_fn matcher;
};

static struct cclass_fun cclass_funtab[] = {
#line 23 "tr.bi"
{ "alnum", sizeof("alnum")-1, match_alnum },
#line 23

#line 24 "tr.bi"
{ "alpha", sizeof("alpha")-1, match_alpha },
#line 24

#line 25 "tr.bi"
{ "blank", sizeof("blank")-1, match_blank },
#line 25

#line 26 "tr.bi"
{ "cntrl", sizeof("cntrl")-1, match_cntrl },
#line 26

#line 27 "tr.bi"
{ "digit", sizeof("digit")-1, match_digit },
#line 27

#line 28 "tr.bi"
{ "graph", sizeof("graph")-1, match_graph },
#line 28

#line 29 "tr.bi"
{ "lower", sizeof("lower")-1, match_lower },
#line 29

#line 30 "tr.bi"
{ "print", sizeof("print")-1, match_print },
#line 30

#line 31 "tr.bi"
{ "punct", sizeof("punct")-1, match_punct },
#line 31

#line 32 "tr.bi"
{ "space", sizeof("space")-1, match_space },
#line 32

#line 33 "tr.bi"
{ "upper", sizeof("upper")-1, match_upper },
#line 33

#line 34 "tr.bi"
{ "xdigit", sizeof("xdigit")-1, match_xdigit },
#line 34

#line 45 "tr.bi"

{ NULL }
};

static class_matcher_fn
find_cclass(unsigned char const *name, int len)
{
	struct cclass_fun *cp;
	for (cp = cclass_funtab; cp->name; cp++)
		if (len == cp->len && memcmp(name, cp->name, len) == 0)
			return cp->matcher;
	return NULL;
}

static unsigned char const *
scan_cclass(eval_environ_t env, unsigned char const *s)
{
	unsigned char const *start = s;

	s++;
	if (*s == '!')
		s++;
	if (*s == ']')
		s++;
	while (*s != ']') {
			if (!(*s != 0))
#line 70
		(
#line 70
	env_throw_bi(env, mfe_inval, NULL, _("unfinished character class near %s"),start)
#line 70
)
#line 73
;
		if (*s == '[') {
			if (s[1] == ':') {
				int n = strcspn((char*)s+2, ":");
					if (!(s[2+n] == ':' && s[3+n] == ']'))
#line 77
		(
#line 77
	env_throw_bi(env, mfe_inval, NULL, _("unclosed character class near %s"),s)
#line 77
)
#line 80
;
					if (!(find_cclass(s + 2, n)))
#line 81
		(
#line 81
	env_throw_bi(env, mfe_inval, NULL, _("unrecognized character class %*.*s"),n,n,s + 2)
#line 81
)
#line 84
;
				s += n + 4;
				continue;
			}
		}
		if (s[1] == '-') {
			if (s[2] != ']') {
					if (!(s[2] > s[0]))
#line 91
		(
#line 91
	env_throw_bi(env, mfe_range, NULL, _("descending range in character class near %s"),s)
#line 91
)
#line 94
;
				s += 3;
				continue;
			}
		}
		s++;
	}
	return s;
}

static int
match_cclass(int c, unsigned char const **class_ptr)
{
	unsigned char const *cl = *class_ptr + 1;
	int result = 1;

	if (*cl == '!') {
		result = 0;
		cl++;
	}
	if (*cl == ']') {
		cl++;
		if (c == ']')
			goto end;
	}
	for (;;) {
		if (*cl == ']') {
			*class_ptr = cl;
			return !result;
		}

		if (cl[0] == '[' && cl[1] == ':') {
			int n = strcspn((char*)cl+2, ":");
			class_matcher_fn f = find_cclass(cl+2, n);
			cl += n + 4;
			if (f(c))
				break;
		} else if (cl[1] == '-' && cl[2] != ']') {
			cl += 3;
			if (cl[-3] <= c && c <= cl[-1])
				break;
		} else {
			cl++;
			if (cl[-1] == c)
				break;
		}
	}

end:
	/* Skip the rest of class */
	while (*cl != ']') {
		if (cl[0] == '[' && cl[1] == ':')
			cl += strcspn((char*)cl+2, ":") + 4;
		else if (cl[1] == '-' && cl[2] != ']')
			cl += 3;
		else
			cl++;
	}
	*class_ptr = cl;
	return result;
}

#line 288


void
#line 290
bi_tr(eval_environ_t env)
#line 290

#line 290

#line 290 "tr.bi"
{
#line 290
	
#line 290

#line 290
        char * MFL_DATASEG input;
#line 290
        char * MFL_DATASEG set1;
#line 290
        char * MFL_DATASEG set2;
#line 290
        
#line 290

#line 290
        get_string_arg(env, 0, &input);
#line 290
        get_string_arg(env, 1, &set1);
#line 290
        get_string_arg(env, 2, &set2);
#line 290
        
#line 290
        adjust_stack(env, 3);
#line 290

#line 290

#line 290
	if (builtin_module_trace(BUILTIN_IDX_tr))
#line 290
		prog_trace(env, "tr %s %s %s",input, set1, set2);;
#line 290

{
	char *s, *start;
#line 292

#line 292
	/* Operate on usigned characters. */
#line 292
	unsigned char const *subj, *src;
#line 292
	unsigned char const *dst;
#line 292

#line 292
	/* Check consistency of both sets. */
#line 292
			if (!(set2[0] != 0))
#line 292
		(
#line 292
	env_throw_bi(env, mfe_inval, "tr", _("replacement set cannot be empty"))
#line 292
)
#line 292
;
#line 292

#line 292
	for (src = (unsigned char const *)set1,
#line 292
		  dst = (unsigned char const *)set2;
#line 292
	     *src;
#line 292
	     src++) {
#line 292
		if (*src == '[') {
#line 292
			src = scan_cclass(env, src);
#line 292
		} else if (src > (unsigned char *)set1 &&
#line 292
			   src[0] == '-' && src[1]) {
#line 292
				if (!(src[1] > src[-1]))
#line 292
		(
#line 292
	env_throw_bi(env, mfe_range, "tr", _("ranges in source set must be in "
				    "ascending order"))
#line 292
)
#line 292
;
#line 292
								if (!(dst[0] == src[0] && dst[1]))
#line 292
		(
#line 292
	env_throw_bi(env, mfe_inval, "tr", _("ranges should appear at the same "
					    "offset in both source and "
					    "replacement sets"))
#line 292
)
#line 292
;
#line 292
					if (!((dst[1] > dst[-1] ?
#line 292
					   (dst[1] - dst[-1]) :
#line 292
					   (dst[-1] - dst[1])) == src[1] - src[-1]))
#line 292
		(
#line 292
	env_throw_bi(env, mfe_range, "tr", _("source and replacement ranges "
					    "differ in size"))
#line 292
)
#line 292
;
#line 292
		}
#line 292
					dst++;
#line 292
			if (*dst == 0)
#line 292
				break;
#line 292
	}
#line 292

#line 292
	/* Allocate result string. */
#line 292
	heap_obstack_begin(env);
#line 292
	start = heap_obstack_grow(env, NULL, strlen(input) + 1);
#line 292
	s = start;
#line 292

#line 292

#line 292

#line 292
	/* Now translate the input. */
#line 292
	for (subj = (unsigned char const *)input; *subj; subj++, s++) {
#line 292
		int c = *subj;
#line 292

#line 292
		/* Store untranslated character. */
#line 292
		*s = c;
#line 292

#line 292
		/* Walk over source and destination (replacement) sets. */
#line 292
		src = (unsigned char const *)set1;
#line 292
		dst = (unsigned char const *)set2;
#line 292
		if (*src == '-') {
#line 292
			if (c == '-') {
#line 292
				*s = *dst;
#line 292
				continue;
#line 292
			}
#line 292
			src++;
#line 292
			dst++;
#line 292

#line 292
		}
#line 292
		while (*src) {
#line 292
			if (*src == '[') {
#line 292
				if (match_cclass(*s, &src)) {
#line 292
					*s = *dst;
#line 292
					break;
#line 292
				}
#line 292
			} else if (src > (unsigned char const *)set1 &&
#line 292
				   src[0] == '-' && src[1]) {
#line 292
				if (src[-1] < c && c <= src[1]) {
#line 292
						if (dst[1] > dst[-1])
#line 292
							*s = dst[-1] + c - src[-1];
#line 292
						else
#line 292
							*s = dst[-1] - c + src[-1];
#line 292
					break;
#line 292
				} else {
#line 292
					src++;
#line 292
					dst++;
#line 292
				}
#line 292
			} else if (src[0] == c) {//FIXME
#line 292
				*s = *dst;
#line 292
				break;
#line 292
			}
#line 292
			src++;
#line 292
			if (dst[1])
#line 292
				dst++;
#line 292
		}
#line 292
	}
#line 292
	*s = 0;
#line 292
	heap_obstack_truncate(env, s - start + 1);
#line 292
	
#line 292
do {
#line 292
  push(env, (STKVAL) (heap_obstack_finish(env)));
#line 292
  goto endlab;
#line 292
} while (0);
#line 292

#line 292

}
endlab:
#line 294
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 294
	return;
#line 294
}

void
#line 296
bi_sq(eval_environ_t env)
#line 296

#line 296

#line 296 "tr.bi"
{
#line 296
	
#line 296

#line 296
        char * MFL_DATASEG input;
#line 296
        char * MFL_DATASEG set1;
#line 296
        
#line 296

#line 296
        get_string_arg(env, 0, &input);
#line 296
        get_string_arg(env, 1, &set1);
#line 296
        
#line 296
        adjust_stack(env, 2);
#line 296

#line 296

#line 296
	if (builtin_module_trace(BUILTIN_IDX_tr))
#line 296
		prog_trace(env, "sq %s %s",input, set1);;
#line 296

{
	char *s, *start;
#line 298

#line 298
	/* Operate on usigned characters. */
#line 298
	unsigned char const *subj, *src;
#line 298

#line 298
	/* Check consistency of both sets. */
#line 298
	
#line 298

#line 298
	for (src = (unsigned char const *)set1;
#line 298
	     *src;
#line 298
	     src++) {
#line 298
		if (*src == '[') {
#line 298
			src = scan_cclass(env, src);
#line 298
		} else if (src > (unsigned char *)set1 &&
#line 298
			   src[0] == '-' && src[1]) {
#line 298
				if (!(src[1] > src[-1]))
#line 298
		(
#line 298
	env_throw_bi(env, mfe_range, "sq", _("ranges in source set must be in "
				    "ascending order"))
#line 298
)
#line 298
;
#line 298
					}
#line 298
			}
#line 298

#line 298
	/* Allocate result string. */
#line 298
	heap_obstack_begin(env);
#line 298
	start = heap_obstack_grow(env, NULL, strlen(input) + 1);
#line 298
	s = start;
#line 298

#line 298

#line 298

#line 298
	/* Now translate the input. */
#line 298
	for (subj = (unsigned char const *)input; *subj; subj++, s++) {
#line 298
		int c = *subj;
#line 298

#line 298
		/* Store untranslated character. */
#line 298
		*s = c;
#line 298

#line 298
		/* Walk over source and destination (replacement) sets. */
#line 298
		src = (unsigned char const *)set1;
#line 298
		if (*src == '-') {
#line 298
			if (c == '-') {
#line 298
				if (s > start && c == s[-1]) s--;
#line 298
				continue;
#line 298
			}
#line 298
			src++;
#line 298

#line 298
		}
#line 298
		while (*src) {
#line 298
			if (*src == '[') {
#line 298
				if (match_cclass(*s, &src)) {
#line 298
					if (s > start && c == s[-1]) s--;
#line 298
					break;
#line 298
				}
#line 298
			} else if (src > (unsigned char const *)set1 &&
#line 298
				   src[0] == '-' && src[1]) {
#line 298
				if (src[-1] < c && c <= src[1]) {
#line 298
					if (s > start && c == s[-1]) s--;
#line 298
					break;
#line 298
				} else {
#line 298
					src++;
#line 298
				}
#line 298
			} else if (src[0] == c) {//FIXME
#line 298
				if (s > start && c == s[-1]) s--;
#line 298
				break;
#line 298
			}
#line 298
			src++;
#line 298
		}
#line 298
	}
#line 298
	*s = 0;
#line 298
	heap_obstack_truncate(env, s - start + 1);
#line 298
	
#line 298
do {
#line 298
  push(env, (STKVAL) (heap_obstack_finish(env)));
#line 298
  goto endlab;
#line 298
} while (0);
#line 298

#line 298

}
endlab:
#line 300
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 300
	return;
#line 300
}

void
#line 302
bi_dc(eval_environ_t env)
#line 302

#line 302

#line 302 "tr.bi"
{
#line 302
	
#line 302

#line 302
        char * MFL_DATASEG input;
#line 302
        char * MFL_DATASEG set1;
#line 302
        
#line 302

#line 302
        get_string_arg(env, 0, &input);
#line 302
        get_string_arg(env, 1, &set1);
#line 302
        
#line 302
        adjust_stack(env, 2);
#line 302

#line 302

#line 302
	if (builtin_module_trace(BUILTIN_IDX_tr))
#line 302
		prog_trace(env, "dc %s %s",input, set1);;
#line 302

{
	char *s, *start;
#line 304

#line 304
	/* Operate on usigned characters. */
#line 304
	unsigned char const *subj, *src;
#line 304

#line 304
	/* Check consistency of both sets. */
#line 304
	
#line 304

#line 304
	for (src = (unsigned char const *)set1;
#line 304
	     *src;
#line 304
	     src++) {
#line 304
		if (*src == '[') {
#line 304
			src = scan_cclass(env, src);
#line 304
		} else if (src > (unsigned char *)set1 &&
#line 304
			   src[0] == '-' && src[1]) {
#line 304
				if (!(src[1] > src[-1]))
#line 304
		(
#line 304
	env_throw_bi(env, mfe_range, "dc", _("ranges in source set must be in "
				    "ascending order"))
#line 304
)
#line 304
;
#line 304
					}
#line 304
			}
#line 304

#line 304
	/* Allocate result string. */
#line 304
	heap_obstack_begin(env);
#line 304
	start = heap_obstack_grow(env, NULL, strlen(input) + 1);
#line 304
	s = start;
#line 304

#line 304

#line 304

#line 304
	/* Now translate the input. */
#line 304
	for (subj = (unsigned char const *)input; *subj; subj++, s++) {
#line 304
		int c = *subj;
#line 304

#line 304
		/* Store untranslated character. */
#line 304
		*s = c;
#line 304

#line 304
		/* Walk over source and destination (replacement) sets. */
#line 304
		src = (unsigned char const *)set1;
#line 304
		if (*src == '-') {
#line 304
			if (c == '-') {
#line 304
				s--;
#line 304
				continue;
#line 304
			}
#line 304
			src++;
#line 304

#line 304
		}
#line 304
		while (*src) {
#line 304
			if (*src == '[') {
#line 304
				if (match_cclass(*s, &src)) {
#line 304
					s--;
#line 304
					break;
#line 304
				}
#line 304
			} else if (src > (unsigned char const *)set1 &&
#line 304
				   src[0] == '-' && src[1]) {
#line 304
				if (src[-1] < c && c <= src[1]) {
#line 304
						s--;
#line 304
					break;
#line 304
				} else {
#line 304
					src++;
#line 304
				}
#line 304
			} else if (src[0] == c) {//FIXME
#line 304
				s--;
#line 304
				break;
#line 304
			}
#line 304
			src++;
#line 304
		}
#line 304
	}
#line 304
	*s = 0;
#line 304
	heap_obstack_truncate(env, s - start + 1);
#line 304
	
#line 304
do {
#line 304
  push(env, (STKVAL) (heap_obstack_finish(env)));
#line 304
  goto endlab;
#line 304
} while (0);
#line 304

#line 304

}
endlab:
#line 306
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 306
	return;
#line 306
}


#line 1026 "../../src/builtin/snarf.m4"

#line 1026

#line 1026

#line 1026
void
#line 1026
tr_init_builtin(void)
#line 1026
{
#line 1026
	
#line 1026
	#line 290 "tr.bi"
va_builtin_install_ex("tr", bi_tr, 0, dtype_string, 3, 0, 0|0, dtype_string, dtype_string, dtype_string);
#line 296 "tr.bi"
va_builtin_install_ex("sq", bi_sq, 0, dtype_string, 2, 0, 0|0, dtype_string, dtype_string);
#line 302 "tr.bi"
va_builtin_install_ex("dc", bi_dc, 0, dtype_string, 2, 0, 0|0, dtype_string, dtype_string);

#line 1026 "../../src/builtin/snarf.m4"
	
#line 1026
}
#line 1026 "../../src/builtin/snarf.m4"

