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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return dest;
}

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

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

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

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

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

	switch (ch) {
	case '*':
Chris Allegretta's avatar
Chris Allegretta committed
422
423
	    retrypat = pattern;
	    retrytext = text;
Chris Allegretta's avatar
Chris Allegretta committed
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
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
	    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) {
471
		if (*text != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
472
473
474
475
		    text++;
		break;
	    }

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

	    return FALSE;
	}

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

    return TRUE;
}
491
#endif