Initial revision
[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 "utility.h"
26
27 #if 0
28
29 extern char *
30 join_paths(char * buffer, const char * a, const char * b)
31 {
32         int     length = 0;
33
34         if ( a && *a ) {
35                 length = strlen(a);
36                 memcpy(buffer, a, length);
37         }
38         if ( b && *b ) {
39                 if ( length > 0 && buffer[length - 1] != '/' )
40                         buffer[length++] = '/';
41                 if ( *b == '/' )
42                         b++;
43                 strcpy(&buffer[length], b);
44         }
45         return buffer;
46 }
47
48 #endif
49
50
51
52
53
54
55 static  CHUNK * chunkList;
56
57 extern void
58 name_and_error(const char * name)
59 {
60         fprintf(stderr, "%s: %s\n", name, strerror(errno));
61 }
62
63
64
65 /*
66  * Return the standard ls-like mode string from a file mode.
67  * This is static and so is overwritten on each call.
68  */
69 const char *
70 modeString(int mode)
71 {
72         static  char    buf[12];
73
74         strcpy(buf, "----------");
75
76         /*
77          * Fill in the file type.
78          */
79         if (S_ISDIR(mode))
80                 buf[0] = 'd';
81         if (S_ISCHR(mode))
82                 buf[0] = 'c';
83         if (S_ISBLK(mode))
84                 buf[0] = 'b';
85         if (S_ISFIFO(mode))
86                 buf[0] = 'p';
87 #ifdef  S_ISLNK
88         if (S_ISLNK(mode))
89                 buf[0] = 'l';
90 #endif
91 #ifdef  S_ISSOCK
92         if (S_ISSOCK(mode))
93                 buf[0] = 's';
94 #endif
95
96         /*
97          * Now fill in the normal file permissions.
98          */
99         if (mode & S_IRUSR)
100                 buf[1] = 'r';
101         if (mode & S_IWUSR)
102                 buf[2] = 'w';
103         if (mode & S_IXUSR)
104                 buf[3] = 'x';
105         if (mode & S_IRGRP)
106                 buf[4] = 'r';
107         if (mode & S_IWGRP)
108                 buf[5] = 'w';
109         if (mode & S_IXGRP)
110                 buf[6] = 'x';
111         if (mode & S_IROTH)
112                 buf[7] = 'r';
113         if (mode & S_IWOTH)
114                 buf[8] = 'w';
115         if (mode & S_IXOTH)
116                 buf[9] = 'x';
117
118         /*
119          * Finally fill in magic stuff like suid and sticky text.
120          */
121         if (mode & S_ISUID)
122                 buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
123         if (mode & S_ISGID)
124                 buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
125         if (mode & S_ISVTX)
126                 buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
127
128         return buf;
129 }
130
131
132 /*
133  * Get the time string to be used for a file.
134  * This is down to the minute for new files, but only the date for old files.
135  * The string is returned from a static buffer, and so is overwritten for
136  * each call.
137  */
138 const char *
139 timeString(time_t timeVal)
140 {
141         time_t          now;
142         char *          str;
143         static  char    buf[26];
144
145         time(&now);
146
147         str = ctime(&timeVal);
148
149         strcpy(buf, &str[4]);
150         buf[12] = '\0';
151
152         if ((timeVal > now) || (timeVal < now - 365*24*60*60L))
153         {
154                 strcpy(&buf[7], &str[20]);
155                 buf[11] = '\0';
156         }
157
158         return buf;
159 }
160
161
162 /*
163  * Return TRUE if a fileName is a directory.
164  * Nonexistant files return FALSE.
165  */
166 BOOL
167 isDirectory(const char * name)
168 {
169         struct  stat    statBuf;
170
171         if (stat(name, &statBuf) < 0)
172                 return FALSE;
173
174         return S_ISDIR(statBuf.st_mode);
175 }
176
177
178 /*
179  * Return TRUE if a filename is a block or character device.
180  * Nonexistant files return FALSE.
181  */
182 BOOL
183 isDevice(const char * name)
184 {
185         struct  stat    statBuf;
186
187         if (stat(name, &statBuf) < 0)
188                 return FALSE;
189
190         return S_ISBLK(statBuf.st_mode) || S_ISCHR(statBuf.st_mode);
191 }
192
193
194 /*
195  * Copy one file to another, while possibly preserving its modes, times,
196  * and modes.  Returns TRUE if successful, or FALSE on a failure with an
197  * error message output.  (Failure is not indicted if the attributes cannot
198  * be set.)
199  */
200 BOOL
201 copyFile(
202         const char *    srcName,
203         const char *    destName,
204         BOOL            setModes
205 )
206 {
207         int             rfd;
208         int             wfd;
209         int             rcc;
210         char            buf[BUF_SIZE];
211         struct  stat    statBuf1;
212         struct  stat    statBuf2;
213         struct  utimbuf times;
214         
215         if (stat(srcName, &statBuf1) < 0)
216         {
217                 perror(srcName);
218
219                 return FALSE;
220         }
221
222         if (stat(destName, &statBuf2) < 0)
223         {
224                 statBuf2.st_ino = -1;
225                 statBuf2.st_dev = -1;
226         }
227
228         if ((statBuf1.st_dev == statBuf2.st_dev) &&
229                 (statBuf1.st_ino == statBuf2.st_ino))
230         {
231                 fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);
232
233                 return FALSE;
234         }
235
236         rfd = open(srcName, O_RDONLY);
237
238         if (rfd < 0)
239         {
240                 perror(srcName);
241
242                 return FALSE;
243         }
244
245         wfd = creat(destName, statBuf1.st_mode);
246
247         if (wfd < 0)
248         {
249                 perror(destName);
250                 close(rfd);
251
252                 return FALSE;
253         }
254
255         while ((rcc = read(rfd, buf, sizeof(buf))) > 0)
256         {
257                 if (fullWrite(wfd, buf, rcc) < 0)
258                         goto error_exit;
259         }
260
261         if (rcc < 0)
262         {
263                 perror(srcName);
264                 goto error_exit;
265         }
266
267         (void) close(rfd);
268
269         if (close(wfd) < 0)
270         {
271                 perror(destName);
272
273                 return FALSE;
274         }
275
276         if (setModes)
277         {
278                 (void) chmod(destName, statBuf1.st_mode);
279
280                 (void) chown(destName, statBuf1.st_uid, statBuf1.st_gid);
281
282                 times.actime = statBuf1.st_atime;
283                 times.modtime = statBuf1.st_mtime;
284
285                 (void) utime(destName, &times);
286         }
287
288         return TRUE;
289
290
291 error_exit:
292         close(rfd);
293         close(wfd);
294
295         return FALSE;
296 }
297
298
299 /*
300  * Build a path name from the specified directory name and file name.
301  * If the directory name is NULL, then the original fileName is returned.
302  * The built path is in a static area, and is overwritten for each call.
303  */
304 const char *
305 buildName(const char * dirName, const char * fileName)
306 {
307         const char *    cp;
308         static  char    buf[PATH_LEN];
309
310         if ((dirName == NULL) || (*dirName == '\0'))
311                 return fileName;
312
313         cp = strrchr(fileName, '/');
314
315         if (cp)
316                 fileName = cp + 1;
317
318         strcpy(buf, dirName);
319         strcat(buf, "/");
320         strcat(buf, fileName);
321
322         return buf;
323 }
324
325
326
327 /*
328  * Expand the wildcards in a fileName wildcard pattern, if any.
329  * Returns an argument list with matching fileNames in sorted order.
330  * The expanded names are stored in memory chunks which can later all
331  * be freed at once.  The returned list is only valid until the next
332  * call or until the next command.  Returns zero if the name is not a
333  * wildcard, or returns the count of matched files if the name is a
334  * wildcard and there was at least one match, or returns -1 if either
335  * no fileNames matched or there was an allocation error.
336  */
337 int
338 expandWildCards(const char * fileNamePattern, const char *** retFileTable)
339 {
340         const char *    last;
341         const char *    cp1;
342         const char *    cp2;
343         const char *    cp3;
344         char *          str;
345         DIR *           dirp;
346         struct dirent * dp;
347         int             dirLen;
348         int             newFileTableSize;
349         char **         newFileTable;
350         char            dirName[PATH_LEN];
351
352         static int      fileCount;
353         static int      fileTableSize;
354         static char **  fileTable;
355
356         /*
357          * Clear the return values until we know their final values.
358          */
359         fileCount = 0;
360         *retFileTable = NULL;
361
362         /*
363          * Scan the file name pattern for any wildcard characters.
364          */
365         cp1 = strchr(fileNamePattern, '*');
366         cp2 = strchr(fileNamePattern, '?');
367         cp3 = strchr(fileNamePattern, '[');
368
369         /*
370          * If there are no wildcard characters then return zero to
371          * indicate that there was actually no wildcard pattern.
372          */
373         if ((cp1 == NULL) && (cp2 == NULL) && (cp3 == NULL))
374                 return 0;
375
376         /*
377          * There are wildcards in the specified filename.
378          * Get the last component of the file name.
379          */
380         last = strrchr(fileNamePattern, '/');
381
382         if (last)
383                 last++;
384         else
385                 last = fileNamePattern;
386
387         /*
388          * If any wildcards were found before the last filename component
389          * then return an error.
390          */
391         if ((cp1 && (cp1 < last)) || (cp2 && (cp2 < last)) ||
392                 (cp3 && (cp3 < last)))
393         {
394                 fprintf(stderr,
395                 "Wildcards only implemented for last file name component\n");
396
397                 return -1;
398         }
399
400         /*
401          * Assume at first that we are scanning the current directory.
402          */
403         dirName[0] = '.';
404         dirName[1] = '\0';
405
406         /*
407          * If there was a directory given as part of the file name then
408          * copy it and null terminate it.
409          */
410         if (last != fileNamePattern)
411         {
412                 memcpy(dirName, fileNamePattern, last - fileNamePattern);
413                 dirName[last - fileNamePattern - 1] = '\0';
414
415                 if (dirName[0] == '\0')
416                 {
417                         dirName[0] = '/';
418                         dirName[1] = '\0';
419                 }
420         }
421
422         /*
423          * Open the directory containing the files to be checked.
424          */
425         dirp = opendir(dirName);
426
427         if (dirp == NULL)
428         {
429                 perror(dirName);
430
431                 return -1;
432         }
433
434         /*
435          * Prepare the directory name for use in making full path names.
436          */
437         dirLen = strlen(dirName);
438
439         if (last == fileNamePattern)
440         {
441                 dirLen = 0;
442                 dirName[0] = '\0';
443         }
444         else if (dirName[dirLen - 1] != '/')
445         {
446                 dirName[dirLen++] = '/';
447                 dirName[dirLen] = '\0';
448         }
449
450         /*
451          * Find all of the files in the directory and check them against
452          * the wildcard pattern.
453          */
454         while ((dp = readdir(dirp)) != NULL)
455         {
456                 /*
457                  * Skip the current and parent directories.
458                  */
459                 if ((strcmp(dp->d_name, ".") == 0) ||
460                         (strcmp(dp->d_name, "..") == 0))
461                 {
462                         continue;
463                 }
464
465                 /*
466                  * If the file name doesn't match the pattern then skip it.
467                  */
468                 if (!match(dp->d_name, last))
469                         continue;
470
471                 /*
472                  * This file name is selected.
473                  * See if we need to reallocate the file name table.
474                  */
475                 if (fileCount >= fileTableSize)
476                 {
477                         /*
478                          * Increment the file table size and reallocate it.
479                          */
480                         newFileTableSize = fileTableSize + EXPAND_ALLOC;
481
482                         newFileTable = (char **) realloc((char *) fileTable,
483                                 (newFileTableSize * sizeof(char *)));
484
485                         if (newFileTable == NULL)
486                         {
487                                 fprintf(stderr, "Cannot allocate file list\n");
488                                 closedir(dirp);
489
490                                 return -1;
491                         }
492
493                         fileTable = newFileTable;
494                         fileTableSize = newFileTableSize;
495                 }
496
497                 /*
498                  * Allocate space for storing the file name in a chunk.
499                  */
500                 str = getChunk(dirLen + strlen(dp->d_name) + 1);
501
502                 if (str == NULL)
503                 {
504                         fprintf(stderr, "No memory for file name\n");
505                         closedir(dirp);
506
507                         return -1;
508                 }
509
510                 /*
511                  * Save the file name in the chunk.
512                  */
513                 if (dirLen)
514                         memcpy(str, dirName, dirLen);
515
516                 strcpy(str + dirLen, dp->d_name);
517
518                 /*
519                  * Save the allocated file name into the file table.
520                  */
521                 fileTable[fileCount++] = str;
522         }
523
524         /*
525          * Close the directory and check for any matches.
526          */
527         closedir(dirp);
528
529         if (fileCount == 0)
530         {
531                 fprintf(stderr, "No matches\n");
532
533                 return -1;
534         }
535
536         /*
537          * Sort the list of file names.
538          */
539         qsort((void *) fileTable, fileCount, sizeof(char *), nameSort);
540
541         /*
542          * Return the file list and count.
543          */
544         *retFileTable = (const char **) fileTable;
545
546         return fileCount;
547 }
548
549
550 /*
551  * Sort routine for list of fileNames.
552  */
553 int
554 nameSort(const void * p1, const void * p2)
555 {
556         const char **   s1;
557         const char **   s2;
558
559         s1 = (const char **) p1;
560         s2 = (const char **) p2;
561
562         return strcmp(*s1, *s2);
563 }
564
565
566
567 /*
568  * Routine to see if a text string is matched by a wildcard pattern.
569  * Returns TRUE if the text is matched, or FALSE if it is not matched
570  * or if the pattern is invalid.
571  *  *           matches zero or more characters
572  *  ?           matches a single character
573  *  [abc]       matches 'a', 'b' or 'c'
574  *  \c          quotes character c
575  *  Adapted from code written by Ingo Wilken.
576  */
577 BOOL
578 match(const char * text, const char * pattern)
579 {
580         const char *    retryPat;
581         const char *    retryText;
582         int             ch;
583         BOOL            found;
584
585         retryPat = NULL;
586         retryText = NULL;
587
588         while (*text || *pattern)
589         {
590                 ch = *pattern++;
591
592                 switch (ch)
593                 {
594                         case '*':  
595                                 retryPat = pattern;
596                                 retryText = text;
597                                 break;
598
599                         case '[':  
600                                 found = FALSE;
601
602                                 while ((ch = *pattern++) != ']')
603                                 {
604                                         if (ch == '\\')
605                                                 ch = *pattern++;
606
607                                         if (ch == '\0')
608                                                 return FALSE;
609
610                                         if (*text == ch)
611                                                 found = TRUE;
612                                 }
613
614                                 if (!found)
615                                 {
616                                         pattern = retryPat;
617                                         text = ++retryText;
618                                 }
619
620                                 /* fall into next case */
621
622                         case '?':  
623                                 if (*text++ == '\0')
624                                         return FALSE;
625
626                                 break;
627
628                         case '\\':  
629                                 ch = *pattern++;
630
631                                 if (ch == '\0')
632                                         return FALSE;
633
634                                 /* fall into next case */
635
636                         default:        
637                                 if (*text == ch)
638                                 {
639                                         if (*text)
640                                                 text++;
641                                         break;
642                                 }
643
644                                 if (*text)
645                                 {
646                                         pattern = retryPat;
647                                         text = ++retryText;
648                                         break;
649                                 }
650
651                                 return FALSE;
652                 }
653
654                 if (pattern == NULL)
655                         return FALSE;
656         }
657
658         return TRUE;
659 }
660
661
662 /*
663  * Take a command string and break it up into an argc, argv list while
664  * handling quoting and wildcards.  The returned argument list and
665  * strings are in static memory, and so are overwritten on each call.
666  * The argument list is ended with a NULL pointer for convenience.
667  * Returns TRUE if successful, or FALSE on an error with a message
668  * already output.
669  */
670 BOOL
671 makeArgs(const char * cmd, int * retArgc, const char *** retArgv)
672 {
673         const char *            argument;
674         char *                  cp;
675         char *                  cpOut;
676         char *                  newStrings;
677         const char **           fileTable;
678         const char **           newArgTable;
679         int                     newArgTableSize;
680         int                     fileCount;
681         int                     len;
682         int                     ch;
683         int                     quote;
684         BOOL                    quotedWildCards;
685         BOOL                    unquotedWildCards;
686
687         static int              stringsLength;
688         static char *           strings;
689         static int              argCount;
690         static int              argTableSize;
691         static const char **    argTable;
692
693         /*
694          * Clear the returned values until we know them.
695          */
696         argCount = 0;
697         *retArgc = 0;
698         *retArgv = NULL;
699
700         /*
701          * Copy the command string into a buffer that we can modify,
702          * reallocating it if necessary.
703          */
704         len = strlen(cmd) + 1;
705
706         if (len > stringsLength)
707         {
708                 newStrings = realloc(strings, len);
709
710                 if (newStrings == NULL)
711                 {
712                         fprintf(stderr, "Cannot allocate string\n");
713
714                         return FALSE;
715                 }
716
717                 strings = newStrings;
718                 stringsLength = len;
719         }
720
721         memcpy(strings, cmd, len);
722         cp = strings;
723
724         /*
725          * Keep parsing the command string as long as there are any
726          * arguments left.
727          */
728         while (*cp)
729         {
730                 /*
731                  * Save the beginning of this argument.
732                  */
733                 argument = cp;
734                 cpOut = cp;
735
736                 /*
737                  * Reset quoting and wildcarding for this argument.
738                  */
739                 quote = '\0';
740                 quotedWildCards = FALSE;
741                 unquotedWildCards = FALSE;
742
743                 /*
744                  * Loop over the string collecting the next argument while
745                  * looking for quoted strings or quoted characters, and
746                  * remembering whether there are any wildcard characters
747                  * in the argument.
748                  */
749                 while (*cp)
750                 {
751                         ch = *cp++;
752
753                         /*
754                          * If we are not in a quote and we see a blank then
755                          * this argument is done.
756                          */
757                         if (isBlank(ch) && (quote == '\0'))
758                                 break;
759
760                         /*
761                          * If we see a backslash then accept the next
762                          * character no matter what it is.
763                          */
764                         if (ch == '\\')
765                         {
766                                 ch = *cp++;
767
768                                 /*
769                                  * Make sure there is a next character.
770                                  */
771                                 if (ch == '\0')
772                                 {
773                                         fprintf(stderr,
774                                                 "Bad quoted character\n");
775
776                                         return FALSE;
777                                 }
778
779                                 /*
780                                  * Remember whether the quoted character
781                                  * is a wildcard.
782                                  */
783                                 if (isWildCard(ch))
784                                         quotedWildCards = TRUE;
785
786                                 *cpOut++ = ch;
787
788                                 continue;
789                         }
790
791                         /*
792                          * If we see one of the wildcard characters then
793                          * remember whether it was seen inside or outside
794                          * of quotes.
795                          */
796                         if (isWildCard(ch))
797                         {
798                                 if (quote)
799                                         quotedWildCards = TRUE;
800                                 else
801                                         unquotedWildCards = TRUE;
802                         }
803
804                         /*
805                          * If we were in a quote and we saw the same quote
806                          * character again then the quote is done.
807                          */
808                         if (ch == quote)
809                         {
810                                 quote = '\0';
811
812                                 continue;
813                         }
814
815                         /*
816                          * If we weren't in a quote and we see either type
817                          * of quote character, then remember that we are
818                          * now inside of a quote.
819                          */
820                         if ((quote == '\0') && ((ch == '\'') || (ch == '"')))
821                         {
822                                 quote = ch;
823
824                                 continue;
825                         }
826
827                         /*
828                          * Store the character.
829                          */
830                         *cpOut++ = ch;
831                 }
832
833                 /*
834                  * Make sure that quoting is terminated properly.
835                  */
836                 if (quote)
837                 {
838                         fprintf(stderr, "Unmatched quote character\n");
839
840                         return FALSE;
841                 }
842
843                 /*
844                  * Null terminate the argument if it had shrunk, and then
845                  * skip over all blanks to the next argument, nulling them
846                  * out too.
847                  */
848                 if (cp != cpOut)
849                         *cpOut = '\0';
850
851                 while (isBlank(*cp))
852                         *cp++ = '\0';
853
854                 /*
855                  * If both quoted and unquoted wildcards were used then
856                  * complain since we don't handle them properly.
857                  */
858                 if (quotedWildCards && unquotedWildCards)
859                 {
860                         fprintf(stderr,
861                                 "Cannot use quoted and unquoted wildcards\n");
862
863                         return FALSE;
864                 }
865
866                 /*
867                  * Expand the argument into the matching filenames or accept
868                  * it as is depending on whether there were any unquoted
869                  * wildcard characters in it.
870                  */
871                 if (unquotedWildCards)
872                 {
873                         /*
874                          * Expand the argument into the matching filenames.
875                          */
876                         fileCount = expandWildCards(argument, &fileTable);
877
878                         /*
879                          * Return an error if the wildcards failed to match.
880                          */
881                         if (fileCount < 0)
882                                 return FALSE;
883
884                         if (fileCount == 0)
885                         {
886                                 fprintf(stderr, "Wildcard expansion error\n");
887
888                                 return FALSE;
889                         }
890                 }
891                 else
892                 {
893                         /*
894                          * Set up to only store the argument itself.
895                          */
896                         fileTable = &argument;
897                         fileCount = 1;
898                 }
899
900                 /*
901                  * Now reallocate the argument table to hold the file name.
902                  */
903                 if (argCount + fileCount >= argTableSize)
904                 {
905                         newArgTableSize = argCount + fileCount + 1;
906
907                         newArgTable = (const char **) realloc(argTable,
908                                 (sizeof(const char *) * newArgTableSize));
909
910                         if (newArgTable == NULL)
911                         {
912                                 fprintf(stderr, "No memory for arg list\n");
913
914                                 return FALSE;
915                         }
916
917                         argTable = newArgTable;
918                         argTableSize = newArgTableSize;
919                 }
920
921                 /*
922                  * Copy the new arguments to the end of the old ones.
923                  */
924                 memcpy((void *) &argTable[argCount], (const void *) fileTable,
925                         (sizeof(const char **) * fileCount));
926
927                 /*
928                  * Add to the argument count.
929                  */
930                 argCount += fileCount;
931         }
932
933         /*
934          * Null terminate the argument list and return it.
935          */
936         argTable[argCount] = NULL;
937
938         *retArgc = argCount;
939         *retArgv = argTable;
940
941         return TRUE;
942 }
943
944
945 /*
946  * Make a NULL-terminated string out of an argc, argv pair.
947  * Returns TRUE if successful, or FALSE if the string is too long,
948  * with an error message given.  This does not handle spaces within
949  * arguments correctly.
950  */
951 BOOL
952 makeString(
953         int             argc,
954         const char **   argv,
955         char *          buf,
956         int             bufLen
957 )
958 {
959         int     len;
960
961         while (argc-- > 0)
962         {
963                 len = strlen(*argv);
964
965                 if (len >= bufLen)
966                 {
967                         fprintf(stderr, "Argument string too long\n");
968
969                         return FALSE;
970                 }
971
972                 strcpy(buf, *argv++);
973
974                 buf += len;
975                 bufLen -= len;
976
977                 if (argc)
978                         *buf++ = ' ';
979
980                 bufLen--; 
981         }
982
983         *buf = '\0';
984
985         return TRUE;
986 }
987
988
989 /*
990  * Allocate a chunk of memory (like malloc).
991  * The difference, though, is that the memory allocated is put on a
992  * list of chunks which can be freed all at one time.  You CAN NOT free
993  * an individual chunk.
994  */
995 char *
996 getChunk(int size)
997 {
998         CHUNK * chunk;
999
1000         if (size < CHUNK_INIT_SIZE)
1001                 size = CHUNK_INIT_SIZE;
1002
1003         chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNK_INIT_SIZE);
1004
1005         if (chunk == NULL)
1006                 return NULL;
1007
1008         chunk->next = chunkList;
1009         chunkList = chunk;
1010
1011         return chunk->data;
1012 }
1013
1014
1015 /*
1016  * Duplicate a string value using the chunk allocator.
1017  * The returned string cannot be individually freed, but can only be freed
1018  * with other strings when freeChunks is called.  Returns NULL on failure.
1019  */
1020 char *
1021 chunkstrdup(const char * str)
1022 {
1023         int     len;
1024         char *  newStr;
1025
1026         len = strlen(str) + 1;
1027         newStr = getChunk(len);
1028
1029         if (newStr)
1030                 memcpy(newStr, str, len);
1031
1032         return newStr;
1033 }
1034
1035
1036 /*
1037  * Free all chunks of memory that had been allocated since the last
1038  * call to this routine.
1039  */
1040 void
1041 freeChunks(void)
1042 {
1043         CHUNK * chunk;
1044
1045         while (chunkList)
1046         {
1047                 chunk = chunkList;
1048                 chunkList = chunk->next;
1049                 free((char *) chunk);
1050         }
1051 }
1052
1053
1054 /*
1055  * Write all of the supplied buffer out to a file.
1056  * This does multiple writes as necessary.
1057  * Returns the amount written, or -1 on an error.
1058  */
1059 int
1060 fullWrite(int fd, const char * buf, int len)
1061 {
1062         int     cc;
1063         int     total;
1064
1065         total = 0;
1066
1067         while (len > 0)
1068         {
1069                 cc = write(fd, buf, len);
1070
1071                 if (cc < 0)
1072                         return -1;
1073
1074                 buf += cc;
1075                 total+= cc;
1076                 len -= cc;
1077         }
1078
1079         return total;
1080 }
1081
1082
1083 /*
1084  * Read all of the supplied buffer from a file.
1085  * This does multiple reads as necessary.
1086  * Returns the amount read, or -1 on an error.
1087  * A short read is returned on an end of file.
1088  */
1089 int
1090 fullRead(int fd, char * buf, int len)
1091 {
1092         int     cc;
1093         int     total;
1094
1095         total = 0;
1096
1097         while (len > 0)
1098         {
1099                 cc = read(fd, buf, len);
1100
1101                 if (cc < 0)
1102                         return -1;
1103
1104                 if (cc == 0)
1105                         break;
1106
1107                 buf += cc;
1108                 total+= cc;
1109                 len -= cc;
1110         }
1111
1112         return total;
1113 }
1114
1115
1116 /*
1117  * Read all of the supplied buffer from a file.
1118  * This does multiple reads as necessary.
1119  * Returns the amount read, or -1 on an error.
1120  * A short read is returned on an end of file.
1121  */
1122 int
1123 recursive( const char *fileName, BOOL followLinks, const char* pattern, 
1124         int (*fileAction)(const char* fileName, const struct stat* statbuf), 
1125         int (*dirAction)(const char* fileName, const struct stat* statbuf))
1126 {
1127     int             status;
1128     struct stat     statbuf;
1129     struct dirent*  next;
1130
1131     if (followLinks)
1132         status = stat(fileName, &statbuf);
1133     else
1134         status = lstat(fileName, &statbuf);
1135
1136     if (status < 0) {
1137         perror(fileName);
1138         return( -1);
1139     }
1140
1141     if (S_ISREG(statbuf.st_mode)) {
1142         if (match(fileName, pattern)) {
1143             if (fileAction==NULL)
1144                 fprintf( stdout, "%s\n", fileName);
1145             else
1146                 return(fileAction(fileName, &statbuf));
1147         }
1148     }
1149     else if (S_ISDIR(statbuf.st_mode)) {
1150         if (dirAction==NULL) {
1151             DIR *dir;
1152             if (! match(fileName, pattern))
1153                 return 1;
1154             dir = opendir(fileName);
1155             if (!dir) {
1156                 perror(fileName);
1157                 return( -1);
1158             }
1159             while ((next = readdir (dir)) != NULL) {
1160                     status = recursive(fileName, followLinks, pattern, fileAction, dirAction);
1161                     if (status < 0) {
1162                         closedir(dir);
1163                         return(status);
1164                     }
1165             }
1166             status = closedir (dir);
1167             if (status < 0) {
1168                 perror(fileName);
1169                 return( -1);
1170             }
1171         }
1172         else
1173             return(dirAction(fileName, &statbuf));
1174     }
1175     return( 1);
1176
1177 }
1178
1179
1180
1181 /* END CODE */