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 <err.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
33
34#include <string.h>
35#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined( _NetBSD__) || defined(__DragonFly__)
36#define HAS_STRLCAT
37#endif
38
39#include <sys/types.h>
40#include <sys/time.h>
41
42#include "nimisewi.h"
43#include "nimitoki.h" /* nimi_toki, suli_pi_nimi_toki */
44
45static void
46open_e_nanpa_sewi(void)
47{
48 /* initialize pseudorandom generator using pid, microseconds and seconds
49 *
50 * for such silly program there's no need to implement cryptographically
51 * secure random number generator.
52 */
53
54 pid_t pid;
55 struct timeval time;
56
57 gettimeofday(&time, NULL);
58 pid = getpid();
59 srand(pid * time.tv_usec * time.tv_sec);
60}
61
62static const char
63*pana_e_nimi(const int nanpa_nimi)
64{
65 /* wrapper function to get word from array
66 *
67 * noun phrases in toki pona could have arbitrary amount of words and they
68 * are left grouped (the leftmost word is main, and words to the right
69 * are modifying it:
70 *
71 * ((nimi) sewi)
72 *
73 * special word 'pi' could be used to alter this grouping order to achieve
74 * something like english preposition 'of'
75 *
76 * ((suli) pi ((nimi) mute))
77 *
78 * this functions inserts 'pi' in the middle to avoid generating
79 * very heavy phrases.
80 */
81
82 if (nanpa_nimi == -1) {
83 return "pi";
84 } else if ((unsigned long) nanpa_nimi > suli_pi_nimi_toki) {
85 err(EXIT_FAILURE, "index out of bounds");
86 } else {
87 return nimi_toki[nanpa_nimi];
88 }
89}
90
91static void
92nanpa_nimi_pana_e_pi(struct nanpa_nimi *nn)
93{
94 /* handle a 'pi' instertion */
95 if (nn->nanpa_nimi == 2) {
96 /* this is made to allow for phrases with following structures:
97 *
98 * - word1 word2 word3
99 * - word1 pi word2 word3
100 */
101 nn->pi_li_lon = rand() % 2;
102 } else if (nn->nanpa_nimi > 2) {
103 nn->pi_li_lon = 1;
104 }
105 if (nn->pi_li_lon) {
106 /* since we insert whole word, number of words must be increased too */
107 nn->nanpa_nimi++;
108 nn->nanpa_pi = (nn->nanpa_nimi / 2);
109 }
110}
111
112static struct nanpa_nimi
113*pana_e_nanpa_nimi(void)
114{
115 /* generate nanpa_nimi with all the values useful for phrase generation
116 *
117 * when used, one should call weka_e_nanpa_nimi();
118 */
119
120 int i;
121 struct nanpa_nimi *nn;
122
123 nn = malloc(sizeof(struct nanpa_nimi));
124 if (nn == NULL) {
125 return NULL;
126 }
127
128 /* initialize nanpa_nimi with default values */
129 nn->pi_li_lon = 0;
130 nn->nanpa_pi = -1;
131 nn->nanpa_sewi_nimi = NULL;
132 nn->suli_pi_nimi_sewi = 0;
133
134 nn->nanpa_nimi = (rand() % 6);
135
136 /* to use with arbitrary wordlist, remove following function call */
137 nanpa_nimi_pana_e_pi(nn);
138
139 nn->nanpa_sewi_nimi = calloc(nn->nanpa_nimi + 1, sizeof(int));
140 if (nn->nanpa_sewi_nimi == NULL) {
141 free(nn);
142 return NULL;
143 }
144 for (i = 0; i <= nn->nanpa_nimi; i++) {
145 nn->nanpa_sewi_nimi[i] = rand() % suli_pi_nimi_toki;
146 }
147 if (nn->pi_li_lon) {
148 nn->nanpa_sewi_nimi[nn->nanpa_pi] = -1;
149 }
150
151 for (i = 0; i <= nn->nanpa_nimi; i++) {
152 nn->suli_pi_nimi_sewi += sizeof(char);
153 nn->suli_pi_nimi_sewi += strlen(pana_e_nimi(nn->nanpa_sewi_nimi[i]));
154 }
155
156 return nn;
157}
158
159static void
160weka_e_nanpa_nimi(struct nanpa_nimi *nn)
161{
162 if (nn != NULL) {
163 free(nn->nanpa_sewi_nimi);
164 free(nn);
165 }
166}
167
168static void
169string_cat(char *dst, const char *src, size_t size) {
170#ifdef HAS_STRLCAT
171 strlcat(dst, src, size);
172#else
173 strcat(dst, src);
174#endif
175}
176
177char
178*nimi_sewi()
179{
180 int i;
181 char *nimi_pana;
182 struct nanpa_nimi *nn;
183
184 open_e_nanpa_sewi();
185
186 nn = pana_e_nanpa_nimi();
187
188 nimi_pana = calloc(nn->suli_pi_nimi_sewi + 1, sizeof(char));
189 if (nimi_pana == NULL) {
190 weka_e_nanpa_nimi(nn);
191 return NULL;
192 }
193
194 for (i = 0; i < nn->nanpa_nimi; i++) {
195 string_cat(nimi_pana,
196 pana_e_nimi(nn->nanpa_sewi_nimi[i]),
197 nn->suli_pi_nimi_sewi);
198 string_cat(nimi_pana, " ", nn->suli_pi_nimi_sewi);
199 }
200 string_cat(nimi_pana,
201 pana_e_nimi(nn->nanpa_sewi_nimi[i]),
202 nn->suli_pi_nimi_sewi);
203
204 weka_e_nanpa_nimi(nn);
205
206 return nimi_pana;
207}
208
209void
210weka_e_nimi_sewi(char *nimi_sewi)
211{
212 if (nimi_sewi != NULL) {
213 free(nimi_sewi);
214 }
215}