]> Dogcows Code - chaz/tar/blob - src/misc.c
GNU tar 1.12
[chaz/tar] / src / misc.c
1 /* Miscellaneous functions, not really specific to GNU tar.
2 Copyright (C) 1988, 92, 94, 95, 96, 97 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12 Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 59 Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18 #include "system.h"
19
20 #include "backupfile.h"
21 #include "rmt.h"
22
23 /* The following inclusion for crosschecking prototypes, only. */
24 #include "common.h"
25 \f
26 /* Handling strings. */
27
28 #define ISPRINT(Char) (ISASCII (Char) && isprint (Char))
29
30 /*-------------------------------------------------------------------------.
31 | Assign STRING to a copy of VALUE if not NULL, or to NULL. If STRING was |
32 | not NULL, it is freed first. |
33 `-------------------------------------------------------------------------*/
34
35 void
36 assign_string (char **string, const char *value)
37 {
38 if (*string)
39 free (*string);
40 *string = value ? xstrdup (value) : NULL;
41 }
42
43 /*------------------------------------------------------------------------.
44 | Allocate a copy of the string quoted as in C, and returns that. If the |
45 | string does not have to be quoted, it returns the NULL string. The |
46 | allocated copy should normally be freed with free() after the caller is |
47 | done with it. |
48 | |
49 | This is used in two contexts only: either listing a tar file for the |
50 | --list (-t) option, or generating the directory file in incremental |
51 | dumps. |
52 `------------------------------------------------------------------------*/
53
54 char *
55 quote_copy_string (const char *string)
56 {
57 const char *source = string;
58 char *destination = NULL;
59 char *buffer = NULL;
60 int copying = 0;
61
62 while (*source)
63 {
64 int character = (unsigned char) *source++;
65
66 if (character == '\\')
67 {
68 if (!copying)
69 {
70 int length = (source - string) - 1;
71
72 copying = 1;
73 buffer = (char *) xmalloc (length + 5 + strlen (source) * 4);
74 memcpy (buffer, string, (size_t) length);
75 destination = buffer + length;
76 }
77 *destination++ = '\\';
78 *destination++ = '\\';
79 }
80 else if (ISPRINT (character))
81 {
82 if (copying)
83 *destination++ = character;
84 }
85 else
86 {
87 if (!copying)
88 {
89 int length = (source - string) - 1;
90
91 copying = 1;
92 buffer = (char *) xmalloc (length + 5 + strlen (source) * 4);
93 memcpy (buffer, string, (size_t) length);
94 destination = buffer + length;
95 }
96 *destination++ = '\\';
97 switch (character)
98 {
99 case '\n':
100 *destination++ = 'n';
101 break;
102
103 case '\t':
104 *destination++ = 't';
105 break;
106
107 case '\f':
108 *destination++ = 'f';
109 break;
110
111 case '\b':
112 *destination++ = 'b';
113 break;
114
115 case '\r':
116 *destination++ = 'r';
117 break;
118
119 case '\177':
120 *destination++ = '?';
121 break;
122
123 default:
124 *destination++ = (character >> 6) + '0';
125 *destination++ = ((character >> 3) & 07) + '0';
126 *destination++ = (character & 07) + '0';
127 break;
128 }
129 }
130 }
131 if (copying)
132 {
133 *destination = '\0';
134 return buffer;
135 }
136 return NULL;
137 }
138
139 /*-------------------------------------------------------------------------.
140 | Takes a quoted C string (like those produced by quote_copy_string) and |
141 | turns it back into the un-quoted original. This is done in place. |
142 | Returns 0 only if the string was not properly quoted, but completes the |
143 | unquoting anyway. |
144 | |
145 | This is used for reading the saved directory file in incremental dumps. |
146 | It is used for decoding old `N' records (demangling names). But also, |
147 | it is used for decoding file arguments, would they come from the shell |
148 | or a -T file, and for decoding the --exclude argument. |
149 `-------------------------------------------------------------------------*/
150
151 int
152 unquote_string (char *string)
153 {
154 int result = 1;
155 char *source = string;
156 char *destination = string;
157
158 while (*source)
159 if (*source == '\\')
160 switch (*++source)
161 {
162 case '\\':
163 *destination++ = '\\';
164 source++;
165 break;
166
167 case 'n':
168 *destination++ = '\n';
169 source++;
170 break;
171
172 case 't':
173 *destination++ = '\t';
174 source++;
175 break;
176
177 case 'f':
178 *destination++ = '\f';
179 source++;
180 break;
181
182 case 'b':
183 *destination++ = '\b';
184 source++;
185 break;
186
187 case 'r':
188 *destination++ = '\r';
189 source++;
190 break;
191
192 case '?':
193 *destination++ = 0177;
194 source++;
195 break;
196
197 case '0':
198 case '1':
199 case '2':
200 case '3':
201 case '4':
202 case '5':
203 case '6':
204 case '7':
205 {
206 int value = *source++ - '0';
207
208 if (*source < '0' || *source > '7')
209 {
210 *destination++ = value;
211 break;
212 }
213 value = value * 8 + *source++ - '0';
214 if (*source < '0' || *source > '7')
215 {
216 *destination++ = value;
217 break;
218 }
219 value = value * 8 + *source++ - '0';
220 *destination++ = value;
221 break;
222 }
223
224 default:
225 result = 0;
226 *destination++ = '\\';
227 if (*source)
228 *destination++ = *source++;
229 break;
230 }
231 else if (source != destination)
232 *destination++ = *source++;
233 else
234 source++, destination++;
235
236 if (source != destination)
237 *destination = '\0';
238 return result;
239 }
240 \f
241 /* Sorting lists. */
242
243 /*---.
244 | ? |
245 `---*/
246
247 char *
248 merge_sort (char *list, int length, int offset, int (*compare) (char *, char *))
249 {
250 char *first_list;
251 char *second_list;
252 int first_length;
253 int second_length;
254 char *result;
255 char **merge_point;
256 char *cursor;
257 int counter;
258
259 #define SUCCESSOR(Pointer) \
260 (*((char **) (((char *) (Pointer)) + offset)))
261
262 if (length == 1)
263 return list;
264
265 if (length == 2)
266 {
267 if ((*compare) (list, SUCCESSOR (list)) > 0)
268 {
269 result = SUCCESSOR (list);
270 SUCCESSOR (result) = list;
271 SUCCESSOR (list) = NULL;
272 return result;
273 }
274 return list;
275 }
276
277 first_list = list;
278 first_length = (length + 1) / 2;
279 second_length = length / 2;
280 for (cursor = list, counter = first_length - 1;
281 counter;
282 cursor = SUCCESSOR (cursor), counter--)
283 continue;
284 second_list = SUCCESSOR (cursor);
285 SUCCESSOR (cursor) = NULL;
286
287 first_list = merge_sort (first_list, first_length, offset, compare);
288 second_list = merge_sort (second_list, second_length, offset, compare);
289
290 merge_point = &result;
291 while (first_list && second_list)
292 if ((*compare) (first_list, second_list) < 0)
293 {
294 cursor = SUCCESSOR (first_list);
295 *merge_point = first_list;
296 merge_point = &SUCCESSOR (first_list);
297 first_list = cursor;
298 }
299 else
300 {
301 cursor = SUCCESSOR (second_list);
302 *merge_point = second_list;
303 merge_point = &SUCCESSOR (second_list);
304 second_list = cursor;
305 }
306 if (first_list)
307 *merge_point = first_list;
308 else
309 *merge_point = second_list;
310
311 return result;
312
313 #undef SUCCESSOR
314 }
315 \f
316 /* File handling. */
317
318 /* Saved names in case backup needs to be undone. */
319 static char *before_backup_name = NULL;
320 static char *after_backup_name = NULL;
321
322 /*------------------------------------------------------------------------.
323 | Returns nonzero if p is `.' or `..'. This could be a macro for speed. |
324 `------------------------------------------------------------------------*/
325
326 /* Early Solaris 2.4 readdir may return d->d_name as `' in NFS-mounted
327 directories. The workaround here skips `' just like `.'. Without it,
328 GNU tar would then treat `' much like `.' and loop endlessly. */
329
330 int
331 is_dot_or_dotdot (const char *p)
332 {
333 return (p[0] == '\0'
334 || (p[0] == '.' && (p[1] == '\0'
335 || (p[1] == '.' && p[2] == '\0'))));
336 }
337
338 /*-------------------------------------------------------------------------.
339 | Delete PATH, whatever it might be. If RECURSE, first recursively delete |
340 | the contents of PATH when it is a directory. Return zero on any error, |
341 | with errno set. As a special case, if we fail to delete a directory |
342 | when not RECURSE, do not set errno (just be tolerant to this error). |
343 `-------------------------------------------------------------------------*/
344
345 int
346 remove_any_file (const char *path, int recurse)
347 {
348 struct stat stat_buffer;
349
350 if (lstat (path, &stat_buffer) < 0)
351 return 0;
352
353 if (S_ISDIR (stat_buffer.st_mode))
354 if (recurse)
355 {
356 DIR *dirp = opendir (path);
357 struct dirent *dp;
358
359 if (dirp == NULL)
360 return 0;
361
362 while (dp = readdir (dirp), dp && !is_dot_or_dotdot (dp->d_name))
363 {
364 char *path_buffer = new_name (path, dp->d_name);
365
366 if (!remove_any_file (path_buffer, 1))
367 {
368 int saved_errno = errno;
369
370 free (path_buffer);
371 closedir (dirp);
372 errno = saved_errno; /* FIXME: errno should be read-only */
373 return 0;
374 }
375 free (path_buffer);
376 }
377 closedir (dirp);
378 return rmdir (path) >= 0;
379 }
380 else
381 {
382 /* FIXME: Saving errno might not be needed anymore, now that
383 extract_archive tests for the special case before recovery. */
384 int saved_errno = errno;
385
386 if (rmdir (path) >= 0)
387 return 1;
388 errno = saved_errno; /* FIXME: errno should be read-only */
389 return 0;
390 }
391
392 return unlink (path) >= 0;
393 }
394
395 /*-------------------------------------------------------------------------.
396 | Check if PATH already exists and make a backup of it right now. Return |
397 | success (nonzero) only if the backup in either unneeded, or successful. |
398 | |
399 | For now, directories are considered to never need backup. If ARCHIVE is |
400 | nonzero, this is the archive and so, we do not have to backup block or |
401 | character devices, nor remote entities. |
402 `-------------------------------------------------------------------------*/
403
404 int
405 maybe_backup_file (const char *path, int archive)
406 {
407 struct stat file_stat;
408
409 /* Check if we really need to backup the file. */
410
411 if (archive && _remdev (path))
412 return 1;
413
414 if (stat (path, &file_stat))
415 {
416 if (errno == ENOENT)
417 return 1;
418
419 ERROR ((0, errno, "%s", path));
420 return 0;
421 }
422
423 if (S_ISDIR (file_stat.st_mode))
424 return 1;
425
426 #ifdef S_ISBLK
427 if (archive && S_ISBLK (file_stat.st_mode))
428 return 1;
429 #endif
430
431 #ifdef S_ISCHR
432 if (archive && S_ISCHR (file_stat.st_mode))
433 return 1;
434 #endif
435
436 assign_string (&before_backup_name, path);
437
438 /* A run situation may exist between Emacs or other GNU programs trying to
439 make a backup for the same file simultaneously. If theoretically
440 possible, real problems are unlikely. Doing any better would require a
441 convention, GNU-wide, for all programs doing backups. */
442
443 assign_string (&after_backup_name, NULL);
444 after_backup_name = find_backup_file_name (path);
445 if (after_backup_name == NULL)
446 FATAL_ERROR ((0, 0, "Virtual memory exhausted"));
447
448 if (rename (before_backup_name, after_backup_name) == 0)
449 {
450 if (verbose_option)
451 fprintf (stdlis, _("Renaming previous `%s' to `%s'\n"),
452 before_backup_name, after_backup_name);
453 return 1;
454 }
455
456 /* The backup operation failed. */
457
458 ERROR ((0, errno, _("%s: Cannot rename for backup"), before_backup_name));
459 assign_string (&after_backup_name, NULL);
460 return 0;
461 }
462
463 /*-----------------------------------------------------------------------.
464 | Try to restore the recently backed up file to its original name. This |
465 | is usually only needed after a failed extraction. |
466 `-----------------------------------------------------------------------*/
467
468 void
469 undo_last_backup (void)
470 {
471 if (after_backup_name)
472 {
473 if (rename (after_backup_name, before_backup_name) != 0)
474 ERROR ((0, errno, _("%s: Cannot rename from backup"),
475 before_backup_name));
476 if (verbose_option)
477 fprintf (stdlis, _("Renaming `%s' back to `%s'\n"),
478 after_backup_name, before_backup_name);
479 assign_string (&after_backup_name, NULL);
480 }
481 }
This page took 0.052011 seconds and 5 git commands to generate.