utils.c 16 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   utils.c                                                              *
 *                                                                        *
5
 *   Copyright (C) 1999-2004 Chris Allegretta                             *
Chris Allegretta's avatar
Chris Allegretta committed
6
7
 *   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 *
8
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
11
12
13
14
15
16
17
18
19
20
21
 *   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, write to the Free Software          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

22
23
24
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
25

Chris Allegretta's avatar
Chris Allegretta committed
26
27
#include <stdlib.h>
#include <string.h>
28
29
#include <stdio.h>
#include <unistd.h>
Chris Allegretta's avatar
Chris Allegretta committed
30
#include <ctype.h>
31
#include <errno.h>
Chris Allegretta's avatar
Chris Allegretta committed
32
#include <assert.h>
Chris Allegretta's avatar
Chris Allegretta committed
33
#include "proto.h"
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
34
#include "nano.h"
Chris Allegretta's avatar
Chris Allegretta committed
35

36
#ifdef HAVE_REGEX_H
37
38
39
40
41
42
43
44
45
46
#ifdef BROKEN_REGEXEC
#undef regexec
int regexec_safe(const regex_t *preg, const char *string, size_t nmatch,
	regmatch_t pmatch[], int eflags)
{
    if (string != NULL && *string != '\0')
	return regexec(preg, string, nmatch, pmatch, eflags);
    return REG_NOMATCH;
}
#define regexec(preg, string, nmatch, pmatch, eflags) regexec_safe(preg, string, nmatch, pmatch, eflags)
47
48
49
50
#endif /* BROKEN_REGEXEC */

int regexp_bol_or_eol(const regex_t *preg, const char *string)
{
51
52
    return (regexec(preg, string, 0, NULL, 0) == 0 &&
	regexec(preg, string, 0, NULL, REG_NOTBOL | REG_NOTEOL) ==
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
53
	REG_NOMATCH);
54
55
}
#endif /* HAVE_REGEX_H */
56

57
58
59
60
61
62
63
64
65
66
#ifndef HAVE_ISBLANK
/* This function is equivalent to isblank(). */
int is_blank_char(int c)
{
    return (c == '\t' || c == ' ');
}
#endif

/* This function is equivalent to iscntrl(), except in that it also
 * handles control characters with their high bits set. */
Chris Allegretta's avatar
Chris Allegretta committed
67
68
int is_cntrl_char(int c)
{
69
    return (-128 <= c && c < -96) || (0 <= c && c < 32) ||
70
	(127 <= c && c < 160);
Chris Allegretta's avatar
Chris Allegretta committed
71
72
}

73
74
75
76
77
78
79
/* Return TRUE if the character c is in byte range, and FALSE
 * otherwise. */
bool is_byte_char(int c)
{
    return (unsigned int)c == (unsigned char)c;
}

