/* * Copyright 2012, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #include #include #include #include #include #include #include #include #include static struct option const kLongOptions[] = { {"help", no_argument, 0, 'h'}, {"force", no_argument, 0, 'f'}, {"cross-file", no_argument, 0, 'x'}, {"verbose", no_argument, 0, 'v'}, {NULL} }; // command flags #define FLAG_VERBOSE 1 #define FLAG_FORCE 2 #define FLAG_DO_NOT_FOLLOW_LINKS 4 extern const char *__progname; static const char *kProgramName = __progname; static const size_t kBufferSize = 1024 * 1024; static uint8 kBuffer[kBufferSize]; status_t moveAttribute(const char* fromFile, const char* fromAttr, const char* toFile, const char* toAttr, uint32 flags) { int fromFileFD = open(fromFile, O_RDONLY); if (fromFileFD < 0) return errno; FileDescriptorCloser fromFileFDCloser(fromFileFD); FileDescriptorCloser toFileFDCloser; int toFileFD = fromFileFD; if (strcmp(fromFile, toFile) != 0) { toFileFD = open(toFile, O_RDONLY); if (toFileFD < 0) return errno; toFileFDCloser.SetTo(toFileFD); } attr_info fromInfo; if (fs_stat_attr(fromFileFD, fromAttr, &fromInfo) != 0) { // TODO: optionally fail if ((flags & FLAG_VERBOSE) != 0) fprintf(stderr, "Warning: file \"%s\" does not have attribute " "\"%s\"\n", fromFile, fromAttr); return B_OK; } attr_info toInfo; if ((flags & FLAG_FORCE) == 0 && fs_stat_attr(toFileFD, toAttr, &toInfo) == 0) return B_FILE_EXISTS; int fromAttrFD = fs_fopen_attr(fromFileFD, fromAttr, fromInfo.type, O_RDONLY | O_EXCL); if (fromAttrFD < 0) return errno; FileDescriptorCloser fromAttrFDCloser(fromAttrFD); int toAttrFD = fs_fopen_attr(toFileFD, toAttr, fromInfo.type, O_CREAT | O_WRONLY | O_TRUNC); if (fromAttrFD < 0) return errno; FileDescriptorCloser toAttrFDCloser(toAttrFD); size_t bytesLeft = fromInfo.size; off_t offset = 0; while (bytesLeft > 0) { size_t size = min_c(kBufferSize, bytesLeft); ssize_t bytesRead = read_pos(fromAttrFD, offset, kBuffer, size); if (bytesRead < 0) { fs_remove_attr(toFileFD, toAttr); return errno; } ssize_t bytesWritten = write_pos(toAttrFD, offset, kBuffer, bytesRead); if (bytesWritten != bytesRead) { fs_remove_attr(toFileFD, toAttr); if (bytesWritten >= 0) return B_IO_ERROR; return errno; } bytesLeft -= bytesRead; offset += bytesRead; } if (fs_remove_attr(fromFileFD, fromAttr) < 0) return errno; return B_OK; } status_t moveAttributes(const char* fromFile, const char* toFile, const char* attrPattern, uint32 flags) { // TODO: implement me (with pattern support) return EXIT_FAILURE; } status_t renameAttribute(const char* fileName, const char* fromAttr, const char* toAttr, uint32 flags) { if ((flags & FLAG_VERBOSE) != 0) printf("Rename attribute: %s\n", fileName); status_t status = moveAttribute(fileName, fromAttr, fileName, toAttr, flags); if (status != B_OK) { fprintf(stderr, "%s: Could not rename attribute for file \"%s\": %s\n", kProgramName, fileName, strerror(status)); } return status; } void usage(int returnValue) { fprintf(stderr, "usage: %s [-fPv] attr-from attr-to file1 [file2...]\n" " or: %s -x[fPv] from-file to-file\n\n" "\t-f|--force Overwrite existing attributes\n" "\t-P Don't resolve links\n" "\t-x|--cross-file Moves the attributes to another file\n" "\t-v|--verbose Verbose output\n", kProgramName, kProgramName); exit(returnValue); } int main(int argc, char** argv) { uint32 flags = 0; bool crossFile = false; int c; while ((c = getopt_long(argc, argv, "hfxPv", kLongOptions, NULL)) != -1) { switch (c) { case 0: break; case 'f': flags |= FLAG_FORCE; break; case 'x': crossFile = true; break; case 'P': flags |= FLAG_DO_NOT_FOLLOW_LINKS; break; case 'v': flags |= FLAG_VERBOSE; break; case 'h': usage(EXIT_SUCCESS); break; default: usage(EXIT_FAILURE); break; } } if (argc - optind < 3) usage(EXIT_FAILURE); if (crossFile) { const char* attrPattern = argv[optind++]; const char* fromFile = argv[optind++]; const char* toFile = argv[optind++]; return moveAttributes(fromFile, toFile, attrPattern, flags); } const char* fromAttr = argv[optind++]; const char* toAttr = argv[optind++]; int returnCode = EXIT_SUCCESS; for (int i = optind; i < argc; i++) { if (renameAttribute(argv[i], fromAttr, toAttr, flags) != B_OK) returnCode = EXIT_FAILURE; } return returnCode; }