all repos — nimisewi_c @ bdcc9d24189821bfe47521f439430164ede92302

simple random toki pona phrase generator

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        err(EXIT_FAILURE, "memory allocation failed");
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}