80
81
82
83
84
int num_of_digits(int n)
{
    int i = 1;

    if (n < 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
85
	n = -n;
86
87
88
89
90
91
92
93
94

    while (n > 10) {
	n /= 10;
	i++;
    }

    return i;
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
95
96
97
/* Read a ssize_t from str, and store it in *val (if val is not NULL).
 * On error, we return FALSE and don't change *val.  Otherwise, we
 * return TRUE. */
98
bool parse_num(const char *str, ssize_t *val)
99
100
101
102
103
104
105
{
    char *first_error;
    ssize_t j;

    assert(str != NULL);
    j = (ssize_t)strtol(str, &first_error, 10);
    if (errno == ERANGE || *str == '\0' || *first_error != '\0')
106
	return FALSE;
107
108
    if (val != NULL)
	*val = j;
109
    return TRUE;
110
111
}

Chris Allegretta's avatar
Chris Allegretta committed
112
113
/* Fix the memory allocation for a string. */
void align(char **strp)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
114
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
115
116
117
    assert(strp != NULL);
    if (*strp != NULL)
	*strp = charealloc(*strp, strlen(*strp) + 1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
118
119
}

Chris Allegretta's avatar
Chris Allegretta committed
120
121
/* Null a string at a certain index and align it. */
void null_at(char **data, size_t index)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
122
{
Chris Allegretta's avatar
Chris Allegretta committed
123
    assert(data != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
124
    *data = charealloc(*data, index + 1);
Chris Allegretta's avatar
Chris Allegretta committed
125
    (*data)[index] = '\0';
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
126
127
}

Chris Allegretta's avatar
Chris Allegretta committed
128
129
130
/* For non-null-terminated lines.  A line, by definition, shouldn't
 * normally have newlines in it, so encode its nulls as newlines. */
void unsunder(char *str, size_t true_len)
Chris Allegretta's avatar
Chris Allegretta committed
131
{
Chris Allegretta's avatar
Chris Allegretta committed
132
    assert(str != NULL);
133
    for (; true_len > 0; true_len--, str++) {
Chris Allegretta's avatar
Chris Allegretta committed
134
135
	if (*str == '\0')
	    *str = '\n';
136
    }
Chris Allegretta's avatar
Chris Allegretta committed
137
}
Chris Allegretta's avatar
Chris Allegretta committed
138

Chris Allegretta's avatar
Chris Allegretta committed
139
140
141
142
143
/* For non-null-terminated lines.  A line, by definition, shouldn't
 * normally have newlines in it, so decode its newlines into nulls. */
void sunder(char *str)
{
    assert(str != NULL);
144
    for (; *str != '\0'; str++) {
Chris Allegretta's avatar
Chris Allegretta committed
145
146
	if (*str == '\n')
	    *str = '\0';
147
    }
Chris Allegretta's avatar
Chris Allegretta committed
148
149
}

150
151
152
153
154
#ifndef HAVE_STRCASECMP
/* This function is equivalent to strcasecmp(). */
int nstricmp(const char *s1, const char *s2)
{
    assert(s1 != NULL && s2 != NULL);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
155

156
157
158
159
    for (; *s1 != '\0' && *s2 != '\0'; s1++, s2++) {
	if (tolower(*s1) != tolower(*s2))
	    break;
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
160

161
162
163
164
165
166
167
168
169
    return (tolower(*s1) - tolower(*s2));
}
#endif

#ifndef HAVE_STRNCASECMP
/* This function is equivalent to strncasecmp(). */
int nstrnicmp(const char *s1, const char *s2, size_t n)
{
    assert(s1 != NULL && s2 != NULL);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
170

171
172
173
174
    for (; n > 0 && *s1 != '\0' && *s2 != '\0'; n--, s1++, s2++) {
	if (tolower(*s1) != tolower(*s2))
	    break;
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
175

176
177
    if (n > 0)
	return (tolower(*s1) - tolower(*s2));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
178
    else
179
	return 0;
180
181
182
}
#endif

183
#ifndef HAVE_STRCASESTR
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* This function is equivalent to strcasestr().  It was adapted from
 * mutt's mutt_stristr() function. */
const char *nstristr(const char *haystack, const char *needle)
{
    assert(haystack != NULL && needle != NULL);

    for (; *haystack != '\0'; haystack++) {
	const char *p = haystack;
	const char *q = needle;

	for (; tolower(*p) == tolower(*q) && *q != '\0'; p++, q++)
	    ;

	if (*q == '\0')
	    return haystack;
    }

    return NULL;
}
203
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
204

205
206
/* None of this is needed if we're using NANO_SMALL! */
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
207
208
const char *revstrstr(const char *haystack, const char *needle, const
	char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
209
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
210
    for (; rev_start >= haystack; rev_start--) {
Chris Allegretta's avatar
Chris Allegretta committed
211
	const char *r, *q;
Chris Allegretta's avatar
Chris Allegretta committed
212

Chris Allegretta's avatar
Chris Allegretta committed
213
	for (r = rev_start, q = needle ; *q == *r && *q != '\0'; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
214
215
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
216
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
217
    }
Chris Allegretta's avatar
Chris Allegretta committed
218
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
219
220
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
221
222
const char *revstristr(const char *haystack, const char *needle, const
	char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
223
{
Chris Allegretta's avatar
Chris Allegretta committed
224
225
    for (; rev_start >= haystack; rev_start--) {
	const char *r = rev_start, *q = needle;
Chris Allegretta's avatar
Chris Allegretta committed
226

Chris Allegretta's avatar
Chris Allegretta committed
227
	for (; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
228
229
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
230
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
231
    }
Chris Allegretta's avatar
Chris Allegretta committed
232
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
233
}
Chris Allegretta's avatar
Chris Allegretta committed
234
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
235

236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#ifndef HAVE_STRNLEN
/* This function is equivalent to strnlen(). */
size_t nstrnlen(const char *s, size_t maxlen)
{
    size_t n = 0;

    assert(s != NULL);

    for (; maxlen > 0 && *s != '\0'; maxlen--, n++, s++)
	;

    return n;
}
#endif

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
251
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#ifndef HAVE_GETLINE
/* This function is equivalent to getline().  It was adapted from
 * GNU mailutils' getline() function. */
ssize_t ngetline(char **lineptr, size_t *n, FILE *stream)
{
    return getdelim(lineptr, n, '\n', stream);
}
#endif

#ifndef HAVE_GETDELIM
/* This function is equivalent to getdelim().  It was adapted from
 * GNU mailutils' getdelim() function. */
ssize_t ngetdelim(char **lineptr, size_t *n, int delim, FILE *stream)
{
    size_t indx = 0;
    int c;

    /* Sanity checks. */
    if (lineptr == NULL || n == NULL || stream == NULL)
	return -1;

    /* Allocate the line the first time. */
    if (*lineptr == NULL) {
275
276
	*lineptr = charalloc(128);
	*n = 128;
277
278
279
280
281
    }

    while ((c = getc(stream)) != EOF) {
	/* Check if more memory is needed. */
	if (indx >= *n) {
282
283
	    *lineptr = charealloc(*lineptr, *n + 128);
	    *n += 128;
284
285
286
287
288
289
290
291
292
293
294
295
	}

	/* Push the result in the line. */
	(*lineptr)[indx++] = (char)c;

	/* Bail out. */
	if (c == delim)
	    break;
    }

    /* Make room for the null character. */
    if (indx >= *n) {
296
297
	*lineptr = charealloc(*lineptr, *n + 128);
	*n += 128;
298
299
300
    }

    /* Null terminate the buffer. */
301
    null_at(lineptr, indx++);
302
    *n = indx;
303
304
305
306
307
308

    /* The last line may not have the delimiter, we have to return what
     * we got and the error will be seen on the next iteration. */
    return (c == EOF && (indx - 1) == 0) ? -1 : indx - 1;
}
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
309
#endif /* !NANO_SMALL && ENABLE_NANORC */
310

311
312
313
314
315
316
/* If we are searching backwards, we will find the last match that
 * starts no later than start.  Otherwise we find the first match
 * starting no earlier than start.  If we are doing a regexp search, we
 * fill in the global variable regmatches with at most 9 subexpression
 * matches.  Also, all .rm_so elements are relative to the start of the
 * whole match, so regmatches[0].rm_so == 0. */
Chris Allegretta's avatar
Chris Allegretta committed
317
const char *strstrwrapper(const char *haystack, const char *needle,
318
	const char *start)
Chris Allegretta's avatar
Chris Allegretta committed
319
{
320
    /* start can be 1 character before the start or after the end of the
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
321
     * line.  In either case, we just say no match was found. */
322
323
324
    if ((start > haystack && *(start - 1) == '\0') || start < haystack)
	return NULL;
    assert(haystack != NULL && needle != NULL && start != NULL);
325
#ifdef HAVE_REGEX_H
326
    if (ISSET(USE_REGEXP)) {
327
#ifndef NANO_SMALL
328
	if (ISSET(REVERSE_SEARCH)) {
329
330
	    if (regexec(&search_regexp, haystack, 1, regmatches, 0) == 0
		&& haystack + regmatches[0].rm_so <= start) {
331
332
		const char *retval = haystack + regmatches[0].rm_so;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
333
		/* Search forward until there are no more matches. */
334
		while (regexec(&search_regexp, retval + 1, 1, regmatches,
335
336
			REG_NOTBOL) == 0 && retval + 1 +
			regmatches[0].rm_so <= start)
337
338
339
340
341
342
		    retval += 1 + regmatches[0].rm_so;
		/* Finally, put the subexpression matches in global
		 * variable regmatches.  The REG_NOTBOL flag doesn't
		 * matter now. */
		regexec(&search_regexp, retval, 10, regmatches, 0);
		return retval;
Chris Allegretta's avatar
Chris Allegretta committed
343
	    }
344
345
	} else
#endif /* !NANO_SMALL */
346
347
348
	if (regexec(&search_regexp, start, 10, regmatches,
		start > haystack ? REG_NOTBOL : 0) == 0) {
	    const char *retval = start + regmatches[0].rm_so;
349
350
351

	    regexec(&search_regexp, retval, 10, regmatches, 0);
	    return retval;
352
	}
353
	return NULL;
354
    }
355
#endif /* HAVE_REGEX_H */
356
#if !defined(DISABLE_SPELLER) || !defined(NANO_SMALL)
357
    if (ISSET(CASE_SENSITIVE)) {
358
#ifndef NANO_SMALL
359
	if (ISSET(REVERSE_SEARCH))
360
	    return revstrstr(haystack, needle, start);
361
	else
362
#endif
363
	    return strstr(start, needle);
364
365
366
367
368
    }
#endif /* !DISABLE_SPELLER || !NANO_SMALL */
#ifndef NANO_SMALL
    else if (ISSET(REVERSE_SEARCH))
	return revstristr(haystack, needle, start);
369
#endif
370
    return strcasestr(start, needle);
Chris Allegretta's avatar
Chris Allegretta committed
371
}
Chris Allegretta's avatar
Chris Allegretta committed
372

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
373
374
375
/* This is a wrapper for the perror() function.  The wrapper takes care
 * of ncurses, calls perror (which writes to stderr), then refreshes the
 * screen.  Note that nperror() causes the window to flicker once. */
376
377
void nperror(const char *s)
{
378
    /* leave ncurses mode, go to the terminal */
379
380
381
382
383
384
    if (endwin() != ERR) {
	perror(s);		/* print the error */
	total_refresh();	/* return to ncurses and repaint */
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
385
386
/* Thanks BG, many ppl have been asking for this... */
void *nmalloc(size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
387
{
Chris Allegretta's avatar
Chris Allegretta committed
388
    void *r = malloc(howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
389

Chris Allegretta's avatar
Chris Allegretta committed
390
391
    if (r == NULL && howmuch != 0)
	die(_("nano is out of memory!"));
392

Chris Allegretta's avatar
Chris Allegretta committed
393
    return r;
394
395
}

Chris Allegretta's avatar
Chris Allegretta committed
396
void *nrealloc(void *ptr, size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
397
{
Chris Allegretta's avatar
Chris Allegretta committed
398
    void *r = realloc(ptr, howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
399

Chris Allegretta's avatar
Chris Allegretta committed
400
401
    if (r == NULL && howmuch != 0)
	die(_("nano is out of memory!"));
Chris Allegretta's avatar
Chris Allegretta committed
402
403
404

    return r;
}
Robert Siemborski's avatar
Robert Siemborski committed
405

406
407
408
409
/* Copy the first n characters of one malloc()ed string to another
 * pointer.  Should be used as: "dest = mallocstrncpy(dest, src,
 * n);". */
char *mallocstrncpy(char *dest, const char *src, size_t n)
410
{
411
412
    if (src == NULL)
	src = "";
413

414
    if (src != dest)
415
416
	free(dest);

417
418
    dest = charalloc(n);
    strncpy(dest, src, n);
419
420
421
422

    return dest;
}

423
424
425
426
427
428
429
/* Copy one malloc()ed string to another pointer.  Should be used as:
 * "dest = mallocstrcpy(dest, src);". */
char *mallocstrcpy(char *dest, const char *src)
{
    return mallocstrncpy(dest, src, src == NULL ? 1 : strlen(src) + 1);
}

430
431
432
433
434
435
436
437
438
439
/* Free the malloc()ed string at dest and return the malloc()ed string
 * at src.  Should be used as: "answer = mallocstrassn(answer,
 * real_dir_from_tilde(answer));". */
char *mallocstrassn(char *dest, char *src)
{
    free(dest);
    return src;
}

/* Append a new magicline to filebot. */
440
441
void new_magicline(void)
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
442
    filebot->next = (filestruct *)nmalloc(sizeof(filestruct));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
443
    filebot->next->data = mallocstrcpy(NULL, "");
Robert Siemborski's avatar
Robert Siemborski committed
444
445
446
447
448
    filebot->next->prev = filebot;
    filebot->next->next = NULL;
    filebot->next->lineno = filebot->lineno + 1;
    filebot = filebot->next;
    totlines++;
449
    totsize++;
Robert Siemborski's avatar
Robert Siemborski committed
450
}
Chris Allegretta's avatar
Chris Allegretta committed
451

452
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
453
454
/* Remove the magicline from filebot, if there is one and it isn't the
 * only line in the file. */
455
456
void remove_magicline(void)
{
457
    if (filebot->data[0] == '\0' && filebot->prev != NULL) {
458
459
460
461
462
463
464
465
	filebot = filebot->prev;
	free_filestruct(filebot->next);
	filebot->next = NULL;
	totlines--;
	totsize--;
    }
}

466
467
468
469
470
471
472
473
474
475
476
/* Set top_x and bot_x to the top and bottom x-coordinates of the mark,
 * respectively, based on the locations of top and bot.  If
 * right_side_up isn't NULL, set it to TRUE If the mark begins with
 * (mark_beginbuf, mark_beginx) and ends with (current, current_x), or
 * FALSE otherwise. */
void mark_order(const filestruct **top, size_t *top_x, const filestruct
	**bot, size_t *bot_x, bool *right_side_up)
{
    assert(top != NULL && top_x != NULL && bot != NULL && bot_x != NULL);

    if ((current->lineno == mark_beginbuf->lineno && current_x >
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
477
	mark_beginx) || current->lineno > mark_beginbuf->lineno) {
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
	*top = mark_beginbuf;
	*top_x = mark_beginx;
	*bot = current;
	*bot_x = current_x;
	if (right_side_up != NULL)
	    *right_side_up = TRUE;
    } else {
	*bot = mark_beginbuf;
	*bot_x = mark_beginx;
	*top = current;
	*top_x = current_x;
	if (right_side_up != NULL)
	    *right_side_up = FALSE;
    }
}
#endif

495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
/* Calculate the number of lines and the number of characters between
 * begin and end, and return them in lines and size, respectively. */
void get_totals(const filestruct *begin, const filestruct *end, int
	*lines, long *size)
{
    const filestruct *f;

    if (lines != NULL)
	*lines = 0;
    if (size != NULL)
	*size = 0;

    /* Go through the lines from begin to end->prev, if we can. */
    for (f = begin; f != NULL && f != end; f = f->next) {
	/* Count this line. */
510
511
	if (lines != NULL)
	    (*lines)++;
512
513

	/* Count the number of characters on this line. */
514
515
	if (size != NULL) {
	    *size += strlen(f->data);
516

517
518
519
520
	    /* Count the newline if we have one. */
	    if (f->next != NULL)
		(*size)++;
	}
521
522
523
524
525
    }

    /* Go through the line at end, if we can. */
    if (f != NULL) {
	/* Count this line. */
526
527
	if (lines != NULL)
	    (*lines)++;
528
529

	/* Count the number of characters on this line. */
530
531
	if (size != NULL) {
	    *size += strlen(f->data);
532

533
534
535
536
	    /* Count the newline if we have one. */
	    if (f->next != NULL)
		(*size)++;
	}
537
538
539
    }
}

540
#ifndef DISABLE_TABCOMP
Chris Allegretta's avatar
Chris Allegretta committed
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
/*
 * Routine to see if a text string is matched by a wildcard pattern.
 * Returns TRUE if the text is matched, or FALSE if it is not matched
 * or if the pattern is invalid.
 *  *		matches zero or more characters
 *  ?		matches a single character
 *  [abc]	matches 'a', 'b' or 'c'
 *  \c		quotes character c
 * Adapted from code written by Ingo Wilken, and
 * then taken from sash, Copyright (c) 1999 by David I. Bell
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 * Permission to distribute this code under the GPL has been granted.
 */
int check_wildcard_match(const char *text, const char *pattern)
{
Chris Allegretta's avatar
Chris Allegretta committed
557
558
    const char *retrypat;
    const char *retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
559
560
561
562
    int ch;
    int found;
    int len;

Chris Allegretta's avatar
Chris Allegretta committed
563
564
    retrypat = NULL;
    retrytext = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
565

566
    while (*text != '\0' || *pattern != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
567
568
569
570
	ch = *pattern++;

	switch (ch) {
	case '*':
Chris Allegretta's avatar
Chris Allegretta committed
571
572
	    retrypat = pattern;
	    retrytext = text;
Chris Allegretta's avatar
Chris Allegretta committed
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
	    break;

	case '[':
	    found = FALSE;

	    while ((ch = *pattern++) != ']') {
		if (ch == '\\')
		    ch = *pattern++;

		if (ch == '\0')
		    return FALSE;

		if (*text == ch)
		    found = TRUE;
	    }
	    len = strlen(text);
589
	    if (!found && len != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
590
591
		return FALSE;
	    }
592
	    if (found) {
Chris Allegretta's avatar
Chris Allegretta committed
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
		if (strlen(pattern) == 0 && len == 1) {
		    return TRUE;
		}
		if (len != 0) {
		    text++;
		    continue;
		}
	    }

	    /* fall into next case */

	case '?':
	    if (*text++ == '\0')
		return FALSE;

	    break;

	case '\\':
	    ch = *pattern++;

	    if (ch == '\0')
		return FALSE;

	    /* fall into next case */

	default:
	    if (*text == ch) {
620
		if (*text != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
621
622
623
624
		    text++;
		break;
	    }

625
	    if (*text != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
626
627
		pattern = retrypat;
		text = ++retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
628
629
630
631
632
633
		break;
	    }

	    return FALSE;
	}

634
	if (pattern == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
635
636
637
638
639
	    return FALSE;
    }

    return TRUE;
}
640
#endif