logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

truncate.c (3266B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include "../config.h"
  6. #include "../lib/bitmasks.h" // FIELD_CLR
  7. #include "../libutils/getopt_nolong.h"
  8. #include "../libutils/truncation.h" // parse_size
  9. #include <errno.h>
  10. #include <fcntl.h> // open
  11. #include <stdbool.h>
  12. #include <stdio.h> // fprintf
  13. #include <stdlib.h> // abort
  14. #include <string.h> // strerror
  15. #include <sys/stat.h>
  16. #include <unistd.h> // getopt
  17. #ifdef HAS_GETOPT_LONG
  18. #include <getopt.h>
  19. #endif
  20. const char *argv0 = "truncate";
  21. static void
  22. usage(void)
  23. {
  24. fprintf(stderr, "Usage: truncate [-co] [-r ref_file] [-s size] file...\n");
  25. }
  26. int
  27. main(int argc, char *argv[])
  28. {
  29. int open_flags = O_WRONLY | O_CREAT | O_NONBLOCK;
  30. bool size_set = false;
  31. struct truncation tr = {
  32. .op = OP_SET,
  33. .size = 0,
  34. };
  35. char *ref_file = NULL;
  36. #ifdef HAS_GETOPT_LONG
  37. // Strictly for GNUisms compatibility so no long-only options
  38. // clang-format off
  39. static struct option opts[] = {
  40. {"no-create", no_argument, NULL, 'c'},
  41. {"reference", required_argument, NULL, 'r'},
  42. {"size", required_argument, NULL, 's'},
  43. {0, 0, 0, 0},
  44. };
  45. // clang-format on
  46. // Need + as first character to get POSIX-style option parsing
  47. for(int c = -1; (c = getopt_long(argc, argv, "+:cr:s:", opts, NULL)) != -1;)
  48. #else
  49. for(int c = -1; (c = getopt_nolong(argc, argv, ":cr:s:")) != -1;)
  50. #endif
  51. {
  52. switch(c)
  53. {
  54. case 'c':
  55. FIELD_CLR(open_flags, O_CREAT);
  56. break;
  57. case 'r':
  58. if(size_set)
  59. {
  60. fprintf(stderr, "truncate: error: Truncation size can only be set once\n");
  61. usage();
  62. return 1;
  63. }
  64. ref_file = optarg;
  65. size_set = true;
  66. break;
  67. case 's':
  68. if(size_set)
  69. {
  70. fprintf(stderr, "truncate: error: Truncation size can only be set once\n");
  71. usage();
  72. return 1;
  73. }
  74. if(parse_size(optarg, &tr) < 0)
  75. {
  76. usage();
  77. return 1;
  78. }
  79. size_set = true;
  80. break;
  81. case ':':
  82. fprintf(stderr, "truncate: error: Missing operand for option: '-%c'\n", optopt);
  83. usage();
  84. return 1;
  85. case '?':
  86. GETOPT_UNKNOWN_OPT
  87. usage();
  88. return 1;
  89. default:
  90. abort();
  91. }
  92. }
  93. argc -= optind;
  94. argv += optind;
  95. if(argc < 1) usage();
  96. if(!size_set)
  97. {
  98. fprintf(stderr,
  99. "truncate: error: target size wasn't set, you need to pass one of -d / -r / -s\n");
  100. return 1;
  101. }
  102. if(ref_file != NULL)
  103. {
  104. struct stat ref_stats;
  105. if(stat(optarg, &ref_stats) < 0)
  106. {
  107. fprintf(stderr,
  108. "truncate: error: Couldn't get status for file '%s': %s\n",
  109. optarg,
  110. strerror(errno));
  111. return 1;
  112. }
  113. tr.op = OP_SET;
  114. tr.size = ref_stats.st_size;
  115. }
  116. for(int argi = 0; argi < argc; argi++)
  117. {
  118. char *arg = argv[argi];
  119. // same flags to open as touch(1)
  120. int fd = open(arg, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
  121. if(fd < 0)
  122. {
  123. fprintf(stderr, "truncate: error: Failed to open '%s': %s\n", arg, strerror(errno));
  124. return 1;
  125. }
  126. if(apply_truncation(fd, tr, arg) < 0) return 1;
  127. if(close(fd) < 0)
  128. {
  129. fprintf(stderr, "truncate: error: Failed closing fd for '%s': %s\n", arg, strerror(errno));
  130. return 1;
  131. }
  132. }
  133. return 0;
  134. }