1/*
2 * Copyright (c) 2002 by The XFree86 Project, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 * Except as contained in this notice, the name of the XFree86 Project shall
23 * not be used in advertising or otherwise to promote the sale, use or other
24 * dealings in this Software without prior written authorization from the
25 * XFree86 Project.
26 *
27 * Author: Paulo César Pereira de Andrade
28 */
29
30/* $XFree86: xc/programs/xedit/lisp/regex.c,v 1.10tsi Exp $ */
31
32#include "lisp/regex.h"
33#include "lisp/private.h"
34#include "lisp/helper.h"
35
36/*
37 * Prototypes
38 */
39static re_cod *LispRecomp(LispBuiltin*, char*, int);
40
41/*
42 * Initialization
43 */
44LispObj *Knomatch;
45
46/*
47 * Implementation
48 */
49static re_cod *
50LispRecomp(LispBuiltin *builtin, char *pattern, int cflags)
51{
52    int code;
53    re_cod *regex = LispMalloc(sizeof(re_cod));
54
55    if ((code = recomp(regex, pattern, cflags)) != 0) {
56	char buffer[256];
57
58	reerror(code, regex, buffer, sizeof(buffer));
59	refree(regex);
60	LispFree(regex);
61	LispDestroy("%s: recomp(\"%s\"): %s", STRFUN(builtin), pattern, buffer);
62    }
63
64    return (regex);
65}
66
67void
68LispRegexInit(void)
69{
70    Knomatch = KEYWORD("NOMATCH");
71}
72
73LispObj *
74Lisp_Recomp(LispBuiltin *builtin)
75/*
76 re-comp pattern &key nospec icase nosub newline
77 */
78{
79    re_cod *regex;
80    int cflags = 0;
81
82    LispObj *result;
83
84    LispObj *pattern, *nospec, *icase, *nosub, *newline;
85
86    newline = ARGUMENT(4);
87    nosub = ARGUMENT(3);
88    icase = ARGUMENT(2);
89    nospec = ARGUMENT(1);
90    pattern = ARGUMENT(0);
91
92    /* Don't generate an error if it is already a compiled regex. */
93    if (REGEXP(pattern))
94	return (pattern);
95
96    CHECK_STRING(pattern);
97
98    if (nospec != UNSPEC && nospec != NIL)
99	cflags |= RE_NOSPEC;
100    if (icase != UNSPEC && icase != NIL)
101	cflags |= RE_ICASE;
102    if (nosub != UNSPEC && nosub != NIL)
103	cflags |= RE_NOSUB;
104    if (newline != UNSPEC && newline != NIL)
105	cflags |= RE_NEWLINE;
106
107    regex = LispRecomp(builtin, THESTR(pattern), cflags);
108    result = LispNew(pattern, NIL);
109    result->type = LispRegex_t;
110    result->data.regex.regex = regex;
111    result->data.regex.pattern = pattern;
112    result->data.regex.options = cflags;
113    LispMused(regex);
114
115    return (result);
116}
117
118LispObj *
119Lisp_Reexec(LispBuiltin *builtin)
120/*
121 re-exec regex string &key count start end notbol noteol
122 */
123{
124    size_t nmatch;
125    re_mat match[10];
126    long start, end, length;
127    int code, cflags, eflags;
128    char *string;
129    LispObj *result;
130    re_cod *regexp;
131
132    LispObj *regex, *ostring, *count, *ostart, *oend, *notbol, *noteol;
133
134    noteol = ARGUMENT(6);
135    notbol = ARGUMENT(5);
136    oend = ARGUMENT(4);
137    ostart = ARGUMENT(3);
138    count = ARGUMENT(2);
139    ostring = ARGUMENT(1);
140    regex = ARGUMENT(0);
141
142    if (STRINGP(regex))
143	regexp = LispRecomp(builtin, THESTR(regex), cflags = 0);
144    else {
145	CHECK_REGEX(regex);
146	regexp = regex->data.regex.regex;
147	cflags = regex->data.regex.options;
148    }
149
150    CHECK_STRING(ostring);
151
152    if (count == UNSPEC)
153	nmatch = 1;
154    else {
155	CHECK_INDEX(count);
156	nmatch = FIXNUM_VALUE(count);
157	if (nmatch > 10)
158	    LispDestroy("%s: COUNT cannot be larger than 10", STRFUN(builtin));
159    }
160    if (nmatch && (cflags & RE_NOSUB))
161	nmatch = 1;
162
163    eflags = RE_STARTEND;
164    if (notbol != UNSPEC && notbol != NIL)
165	eflags |= RE_NOTBOL;
166    if (noteol != UNSPEC && noteol != NIL)
167	eflags |= RE_NOTEOL;
168
169    string = THESTR(ostring);
170    LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
171			      &start, &end, &length);
172
173    match[0].rm_so = start;
174    match[0].rm_eo = end;
175    code = reexec(regexp, string, nmatch, &match[0], eflags);
176
177    if (code == 0) {
178	if (nmatch && match[0].rm_eo >= match[0].rm_so) {
179	    result = CONS(CONS(FIXNUM(match[0].rm_so),
180			       FIXNUM(match[0].rm_eo)), NIL);
181	    if (nmatch > 1 && match[1].rm_eo >= match[1].rm_so) {
182		int i;
183		GC_ENTER();
184		LispObj *cons = result;
185
186		GC_PROTECT(result);
187		for (i = 1;
188		     i < nmatch && match[i].rm_eo >= match[i].rm_so;
189		     i++) {
190		    RPLACD(cons, CONS(CONS(FIXNUM(match[i].rm_so),
191					   FIXNUM(match[i].rm_eo)), NIL));
192		    cons = CDR(cons);
193		}
194		GC_LEAVE();
195	    }
196	}
197	else
198	    result = NIL;
199    }
200    else
201	result = Knomatch;
202
203    /* Maybe shoud cache compiled regex, but better the caller do it */
204    if (!XREGEXP(regex)) {
205	refree(regexp);
206	LispFree(regexp);
207    }
208
209    return (result);
210}
211
212LispObj *
213Lisp_Rep(LispBuiltin *builtin)
214/*
215 re-p object
216 */
217{
218    LispObj *object;
219
220    object = ARGUMENT(0);
221
222    return (REGEXP(object) ? T : NIL);
223}
224