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 pid_t pid;
68 struct timeval time;
69
70 gettimeofday(&time, NULL);
71 pid = getpid();
72 srand(pid * time.tv_usec * time.tv_sec);
73
74 return rand() % limit;
75#endif
76}
77
78static size_t
79catstr(char *dst, const char *src, size_t size)
80{
81#ifdef HAVE_STRLCAT
82 return strlcat(dst, src, size);
83#else
84 strncat(dst, src, size);
85 return strlen(dst);
86#endif
87}
88
89static const char
90*pana_e_nimi(const int nanpa_nimi)
91{
92 /* wrapper function to get word from array
93 *
94 * noun phrases in toki pona could have arbitrary amount of words and they
95 * are left grouped (the leftmost word is main, and words to the right
96 * are modifying it:
97 *
98 * ((nimi) sewi)
99 *
100 * special word 'pi' could be used to alter this grouping order to achieve
101 * something like english preposition 'of'
102 *
103 * ((suli) pi ((nimi) mute))
104 *
105 * this functions inserts 'pi' in the middle to avoid generating
106 * very heavy phrases.
107 */
108
109 if (nanpa_nimi == -1) {
110 return "pi";
111 } else if ((unsigned long) nanpa_nimi > suli_pi_nimi_toki) {
112 err(EXIT_FAILURE, "index out of bounds");
113 } else {
114 return nimi_toki[nanpa_nimi];
115 }
116}
117
118static void
119nanpa_nimi_pana_e_pi(struct nanpa_nimi *nn)
120{
121 /* handle a 'pi' instertion */
122 if (nn->nanpa_nimi == 2) {
123 /* this is made to allow for phrases with following structures:
124 *
125 * - word1 word2 word3
126 * - word1 pi word2 word3
127 */
128 nn->pi_li_lon = nsrand(2);
129 } else if (nn->nanpa_nimi > 2) {
130 nn->pi_li_lon = 1;
131 }
132 if (nn->pi_li_lon) {
133 /* since we insert whole word, number of words must be increased too */
134 nn->nanpa_nimi++;
135 nn->nanpa_pi = (nn->nanpa_nimi / 2);
136 }
137}
138
139static struct nanpa_nimi
140*pana_e_nanpa_nimi(unsigned int nanpa_nimi_sewi)
141{
142 /* generate nanpa_nimi with all the values useful for phrase generation
143 *
144 * when used, one should call weka_e_nanpa_nimi();
145 */
146
147 int i;
148 struct nanpa_nimi *nn;
149
150 nn = malloc(sizeof(struct nanpa_nimi));
151 if (nn == NULL) {
152 return NULL;
153 }
154
155 /* initialize nanpa_nimi with default values */
156 nn->pi_li_lon = 0;
157 nn->nanpa_pi = -1;
158 nn->nanpa_sewi_nimi = NULL;
159 nn->suli_pi_nimi_sewi = 0;
160
161 nn->nanpa_nimi = (nsrand(nanpa_nimi_sewi));
162
163 /* to use with arbitrary wordlist, remove following function call */
164 nanpa_nimi_pana_e_pi(nn);
165
166 nn->nanpa_sewi_nimi = calloc(nn->nanpa_nimi + 1, sizeof(int));
167 if (nn->nanpa_sewi_nimi == NULL) {
168 free(nn);
169 return NULL;
170 }
171 for (i = 0; i <= nn->nanpa_nimi; i++) {
172 nn->nanpa_sewi_nimi[i] = nsrand(suli_pi_nimi_toki);
173 }
174 if (nn->pi_li_lon) {
175 nn->nanpa_sewi_nimi[nn->nanpa_pi] = -1;
176 }
177
178 for (i = 0; i <= nn->nanpa_nimi; i++) {
179 nn->suli_pi_nimi_sewi += sizeof(char);
180 nn->suli_pi_nimi_sewi += strlen(pana_e_nimi(nn->nanpa_sewi_nimi[i]));
181 }
182
183 return nn;
184}
185
186static void
187weka_e_nanpa_nimi(struct nanpa_nimi *nn)
188{
189 if (nn != NULL) {
190 free(nn->nanpa_sewi_nimi);
191 free(nn);
192 }
193}
194
195char
196*nimi_sewi(unsigned int nanpa_nimi_sewi)
197{
198 int i;
199 char *nimi_pana;
200 struct nanpa_nimi *nn;
201
202 nn = pana_e_nanpa_nimi(nanpa_nimi_sewi);
203
204 nimi_pana = calloc(nn->suli_pi_nimi_sewi + 1, sizeof(char));
205 if (nimi_pana == NULL) {
206 weka_e_nanpa_nimi(nn);
207 err(EXIT_FAILURE, "memory allocation failed");
208 }
209
210 for (i = 0; i < nn->nanpa_nimi; i++) {
211 catstr(nimi_pana,
212 pana_e_nimi(nn->nanpa_sewi_nimi[i]),
213 nn->suli_pi_nimi_sewi);
214 catstr(nimi_pana, " ", nn->suli_pi_nimi_sewi);
215 }
216 catstr(nimi_pana,
217 pana_e_nimi(nn->nanpa_sewi_nimi[i]),
218 nn->suli_pi_nimi_sewi);
219
220 weka_e_nanpa_nimi(nn);
221
222 return nimi_pana;
223}
224
225void
226weka_e_nimi_sewi(char *nimi_sewi)
227{
228 if (nimi_sewi != NULL) {
229 free(nimi_sewi);
230 }
231}