logo

utils-std

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

truncation.c (2971B)


  1. // Collection of Unix tools, comparable to coreutils
  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 "truncation.h"
  6. #include <assert.h>
  7. #include <ctype.h> // toupper
  8. #include <errno.h>
  9. #include <stdio.h> // fprintf
  10. #include <stdlib.h> // strtol
  11. #include <string.h> // strerror
  12. #include <sys/stat.h> // fstat
  13. #include <unistd.h> // ftruncate
  14. extern char *argv0;
  15. int
  16. apply_truncation(int fd, struct truncation tr, char *arg)
  17. {
  18. if(tr.op != OP_SET)
  19. {
  20. struct stat stats;
  21. assert(errno == 0);
  22. if(fstat(fd, &stats) < 0)
  23. {
  24. fprintf(
  25. stderr, "truncate: error: Failed to get status of file '%s': %s\n", arg, strerror(errno));
  26. return -1;
  27. }
  28. int q;
  29. switch(tr.op)
  30. {
  31. case OP_INC:
  32. tr.size += stats.st_size;
  33. break;
  34. case OP_DEC:
  35. tr.size = stats.st_size - tr.size;
  36. if(tr.size < 0) tr.size = 0;
  37. break;
  38. case OP_CHK_DOWN:
  39. q = stats.st_size / tr.size;
  40. tr.size = tr.size * q;
  41. break;
  42. case OP_CHK_UP:
  43. q = stats.st_size / tr.size;
  44. if(stats.st_size % tr.size) q++;
  45. tr.size = tr.size * q;
  46. break;
  47. default:
  48. abort();
  49. }
  50. }
  51. if(ftruncate(fd, tr.size) < 0)
  52. {
  53. fprintf(stderr,
  54. "truncate: error: Failed to truncate '%s' to %ld: %s\n",
  55. arg,
  56. tr.size,
  57. strerror(errno));
  58. return -1;
  59. }
  60. return 0;
  61. }
  62. int
  63. apply_size_suffix(unsigned long *size, char *endptr)
  64. {
  65. char units[] = "KMGTPEZ";
  66. if(endptr[0] == '\0') return 0;
  67. size_t i = 0;
  68. unsigned long si = 1, iec = 1;
  69. char pfx = toupper(endptr[0]);
  70. for(; i < sizeof(units); i++)
  71. {
  72. si *= 1000;
  73. iec *= 1024;
  74. if(units[i] == pfx) break;
  75. }
  76. if(i >= sizeof(units) || si == 1)
  77. {
  78. fprintf(stderr, "%s: error: Unrecognised unit '%s'\n", argv0, endptr);
  79. return -1;
  80. }
  81. if(endptr[1] == 0 || (endptr[1] == 'i' && endptr[2] == 'B' && endptr[3] == 0))
  82. {
  83. *size *= iec;
  84. return 0;
  85. }
  86. if(endptr[1] == 'B' && endptr[2] == 0)
  87. {
  88. *size *= si;
  89. return 0;
  90. }
  91. fprintf(stderr, "%s: error: Unrecognised suffix '%s'\n", argv0, endptr);
  92. return -1;
  93. }
  94. int
  95. parse_size(const char *arg, struct truncation *buf)
  96. {
  97. assert(arg != NULL);
  98. assert(buf != NULL);
  99. if(arg[0] == 0)
  100. {
  101. fprintf(stderr, "%s: error: Got empty size argument\n", argv0);
  102. buf->size = 0;
  103. return -1;
  104. }
  105. enum operation_e op = OP_SET;
  106. switch(arg[0])
  107. {
  108. case '+':
  109. op = OP_INC;
  110. arg++;
  111. break;
  112. case '-':
  113. op = OP_DEC;
  114. arg++;
  115. break;
  116. case '/':
  117. op = OP_CHK_DOWN;
  118. arg++;
  119. break;
  120. case '%':
  121. op = OP_CHK_UP;
  122. arg++;
  123. break;
  124. // no default case intended
  125. }
  126. assert(errno == 0);
  127. char *endptr = NULL;
  128. unsigned long size = strtoul(arg, &endptr, 10);
  129. if(errno != 0)
  130. {
  131. fprintf(
  132. stderr, "%s: error: Failed parsing '%s' as a number: %s\n", argv0, arg, strerror(errno));
  133. return -1;
  134. }
  135. if(endptr != NULL)
  136. {
  137. if(apply_size_suffix(&size, endptr) < 0) return -1;
  138. }
  139. buf->size = size;
  140. buf->op = op;
  141. return 0;
  142. }