cp.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. cp.c
  5. Abstract:
  6. This module implements the cp (copy) utility.
  7. Author:
  8. Evan Green 3-Jul-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/lib/types.h>
  16. #include <assert.h>
  17. #include <errno.h>
  18. #include <getopt.h>
  19. #include <libgen.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include "swlib.h"
  24. //
  25. // ---------------------------------------------------------------- Definitions
  26. //
  27. #define CP_VERSION_MAJOR 1
  28. #define CP_VERSION_MINOR 0
  29. #define CP_USAGE \
  30. "usage: cp [-fip] source_file target_file\n" \
  31. " cp [-fip] source_file... target\n" \
  32. " cp -R [-H|-L|-P] [-fip] source_file... target\n\n" \
  33. "The cp utility copies one or more files or directories. Options are:\n" \
  34. " -f, --force -- If the file exists and cannot be truncated, attempt \n" \
  35. " to unlink it.\n" \
  36. " -i, --interactive -- Prompt before overwriting any existing file.\n" \
  37. " -p, --preserve -- Preserve file permissions, owners, and access " \
  38. "times.\n" \
  39. " -R, --recursive -- Recursively copy subdirectories of each operand.\n" \
  40. " -r -- Recursive, same as -R.\n" \
  41. " -H -- Follow symbolic links specified in operands only.\n" \
  42. " -L, --dereference -- Follow all symbolic links.\n" \
  43. " -P, --no-dereference -- Do not follow symbolic links.\n" \
  44. " -v, --verbose -- Print files being copied.\n" \
  45. " --help -- Show this help text and exit.\n" \
  46. " --version -- Print the application version information and exit.\n"
  47. #define CP_OPTIONS_STRING "fipRrHLPv"
  48. //
  49. // ------------------------------------------------------ Data Type Definitions
  50. //
  51. //
  52. // ----------------------------------------------- Internal Function Prototypes
  53. //
  54. //
  55. // -------------------------------------------------------------------- Globals
  56. //
  57. struct option CpLongOptions[] = {
  58. {"force", no_argument, 0, 'f'},
  59. {"interactive", no_argument, 0, 'i'},
  60. {"preserve", no_argument, 0, 'p'},
  61. {"recursive", no_argument, 0, 'R'},
  62. {"dereference", no_argument, 0, 'L'},
  63. {"no-dereference", no_argument, 0, 'P'},
  64. {"help", no_argument, 0, 'h'},
  65. {"version", no_argument, 0, 'V'},
  66. {"verbose", no_argument, 0, 'v'},
  67. {NULL, 0, 0, 0},
  68. };
  69. //
  70. // ------------------------------------------------------------------ Functions
  71. //
  72. INT
  73. CpMain (
  74. INT ArgumentCount,
  75. CHAR **Arguments
  76. )
  77. /*++
  78. Routine Description:
  79. This routine is the main entry point for the cp utility.
  80. Arguments:
  81. ArgumentCount - Supplies the number of command line arguments the program
  82. was invoked with.
  83. Arguments - Supplies a tokenized array of command line arguments.
  84. Return Value:
  85. Returns an integer exit code. 0 for success, nonzero otherwise.
  86. --*/
  87. {
  88. PSTR AppendedPath;
  89. ULONG AppendedPathSize;
  90. PSTR Argument;
  91. ULONG ArgumentIndex;
  92. PSTR FirstSource;
  93. INT Option;
  94. ULONG Options;
  95. PSTR SourceBaseName;
  96. ULONG SourceCount;
  97. struct stat Stat;
  98. int Status;
  99. PSTR Target;
  100. BOOL TargetIsDirectory;
  101. int TotalStatus;
  102. FirstSource = NULL;
  103. SourceCount = 0;
  104. Target = NULL;
  105. Options = COPY_OPTION_FOLLOW_OPERAND_LINKS;
  106. TotalStatus = 0;
  107. //
  108. // Process the control arguments.
  109. //
  110. while (TRUE) {
  111. Option = getopt_long(ArgumentCount,
  112. Arguments,
  113. CP_OPTIONS_STRING,
  114. CpLongOptions,
  115. NULL);
  116. if (Option == -1) {
  117. break;
  118. }
  119. if ((Option == '?') || (Option == ':')) {
  120. Status = 1;
  121. goto MainEnd;
  122. }
  123. switch (Option) {
  124. case 'f':
  125. Options |= COPY_OPTION_UNLINK;
  126. Options &= ~COPY_OPTION_INTERACTIVE;
  127. break;
  128. case 'i':
  129. Options |= COPY_OPTION_INTERACTIVE;
  130. break;
  131. case 'p':
  132. Options |= COPY_OPTION_PRESERVE_PERMISSIONS;
  133. break;
  134. case 'R':
  135. case 'r':
  136. Options |= COPY_OPTION_RECURSIVE;
  137. break;
  138. case 'H':
  139. Options |= COPY_OPTION_FOLLOW_OPERAND_LINKS;
  140. Options &= ~COPY_OPTION_FOLLOW_LINKS;
  141. break;
  142. case 'L':
  143. Options |= COPY_OPTION_FOLLOW_LINKS;
  144. Options &= ~COPY_OPTION_FOLLOW_OPERAND_LINKS;
  145. break;
  146. case 'P':
  147. Options &= ~(COPY_OPTION_FOLLOW_OPERAND_LINKS |
  148. COPY_OPTION_FOLLOW_LINKS);
  149. break;
  150. case 'v':
  151. Options |= COPY_OPTION_VERBOSE;
  152. break;
  153. case 'V':
  154. SwPrintVersion(CP_VERSION_MAJOR, CP_VERSION_MINOR);
  155. return 1;
  156. case 'h':
  157. printf(CP_USAGE);
  158. return 1;
  159. default:
  160. assert(FALSE);
  161. Status = 1;
  162. goto MainEnd;
  163. }
  164. }
  165. ArgumentIndex = optind;
  166. if (ArgumentIndex > ArgumentCount) {
  167. ArgumentIndex = ArgumentCount;
  168. }
  169. if (ArgumentIndex < ArgumentCount) {
  170. FirstSource = Arguments[ArgumentIndex];
  171. Target = Arguments[ArgumentCount - 1];
  172. }
  173. SourceCount = ArgumentCount - ArgumentIndex;
  174. //
  175. // Fail if there were not enough arguments.
  176. //
  177. if ((Target == NULL) || (SourceCount <= 1)) {
  178. SwPrintError(0, NULL, "Argument expected. Try --help for usage");
  179. return 1;
  180. }
  181. SourceCount -= 1;
  182. //
  183. // Figure out if the target is a directory.
  184. //
  185. TargetIsDirectory = FALSE;
  186. Status = SwStat(Target, TRUE, &Stat);
  187. if (Status == 0) {
  188. if (S_ISDIR(Stat.st_mode)) {
  189. TargetIsDirectory = TRUE;
  190. }
  191. } else if (Status != ENOENT) {
  192. TotalStatus = Status;
  193. SwPrintError(TotalStatus, Target, "Failed to stat target");
  194. goto MainEnd;
  195. }
  196. //
  197. // If there are only two operands and the target is not a directory and the
  198. // source is not a directory, then just copy the source to the destination
  199. // directly.
  200. //
  201. if ((SourceCount == 1) && (TargetIsDirectory == FALSE)) {
  202. Status = SwStat(FirstSource, TRUE, &Stat);
  203. if (Status != 0) {
  204. SwPrintError(Status, FirstSource, "Cannot stat");
  205. goto MainEnd;
  206. }
  207. if (!S_ISDIR(Stat.st_mode)) {
  208. Status = SwCopy(Options, FirstSource, Target);
  209. goto MainEnd;
  210. }
  211. }
  212. //
  213. // If there's more than one source and the target is not a directory, that's
  214. // a problem.
  215. //
  216. if ((SourceCount > 1) && (TargetIsDirectory == FALSE)) {
  217. TotalStatus = ENOTDIR;
  218. SwPrintError(TotalStatus, Target, "Cannot copy to");
  219. goto MainEnd;
  220. }
  221. //
  222. // Loop through the arguments again and perform the moves.
  223. //
  224. while (ArgumentIndex < ArgumentCount) {
  225. Argument = Arguments[ArgumentIndex];
  226. ArgumentIndex += 1;
  227. //
  228. // Skip the target.
  229. //
  230. if (Argument == Target) {
  231. continue;
  232. }
  233. //
  234. // Get the final name component of the source, because
  235. // "cp mydir/myfile mydir2" results in a destination file of
  236. // "mydir2/myfile", not "mydir2/mydir/myfile".
  237. //
  238. SourceBaseName = basename(Argument);
  239. if (SourceBaseName == NULL) {
  240. TotalStatus = ENOMEM;
  241. continue;
  242. }
  243. //
  244. // Create an appended version of the path if the target is a directory.
  245. //
  246. if (TargetIsDirectory != FALSE) {
  247. Status = SwAppendPath(Target,
  248. strlen(Target) + 1,
  249. SourceBaseName,
  250. strlen(SourceBaseName) + 1,
  251. &AppendedPath,
  252. &AppendedPathSize);
  253. if (Status == FALSE) {
  254. TotalStatus = EINVAL;
  255. continue;
  256. }
  257. Status = SwCopy(Options, Argument, AppendedPath);
  258. free(AppendedPath);
  259. } else {
  260. Status = SwCopy(Options, Argument, Target);
  261. }
  262. if (Status != 0) {
  263. TotalStatus = Status;
  264. }
  265. }
  266. MainEnd:
  267. if ((TotalStatus == 0) && (Status != 0)) {
  268. TotalStatus = Status;
  269. }
  270. return TotalStatus;
  271. }
  272. //
  273. // --------------------------------------------------------- Internal Functions
  274. //