]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
b8e58033788b1fd3f56ef1b45fb238bf1ef10daf
[bacula/bacula] / bacula / src / lib / util.c
1 /*
2  *   util.c  miscellaneous utility subroutines for Bacula
3  * 
4  *    Kern Sibbald, MM
5  *
6  *   Version $Id$
7  */
8
9 /*
10    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of
15    the License, or (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public
23    License along with this program; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"
30 #include "findlib/find.h"
31
32 /*
33  * Various Bacula Utility subroutines
34  *
35  */
36
37 /*
38  * Convert a string to btime_t (64 bit seconds)
39  * Returns 0: if error
40            1: if OK, and value stored in value
41  */
42 int string_to_btime(char *str, btime_t *value)
43 {
44    int i, ch, len;
45    btime_t val;
46    static int  mod[] = {'*', 's', 'm', 'h', 'd', 'w', 'o', 'q', 'y', 0};
47    static int mult[] = {1,    1,  60, 60*60, 60*60*24, 60*60*24*7, 60*60*24*30, 
48                   60*60*24*91, 60*60*24*365};
49
50    /* Look for modifier */
51    len = strlen(str);
52    ch = str[len - 1];
53    i = 0;
54    if (ISALPHA(ch)) {
55       if (ISUPPER(ch)) {
56          ch = tolower(ch);
57       }
58       while (mod[++i] != 0) {
59          if (ch == mod[i]) {
60             len--;
61             str[len] = 0; /* strip modifier */
62             break;
63          }
64       }
65    }
66    if (mod[i] == 0 || !is_a_number(str)) {
67       return 0;
68    }
69    val = (btime_t)strtod(str, NULL);
70    if (errno != 0 || val < 0) {
71       return 0;
72    }
73    *value = val * mult[i];
74    return 1;
75
76 }
77
78 char *edit_btime(btime_t val, char *buf)
79 {
80    char mybuf[30];
81    static int mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
82    static char *mod[]  = {"year",  "month",  "day", "hour", "min"};
83    int i;
84    uint32_t times;
85
86    *buf = 0;
87    for (i=0; i<5; i++) {
88       times = val / mult[i];
89       if (times > 0) {
90          val = val - (btime_t)times * mult[i];
91          sprintf(mybuf, "%d %s%s ", times, mod[i], times>1?"s":"");
92          strcat(buf, mybuf);
93       }
94    }
95    if (val == 0 && strlen(buf) == 0) {     
96       strcat(buf, "0 secs");
97    } else if (val != 0) {
98       sprintf(mybuf, "%d sec%s", (uint32_t)val, val>1?"s":"");
99       strcat(buf, mybuf);
100    }
101    return buf;
102 }
103
104 /*
105  * Check if specified string is a number or not.
106  *  Taken from SQLite, cool, thanks.
107  */
108 int is_a_number(const char *n)
109 {
110    int digit_seen = 0;
111
112    if( *n == '-' || *n == '+' ) {
113       n++;
114    }
115    while (ISDIGIT(*n)) {
116       digit_seen = 1;
117       n++;
118    }
119    if (digit_seen && *n == '.') {
120       n++;
121       while (ISDIGIT(*n)) { n++; }
122    }
123    if (digit_seen && (*n == 'e' || *n == 'E')
124        && (ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && ISDIGIT(n[2])))) {
125       n += 2;                         /* skip e- or e+ or e digit */
126       while (ISDIGIT(*n)) { n++; }
127    }
128    return digit_seen && *n==0;
129 }
130
131
132 /*
133  * Edit an integer number with commas, the supplied buffer
134  * must be at least 27 bytes long.  The incoming number
135  * is always widened to 64 bits.
136  */
137 char *edit_uint64_with_commas(uint64_t val, char *buf)
138 {
139    sprintf(buf, "%" lld, val);
140    return add_commas(buf, buf);
141 }
142
143 /*
144  * Edit an integer number, the supplied buffer
145  * must be at least 27 bytes long.  The incoming number
146  * is always widened to 64 bits.
147  */
148 char *edit_uint64(uint64_t val, char *buf)
149 {
150    sprintf(buf, "%" lld, val);
151    return buf;
152 }
153
154
155 /*
156  * Add commas to a string, which is presumably
157  * a number.  
158  */
159 char *add_commas(char *val, char *buf)
160 {
161    int len, nc;
162    char *p, *q;
163    int i;
164
165    if (val != buf) {
166       strcpy(buf, val);
167    }
168    len = strlen(buf);
169    if (len < 1) {
170       len = 1;
171    }
172    nc = (len - 1) / 3;
173    p = buf+len;
174    q = p + nc;
175    *q-- = *p--;
176    for ( ; nc; nc--) {
177       for (i=0; i < 3; i++) {
178           *q-- = *p--;
179       }
180       *q-- = ',';
181    }   
182    return buf;
183 }
184
185
186 /* Convert a string in place to lower case */
187 void lcase(char *str)
188 {
189    while (*str) {
190       if (ISUPPER(*str))
191          *str = tolower((int)(*str));
192        str++;
193    }
194 }
195
196 /* Convert spaces to non-space character. 
197  * This makes scanf of fields containing spaces easier.
198  */
199 void
200 bash_spaces(char *str)
201 {
202    while (*str) {
203       if (*str == ' ')
204          *str = 0x1;
205       str++;
206    }
207 }
208
209 /* Convert non-space characters (0x1) back into spaces */
210 void
211 unbash_spaces(char *str)
212 {
213    while (*str) {
214      if (*str == 0x1)
215         *str = ' ';
216      str++;
217    }
218 }
219
220 /* Strip any trailing junk from the command */
221 void strip_trailing_junk(char *cmd)
222 {
223    char *p;
224    p = cmd + strlen(cmd) - 1;
225
226    /* strip trailing junk from command */
227    while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
228       *p-- = 0;
229 }
230
231 /* Strip any trailing slashes from a directory path */
232 void strip_trailing_slashes(char *dir)
233 {
234    char *p;
235    p = dir + strlen(dir) - 1;
236
237    /* strip trailing slashes */
238    while ((p >= dir) && (*p == '/'))
239       *p-- = 0;
240 }
241
242 /*
243  * Skip spaces
244  *  Returns: 0 on failure (EOF)             
245  *           1 on success
246  *           new address in passed parameter 
247  */
248 int skip_spaces(char **msg)
249 {
250    char *p = *msg;
251    if (!p) {
252       return 0;
253    }
254    while (*p && *p == ' ') {
255       p++;
256    }
257    *msg = p;
258    return *p ? 1 : 0;
259 }
260
261 /*
262  * Skip nonspaces
263  *  Returns: 0 on failure (EOF)             
264  *           1 on success
265  *           new address in passed parameter 
266  */
267 int skip_nonspaces(char **msg)
268 {
269    char *p = *msg;
270
271    if (!p) {
272       return 0;
273    }
274    while (*p && *p != ' ') {
275       p++;
276    }
277    *msg = p;
278    return *p ? 1 : 0;
279 }
280
281 /* folded search for string - case insensitive */
282 int
283 fstrsch(char *a, char *b)   /* folded case search */
284 {
285    register char *s1,*s2;
286    register char c1, c2;
287
288    s1=a;
289    s2=b;
290    while (*s1) {                      /* do it the fast way */
291       if ((*s1++ | 0x20) != (*s2++ | 0x20))
292          return 0;                    /* failed */
293    }
294    while (*a) {                       /* do it over the correct slow way */
295       if (ISUPPER(c1 = *a)) {
296          c1 = tolower((int)c1);
297       }
298       if (ISUPPER(c2 = *b)) {
299          c2 = tolower((int)c2);
300       }
301       if (c1 != c2) {
302          return 0;
303       }
304       a++;
305       b++;
306    }
307    return 1;
308 }
309
310
311 char *encode_time(time_t time, char *buf)
312 {
313    struct tm tm;
314    int n;
315
316    if (localtime_r(&time, &tm)) {
317       n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
318                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
319                    tm.tm_hour, tm.tm_min, tm.tm_sec);
320    }
321    return buf+n;
322 }
323
324 /***********************************************************************
325  * Encode the mode bits into a 10 character string like LS does
326  ***********************************************************************/
327
328 char *encode_mode(mode_t mode, char *buf)
329 {
330   char *cp = buf;  
331
332   *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
333           S_ISLNK(mode) ? 'l' : '-';
334   *cp++ = mode & S_IRUSR ? 'r' : '-';
335   *cp++ = mode & S_IWUSR ? 'w' : '-';
336   *cp++ = (mode & S_ISUID
337                ? (mode & S_IXUSR ? 's' : 'S')
338                : (mode & S_IXUSR ? 'x' : '-'));
339   *cp++ = mode & S_IRGRP ? 'r' : '-';
340   *cp++ = mode & S_IWGRP ? 'w' : '-';
341   *cp++ = (mode & S_ISGID
342                ? (mode & S_IXGRP ? 's' : 'S')
343                : (mode & S_IXGRP ? 'x' : '-'));
344   *cp++ = mode & S_IROTH ? 'r' : '-';
345   *cp++ = mode & S_IWOTH ? 'w' : '-';
346   *cp++ = (mode & S_ISVTX
347                ? (mode & S_IXOTH ? 't' : 'T')
348                : (mode & S_IXOTH ? 'x' : '-'));
349   *cp = '\0';
350   return cp;
351 }
352
353 #ifdef WORKING
354 extern char *getuser(uid_t uid);
355 extern char *getgroup(gid_t gid);
356
357 void print_ls_output(char *fname, char *lname, int type, struct stat *statp)
358 {
359    char buf[1000]; 
360    char *p, *f;
361    int n;
362
363    p = encode_mode(statp->st_mode, buf);
364    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
365    p += n;
366    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
367    p += n;
368    n = sprintf(p, "%8ld  ", statp->st_size);
369    p += n;
370    p = encode_time(statp->st_ctime, p);
371    *p++ = ' ';
372    *p++ = ' ';
373    for (f=fname; *f; )
374       *p++ = *f++;
375    if (type == FT_LNK) {
376       *p++ = ' ';
377       *p++ = '-';
378       *p++ = '>';
379       *p++ = ' ';
380       /* Copy link name */
381       for (f=lname; *f; )
382          *p++ = *f++;
383    }
384    *p++ = '\n';
385    *p = 0;
386    fputs(buf, stdout);
387 }
388 #endif
389
390 int do_shell_expansion(char *name)
391 {
392 /*  ****FIXME***** this should work for Win32 too */
393 #define UNIX
394 #ifdef UNIX
395 #ifndef PATH_MAX
396 #define PATH_MAX 512
397 #endif
398
399    int pid, wpid, stat;
400    int waitstatus;
401    char *shellcmd;
402    void (*istat)(int), (*qstat)(int);
403    int i;
404    char echout[PATH_MAX + 256];
405    int pfd[2];
406    static char meta[] = "~\\$[]*?`'<>\"";
407    int found = FALSE;
408    int len;
409
410    /* Check if any meta characters are present */
411    len = strlen(meta);
412    for (i = 0; i < len; i++) {
413       if (strchr(name, meta[i])) {
414          found = TRUE;
415          break;
416       }
417    }
418    stat = 0;
419    if (found) {
420 #ifdef nt
421        /* If the filename appears to be a DOS filename,
422           convert all backward slashes \ to Unix path
423           separators / and insert a \ infront of spaces. */
424        len = strlen(name);
425        if (len >= 3 && name[1] == ':' && name[2] == '\\') {
426           for (i=2; i<len; i++)
427              if (name[i] == '\\')
428                 name[i] = '/';
429        }
430 #else
431        /* Pass string off to the shell for interpretation */
432        if (pipe(pfd) == -1)
433           return 0;
434        switch(pid = fork()) {
435        case -1:
436           break;
437
438        case 0:                            /* child */
439           /* look for shell */
440           if ((shellcmd = getenv("SHELL")) == NULL)
441              shellcmd = "/bin/sh";
442           close(1); dup(pfd[1]);          /* attach pipes to stdin and stdout */
443           close(2); dup(pfd[1]);
444           for (i = 3; i < 32; i++)        /* close everything else */
445              close(i);
446           strcpy(echout, "echo ");        /* form echo command */
447           strcat(echout, name);
448           execl(shellcmd, shellcmd, "-c", echout, NULL); /* give to shell */
449           exit(127);                      /* shouldn't get here */
450
451        default:                           /* parent */
452           /* read output from child */
453           i = read(pfd[0], echout, sizeof echout);
454           echout[--i] = 0;                /* set end of string */
455           /* look for first word or first line. */
456           while (--i >= 0) {
457              if (echout[i] == ' ' || echout[i] == '\n')
458                 echout[i] = 0;            /* keep only first one */
459           }
460           istat = signal(SIGINT, SIG_IGN);
461           qstat = signal(SIGQUIT, SIG_IGN);
462           /* wait for child to exit */
463           while ((wpid = wait(&waitstatus)) != pid && wpid != -1)
464              { ; }
465           signal(SIGINT, istat);
466           signal(SIGQUIT, qstat);
467           strcpy(name, echout);
468           stat = 1;
469           break;
470        }
471        close(pfd[0]);                     /* close pipe */
472        close(pfd[1]);
473 #endif /* nt */
474    }
475    return stat;
476
477 #endif /* UNIX */
478
479 #if  MSC | MSDOS | __WATCOMC__
480
481    char prefix[100], *env, *getenv();
482
483    /* Home directory reference? */
484    if (*name == '~' && (env=getenv("HOME"))) {
485       strcpy(prefix, env);            /* copy HOME directory name */
486       name++;                         /* skip over ~ in name */
487       strcat(prefix, name);
488       name--;                         /* get back to beginning */
489       strcpy(name, prefix);           /* move back into name */
490    }
491    return 1;
492 #endif
493
494 }