Main Menu
      Contact TripleDuck
      About Analogy With Water
      Musical Releases By Album
      Programming Stuff
            Random.C
      The Meaning of the Name

One thing we all need is either good random generation and good random password.  This program is designed to do just that. random.c uses reentract random number generation (can be disabled if you system does not support it) and generates a string of letters and numbers.
Here are the best passwords:
  • Randomly selected characters that are uppercase, lowercase, and include numbers. Including the _ symbol is also a good idea.
  • Passwords should be 10-12 characters minimum
  • It should be difficult for ANYONE to remember - if any sites tell you it's OK when it is easy for you to remember, that means it is easy for anyone to remember and usually guess
  • Don't use the same password for a bunch of sites online - if they hack your account, they can get all sorts of information about you and find out where else you've registered and then have access to those accounts too - DON'T USE THE SAME PASSWORD!
Here is the source code for the program.  The Makefile is included below
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

//#define _RND_DEBUG 1

/*
 * random.c - generates a string/word of a given length using a given set of
 *            valid characters - defaults to a-z, A-Z, and 0-9 with a length
 *            of 32 characters - the from list can be set by command line
 *            switches and is limited to a range of 10 characters to 128
 *            characters - this allows you to generate random names to fit
 *            your needs (the more the better!) - lengths are limited to
 *            8 to 64 characters
 *
 *            please note that this utility uses drand48/drand48_r BSD
 *            randomization library calls and it must available on your
 *            system before this will compile
 *
 *            also note that this will take advantage or /dev/urandom if
 *            it is available on your system
 *
 * command line options
 *
 * -?        prints usage information (like this)
 * -l ##     sets the length of the printed string
 * -c list   sets the character list used to generate the string
 * -t        simply prints the length and char list, to verify your passed
 *           params are being interpreted correctly
 */

//  reentrant randomization functionality enabled by default
#define __USE_REENTRANT 1

//  very non-standard but much more pleasing to use
#define Unshort unsigned short
#define Unlong  unsigned long
#define Unint  unsigned int
#define STm    struct tm

#ifdef __USE_REENTRANT
#define Drand48_data struct drand48_data
#endif

//  why does C not do this already? I will never know
#define bool short
#define true (1==1)
#define false (1==0)

#define RETURN_NORMAL 0
#define RETURN_INVALIDARG 1
#define RETURN_SEEDFAILED 2
#define RETURN_RNDFAILED 3

#define FROM_DEFAULT "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
#define FROM_MIN 10
#define FROM_MAX 128
#define LEN_DEFAULT 32
#define LEN_MIN 8
#define LEN_MAX 64

//  see functions below for more information on their functionality

void errorInvalidArg(const int, const char *, const char *);
void errorGeneric(const char *);
void printUsage(const char *);
#ifdef __USE_REENTRANT
bool initializeSeed(Drand48_data *);
bool rndInt(Drand48_data *rnd, const int, const int, int *);
#else
bool initializeSeed();
bool rndInt(const int, const int, int *);
#endif

