utils.c 11.5 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
#include "config.h"

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

33
#ifdef HAVE_REGEX_H
34
35
36
37
38
39
40
41
42
43
#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)
44
45
46
47
48
49
50
51
52
53
#endif /* BROKEN_REGEXEC */

/* Assume that string will be found by regexec() if the REG_NOTBOL and
 * REG_NOTEOL glags are not set. */
int regexp_bol_or_eol(const regex_t *preg, const char *string)
{
    return (regexec(preg, string, 0, NULL, REG_NOTBOL | REG_NOTEOL) ==
	REG_NOMATCH);
}
#endif /* HAVE_REGEX_H */
54

Chris Allegretta's avatar
Chris Allegretta committed
55
56
int is_cntrl_char(int c)
{
57
58
    return (-128 <= c && c < -96) || (0 <= c && c < 32) ||
		(127 <= c && c < 160);
Chris Allegretta's avatar
Chris Allegretta committed
59
60
}

61
62
63
64
65
int num_of_digits(int n)
{
    int i = 1;

    if (n < 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
66
	n = -n;
67
68
69
70
71
72
73
74
75

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

    return i;
}

Chris Allegretta's avatar
Chris Allegretta committed
76
77
/* Fix the memory allocation for a string. */
void align(char **strp)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
78
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
79
80
81
    assert(strp != NULL);
    if (*strp != NULL)
	*strp = charealloc(*strp, strlen(*strp) + 1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
82
83
}

Chris Allegretta's avatar
Chris Allegretta committed
84
85
/* 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
86
{
Chris Allegretta's avatar
Chris Allegretta committed
87
    assert(data != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
88
    *data = charealloc(*data, index + 1);
Chris Allegretta's avatar
Chris Allegretta committed
89
    (*data)[index] = '\0';
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
90
91
}

Chris Allegretta's avatar
Chris Allegretta committed
92
93
94
/* 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
95
{
Chris Allegretta's avatar
Chris Allegretta committed
96
    assert(str != NULL);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
97
    for (; true_len > 0; true_len--, str++)
Chris Allegretta's avatar
Chris Allegretta committed
98
99
100
	if (*str == '\0')
	    *str = '\n';
}
Chris Allegretta's avatar
Chris Allegretta committed
101

Chris Allegretta's avatar
Chris Allegretta committed
102
103
104
105
106
/* 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);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
107
    for (; *str != '\0'; str++)
Chris Allegretta's avatar
Chris Allegretta committed
108
109
	if (*str == '\n')
	    *str = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
110
111
}

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#ifndef HAVE_STRCASECMP
/* This function is equivalent to strcasecmp(). */
int nstricmp(const char *s1, const char *s2)
{
    assert(s1 != NULL && s2 != NULL);
    for (; *s1 != '\0' && *s2 != '\0'; s1++, s2++) {
	if (tolower(*s1) != tolower(*s2))
	    break;
    }
    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);
    for (; n > 0 && *s1 != '\0' && *s2 != '\0'; n--, s1++, s2++) {
	if (tolower(*s1) != tolower(*s2))
	    break;
    }
    if (n > 0)
	return (tolower(*s1) - tolower(*s2));
    else if (n == 0)
	return 0;
    else if (n < 0)
	return -1;
}
#endif

