Imported Upstream version 2.3.1
[platform/upstream/nano.git] / src / utils.c
1 /* $Id: utils.c 4453 2009-12-02 03:36:22Z astyanax $ */
2 /**************************************************************************
3  *   utils.c                                                              *
4  *                                                                        *
5  *   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,  *
6  *   2008, 2009 Free Software Foundation, Inc.                            *
7  *   This program is free software; you can redistribute it and/or modify *
8  *   it under the terms of the GNU General Public License as published by *
9  *   the Free Software Foundation; either version 3, or (at your option)  *
10  *   any later version.                                                   *
11  *                                                                        *
12  *   This program is distributed in the hope that it will be useful, but  *
13  *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
15  *   General Public License for more details.                             *
16  *                                                                        *
17  *   You should have received a copy of the GNU General Public License    *
18  *   along with this program; if not, write to the Free Software          *
19  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
20  *   02110-1301, USA.                                                     *
21  *                                                                        *
22  **************************************************************************/
23
24 #include "proto.h"
25
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <pwd.h>
30 #include <ctype.h>
31 #include <errno.h>
32
33 /* Return the number of decimal digits in n. */
34 int digits(size_t n)
35 {
36     int i;
37
38     if (n == 0)
39         i = 1;
40     else {
41         for (i = 0; n != 0; n /= 10, i++)
42             ;
43     }
44
45     return i;
46 }
47
48 /* Return the user's home directory.  We use $HOME, and if that fails,
49  * we fall back on the home directory of the effective user ID. */
50 void get_homedir(void)
51 {
52     if (homedir == NULL) {
53         const char *homenv = getenv("HOME");
54
55         if (homenv == NULL) {
56             const struct passwd *userage = getpwuid(geteuid());
57
58             if (userage != NULL)
59                 homenv = userage->pw_dir;
60         }
61         homedir = mallocstrcpy(NULL, homenv);
62     }
63 }
64
65 /* Read a ssize_t from str, and store it in *val (if val is not NULL).
66  * On error, we return FALSE and don't change *val.  Otherwise, we
67  * return TRUE. */
68 bool parse_num(const char *str, ssize_t *val)
69 {
70     char *first_error;
71     ssize_t j;
72
73     assert(str != NULL);
74
75     j = (ssize_t)strtol(str, &first_error, 10);
76
77     if (errno == ERANGE || *str == '\0' || *first_error != '\0')
78         return FALSE;
79
80     if (val != NULL)
81         *val = j;
82
83     return TRUE;
84 }
85
86 /* Read two ssize_t's, separated by a comma, from str, and store them in
87  * *line and *column (if they're not both NULL).  Return FALSE on error,
88  * or TRUE otherwise. */
89 bool parse_line_column(const char *str, ssize_t *line, ssize_t *column)
90 {
91     bool retval = TRUE;
92     const char *comma;
93
94     assert(str != NULL);
95
96     comma = strchr(str, ',');
97
98     if (comma != NULL && column != NULL) {
99         if (!parse_num(comma + 1, column))
100             retval = FALSE;
101     }
102
103     if (line != NULL) {
104         if (comma != NULL) {
105             char *str_line = mallocstrncpy(NULL, str, comma - str + 1);
106             str_line[comma - str] = '\0';
107
108             if (str_line[0] != '\0' && !parse_num(str_line, line))
109                 retval = FALSE;
110
111             free(str_line);
112         } else if (!parse_num(str, line))
113             retval = FALSE;
114     }
115
116     return retval;
117 }
118
119 /* Fix the memory allocation for a string. */
120 void align(char **str)
121 {
122     assert(str != NULL);
123
124     if (*str != NULL)
125         *str = charealloc(*str, strlen(*str) + 1);
126 }
127
128 /* Null a string at a certain index and align it. */
129 void null_at(char **data, size_t index)
130 {
131     assert(data != NULL);
132
133     *data = charealloc(*data, index + 1);
134     (*data)[index] = '\0';
135 }
136
137 /* For non-null-terminated lines.  A line, by definition, shouldn't
138  * normally have newlines in it, so encode its nulls as newlines. */
139 void unsunder(char *str, size_t true_len)
140 {
141     assert(str != NULL);
142
143     for (; true_len > 0; true_len--, str++) {
144         if (*str == '\0')
145             *str = '\n';
146     }
147 }
148
149 /* For non-null-terminated lines.  A line, by definition, shouldn't
150  * normally have newlines in it, so decode its newlines as nulls. */
151 void sunder(char *str)
152 {
153     assert(str != NULL);
154
155     for (; *str != '\0'; str++) {
156         if (*str == '\n')
157             *str = '\0';
158     }
159 }
160
161 /* These functions, ngetline() (originally getline()) and ngetdelim()
162  * (originally getdelim()), were adapted from GNU mailutils 0.5
163  * (mailbox/getline.c).  Here is the notice from that file, after
164  * converting to the GPL via LGPL clause 3, and with the Free Software
165  * Foundation's address and the copyright years updated:
166  *
167  * GNU Mailutils -- a suite of utilities for electronic mail
168  * Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
169  * Free Software Foundation, Inc.
170  *
171  * This library is free software; you can redistribute it and/or
172  * modify it under the terms of the GNU General Public License as
173  * published by the Free Software Foundation; either version 3 of the
174  * License, or (at your option) any later version.
175  *
176  * This library is distributed in the hope that it will be useful,
177  * but WITHOUT ANY WARRANTY; without even the implied warranty of
178  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
179  * General Public License for more details.
180  *
181  * You should have received a copy of the GNU General Public License
182  * along with this library; if not, write to the Free Software
183  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
184  * 02110-1301, USA. */
185
186 #ifdef ENABLE_NANORC
187
188 #ifndef HAVE_GETDELIM
189 /* This function is equivalent to getdelim(). */
190 ssize_t ngetdelim(char **lineptr, size_t *n, int delim, FILE *stream)
191 {
192     size_t indx = 0;
193     int c;
194
195     /* Sanity checks. */
196     if (lineptr == NULL || n == NULL || stream == NULL ||
197         fileno(stream) == -1) {
198         errno = EINVAL;
199         return -1;
200     }
201
202     /* Allocate the line the first time. */
203     if (*lineptr == NULL) {
204         *n = MAX_BUF_SIZE;
205         *lineptr = charalloc(*n);
206     }
207
208     while ((c = getc(stream)) != EOF) {
209         /* Check if more memory is needed. */
210         if (indx >= *n) {
211             *n += MAX_BUF_SIZE;
212             *lineptr = charealloc(*lineptr, *n);
213         }
214
215         /* Put the result in the line. */
216         (*lineptr)[indx++] = (char)c;
217
218         /* Bail out. */
219         if (c == delim)
220             break;
221     }
222
223     /* Make room for the null character. */
224     if (indx >= *n) {
225         *n += MAX_BUF_SIZE;
226         *lineptr = charealloc(*lineptr, *n);
227     }
228
229     /* Null-terminate the buffer. */
230     null_at(lineptr, indx++);
231     *n = indx;
232
233     /* The last line may not have the delimiter.  We have to return what
234      * we got, and the error will be seen on the next iteration. */
235     return (c == EOF && (indx - 1) == 0) ? -1 : indx - 1;
236 }
237 #endif
238
239 #ifndef HAVE_GETLINE
240 /* This function is equivalent to getline(). */
241 ssize_t ngetline(char **lineptr, size_t *n, FILE *stream)
242 {
243     return getdelim(lineptr, n, '\n', stream);
244 }
245 #endif
246 #endif /* ENABLE_NANORC */
247
248 #ifdef HAVE_REGEX_H
249 /* Do the compiled regex in preg and the regex in string match the
250  * beginning or end of a line? */
251 bool regexp_bol_or_eol(const regex_t *preg, const char *string)
252 {
253     return (regexec(preg, string, 0, NULL, 0) == 0 &&
254         regexec(preg, string, 0, NULL, REG_NOTBOL | REG_NOTEOL) ==
255         REG_NOMATCH);
256 }
257
258 /* Fix the regex if we're on platforms which requires an adjustment
259  * from GNU-style to BSD-style word boundaries.  */
260 const char *fixbounds(const char *r) {
261 #ifndef GNU_WORDBOUNDS
262     int i, j = 0;
263     char *r2 = charalloc(strlen(r) * 5);
264     char *r3;
265
266 #ifdef DEBUG
267     fprintf(stderr, "fixbounds(): Start string = \"%s\"\n", r);
268 #endif
269
270     for (i = 0; i < strlen(r); i++) {
271         if (r[i] != '\0' && r[i] == '\\' && (r[i+1] == '>' || r[i+1] == '<')) {
272             strcpy(&r2[j], "[[:");
273             r2[j+3] = r[i+1];
274             strcpy(&r2[j+4], ":]]");
275             i++;
276             j += 6;
277         } else
278             r2[j] = r[i];
279         j++;
280     }
281     r2[j] = '\0';
282     r3 = mallocstrcpy(NULL, r2);
283     free(r2);
284 #ifdef DEBUG
285     fprintf(stderr, "fixbounds(): Ending string = \"%s\"\n", r3);
286 #endif
287     return (const char *) r3;
288 #endif
289
290     return r;
291 }
292
293 #endif
294
295 #ifndef DISABLE_SPELLER
296 /* Is the word starting at position pos in buf a whole word? */
297 bool is_whole_word(size_t pos, const char *buf, const char *word)
298 {
299     char *p = charalloc(mb_cur_max()), *r = charalloc(mb_cur_max());
300     size_t word_end = pos + strlen(word);
301     bool retval;
302
303     assert(buf != NULL && pos <= strlen(buf) && word != NULL);
304
305     parse_mbchar(buf + move_mbleft(buf, pos), p, NULL);
306     parse_mbchar(buf + word_end, r, NULL);
307
308     /* If we're at the beginning of the line or the character before the
309      * word isn't a non-punctuation "word" character, and if we're at
310      * the end of the line or the character after the word isn't a
311      * non-punctuation "word" character, we have a whole word. */
312     retval = (pos == 0 || !is_word_mbchar(p, FALSE)) &&
313         (word_end == strlen(buf) || !is_word_mbchar(r, FALSE));
314
315     free(p);
316     free(r);
317
318     return retval;
319 }
320 #endif /* !DISABLE_SPELLER */
321
322 /* If we are searching backwards, we will find the last match that
323  * starts no later than start.  Otherwise we find the first match
324  * starting no earlier than start.  If we are doing a regexp search, we
325  * fill in the global variable regmatches with at most 9 subexpression
326  * matches.  Also, all .rm_so elements are relative to the start of the
327  * whole match, so regmatches[0].rm_so == 0. */
328 const char *strstrwrapper(const char *haystack, const char *needle,
329         const char *start)
330 {
331     /* start can be 1 character before the start or after the end of the
332      * line.  In either case, we just say no match was found. */
333     if ((start > haystack && *(start - 1) == '\0') || start < haystack)
334         return NULL;
335
336     assert(haystack != NULL && needle != NULL && start != NULL);
337
338 #ifdef HAVE_REGEX_H
339     if (ISSET(USE_REGEXP)) {
340 #ifndef NANO_TINY
341         if (ISSET(BACKWARDS_SEARCH)) {
342             if (regexec(&search_regexp, haystack, 1, regmatches,
343                 0) == 0 && haystack + regmatches[0].rm_so <= start) {
344                 const char *retval = haystack + regmatches[0].rm_so;
345
346                 /* Search forward until there are no more matches. */
347                 while (regexec(&search_regexp, retval + 1, 1,
348                         regmatches, REG_NOTBOL) == 0 &&
349                         retval + regmatches[0].rm_so + 1 <= start)
350                     retval += regmatches[0].rm_so + 1;
351                 /* Finally, put the subexpression matches in global
352                  * variable regmatches.  The REG_NOTBOL flag doesn't
353                  * matter now. */
354                 regexec(&search_regexp, retval, 10, regmatches, 0);
355                 return retval;
356             }
357         } else
358 #endif /* !NANO_TINY */
359         if (regexec(&search_regexp, start, 10, regmatches,
360                 (start > haystack) ? REG_NOTBOL : 0) == 0) {
361             const char *retval = start + regmatches[0].rm_so;
362
363             regexec(&search_regexp, retval, 10, regmatches, 0);
364             return retval;
365         }
366         return NULL;
367     }
368 #endif /* HAVE_REGEX_H */
369 #if !defined(NANO_TINY) || !defined(DISABLE_SPELLER)
370     if (ISSET(CASE_SENSITIVE)) {
371 #ifndef NANO_TINY
372         if (ISSET(BACKWARDS_SEARCH))
373             return revstrstr(haystack, needle, start);
374         else
375 #endif
376             return strstr(start, needle);
377     }
378 #endif /* !DISABLE_SPELLER || !NANO_TINY */
379 #ifndef NANO_TINY
380     else if (ISSET(BACKWARDS_SEARCH))
381         return mbrevstrcasestr(haystack, needle, start);
382 #endif
383     return mbstrcasestr(start, needle);
384 }
385
386 /* This is a wrapper for the perror() function.  The wrapper temporarily
387  * leaves curses mode, calls perror() (which writes to stderr), and then
388  * reenters curses mode, updating the screen in the process.  Note that
389  * nperror() causes the window to flicker once. */
390 void nperror(const char *s)
391 {
392     endwin();
393     perror(s);
394     doupdate();
395 }
396
397 /* This is a wrapper for the malloc() function that properly handles
398  * things when we run out of memory.  Thanks, BG, many people have been
399  * asking for this... */
400 void *nmalloc(size_t howmuch)
401 {
402     void *r = malloc(howmuch);
403
404     if (r == NULL && howmuch != 0)
405         die(_("nano is out of memory!"));
406
407     return r;
408 }
409
410 /* This is a wrapper for the realloc() function that properly handles
411  * things when we run out of memory. */
412 void *nrealloc(void *ptr, size_t howmuch)
413 {
414     void *r = realloc(ptr, howmuch);
415
416     if (r == NULL && howmuch != 0)
417         die(_("nano is out of memory!"));
418
419     return r;
420 }
421
422 /* Copy the first n characters of one malloc()ed string to another
423  * pointer.  Should be used as: "dest = mallocstrncpy(dest, src,
424  * n);". */
425 char *mallocstrncpy(char *dest, const char *src, size_t n)
426 {
427     if (src == NULL)
428         src = "";
429
430     if (src != dest)
431         free(dest);
432
433     dest = charalloc(n);
434     strncpy(dest, src, n);
435
436     return dest;
437 }
438
439 /* Copy one malloc()ed string to another pointer.  Should be used as:
440  * "dest = mallocstrcpy(dest, src);". */
441 char *mallocstrcpy(char *dest, const char *src)
442 {
443     return mallocstrncpy(dest, src, (src == NULL) ? 1 :
444         strlen(src) + 1);
445 }
446
447 /* Free the malloc()ed string at dest and return the malloc()ed string
448  * at src.  Should be used as: "answer = mallocstrassn(answer,
449  * real_dir_from_tilde(answer));". */
450 char *mallocstrassn(char *dest, char *src)
451 {
452     free(dest);
453     return src;
454 }
455
456 /* nano scrolls horizontally within a line in chunks.  Return the column
457  * number of the first character displayed in the edit window when the
458  * cursor is at the given column.  Note that (0 <= column -
459  * get_page_start(column) < COLS). */
460 size_t get_page_start(size_t column)
461 {
462     if (column == 0 || column < COLS - 1)
463         return 0;
464     else if (COLS > 8)
465         return column - 7 - (column - 7) % (COLS - 8);
466     else
467         return column - (COLS - 2);
468 }
469
470 /* Return the placewewant associated with current_x, i.e. the zero-based
471  * column position of the cursor.  The value will be no smaller than
472  * current_x. */
473 size_t xplustabs(void)
474 {
475     return strnlenpt(openfile->current->data, openfile->current_x);
476 }
477
478 /* Return the index in s of the character displayed at the given column,
479  * i.e. the largest value such that strnlenpt(s, actual_x(s, column)) <=
480  * column. */
481 size_t actual_x(const char *s, size_t column)
482 {
483     size_t i = 0;
484         /* The position in s, returned. */
485     size_t len = 0;
486         /* The screen display width to s[i]. */
487
488     assert(s != NULL);
489
490     while (*s != '\0') {
491         int s_len = parse_mbchar(s, NULL, &len);
492
493         if (len > column)
494             break;
495
496         i += s_len;
497         s += s_len;
498     }
499
500     return i;
501 }
502
503 /* A strnlen() with tabs and multicolumn characters factored in, similar
504  * to xplustabs().  How many columns wide are the first maxlen characters
505  * of s? */
506 size_t strnlenpt(const char *s, size_t maxlen)
507 {
508     size_t len = 0;
509         /* The screen display width to s[i]. */
510
511     if (maxlen == 0)
512         return 0;
513
514     assert(s != NULL);
515
516     while (*s != '\0') {
517         int s_len = parse_mbchar(s, NULL, &len);
518
519         s += s_len;
520
521         if (maxlen <= s_len)
522             break;
523
524         maxlen -= s_len;
525     }
526
527     return len;
528 }
529
530 /* A strlen() with tabs and multicolumn characters factored in, similar
531  * to xplustabs().  How many columns wide is s? */
532 size_t strlenpt(const char *s)
533 {
534     return strnlenpt(s, (size_t)-1);
535 }
536
537 /* Append a new magicline to filebot. */
538 void new_magicline(void)
539 {
540     openfile->filebot->next = (filestruct *)nmalloc(sizeof(filestruct));
541     openfile->filebot->next->data = mallocstrcpy(NULL, "");
542     openfile->filebot->next->prev = openfile->filebot;
543     openfile->filebot->next->next = NULL;
544     openfile->filebot->next->lineno = openfile->filebot->lineno + 1;
545 #ifdef ENABLE_COLOR
546     openfile->filebot->next->multidata = NULL;
547 #endif
548     openfile->filebot = openfile->filebot->next;
549     openfile->totsize++;
550 }
551
552 #ifndef NANO_TINY
553 /* Remove the magicline from filebot, if there is one and it isn't the
554  * only line in the file.  Assume that edittop and current are not at
555  * filebot. */
556 void remove_magicline(void)
557 {
558     if (openfile->filebot->data[0] == '\0' &&
559         openfile->filebot != openfile->fileage) {
560         assert(openfile->filebot != openfile->edittop && openfile->filebot != openfile->current);
561
562         openfile->filebot = openfile->filebot->prev;
563         free_filestruct(openfile->filebot->next);
564         openfile->filebot->next = NULL;
565         openfile->totsize--;
566     }
567 }
568
569 /* Set top_x and bot_x to the top and bottom x-coordinates of the mark,
570  * respectively, based on the locations of top and bot.  If
571  * right_side_up isn't NULL, set it to TRUE if the mark begins with
572  * (mark_begin, mark_begin_x) and ends with (current, current_x), or
573  * FALSE otherwise. */
574 void mark_order(const filestruct **top, size_t *top_x, const filestruct
575         **bot, size_t *bot_x, bool *right_side_up)
576 {
577     assert(top != NULL && top_x != NULL && bot != NULL && bot_x != NULL);
578
579     if ((openfile->current->lineno == openfile->mark_begin->lineno &&
580         openfile->current_x > openfile->mark_begin_x) ||
581         openfile->current->lineno > openfile->mark_begin->lineno) {
582         *top = openfile->mark_begin;
583         *top_x = openfile->mark_begin_x;
584         *bot = openfile->current;
585         *bot_x = openfile->current_x;
586         if (right_side_up != NULL)
587             *right_side_up = TRUE;
588     } else {
589         *bot = openfile->mark_begin;
590         *bot_x = openfile->mark_begin_x;
591         *top = openfile->current;
592         *top_x = openfile->current_x;
593         if (right_side_up != NULL)
594             *right_side_up = FALSE;
595     }
596 }
597 #endif
598
599 /* Calculate the number of characters between begin and end, and return
600  * it. */
601 size_t get_totsize(const filestruct *begin, const filestruct *end)
602 {
603     size_t totsize = 0;
604     const filestruct *f;
605
606     /* Go through the lines from begin to end->prev, if we can. */
607     for (f = begin; f != end && f != NULL; f = f->next) {
608         /* Count the number of characters on this line. */
609         totsize += mbstrlen(f->data);
610
611         /* Count the newline if we have one. */
612         if (f->next != NULL)
613             totsize++;
614     }
615
616     /* Go through the line at end, if we can. */
617     if (f != NULL) {
618         /* Count the number of characters on this line. */
619         totsize += mbstrlen(f->data);
620
621         /* Count the newline if we have one. */
622         if (f->next != NULL)
623             totsize++;
624     }
625
626     return totsize;
627 }
628
629 /* Get back a pointer given a line number in the current openfilestruct */
630 filestruct *fsfromline(ssize_t lineno)
631 {
632     filestruct *f = openfile->current;
633
634     if (lineno <= openfile->current->lineno)
635         for (; f->lineno != lineno && f != openfile->fileage; f = f->prev)
636            ;
637     else
638         for (; f->lineno != lineno && f->next != NULL; f = f->next)
639             ;
640
641     if (f->lineno != lineno)
642         f = NULL;
643     return f;
644 }
645
646 #ifdef DEBUG
647 /* Dump the filestruct inptr to stderr. */
648 void dump_filestruct(const filestruct *inptr)
649 {
650     if (inptr == openfile->fileage)
651         fprintf(stderr, "Dumping file buffer to stderr...\n");
652     else if (inptr == cutbuffer)
653         fprintf(stderr, "Dumping cutbuffer to stderr...\n");
654     else
655         fprintf(stderr, "Dumping a buffer to stderr...\n");
656
657     while (inptr != NULL) {
658         fprintf(stderr, "(%ld) %s\n", (long)inptr->lineno, inptr->data);
659         inptr = inptr->next;
660     }
661 }
662
663 /* Dump the current buffer's filestruct to stderr in reverse. */
664 void dump_filestruct_reverse(void)
665 {
666     const filestruct *fileptr = openfile->filebot;
667
668     while (fileptr != NULL) {
669         fprintf(stderr, "(%ld) %s\n", (long)fileptr->lineno,
670                 fileptr->data);
671         fileptr = fileptr->prev;
672     }
673 }
674 #endif /* DEBUG */