More stuff
[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(
62          const char *srcName,
63          const char *destName, int setModes, int followLinks)
64 {
65     int rfd;
66     int wfd;
67     int rcc;
68     int result;
69     char buf[BUF_SIZE];
70     struct stat srcStatBuf;
71     struct stat dstStatBuf;
72     struct utimbuf times;
73
74     if (followLinks == FALSE)
75         result = stat(srcName, &srcStatBuf);
76     else 
77         result = lstat(srcName, &srcStatBuf);
78
79     if (result < 0) {
80         perror(srcName);
81         return FALSE;
82     }
83
84     if (followLinks == FALSE)
85         result = stat(destName, &dstStatBuf);
86     else 
87         result = lstat(destName, &dstStatBuf);
88     if (result < 0) {
89         dstStatBuf.st_ino = -1;
90         dstStatBuf.st_dev = -1;
91     }
92
93     if ((srcStatBuf.st_dev == dstStatBuf.st_dev) &&
94         (srcStatBuf.st_ino == dstStatBuf.st_ino)) {
95         fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);
96         return FALSE;
97     }
98
99     if (S_ISDIR(srcStatBuf.st_mode)) {
100         //fprintf(stderr, "copying directory %s to %s\n", srcName, destName);
101         /* Make sure the directory is writable */
102         if (mkdir(destName, 0777777 ^ umask(0))) {
103             perror(destName);
104             return (FALSE);
105         }
106     } else if (S_ISLNK(srcStatBuf.st_mode)) {
107         char *link_val;
108         int link_size;
109
110         //fprintf(stderr, "copying link %s to %s\n", srcName, destName);
111         link_val = (char *) alloca(PATH_MAX + 2);
112         link_size = readlink(srcName, link_val, PATH_MAX + 1);
113         if (link_size < 0) {
114             perror(srcName);
115             return (FALSE);
116         }
117         link_val[link_size] = '\0';
118         if (symlink(link_val, destName)) {
119             perror(destName);
120             return (FALSE);
121         }
122     } else if (S_ISFIFO(srcStatBuf.st_mode)) {
123         //fprintf(stderr, "copying fifo %s to %s\n", srcName, destName);
124         if (mkfifo(destName, 644)) {
125             perror(destName);
126             return (FALSE);
127         }
128     } else if (S_ISBLK(srcStatBuf.st_mode) || S_ISCHR(srcStatBuf.st_mode) 
129             || S_ISSOCK (srcStatBuf.st_mode)) {
130         //fprintf(stderr, "copying soc, blk, or chr %s to %s\n", srcName, destName);
131         if (mknod(destName, srcStatBuf.st_mode, srcStatBuf.st_rdev)) {
132             perror(destName);
133             return (FALSE);
134         }
135     } else if (S_ISREG(srcStatBuf.st_mode)) {
136         //fprintf(stderr, "copying regular file %s to %s\n", srcName, destName);
137         rfd = open(srcName, O_RDONLY);
138         if (rfd < 0) {
139             perror(srcName);
140             return FALSE;
141         }
142
143         wfd = creat(destName, srcStatBuf.st_mode);
144         if (wfd < 0) {
145             perror(destName);
146             close(rfd);
147             return FALSE;
148         }
149
150         while ((rcc = read(rfd, buf, sizeof(buf))) > 0) {
151             if (fullWrite(wfd, buf, rcc) < 0)
152                 goto error_exit;
153         }
154         if (rcc < 0) {
155             goto error_exit;
156         }
157
158         close(rfd);
159         if (close(wfd) < 0) {
160             return FALSE;
161         }
162     }
163
164     if (setModes == TRUE) {
165         //fprintf(stderr, "Setting permissions for %s\n", destName);
166         chmod(destName, srcStatBuf.st_mode);
167         if (followLinks == TRUE)
168             chown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid);
169         else
170             lchown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid);
171
172         times.actime = srcStatBuf.st_atime;
173         times.modtime = srcStatBuf.st_mtime;
174
175         utime(destName, &times);
176     }
177
178     return TRUE;
179
180
181   error_exit:
182     //fprintf(stderr, "choking on %s\n", destName);
183     perror(destName);
184     close(rfd);
185     close(wfd);
186
187     return FALSE;
188 }
189 #endif
190
191
192 #ifdef BB_MV
193 /*
194  * Build a path name from the specified directory name and file name.
195  * If the directory name is NULL, then the original fileName is returned.
196  * The built path is in a static area, and is overwritten for each call.
197  */
198 char *buildName(const char *dirName, const char *fileName)
199 {
200     const char *cp;
201     static char buf[PATH_LEN];
202
203     if ((dirName == NULL) || (*dirName == '\0')) {
204         strcpy(buf, fileName);
205         return buf;
206     }
207
208     cp = strrchr(fileName, '/');
209
210     if (cp)
211         fileName = cp + 1;
212
213     strcpy(buf, dirName);
214     strcat(buf, "/");
215
216     return buf;
217 }
218 #endif
219
220
221
222 /*
223  * Return the standard ls-like mode string from a file mode.
224  * This is static and so is overwritten on each call.
225  */
226 const char *modeString(int mode)
227 {
228     static char buf[12];
229
230     strcpy(buf, "----------");
231
232     /*
233      * Fill in the file type.
234      */
235     if (S_ISDIR(mode))
236         buf[0] = 'd';
237     if (S_ISCHR(mode))
238         buf[0] = 'c';
239     if (S_ISBLK(mode))
240         buf[0] = 'b';
241     if (S_ISFIFO(mode))
242         buf[0] = 'p';
243 #ifdef  S_ISLNK
244     if (S_ISLNK(mode))
245         buf[0] = 'l';
246 #endif
247 #ifdef  S_ISSOCK
248     if (S_ISSOCK(mode))
249         buf[0] = 's';
250 #endif
251
252     /*
253      * Now fill in the normal file permissions.
254      */
255     if (mode & S_IRUSR)
256         buf[1] = 'r';
257     if (mode & S_IWUSR)
258         buf[2] = 'w';
259     if (mode & S_IXUSR)
260         buf[3] = 'x';
261     if (mode & S_IRGRP)
262         buf[4] = 'r';
263     if (mode & S_IWGRP)
264         buf[5] = 'w';
265     if (mode & S_IXGRP)
266         buf[6] = 'x';
267     if (mode & S_IROTH)
268         buf[7] = 'r';
269     if (mode & S_IWOTH)
270         buf[8] = 'w';
271     if (mode & S_IXOTH)
272         buf[9] = 'x';
273
274     /*
275      * Finally fill in magic stuff like suid and sticky text.
276      */
277     if (mode & S_ISUID)
278         buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
279     if (mode & S_ISGID)
280         buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
281     if (mode & S_ISVTX)
282         buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
283
284     return buf;
285 }
286
287
288 #ifdef BB_TAR
289 /*
290  * Get the time string to be used for a file.
291  * This is down to the minute for new files, but only the date for old files.
292  * The string is returned from a static buffer, and so is overwritten for
293  * each call.
294  */
295 const char *timeString(time_t timeVal)
296 {
297     time_t now;
298     char *str;
299     static char buf[26];
300
301     time(&now);
302
303     str = ctime(&timeVal);
304
305     strcpy(buf, &str[4]);
306     buf[12] = '\0';
307
308     if ((timeVal > now) || (timeVal < now - 365 * 24 * 60 * 60L)) {
309         strcpy(&buf[7], &str[20]);
310         buf[11] = '\0';
311     }
312
313     return buf;
314 }
315
316
317 /*
318  * Routine to see if a text string is matched by a wildcard pattern.
319  * Returns TRUE if the text is matched, or FALSE if it is not matched
320  * or if the pattern is invalid.
321  *  *           matches zero or more characters
322  *  ?           matches a single character
323  *  [abc]       matches 'a', 'b' or 'c'
324  *  \c          quotes character c
325  *  Adapted from code written by Ingo Wilken.
326  */
327 int match(const char *text, const char *pattern)
328 {
329     const char *retryPat;
330     const char *retryText;
331     int ch;
332     int found;
333
334     retryPat = NULL;
335     retryText = NULL;
336
337     while (*text || *pattern) {
338         ch = *pattern++;
339
340         switch (ch) {
341         case '*':
342             retryPat = pattern;
343             retryText = text;
344             break;
345
346         case '[':
347             found = FALSE;
348
349             while ((ch = *pattern++) != ']') {
350                 if (ch == '\\')
351                     ch = *pattern++;
352
353                 if (ch == '\0')
354                     return FALSE;
355
356                 if (*text == ch)
357                     found = TRUE;
358             }
359
360             if (!found) {
361                 pattern = retryPat;
362                 text = ++retryText;
363             }
364
365             /* fall into next case */
366
367         case '?':
368             if (*text++ == '\0')
369                 return FALSE;
370
371             break;
372
373         case '\\':
374             ch = *pattern++;
375
376             if (ch == '\0')
377                 return FALSE;
378
379             /* fall into next case */
380
381         default:
382             if (*text == ch) {
383                 if (*text)
384                     text++;
385                 break;
386             }
387
388             if (*text) {
389                 pattern = retryPat;
390                 text = ++retryText;
391                 break;
392             }
393
394             return FALSE;
395         }
396
397         if (pattern == NULL)
398             return FALSE;
399     }
400
401     return TRUE;
402 }
403
404
405 /*
406  * Write all of the supplied buffer out to a file.
407  * This does multiple writes as necessary.
408  * Returns the amount written, or -1 on an error.
409  */
410 int fullWrite(int fd, const char *buf, int len)
411 {
412     int cc;
413     int total;
414
415     total = 0;
416
417     while (len > 0) {
418         cc = write(fd, buf, len);
419
420         if (cc < 0)
421             return -1;
422
423         buf += cc;
424         total += cc;
425         len -= cc;
426     }
427
428     return total;
429 }
430
431
432 /*
433  * Read all of the supplied buffer from a file.
434  * This does multiple reads as necessary.
435  * Returns the amount read, or -1 on an error.
436  * A short read is returned on an end of file.
437  */
438 int fullRead(int fd, char *buf, int len)
439 {
440     int cc;
441     int total;
442
443     total = 0;
444
445     while (len > 0) {
446         cc = read(fd, buf, len);
447
448         if (cc < 0)
449             return -1;
450
451         if (cc == 0)
452             break;
453
454         buf += cc;
455         total += cc;
456         len -= cc;
457     }
458
459     return total;
460 }
461 #endif
462
463
464 #if defined (BB_CHOWN) || defined (BB_CP) || defined (BB_FIND) || defined (BB_LS)
465 /*
466  * Walk down all the directories under the specified 
467  * location, and do something (something specified
468  * by the fileAction and dirAction function pointers).
469  */
470 int
471 recursiveAction(const char *fileName, int recurse, int followLinks,
472                 int (*fileAction) (const char *fileName),
473                 int (*dirAction) (const char *fileName))
474 {
475     int status;
476     struct stat statbuf;
477     struct dirent *next;
478
479     if (followLinks)
480         status = lstat(fileName, &statbuf);
481     else
482         status = stat(fileName, &statbuf);
483     if (status < 0) {
484         perror(fileName);
485         return (FALSE);
486     }
487
488     if (recurse == FALSE) {
489         if (S_ISDIR(statbuf.st_mode)) {
490             if (dirAction == NULL)
491                 return (TRUE);
492             else
493                 return (dirAction(fileName));
494         } else {
495             if (fileAction == NULL)
496                 return (TRUE);
497             else
498                 return (fileAction(fileName));
499         }
500     }
501
502     if (S_ISDIR(statbuf.st_mode)) {
503         DIR *dir;
504         dir = opendir(fileName);
505         if (!dir) {
506             perror(fileName);
507             return (FALSE);
508         }
509         if (dirAction != NULL) {
510             status = dirAction(fileName);
511             if (status == FALSE) {
512                 perror(fileName);
513                 return (FALSE);
514             }
515         }
516         while ((next = readdir(dir)) != NULL) {
517             char nextFile[NAME_MAX];
518             if ((strcmp(next->d_name, "..") == 0)
519                 || (strcmp(next->d_name, ".") == 0)) {
520                 continue;
521             }
522             sprintf(nextFile, "%s/%s", fileName, next->d_name);
523             status =
524                 recursiveAction(nextFile, TRUE, followLinks, fileAction,
525                                 dirAction);
526             if (status < 0) {
527                 closedir(dir);
528                 return (FALSE);
529             }
530         }
531         status = closedir(dir);
532         if (status < 0) {
533             perror(fileName);
534             return (FALSE);
535         }
536     } else {
537         if (fileAction == NULL)
538             return (TRUE);
539         else
540             return (fileAction(fileName));
541     }
542     return (TRUE);
543 }
544
545 #endif
546
547 /* END CODE */