143
144
/* None of this is needed if we're using NANO_SMALL! */
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
145
const char *revstrstr(const char *haystack, const char *needle,
146
			const char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
147
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
148
    for (; rev_start >= haystack; rev_start--) {
Chris Allegretta's avatar
Chris Allegretta committed
149
	const char *r, *q;
Chris Allegretta's avatar
Chris Allegretta committed
150

Chris Allegretta's avatar
Chris Allegretta committed
151
	for (r = rev_start, q = needle ; *q == *r && *q != '\0'; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
152
153
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
154
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
155
    }
Chris Allegretta's avatar
Chris Allegretta committed
156
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
157
158
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
159
const char *revstristr(const char *haystack, const char *needle,
160
			const char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
161
{
Chris Allegretta's avatar
Chris Allegretta committed
162
163
    for (; rev_start >= haystack; rev_start--) {
	const char *r = rev_start, *q = needle;
Chris Allegretta's avatar
Chris Allegretta committed
164

Chris Allegretta's avatar
Chris Allegretta committed
165
	for (; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
166
167
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
168
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
169
    }
Chris Allegretta's avatar
Chris Allegretta committed
170
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
171
}
Chris Allegretta's avatar
Chris Allegretta committed
172
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
173

174
/* This is now mutt's version (called mutt_stristr) because it doesn't
175
 * use memory allocation to do a simple search (yuck). */
Chris Allegretta's avatar
Chris Allegretta committed
176
const char *stristr(const char *haystack, const char *needle)
Chris Allegretta's avatar
Chris Allegretta committed
177
{
178
    const char *p, *q;
Chris Allegretta's avatar
Chris Allegretta committed
179

180
    if (haystack == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
181
	return NULL;
182
183
    if (needle == NULL)
	return haystack;
184
    
185
186
    while (*(p = haystack) != '\0') {
	for (q = needle; *p != 0 && *q != 0 && tolower(*p) == tolower(*q); p++, q++)
187
	    ;
188
	if (*q == 0)
Chris Allegretta's avatar
Chris Allegretta committed
189
	    return haystack;
Chris Allegretta's avatar
Chris Allegretta committed
190
	haystack++;
Chris Allegretta's avatar
Chris Allegretta committed
191
    }
192
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
193
194
}

195
196
197
198
199
200
/* 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
201
const char *strstrwrapper(const char *haystack, const char *needle,
202
	const char *start)
Chris Allegretta's avatar
Chris Allegretta committed
203
{
204
205
206
207
208
    /* start can be 1 character before the start or after the end of the
     * line.  In either case, we just say there is no match found. */
    if ((start > haystack && *(start - 1) == '\0') || start < haystack)
	return NULL;
    assert(haystack != NULL && needle != NULL && start != NULL);
209
#ifdef HAVE_REGEX_H
210
    if (ISSET(USE_REGEXP)) {
211
#ifndef NANO_SMALL
212
	if (ISSET(REVERSE_SEARCH)) {
213
214
	    if (regexec(&search_regexp, haystack, 1, regmatches, 0) == 0
		&& haystack + regmatches[0].rm_so <= start) {
215
216
217
		const char *retval = haystack + regmatches[0].rm_so;

		/* Search forward until there is no more match. */
218
		while (regexec(&search_regexp, retval + 1, 1, regmatches,
219
220
			REG_NOTBOL) == 0 && retval + 1 +
			regmatches[0].rm_so <= start)
221
222
223
224
225
226
		    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
227
	    }
228
229
	} else
#endif /* !NANO_SMALL */
230
231
232
	if (regexec(&search_regexp, start, 10, regmatches,
		start > haystack ? REG_NOTBOL : 0) == 0) {
	    const char *retval = start + regmatches[0].rm_so;
233
234
235

	    regexec(&search_regexp, retval, 10, regmatches, 0);
	    return retval;
236
	}
237
	return NULL;
238
    }
239
#endif /* HAVE_REGEX_H */
240
#if !defined(DISABLE_SPELLER) || !defined(NANO_SMALL)
241
    if (ISSET(CASE_SENSITIVE)) {
242
#ifndef NANO_SMALL
243
	if (ISSET(REVERSE_SEARCH))
244
	    return revstrstr(haystack, needle, start);
245
	else
246
#endif
247
	    return strstr(haystack, needle);
248
249
250
251
252
    }
#endif /* !DISABLE_SPELLER || !NANO_SMALL */
#ifndef NANO_SMALL
    else if (ISSET(REVERSE_SEARCH))
	return revstristr(haystack, needle, start);
253
#endif
254
    return stristr(start, needle);
Chris Allegretta's avatar
Chris Allegretta committed
255
}
Chris Allegretta's avatar
Chris Allegretta committed
256

257
258
/* This is a wrapper for the perror function.  The wrapper takes care of 
 * ncurses, calls perror (which writes to STDERR), then refreshes the 
Chris Allegretta's avatar
Chris Allegretta committed
259
 * screen.  Note that nperror causes the window to flicker once. */
260
261
void nperror(const char *s)
{
262
    /* leave ncurses mode, go to the terminal */
263
264
265
266
267
268
    if (endwin() != ERR) {
	perror(s);		/* print the error */
	total_refresh();	/* return to ncurses and repaint */
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
269
270
/* Thanks BG, many ppl have been asking for this... */
void *nmalloc(size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
271
{
Chris Allegretta's avatar
Chris Allegretta committed
272
    void *r = malloc(howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
273

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

Chris Allegretta's avatar
Chris Allegretta committed
277
    return r;
278
279
}

Chris Allegretta's avatar
Chris Allegretta committed
280
void *nrealloc(void *ptr, size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
281
{
Chris Allegretta's avatar
Chris Allegretta committed
282
    void *r = realloc(ptr, howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
283

Chris Allegretta's avatar
Chris Allegretta committed
284
285
    if (r == NULL && howmuch != 0)
	die(_("nano is out of memory!"));
Chris Allegretta's avatar
Chris Allegretta committed
286
287
288

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

Chris Allegretta's avatar
Chris Allegretta committed
290
291
292
/* Copy one malloc()ed string to another pointer.  Should be used as:
 * dest = mallocstrcpy(dest, src); */
char *mallocstrcpy(char *dest, const char *src)
293
{
294
    if (src == dest)
Chris Allegretta's avatar
Chris Allegretta committed
295
	return dest;
296

297
    if (dest != NULL)
298
299
	free(dest);

300
    if (src == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
301
	return NULL;
302

303
    dest = charalloc(strlen(src) + 1);
304
305
306
307
308
    strcpy(dest, src);

    return dest;
}

Chris Allegretta's avatar
Chris Allegretta committed
309
/* Append a new magic-line to filebot. */
310
311
void new_magicline(void)
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
312
    filebot->next = (filestruct *)nmalloc(sizeof(filestruct));
313
    filebot->next->data = charalloc(1);
Robert Siemborski's avatar
Robert Siemborski committed
314
315
316
317
318
319
    filebot->next->data[0] = '\0';
    filebot->next->prev = filebot;
    filebot->next->next = NULL;
    filebot->next->lineno = filebot->lineno + 1;
    filebot = filebot->next;
    totlines++;
320
    totsize++;
Robert Siemborski's avatar
Robert Siemborski committed
321
}
Chris Allegretta's avatar
Chris Allegretta committed
322

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#ifndef NANO_SMALL
/* 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. */
void mark_order(const filestruct **top, size_t *top_x,
		const filestruct **bot, size_t *bot_x)
{
    assert(top != NULL && top_x != NULL && bot != NULL && bot_x != NULL);
    if ((current->lineno == mark_beginbuf->lineno && current_x > mark_beginx)
	|| current->lineno > mark_beginbuf->lineno) {
	*top = mark_beginbuf;
	*top_x = mark_beginx;
	*bot = current;
	*bot_x = current_x;
    } else {
	*bot = mark_beginbuf;
	*bot_x = mark_beginx;
	*top = current;
	*top_x = current_x;
    }
}
#endif

345
#ifndef DISABLE_TABCOMP
Chris Allegretta's avatar
Chris Allegretta committed
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/*
 * 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
362
363
    const char *retrypat;
    const char *retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
364
365
366
367
    int ch;
    int found;
    int len;

Chris Allegretta's avatar
Chris Allegretta committed
368
369
    retrypat = NULL;
    retrytext = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
370

371
    while (*text != '\0' || *pattern != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
372
373
374
375
	ch = *pattern++;

	switch (ch) {
	case '*':
Chris Allegretta's avatar
Chris Allegretta committed
376
377
	    retrypat = pattern;
	    retrytext = text;
Chris Allegretta's avatar
Chris Allegretta committed
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
	    break;

	case '[':
	    found = FALSE;

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

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

		if (*text == ch)
		    found = TRUE;
	    }
	    len = strlen(text);
	    if (found == FALSE && len != 0) {
		return FALSE;
	    }
	    if (found == TRUE) {
		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) {
425
		if (*text != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
426
427
428
429
		    text++;
		break;
	    }

430
	    if (*text != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
431
432
		pattern = retrypat;
		text = ++retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
433
434
435
436
437
438
		break;
	    }

	    return FALSE;
	}

439
	if (pattern == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
440
441
442
443
444
	    return FALSE;
    }

    return TRUE;
}
445
#endif