mistress dice.c
  1/*
  2 * Public domain software
  3 * Dedicated to Stephen Bourne
  4 */
  5
  6#define _BSD_SOURCE
  7#define _XOPEN_SOURCE
  8#include <stdarg.h>
  9#include <stdio.h>
 10#include <stdlib.h>
 11#include <string.h>
 12#include <time.h>
 13#include <unistd.h>
 14
 15#include "mac.h"
 16
 17TYPE STRUCT DICE
 18{
 19	INT	ndic;
 20	INT	nsid;
 21	CONST STRUCT DICE	*next;
 22} DICE;
 23
 24STATIC CONST STRING	argv0;
 25STATIC BOOL	Cflag,Sflag,qflag;
 26
 27NORETURN STATIC VOID	usage();
 28NORETURN STATIC VOID	error(CONST STRING, ...);
 29STATIC VOID	initrand();
 30STATIC VOID	fixargv0(CONST STRING);
 31STATIC CONST DICE	*parsedice(CONST STRING);
 32STATIC CONST STRING	getlin();
 33STATIC INT	rolldice(CONST DICE *);
 34STATIC VOID	coinflip();
 35STATIC VOID	*xmalloc(SIZE);
 36
 37INT
 38main(INT argc, CHAR **argv)
 39{
 40	fixargv0(argv[0]); initrand();
 41	INT ch; WHILE (ch=getopt(argc,argv,"CSq"))!=-1
 42	DO	SWITCH ch IN
 43		CASE 'C':	Cflag=TRUE; BREAK;
 44		CASE 'S':	Sflag=TRUE; BREAK;
 45		CASE 'q':	qflag=TRUE; BREAK;
 46		OUT:	usage();
 47		ENDSW;
 48	OD; argc-=optind; argv+=optind;
 49	IF Cflag THEN coinflip(); RETURN 0 FI;
 50	CONST STRING e;
 51	IF Sflag THEN e=getlin() ELIF argc<1 THEN usage() ELSE e=argv[0] FI;
 52	AUTO d=parsedice(e);
 53	IF NOT qflag THEN printf("%s=",e) FI; printf("%d\n",rolldice(d));
 54	RETURN 0;
 55}
 56
 57STATIC VOID
 58initrand()
 59{
 60	srandom(time(NIL)+getpid());
 61}
 62
 63NORETURN STATIC VOID
 64usage()
 65{
 66	fprintf(STDERR, "usage:\t%s -C [-q]\n\t%s -S [-q]\n\t%s [-q] expr\n",
 67		argv0, argv0, argv0);
 68	exit(1);
 69}
 70
 71NORETURN STATIC VOID
 72error(CONST STRING fmt, ...)
 73{
 74	fprintf(STDERR,"%s: ",argv0);
 75	VALIST ap;
 76	va_start(ap, fmt);
 77	vfprintf(STDERR,fmt,ap);
 78	fputc('\n',STDERR);
 79	va_end(ap);
 80	exit(1);
 81}
 82
 83STATIC VOID
 84fixargv0(CONST STRING s)
 85{
 86	IF NOT (argv0=strrchr(s,'/')) THEN argv0=s ELSE argv0++ FI;
 87}
 88
 89STATIC CONST DICE *
 90parsedice(CONST STRING e)
 91{
 92	CHAR *end;
 93	DICE *p=xmalloc(SIZEOF(DICE));
 94	IF NOT *e THEN error("expected number of dice") FI;
 95	p->ndic=strtol(e,&end,10); e=end;
 96	IF *e++!='d' THEN error("expected `d' in expression") FI;
 97	IF NOT *e THEN error("expected number of sides") FI;
 98	p->nsid=strtol(e,&end,10); e=end;
 99	IF p->nsid==0 THEN error("can't have zero-sided die") FI;
100	IF NOT *e
101	THEN	RETURN p
102	ELIF *e++!='+'
103	THEN	error("expected `+' in expression")
104	FI;
105	p->next=parsedice(e); RETURN p;
106}
107
108STATIC CONST STRING
109getlin()
110{
111	STRING buf=xmalloc(80);
112	(VOID)fgets(buf,80,STDIN);
113	buf[strcspn(buf,"\n")]=0;
114	RETURN buf;
115}
116
117STATIC INT
118rolldice(CONST DICE *dice)
119{
120	INT res=0;
121	FOR AUTO d=dice; d; d=d->next
122	DO	FOR INT i=0; i<d->ndic; i++ DO res+=random()%d->nsid+1 OD;
123	OD;
124	RETURN res;
125}
126
127STATIC VOID
128coinflip()
129{
130	IF random()&1
131	THEN	printf("%s\n",qflag?"0":"heads")
132	ELSE	printf("%s\n",qflag?"1":"tails")
133	FI;
134}
135
136STATIC VOID *
137xmalloc(SIZE n)
138{
139	VOID *p=malloc(n); IF NOT p THEN abort() FI;
140	RETURN memset(p,0,n);
141}