Add a changelog
[pgp-tools.git] / keyanalyze / pgpring / lib.c
1 /*
2 * Copyright (C) 1996-2000 Michael R. Elkins <me@cs.hmc.edu>
3 * Copyright (C) 1999-2000 Thomas Roessler <roessler@guug.de>
4 *
5 * This program is free software; you can redistribute it
6 * and/or modify it under the terms of the GNU General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later
9 * version.
10 *
11 * This program is distributed in the hope that it will be
12 * useful, but WITHOUT ANY WARRANTY; without even the implied
13 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more
15 * details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111, USA.
21 */
22
23 /*
24 * This file used to contain some more functions, namely those
25 * which are now in muttlib.c. They have been removed, so we have
26 * some of our "standard" functions in external programs, too.
27 */
28
29 #include <string.h>
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <sys/wait.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <pwd.h>
38
39 #include "lib.h"
40
41 void mutt_nocurses_error (const char *fmt, ...)
42 {
43 va_list ap;
44
45 va_start (ap, fmt);
46 vfprintf (stderr, fmt, ap);
47 va_end (ap);
48 fputc ('\n', stderr);
49 }
50
51 void *safe_calloc (size_t nmemb, size_t size)
52 {
53 void *p;
54
55 if (!nmemb || !size)
56 return NULL;
57 if (!(p = calloc (nmemb, size)))
58 {
59 mutt_error _("Out of memory!");
60 sleep (1);
61 mutt_exit (1);
62 }
63 return p;
64 }
65
66 void *safe_malloc (size_t siz)
67 {
68 void *p;
69
70 if (siz == 0)
71 return 0;
72 if ((p = (void *) malloc (siz)) == 0) /* __MEM_CHECKED__ */
73 {
74 mutt_error _("Out of memory!");
75 sleep (1);
76 mutt_exit (1);
77 }
78 return (p);
79 }
80
81 void safe_realloc (void *p2, size_t siz)
82 {
83 void **p = (void **)p2;
84 void *r;
85
86 if (siz == 0)
87 {
88 if (*p)
89 {
90 free (*p); /* __MEM_CHECKED__ */
91 *p = NULL;
92 }
93 return;
94 }
95
96 if (*p)
97 r = (void *) realloc (*p, siz); /* __MEM_CHECKED__ */
98 else
99 {
100 /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x --- __MEM_CHECKED__ */
101 r = (void *) malloc (siz); /* __MEM_CHECKED__ */
102 }
103
104 if (!r)
105 {
106 mutt_error _("Out of memory!");
107 sleep (1);
108 mutt_exit (1);
109 }
110
111 *p = r;
112 }
113
114 void safe_free (void *p2)
115 {
116 void **p = (void **)p2;
117 if (*p)
118 {
119 free (*p); /* __MEM_CHECKED__ */
120 *p = 0;
121 }
122 }
123
124 int safe_fclose (FILE **f)
125 {
126 int r = 0;
127
128 if (*f)
129 r = fclose (*f);
130
131 *f = NULL;
132 return r;
133 }
134
135 char *safe_strdup (const char *s)
136 {
137 char *p;
138 size_t l;
139
140 if (!s || !*s)
141 return 0;
142 l = strlen (s) + 1;
143 p = (char *)safe_malloc (l);
144 memcpy (p, s, l);
145 return (p);
146 }
147
148 void mutt_str_replace (char **p, const char *s)
149 {
150 safe_free (p);
151 *p = safe_strdup (s);
152 }
153
154 void mutt_str_adjust (char **p)
155 {
156 if (!p || !*p) return;
157 safe_realloc ((void **) p, strlen (*p) + 1);
158 }
159
160 /* convert all characters in the string to lowercase */
161 char *mutt_strlower (char *s)
162 {
163 char *p = s;
164
165 while (*p)
166 {
167 *p = tolower (*p);
168 p++;
169 }
170
171 return (s);
172 }
173
174 void mutt_unlink (const char *s)
175 {
176 FILE *f;
177 struct stat sb;
178 char buf[2048];
179
180 if (stat (s, &sb) == 0)
181 {
182 if ((f = fopen (s, "r+")))
183 {
184 unlink (s);
185 memset (buf, 0, sizeof (buf));
186 while (sb.st_size > 0)
187 {
188 fwrite (buf, 1, sizeof (buf), f);
189 sb.st_size -= sizeof (buf);
190 }
191 fclose (f);
192 }
193 }
194 }
195
196 int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
197 {
198 char buf[2048];
199 size_t chunk;
200
201 while (size > 0)
202 {
203 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
204 if ((chunk = fread (buf, 1, chunk, in)) < 1)
205 break;
206 if (fwrite (buf, 1, chunk, out) != chunk)
207 {
208 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
209 return (-1);
210 }
211 size -= chunk;
212 }
213
214 return 0;
215 }
216
217 int mutt_copy_stream (FILE *fin, FILE *fout)
218 {
219 size_t l;
220 char buf[LONG_STRING];
221
222 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
223 {
224 if (fwrite (buf, 1, l, fout) != l)
225 return (-1);
226 }
227
228 return 0;
229 }
230
231 static int
232 compare_stat (struct stat *osb, struct stat *nsb)
233 {
234 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
235 osb->st_rdev != nsb->st_rdev)
236 {
237 return -1;
238 }
239
240 return 0;
241 }
242
243 int safe_symlink(const char *oldpath, const char *newpath)
244 {
245 struct stat osb, nsb;
246
247 if(!oldpath || !newpath)
248 return -1;
249
250 if(unlink(newpath) == -1 && errno != ENOENT)
251 return -1;
252
253 if (oldpath[0] == '/')
254 {
255 if (symlink (oldpath, newpath) == -1)
256 return -1;
257 }
258 else
259 {
260 char abs_oldpath[_POSIX_PATH_MAX];
261
262 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
263 (strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 > sizeof abs_oldpath))
264 return -1;
265
266 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
267 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
268 if (symlink (abs_oldpath, newpath) == -1)
269 return -1;
270 }
271
272 if(stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1
273 || compare_stat(&osb, &nsb) == -1)
274 {
275 unlink(newpath);
276 return -1;
277 }
278
279 return 0;
280 }
281
282 /*
283 * This function is supposed to do nfs-safe renaming of files.
284 *
285 * Warning: We don't check whether src and target are equal.
286 */
287
288 int safe_rename (const char *src, const char *target)
289 {
290 struct stat ssb, tsb;
291
292 if (!src || !target)
293 return -1;
294
295 if (link (src, target) != 0)
296 {
297
298 /*
299 * Coda does not allow cross-directory links, but tells
300 * us it's a cross-filesystem linking attempt.
301 *
302 * However, the Coda rename call is allegedly safe to use.
303 *
304 * With other file systems, rename should just fail when
305 * the files reside on different file systems, so it's safe
306 * to try it here.
307 *
308 */
309
310 if (errno == EXDEV)
311 return rename (src, target);
312
313 return -1;
314 }
315
316 /*
317 * Stat both links and check if they are equal.
318 */
319
320 if (stat (src, &ssb) == -1)
321 {
322 return -1;
323 }
324
325 if (stat (target, &tsb) == -1)
326 {
327 return -1;
328 }
329
330 /*
331 * pretend that the link failed because the target file
332 * did already exist.
333 */
334
335 if (compare_stat (&ssb, &tsb) == -1)
336 {
337 errno = EEXIST;
338 return -1;
339 }
340
341 /*
342 * Unlink the original link. Should we really ignore the return
343 * value here? XXX
344 */
345
346 unlink (src);
347
348 return 0;
349 }
350
351 int safe_open (const char *path, int flags)
352 {
353 struct stat osb, nsb;
354 int fd;
355
356 if ((fd = open (path, flags, 0600)) < 0)
357 return fd;
358
359 /* make sure the file is not symlink */
360 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
361 compare_stat(&osb, &nsb) == -1)
362 {
363 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
364 close (fd);
365 return (-1);
366 }
367
368 return (fd);
369 }
370
371 /* when opening files for writing, make sure the file doesn't already exist
372 * to avoid race conditions.
373 */
374 FILE *safe_fopen (const char *path, const char *mode)
375 {
376 if (mode[0] == 'w')
377 {
378 int fd;
379 int flags = O_CREAT | O_EXCL;
380
381 #ifdef O_NOFOLLOW
382 flags |= O_NOFOLLOW;
383 #endif
384
385 if (mode[1] == '+')
386 flags |= O_RDWR;
387 else
388 flags |= O_WRONLY;
389
390 if ((fd = safe_open (path, flags)) < 0)
391 return (NULL);
392
393 return (fdopen (fd, mode));
394 }
395 else
396 return (fopen (path, mode));
397 }
398
399 static char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
400
401 void mutt_sanitize_filename (char *f, short slash)
402 {
403 if (!f) return;
404
405 for (; *f; f++)
406 {
407 if ((slash && *f == '/') || !strchr (safe_chars, *f))
408 *f = '_';
409 }
410 }
411
412 /* these characters must be escaped in regular expressions */
413
414 static char rx_special_chars[] = "^.[$()|*+?{\\";
415
416 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
417 {
418 while (*src && --destlen > 2)
419 {
420 if (strchr (rx_special_chars, *src))
421 {
422 *dest++ = '\\';
423 destlen--;
424 }
425 *dest++ = *src++;
426 }
427
428 *dest = '\0';
429
430 if (*src)
431 return -1;
432 else
433 return 0;
434 }
435
436 /* Read a line from ``fp'' into the dynamically allocated ``s'',
437 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
438 * If a line ends with "\", this char and the linefeed is removed,
439 * and the next line is read too.
440 */
441 char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line)
442 {
443 size_t offset = 0;
444 char *ch;
445
446 if (!s)
447 {
448 s = safe_malloc (STRING);
449 *size = STRING;
450 }
451
452 FOREVER
453 {
454 if (fgets (s + offset, *size - offset, fp) == NULL)
455 {
456 safe_free (&s);
457 return NULL;
458 }
459 if ((ch = strchr (s + offset, '\n')) != NULL)
460 {
461 (*line)++;
462 *ch = 0;
463 if (ch > s && *(ch - 1) == '\r')
464 *--ch = 0;
465 if (ch == s || *(ch - 1) != '\\')
466 return s;
467 offset = ch - s - 1;
468 }
469 else
470 {
471 int c;
472 c = getc (fp); /* This is kind of a hack. We want to know if the
473 char at the current point in the input stream is EOF.
474 feof() will only tell us if we've already hit EOF, not
475 if the next character is EOF. So, we need to read in
476 the next character and manually check if it is EOF. */
477 if (c == EOF)
478 {
479 /* The last line of fp isn't \n terminated */
480 (*line)++;
481 return s;
482 }
483 else
484 {
485 ungetc (c, fp); /* undo our dammage */
486 /* There wasn't room for the line -- increase ``s'' */
487 offset = *size - 1; /* overwrite the terminating 0 */
488 *size += STRING;
489 safe_realloc (&s, *size);
490 }
491 }
492 }
493 }
494
495 char *
496 mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
497 {
498 size_t len;
499
500 len = end - beg;
501 if (len > destlen - 1)
502 len = destlen - 1;
503 memcpy (dest, beg, len);
504 dest[len] = 0;
505 return dest;
506 }
507
508 char *mutt_substrdup (const char *begin, const char *end)
509 {
510 size_t len;
511 char *p;
512
513 if (end)
514 len = end - begin;
515 else
516 len = strlen (begin);
517
518 p = safe_malloc (len + 1);
519 memcpy (p, begin, len);
520 p[len] = 0;
521 return p;
522 }
523
524 /* prepare a file name to survive the shell's quoting rules.
525 * From the Unix programming FAQ by way of Liviu.
526 */
527
528 size_t mutt_quote_filename (char *d, size_t l, const char *f)
529 {
530 size_t i, j = 0;
531
532 if(!f)
533 {
534 *d = '\0';
535 return 0;
536 }
537
538 /* leave some space for the trailing characters. */
539 l -= 6;
540
541 d[j++] = '\'';
542
543 for(i = 0; j < l && f[i]; i++)
544 {
545 if(f[i] == '\'' || f[i] == '`')
546 {
547 d[j++] = '\'';
548 d[j++] = '\\';
549 d[j++] = f[i];
550 d[j++] = '\'';
551 }
552 else
553 d[j++] = f[i];
554 }
555
556 d[j++] = '\'';
557 d[j] = '\0';
558
559 return j;
560 }
561
562 /* NULL-pointer aware string comparison functions */
563
564 int mutt_strcmp(const char *a, const char *b)
565 {
566 return strcmp(NONULL(a), NONULL(b));
567 }
568
569 int mutt_strcasecmp(const char *a, const char *b)
570 {
571 return strcasecmp(NONULL(a), NONULL(b));
572 }
573
574 int mutt_strncmp(const char *a, const char *b, size_t l)
575 {
576 return strncmp(NONULL(a), NONULL(b), l);
577 }
578
579 int mutt_strncasecmp(const char *a, const char *b, size_t l)
580 {
581 return strncasecmp(NONULL(a), NONULL(b), l);
582 }
583
584 size_t mutt_strlen(const char *a)
585 {
586 return a ? strlen (a) : 0;
587 }
588
589 const char *mutt_stristr (const char *haystack, const char *needle)
590 {
591 const char *p, *q;
592
593 if (!haystack)
594 return NULL;
595 if (!needle)
596 return (haystack);
597
598 while (*(p = haystack))
599 {
600 for (q = needle; *p && *q && tolower (*p) == tolower (*q); p++, q++)
601 ;
602 if (!*q)
603 return (haystack);
604 haystack++;
605 }
606 return NULL;
607 }
608
609 char *mutt_skip_whitespace (char *p)
610 {
611 SKIPWS (p);
612 return p;
613 }
614
615 void mutt_remove_trailing_ws (char *s)
616 {
617 char *p;
618
619 for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
620 *p = 0;
621 }