/**
* @file fsize.c
* @license This file is licensed under the GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007. You may obtain a copy of this license at https://www.gnu.org/licenses/gpl-3.0.en.html.
* @author Tushar Chaurasia (Dark-CodeX)
*/
/**
* @file fsize.c
* @license This file is licensed under the GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007. You may obtain a copy of this license at https://www.gnu.org/licenses/gpl-3.0.en.html.
* @author Tushar Chaurasia (Dark-CodeX)
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <ctype.h>
#if defined _WIN32 || defined _WIN64 || defined __CYGWIN__
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#endif
#define FSIZE_VER "1.0.0"
void show_help(void)
{
puts(
"Usage: [option] [files]"[files]\n"
"Options:"\n"
" --help Display this help message."\n"
" --version Display the version of the app."\n"
" --size=<unit_of_byte> Define output unit."\n"
" =bit, bits Output in bits."\n"
" =byte, bytes Output in bytes (default)."\n"
" =kib Output in kibibytes."\n"
" =mib Output in mebibytes."\n"
" =gib Output in gibibytes."\n"
" =kb Output in kilobytes."\n"
" =mb Output in megabytes."\n"
" =gb Output in gigabytes."\n"
);
}
bool is_directory(const char* loc)
{
if (!loc)
return false;
#if defined _WIN32 || defined _WIN64 || defined __CYGWIN__
DWORD fileAttributes = GetFileAttributesA(loc);
if (fileAttributes == INVALID_FILE_ATTRIBUTES)
return false;
return fileAttributes & FILE_ATTRIBUTE_DIRECTORY;
#else
struct stat buffer;
return stat(loc, &buffer) == 0 && S_ISDIR(buffer.st_mode);
#endif
}
typedef struct UNIT
{
const char* input_name;
const char* output_name;
const char* output_format;
const unsigned long long divider;
} UNIT;
UNIT UNITS[] = {
// Put the default unit first
{ "byte", "bytes", "%0.f", 8ULL },
{"bit", "bits", "%0.f", 1ULL},
{"bits", "bits", "%0.f", 1ULL},
{"bytes", "bytes", "%0.f", 8ULL},
{"kib", "kibibytes" , "%0.3f", 8ULL<<10},
{"mib", "mebibytes", "%0.3f", 8ULL<<20},
{"gib", "gibibytes", "%0.3f", 8ULL<<30},
{"kb", "kilobytes", "%0.3f", 8ULL*1000},
{"mb", "megabytes", "%0.3f", 8ULL*1000*1000},
{"gb", "gigabytes", "%0.3f", 8ULL*1000*1000*1000},
// Put the "no unit" last
{ NULL, NULL, NULL, 0 },
};
UNIT* parse_unit(char* unit_name, int const argc)
{
UNIT* NO_UNIT = &UNITS[sizeof(UNITS) / sizeof(UNITUNITS[0]) - 1];
UNIT* result = &UNITS[0];
// Parse unit as lower case
for (int c = 0; unit_name[c]; ++c) {
unit_name[c] = (char) tolower((unsigned char)unit_name[c]);
}
// Unit lookup by input name
bool found = false;
for (size_t j = 0; UNITS[j].input_name != NULL; j++)
{
if (strcmp(unit_name, UNITS[j].input_name) == 0)
{
result = &UNITS[j];
found = true;
}
}
// Error handling
if (!found)
{
fprintf(stderr, "err: '%s' is not a valid unit, try using '--help' flag\n", unit_name);
return NO_UNIT;
}
if (argc == 2)
{
fprintf(stderr, "err: no file was given\n");
return NO_UNIT;
}
return result;
}
void print_file_size(char* file_name, UNIT unit)
{
if (is_directory(file_name))
{
fprintf(stderr, "err: '%s': Is a directory\n", file_name);
return;
}
FILE* fptr = fopen(file_name, "rb");
if (!fptr)
{
fprintf(stderr, "err: fopen(): '%s': %s\n", file_name, strerror(errno));
return;
}
int result = fseek(fptr, 0, SEEK_END);
if (result != 0)
{
fprintf(stderr, "err: fseek(): '%s': %s\n", file_name, strerror(errno));
fclose(fptr);
return;
}
size_t size_in_bytes = ftell(fptr);
fclose(fptr);
char format[] = "xxxxxx %s : %s\n";
sprintf_s(format, sizeof(format) - 1, "%s %%s : %%s\n", unit.output_format);
printf(format, 8.f * (float)size_in_bytes / (float)unit.divider, unit.output_name, file_name);
}
int main(int argc, char** argv)
{
if (argc == 1)
{
fprintf(stderr, "err: no file was given\n");
return EXIT_FAILURE;
}
int current_arg = 1;
if (strcmp(argv[current_arg], "--help") == 0)
{
show_help();
return EXIT_SUCCESS;
}
if (strcmp(argv[current_arg], "--version") == 0)
{
printf("%s: %s\n", argv[0], FSIZE_VER);
return EXIT_SUCCESS;
}
UNIT* unit = &UNITS[0];
if (strncmp(argv[1]argv[current_arg], "--size=", sizeof("--size=") - 1) == 0)
{
unit = parse_unit(argv[current_arg] + sizeof("--size=") - 1, argc);
if (unit->input_name == NULL) return EXIT_FAILURE;
current_arg++;
}
for (; current_arg < argc; current_arg++)
{
print_file_size(argv[current_arg], *unit);
}
return EXIT_SUCCESS;
}
After: 174180 lines, 4 helper methods, main has 33 lines and the 5 steps of processing are clearly visible. No switch/case and no if/else cascade.