int main(int argc, char **argv)
{
#ifdef __USE_REENTRANT
  //  if we're using the reentrant randomization functions, setup the struct now
  Drand48_data rnd;
#endif

  //  declare the space to be used for selecting random characters
  char from[FROM_MAX];
  //  test only, set to false as default
  bool test = false;
  //  sets the number of letters to use for randomization
  int  len  = 0;
  int  num;
  int  x;
 
#ifdef _RND_DEBUG
  printf("zeroing out the from list\n");
#endif
  //  zero out the from list
  memset(from, 0, sizeof(char) * FROM_MAX);

  //  loop through all command line args and find what they are passing in
  // or if there are problems with the passed params
#ifdef _RND_DEBUG
  printf("looping through %d command line args\n", argc);
#endif
  for(x = 1; x < argc; x++) {
#ifdef _RND_DEBUG
    printf("  on arg %d: %s\n", x, argv[x]);
#endif
    if(strcmp(argv[x], "-l") == 0) {
#ifdef _RND_DEBUG
      printf("    arg is -l, do we have the required value?\n");
#endif
      if(x >= argc) {
#ifdef _RND_DEBUG
        printf("      no we are missing the value\n");
#endif
        errorInvalidArg(x + 1, argv[x], "Value missing");
        return RETURN_INVALIDARG;
      }
#ifdef _RND_DEBUG
        printf("      getting len from argv %d: %s\n", x + 1, argv[x + 1]);
#endif
      len = atoi(argv[x + 1]);
#ifdef _RND_DEBUG
        printf("    len is now %d\n", len);
#endif
    }
    else if(strcmp(argv[x], "-c") == 0) {
#ifdef _RND_DEBUG
      printf("    arg is -c, do we have the required value?\n");
#endif
      if(x >= argc) {
#ifdef _RND_DEBUG
        printf("      no we are missing the value\n");
#endif
        errorInvalidArg(x + 1, argv[x], "Character list missing");
        return RETURN_INVALIDARG;
      }
#ifdef _RND_DEBUG
        printf("      getting character list from argv %d: %s\n", x + 1, argv[x + 1]);
#endif
      strncpy(from, argv[x + 1], FROM_MAX);
#ifdef _RND_DEBUG
        printf("    character list is now '%s'\n", from);
#endif
    }
    else if(strcmp(argv[x], "-?") == 0) {
#ifdef _RND_DEBUG
      printf("    arg is -?, printing usage information\n");
#endif
      printUsage(argv[0]);
      return RETURN_NORMAL;
    }
    else if(strcmp(argv[x], "-t") == 0) {
#ifdef _RND_DEBUG
      printf("    arg is -t, test is now true\n");
#endif
      test = true;
    }
    else {
#ifdef _RND_DEBUG
      printf("    arg is '%s', an unknown parameter\n", argv[x]);
#endif
      errorInvalidArg(x + 1, argv[x], "Unknown command line parameter");
    }
  }
#ifdef _RND_DEBUG
  printf("  command line argument loop is finished\n");
#endif

#ifdef _RND_DEBUG
  printf("  validating the length, currently set to %d\n", len);
#endif
  //  make sure the length is in a value range
  if(len == 0) { 
#ifdef _RND_DEBUG
    printf("    length zero, setting to default of %d\n", LEN_DEFAULT);
#endif
    len = LEN_DEFAULT; 
  }
  else if(len < LEN_MIN) { 
#ifdef _RND_DEBUG
    printf("    length less than minimum, setting to minimum of %d\n", LEN_MIN);
#endif
    len = LEN_MIN; 
  }
  else if(len > LEN_MAX) { 
#ifdef _RND_DEBUG
    printf("    length greater than maximum, setting to maximum of %d\n", LEN_MAX);
#endif
    len = LEN_MAX; 
  }

#ifdef _RND_DEBUG
  printf("  checking if enough characters exist in the character list: %s\n", from);
#endif
  //  if the from is too short, use the default
  if(strlen(from) < FROM_MIN) { 
#ifdef _RND_DEBUG
      printf("    list length only %d, using default list %s\n", strlen(from), FROM_DEFAULT);
#endif
    strncpy(from, FROM_DEFAULT, FROM_MAX); 
  }

#ifdef _RND_DEBUG
  printf("  checking to see if we're only viewing our settings (test == true)\n");
#endif
  //  if testing, just print the settings and stop  
  if(test) {
#ifdef _RND_DEBUG
    printf("    test is true, showing settings\n");
#endif
    printf("Set length: %d\nSet charset: %s\n", len, from);
    return RETURN_NORMAL;
  }

#ifdef __USE_REENTRANT
#ifdef _RND_DEBUG
  printf("  attempting to initialize reentrant seed\n");
#endif
  if(!initializeSeed(&rnd)) {
#else
#ifdef _RND_DEBUG
  printf("  attempting to initialize non-reentrant seed\n");
#endif
  if(!initializeSeed()) {
#endif
#ifdef _RND_DEBUG
    printf("    seed initialization failed\n");
#endif
    return RETURN_SEEDFAILED;
  }

#ifdef _RND_DEBUG
  printf("  ready to generate random string of %d letters\n", len);
#endif

  for(x = 0; x < len; x++) {
#ifdef __USE_REENTRANT
    if(!rndInt(&rnd, 0, strlen(from), &num)) {
#ifdef _RND_DEBUG
      printf("    reentrant rndInt failed\n");
#endif
#else
    if(!rndInt(0, strlen(from), &num)) {
#ifdef _RND_DEBUG
      printf("    non-reentrant rndInt failed\n");
#endif
#endif
      return RETURN_RNDFAILED;
    }
    //  generate one character and print it
    fputc(from[num], stdout);
  }

#ifdef _RND_DEBUG
  printf("  finished!\n");
#endif

  return RETURN_NORMAL;
}

/////////////////////////////////////////////////////////////////////////////
//
//  prints a custom 'invalid argument' error message
//
void errorInvalidArg(const int pos, const char *what, const char *message)
{
  fprintf(stderr, "Invalid Argument '%s' at '%d': %s\n", what, pos, message);
}

/////////////////////////////////////////////////////////////////////////////
// 
//  prints a generic error message
//
void errorGeneric(const char *message)
{
  fprintf(stderr, "%s\n", message);
}

/////////////////////////////////////////////////////////////////////////////
//
//  prints the usage information
//
void printUsage(const char *binaryName)
{
  char *ptr = NULL;

#ifdef _RND_DEBUG
  printf("  printUsage: locating binary app name from path %s\n", binaryName);
#endif
  ptr = strrchr(binaryName, '/');

  if(ptr == NULL) {
    ptr = (char *)binaryName;
#ifdef _RND_DEBUG
    printf("  printUsage:   binaryName does not contain '/'\n");
#endif
  }
  else {
    ptr++;
  }

#ifdef _RND_DEBUG
  printf("  printUsage:   binary is %s\n", ptr);
#endif

  printf("Usage:\n\n\t%s  [-?]  [-c charlist] [-l length]\n\n"
         "\t%-18sPrints usage information (this display)\n\n"
         "\t%-18sSets the character list used for randomness.\n"
         "\t%-18sDefault:\n"
         "\t%-18s%s\n"
         "\t%-18sMust be at least %d characters and no more than %d\n\n"
         "\t%-18sSets the length of the random return. Default is %d\n"
         "\t%-18sMust be at least %d and no more than %d\n\n"
         "\t%-18sTest mode, only prints what length and charset are used\n\n",
         ptr, "-?", "-c charlist", "", "", FROM_DEFAULT, "", FROM_MIN, FROM_MAX, 
         "-l length", LEN_DEFAULT, "", LEN_MIN, LEN_MAX, "-t");
}

/////////////////////////////////////////////////////////////////////////////
//
//  initializes the random seed by first trying urandom, then random, then
// by using the system clock (which uses microseconds - never use 'time' for
// seeding the randomizer! Every time the function is called fresh within
// a one second period, the results will always be the same!)
//
#ifdef __USE_REENTRANT
bool initializeSeed(Drand48_data *buffer)
#else
bool initializeSeed()
#endif
{
  FILE *fp = NULL;
  Unlong  seed;
  Unlong  ret;

  seed = 0l;

#ifdef __USE_REENTRANT
  //  did we receive a valid buffer?
  if(buffer == NULL) {
    errorInvalidArg(1, "buffer", "NULL buffer");
    return false;
  }

  memset(buffer, 0, sizeof(Drand48_data));
#endif

  //  first we try to open urandom
  fp = fopen("/dev/urandom", "rb");
  if(fp == NULL) {
    //  failed, now try to open random
    fp = fopen("/dev/random", "rb");
    if(fp == NULL) {
      //  failed, now use system clock - instead of using second-limited 
      // time, we use clock for more precision (assumed)
      seed = (Unlong)clock();
    }
    else {
      errorGeneric("random seed initialization failed!");
      return false;
    }
  }

  //  did we open a file handle to a random stream?
  if(fp != NULL) {
    //  yes, so read an Unlong
    ret = fread(&seed, sizeof(Unlong), 1, fp);
    fclose(fp);
    //  make sure 
    if(ret < 1) { 
      errorGeneric("unable to retrieve data from random stream");
      return false; 
    }
  }

#ifdef __USE_REENTRANT
  srand48_r(seed, buffer);
#else
  srand48(seed);
#endif

  return true;
}

/////////////////////////////////////////////////////////////////////////////
//
//  generate a random integer and store it in 'number'
//
#ifdef __USE_REENTRANT
bool rndInt(Drand48_data *rnd, const int lowval, const int highval, int *number)
#else
bool rndInt(const int lowval, const int highval, int *number)
#endif
{
  int    range = highval - lowval;
  int    num;
  double p;

  if(number == NULL) {
#ifdef __USE_REENTRANT
    errorInvalidArg(4, "number", "NULL param");
#else
    errorInvalidArg(3, "number", "NULL param");
#endif
    return false;
  }

  do {
#ifdef __USE_REENTRANT
    drand48_r(rnd, &p);
#else
    p   = drand48();
#endif
    num = (int)((double)range * p);
  } while(range <= num);
  
  *number = num + lowval;
  return true;
}

#
# Makefile autogenerated Sun Sep  6 12:39:07 2009
#

CC=gcc
CFLAGS= -c -Wall
LINKFLAGS= -Wall
INSTALLDIR=/usr/local/bin/
BINARYNAME=random

all: a962031992b7b5934db41bf925b3487e
        $(CC) $(LINKFLAGS) -o $(BINARYNAME) random.o


a962031992b7b5934db41bf925b3487e: random.o

random.o: random.c
        $(CC) $(CFLAGS) random.c


clean:
        rm -rf *.o a962031992b7b5934db41bf925b3487e
        rm -rf $(BINARYNAME)

install:
        cp $(BINARYNAME) $(INSTALLDIR)