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