latest and greatest.
[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  * Write all of the supplied buffer out to a file.
410  * This does multiple writes as necessary.
411  * Returns the amount written, or -1 on an error.
412  */
413 int
414 fullWrite(int fd, const char * buf, int len)
415 {
416         int     cc;
417         int     total;
418
419         total = 0;
420
421         while (len > 0)
422         {
423                 cc = write(fd, buf, len);
424
425                 if (cc < 0)
426                         return -1;
427
428                 buf += cc;
429                 total+= cc;
430                 len -= cc;
431         }
432
433         return total;
434 }
435
436
437 /*
438  * Read all of the supplied buffer from a file.
439  * This does multiple reads as necessary.
440  * Returns the amount read, or -1 on an error.
441  * A short read is returned on an end of file.
442  */
443 int
444 fullRead(int fd, char * buf, int len)
445 {
446         int     cc;
447         int     total;
448
449         total = 0;
450
451         while (len > 0)
452         {
453                 cc = read(fd, buf, len);
454
455                 if (cc < 0)
456                         return -1;
457
458                 if (cc == 0)
459                         break;
460
461                 buf += cc;
462                 total+= cc;
463                 len -= cc;
464         }
465
466         return total;
467 }
468
469
470 /*
471  * Walk down all the directories under the specified 
472  * location, and do something (something specified
473  * by the fileAction and dirAction function pointers).
474  */
475 int
476 recursiveAction( const char *fileName, BOOL followLinks,
477         int (*fileAction)(const char* fileName), 
478         int (*dirAction)(const char* fileName))
479 {
480     int             status;
481     struct stat     statbuf;
482     struct dirent*  next;
483
484     if (followLinks)
485         status = stat(fileName, &statbuf);
486     else
487         status = lstat(fileName, &statbuf);
488
489     if (status < 0) {
490         perror(fileName);
491         return( FALSE);
492     }
493
494     if (S_ISDIR(statbuf.st_mode)) {
495         DIR *dir;
496         dir = opendir(fileName);
497         if (!dir) {
498             perror(fileName);
499             return(FALSE);
500         }
501         while ((next = readdir (dir)) != NULL) {
502                 char nextFile[NAME_MAX];
503                 if ( (strcmp(next->d_name, "..") == 0) || (strcmp(next->d_name, ".") == 0) ) {
504                     continue;
505                 }
506                 sprintf(nextFile, "%s/%s", fileName, next->d_name);
507                 status = recursiveAction(nextFile, followLinks, fileAction, dirAction);
508                 if (status < 0) {
509                     closedir(dir);
510                     return(FALSE);
511                 }
512         }
513         status = closedir (dir);
514         if (status < 0) {
515             perror(fileName);
516             return( FALSE);
517         }
518         if (dirAction==NULL)
519             return(TRUE);
520         else
521             return(dirAction(fileName));
522     }
523     else {
524         if (fileAction==NULL)
525             return(TRUE);
526         else
527             return(fileAction(fileName));
528     }
529 }
530
531
532
533 /* END CODE */