nimisewi.c (view raw)
1/* Copyright (c) 2021, la-ninpre
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 *
15 * *************************************************************************
16 *
17 * this file contains main code for nimisewi.
18 *
19 * nimisewi is a small program that generates random toki pona noun phrase.
20 * toki pona is a small constructed language created by sonja lang
21 * (see https://tokipona.org).
22 *
23 * functions and variables here a primarily named in tokipona, just because
24 * i can. but just in case there are annotations in english.
25 *
26 * sona nanpa li ike. taso ona li pali e ilo pona.
27 */
28
29#include <config.h>
30#include <err.h>
31#include <stdio.h>
32#include <unistd.h>
33#include <stdint.h>
34#include <sys/types.h>
35#include <sys/time.h>
36
37#ifdef HAVE_BSD_STDLIB_H
38 #include <bsd/stdlib.h>
39#else
40 #include <stdlib.h>
41#endif
42
43#ifdef HAVE_BSD_STRING_H
44 #include <bsd/string.h>
45#else
46 #include <string.h>
47#endif
48
49#include "nimisewi.h"
50#include "nimitoki.h" /* nimi_toki, suli_pi_nimi_toki */
51
52/* struct to hold stuff useful for random index generation */
53struct nanpa_nimi {
54 int nanpa_nimi; /* number of words - 1 */
55 int pi_li_lon; /* whether to insert 'pi' or not */
56 int nanpa_pi; /* index to insert 'pi', if pi_li_lon is 1 */
57 int *nanpa_sewi_nimi; /* random indices of words */
58 size_t suli_pi_nimi_sewi; /* length of generated phrase */
59};
60
61static uint32_t
62nsrand(uint32_t limit)
63{
64#ifdef HAVE_ARC4RANDOM_UNIFORM
65 return arc4random_uniform(limit);
66#else
67 return rand() % limit;
68#endif
69}
70
71static size_t
72catstr(char *dst, const char *src, size_t size)
73{
74#ifdef HAVE_STRLCAT
75 return strlcat(dst, src, size);
76#else
77 strncat(dst, src, size);
78 return strlen(dst);
79#endif
80}
81
82static void
83open_e_nanpa_sewi(void)
84{
85 /* initialize pseudorandom generator using pid, microseconds and seconds
86 *
87 * for such silly program there's no need to implement cryptographically
88 * secure random number generator.
89 */
90
91 pid_t pid;
92 struct timeval time;
93
94 gettimeofday(&time, NULL);
95 pid = getpid();
96 srand(pid * time.tv_usec * time.tv_sec);
97}
98
99static const char
100*pana_e_nimi(const int nanpa_nimi)
101{
102 /* wrapper function to get word from array
103 *
104 * noun phrases in toki pona could have arbitrary amount of words and they
105 * are left grouped (the leftmost word is main, and words to the right
106 * are modifying it:
107 *
108 * ((nimi) sewi)
109 *
110 * special word 'pi' could be used to alter this grouping order to achieve
111 * something like english preposition 'of'
112 *
113 * ((suli) pi ((nimi) mute))
114 *
115 * this functions inserts 'pi' in the middle to avoid generating
116 * very heavy phrases.
117 */
118
119 if (nanpa_nimi == -1) {
120 return "pi";
121 } else if ((unsigned long) nanpa_nimi > suli_pi_nimi_toki) {
122 err(EXIT_FAILURE, "index out of bounds");
123 } else {
124 return nimi_toki[nanpa_nimi];
125 }
126}
127
128static void
129nanpa_nimi_pana_e_pi(struct nanpa_nimi *nn)
130{
131 /* handle a 'pi' instertion */
132 if (nn->nanpa_nimi == 2) {
133 /* this is made to allow for phrases with following structures:
134 *
135 * - word1 word2 word3
136 * - word1 pi word2 word3
137 */
138 nn->pi_li_lon = nsrand(2);
139 } else if (nn->nanpa_nimi > 2) {
140 nn->pi_li_lon = 1;
141 }
142 if (nn->pi_li_lon) {
143 /* since we insert whole word, number of words must be increased too */
144 nn->nanpa_nimi++;
145 nn->nanpa_pi = (nn->nanpa_nimi / 2);
146 }
147}
148
149static struct nanpa_nimi
150*pana_e_nanpa_nimi(void)
151{
152 /* generate nanpa_nimi with all the values useful for phrase generation
153 *
154 * when used, one should call weka_e_nanpa_nimi();
155 */
156
157 int i;
158 struct nanpa_nimi *nn;
159
160 nn = malloc(sizeof(struct nanpa_nimi));
161 if (nn == NULL) {
162 return NULL;
163 }
164
165 /* initialize nanpa_nimi with default values */
166 nn->pi_li_lon = 0;
167 nn->nanpa_pi = -1;
168 nn->nanpa_sewi_nimi = NULL;
169 nn->suli_pi_nimi_sewi = 0;
170
171 nn->nanpa_nimi = (nsrand(6));
172
173 /* to use with arbitrary wordlist, remove following function call */
174 nanpa_nimi_pana_e_pi(nn);
175
176 nn->nanpa_sewi_nimi = calloc(nn->nanpa_nimi + 1, sizeof(int));
177 if (nn->nanpa_sewi_nimi == NULL) {
178 free(nn);
179 return NULL;
180 }
181 for (i = 0; i <= nn->nanpa_nimi; i++) {
182 nn->nanpa_sewi_nimi[i] = nsrand(suli_pi_nimi_toki);
183 }
184 if (nn->pi_li_lon) {
185 nn->nanpa_sewi_nimi[nn->nanpa_pi] = -1;
186 }
187
188 for (i = 0; i <= nn->nanpa_nimi; i++) {
189 nn->suli_pi_nimi_sewi += sizeof(char);
190 nn->suli_pi_nimi_sewi += strlen(pana_e_nimi(nn->nanpa_sewi_nimi[i]));
191 }
192
193 return nn;
194}
195
196static void
197weka_e_nanpa_nimi(struct nanpa_nimi *nn)
198{
199 if (nn != NULL) {
200 free(nn->nanpa_sewi_nimi);
201 free(nn);
202 }
203}
204
205char
206*nimi_sewi()
207{
208 int i;
209 char *nimi_pana;
210 struct nanpa_nimi *nn;
211
212#ifndef HAVE_ARC4RANDOM_UNIFORM
213 open_e_nanpa_sewi();
214#endif
215
216 nn = pana_e_nanpa_nimi();
217
218 nimi_pana = calloc(nn->suli_pi_nimi_sewi + 1, sizeof(char));
219 if (nimi_pana == NULL) {
220 weka_e_nanpa_nimi(nn);
221 return NULL;
222 }
223
224 for (i = 0; i < nn->nanpa_nimi; i++) {
225 catstr(nimi_pana,
226 pana_e_nimi(nn->nanpa_sewi_nimi[i]),
227 nn->suli_pi_nimi_sewi);
228 catstr(nimi_pana, " ", nn->suli_pi_nimi_sewi);
229 }
230 catstr(nimi_pana,
231 pana_e_nimi(nn->nanpa_sewi_nimi[i]),
232 nn->suli_pi_nimi_sewi);
233
234 weka_e_nanpa_nimi(nn);
235
236 return nimi_pana;
237}
238
239void
240weka_e_nimi_sewi(char *nimi_sewi)
241{
242 if (nimi_sewi != NULL) {
243 free(nimi_sewi);
244 }
245}