logo

utils-std

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

truncation.c (2928B)


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