Skip to content

Instantly share code, notes, and snippets.

@bradley219
Last active February 14, 2020 00:35
Show Gist options
  • Save bradley219/e924444a4add81018da8d53273833646 to your computer and use it in GitHub Desktop.
Save bradley219/e924444a4add81018da8d53273833646 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <getopt.h>
#define VERSION "1.1"
typedef enum {
NONE = 0,
HEX = 1 << 0,
DEC = 1 << 1,
OCT = 1 << 2,
BIN = 1 << 3,
} format_t;
void usage(char *cmd) {
fprintf(stderr, "Usage: %s [OPTIONS] <from>\n", cmd);
fprintf(stderr, "<from> is an integer that may be specified as one of the following formats: 31. 0x1f 037 b11111\n");
fprintf(stderr, "OPTIONS\n");
fprintf(stderr, "-o --output [FORMAT] Set the output format. Default is to output in all formats. Available formats: (dec|hex|oct|bin).\n");
fprintf(stderr, "-h --help Display this help message.\n");
fprintf(stderr, "-v --version Display version number.\n");
}
void version(void) {
printf("Version " VERSION "\n");
}
int parse(char *input, long long *output) {
format_t fmt = NONE;
size_t len = strlen(input);
if (len > 2 && strncmp("0x", input, 2) == 0) {
fmt = HEX;
input += 2;
} else if (len > 2 && strncmp("0X", input, 2) == 0) {
fmt = HEX;
input += 2;
} else if (len > 1 && *input == 'x') {
fmt = HEX;
input += 1;
} else if (len > 1 && *input == '0') {
// avoid accidental confusion with binary not prefixed with 'b'
const char *c = input;
while (*c != '\0') {
if (!(*c == '0' || *c == '1')) { // not a 1 or 0
fmt = OCT;
input += 1;
break;
}
c++;
}
} else if (len > 1 && *input == 'b') {
fmt = BIN;
input += 1;
} else if (len > 1 && input[len-1] == '.') {
fmt = DEC;
input[len-1] = '\0';
}
if (fmt == NONE) {
int possible = HEX | DEC | OCT;
const char *c = input;
while (*c != '\0') {
if (!(*c == '0' || *c == '1')) { // not a 1 or 0
possible &= ~BIN;
}
if (!(*c <= '7' && *c >= '0')) { // not in [0-7]
possible &= ~OCT;
}
if (!(*c <= '9' && *c >= '0')) { // not in [0-9]
possible &= ~DEC;
}
if (!((*c <= 'f' && *c >= 'a') || (*c <= 'F' && *c >= 'A') || (*c <= '9' && *c >= '0'))) { // not in [f-aF-A0-9]
possible &= ~HEX;
}
c++;
}
switch (possible) {
case HEX:
fmt = HEX;
break;
case DEC:
fmt = DEC;
break;
case OCT:
fmt = OCT;
break;
case BIN:
fmt = BIN;
break;
default:
break;
}
}
long long parsed = 0;
char *endptr = NULL;
switch (fmt) {
case HEX:
fprintf(stderr, "Format detected as hexadecimal\n");
fmt = HEX;
parsed = strtoll(input, &endptr, 16);
break;
case DEC:
fprintf(stderr, "Format detected as decimal\n");
fmt = DEC;
parsed = strtoll(input, &endptr, 10);
break;
case OCT:
fprintf(stderr, "Format detected as octal\n");
fmt = OCT;
parsed = strtoll(input, &endptr, 8);
break;
case BIN:
fprintf(stderr, "Format detected as binary\n");
fmt = BIN;
parsed = strtoll(input, &endptr, 2);
break;
default:
fprintf(stderr, "Unable to determine format for `%s'\n", input);
return -1;
break;
}
if (errno == ERANGE) {
perror("Unable to parse");
return -1;
}
if (*endptr != '\0') {
fprintf(stderr, "Invalid character found starting at `%s'\n", endptr);
return -1;
}
*output = parsed;
return 0;
}
void revstr(char *str) {
char tmp;
char *p1 = str;
char *p2 = str + strlen(str) - 1;
while (p2 > p1) {
tmp = *p1;
*p1++ = *p2;
*p2-- = tmp;
}
}
int binstr(long num, char *str, int breaks) {
char *c = str;
long v = num;
for (int i = 0; i < sizeof(long) * 8; i++) {
*c = !!(v & 0x1) ? '1' : '0';
v >>= 1;
c++;
if (v == 0) {
break;
}
if (breaks && !((i+1) % breaks)) {
*c++ = ' ';
}
}
*c = '\0';
revstr(str);
return c - str;
}
void printnum(long long num, format_t format) {
char str[1024];
if (format == NONE) {
int d = 0;
int h = 0;
int o = 0;
int b = 0;
d = sprintf(str, "%lld ", num);
h = sprintf(str + d, "0x%llx ", num);
o = sprintf(str + d + h, "0%llo ", num);
b = binstr(num, str + d + h + o, 4);
printf("%-*s%-*s%-*s%-*s\n", d, "dec", h, "hex", o, "oct", b, "bin");
printf("%s\n", str);
binstr(num, str, 0);
printf("%*s%s\n", d + h + o, "", str);
} else if (format == DEC) {
printf("%lld\n", num);
} else if (format == HEX) {
printf("0x%llx\n", num);
} else if (format == OCT) {
printf("0%llo\n", num);
} else if (format == BIN) {
binstr(num, str, 0);
printf("%s\n", str);
}
}
int main(int argc, char *argv[]) {
static struct option long_options[] = {
{"output", required_argument, 0, 'o'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
format_t output_format = NONE;
while (1) {
int option_index = 0;
int c = getopt_long (argc, argv, "hvo:", long_options, &option_index);
if (c == -1) {
break;
}
switch (c) {
case 0:
break;
case 'o':
if (strcmp(optarg, "dec") == 0) {
output_format = DEC;
} else if (strcmp(optarg, "hex") == 0) {
output_format = HEX;
} else if (strcmp(optarg, "oct") == 0) {
output_format = OCT;
} else if (strcmp(optarg, "bin") == 0) {
output_format = BIN;
} else {
fprintf(stderr, "unknown output format `%s'\n", optarg);
return -1;
}
break;
case 'v':
version();
return 0;
break;
case 'h':
usage(argv[0]);
return 0;
break;
case '?':
break;
default:
usage(argv[0]);
return -1;
break;
}
}
char *cmd = argv[0];
argc -= optind;
argv += optind;
if (argc != 1) {
usage(cmd);
return -1;
}
long long parsed = 0;
if (parse(argv[0], &parsed) != 0) {
return -1;
}
printnum(parsed, output_format);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment