source: trunk/mondo/src/common/libmondo-string.c @ 900

Last change on this file since 900 was 900, checked in by Bruno Cornec, 14 years ago

Huge patch to introduce low level functions that will bw used everywhere (mr_free, mr_asprintf, ...)
Nearly linking now due to that.

  • Property svn:keywords set to Id
File size: 28.8 KB
Line 
1/*
2   $Id: libmondo-string.c 900 2006-10-24 06:49:18Z bruno $
3*/
4
5/**
6 * @file
7 * Functions for handling strings.
8 */
9
10#include "my-stuff.h"
11#include "mondostructures.h"
12#include "libmondo-string.h"
13#include "libmondo-files-EXT.h"
14#include "newt-specific-EXT.h"
15#include "libmondo-tools-EXT.h"
16#include <math.h>
17#include "mr_mem.h"
18
19/*@unused@*/
20//static char cvsid[] = "$Id: libmondo-string.c 900 2006-10-24 06:49:18Z bruno $";
21
22extern int g_current_media_number;
23extern long long g_tape_posK;
24
25/**
26 * @addtogroup stringGroup
27 * @{
28 */
29/**
30 * Build a partition name from a drive and a partition number.
31 * @param drive The drive basename of the partition name (e.g. /dev/hda)
32 * @param partno The partition number (e.g. 1)
33 * @param partition Where to put the partition name (e.g. /dev/hda1)
34 * @return @p partition. The caller has to free it.
35 * @note If @p drive ends in a digit, then 'p' (on Linux) or 's' (on *BSD) is added before @p partno.
36 */
37char *build_partition_name(const char *drive, int partno)
38{
39    char *p, *c;
40    char *partition;
41
42    assert_string_is_neither_NULL_nor_zerolength(drive);
43    assert(partno >= 0);
44
45    mr_asprintf(&partition, "%s", drive);
46    p = partition;
47    /* is this a devfs device path? */
48    c = strrchr(partition, '/');
49    if (c && strncmp(c, "/disc", 5) == 0) {
50        /* yup it's devfs, return the "part" path */
51        /* format /dev/.../disc */
52        strcpy(c + 1, "part");
53    } else {
54        p += strlen(p);
55        if (isdigit(p[-1])) {
56            p = partition;
57#ifdef BSD
58            mr_asprintf(&partition, "%ss", p);
59#else
60            /* format /dev/cciss/c0d0 */
61            mr_asprintf(&partition, "%sp", p);
62#endif
63            mr_free(p);
64        }
65    }
66    p = partition;
67    mr_asprintf(&partition, "%s%d", p, partno);
68    mr_free(p);
69    return (partition);
70}
71
72
73/**
74 * Pad a string on both sides so it appears centered.
75 * @param in_out The string to be center-padded (modified). The caller needs to free this string
76 * @param width The width of the final result.
77 */
78void center_string(char *in_out, int width)
79{
80    char *scratch;
81    char *out;
82    char *p;
83    int i;                      /* purpose */
84    int len;                    /* purpose */
85    int mid;                    /* purpose */
86    int x;                      /* purpose */
87
88    assert(in_out != NULL);
89    assert(width > 2);
90
91    if (strlen(in_out) == 0) {
92        return;
93    }
94    for (p = in_out; *p == ' '; p++);
95    mr_asprintf(&scratch, p);
96    strip_spaces (scratch);
97    len = (int) strlen(scratch);
98    mid = width / 2;
99    x = mid - len / 2;
100    for (i = 0; i < x; i++) {
101        in_out[i] = ' ';
102    }
103    in_out[i] = '\0';
104    mr_asprintf(&out, "%s%s", in_out, scratch);
105    mr_free(scratch);
106    in_out = out;
107    for (i = x + len ; i < width - 1; i++) {
108            in_out[i] = ' ';
109        }
110    in_out[i] = '\0';
111}
112
113/* sout is allocated here and must be freed by the caller */
114
115inline void turn_wildcard_chars_into_literal_chars(char *sout, char *sin)
116{
117    char *p;
118    char *q = NULL;
119    char *sav;
120    char r;
121    bool end = FALSE;
122
123    mr_asprintf(&sav, sin);
124    p = sav;
125    while ((*p != '\0') && (! end)) {
126        if (strchr("[]*?", *p)) {
127            r = *p;                 // keep the wildcard char
128            *p = '\0';              // trunc the final string
129            p++;                    // continue on sav
130                                    // build the new string by recursion
131            turn_wildcard_chars_into_literal_chars(q,p);
132            mr_asprintf(&sout, "%s\\%c%s", sav, r, q);
133            mr_free(q);
134            mr_free(sav);
135            end = TRUE;
136        }
137        p++;
138    }
139    if (!end) {
140        sout = sav;
141    }
142}
143
144
145/**
146 * Turn an entry from the RAID editor's disklist into a GUI-friendly string.
147 * The format is: the device left-aligned and padded to 24 places, followed by a space and the
148 * index, right-aligned and padded to eight places. The total string length
149 * is exactly 33.
150 * @param disklist The disklist to operate on.
151 * @param lino The line number from @p disklist to convert to a string.
152 * @return The string form of the disklist entry.
153 * @note The returned string points to static storage and will be overwritten with each call.
154 */
155char *disklist_entry_to_string(struct list_of_disks *disklist, int lino)
156{
157
158    /*@ buffers ********************************************************** */
159    char *output;
160
161    assert(disklist != NULL);
162
163    mr_asprintf(&output, "%-24s %8d", disklist->el[lino].device,
164             disklist->el[lino].index);
165    return (output);
166}
167
168
169/**
170 * Turn a "friendly" sizestring into a number of megabytes.
171 * Supports the suffixes 'k'/'K', 'm'/'M', and 'g'/'G'. Calls
172 * fatal_error() if an unknown suffix is encountered.
173 * @param incoming The sizestring to convert (e.g. "40m", "2g").
174 * @return The size in megabytes.
175 */
176long friendly_sizestr_to_sizelong(char *incoming)
177{
178    long outval;
179    int i;
180    char *tmp;
181    char ch;
182
183    assert_string_is_neither_NULL_nor_zerolength(incoming);
184
185    if (!incoming[0]) {
186        return (0);
187    }
188    if (strchr(incoming, '.')) {
189        fatal_error("Please use integers only. No decimal points.");
190    }
191    mr_asprintf(&tmp, "%s", incoming);
192    i = (int) strlen(tmp);
193    if (tmp[i - 1] == 'B' || tmp[i - 1] == 'b') {
194        tmp[i - 1] = '\0';
195    }
196    for (i = 0; i < (int) strlen(tmp) && isdigit(tmp[i]); i++);
197    ch = tmp[i];
198    tmp[i] = '\0';
199    outval = atol(tmp);
200    mr_free(tmp);
201
202    if (ch == 'g' || ch == 'G') {
203        outval = outval * 1024;
204    } else if (ch == 'k' || ch == 'K') {
205        outval = outval / 1024;
206    } else if (ch == 't' || ch == 'T')  // terabyte
207    {
208        outval *= 1048576;
209    } else if (ch == 'Y' || ch == 'y')  // yottabyte - the biggest measure in the info file
210    {
211        log_it
212            ("Oh my gosh. You actually think a YOTTABYTE will get you anywhere? What're you going to do with 1,208,925,819,614,629,174,706,176 bytes of data?!?!");
213        popup_and_OK
214            (_("That sizespec is more than 1,208,925,819,614,629,174,706,176 bytes. You have a shocking amount of data. Please send a screenshot to the list :-)"));
215        fatal_error("Integer overflow.");
216    } else if (ch != 'm' && ch != 'M') {
217        mr_asprintf(&tmp, "Re: parameter '%s' - bad multiplier ('%c')",
218                 incoming, ch);
219        fatal_error(tmp);
220    }
221    return (outval);
222}
223
224
225/**
226 * Add spaces to the right of @p incoming to make it @p width characters wide.
227 * @param incoming The string to left-pad.
228 * @param width The width to pad it to.
229 * @return The left-padded string.
230 * @note The returned string points to static storage that will be overwritten with each call.
231 * @bug Why does center_string() modify its argument but leftpad_string() returns a modified copy?
232 */
233/* BERLIOS; useless ?
234char *leftpad_string(char *incoming, int width)
235{
236    char *output;
237
238    int i;
239
240    assert(incoming != NULL);
241    assert(width > 2);
242
243    mr_asprintf(&output, "%s", incoming);
244    for (i = (int) strlen(output); i < width; i++) {
245        output[i] = ' ';
246    }
247    output[i] = '\0';
248    return (output);
249}
250*/
251
252
253
254/**
255 * Turn a marker byte (e.g. BLK_START_OF_BACKUP) into a string (e.g. "BLK_START_OF_BACKUP").
256 * Unknown markers are identified as "BLK_UNKNOWN (%d)" where %d is the decimal value.
257 * @param marker The marker byte to stringify.
258 * @return @p marker as a string. this should be freed by the caller
259 * @note The returned string points to static storage that will be overwritten with each call.
260 */
261char *marker_to_string(int marker)
262{
263    /*@ buffer ****************************************************** */
264    char *outstr;
265
266
267    /*@ end vars *************************************************** */
268
269    switch (marker) {
270    case BLK_START_OF_BACKUP:
271        mr_asprintf(&outstr, "%s", "BLK_START_OF_BACKUP");
272        break;
273    case BLK_START_OF_TAPE:
274        mr_asprintf(&outstr, "%s", "BLK_START_OF_TAPE");
275        break;
276    case BLK_START_AN_AFIO_OR_SLICE:
277        mr_asprintf(&outstr, "%s", "BLK_START_AN_AFIO_OR_SLICE");
278        break;
279    case BLK_STOP_AN_AFIO_OR_SLICE:
280        mr_asprintf(&outstr, "%s", "BLK_STOP_AN_AFIO_OR_SLICE");
281        break;
282    case BLK_START_AFIOBALLS:
283        mr_asprintf(&outstr, "%s", "BLK_START_AFIOBALLS");
284        break;
285    case BLK_STOP_AFIOBALLS:
286        mr_asprintf(&outstr, "%s", "BLK_STOP_AFIOBALLS");
287        break;
288    case BLK_STOP_BIGGIEFILES:
289        mr_asprintf(&outstr, "%s", "BLK_STOP_BIGGIEFILES");
290        break;
291    case BLK_START_A_NORMBIGGIE:
292        mr_asprintf(&outstr, "%s", "BLK_START_A_NORMBIGGIE");
293        break;
294    case BLK_START_A_PIHBIGGIE:
295        mr_asprintf(&outstr, "%s", "BLK_START_A_PIHBIGGIE");
296        break;
297    case BLK_START_EXTENDED_ATTRIBUTES:
298        mr_asprintf(&outstr, "%s", "BLK_START_EXTENDED_ATTRIBUTES");
299        break;
300    case BLK_STOP_EXTENDED_ATTRIBUTES:
301        mr_asprintf(&outstr, "%s", "BLK_STOP_EXTENDED_ATTRIBUTES");
302        break;
303    case BLK_START_EXAT_FILE:
304        mr_asprintf(&outstr, "%s", "BLK_START_EXAT_FILE");
305        break;
306    case BLK_STOP_EXAT_FILE:
307        mr_asprintf(&outstr, "%s", "BLK_STOP_EXAT_FILE");
308        break;
309    case BLK_START_BIGGIEFILES:
310        mr_asprintf(&outstr, "%s", "BLK_START_BIGGIEFILES");
311        break;
312    case BLK_STOP_A_BIGGIE:
313        mr_asprintf(&outstr, "%s", "BLK_STOP_A_BIGGIE");
314        break;
315    case BLK_END_OF_TAPE:
316        mr_asprintf(&outstr, "%s", "BLK_END_OF_TAPE");
317        break;
318    case BLK_END_OF_BACKUP:
319        mr_asprintf(&outstr, "%s", "BLK_END_OF_BACKUP");
320        break;
321    case BLK_ABORTED_BACKUP:
322        mr_asprintf(&outstr, "%s", "BLK_ABORTED_BACKUP");
323        break;
324    case BLK_START_FILE:
325        mr_asprintf(&outstr, "%s", "BLK_START_FILE");
326        break;
327    case BLK_STOP_FILE:
328        mr_asprintf(&outstr, "%s", "BLK_STOP_FILE");
329        break;
330    default:
331        mr_asprintf(&outstr, "%s (%d)", "BLK_UNKNOWN", marker);
332        break;
333    }
334    return (outstr);
335}
336
337
338/**
339 * Turn a line from the mountlist into a GUI-friendly string.
340 * The format is as follows: the left-aligned @p device field padded to 24 places,
341 * a space, the left-aligned @p mountpoint field again padded to 24 places, a space,
342 * the left-aligned @p format field padded to 10 places, a space, and the right-aligned
343 * @p size field (in MB) padded to 8 places. The total string length is exactly 69.
344 * @param mountlist The mountlist to operate on.
345 * @param lino The line number in @p mountlist to stringify.
346 * @return The string form of <tt>mountlist</tt>-\>el[<tt>lino</tt>]. To be freed by the caller
347 * @note The returned string points to static storage and will be overwritten with each call.
348 */
349char *mountlist_entry_to_string(struct mountlist_itself *mountlist,
350                                int lino)
351{
352
353    /*@ buffer *********************************************************** */
354    char *output;
355
356    assert(mountlist != NULL);
357
358    mr_asprintf(&output, "%-24s %-24s %-10s %8lld",
359             mountlist->el[lino].device, mountlist->el[lino].mountpoint,
360             mountlist->el[lino].format, mountlist->el[lino].size / 1024);
361    return (output);
362}
363
364
365/**
366 * Generate a friendly string containing "X blah blah disk(s)"
367 * @param noof_disks The number of disks (the X).
368 * @param label The "blah blah" part in the middle. If you leave this blank
369 * there will be a weird double space in the middle, so pass *something*.
370 * @return The string containing "X blah blah disk(s)". To be  freed by the caller
371 * @note The returned string points to static storage and will be overwritten with each call.
372 */
373char *number_of_disks_as_string(int noof_disks, char *label)
374{
375
376    /*@ buffers ********************************************************* */
377    char *output;
378
379    /*@ char     ******************************************************** */
380    char p;
381
382    assert(label != NULL);
383
384    if (noof_disks > 1) {
385        p = 's';
386    } else {
387        p = ' ';
388    }
389    mr_asprintf(&output, "%d %s disk%c    ", noof_disks, label, p);
390    /* BERLIOS: replaced with space^^^^ here !
391       while (strlen(output) < 14) {
392       strcat(output, " ");
393       }
394     */
395    return (output);
396}
397
398/**
399 * Change @p i into a friendly string. If @p i is \<= 10 then write out the
400 * number (e.g. "one", "two", ..., "nine", "ten", "11", ...).
401 * @param i The number to stringify.
402 * @return The string form of @p i. To be freed by caller.
403 * @note The returned value points to static strorage that will be overwritten with each call.
404 */
405char *number_to_text(int i)
406{
407
408    /*@ buffers ***************************************************** */
409    char *output;
410
411
412    /*@ end vars *************************************************** */
413
414    switch (i) {
415    case 0:
416        mr_asprintf(&output, "%s", "zero");
417        break;
418    case 1:
419        mr_asprintf(&output, "%s", "one");
420        break;
421    case 2:
422        mr_asprintf(&output, "%s", "two");
423        break;
424    case 3:
425        mr_asprintf(&output, "%s", "three");
426        break;
427    case 4:
428        mr_asprintf(&output, "%s", "four");
429        break;
430    case 5:
431        mr_asprintf(&output, "%s", "five");
432        break;
433    case 6:
434        mr_asprintf(&output, "%s", "six");
435        break;
436    case 7:
437        mr_asprintf(&output, "%s", "seven");
438        break;
439    case 8:
440        mr_asprintf(&output, "%s", "eight");
441        break;
442    case 9:
443        mr_asprintf(&output, "%s", "nine");
444    case 10:
445        mr_asprintf(&output, "%s", "ten");
446    default:
447        mr_asprintf(&output, "%d", i);
448    }
449    return (output);
450}
451
452
453/**
454 * Replace all occurences of @p token with @p value while copying @p ip to @p output.
455 * @param ip The input string containing zero or more <tt>token</tt>s.
456 * @param output The output string written with the <tt>token</tt>s replaced by @p value.
457 * @param token The token to be relaced with @p value.
458 * @param value The value to replace @p token.
459 */
460void resolve_naff_tokens(char *output, char *ip, char *value, char *token)
461{
462    /*@ buffers *** */
463    char *input;
464
465    /*@ pointers * */
466    char *p;
467
468    assert_string_is_neither_NULL_nor_zerolength(ip);
469    assert_string_is_neither_NULL_nor_zerolength(token);
470    assert(value != NULL);
471
472    strcpy(output, ip);         /* just in case the token doesn't appear in string at all */
473    mr_asprintf(&input, "%s", ip);
474    while (strstr(input, token)) {
475        strcpy(output, input);
476        p = strstr(output, token);
477        *p = '\0';
478        strcat(output, value);
479        p = strstr(input, token) + strlen(token);
480        strcat(output, p);
481        mr_free(input);
482        mr_asprintf(&input, "%s", output);
483    }
484    mr_free(input);
485}
486
487
488/**
489 * Generate the filename of slice @p sliceno of biggiefile @p bigfileno
490 * in @p path with suffix @p s. The format is as follows: @p path, followed
491 * by "/slice-" and @p bigfileno zero-padded to 7 places, followed by
492 * a dot and @p sliceno zero-padded to 5 places, followed by ".dat" and the
493 * suffix. The string is a minimum of 24 characters long.
494 * @param bigfileno The biggiefile number. Starts from 0.
495 * @param sliceno The slice number of biggiefile @p bigfileno. 0 is a "header"
496 * slice (no suffix) containing the biggiestruct, then are the compressed
497 * slices, then an empty uncompressed "trailer" slice.
498 * @param path The path to append (with a / in the middle) to the slice filename.
499 * @param s If not "" then add a "." and this to the end.
500 * @return The slice filename. To be freed by caller
501 * @note The returned value points to static storage and will be overwritten with each call.
502 */
503char *slice_fname(long bigfileno, long sliceno, char *path, char *s)
504{
505
506    /*@ buffers **************************************************** */
507    char *output = NULL;
508    char *suffix = NULL;
509
510    /*@ end vars *************************************************** */
511
512    assert_string_is_neither_NULL_nor_zerolength(path);
513
514    if (s != NULL) {
515        mr_asprintf(&suffix, ".%s", s);
516    } else {
517        mr_asprintf(&suffix, "%s", "");
518    }
519    mr_asprintf(&output, "%s/slice-%07ld.%05ld.dat%s", path, bigfileno,
520             sliceno, suffix);
521    mr_free(suffix);
522    return (output);
523}
524
525
526/**
527 * Generate a spinning symbol based on integer @p i.
528 * The symbol rotates through the characters / - \ | to form an ASCII "spinner"
529 * if successively written to the same location on screen.
530 * @param i The amount of progress or whatever else to use to determine the character
531 * for this iteration of the spinner.
532 * @return The character for this iteration.
533 */
534int special_dot_char(int i)
535{
536    switch (i % 4) {
537    case 0:
538        return ('/');
539    case 1:
540        return ('-');
541    case 2:
542        return ('\\');
543    case 3:
544        return ('|');
545    default:
546        return ('.');
547    }
548    return ('.');
549}
550
551
552/**
553 * Wrap @p flaws_str across three lines. The first two are no more than 74 characters wide.
554 * @param flaws_str The original string to split.
555 * @param flaws_str_A Where to put the first 74-or-less characters.
556 * @param flaws_str_B Where to put the second 74-or-less characters.
557 * @param flaws_str_C Where to put the rest.
558 * @param res The result of the original evaluate_mountlist() operation.
559 * @return TRUE if res == 0, FALSE otherwise.
560 */
561bool
562spread_flaws_across_three_lines(char *flaws_str, char *flaws_str_A,
563                                char *flaws_str_B, char *flaws_str_C,
564                                int res)
565{
566
567    /*@ int ************************************************************* */
568    int i = 0;
569
570    /*@ initialize ****************************************************** */
571    assert(flaws_str != NULL);
572
573    flaws_str_A = NULL;
574    flaws_str_B = NULL;
575    flaws_str_C = NULL;
576
577    if (!res && !strlen(flaws_str)) {
578        return (TRUE);
579    }
580    if (strlen(flaws_str) > 0) {
581        mr_asprintf(&flaws_str_A, "%s", flaws_str + 1);
582    }
583    if (strlen(flaws_str_A) >= 74) {
584        for (i = 74; flaws_str_A[i] != ' '; i--);
585        mr_asprintf(&flaws_str_B, "%s", flaws_str_A + i + 1);
586        flaws_str_A[i] = '\0';
587    }
588    if (strlen(flaws_str_B) >= 74) {
589        for (i = 74; flaws_str_B[i] != ' '; i--);
590        mr_asprintf(&flaws_str_C, "%s", flaws_str_B + i + 1);
591        flaws_str_B[i] = '\0';
592    }
593    if (res) {
594        return (FALSE);
595    } else {
596        return (TRUE);
597    }
598}
599
600
601/**
602 * Compare @p stringA and @p stringB. This uses an ASCII sort for everything
603 * up to the digits on the end but a numerical sort for the digits on the end.
604 * @param stringA The first string to compare.
605 * @param stringB The second string to compare.
606 * @return The same as strcmp() - <0 if A<B, 0 if A=B, >0 if A>B.
607 * @note This function only does a numerical sort on the @e last set of numbers. If
608 * there are any in the middle those will be sorted ASCIIbetically.
609 */
610int strcmp_inc_numbers(char *stringA, char *stringB)
611{
612    /*@ int ********************************************************* */
613    int i;
614    int start_of_numbers_in_A;
615    int start_of_numbers_in_B;
616    int res;
617
618    /*@ long ******************************************************* */
619    long numA;
620    long numB;
621
622    /*@ end vars *************************************************** */
623    assert(stringA != NULL);
624    assert(stringB != NULL);
625
626    if (strlen(stringA) == strlen(stringB)) {
627        return (strcmp(stringA, stringB));
628    }
629    for (i = (int) strlen(stringA); i > 0 && isdigit(stringA[i - 1]); i--);
630    if (i == (int) strlen(stringA)) {
631        return (strcmp(stringA, stringB));
632    }
633    start_of_numbers_in_A = i;
634    for (i = (int) strlen(stringB); i > 0 && isdigit(stringB[i - 1]); i--);
635    if (i == (int) strlen(stringB)) {
636        return (strcmp(stringA, stringB));
637    }
638    start_of_numbers_in_B = i;
639    if (start_of_numbers_in_A != start_of_numbers_in_B) {
640        return (strcmp(stringA, stringB));
641    }
642    res = strncmp(stringA, stringB, (size_t) i);
643    if (res) {
644        return (res);
645    }
646    numA = atol(stringA + start_of_numbers_in_A);
647    numB = atol(stringB + start_of_numbers_in_B);
648    return ((int) (numA - numB));
649}
650
651
652
653/**
654 * Strip excess baggage from @p input, which should be a line from afio.
655 * For now this copies the whole line unless it finds a set of quotes, in which case
656 * it copies their contents only.
657 * @param input The input line (presumably from afio).
658 * @return The stripped line.
659 * @note The returned string points to static storage that will be overwritten with each call.
660 */
661char *strip_afio_output_line(char *input)
662{
663    /*@ buffer ****************************************************** */
664    char *output;
665
666    /*@ pointers **************************************************** */
667    char *p;
668    char *q;
669    /*@ end vars *************************************************** */
670
671    assert(input != NULL);
672    mr_asprintf(&output, "%s", input);
673    p = strchr(input, '\"');
674    if (p) {
675        q = strchr(++p, '\"');
676        if (q) {
677            mr_free(output);
678            mr_asprintf(&output, "%s", p);
679            *(strchr(output, '\"')) = '\0';
680        }
681    }
682    return (output);
683}
684
685
686/**
687 * Remove all characters whose ASCII value is less than or equal to 32
688 * (spaces and control characters) from both sides of @p in_out.
689 * @param in_out The string to strip spaces/control characters from (modified).
690 */
691void strip_spaces(char *in_out)
692{
693    /*@ buffers ***************************************************** */
694    char *tmp;
695    char *tmp1;
696
697    /*@ pointers **************************************************** */
698    char *p;
699
700    /*@ int ******************************************************** */
701    int i;
702    int original_incoming_length;
703
704    /*@ end vars *************************************************** */
705
706    assert(in_out != NULL);
707    original_incoming_length = (int) strlen(in_out);
708    for (i = 0; in_out[i] <= ' ' && i < (int) strlen(in_out); i++);
709    mr_asprintf(&tmp, "%s", in_out + i);
710    for (i = (int) strlen(tmp); i > 0 && tmp[i - 1] <= 32; i--);
711    tmp[i] = '\0';
712    for (i = 0; i < original_incoming_length; i++) {
713        in_out[i] = ' ';
714    }
715    in_out[i] = '\0';
716    i = 0;
717    p = tmp;
718    while (*p != '\0') {
719        in_out[i] = *(p++);
720        in_out[i + 1] = '\0';
721        if (in_out[i] < 32 && i > 0) {
722            if (in_out[i] == 8) {
723                i--;
724            } else if (in_out[i] == 9) {
725                in_out[i++] = ' ';
726            } else if (in_out[i] == '\r') {
727                mr_asprintf(&tmp1, "%s", in_out + i);
728                strcpy(in_out, tmp1);
729                mr_free(tmp1);
730                i = -1;
731                continue;
732            } else if (in_out[i] == '\t') {
733                for (i++; i % 5; i++);
734            } else if (in_out[i] >= 10 && in_out[i] <= 13) {
735                break;
736            } else {
737                i--;
738            }
739        } else {
740            i++;
741        }
742    }
743    in_out[i] = '\0';
744    mr_free(tmp);
745}
746
747
748/**
749 * If there are double quotes "" around @p incoming then remove them.
750 * This does not affect other quotes that may be embedded within the string.
751 * @param incoming The string to trim quotes from (modified).
752 * @return @p outcoming. To be freed by caller
753 */
754char *trim_empty_quotes(char *incoming)
755{
756    /*@ buffer ****************************************************** */
757    char *outgoing;
758
759    /*@ end vars *************************************************** */
760    assert(incoming != NULL);
761
762    if (incoming[0] == '\"' && incoming[strlen(incoming) - 1] == '\"') {
763        mr_asprintf(&outgoing, "%s", incoming + 1);
764        outgoing[strlen(outgoing) - 1] = '\0';
765    } else {
766        mr_asprintf(&outgoing, incoming);
767    }
768    return (outgoing);
769}
770
771
772
773
774/**
775 * Remove any partition info from @p partition, leaving just the drive name.
776 * @param partition The partition name soon-to-become drive name. (modified)
777 * @return @p partition.
778 */
779char *truncate_to_drive_name(char *partition)
780{
781    int i = strlen(partition) - 1;
782    char *c;
783
784#ifdef __FreeBSD__
785
786    if (islower(partition[i]))  // BSD subpartition
787        i--;
788    if (partition[i - 1] == 's') {
789        while (isdigit(partition[i]))
790            i--;
791        i--;
792    }
793    partition[i + 1] = '\0';
794
795#else
796
797    assert_string_is_neither_NULL_nor_zerolength(partition);
798    /* first see if it's a devfs style device */
799    c = strrchr(partition, '/');
800    if (c && strncmp(c, "/part", 5) == 0) {
801        /* yup it's devfs, return the "disc" path */
802        strncpy(c + 1, "disc", (size_t)5);
803        return partition;
804    }
805
806    for (i = strlen(partition); isdigit(partition[i - 1]); i--)
807        continue;
808    if (partition[i - 1] == 'p' && isdigit(partition[i - 2])) {
809        i--;
810    }
811    partition[i] = '\0';
812
813#endif
814
815    return partition;
816}
817
818
819/**
820 * Turn a RAID level number (-1 to 5) into a friendly string. The string
821 * is either "Linear RAID" for -1, or " RAID %-2d " (%d = @p raid_level)
822 * for anything else.
823 * @param raid_level The RAID level to stringify.
824 * @return The string form of @p raid_level. To be freed by caller
825 * @note The returned value points to static storage that will be overwritten with each call.
826 */
827char *turn_raid_level_number_to_string(int raid_level)
828{
829
830    /*@ buffer ********************************************************** */
831    char *output;
832
833    if (raid_level >= 0) {
834        mr_asprintf(&output, " RAID %-2d ", raid_level);
835    } else {
836        mr_asprintf(&output, "Linear RAID");
837    }
838    return (output);
839}
840
841
842/**
843 * Determine the severity (1-3, 1 being low) of the fact that
844 * @p fn changed in the live filesystem (verify/compare).
845 * @param fn The filename that changed.
846 * @param out_reason If non-NULL, a descriptive reason for the difference will be copied here.
847 * @return The severity (1-3).
848 */
849int severity_of_difference(char *fn, char *out_reason)
850{
851    int sev = 0;
852    char *reason;
853    char *filename;
854
855    // out_reason might be null on purpose, so don't bomb if it is :) OK?
856    assert_string_is_neither_NULL_nor_zerolength(fn);
857    if (!strncmp(fn, MNT_RESTORING, strlen(MNT_RESTORING))) {
858        mr_asprintf(&filename, "%s", fn + strlen(MNT_RESTORING));
859    } else if (fn[0] != '/') {
860        mr_asprintf(&filename, "/%s", fn);
861    } else {
862        mr_asprintf(&filename, "%s", fn);
863    }
864
865    if (!strncmp(filename, "/var/", 5)) {
866        sev = 2;
867        mr_asprintf(&reason,
868                 _("/var's contents will change regularly, inevitably."));
869    }
870    if (!strncmp(filename, "/home", 5)) {
871        sev = 2;
872        mr_asprintf(&reason,
873                 _("It's in your /home partiton. Therefore, it is important."));
874    }
875    if (!strncmp(filename, "/usr/", 5)) {
876        sev = 3;
877        mr_asprintf(&reason,
878                 _("You may have installed/removed software during the backup."));
879    }
880    if (!strncmp(filename, "/etc/", 5)) {
881        sev = 3;
882        mr_asprintf(&reason,
883                 _("Do not edit config files while backing up your PC."));
884    }
885    if (!strcmp(filename, "/etc/adjtime")
886        || !strcmp(filename, "/etc/mtab")) {
887        sev = 1;
888        mr_asprintf(&reason, _("This file changes all the time. It's OK."));
889    }
890    if (!strncmp(filename, "/root/", 6)) {
891        sev = 3;
892        mr_asprintf(&reason,
893                 _("Were you compiling/editing something in /root?"));
894    }
895    if (!strncmp(filename, "/root/.", 7)) {
896        sev = 2;
897        mr_asprintf(&reason, _("Temp or 'dot' files changed in /root."));
898    }
899    if (!strncmp(filename, "/var/lib/", 9)) {
900        sev = 2;
901        mr_asprintf(&reason, _("Did you add/remove software during backing?"));
902    }
903    if (!strncmp(filename, "/var/lib/rpm", 12)) {
904        sev = 3;
905        mr_asprintf(&reason, _("Did you add/remove software during backing?"));
906    }
907    if (!strncmp(filename, "/var/lib/slocate", 16)) {
908        sev = 1;
909        mr_asprintf(&reason,
910                 _("The 'update' daemon ran during backup. This does not affect the integrity of your backup."));
911    }
912    if (!strncmp(filename, "/var/log/", 9)
913        || strstr(filename, "/.xsession")
914        || !strcmp(filename + strlen(filename) - 4, ".log")) {
915        sev = 1;
916        mr_asprintf(&reason,
917                 _("Log files change frequently as the computer runs. Fret not."));
918    }
919    if (!strncmp(filename, "/var/spool", 10)) {
920        sev = 1;
921        mr_asprintf(&reason,
922                 _("Background processes or printers were active. This does not affect the integrity of your backup."));
923    }
924    if (!strncmp(filename, "/var/spool/mail", 10)) {
925        sev = 2;
926        mr_asprintf(&reason, _("Mail was sent/received during backup."));
927    }
928    if (filename[strlen(filename) - 1] == '~') {
929        sev = 1;
930        mr_asprintf(&reason,
931                 _("Backup copy of another file which was modified recently."));
932    }
933    if (strstr(filename, "cache")) {
934        sev = 1;
935        mr_asprintf(&reason,
936                 _("Part of a cache of data. Caches change from time to time. Don't worry."));
937    }
938    if (!strncmp(filename, "/var/run/", 9)
939        || !strncmp(filename, "/var/lock", 8)
940        || strstr(filename, "/.DCOPserver") || strstr(filename, "/.MCOP")
941        || strstr(filename, "/.Xauthority")) {
942        sev = 1;
943        mr_asprintf(&reason,
944                 "Temporary file (a lockfile, perhaps) used by software such as X or KDE to register its presence.");
945    }
946    mr_free(filename);
947
948    if (sev == 0) {
949        sev = 3;
950        mr_asprintf(&reason,
951                 _("Changed since backup. Consider running a differential backup in a day or two."));
952    }
953
954    out_reason = reason;
955    return (sev);
956}
957
958
959
960/**
961 * Compare the filenames in two filelist entries (s_filelist_entry*) casted
962 * to void*.
963 * @param va The first filelist entry, cast as a @c void pointer.
964 * @param vb The second filelist entry, cast as a @c void pointer.
965 * @return The return value of strcmp().
966 */
967int compare_two_filelist_entries(void *va, void *vb)
968{
969    static int res;
970    struct s_filelist_entry *fa, *fb;
971
972    assert(va != NULL);
973    assert(vb != NULL);
974    fa = (struct s_filelist_entry *) va;
975    fb = (struct s_filelist_entry *) vb;
976    res = strcmp(fa->filename, fb->filename);
977    return (res);
978}
979
980
981/**
982 * Generate a line intended to be passed to update_evalcall_form(), indicating
983 * the current media fill percentage (or number of kilobytes if size is not known).
984 * @param bkpinfo The backup media structure. Fields used:
985 * - @c bkpinfo->backup_media_type
986 * - @c bkpinfo->media_size
987 * - @c bkpinfo->scratchdir
988 * @return The string indicating media fill. Needs to be freed by caller
989 * @note The returned string points to static storage that will be overwritten with each call.
990 */
991char *percent_media_full_comment(struct s_bkpinfo *bkpinfo)
992{
993    /*@ int *********************************************** */
994    int percentage = 0;
995    int i = 0;
996    int j = 0;
997
998    /*@ buffers ******************************************* */
999    char *outstr = NULL;
1000    char *tmp1 = NULL;
1001    char *tmp2 = NULL;
1002    char *prepstr = NULL;
1003    char *p = NULL;
1004
1005    assert(bkpinfo != NULL);
1006
1007    if (bkpinfo->media_size[g_current_media_number] <= 0) {
1008        mr_asprintf(&outstr, _("Volume %d: %'lld kilobytes archived so far"),
1009                 g_current_media_number, g_tape_posK);
1010
1011        return (outstr);
1012    }
1013
1014/* update screen */
1015    if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) {
1016        percentage =
1017            (int) (g_tape_posK / 10 /
1018                   bkpinfo->media_size[g_current_media_number]);
1019        mr_asprintf(&prepstr, _("Volume %d: ["), g_current_media_number);
1020    } else {
1021        percentage =
1022            (int) (space_occupied_by_cd(bkpinfo->scratchdir) * 100 / 1024 /
1023                   bkpinfo->media_size[g_current_media_number]);
1024        mr_asprintf(&prepstr, "%s %d: [",
1025                 bkpinfo->backup_media_string,
1026                 g_current_media_number);
1027    }
1028    if (percentage > 100) {
1029        percentage = 100;
1030    }
1031    j = trunc(percentage / 5);
1032    tmp1 = (char *) malloc((j + 1) * sizeof(char));
1033    for (i = 0, p = tmp1; i < j; i++, p++) {
1034        *p = '*';
1035    }
1036    *p = '\0';
1037
1038    tmp2 = (char *) malloc((20 - j + 1) * sizeof(char));
1039    for (i = 0, p = tmp2; i < 20 - j; i++, p++) {
1040        *p = '.';
1041    }
1042    *p = '\0';
1043
1044    /* BERLIOS There is a bug here I can't solve for the moment. If you
1045     * replace %% in the format below by 'percent' it just works, but
1046     * like this it creates a huge number. Memory pb somewhere */
1047    /*
1048       log_it("percentage: %d", percentage);
1049       mr_asprintf(&outstr, "%s%s%s] %3d%% used", prepstr, tmp1, tmp2, percentage);
1050     */
1051    mr_asprintf(&outstr, _("%s%s%s] %3d percent used"), prepstr, tmp1, tmp2,
1052             percentage);
1053    mr_free(prepstr);
1054    mr_free(tmp1);
1055    mr_free(tmp2);
1056    return (outstr);
1057}
Note: See TracBrowser for help on using the repository browser.