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 <sys/types.h>
31 //#include <sys/stat.h>
32 #include <dirent.h>
33 #include <time.h>
34 #include <utime.h>
35
36 #if 0
37
38 extern char *
39 join_paths(char * buffer, const char * a, const char * b)
40 {
41         int     length = 0;
42
43         if ( a && *a ) {
44                 length = strlen(a);
45                 memcpy(buffer, a, length);
46         }
47         if ( b && *b ) {
48                 if ( length > 0 && buffer[length - 1] != '/' )
49                         buffer[length++] = '/';
50                 if ( *b == '/' )
51                         b++;
52                 strcpy(&buffer[length], b);
53         }
54         return buffer;
55 }
56
57 #endif
58
59
60
61
62
63
64 static  CHUNK * chunkList;
65
66
67 /*
68  * Return the standard ls-like mode string from a file mode.
69  * This is static and so is overwritten on each call.
70  */
71 const char *
72 modeString(int mode)
73 {
74         static  char    buf[12];
75
76         strcpy(buf, "----------");
77
78         /*
79          * Fill in the file type.
80          */
81         if (S_ISDIR(mode))
82                 buf[0] = 'd';
83         if (S_ISCHR(mode))
84                 buf[0] = 'c';
85         if (S_ISBLK(mode))
86                 buf[0] = 'b';
87         if (S_ISFIFO(mode))
88                 buf[0] = 'p';
89 #ifdef  S_ISLNK
90         if (S_ISLNK(mode))
91                 buf[0] = 'l';
92 #endif
93 #ifdef  S_ISSOCK
94         if (S_ISSOCK(mode))
95                 buf[0] = 's';
96 #endif
97
98         /*
99          * Now fill in the normal file permissions.
100          */
101         if (mode & S_IRUSR)
102                 buf[1] = 'r';
103         if (mode & S_IWUSR)
104                 buf[2] = 'w';
105         if (mode & S_IXUSR)
106                 buf[3] = 'x';
107         if (mode & S_IRGRP)
108                 buf[4] = 'r';
109         if (mode & S_IWGRP)
110                 buf[5] = 'w';
111         if (mode & S_IXGRP)
112                 buf[6] = 'x';
113         if (mode & S_IROTH)
114                 buf[7] = 'r';
115         if (mode & S_IWOTH)
116                 buf[8] = 'w';
117         if (mode & S_IXOTH)
118                 buf[9] = 'x';
119
120         /*
121          * Finally fill in magic stuff like suid and sticky text.
122          */
123         if (mode & S_ISUID)
124                 buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
125         if (mode & S_ISGID)
126                 buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
127         if (mode & S_ISVTX)
128                 buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
129
130         return buf;
131 }
132
133
134 /*
135  * Return TRUE if a fileName is a directory.
136  * Nonexistant files return FALSE.
137  */
138 BOOL
139 isDirectory(const char * name)
140 {
141         struct  stat    statBuf;
142
143         if (stat(name, &statBuf) < 0)
144                 return FALSE;
145
146         return S_ISDIR(statBuf.st_mode);
147 }
148
149
150 /*
151  * Return TRUE if a filename is a block or character device.
152  * Nonexistant files return FALSE.
153  */
154 BOOL
155 isDevice(const char * name)
156 {
157         struct  stat    statBuf;
158
159         if (stat(name, &statBuf) < 0)
160                 return FALSE;
161
162         return S_ISBLK(statBuf.st_mode) || S_ISCHR(statBuf.st_mode);
163 }
164
165
166 /*
167  * Copy one file to another, while possibly preserving its modes, times,
168  * and modes.  Returns TRUE if successful, or FALSE on a failure with an
169  * error message output.  (Failure is not indicted if the attributes cannot
170  * be set.)
171  */
172 BOOL
173 copyFile(
174         const char *    srcName,
175         const char *    destName,
176         BOOL            setModes
177 )
178 {
179         int             rfd;
180         int             wfd;
181         int             rcc;
182         char            buf[BUF_SIZE];
183         struct  stat    statBuf1;
184         struct  stat    statBuf2;
185         struct  utimbuf times;
186         
187         if (stat(srcName, &statBuf1) < 0)
188         {
189                 perror(srcName);
190
191                 return FALSE;
192         }
193
194         if (stat(destName, &statBuf2) < 0)
195         {
196                 statBuf2.st_ino = -1;
197                 statBuf2.st_dev = -1;
198         }
199
200         if ((statBuf1.st_dev == statBuf2.st_dev) &&
201                 (statBuf1.st_ino == statBuf2.st_ino))
202         {
203                 fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);
204
205                 return FALSE;
206         }
207
208         rfd = open(srcName, O_RDONLY);
209
210         if (rfd < 0)
211         {
212                 perror(srcName);
213
214                 return FALSE;
215         }
216
217         wfd = creat(destName, statBuf1.st_mode);
218
219         if (wfd < 0)
220         {
221                 perror(destName);
222                 close(rfd);
223
224                 return FALSE;
225         }
226
227         while ((rcc = read(rfd, buf, sizeof(buf))) > 0)
228         {
229                 if (fullWrite(wfd, buf, rcc) < 0)
230                         goto error_exit;
231         }
232
233         if (rcc < 0)
234         {
235                 perror(srcName);
236                 goto error_exit;
237         }
238
239         (void) close(rfd);
240
241         if (close(wfd) < 0)
242         {
243                 perror(destName);
244
245                 return FALSE;
246         }
247
248         if (setModes)
249         {
250                 (void) chmod(destName, statBuf1.st_mode);
251
252                 (void) chown(destName, statBuf1.st_uid, statBuf1.st_gid);
253
254                 times.actime = statBuf1.st_atime;
255                 times.modtime = statBuf1.st_mtime;
256
257                 (void) utime(destName, &times);
258         }
259
260         return TRUE;
261
262
263 error_exit:
264         close(rfd);
265         close(wfd);
266
267         return FALSE;
268 }
269
270
271 /*
272  * Build a path name from the specified directory name and file name.
273  * If the directory name is NULL, then the original fileName is returned.
274  * The built path is in a static area, and is overwritten for each call.
275  */
276 const char *
277 buildName(const char * dirName, const char * fileName)
278 {
279         const char *    cp;
280         static  char    buf[PATH_LEN];
281
282         if ((dirName == NULL) || (*dirName == '\0'))
283                 return fileName;
284
285         cp = strrchr(fileName, '/');
286
287         if (cp)
288                 fileName = cp + 1;
289
290         strcpy(buf, dirName);
291         strcat(buf, "/");
292         strcat(buf, fileName);
293
294         return buf;
295 }
296
297
298
299 /*
300  * Make a NULL-terminated string out of an argc, argv pair.
301  * Returns TRUE if successful, or FALSE if the string is too long,
302  * with an error message given.  This does not handle spaces within
303  * arguments correctly.
304  */
305 BOOL
306 makeString(
307         int             argc,
308         const char **   argv,
309         char *          buf,
310         int             bufLen
311 )
312 {
313         int     len;
314
315         while (argc-- > 0)
316         {
317                 len = strlen(*argv);
318
319                 if (len >= bufLen)
320                 {
321                         fprintf(stderr, "Argument string too long\n");
322
323                         return FALSE;
324                 }
325
326                 strcpy(buf, *argv++);
327
328                 buf += len;
329                 bufLen -= len;
330
331                 if (argc)
332                         *buf++ = ' ';
333
334                 bufLen--; 
335         }
336
337         *buf = '\0';
338
339         return TRUE;
340 }
341
342
343 /*
344  * Allocate a chunk of memory (like malloc).
345  * The difference, though, is that the memory allocated is put on a
346  * list of chunks which can be freed all at one time.  You CAN NOT free
347  * an individual chunk.
348  */
349 char *
350 getChunk(int size)
351 {
352         CHUNK * chunk;
353
354         if (size < CHUNK_INIT_SIZE)
355                 size = CHUNK_INIT_SIZE;
356
357         chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNK_INIT_SIZE);
358
359         if (chunk == NULL)
360                 return NULL;
361
362         chunk->next = chunkList;
363         chunkList = chunk;
364
365         return chunk->data;
366 }
367
368
369 /*
370  * Duplicate a string value using the chunk allocator.
371  * The returned string cannot be individually freed, but can only be freed
372  * with other strings when freeChunks is called.  Returns NULL on failure.
373  */
374 char *
375 chunkstrdup(const char * str)
376 {
377         int     len;
378         char *  newStr;
379
380         len = strlen(str) + 1;
381         newStr = getChunk(len);
382
383         if (newStr)
384                 memcpy(newStr, str, len);
385
386         return newStr;
387 }
388
389
390 /*
391  * Free all chunks of memory that had been allocated since the last
392  * call to this routine.
393  */
394 void
395 freeChunks(void)
396 {
397         CHUNK * chunk;
398
399         while (chunkList)
400         {
401                 chunk = chunkList;
402                 chunkList = chunk->next;
403                 free((char *) chunk);
404         }
405 }
406
407
408 /*
409  * Get the time string to be used for a file.
410  * This is down to the minute for new files, but only the date for old files.
411  * The string is returned from a static buffer, and so is overwritten for
412  * each call.
413  */
414 const char *
415 timeString(time_t timeVal)
416 {
417         time_t          now;
418         char *          str;
419         static  char    buf[26];
420
421         time(&now);
422
423         str = ctime(&timeVal);
424
425         strcpy(buf, &str[4]);
426         buf[12] = '\0';
427
428         if ((timeVal > now) || (timeVal < now - 365*24*60*60L))
429         {
430                 strcpy(&buf[7], &str[20]);
431                 buf[11] = '\0';
432         }
433
434         return buf;
435 }
436
437
438 /*
439  * Routine to see if a text string is matched by a wildcard pattern.
440  * Returns TRUE if the text is matched, or FALSE if it is not matched
441  * or if the pattern is invalid.
442  *  *           matches zero or more characters
443  *  ?           matches a single character
444  *  [abc]       matches 'a', 'b' or 'c'
445  *  \c          quotes character c
446  *  Adapted from code written by Ingo Wilken.
447  */
448 BOOL
449 match(const char * text, const char * pattern)
450 {
451         const char *    retryPat;
452         const char *    retryText;
453         int             ch;
454         BOOL            found;
455
456         retryPat = NULL;
457         retryText = NULL;
458
459         while (*text || *pattern)
460         {
461                 ch = *pattern++;
462
463                 switch (ch)
464                 {
465                         case '*':  
466                                 retryPat = pattern;
467                                 retryText = text;
468                                 break;
469
470                         case '[':  
471                                 found = FALSE;
472
473                                 while ((ch = *pattern++) != ']')
474                                 {
475                                         if (ch == '\\')
476                                                 ch = *pattern++;
477
478                                         if (ch == '\0')
479                                                 return FALSE;
480
481                                         if (*text == ch)
482                                                 found = TRUE;
483                                 }
484
485                                 if (!found)
486                                 {
487                                         pattern = retryPat;
488                                         text = ++retryText;
489                                 }
490
491                                 /* fall into next case */
492
493                         case '?':  
494                                 if (*text++ == '\0')
495                                         return FALSE;
496
497                                 break;
498
499                         case '\\':  
500                                 ch = *pattern++;
501
502                                 if (ch == '\0')
503                                         return FALSE;
504
505                                 /* fall into next case */
506
507                         default:        
508                                 if (*text == ch)
509                                 {
510                                         if (*text)
511                                                 text++;
512                                         break;
513                                 }
514
515                                 if (*text)
516                                 {
517                                         pattern = retryPat;
518                                         text = ++retryText;
519                                         break;
520                                 }
521
522                                 return FALSE;
523                 }
524
525                 if (pattern == NULL)
526                         return FALSE;
527         }
528
529         return TRUE;
530 }
531
532
533 /*
534  * Write all of the supplied buffer out to a file.
535  * This does multiple writes as necessary.
536  * Returns the amount written, or -1 on an error.
537  */
538 int
539 fullWrite(int fd, const char * buf, int len)
540 {
541         int     cc;
542         int     total;
543
544         total = 0;
545
546         while (len > 0)
547         {
548                 cc = write(fd, buf, len);
549
550                 if (cc < 0)
551                         return -1;
552
553                 buf += cc;
554                 total+= cc;
555                 len -= cc;
556         }
557
558         return total;
559 }
560
561
562 /*
563  * Read all of the supplied buffer from a file.
564  * This does multiple reads as necessary.
565  * Returns the amount read, or -1 on an error.
566  * A short read is returned on an end of file.
567  */
568 int
569 fullRead(int fd, char * buf, int len)
570 {
571         int     cc;
572         int     total;
573
574         total = 0;
575
576         while (len > 0)
577         {
578                 cc = read(fd, buf, len);
579
580                 if (cc < 0)
581                         return -1;
582
583                 if (cc == 0)
584                         break;
585
586                 buf += cc;
587                 total+= cc;
588                 len -= cc;
589         }
590
591         return total;
592 }
593
594
595 /*
596  * Walk down all the directories under the specified 
597  * location, and do something (something specified
598  * by the fileAction and dirAction function pointers).
599  */
600 int
601 recursiveAction( const char *fileName, BOOL recurse, BOOL followLinks,
602         int (*fileAction)(const char* fileName), 
603         int (*dirAction)(const char* fileName))
604 {
605     int             status;
606     struct stat     statbuf;
607     struct dirent*  next;
608
609     if (!recurse && S_ISDIR(statbuf.st_mode)) {
610         if (dirAction==NULL)
611             return(TRUE);
612         else
613             return(dirAction(fileName));
614     } else {
615         if (fileAction==NULL)
616             return(TRUE);
617         else
618             return(fileAction(fileName));
619     }
620
621     if (followLinks)
622         status = stat(fileName, &statbuf);
623     else
624         status = lstat(fileName, &statbuf);
625
626     if (status < 0) {
627         perror(fileName);
628         return( FALSE);
629     }
630
631     if (S_ISDIR(statbuf.st_mode)) {
632         DIR *dir;
633         dir = opendir(fileName);
634         if (!dir) {
635             perror(fileName);
636             return(FALSE);
637         }
638         while ((next = readdir (dir)) != NULL) {
639                 char nextFile[NAME_MAX];
640                 if ( (strcmp(next->d_name, "..") == 0) || (strcmp(next->d_name, ".") == 0) ) {
641                     continue;
642                 }
643                 sprintf(nextFile, "%s/%s", fileName, next->d_name);
644                 status = recursiveAction(nextFile, TRUE, followLinks, fileAction, dirAction);
645                 if (status < 0) {
646                     closedir(dir);
647                     return(FALSE);
648                 }
649         }
650         status = closedir (dir);
651         if (status < 0) {
652             perror(fileName);
653             return( FALSE);
654         }
655         if (dirAction==NULL)
656             return(TRUE);
657         else
658             return(dirAction(fileName));
659     }
660     else {
661         if (fileAction==NULL)
662             return(TRUE);
663         else
664             return(fileAction(fileName));
665     }
666 }
667
668
669
670 /* END CODE */
671
672
673
674
675
676
677
678
679
680
681