utils.c 16.2 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-2005 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
#ifdef BROKEN_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;
}
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
45
#endif
46
47
48

int regexp_bol_or_eol(const regex_t *preg, const char *string)
{
49
50
    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
51
	REG_NOMATCH);
52
53
}
#endif /* HAVE_REGEX_H */
54

55
56
57
58
59
int num_of_digits(int n)
{
    int i = 1;

    if (n < 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
60
	n = -n;
61
62
63
64
65
66
67
68
69

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

    return i;
}

70
bool is_byte(int c)
71
{
72
    return ((unsigned int)c == (unsigned char)c);
73
74
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
75
76
77
/* 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. */
78
bool parse_num(const char *str, ssize_t *val)
79
80
81
82
83
{
    char *first_error;
    ssize_t j;

    assert(str != NULL);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
84

85
    j = (ssize_t)strtol(str, &first_error, 10);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
86

87
    if (errno == ERANGE || *str == '\0' || *first_error != '\0')
88
	return FALSE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
89

90
91
    if (val != NULL)
	*val = j;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
92

93
    return TRUE;
94
95
}

96
/* Return the index in buf of the beginning of the character before the
97
 * one at pos. */
98
size_t move_left(const char *buf, size_t pos)
99
100
101
{
    size_t pos_prev = pos;

102
    assert(str != NULL && pos <= strlen(buf));
103
104
105
106

    /* There is no library function to move backward one multibyte
     * character.  Here is the naive, O(pos) way to do it. */
    while (TRUE) {
107
	int buf_mb_len = parse_mbchar(buf + pos - pos_prev, NULL
108
109
110
#ifdef NANO_WIDE
		, NULL
#endif
111
		, NULL);
112

113
	if (pos_prev <= buf_mb_len)
114
115
	    break;

116
	pos_prev -= buf_mb_len;
117
118
119
120
121
    }

    return pos - pos_prev;
}

122
/* Return the index in buf of the beginning of the character after the
123
 * one at pos. */
124
size_t move_right(const char *buf, size_t pos)
125
{
126
    return pos + parse_mbchar(buf + pos, NULL
127
128
129
#ifdef NANO_WIDE
	, NULL
#endif
130
	, NULL);
131
132
}

Chris Allegretta's avatar
Chris Allegretta committed
133
134
/* Fix the memory allocation for a string. */
void align(char **strp)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
135
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
136
137
138
    assert(strp != NULL);
    if (*strp != NULL)
	*strp = charealloc(*strp, strlen(*strp) + 1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
139
140
}

Chris Allegretta's avatar
Chris Allegretta committed
141
142
/* 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
143
{
Chris Allegretta's avatar
Chris Allegretta committed
144
    assert(data != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
145
    *data = charealloc(*data, index + 1);
Chris Allegretta's avatar
Chris Allegretta committed
146
    (*data)[index] = '\0';
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
147
148
}

Chris Allegretta's avatar
Chris Allegretta committed
149
150
151
/* 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
152
{
Chris Allegretta's avatar
Chris Allegretta committed
153
    assert(str != NULL);
154
    for (; true_len > 0; true_len--, str++) {
Chris Allegretta's avatar
Chris Allegretta committed
155
156
	if (*str == '\0')
	    *str = '\n';
157
    }
Chris Allegretta's avatar
Chris Allegretta committed
158
}
Chris Allegretta's avatar
Chris Allegretta committed
159

Chris Allegretta's avatar
Chris Allegretta committed
160
161
162
163
164
/* 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);
165
    for (; *str != '\0'; str++) {
Chris Allegretta's avatar
Chris Allegretta committed
166
167
	if (*str == '\n')
	    *str = '\0';
168
    }
Chris Allegretta's avatar
Chris Allegretta committed
169
170
}

171
172
173
174
175
#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
176

177
178
179
180
    for (; *s1 != '\0' && *s2 != '\0'; s1++, s2++) {
	if (tolower(*s1) != tolower(*s2))
	    break;
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
181

182
183
184
185
186
187
188
189
190
    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
191

192
193
194
195
    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
196

197
198
    if (n > 0)
	return (tolower(*s1) - tolower(*s2));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
199
    else
200
	return 0;
201
202
203
}
#endif

204
#ifndef HAVE_STRCASESTR
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/* 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;
}
224
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
225

226
227
/* None of this is needed if we're using NANO_SMALL! */
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
228
229
const char *revstrstr(const char *haystack, const char *needle, const
	char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
230
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
231
    for (; rev_start >= haystack; rev_start--) {
Chris Allegretta's avatar
Chris Allegretta committed
232
	const char *r, *q;
Chris Allegretta's avatar
Chris Allegretta committed
233

Chris Allegretta's avatar
Chris Allegretta committed
234
	for (r = rev_start, q = needle ; *q == *r && *q != '\0'; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
235
236
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
237
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
238
    }
Chris Allegretta's avatar
Chris Allegretta committed
239
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
240
241
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
242
243
const char *revstristr(const char *haystack, const char *needle, const
	char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
244
{
Chris Allegretta's avatar
Chris Allegretta committed
245
246
    for (; rev_start >= haystack; rev_start--) {
	const char *r = rev_start, *q = needle;
Chris Allegretta's avatar
Chris Allegretta committed
247

Chris Allegretta's avatar
Chris Allegretta committed
248
	for (; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
249
250
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
251
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
252
    }
Chris Allegretta's avatar
Chris Allegretta committed
253
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
254
}
Chris Allegretta's avatar
Chris Allegretta committed
255
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
256

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
#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
272
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#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) {
296
297
	*lineptr = charalloc(128);
	*n = 128;
298
299
300
301
302
    }

    while ((c = getc(stream)) != EOF) {
	/* Check if more memory is needed. */
	if (indx >= *n) {
303
304
	    *lineptr = charealloc(*lineptr, *n + 128);
	    *n += 128;
305
306
307
308
309
310
311
312
313
314
315
316
	}

	/* 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) {
317
318
	*lineptr = charealloc(*lineptr, *n + 128);
	*n += 128;
319
320
321
    }

    /* Null terminate the buffer. */
322
    null_at(lineptr, indx++);
323
    *n = indx;
324
325
326
327
328
329

    /* 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
330
#endif /* !NANO_SMALL && ENABLE_NANORC */
331

332
333
334
335
336
337
/* 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
338
const char *strstrwrapper(const char *haystack, const char *needle,
339
	const char *start)
Chris Allegretta's avatar
Chris Allegretta committed
340
{
341
    /* start can be 1 character before the start or after the end of the
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
342
     * line.  In either case, we just say no match was found. */
343
344
345
    if ((start > haystack && *(start - 1) == '\0') || start < haystack)
	return NULL;
    assert(haystack != NULL && needle != NULL && start != NULL);
346
#ifdef HAVE_REGEX_H
347
    if (ISSET(USE_REGEXP)) {
348
#ifndef NANO_SMALL
349
	if (ISSET(REVERSE_SEARCH)) {
350
351
	    if (regexec(&search_regexp, haystack, 1, regmatches, 0) == 0
		&& haystack + regmatches[0].rm_so <= start) {
352
353
		const char *retval = haystack + regmatches[0].rm_so;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
354
		/* Search forward until there are no more matches. */
355
		while (regexec(&search_regexp, retval + 1, 1, regmatches,
356
357
			REG_NOTBOL) == 0 && retval + 1 +
			regmatches[0].rm_so <= start)
358
359
360
361
362
363
		    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
364
	    }
365
366
	} else
#endif /* !NANO_SMALL */
367
368
369
	if (regexec(&search_regexp, start, 10, regmatches,
		start > haystack ? REG_NOTBOL : 0) == 0) {
	    const char *retval = start + regmatches[0].rm_so;
370
371
372

	    regexec(&search_regexp, retval, 10, regmatches, 0);
	    return retval;
373
	}
374
	return NULL;
375
    }
376
#endif /* HAVE_REGEX_H */
377
#if !defined(DISABLE_SPELLER) || !defined(NANO_SMALL)
378
    if (ISSET(CASE_SENSITIVE)) {
379
#ifndef NANO_SMALL
380
	if (ISSET(REVERSE_SEARCH))
381
	    return revstrstr(haystack, needle, start);
382
	else
383
#endif
384
	    return strstr(start, needle);
385
386
387
388
389
    }
#endif /* !DISABLE_SPELLER || !NANO_SMALL */
#ifndef NANO_SMALL
    else if (ISSET(REVERSE_SEARCH))
	return revstristr(haystack, needle, start);
390
#endif
391
    return strcasestr(start, needle);
Chris Allegretta's avatar
Chris Allegretta committed
392
}
Chris Allegretta's avatar
Chris Allegretta committed
393

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
394
395
396
/* 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. */
397
398
void nperror(const char *s)
{
399
    /* leave ncurses mode, go to the terminal */
400
401
402
403
404
405
    if (endwin() != ERR) {
	perror(s);		/* print the error */
	total_refresh();	/* return to ncurses and repaint */
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
406
407
/* Thanks BG, many ppl have been asking for this... */
void *nmalloc(size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
408
{
Chris Allegretta's avatar
Chris Allegretta committed
409
    void *r = malloc(howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
410

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

Chris Allegretta's avatar
Chris Allegretta committed
414
    return r;
415
416
}

Chris Allegretta's avatar
Chris Allegretta committed
417
void *nrealloc(void *ptr, size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
418
{
Chris Allegretta's avatar
Chris Allegretta committed
419
    void *r = realloc(ptr, howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
420

Chris Allegretta's avatar
Chris Allegretta committed
421
422
    if (r == NULL && howmuch != 0)
	die(_("nano is out of memory!"));
Chris Allegretta's avatar
Chris Allegretta committed
423
424
425

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

427
428
429
430
/* 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)
431
{
432
433
    if (src == NULL)
	src = "";
434

435
    if (src != dest)
436
437
	free(dest);

438
439
    dest = charalloc(n);
    strncpy(dest, src, n);
440
441
442
443

    return dest;
}

444
445
446
447
448
449
450
/* 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);
}

451
452
453
454
455
456
457
458
459
460
/* 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. */
461
462
void new_magicline(void)
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
463
    filebot->next = (filestruct *)nmalloc(sizeof(filestruct));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
464
    filebot->next->data = mallocstrcpy(NULL, "");
Robert Siemborski's avatar
Robert Siemborski committed
465
466
467
468
469
    filebot->next->prev = filebot;
    filebot->next->next = NULL;
    filebot->next->lineno = filebot->lineno + 1;
    filebot = filebot->next;
    totlines++;
470
    totsize++;
Robert Siemborski's avatar
Robert Siemborski committed
471
}
Chris Allegretta's avatar
Chris Allegretta committed
472

473
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
474
475
/* Remove the magicline from filebot, if there is one and it isn't the
 * only line in the file. */
476
477
void remove_magicline(void)
{
478
    if (filebot->data[0] == '\0' && filebot->prev != NULL) {
479
480
481
482
483
484
485
486
	filebot = filebot->prev;
	free_filestruct(filebot->next);
	filebot->next = NULL;
	totlines--;
	totsize--;
    }
}

487
488
489
490
491
492
493
494
495
496
497
/* 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
498
	mark_beginx) || current->lineno > mark_beginbuf->lineno) {
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
	*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

516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
/* 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. */
531
532
	if (lines != NULL)
	    (*lines)++;
533
534

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

538
539
540
541
	    /* Count the newline if we have one. */
	    if (f->next != NULL)
		(*size)++;
	}
542
543
544
545
546
    }

    /* Go through the line at end, if we can. */
    if (f != NULL) {
	/* Count this line. */
547
548
	if (lines != NULL)
	    (*lines)++;
549
550

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

554
555
556
557
	    /* Count the newline if we have one. */
	    if (f->next != NULL)
		(*size)++;
	}
558
559
560
    }
}

561
#ifndef DISABLE_TABCOMP
Chris Allegretta's avatar
Chris Allegretta committed
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
/*
 * 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
578
579
    const char *retrypat;
    const char *retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
580
581
582
583
    int ch;
    int found;
    int len;

Chris Allegretta's avatar
Chris Allegretta committed
584
585
    retrypat = NULL;
    retrytext = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
586

587
    while (*text != '\0' || *pattern != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
588
589
590
591
	ch = *pattern++;

	switch (ch) {
	case '*':
Chris Allegretta's avatar
Chris Allegretta committed
592
593
	    retrypat = pattern;
	    retrytext = text;
Chris Allegretta's avatar
Chris Allegretta committed
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
	    break;

	case '[':
	    found = FALSE;

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

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

		if (*text == ch)
		    found = TRUE;
	    }
	    len = strlen(text);
610
	    if (!found && len != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
611
612
		return FALSE;
	    }
613
	    if (found) {
Chris Allegretta's avatar
Chris Allegretta committed
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
		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) {
641
		if (*text != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
642
643
644
645
		    text++;
		break;
	    }

646
	    if (*text != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
647
648
		pattern = retrypat;
		text = ++retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
649
650
651
652
653
654
		break;
	    }

	    return FALSE;
	}

655
	if (pattern == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
656
657
658
659
660
	    return FALSE;
    }

    return TRUE;
}
661
#endif