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}