utils.c 10.9 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
34
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)
#endif

Chris Allegretta's avatar
Chris Allegretta committed
45
46
int is_cntrl_char(int c)
{
47
48
    return (-128 <= c && c < -96) || (0 <= c && c < 32) ||
		(127 <= c && c < 160);
Chris Allegretta's avatar
Chris Allegretta committed
49
50
}

51
52
53
54
55
int num_of_digits(int n)
{
    int i = 1;

    if (n < 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
56
	n = -n;
57
58
59
60
61
62
63
64
65

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

    return i;
}

Chris Allegretta's avatar
Chris Allegretta committed
66
67
/* Fix the memory allocation for a string. */
void align(char **strp)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
68
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
69
70
71
    assert(strp != NULL);
    if (*strp != NULL)
	*strp = charealloc(*strp, strlen(*strp) + 1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
72
73
}

Chris Allegretta's avatar
Chris Allegretta committed
74
75
/* 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
76
{
Chris Allegretta's avatar
Chris Allegretta committed
77
    assert(data != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
78
    *data = charealloc(*data, index + 1);
Chris Allegretta's avatar
Chris Allegretta committed
79
    (*data)[index] = '\0';
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
80
81
}

Chris Allegretta's avatar
Chris Allegretta committed
82
83
84
/* 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
85
{
Chris Allegretta's avatar
Chris Allegretta committed
86
    assert(str != NULL);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
87
    for (; true_len > 0; true_len--, str++)
Chris Allegretta's avatar
Chris Allegretta committed
88
89
90
	if (*str == '\0')
	    *str = '\n';
}
Chris Allegretta's avatar
Chris Allegretta committed
91

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

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#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

133
134
/* None of this is needed if we're using NANO_SMALL! */
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
135
const char *revstrstr(const char *haystack, const char *needle,
136
			const char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
137
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
138
    for (; rev_start >= haystack; rev_start--) {
Chris Allegretta's avatar
Chris Allegretta committed
139
	const char *r, *q;
Chris Allegretta's avatar
Chris Allegretta committed
140

Chris Allegretta's avatar
Chris Allegretta committed
141
	for (r = rev_start, q = needle ; *q == *r && *q != '\0'; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
142
143
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
144
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
145
    }
Chris Allegretta's avatar
Chris Allegretta committed
146
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
147
148
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
149
const char *revstristr(const char *haystack, const char *needle,
150
			const char *rev_start)
Chris Allegretta's avatar
Chris Allegretta committed
151
{
Chris Allegretta's avatar
Chris Allegretta committed
152
153
    for (; rev_start >= haystack; rev_start--) {
	const char *r = rev_start, *q = needle;
Chris Allegretta's avatar
Chris Allegretta committed
154

Chris Allegretta's avatar
Chris Allegretta committed
155
	for (; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++)
Chris Allegretta's avatar
Chris Allegretta committed
156
157
	    ;
	if (*q == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
158
	    return rev_start;
Chris Allegretta's avatar
Chris Allegretta committed
159
    }
Chris Allegretta's avatar
Chris Allegretta committed
160
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
161
}
Chris Allegretta's avatar
Chris Allegretta committed
162
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
163

164
/* This is now mutt's version (called mutt_stristr) because it doesn't
165
 * use memory allocation to do a simple search (yuck). */
Chris Allegretta's avatar
Chris Allegretta committed
166
const char *stristr(const char *haystack, const char *needle)
Chris Allegretta's avatar
Chris Allegretta committed
167
{
168
    const char *p, *q;
Chris Allegretta's avatar
Chris Allegretta committed
169

170
    if (haystack == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
171
	return NULL;
172
173
    if (needle == NULL)
	return haystack;
174
    
175
176
    while (*(p = haystack) != '\0') {
	for (q = needle; *p != 0 && *q != 0 && tolower(*p) == tolower(*q); p++, q++)
177
	    ;
178
	if (*q == 0)
Chris Allegretta's avatar
Chris Allegretta committed
179
	    return haystack;
Chris Allegretta's avatar
Chris Allegretta committed
180
	haystack++;
Chris Allegretta's avatar
Chris Allegretta committed
181
    }
182
    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
183
184
}

185
186
187
188
189
190
191
/* If we are searching backwards, we will find the last match
 * that starts no later than rev_start.  If we are doing a regexp search,
 * then line_pos should be 0 if haystack starts at the beginning of a
 * line, and positive otherwise.  In the regexp case, 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
192
const char *strstrwrapper(const char *haystack, const char *needle,
193
			const char *rev_start, int line_pos)
Chris Allegretta's avatar
Chris Allegretta committed
194
{
195
#ifdef HAVE_REGEX_H
196
    if (ISSET(USE_REGEXP)) {
197
#ifndef NANO_SMALL
198
199
	if (ISSET(REVERSE_SEARCH)) {
		/* When doing a backwards search, haystack is a whole line. */
200
	    if (regexec(&search_regexp, haystack, 1, regmatches, 0) == 0 &&
201
202
203
204
		    haystack + regmatches[0].rm_so <= rev_start) {
		const char *retval = haystack + regmatches[0].rm_so;

		/* Search forward until there is no more match. */
205
206
		while (regexec(&search_regexp, retval + 1, 1, regmatches,
			    REG_NOTBOL) == 0 &&
207
208
209
210
211
212
213
			retval + 1 + regmatches[0].rm_so <= rev_start)
		    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
214
	    }
215
216
	} else
#endif /* !NANO_SMALL */
217
218
	if (regexec(&search_regexp, haystack, 10, regmatches,
			line_pos > 0 ? REG_NOTBOL : 0) == 0) {
219
220
221
222
	    const char *retval = haystack + regmatches[0].rm_so;

	    regexec(&search_regexp, retval, 10, regmatches, 0);
	    return retval;
223
	}
224
	return NULL;
225
    }
226
#endif /* HAVE_REGEX_H */
227
#ifndef NANO_SMALL
228
    if (ISSET(CASE_SENSITIVE)) {
229
	if (ISSET(REVERSE_SEARCH))
Chris Allegretta's avatar
Chris Allegretta committed
230
	    return revstrstr(haystack, needle, rev_start);
231
	else
232
233
234
	    return strstr(haystack, needle);
    } else if (ISSET(REVERSE_SEARCH))
	return revstristr(haystack, needle, rev_start);
235
#endif
236
    return stristr(haystack, needle);
Chris Allegretta's avatar
Chris Allegretta committed
237
}
Chris Allegretta's avatar
Chris Allegretta committed
238

239
240
/* 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
241
 * screen.  Note that nperror causes the window to flicker once. */
242
243
void nperror(const char *s)
{
244
    /* leave ncurses mode, go to the terminal */
245
246
247
248
249
250
    if (endwin() != ERR) {
	perror(s);		/* print the error */
	total_refresh();	/* return to ncurses and repaint */
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
251
252
/* Thanks BG, many ppl have been asking for this... */
void *nmalloc(size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
253
{
Chris Allegretta's avatar
Chris Allegretta committed
254
    void *r = malloc(howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
255

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

Chris Allegretta's avatar
Chris Allegretta committed
259
    return r;
260
261
}

Chris Allegretta's avatar
Chris Allegretta committed
262
void *nrealloc(void *ptr, size_t howmuch)
Chris Allegretta's avatar
Chris Allegretta committed
263
{
Chris Allegretta's avatar
Chris Allegretta committed
264
    void *r = realloc(ptr, howmuch);
Chris Allegretta's avatar
Chris Allegretta committed
265

Chris Allegretta's avatar
Chris Allegretta committed
266
267
    if (r == NULL && howmuch != 0)
	die(_("nano is out of memory!"));
Chris Allegretta's avatar
Chris Allegretta committed
268
269
270

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

Chris Allegretta's avatar
Chris Allegretta committed
272
273
274
/* Copy one malloc()ed string to another pointer.  Should be used as:
 * dest = mallocstrcpy(dest, src); */
char *mallocstrcpy(char *dest, const char *src)
275
{
276
    if (src == dest)
Chris Allegretta's avatar
Chris Allegretta committed
277
	return dest;
278

279
    if (dest != NULL)
280
281
	free(dest);

282
    if (src == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
283
	return NULL;
284

285
    dest = charalloc(strlen(src) + 1);
286
287
288
289
290
    strcpy(dest, src);

    return dest;
}

Chris Allegretta's avatar
Chris Allegretta committed
291
/* Append a new magic-line to filebot. */
292
293
void new_magicline(void)
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
294
    filebot->next = (filestruct *)nmalloc(sizeof(filestruct));
295
    filebot->next->data = charalloc(1);
Robert Siemborski's avatar
Robert Siemborski committed
296
297
298
299
300
301
    filebot->next->data[0] = '\0';
    filebot->next->prev = filebot;
    filebot->next->next = NULL;
    filebot->next->lineno = filebot->lineno + 1;
    filebot = filebot->next;
    totlines++;
302
    totsize++;
Robert Siemborski's avatar
Robert Siemborski committed
303
}
Chris Allegretta's avatar
Chris Allegretta committed
304

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#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

327
#ifndef DISABLE_TABCOMP
Chris Allegretta's avatar
Chris Allegretta committed
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/*
 * 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
344
345
    const char *retrypat;
    const char *retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
346
347
348
349
    int ch;
    int found;
    int len;

Chris Allegretta's avatar
Chris Allegretta committed
350
351
    retrypat = NULL;
    retrytext = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
352

353
    while (*text != '\0' || *pattern != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
354
355
356
357
	ch = *pattern++;

	switch (ch) {
	case '*':
Chris Allegretta's avatar
Chris Allegretta committed
358
359
	    retrypat = pattern;
	    retrytext = text;
Chris Allegretta's avatar
Chris Allegretta committed
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
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
	    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) {
407
		if (*text != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
408
409
410
411
		    text++;
		break;
	    }

412
	    if (*text != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
413
414
		pattern = retrypat;
		text = ++retrytext;
Chris Allegretta's avatar
Chris Allegretta committed
415
416
417
418
419
420
		break;
	    }

	    return FALSE;
	}

421
	if (pattern == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
422
423
424
425
426
	    return FALSE;
    }

    return TRUE;
}
427
#endif