2 * Copyright (C) 2012 Toni Gundogdu <legatvs@gmail.com>
4 * This file is part of quvi <http://quvi.sourceforge.net/>.
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU Affero General Public
8 * License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General
17 * Public License along with this program. If not, see
18 * <http://www.gnu.org/licenses/>.
22 * Supports Perl-like syntax. Supports the 'i' modifier.
23 * m// - match operation ('m' is optional)
24 * s/// - substitution operation
27 * - Matches/replaces all occurences (similar to 'g' modifier of Perl)
28 * - Similar to Perl-syntax; with the exception of the "property
29 * sequence" -prefix being mandatory (e.g. "%t:s/foo/bar/i")
35 #include <glib/gi18n.h>
39 /* Check if the pattern is a s/// operation. */
40 static gboolean
_chk_opmode_s(const gchar
*p
, lutil_regex_op_t
*op
)
42 static const gchar
*op_s
= "^(%\\w):s/(.*?)/(.*?)/(.*?)$";
54 re
= g_regex_new(op_s
, 0, 0, &e
);
57 g_printerr(_("error: %s: while creating the s/// "
58 "operation mode regex: %s\n"), op_s
, e
->message
);
63 r
= g_regex_match(re
, p
, 0, &m
);
66 *op
= g_new0(struct _lutil_regex_op_s
, 1);
67 (*op
)->sequence
= g_match_info_fetch(m
, 1);
68 (*op
)->mode
= g_strdup("s");
69 (*op
)->regex
= g_match_info_fetch(m
, 2);
70 (*op
)->subst
.replacement
= g_match_info_fetch(m
, 3);
71 (*op
)->modifiers
= g_match_info_fetch(m
, 4);
78 /* Check if the pattern is a m// operation. */
79 static gboolean
_chk_opmode_m(const gchar
*p
, lutil_regex_op_t
*op
)
81 static const gchar
*op_m
= "^(%\\w):(.*?)/(.*?)/(.*?)$";
93 re
= g_regex_new(op_m
, 0, 0, &e
);
96 g_printerr(_("error: %s: while creating the m// "
97 "operation mode regex: %s\n"), op_m
, e
->message
);
102 r
= g_regex_match(re
, p
, 0, &m
);
105 *op
= g_new0(struct _lutil_regex_op_s
, 1);
106 (*op
)->sequence
= g_match_info_fetch(m
, 1);
107 (*op
)->mode
= g_match_info_fetch(m
, 2);
108 if (strlen((*op
)->mode
) ==0)
111 (*op
)->mode
= g_strdup("m");
113 (*op
)->regex
= g_match_info_fetch(m
, 3);
114 (*op
)->modifiers
= g_match_info_fetch(m
, 4);
116 g_match_info_free(m
);
122 static void _dump(lutil_regex_op_t op
)
124 g_message("[%s] op->modifiers=%s", __func__
, op
->modifiers
);
125 g_message("[%s] op->sequence=%s", __func__
, op
->sequence
);
126 g_message("[%s] op->regex=%s", __func__
, op
->regex
);
127 g_message("[%s] op->mode=%s", __func__
, op
->mode
);
128 g_message("[%s] op->subst.replacement=%s", __func__
, op
->subst
.replacement
);
132 static const gchar
*_EINVSYN
=
133 N_("error: %s: invalid syntax: must be either "
134 "m// or s/// operation\n");
136 /* If is_valid is set, then validate the pattern only (and return NULL). */
137 lutil_regex_op_t
lutil_regex_op_new(const gchar
*p
, gboolean
*is_valid
)
139 lutil_regex_op_t op
= NULL
;
141 if (is_valid
!= NULL
)
144 if (_chk_opmode_s(p
, &op
) == FALSE
)
146 if (_chk_opmode_m(p
, &op
) == FALSE
)
148 g_printerr(g_dgettext(GETTEXT_PACKAGE
, _EINVSYN
), p
);
153 if (g_strcmp0(op
->mode
,"s") ==0)
155 /* For lack of a better regex, check this the hard way. */
156 if (op
->subst
.replacement
==NULL
)
158 g_printerr(_("error: %s: invalid s/// operation syntax\n"), p
);
163 if (is_valid
!= NULL
)
167 else if (g_strcmp0(op
->mode
, "m") ==0)
169 if (is_valid
!= NULL
)
173 g_printerr(g_dgettext(GETTEXT_PACKAGE
, _EINVSYN
), p
);
179 if (is_valid
!= NULL
)
181 lutil_regex_op_free(op
);
187 void lutil_regex_op_free(lutil_regex_op_t op
)
192 g_free(op
->subst
.replacement
);
193 g_free(op
->modifiers
);
194 g_free(op
->sequence
);
199 memset(op
, 0, sizeof(struct _lutil_regex_op_s
));
202 static gchar
*_op_m(lutil_regex_op_t op
, const gchar
*s
)
204 GRegexCompileFlags flags
;
210 g_assert(op
!= NULL
);
212 flags
= (g_strrstr(op
->modifiers
, "i") != NULL
) ? G_REGEX_CASELESS
:0;
216 re
= g_regex_new(op
->regex
, flags
, 0, &e
);
219 g_printerr(_("error: %s: while creating m// "
220 "operation mode regex: %s\n"), op
->regex
, e
->message
);
225 g_regex_match_full(re
, s
, -1, 0, 0, &m
, &e
);
228 g_printerr(_("error: %s: while matching: %s\n"), op
->regex
, e
->message
);
233 GString
*t
= g_string_new(NULL
);
234 while (g_match_info_matches(m
) == TRUE
)
236 gchar
*v
= g_match_info_fetch(m
, 0);
237 g_string_append(t
, v
);
239 g_match_info_next(m
, &e
);
242 g_printerr(_("error: while retrieving match info: %s\n"),
248 g_match_info_free(m
);
249 r
= g_strdup(t
->str
);
250 g_string_free(t
, TRUE
);
256 static gchar
*_op_s(lutil_regex_op_t op
, const gchar
*s
)
258 GRegexCompileFlags flags
;
263 flags
= (g_strrstr(op
->modifiers
, "i") != NULL
) ? G_REGEX_CASELESS
:0;
267 re
= g_regex_new(op
->regex
, flags
, 0, &e
);
270 g_printerr(_("error: %s: while creating s/// "
271 "operation mode regex: %s\n"), op
->regex
, e
->message
);
276 r
= g_regex_replace(re
, s
, -1, 0, op
->subst
.replacement
, 0, &e
);
279 g_printerr(_("error: %s: while substituting: %s\n"),
280 op
->regex
, e
->message
);
288 /* g_free the returned string. */
289 gchar
*lutil_regex_op_apply(lutil_regex_op_t op
, const gchar
*s
)
293 g_assert(op
!= NULL
);
296 if (g_strcmp0(op
->mode
,"s") ==0)
298 else if (g_strcmp0(op
->mode
,"m") ==0)
304 /* vim: set ts=2 sw=2 tw=72 expandtab: */