truncation.c (2928B)
- // Collection of Unix tools, comparable to coreutils
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #include "truncation.h"
- #include <assert.h>
- #include <ctype.h> // toupper
- #include <errno.h>
- #include <stdio.h> // fprintf
- #include <stdlib.h> // strtol
- #include <string.h> // strerror
- #include <sys/stat.h> // fstat
- #include <unistd.h> // ftruncate
- extern char *argv0;
- int
- apply_truncation(int fd, struct truncation tr, char *arg)
- {
- if(tr.op != OP_SET)
- {
- struct stat stats;
- if(fstat(fd, &stats) < 0)
- {
- fprintf(
- stderr, "truncate: error: Failed to get status of file '%s': %s\n", arg, strerror(errno));
- return -1;
- }
- int q;
- switch(tr.op)
- {
- case OP_INC:
- tr.size += stats.st_size;
- break;
- case OP_DEC:
- tr.size = stats.st_size - tr.size;
- if(tr.size < 0) tr.size = 0;
- break;
- case OP_CHK_DOWN:
- q = stats.st_size / tr.size;
- tr.size = tr.size * q;
- break;
- case OP_CHK_UP:
- q = stats.st_size / tr.size;
- if(stats.st_size % tr.size) q++;
- tr.size = tr.size * q;
- break;
- default:
- abort();
- }
- }
- if(ftruncate(fd, tr.size) < 0)
- {
- fprintf(stderr,
- "truncate: error: Failed to truncate '%s' to %ld: %s\n",
- arg,
- tr.size,
- strerror(errno));
- return -1;
- }
- return 0;
- }
- int
- apply_size_suffix(unsigned long *size, char *endptr)
- {
- char units[] = "KMGTPEZ";
- if(endptr[0] == '\0') return 0;
- size_t i = 0;
- unsigned long si = 1, iec = 1;
- char pfx = toupper(endptr[0]);
- for(; i < sizeof(units); i++)
- {
- si *= 1000;
- iec *= 1024;
- if(units[i] == pfx) break;
- }
- if(i >= sizeof(units) || si == 1)
- {
- fprintf(stderr, "%s: error: Unrecognised unit '%s'\n", argv0, endptr);
- return -1;
- }
- if(endptr[1] == 0 || (endptr[1] == 'i' && endptr[2] == 'B' && endptr[3] == 0))
- {
- *size *= iec;
- return 0;
- }
- if(endptr[1] == 'B' && endptr[2] == 0)
- {
- *size *= si;
- return 0;
- }
- fprintf(stderr, "%s: error: Unrecognised suffix '%s'\n", argv0, endptr);
- return -1;
- }
- int
- parse_size(const char *arg, struct truncation *buf)
- {
- assert(arg != NULL);
- assert(buf != NULL);
- if(arg[0] == 0)
- {
- fprintf(stderr, "%s: error: Got empty size argument\n", argv0);
- buf->size = 0;
- return -1;
- }
- enum operation_e op = OP_SET;
- switch(arg[0])
- {
- case '+':
- op = OP_INC;
- arg++;
- break;
- case '-':
- op = OP_DEC;
- arg++;
- break;
- case '/':
- op = OP_CHK_DOWN;
- arg++;
- break;
- case '%':
- op = OP_CHK_UP;
- arg++;
- break;
- // no default case intended
- }
- char *endptr = NULL;
- unsigned long size = strtoul(arg, &endptr, 10);
- if(errno != 0)
- {
- fprintf(
- stderr, "%s: error: Failed parsing '%s' as a number: %s\n", argv0, arg, strerror(errno));
- return -1;
- }
- if(endptr != NULL)
- {
- if(apply_size_suffix(&size, endptr) < 0) return -1;
- }
- buf->size = size;
- buf->op = op;
- return 0;
- }