I just wrote a utility program in C that converts numbers of different bases to 'standard' well-known bases (hex, dec, oct, bin). I called it repnum
, short for "represent number."
You will notice a 'template' that I like to employ when writing C programs. Any suggestions?
repnum.h
#ifndef REPNUM_H
#define REPNUM_H
#ifdef __GNUC__
#define _GNU_SOURCE
#elif ! (_POSIX_C_SOURCE >= 2 || _XOPEN_SOURCE)
#define NO_OPTS
#endif
#define BUF_SIZE (1024)
#define PROG_VERSION (1.0f)
#define PROG_NAME "repnum"
#define iseven(n) (n % 2 == 0)
#define isodd(n) (n % 2 != 0)
#define _reporterror(c, ...) \
err(c, __VA_ARGS__)
#define _throwerror(c, ...) \
errx(c, __VA_ARGS__)
/* Not needed for now. Just for later use.
#ifdef NDEBUG
#define _assert(c, ...) \
c ? 0 : _throwerror(2, __VA_ARGS__)
#else
#include <assert.h>
#define _assert(c, ...) \
assert(c)
#endif
*/
/*
* Converts a number to a string containing its binary representation.
* `num` is the number to convert.
* `buf` is the string buffer to use.
* `n` is the length of the string.
* Returns a pointer to the start of the representation. NOT THE START OF `buf`.
* If NULL was returned, then `n` characters are not enough and the function stopped at the middle of processing.
* NOTE: the algorithm used writes binary right to left, so to ensure highest performance,
* characters are written starting by the end of the string.
*/
char *num2bin(unsigned long long, char *, size_t);
/*
* Converts a string to a long.
* Converts `str` as a base `base` number to long and stores it in `num`.
* Supports all bases that strtoull supports.
* Returns:
* 0 - Whole `str` is valid. `num` is changed.
* 1 - Whole `str` is invalid. `num` is not changed.
* 2 - `num` is null or `str` is null or the first character in `str` is a null byte. `num` is not changed.
* 3 - Partial `str` is valid. `num` is changed.
* 4 - Overflow. `num` is not changed.
*/
int str2ull(char *, unsigned long long *, int);
/* Convenience Functions */
static inline unsigned long long ensurenum(char *, unsigned long long *, int);
static inline int ensurebase(int);
static inline void _free(void);
static inline void help(int);
static inline unsigned long long countbits(unsigned long long value);
#ifndef NO_OPTS
static void parseopts(int argc, char **argv);
#endif
#endif /* REPNUM_H */
repnum.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include "repnum.h"
#ifndef NO_OPTS
#ifdef _GNU_SOURCE
#include <getopt.h>
#else
#include <unistd.h>
#endif
#endif
char *_buffer;
struct numinfo {
unsigned long long num;
int base;
};
struct numinfo info;
int main(int argc, char **argv)
{
char *res;
info.base = 0;
parseopts(argc, argv);
if (!(_buffer = calloc(BUF_SIZE, sizeof(char))))
_reporterror(1, "Cannot allocate memory");
atexit(_free);
if (!(res = num2bin(info.num, _buffer, BUF_SIZE)))
_throwerror(1, "%s is a too large number.", argv[1]);
printf("[dec]\t%llu\t=\t[hex]\t%llx\t[oct]\t%llo\t[bin]\t%s\n", info.num, info.num, info.num, res);
return 0;
}
char *num2bin(unsigned long long num, char *buf, size_t n)
{
char s;
buf[--n] = 0;
while (--n && num) {
s = iseven(num) ? '0' : '1';
buf[n-1] = s;
num /= 2;
}
return &buf[n];
}
int str2ull(char *str, unsigned long long *num, int base)
{
char *end;
int status;
if (!str || !*str || !num)
return 2;
if(!(status = strtoull(str, &end, base)) && (end == str))
return 1;
if (errno == ERANGE)
return 4;
*num = status;
return (*end ? 3 : 0);
}
static inline void _free()
{
free(_buffer);
}
static inline void help(int c)
{
puts("Displays a number in various base representations.\n\t-b"
#ifdef _GNU_SOURCE
"|--base"
#else
"\t"
#endif
" [base]\tforce a base. Possible values are 2 through 36.\n\t-v"
#ifdef _GNU_SOURCE
"|--version"
#endif
"\t\toutput version then exit.\n\t"
#ifdef _GNU_SOURCE
"--help\t"
#else
"-h"
#endif
"\t\tview this help.");
exit(c);
}
#ifndef NO_OPTS
static void parseopts(int argc, char **argv)
{
int opt;
#ifdef _GNU_SOURCE
struct option opts[] = {
{ "base", 1, 0, 'b' },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' }
};
#define parser() (getopt_long(argc, argv, "b:hv", opts, 0))
#else
#define parser() (getopt(argc, argv, "b:hv"))
#endif
while ((opt = parser()) != -1) {
switch (opt)
{
case '?':
_throwerror(1, "Unrecognized option: -%c", opt);
case 'b':
info.base = ensurebase(ensurenum(optarg, 0, 10));
break;
case 'h':
help(0);
case 'v':
printf("%s v%.2f. Licenced under the GNU GPL v3 License.\n", PROG_NAME, PROG_VERSION);
exit(0);
}
}
info.num = ensurenum(argv[optind], 0, info.base);
}
#endif
/* Convenience Functions */
static inline unsigned long long ensurenum(char *s, unsigned long long *n, int b)
{
unsigned long long temp;
int status;
char tmp[21];
/* if the user doesn't provide his variable two change, create a temporary one */
if (!n)
n = &temp;
status = str2ull(s, n, b);
/* generate a message to indicate required base if `b` is not 0 [any base]. */
if (b)
sprintf(tmp, "Base %d is required.%c", b, 0);
switch (status)
{
case 1:
case 3:
_throwerror(1, "%s is not a valid number.%s", s, b ? tmp : "");
case 4:
_throwerror(1, "%s is a too large number.", s);
}
return *n;
}
static inline int ensurebase(int b)
{
if (b < 2 || b > 36)
_throwerror(1, "Unsupported base: %d", b);
return b;
}