diff options
author | Eric Andersen <andersen@codepoet.org> | 2000-12-07 00:34:58 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2000-12-07 00:34:58 +0000 |
commit | 3d957c87b7525c3bfefddfc9aa2c24f04995275d (patch) | |
tree | 7e65e9a434acd20aab7654b0a354856813bcf715 | |
parent | 6b78fe383feb80c19af988337ac793ffa126e95b (diff) | |
download | busybox-w32-3d957c87b7525c3bfefddfc9aa2c24f04995275d.tar.gz busybox-w32-3d957c87b7525c3bfefddfc9aa2c24f04995275d.tar.bz2 busybox-w32-3d957c87b7525c3bfefddfc9aa2c24f04995275d.zip |
Patch from Jim Gleason <jimg@lineo.com> to fix tar so it no longer breaks hard
links, and no longer segfault in a certain wierd case.
-rw-r--r-- | Changelog | 2 | ||||
-rw-r--r-- | applets/usage.c | 3 | ||||
-rw-r--r-- | archival/tar.c | 97 | ||||
-rw-r--r-- | tar.c | 97 | ||||
-rw-r--r-- | usage.c | 3 |
5 files changed, 192 insertions, 10 deletions
@@ -43,6 +43,8 @@ | |||
43 | * Larry Doolittle -- \r handled now in echo and tr | 43 | * Larry Doolittle -- \r handled now in echo and tr |
44 | * Matt Kraai -- rewrite of uniq | 44 | * Matt Kraai -- rewrite of uniq |
45 | * Mark Whitley -- remix of xargs | 45 | * Mark Whitley -- remix of xargs |
46 | * Jim Gleason <jimg@lineo.com> -- fixed tar so it no longer breaks | ||
47 | hard links. | ||
46 | 48 | ||
47 | 49 | ||
48 | -Erik Andersen | 50 | -Erik Andersen |
diff --git a/applets/usage.c b/applets/usage.c index bd2321fbc..35d69df42 100644 --- a/applets/usage.c +++ b/applets/usage.c | |||
@@ -1222,8 +1222,7 @@ const char tar_usage[] = | |||
1222 | #endif | 1222 | #endif |
1223 | "[-f tarFile] [FILE(s)] ...\n" | 1223 | "[-f tarFile] [FILE(s)] ...\n" |
1224 | #ifndef BB_FEATURE_TRIVIAL_HELP | 1224 | #ifndef BB_FEATURE_TRIVIAL_HELP |
1225 | "\nCreate, extract, or list files from a tar file. Note that\n" | 1225 | "\nCreate, extract, or list files from a tar file.\n\n" |
1226 | "this version of tar treats hard links as separate files.\n\n" | ||
1227 | "Main operation mode:\n" | 1226 | "Main operation mode:\n" |
1228 | #ifdef BB_FEATURE_TAR_CREATE | 1227 | #ifdef BB_FEATURE_TAR_CREATE |
1229 | "\tc\t\tcreate\n" | 1228 | "\tc\t\tcreate\n" |
diff --git a/archival/tar.c b/archival/tar.c index 7e56fb99a..906fd7eda 100644 --- a/archival/tar.c +++ b/archival/tar.c | |||
@@ -769,6 +769,21 @@ endgame: | |||
769 | 769 | ||
770 | #ifdef BB_FEATURE_TAR_CREATE | 770 | #ifdef BB_FEATURE_TAR_CREATE |
771 | 771 | ||
772 | /* | ||
773 | ** writeTarFile(), writeFileToTarball(), and writeTarHeader() are | ||
774 | ** the only functions that deal with the HardLinkInfo structure. | ||
775 | ** Even these functions use the xxxHardLinkInfo() functions. | ||
776 | */ | ||
777 | typedef struct HardLinkInfo HardLinkInfo; | ||
778 | struct HardLinkInfo | ||
779 | { | ||
780 | HardLinkInfo *next; /* Next entry in list */ | ||
781 | dev_t dev; /* Device number */ | ||
782 | ino_t ino; /* Inode number */ | ||
783 | short linkCount; /* (Hard) Link Count */ | ||
784 | char name[1]; /* Start of filename (must be last) */ | ||
785 | }; | ||
786 | |||
772 | /* Some info to be carried along when creating a new tarball */ | 787 | /* Some info to be carried along when creating a new tarball */ |
773 | struct TarBallInfo | 788 | struct TarBallInfo |
774 | { | 789 | { |
@@ -781,10 +796,62 @@ struct TarBallInfo | |||
781 | to include the tarball into itself */ | 796 | to include the tarball into itself */ |
782 | int verboseFlag; /* Whether to print extra stuff or not */ | 797 | int verboseFlag; /* Whether to print extra stuff or not */ |
783 | char** excludeList; /* List of files to not include */ | 798 | char** excludeList; /* List of files to not include */ |
799 | HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ | ||
800 | HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ | ||
784 | }; | 801 | }; |
785 | typedef struct TarBallInfo TarBallInfo; | 802 | typedef struct TarBallInfo TarBallInfo; |
786 | 803 | ||
787 | 804 | ||
805 | /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ | ||
806 | static void | ||
807 | addHardLinkInfo (HardLinkInfo **hlInfoHeadPtr, dev_t dev, ino_t ino, | ||
808 | short linkCount, const char *name) | ||
809 | { | ||
810 | /* Note: hlInfoHeadPtr can never be NULL! */ | ||
811 | HardLinkInfo *hlInfo; | ||
812 | |||
813 | hlInfo = (HardLinkInfo *)xmalloc(sizeof(HardLinkInfo)+strlen(name)+1); | ||
814 | if (hlInfo) { | ||
815 | hlInfo->next = *hlInfoHeadPtr; | ||
816 | *hlInfoHeadPtr = hlInfo; | ||
817 | hlInfo->dev = dev; | ||
818 | hlInfo->ino = ino; | ||
819 | hlInfo->linkCount = linkCount; | ||
820 | strcpy(hlInfo->name, name); | ||
821 | } | ||
822 | return; | ||
823 | } | ||
824 | |||
825 | static void | ||
826 | freeHardLinkInfo (HardLinkInfo **hlInfoHeadPtr) | ||
827 | { | ||
828 | HardLinkInfo *hlInfo = NULL; | ||
829 | HardLinkInfo *hlInfoNext = NULL; | ||
830 | |||
831 | if (hlInfoHeadPtr) { | ||
832 | hlInfo = *hlInfoHeadPtr; | ||
833 | while (hlInfo) { | ||
834 | hlInfoNext = hlInfo->next; | ||
835 | free(hlInfo); | ||
836 | hlInfo = hlInfoNext; | ||
837 | } | ||
838 | *hlInfoHeadPtr = NULL; | ||
839 | } | ||
840 | return; | ||
841 | } | ||
842 | |||
843 | /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ | ||
844 | static HardLinkInfo * | ||
845 | findHardLinkInfo (HardLinkInfo *hlInfo, dev_t dev, ino_t ino) | ||
846 | { | ||
847 | while(hlInfo) { | ||
848 | if ((ino == hlInfo->ino) && (dev == hlInfo->dev)) | ||
849 | break; | ||
850 | hlInfo = hlInfo->next; | ||
851 | } | ||
852 | return(hlInfo); | ||
853 | } | ||
854 | |||
788 | /* Put an octal string into the specified buffer. | 855 | /* Put an octal string into the specified buffer. |
789 | * The number is zero and space padded and possibly null padded. | 856 | * The number is zero and space padded and possibly null padded. |
790 | * Returns TRUE if successful. */ | 857 | * Returns TRUE if successful. */ |
@@ -879,8 +946,11 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *st | |||
879 | if (! *header.uname) | 946 | if (! *header.uname) |
880 | strcpy(header.uname, "root"); | 947 | strcpy(header.uname, "root"); |
881 | 948 | ||
882 | /* WARNING/NOTICE: I break Hard Links */ | 949 | if (tbInfo->hlInfo) { |
883 | if (S_ISLNK(statbuf->st_mode)) { | 950 | /* This is a hard link */ |
951 | header.typeflag = LNKTYPE; | ||
952 | strncpy(header.linkname, tbInfo->hlInfo->name, sizeof(header.linkname)); | ||
953 | } else if (S_ISLNK(statbuf->st_mode)) { | ||
884 | int link_size=0; | 954 | int link_size=0; |
885 | char buffer[BUFSIZ]; | 955 | char buffer[BUFSIZ]; |
886 | header.typeflag = SYMTYPE; | 956 | header.typeflag = SYMTYPE; |
@@ -948,6 +1018,22 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* | |||
948 | { | 1018 | { |
949 | struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData; | 1019 | struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData; |
950 | 1020 | ||
1021 | /* | ||
1022 | ** Check to see if we are dealing with a hard link. | ||
1023 | ** If so - | ||
1024 | ** Treat the first occurance of a given dev/inode as a file while | ||
1025 | ** treating any additional occurances as hard links. This is done | ||
1026 | ** by adding the file information to the HardLinkInfo linked list. | ||
1027 | */ | ||
1028 | tbInfo->hlInfo = NULL; | ||
1029 | if (statbuf->st_nlink > 1) { | ||
1030 | tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf->st_dev, | ||
1031 | statbuf->st_ino); | ||
1032 | if (tbInfo->hlInfo == NULL) | ||
1033 | addHardLinkInfo (&tbInfo->hlInfoHead, statbuf->st_dev, | ||
1034 | statbuf->st_ino, statbuf->st_nlink, fileName); | ||
1035 | } | ||
1036 | |||
951 | /* It is against the rules to archive a socket */ | 1037 | /* It is against the rules to archive a socket */ |
952 | if (S_ISSOCK(statbuf->st_mode)) { | 1038 | if (S_ISSOCK(statbuf->st_mode)) { |
953 | errorMsg("%s: socket ignored\n", fileName); | 1039 | errorMsg("%s: socket ignored\n", fileName); |
@@ -973,7 +1059,8 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* | |||
973 | } | 1059 | } |
974 | 1060 | ||
975 | /* Now, if the file is a regular file, copy it out to the tarball */ | 1061 | /* Now, if the file is a regular file, copy it out to the tarball */ |
976 | if (S_ISREG(statbuf->st_mode)) { | 1062 | if ((tbInfo->hlInfo == NULL) |
1063 | && (S_ISREG(statbuf->st_mode))) { | ||
977 | int inputFileFd; | 1064 | int inputFileFd; |
978 | char buffer[BUFSIZ]; | 1065 | char buffer[BUFSIZ]; |
979 | ssize_t size=0, readSize=0; | 1066 | ssize_t size=0, readSize=0; |
@@ -1015,6 +1102,7 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv, | |||
1015 | ssize_t size; | 1102 | ssize_t size; |
1016 | struct TarBallInfo tbInfo; | 1103 | struct TarBallInfo tbInfo; |
1017 | tbInfo.verboseFlag = verboseFlag; | 1104 | tbInfo.verboseFlag = verboseFlag; |
1105 | tbInfo.hlInfoHead = NULL; | ||
1018 | 1106 | ||
1019 | /* Make sure there is at least one file to tar up. */ | 1107 | /* Make sure there is at least one file to tar up. */ |
1020 | if (*argv == NULL) | 1108 | if (*argv == NULL) |
@@ -1027,6 +1115,7 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv, | |||
1027 | tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644); | 1115 | tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644); |
1028 | if (tbInfo.tarFd < 0) { | 1116 | if (tbInfo.tarFd < 0) { |
1029 | errorMsg( "Error opening '%s': %s\n", tarName, strerror(errno)); | 1117 | errorMsg( "Error opening '%s': %s\n", tarName, strerror(errno)); |
1118 | freeHardLinkInfo(&tbInfo.hlInfoHead); | ||
1030 | return ( FALSE); | 1119 | return ( FALSE); |
1031 | } | 1120 | } |
1032 | tbInfo.excludeList=excludeList; | 1121 | tbInfo.excludeList=excludeList; |
@@ -1061,8 +1150,10 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv, | |||
1061 | close(tarFd); | 1150 | close(tarFd); |
1062 | if (errorFlag == TRUE) { | 1151 | if (errorFlag == TRUE) { |
1063 | errorMsg("Error exit delayed from previous errors\n"); | 1152 | errorMsg("Error exit delayed from previous errors\n"); |
1153 | freeHardLinkInfo(&tbInfo.hlInfoHead); | ||
1064 | return(FALSE); | 1154 | return(FALSE); |
1065 | } | 1155 | } |
1156 | freeHardLinkInfo(&tbInfo.hlInfoHead); | ||
1066 | return( TRUE); | 1157 | return( TRUE); |
1067 | } | 1158 | } |
1068 | 1159 | ||
@@ -769,6 +769,21 @@ endgame: | |||
769 | 769 | ||
770 | #ifdef BB_FEATURE_TAR_CREATE | 770 | #ifdef BB_FEATURE_TAR_CREATE |
771 | 771 | ||
772 | /* | ||
773 | ** writeTarFile(), writeFileToTarball(), and writeTarHeader() are | ||
774 | ** the only functions that deal with the HardLinkInfo structure. | ||
775 | ** Even these functions use the xxxHardLinkInfo() functions. | ||
776 | */ | ||
777 | typedef struct HardLinkInfo HardLinkInfo; | ||
778 | struct HardLinkInfo | ||
779 | { | ||
780 | HardLinkInfo *next; /* Next entry in list */ | ||
781 | dev_t dev; /* Device number */ | ||
782 | ino_t ino; /* Inode number */ | ||
783 | short linkCount; /* (Hard) Link Count */ | ||
784 | char name[1]; /* Start of filename (must be last) */ | ||
785 | }; | ||
786 | |||
772 | /* Some info to be carried along when creating a new tarball */ | 787 | /* Some info to be carried along when creating a new tarball */ |
773 | struct TarBallInfo | 788 | struct TarBallInfo |
774 | { | 789 | { |
@@ -781,10 +796,62 @@ struct TarBallInfo | |||
781 | to include the tarball into itself */ | 796 | to include the tarball into itself */ |
782 | int verboseFlag; /* Whether to print extra stuff or not */ | 797 | int verboseFlag; /* Whether to print extra stuff or not */ |
783 | char** excludeList; /* List of files to not include */ | 798 | char** excludeList; /* List of files to not include */ |
799 | HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ | ||
800 | HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ | ||
784 | }; | 801 | }; |
785 | typedef struct TarBallInfo TarBallInfo; | 802 | typedef struct TarBallInfo TarBallInfo; |
786 | 803 | ||
787 | 804 | ||
805 | /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ | ||
806 | static void | ||
807 | addHardLinkInfo (HardLinkInfo **hlInfoHeadPtr, dev_t dev, ino_t ino, | ||
808 | short linkCount, const char *name) | ||
809 | { | ||
810 | /* Note: hlInfoHeadPtr can never be NULL! */ | ||
811 | HardLinkInfo *hlInfo; | ||
812 | |||
813 | hlInfo = (HardLinkInfo *)xmalloc(sizeof(HardLinkInfo)+strlen(name)+1); | ||
814 | if (hlInfo) { | ||
815 | hlInfo->next = *hlInfoHeadPtr; | ||
816 | *hlInfoHeadPtr = hlInfo; | ||
817 | hlInfo->dev = dev; | ||
818 | hlInfo->ino = ino; | ||
819 | hlInfo->linkCount = linkCount; | ||
820 | strcpy(hlInfo->name, name); | ||
821 | } | ||
822 | return; | ||
823 | } | ||
824 | |||
825 | static void | ||
826 | freeHardLinkInfo (HardLinkInfo **hlInfoHeadPtr) | ||
827 | { | ||
828 | HardLinkInfo *hlInfo = NULL; | ||
829 | HardLinkInfo *hlInfoNext = NULL; | ||
830 | |||
831 | if (hlInfoHeadPtr) { | ||
832 | hlInfo = *hlInfoHeadPtr; | ||
833 | while (hlInfo) { | ||
834 | hlInfoNext = hlInfo->next; | ||
835 | free(hlInfo); | ||
836 | hlInfo = hlInfoNext; | ||
837 | } | ||
838 | *hlInfoHeadPtr = NULL; | ||
839 | } | ||
840 | return; | ||
841 | } | ||
842 | |||
843 | /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ | ||
844 | static HardLinkInfo * | ||
845 | findHardLinkInfo (HardLinkInfo *hlInfo, dev_t dev, ino_t ino) | ||
846 | { | ||
847 | while(hlInfo) { | ||
848 | if ((ino == hlInfo->ino) && (dev == hlInfo->dev)) | ||
849 | break; | ||
850 | hlInfo = hlInfo->next; | ||
851 | } | ||
852 | return(hlInfo); | ||
853 | } | ||
854 | |||
788 | /* Put an octal string into the specified buffer. | 855 | /* Put an octal string into the specified buffer. |
789 | * The number is zero and space padded and possibly null padded. | 856 | * The number is zero and space padded and possibly null padded. |
790 | * Returns TRUE if successful. */ | 857 | * Returns TRUE if successful. */ |
@@ -879,8 +946,11 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *st | |||
879 | if (! *header.uname) | 946 | if (! *header.uname) |
880 | strcpy(header.uname, "root"); | 947 | strcpy(header.uname, "root"); |
881 | 948 | ||
882 | /* WARNING/NOTICE: I break Hard Links */ | 949 | if (tbInfo->hlInfo) { |
883 | if (S_ISLNK(statbuf->st_mode)) { | 950 | /* This is a hard link */ |
951 | header.typeflag = LNKTYPE; | ||
952 | strncpy(header.linkname, tbInfo->hlInfo->name, sizeof(header.linkname)); | ||
953 | } else if (S_ISLNK(statbuf->st_mode)) { | ||
884 | int link_size=0; | 954 | int link_size=0; |
885 | char buffer[BUFSIZ]; | 955 | char buffer[BUFSIZ]; |
886 | header.typeflag = SYMTYPE; | 956 | header.typeflag = SYMTYPE; |
@@ -948,6 +1018,22 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* | |||
948 | { | 1018 | { |
949 | struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData; | 1019 | struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData; |
950 | 1020 | ||
1021 | /* | ||
1022 | ** Check to see if we are dealing with a hard link. | ||
1023 | ** If so - | ||
1024 | ** Treat the first occurance of a given dev/inode as a file while | ||
1025 | ** treating any additional occurances as hard links. This is done | ||
1026 | ** by adding the file information to the HardLinkInfo linked list. | ||
1027 | */ | ||
1028 | tbInfo->hlInfo = NULL; | ||
1029 | if (statbuf->st_nlink > 1) { | ||
1030 | tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf->st_dev, | ||
1031 | statbuf->st_ino); | ||
1032 | if (tbInfo->hlInfo == NULL) | ||
1033 | addHardLinkInfo (&tbInfo->hlInfoHead, statbuf->st_dev, | ||
1034 | statbuf->st_ino, statbuf->st_nlink, fileName); | ||
1035 | } | ||
1036 | |||
951 | /* It is against the rules to archive a socket */ | 1037 | /* It is against the rules to archive a socket */ |
952 | if (S_ISSOCK(statbuf->st_mode)) { | 1038 | if (S_ISSOCK(statbuf->st_mode)) { |
953 | errorMsg("%s: socket ignored\n", fileName); | 1039 | errorMsg("%s: socket ignored\n", fileName); |
@@ -973,7 +1059,8 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* | |||
973 | } | 1059 | } |
974 | 1060 | ||
975 | /* Now, if the file is a regular file, copy it out to the tarball */ | 1061 | /* Now, if the file is a regular file, copy it out to the tarball */ |
976 | if (S_ISREG(statbuf->st_mode)) { | 1062 | if ((tbInfo->hlInfo == NULL) |
1063 | && (S_ISREG(statbuf->st_mode))) { | ||
977 | int inputFileFd; | 1064 | int inputFileFd; |
978 | char buffer[BUFSIZ]; | 1065 | char buffer[BUFSIZ]; |
979 | ssize_t size=0, readSize=0; | 1066 | ssize_t size=0, readSize=0; |
@@ -1015,6 +1102,7 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv, | |||
1015 | ssize_t size; | 1102 | ssize_t size; |
1016 | struct TarBallInfo tbInfo; | 1103 | struct TarBallInfo tbInfo; |
1017 | tbInfo.verboseFlag = verboseFlag; | 1104 | tbInfo.verboseFlag = verboseFlag; |
1105 | tbInfo.hlInfoHead = NULL; | ||
1018 | 1106 | ||
1019 | /* Make sure there is at least one file to tar up. */ | 1107 | /* Make sure there is at least one file to tar up. */ |
1020 | if (*argv == NULL) | 1108 | if (*argv == NULL) |
@@ -1027,6 +1115,7 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv, | |||
1027 | tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644); | 1115 | tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644); |
1028 | if (tbInfo.tarFd < 0) { | 1116 | if (tbInfo.tarFd < 0) { |
1029 | errorMsg( "Error opening '%s': %s\n", tarName, strerror(errno)); | 1117 | errorMsg( "Error opening '%s': %s\n", tarName, strerror(errno)); |
1118 | freeHardLinkInfo(&tbInfo.hlInfoHead); | ||
1030 | return ( FALSE); | 1119 | return ( FALSE); |
1031 | } | 1120 | } |
1032 | tbInfo.excludeList=excludeList; | 1121 | tbInfo.excludeList=excludeList; |
@@ -1061,8 +1150,10 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv, | |||
1061 | close(tarFd); | 1150 | close(tarFd); |
1062 | if (errorFlag == TRUE) { | 1151 | if (errorFlag == TRUE) { |
1063 | errorMsg("Error exit delayed from previous errors\n"); | 1152 | errorMsg("Error exit delayed from previous errors\n"); |
1153 | freeHardLinkInfo(&tbInfo.hlInfoHead); | ||
1064 | return(FALSE); | 1154 | return(FALSE); |
1065 | } | 1155 | } |
1156 | freeHardLinkInfo(&tbInfo.hlInfoHead); | ||
1066 | return( TRUE); | 1157 | return( TRUE); |
1067 | } | 1158 | } |
1068 | 1159 | ||
@@ -1222,8 +1222,7 @@ const char tar_usage[] = | |||
1222 | #endif | 1222 | #endif |
1223 | "[-f tarFile] [FILE(s)] ...\n" | 1223 | "[-f tarFile] [FILE(s)] ...\n" |
1224 | #ifndef BB_FEATURE_TRIVIAL_HELP | 1224 | #ifndef BB_FEATURE_TRIVIAL_HELP |
1225 | "\nCreate, extract, or list files from a tar file. Note that\n" | 1225 | "\nCreate, extract, or list files from a tar file.\n\n" |
1226 | "this version of tar treats hard links as separate files.\n\n" | ||
1227 | "Main operation mode:\n" | 1226 | "Main operation mode:\n" |
1228 | #ifdef BB_FEATURE_TAR_CREATE | 1227 | #ifdef BB_FEATURE_TAR_CREATE |
1229 | "\tc\t\tcreate\n" | 1228 | "\tc\t\tcreate\n" |