all repos — nimisewi_c @ main

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    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}