Commit 7faab7f0 by HiPhish

Initial commit.

parents
# OS generated
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db
#-------------------------------------------------------------------------------
# Compiler generated
*.o
*.out
*.a
*.plist
roll
#-------------------------------------------------------------------------------
# Editor generated
*~
*.swapfile
*.swp
tags
#-------------------------------------------------------------------------------
# Project-related
build/
# This file is NOT licensed under the GPLv3, which is the license for the rest
# of YouCompleteMe.
#
# Here's the license text for this file:
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org/>
import os
import ycm_core
# These are the compilation flags that will be used in case there's no
# compilation database set (by default, one is not set).
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
flags = [
'-std=c99',
'-g',
'-Wall',
'-Werror'
'-Wextra'
]
# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# You can get CMake to generate this file for you by adding:
# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
# to your CMakeLists.txt file.
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
compilation_database_folder = ''
if os.path.exists( compilation_database_folder ):
database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
database = None
SOURCE_EXTENSIONS = ['.c', '.h']
def DirectoryOfThisScript():
return os.path.dirname( os.path.abspath( __file__ ) )
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
if not working_directory:
return list( flags )
new_flags = []
make_next_absolute = False
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
for flag in flags:
new_flag = flag
if make_next_absolute:
make_next_absolute = False
if not flag.startswith( '/' ):
new_flag = os.path.join( working_directory, flag )
for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break
if flag.startswith( path_flag ):
path = flag[ len( path_flag ): ]
new_flag = path_flag + os.path.join( working_directory, path )
break
if new_flag:
new_flags.append( new_flag )
return new_flags
def IsHeaderFile( filename ):
extension = os.path.splitext( filename )[ 1 ]
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
def GetCompilationInfoForFile( filename ):
# The compilation_commands.json file generated by CMake does not have entries
# for header files. So we do our best by asking the db for flags for a
# corresponding source file, if any. If one exists, the flags for that file
# should be good enough.
if IsHeaderFile( filename ):
basename = os.path.splitext( filename )[ 0 ]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if os.path.exists( replacement_file ):
compilation_info = database.GetCompilationInfoForFile(
replacement_file )
if compilation_info.compiler_flags_:
return compilation_info
return None
return database.GetCompilationInfoForFile( filename )
def FlagsForFile( filename, **kwargs ):
if database:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info = GetCompilationInfoForFile( filename )
if not compilation_info:
return None
final_flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_ )
# NOTE: This is just for YouCompleteMe; it's highly likely that your project
# does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR
# ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT.
try:
final_flags.remove( '-stdlib=libc++' )
except ValueError:
pass
else:
relative_to = DirectoryOfThisScript()
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
return {
'flags': final_flags,
'do_cache': True
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This directory contains auxiliary files related to project management. None of
them are required for actually making it work. They are only relevant if you
are using the related tools during development. If you know what they do you
know how to set them up. Also make sure you exclude them from Git:
.. code::
# From the project root directory
echo path/name >> .git/info/exclude
Where :code:`path` is the path to the "installed" version of the file. Don't
exclude the original files.
The MIT License (MIT)
Copyright (c) 2015 HiPhish
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# ---[ Make variables ]---------------------------------------------------------
CC = cc
CFLAGS = -std=c99 -g -Werror -Wall
PREFIX = ./build
# ---[ Main targets ]-----------------------------------------------------------
# Optimised release build
all: CFLAGS += -O2
all: bin header lib man
# Unoptimised build for debugging.
debug: CFLAGS += -O0
debug: release bin header lib man
# Run static analyser.
analyze: CFLAGS += --analyze -O0
analyze: ./source/roll
# ---[ Product targets ]--------------------------------------------------------
bin: ./source/roll
@mkdir -p $(PREFIX)/bin
@cp ./source/roll $(PREFIX)/bin/roll
header: ./source/roll.c
@mkdir -p $(PREFIX)/include
@cp ./source/libroll.h $(PREFIX)/include/roll.h
lib: ./source/libroll.a
@mkdir -p $(PREFIX)/lib
@cp ./source/libroll.a $(PREFIX)/lib/libroll.a
man: ./man/man1/roll.1
@mkdir -p $(PREFIX)/share/man/man1
@cp ./man/man1/roll.1 $(PREFIX)/share/man/man1/roll.1
doxygen: ./contrib/doxygen/Doxyfile source/roll.c ./source/libroll.c ./source/libroll.h
@mkdir -p $(PREFIX)
@( cat ./contrib/doxygen/Doxyfile ; echo "OUTPUT_DIRECTORY=\"$(PREFIX)/doxygen/\"" ) | doxygen -
ctags: ./tags
./tags: ./source/*.c ./source/*.h
@ctags -R --languages=c
# ---[ Source targets ]---------------------------------------------------------
./source/roll: ./source/roll.o ./source/libroll.a
@$(CC) $(CFLAGS) -o $@ $^
./source/roll.o: ./source/roll.c
@$(CC) $(CFLAGS) -c -o $@ $^
./source/libroll.a: ./source/libroll.o
@ar -rsc $@ $^
./source/libroll.o: ./source/libroll.c ./source/libroll.h
@$(CC) $(CFLAGS) -c -o $@ $<
# ---[ Phony targets ]----------------------------------------------------------
clean:
@rm -rf $(PREFIX)/
@rm -f ./tags
@rm -f ./source/roll ./source/*.o ./source/*.a
.\" Man-page for roll command-line utility
.TH roll 1 "October 2015" "Left footer" "HiPhish's Workshop"
.LO 1
.SH Name
roll \- Generate random numbers from arbitrary dice
.SH Synopsis
.B roll
.RI [ options ]
.IR r "d" s
.R [ ...
.RI [ options ]
.IR r "d" s
.R ]
.P
.SH Description
.B roll
generates uniformly distributed random numbers as if they were rolled using
dice, i.e. within the range of 1 (inclusive) to the number of sides
(inclusive). The instructings are given using the
.IR r "d" s
notation. For examples, to roll two six-sided dice use
.B 2d6
as the argument. The results are printed to the standard output sperated by one
space each.
.P
One can pass several dice configurations in succession, in which case the
results will be printed in the order their dice were specified.
.SH Options
.IP "--seed <seed>, -s <seed>"
Use the specified seed value for random numbers. Using the same seed guarantees
the same result. The seed only applies to successive dice, so make sure you
specify your seed first.
.IP "--no-seed (default)"
Disables a manually set seed, resetting it to the automatic one. See below for
cryptographic considerations.
.IP "--delimiter <delimiter> (default ' ')"
Set the delimiting string between roll results. The delimiter is copied
varbatinm so you can use multiple characters and even non-ASCII characters. The
default is one space.
.SH Cryptographic considerations
.P
The random numbers are generated using the POSIX
.B "random(3)"
function. If no seed is specified it is initialized using the
.B "srandomdev(3)"
function, which is supposed to be suitable for Cryptographic use. Please
consult the documentation of your implementation to be certain.
.SH Caveates
.P
The string will be terminated by a newline folling the last digit, that's
`0x0A` in bytes, there is no delimiter printed after the last digit. Take this
into consideration when you need the bytes thmeselves.
.. default-role:: code
######################################
`roll` - Roll dice on the command-line
######################################
Have you ever found yourself trying to reach maximum nerdage by playing
*Dungeons & Dragons* over a teletype only to find yourself lacking a way of
rolling dice and piping the result into your MUD? Me neither, but I wrote one
anyway, just in case. I guess you could also use it for diceware passphrase
generation, but seriously, who does that?
Building
########
All you need is `make`, a C99 standard-compliant compiler and a POSIX1.2001 compliant
implementation of the standard C library. Then run
.. code:: sh
make [bin header lib man] # build for release
make debug # build everything, but for debugging
make doxygen # build Doxygen documentation
in the root of the project to build the parts you need. If no arguments are
given everything is built. The compiled output can be found in the `build`
directory, or in a custom directory if the `PREFIX` Variable has been defined
(see below).
There is no installation script because the locations are different for
different systems. Just move the contents of the built directories where it is
appropriate for you. Or even better, have your package manager do it for your.
Makefile variables
==================
The following makefile variables can be passed to `make` when building:
======== ============= =======================================================
Variable Default Meaning
======== ============= =======================================================
`CC` cc Compiler
`CFLAGS` -std=c99 -g Compiler flags
-Werror -Wall
`PREFIX` ./build Root build directory, all other build directories are
relative to this.
======== ============= =======================================================
Using
#####
`roll` is both a standalone application as well as a C library. As always the
manpage is the authoritative documentation, consider this the TL;DR version.
Binary
======
Run the binary with this syntax:
.. code:: sh
roll [options] rds [... [options] rds]
What this means is that we specify our roll as the number of rolls, followed
immediately by a `d`, followed immediately by the number of sides per die. To
roll five six-sided dice we would give the argument `5d6`.
The options are `--seed` or `-s` followd by a number, `--no-seed` (the default)
and `--delimiter` followed by a delimiter string (a single space by default).
The delimiter is not printed after the last roll.
The seed is optional. If no seed is given a seed is generated according to the
implementation of `srandomdev()`, which is supposed to be cryptographically
suitable. Specifying the same seed guarantees the same results.
One can specify several rolls in succession, their results will be printed in
that order. A seed will always apply to all successive rolls, even when other
dice are used.
.. code:: sh
roll 3d6 --seed 7 2d8 3d4 --no-seed 2d20
In the above example the first three numbers are printed with unspecified seed,
the next five with a seed of seven and the last two are printed with
unspecified seed again. The argument `--no-seed` resets back the default
behaviour.
Library
=======
The library provides a function for generating uniformly distributed numbers in
the range form `1` to `sides` (both inclusive). The parameter `sides` must not
exceed the maximum allowed number of sides as specified in the header file.
.. code:: cpp
#define ROLLS 5
#define SIDES 6
int random_number[ROLLS];
for (int i = 0; i < 5; ++i) {
random_number[i] = roll_die(SIDES);
}
The function will fail an assertion if your number of sides is too large, so
check your value first. This is not an oversight, it's intentional.
License
#######
The MIT License (MIT)
Copyright (c) 2015 HiPhish
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
/** @file libroll.c
*
* Implementation of the `roll` library.
*/
#include <stdlib.h>
#include <assert.h>
#include "libroll.h"
int roll_die(int sides) {
assert(sides <= ROLL_SIDES_MAX);
if (sides == ROLL_SIDES_MAX) {
return random() + 1;
}
long end = (ROLL_SIDES_MAX/ sides) * sides;
assert(end > 0);
long rand;
while ((rand = random()) >= end) {;}
return (rand % sides) + 1;
}
/** @file libroll.h
*
* Public interface to the `roll` library.
*/
/** Maximum number of sides for a die.
*
* This is one more than the largest number the RNG can generate. That's
* because a die counts from one instead of zero.
*/
#define ROLL_SIDES_MAX 0xFFFFFFFF
/** Rolls a uniform die and returns the result.
*
* @param sides How many dies the die has.
*
* @return Integer between `1` and `sides` (both inclusive).
*
* @pre The caller is responsible for making sure the see was set.
* @pre The number of sides must be less or equal to `ROLL_SIDES_MAX`. This
* is mandatory, otherwise the assertion will fail and the program will
* crash.
*
* The functions uses the POSIX.1-2001 function `random()` to generate the
* roll, and the user is responsible for setting an appropriate seed first.
*
* To ensure uniformity the RNG might have to run more than once, discarding
* unsuitable numbers. Due to the randomness it's impossible to predict the
* number of attempts, but even in the worst case there is only a 50% chance
* of having to discard a number.
*/
int roll_die(int sides);
/** @file roll.c
*
* Main source file for `roll` command-line application.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libroll.h"
/** Possible return values of the application. */
enum return_values {
R_SUCCES, /**< Successful termination. */
R_ARGS_COUNT, /**< Wrong amount of arguments. */
R_ARGS_INV, /**< Invalid arguments. */
R_ARGS_DUP, /**< Duplicate argument. */
R_ARGS_ORDER, /**< Order of arguments wrong. */
R_SIDE_OVERFLOW, /**< Sides too large for RNG. */
};
/** Process arguments passed to the application.
*
* @param argc Number of arguments.
* @param argv Array of argument strings.
* @param sett Pointer to settings object to mutate.
*
* @return Error code, same as for the application.
*/
static int handle_args(int argc, char **argv);
/** Print usage instructions to standard error.
*
* @param error Error code, maps to an error message.
*/
static void print_usage(int error);
int main(int argc, char **argv) {
int return_val = R_SUCCES;
if ((return_val = handle_args(argc, argv))) {
goto failure;
}
/* Print a newline */
puts("");
return return_val;
failure:
print_usage(return_val);
return return_val;
}
static int handle_args(int argc, char **argv) {
/* Make sure we have the right number of arguments */
if (argc == 1) {
return R_ARGS_COUNT;
}
/* Character to delimit results */
char *delimiter = " ";
/* Set a safe seed */
srandomdev();
/* Loop over the arguments */
for (int i = 1; i < argc; ++i) {
char *arg = argv[i]; /* The current option to handle */
char *str = NULL; /* Use of second argument of strtol */
/* Argument is an option */
if (arg[0] == '-') {
/* Option sets the seed */
if (strncmp(arg, "--seed", 6) == 0 || strncmp(arg, "-s", 2) == 0) {
int seed; /* Value to seed */
/* There must be an argument after this */
if (++i == argc) {
return R_ARGS_COUNT;
}
arg = argv[i]; /* Shift to next argument */
seed = (int)strtol(arg, &str, 10);
if (*str != '\0') {
return R_ARGS_INV;
}
srandom(seed);
} else if (strncmp(arg, "--no-seed", 9) == 0) {
srandomdev();
} else if (strncmp(arg, "--delimiter", 11) == 0) {
/* There must be an argument after this */
if (++i == argc) {
return R_ARGS_COUNT;
}
arg = argv[i]; /* Shift to next argument */
delimiter = arg;
} else {
/* Unkown option */
return R_ARGS_INV;
}
} else {
/* The currently specified die settings */
int rolls = 0;
int sides = 0;
int j = 0; /* Index into the argument string */
int d = 0; /* Position of the 'd' character */
/* Loop over the argument string */
for (char c = arg[0]; c != '\0'; c = arg[++j]) {
/* Only `0` - `1`, `d` and `D` are allowed. */
if ((c < '0' || c > '9') && c != 'd' && c != 'D') {
return R_ARGS_INV;
}
/* The `d` marks the split */
if (c == 'd' || c == 'D') {
/* There may be only one `d` per die */
if (d != 0 || arg[j+1] == '\0') {
return R_ARGS_INV;
}
d = j; /* Set the index of the `d` */
}
}
char rolls_s[ d+1]; /* String of rolls. */
char sides_s[j-d+1]; /* String of sides. */
/* The `d` may not be the first character */
if (d == 0) {
return R_ARGS_INV;
}
/* Split `arg` into the two number string */
sprintf(rolls_s, "%.*s", d, arg );
sprintf(sides_s, "%.*s", j-d, arg + d+1);
/* We can be certain this will be successful */
rolls = atoi(rolls_s);
sides = atoi(sides_s);
/* Check for too many sides */
if (sides > ROLL_SIDES_MAX) {
return R_SIDE_OVERFLOW;
}
for (int k = 0; k < rolls; ++k) {
printf("%i", roll_die(sides));
/* Skip the last roll of the last die */
if (!(i == argc-1 && k == rolls-1)) {
printf("%s", delimiter);
}
}
}
}
return R_SUCCES;
}
static void print_usage(int error) {
/* Map error codes to error messages */
char *error_msg[] = {
[R_SUCCES ] = "",
[R_ARGS_COUNT] = "Error: Wrong number of arguments.\n",
[R_ARGS_INV ] = "Error: Invalid arguments.\n",
[R_ARGS_DUP ] = "Error: Duplicate arguments.\n",
[R_ARGS_ORDER] = "Error: Must specify seed before dice.\n",
[R_SIDE_OVERFLOW] = "Error: Sides must not be larger than '2^31 - 1'.\n",
};
char *usage_string = "Usage: roll <rolls>d<sides>\n";
fputs(error_msg[error], stderr);
fputs(usage_string , stderr);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment