16483eba0Schristos /* $OpenBSD$ */
26483eba0Schristos
36483eba0Schristos /*
46483eba0Schristos * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
56483eba0Schristos *
66483eba0Schristos * Permission to use, copy, modify, and distribute this software for any
76483eba0Schristos * purpose with or without fee is hereby granted, provided that the above
86483eba0Schristos * copyright notice and this permission notice appear in all copies.
96483eba0Schristos *
106483eba0Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116483eba0Schristos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126483eba0Schristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136483eba0Schristos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146483eba0Schristos * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
156483eba0Schristos * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
166483eba0Schristos * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176483eba0Schristos */
186483eba0Schristos
196483eba0Schristos #include <sys/types.h>
206483eba0Schristos
216483eba0Schristos #include <regex.h>
226483eba0Schristos #include <string.h>
236483eba0Schristos
246483eba0Schristos #include "tmux.h"
256483eba0Schristos
266483eba0Schristos static void
regsub_copy(char ** buf,ssize_t * len,const char * text,size_t start,size_t end)27*e271dbb8Schristos regsub_copy(char **buf, ssize_t *len, const char *text, size_t start, size_t end)
286483eba0Schristos {
296483eba0Schristos size_t add = end - start;
306483eba0Schristos
316483eba0Schristos *buf = xrealloc(*buf, (*len) + add + 1);
326483eba0Schristos memcpy((*buf) + *len, text + start, add);
336483eba0Schristos (*len) += add;
346483eba0Schristos }
356483eba0Schristos
366483eba0Schristos static void
regsub_expand(char ** buf,ssize_t * len,const char * with,const char * text,regmatch_t * m,u_int n)3730744affSchristos regsub_expand(char **buf, ssize_t *len, const char *with, const char *text,
386483eba0Schristos regmatch_t *m, u_int n)
396483eba0Schristos {
406483eba0Schristos const char *cp;
416483eba0Schristos u_int i;
426483eba0Schristos
436483eba0Schristos for (cp = with; *cp != '\0'; cp++) {
446483eba0Schristos if (*cp == '\\') {
456483eba0Schristos cp++;
466483eba0Schristos if (*cp >= '0' && *cp <= '9') {
476483eba0Schristos i = *cp - '0';
486483eba0Schristos if (i < n && m[i].rm_so != m[i].rm_eo) {
496483eba0Schristos regsub_copy(buf, len, text, m[i].rm_so,
506483eba0Schristos m[i].rm_eo);
516483eba0Schristos continue;
526483eba0Schristos }
536483eba0Schristos }
546483eba0Schristos }
556483eba0Schristos *buf = xrealloc(*buf, (*len) + 2);
566483eba0Schristos (*buf)[(*len)++] = *cp;
576483eba0Schristos }
586483eba0Schristos }
596483eba0Schristos
606483eba0Schristos char *
regsub(const char * pattern,const char * with,const char * text,int flags)616483eba0Schristos regsub(const char *pattern, const char *with, const char *text, int flags)
626483eba0Schristos {
636483eba0Schristos regex_t r;
646483eba0Schristos regmatch_t m[10];
656483eba0Schristos ssize_t start, end, last, len = 0;
666483eba0Schristos int empty = 0;
676483eba0Schristos char *buf = NULL;
686483eba0Schristos
696483eba0Schristos if (*text == '\0')
706483eba0Schristos return (xstrdup(""));
716483eba0Schristos if (regcomp(&r, pattern, flags) != 0)
726483eba0Schristos return (NULL);
736483eba0Schristos
746483eba0Schristos start = 0;
756483eba0Schristos last = 0;
766483eba0Schristos end = strlen(text);
776483eba0Schristos
786483eba0Schristos while (start <= end) {
796483eba0Schristos if (regexec(&r, text + start, nitems(m), m, 0) != 0) {
806483eba0Schristos regsub_copy(&buf, &len, text, start, end);
816483eba0Schristos break;
826483eba0Schristos }
836483eba0Schristos
846483eba0Schristos /*
856483eba0Schristos * Append any text not part of this match (from the end of the
866483eba0Schristos * last match).
876483eba0Schristos */
886483eba0Schristos regsub_copy(&buf, &len, text, last, m[0].rm_so + start);
896483eba0Schristos
906483eba0Schristos /*
916483eba0Schristos * If the last match was empty and this one isn't (it is either
926483eba0Schristos * later or has matched text), expand this match. If it is
936483eba0Schristos * empty, move on one character and try again from there.
946483eba0Schristos */
956483eba0Schristos if (empty ||
966483eba0Schristos start + m[0].rm_so != last ||
976483eba0Schristos m[0].rm_so != m[0].rm_eo) {
986483eba0Schristos regsub_expand(&buf, &len, with, text + start, m,
996483eba0Schristos nitems(m));
1006483eba0Schristos
1016483eba0Schristos last = start + m[0].rm_eo;
1026483eba0Schristos start += m[0].rm_eo;
1036483eba0Schristos empty = 0;
1046483eba0Schristos } else {
1056483eba0Schristos last = start + m[0].rm_eo;
1066483eba0Schristos start += m[0].rm_eo + 1;
1076483eba0Schristos empty = 1;
1086483eba0Schristos }
10968e6ba84Schristos
11068e6ba84Schristos /* Stop now if anchored to start. */
11168e6ba84Schristos if (*pattern == '^') {
11268e6ba84Schristos regsub_copy(&buf, &len, text, start, end);
11368e6ba84Schristos break;
11468e6ba84Schristos }
1156483eba0Schristos }
1166483eba0Schristos buf[len] = '\0';
1176483eba0Schristos
1186483eba0Schristos regfree(&r);
1196483eba0Schristos return (buf);
1206483eba0Schristos }
121