Fixed cp -[aR] and some other stuf.
[oweals/busybox.git] / utility.c
1 /*
2  * Utility routines.
3  *
4  * Copyright (C) 1998 by Erik Andersen <andersee@debian.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * Based in part on code from sash, Copyright (c) 1999 by David I. Bell 
21  * Permission has been granted to redistribute this code under the GPL.
22  *
23  */
24
25 #include "internal.h"
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <time.h>
32 #include <utime.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35
36
37
38 #if defined (BB_CP) || defined (BB_MV)
39 /*
40  * Return TRUE if a fileName is a directory.
41  * Nonexistant files return FALSE.
42  */
43 int isDirectory(const char *name)
44 {
45     struct stat statBuf;
46
47     if (stat(name, &statBuf) < 0)
48         return FALSE;
49
50     return S_ISDIR(statBuf.st_mode);
51 }
52
53
54 /*
55  * Copy one file to another, while possibly preserving its modes, times,
56  * and modes.  Returns TRUE if successful, or FALSE on a failure with an
57  * error message output.  (Failure is not indicted if the attributes cannot
58  * be set.)
59  */
60 int
61 copyFile( const char *srcName, const char *destName, 
62          int setModes, int followLinks)
63 {
64     int rfd;
65     int wfd;
66     int rcc;
67     int result;
68     char buf[BUF_SIZE];
69     struct stat srcStatBuf;
70     struct stat dstStatBuf;
71     struct utimbuf times;
72
73     if (followLinks == FALSE)
74         result = stat(srcName, &srcStatBuf);
75     else 
76         result = lstat(srcName, &srcStatBuf);
77     if (result < 0) {
78         perror(srcName);
79         return FALSE;
80     }
81
82     if (followLinks == FALSE)
83         result = stat(destName, &dstStatBuf);
84     else 
85         result = lstat(destName, &dstStatBuf);
86     if (result < 0) {
87         dstStatBuf.st_ino = -1;
88         dstStatBuf.st_dev = -1;
89     }
90
91     if ((srcStatBuf.st_dev == dstStatBuf.st_dev) &&
92         (srcStatBuf.st_ino == dstStatBuf.st_ino)) {
93         fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);
94         return FALSE;
95     }
96
97     if (S_ISDIR(srcStatBuf.st_mode)) {
98         //fprintf(stderr, "copying directory %s to %s\n", srcName, destName);
99         /* Make sure the directory is writable */
100         if (mkdir(destName, 0777777 ^ umask(0))) {
101             perror(destName);
102             return (FALSE);
103         }
104     } else if (S_ISLNK(srcStatBuf.st_mode)) {
105         char *link_val;
106         int link_size;
107
108         //fprintf(stderr, "copying link %s to %s\n", srcName, destName);
109         link_val = (char *) alloca(PATH_MAX + 2);
110         link_size = readlink(srcName, link_val, PATH_MAX + 1);
111         if (link_size < 0) {
112             perror(srcName);
113             return (FALSE);
114         }
115         link_val[link_size] = '\0';
116         link_size = symlink(link_val, destName);
117         if (link_size != 0) {
118             perror(destName);
119             return (FALSE);
120         }
121     } else if (S_ISFIFO(srcStatBuf.st_mode)) {
122         //fprintf(stderr, "copying fifo %s to %s\n", srcName, destName);
123         if (mkfifo(destName, 644)) {
124             perror(destName);
125             return (FALSE);
126         }
127     } else if (S_ISBLK(srcStatBuf.st_mode) || S_ISCHR(srcStatBuf.st_mode) 
128             || S_ISSOCK (srcStatBuf.st_mode)) {
129         //fprintf(stderr, "copying soc, blk, or chr %s to %s\n", srcName, destName);
130         if (mknod(destName, srcStatBuf.st_mode, srcStatBuf.st_rdev)) {
131             perror(destName);
132             return (FALSE);
133         }
134     } else if (S_ISREG(srcStatBuf.st_mode)) {
135         //fprintf(stderr, "copying regular file %s to %s\n", srcName, destName);
136         rfd = open(srcName, O_RDONLY);
137         if (rfd < 0) {
138             perror(srcName);
139             return FALSE;
140         }
141
142         wfd = creat(destName, srcStatBuf.st_mode);
143         if (wfd < 0) {
144             perror(destName);
145             close(rfd);
146             return FALSE;
147         }
148
149         while ((rcc = read(rfd, buf, sizeof(buf))) > 0) {
150             if (fullWrite(wfd, buf, rcc) < 0)
151                 goto error_exit;
152         }
153         if (rcc < 0) {
154             goto error_exit;
155         }
156
157         close(rfd);
158         if (close(wfd) < 0) {
159             return FALSE;
160         }
161     }
162
163     if (setModes == TRUE) {
164         //fprintf(stderr, "Setting permissions for %s\n", destName);
165         chmod(destName, srcStatBuf.st_mode);
166         if (followLinks == TRUE)
167             chown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid);
168         else
169             lchown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid);
170
171         times.actime = srcStatBuf.st_atime;
172         times.modtime = srcStatBuf.st_mtime;
173
174         utime(destName, &times);
175     }
176
177     return TRUE;
178
179
180   error_exit:
181     perror(destName);
182     close(rfd);
183     close(wfd);
184
185     return FALSE;
186 }
187 #endif
188
189
190 #ifdef BB_MV
191 /*
192  * Build a path name from the specified directory name and file name.
193  * If the directory name is NULL, then the original fileName is returned.
194  * The built path is in a static area, and is overwritten for each call.
195  */
196 char *buildName(const char *dirName, const char *fileName)
197 {
198     const char *cp;
199     static char buf[PATH_LEN];
200
201     if ((dirName == NULL) || (*dirName == '\0')) {
202         strcpy(buf, fileName);
203         return buf;
204     }
205
206     cp = strrchr(fileName, '/');
207
208     if (cp)
209         fileName = cp + 1;
210
211     strcpy(buf, dirName);
212     strcat(buf, "/");
213
214     return buf;
215 }
216 #endif
217
218
219
220 /*
221  * Return the standard ls-like mode string from a file mode.
222  * This is static and so is overwritten on each call.
223  */
224 const char *modeString(int mode)
225 {
226     static char buf[12];
227
228     strcpy(buf, "----------");
229
230     /*
231      * Fill in the file type.
232      */
233     if (S_ISDIR(mode))
234         buf[0] = 'd';
235     if (S_ISCHR(mode))
236         buf[0] = 'c';
237     if (S_ISBLK(mode))
238         buf[0] = 'b';
239     if (S_ISFIFO(mode))
240         buf[0] = 'p';
241 #ifdef  S_ISLNK
242     if (S_ISLNK(mode))
243         buf[0] = 'l';
244 #endif
245 #ifdef  S_ISSOCK
246     if (S_ISSOCK(mode))
247         buf[0] = 's';
248 #endif
249
250     /*
251      * Now fill in the normal file permissions.
252      */
253     if (mode & S_IRUSR)
254         buf[1] = 'r';
255     if (mode & S_IWUSR)
256         buf[2] = 'w';
257     if (mode & S_IXUSR)
258         buf[3] = 'x';
259     if (mode & S_IRGRP)
260         buf[4] = 'r';
261     if (mode & S_IWGRP)
262         buf[5] = 'w';
263     if (mode & S_IXGRP)
264         buf[6] = 'x';
265     if (mode & S_IROTH)
266         buf[7] = 'r';
267     if (mode & S_IWOTH)
268         buf[8] = 'w';
269     if (mode & S_IXOTH)
270         buf[9] = 'x';
271
272     /*
273      * Finally fill in magic stuff like suid and sticky text.
274      */
275     if (mode & S_ISUID)
276         buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
277     if (mode & S_ISGID)
278         buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
279     if (mode & S_ISVTX)
280         buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
281
282     return buf;
283 }
284
285
286 #ifdef BB_TAR
287 /*
288  * Get the time string to be used for a file.
289  * This is down to the minute for new files, but only the date for old files.
290  * The string is returned from a static buffer, and so is overwritten for
291  * each call.
292  */
293 const char *timeString(time_t timeVal)
294 {
295     time_t now;
296     char *str;
297     static char buf[26];
298
299     time(&now);
300
301     str = ctime(&timeVal);
302
303     strcpy(buf, &str[4]);
304     buf[12] = '\0';
305
306     if ((timeVal > now) || (timeVal < now - 365 * 24 * 60 * 60L)) {
307         strcpy(&buf[7], &str[20]);
308         buf[11] = '\0';
309     }
310
311     return buf;
312 }
313
314
315 /*
316  * Routine to see if a text string is matched by a wildcard pattern.
317  * Returns TRUE if the text is matched, or FALSE if it is not matched
318  * or if the pattern is invalid.
319  *  *           matches zero or more characters
320  *  ?           matches a single character
321  *  [abc]       matches 'a', 'b' or 'c'
322  *  \c          quotes character c
323  *  Adapted from code written by Ingo Wilken.
324  */
325 int match(const char *text, const char *pattern)
326 {
327     const char *retryPat;
328     const char *retryText;
329     int ch;
330     int found;
331
332     retryPat = NULL;
333     retryText = NULL;
334
335     while (*text || *pattern) {
336         ch = *pattern++;
337
338         switch (ch) {
339         case '*':
340             retryPat = pattern;
341             retryText = text;
342             break;
343
344         case '[':
345             found = FALSE;
346
347             while ((ch = *pattern++) != ']') {
348                 if (ch == '\\')
349                     ch = *pattern++;
350
351                 if (ch == '\0')
352                     return FALSE;
353
354                 if (*text == ch)
355                     found = TRUE;
356             }
357
358             if (!found) {
359                 pattern = retryPat;
360                 text = ++retryText;
361             }
362
363             /* fall into next case */
364
365         case '?':
366             if (*text++ == '\0')
367                 return FALSE;
368
369             break;
370
371         case '\\':
372             ch = *pattern++;
373
374             if (ch == '\0')
375                 return FALSE;
376
377             /* fall into next case */
378
379         default:
380             if (*text == ch) {
381                 if (*text)
382                     text++;
383                 break;
384             }
385
386             if (*text) {
387                 pattern = retryPat;
388                 text = ++retryText;
389                 break;
390             }
391
392             return FALSE;
393         }
394
395         if (pattern == NULL)
396             return FALSE;
397     }
398
399     return TRUE;
400 }
401
402
403 /*
404  * Write all of the supplied buffer out to a file.
405  * This does multiple writes as necessary.
406  * Returns the amount written, or -1 on an error.
407  */
408 int fullWrite(int fd, const char *buf, int len)
409 {
410     int cc;
411     int total;
412
413     total = 0;
414
415     while (len > 0) {
416         cc = write(fd, buf, len);
417
418         if (cc < 0)
419             return -1;
420
421         buf += cc;
422         total += cc;
423         len -= cc;
424     }
425
426     return total;
427 }
428
429
430 /*
431  * Read all of the supplied buffer from a file.
432  * This does multiple reads as necessary.
433  * Returns the amount read, or -1 on an error.
434  * A short read is returned on an end of file.
435  */
436 int fullRead(int fd, char *buf, int len)
437 {
438     int cc;
439     int total;
440
441     total = 0;
442
443     while (len > 0) {
444         cc = read(fd, buf, len);
445
446         if (cc < 0)
447             return -1;
448
449         if (cc == 0)
450             break;
451
452         buf += cc;
453         total += cc;
454         len -= cc;
455     }
456
457     return total;
458 }
459 #endif
460
461
462 #if defined (BB_CHOWN) || defined (BB_CP) || defined (BB_FIND) || defined (BB_LS)
463 /*
464  * Walk down all the directories under the specified 
465  * location, and do something (something specified
466  * by the fileAction and dirAction function pointers).
467  */
468 int
469 recursiveAction(const char *fileName, int recurse, int followLinks,
470                 int (*fileAction) (const char *fileName),
471                 int (*dirAction) (const char *fileName))
472 {
473     int status;
474     struct stat statbuf;
475     struct dirent *next;
476
477     if (followLinks == FALSE)
478         status = stat(fileName, &statbuf);
479     else
480         status = lstat(fileName, &statbuf);
481
482     if (status < 0) {
483         perror(fileName);
484         return (FALSE);
485     }
486
487     if (recurse == FALSE) {
488         if (S_ISDIR(statbuf.st_mode)) {
489             if (dirAction != NULL)
490                 return (dirAction(fileName));
491             else
492                 return (TRUE);
493         } 
494     }
495
496     if (S_ISDIR(statbuf.st_mode)) {
497         DIR *dir;
498         dir = opendir(fileName);
499         if (!dir) {
500             perror(fileName);
501             return (FALSE);
502         }
503         if (dirAction != NULL) {
504             status = dirAction(fileName);
505             if (status == FALSE) {
506                 perror(fileName);
507                 return (FALSE);
508             }
509         }
510         while ((next = readdir(dir)) != NULL) {
511             char nextFile[NAME_MAX];
512             if ((strcmp(next->d_name, "..") == 0)
513                 || (strcmp(next->d_name, ".") == 0)) {
514                 continue;
515             }
516             sprintf(nextFile, "%s/%s", fileName, next->d_name);
517             status =
518                 recursiveAction(nextFile, TRUE, followLinks, fileAction,
519                                 dirAction);
520             if (status < 0) {
521                 closedir(dir);
522                 return (FALSE);
523             }
524         }
525         status = closedir(dir);
526         if (status < 0) {
527             perror(fileName);
528             return (FALSE);
529         }
530     } else {
531         if (fileAction == NULL)
532             return (TRUE);
533         else
534             return (fileAction(fileName));
535     }
536     return (TRUE);
537 }
538
539 #endif
540
541
542
543 #if defined (BB_TAR) || defined (BB_MKDIR)
544 /*
545  * Attempt to create the directories along the specified path, except for
546  * the final component.  The mode is given for the final directory only,
547  * while all previous ones get default protections.  Errors are not reported
548  * here, as failures to restore files can be reported later.
549  */
550 extern void createPath (const char *name, int mode)
551 {
552     char *cp;
553     char *cpOld;
554     char buf[NAME_MAX];
555
556     strcpy (buf, name);
557
558     cp = strchr (buf, '/');
559
560     while (cp) {
561         cpOld = cp;
562         cp = strchr (cp + 1, '/');
563
564         *cpOld = '\0';
565
566         if (mkdir (buf, cp ? 0777 : mode) == 0)
567             printf ("Directory \"%s\" created\n", buf);
568
569         *cpOld = '/';
570     }
571 }
572 #endif
573
574
575
576 #if defined (BB_CHMOD_CHOWN_CHGRP) || defined (BB_MKDIR)
577 /* [ugoa]{+|-|=}[rwxstl] */
578 extern int parse_mode( const char* s, mode_t* theMode)
579 {
580         mode_t or;
581         mode_t and;
582         mode_t  mode = 0;
583         mode_t  groups = S_ISVTX;
584         char    type;
585         char    c;
586
587         do {
588                 for ( ; ; ) {
589                         switch ( c = *s++ ) {
590                         case '\0':
591                                 return (FALSE);
592                         case 'u':
593                                 groups |= S_ISUID|S_IRWXU;
594                                 continue;
595                         case 'g':
596                                 groups |= S_ISGID|S_IRWXG;
597                                 continue;
598                         case 'o':
599                                 groups |= S_IRWXO;
600                                 continue;
601                         case 'a':
602                                 groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
603                                 continue;
604                         case '+':
605                         case '=':
606                         case '-':
607                                 type = c;
608                                 if ( groups == S_ISVTX ) /* The default is "all" */
609                                         groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
610                                 break;
611                         default:
612                                 if ( c >= '0' && c <= '7' && mode == 0 && groups == S_ISVTX ) {
613                                         and = 0;
614                                         or = strtol(--s, 0, 010);
615                                         return (TRUE);
616                                 }
617                                 else
618                                         return (FALSE);
619                         }
620                         break;
621                 }
622
623                 while ( (c = *s++) != '\0' ) {
624                         switch ( c ) {
625                         case ',':
626                                 break;
627                         case 'r':
628                                 mode |= S_IRUSR|S_IRGRP|S_IROTH;
629                                 continue;
630                         case 'w':
631                                 mode |= S_IWUSR|S_IWGRP|S_IWOTH;
632                                 continue;
633                         case 'x':
634                                 mode |= S_IXUSR|S_IXGRP|S_IXOTH;
635                                 continue;
636                         case 's':
637                                 mode |= S_IXGRP|S_ISUID|S_ISGID;
638                                 continue;
639                         case 't':
640                                 mode |= S_ISVTX;
641                                 continue;
642                         default:
643                                 return (FALSE);
644                         }
645                         break;
646                 }
647                 switch ( type ) {
648                 case '=':
649                         and &= ~(groups);
650                         /* fall through */
651                 case '+':
652                         or |= mode & groups;
653                         break;
654                 case '-':
655                         and &= ~(mode & groups);
656                         or &= and;
657                         break;
658                 }
659         } while ( c == ',' );
660         return (TRUE);
661 }
662 #endif
663
664 /* END CODE */