utils.c 12.4 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"

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

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

56
57
58
59
60
61
62
63
64
65
#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
66
67
int is_cntrl_char(int c)
{
68
    return (-128 <= c && c < -96) || (0 <= c && c < 32) ||
69
	(127 <= c && c < 160);
Chris Allegretta's avatar
Chris Allegretta committed
70
71
}

72
73
74
75
76
int num_of_digits(int n)
{
    int i = 1;

    if (n < 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
77
	n = -n;
78
79
80
81
82
83
84
85
86

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

    return i;
}

87
/* Read an int from str, and store it in *val (if val is not NULL).  On
88
89
90
 * error, we return FALSE and don't change *val.  Otherwise, we return 
 * TRUE. */
bool parse_num(const char *str, ssize_t *val)
91
92
93
94
95
96
97
{
    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')
98
	return FALSE;
99
100
    if (val != NULL)
	*val = j;
101
    return TRUE;
102
103
}

Chris Allegretta's avatar
Chris Allegretta committed
104
105
/* Fix the memory allocation for a string. */
void align(char **strp)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
106
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
107
108
109
    assert(strp != NULL);
    if (*strp != NULL)
	*strp = charealloc(*strp, strlen(*strp) + 1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
110
111
}

Chris Allegretta's avatar
Chris Allegretta committed
112
113
/* 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
114
{
Chris Allegretta's avatar
Chris Allegretta committed
115
    assert(data != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
116
    *data = charealloc(*data, index + 1);
Chris Allegretta's avatar
Chris Allegretta committed
117
    (*data)[index] = '\0';
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
118
119
}

Chris Allegretta's avatar
Chris Allegretta committed
120
121
122
/* 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
123
{
Chris Allegretta's avatar
Chris Allegretta committed
124
    assert(str != NULL);
125
    for (; true_len > 0; true_len--, str++) {
Chris Allegretta's avatar
Chris Allegretta committed
126
127
	if (*str == '\0')
	    *str = '\n';
128
    }
Chris Allegretta's avatar
Chris Allegretta committed
129
}
Chris Allegretta's avatar
Chris Allegretta committed
130

Chris Allegretta's avatar
Chris Allegretta committed
131
132
133
134
135
/* 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);
136
    for (; *str != '\0'; str++) {
Chris Allegretta's avatar
Chris Allegretta committed
137
138
	if (*str == '\n')
	    *str = '\0';
139
    }
Chris Allegretta's avatar
Chris Allegretta committed
140
141
}

142
143
144
145
146
#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
147

148
149
150
151
    for (; *s1 != '\0' && *s2 != '\0'; s1++, s2++) {
	if (tolower(*s1) != tolower(*s2))
	    break;
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
152

153
154
155
156
157
158
159
160
161
    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
162

163
164
165
166
    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
167

168
169
170
171
    if (n > 0)
	return (tolower(*s1) - tolower(*s2));
    else if (n == 0)
	return 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
172
    else
173
174
175
176
	return -1;
}
#endif

177
#ifndef HAVE_STRCASESTR
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* 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;
}
197
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
198

199
200
/* None of this is needed if we're using NANO_SMALL! */
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
201
202
const char *revstrstr(const char *haystack, const char *needle, const
	char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
203
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
204
    for (; rev_start >= haystack; rev_start--) {
Chris Allegretta's avatar
Chris Allegretta committed
205
	const char *r, *q;
Chris Allegretta's avatar
Chris Allegretta committed
206

Chris Allegretta's avatar
Chris Allegretta committed
207
	for (r = rev_start, q = needle ; *q == *r && *q != '\0'; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
208
209
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
210
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
211
    }
Chris Allegretta's avatar
Chris Allegretta committed
212
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
213
214
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
215
216
const char *revstristr(const char *haystack, const char *needle, const
	char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
217
{
Chris Allegretta's avatar
Chris Allegretta committed
218
219
    for (; rev_start >= haystack; rev_start--) {
	const char *r = rev_start, *q = needle;
Chris Allegretta's avatar
Chris Allegretta committed
220

Chris Allegretta's avatar
Chris Allegretta committed
221
	for (; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
222
223
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
224
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
225
    }
Chris Allegretta's avatar
Chris Allegretta committed
226
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
227
}
Chris Allegretta's avatar
Chris Allegretta committed
228
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#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

245
246
247
248
249
250
/* 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
251
const char *strstrwrapper(const char *haystack, const char *needle,
252
	const char *start)
Chris Allegretta's avatar
Chris Allegretta committed
253
{
254
255
256
257
258
    /* 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);
259
#ifdef HAVE_REGEX_H
260
    if (ISSET(USE_REGEXP)) {
261
#ifndef NANO_SMALL
262
	if (ISSET(REVERSE_SEARCH)) {
263
264
	    if (regexec(&search_regexp, haystack, 1, regmatches, 0) == 0
		&& haystack + regmatches[0].rm_so <= start) {
265
266
267
		const char *retval = haystack + regmatches[0].rm_so;

		/* Search forward until there is no more match. */
268
		while (regexec(&search_regexp, retval + 1, 1, regmatches,
269
270
			REG_NOTBOL) == 0 && retval + 1 +
			regmatches[0].rm_so <= start)
271
272
273
274
275
276
		    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
277
	    }
278
279
	} else
#endif /* !NANO_SMALL */
280
281
282
	if (regexec(&search_regexp, start, 10, regmatches,
		start > haystack ? REG_NOTBOL : 0) == 0) {
	    const char *retval = start + regmatches[0].rm_so;
283
284
285

	    regexec(&search_regexp, retval, 10, regmatches, 0);
	    return retval;
286
	}
287
	return NULL;
288
    }
289
#endif /* HAVE_REGEX_H */
290
#if !defined(DISABLE_SPELLER) || !defined(NANO_SMALL)
291
    if (ISSET(CASE_SENSITIVE)) {
292
#ifndef NANO_SMALL
293
	if (ISSET(REVERSE_SEARCH))
294
	    return revstrstr(haystack, needle, start);
295
	else
296
#endif
297
	    return strstr(start, needle);
298
299
300
301
302
    }
#endif /* !DISABLE_SPELLER || !NANO_SMALL */
#ifndef NANO_SMALL
    else if (ISSET(REVERSE_SEARCH))
	return revstristr(haystack, needle, start);
303
#endif
304
    return strcasestr(start, needle);
Chris Allegretta's avatar
Chris Allegretta committed
305
}
Chris Allegretta's avatar
Chris Allegretta committed
306

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
307
308
309
/* 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. */
310
311
void nperror(const char *s)
{
312
    /* leave ncurses mode, go to the terminal */
313
314
315
316
317
318
    if (endwin() != ERR) {
	perror(s);		/* print the error */
	total_refresh();	/* return to ncurses and repaint */
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
319
320
/* Thanks BG, many ppl have been asking for this... */
void *nmalloc(size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
321
{
Chris Allegretta's avatar
Chris Allegretta committed
322
    void *r = malloc(howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
323

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

Chris Allegretta's avatar
Chris Allegretta committed
327
    return r;
328
329
}

Chris Allegretta's avatar
Chris Allegretta committed
330
void *nrealloc(void *ptr, size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
331
{
Chris Allegretta's avatar
Chris Allegretta committed
332
    void *r = realloc(ptr, howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
333

Chris Allegretta's avatar
Chris Allegretta committed
334
335
    if (r == NULL && howmuch != 0)
	die(_("nano is out of memory!"));
Chris Allegretta's avatar
Chris Allegretta committed
336
337
338

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

Chris Allegretta's avatar
Chris Allegretta committed
340
341
342
/* Copy one malloc()ed string to another pointer.  Should be used as:
 * dest = mallocstrcpy(dest, src); */
char *mallocstrcpy(char *dest, const char *src)
343
{
344
345
    if (src == NULL)
	src = "";
346

347
    if (src != dest)
348
349
	free(dest);

350
    dest = charalloc(strlen(src) + 1);
351
352
353
354
355
    strcpy(dest, src);

    return dest;
}

Chris Allegretta's avatar
Chris Allegretta committed
356
/* Append a new magic-line to filebot. */
357
358
void new_magicline(void)
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
359
    filebot->next = (filestruct *)nmalloc(sizeof(filestruct));
360
    filebot->next->data = charalloc(1);
Robert Siemborski's avatar
Robert Siemborski committed
361
362
363
364
365
366
    filebot->next->data[0] = '\0';
    filebot->next->prev = filebot;
    filebot->next->next = NULL;
    filebot->next->lineno = filebot->lineno + 1;
    filebot = filebot->next;
    totlines++;
367
    totsize++;
Robert Siemborski's avatar
Robert Siemborski committed
368
}
Chris Allegretta's avatar
Chris Allegretta committed
369

370
371
372
#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. */
373
374
void mark_order(const filestruct **top, size_t *top_x, const filestruct
	**bot, size_t *bot_x)
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
{
    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

392
#ifndef DISABLE_TABCOMP
Chris Allegretta's avatar
Chris Allegretta committed
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
/*
 * 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
409
410
    const char *retrypat;
    const char *retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
411
412
413
414
    int ch;
    int found;
    int len;

Chris Allegretta's avatar
Chris Allegretta committed
415
416
    retrypat = NULL;
    retrytext = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
417

418
    while (*text != '\0' || *pattern != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
419
420
421
422
	ch = *pattern++;

	switch (ch) {
	case '*':
Chris Allegretta's avatar
Chris Allegretta committed
423
424
	    retrypat = pattern;
	    retrytext = text;
Chris Allegretta's avatar
Chris Allegretta committed
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
	    break;

	case '[':
	    found = FALSE;

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

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

		if (*text == ch)
		    found = TRUE;
	    }
	    len = strlen(text);
441
	    if (!found && len != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
442
443
		return FALSE;
	    }
444
	    if (found) {
Chris Allegretta's avatar
Chris Allegretta committed
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
		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) {
472
		if (*text != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
473
474
475
476
		    text++;
		break;
	    }

477
	    if (*text != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
478
479
		pattern = retrypat;
		text = ++retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
480
481
482
483
484
485
		break;
	    }

	    return FALSE;
	}

486
	if (pattern == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
487
488
489
490
491
	    return FALSE;
    }

    return TRUE;
}
492
#endif