1 /*
2     str_ops.c:
3 
4     Copyright (C) 2005, 2006 Istvan Varga
5               (C) 2005       Matt J. Ingalls, John ffitch
6               (C) 2013   V Lazzarini (new string code)
7 
8     This file is part of Csound.
9 
10     The Csound Library is free software; you can redistribute it
11     and/or modify it under the terms of the GNU Lesser General Public
12     License as published by the Free Software Foundation; either
13     version 2.1 of the License, or (at your option) any later version.
14 
15     Csound is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU Lesser General Public License for more details.
19 
20     You should have received a copy of the GNU Lesser General Public
21     License along with Csound; if not, write to the Free Software
22     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23     02110-1301 USA
24 */
25 
26 #include "csoundCore.h"
27 #define CSOUND_STR_OPS_C    1
28 #include "str_ops.h"
29 #include <ctype.h>
30 #ifdef HAVE_CURL
31 #include <curl/curl.h>
32 #include "corfile.h"
33 #endif
34 
35 #define STRSMAX 8
36 
37 #ifndef HAVE_SNPRINTF
38 /* add any compiler/system that has snprintf() */
39 #if defined(HAVE_C99)
40 #define HAVE_SNPRINTF   1
41 #endif
42 #endif
43 
s_opcode(CSOUND * csound,STRGET_OP * p)44 int32_t s_opcode(CSOUND *csound, STRGET_OP *p){
45     if (p->r->data == NULL){
46       p->r->data = (char *) csound->Malloc(csound, 15);
47       p->r->size = 15;
48     } else if (p->r->size < 15){
49       p->r->data = (char *) csound->ReAlloc(csound, p->r->data, 15);
50       p->r->size = 15;
51     }
52     snprintf(p->r->data, p->r->size, "%f", *p->indx);
53     return OK;
54 }
55 
s_opcode_k(CSOUND * csound,STRGET_OP * p)56 int32_t s_opcode_k(CSOUND *csound, STRGET_OP *p){
57      IGN(csound);
58     snprintf(p->r->data, p->r->size, "%f", *p->indx);
59     return OK;
60 }
61 
62 /* strset by John ffitch */
63 
str_set(CSOUND * csound,int32_t ndx,const char * s)64 static void str_set(CSOUND *csound, int32_t ndx, const char *s)
65 {
66     if (UNLIKELY(csound->strsets == NULL)) {
67       csound->strsmax = STRSMAX;
68       csound->strsets = (char **) csound->Calloc(csound, (csound->strsmax + 1)
69                                                          * sizeof(char*));
70     }
71     if (UNLIKELY(ndx > (int32_t) csound->strsmax)) {
72       int32_t   i, newmax;
73       /* assumes power of two STRSMAX */
74       newmax = (ndx | (STRSMAX - 1)) + 1;
75       csound->strsets = (char**) csound->ReAlloc(csound, csound->strsets,
76                                                  (newmax + 1) * sizeof(char*));
77       for (i = (csound->strsmax + 1); i <= newmax; i++)
78         csound->strsets[i] = NULL;
79       csound->strsmax = newmax;
80     }
81     if (UNLIKELY(ndx < 0))  {  /* -ve index */
82       csound->InitError(csound, Str("illegal strset index"));
83       return;
84     }
85 
86     if (csound->strsets[ndx] != NULL) {
87       if (strcmp(s, csound->strsets[ndx]) == 0)
88         return;
89       if (UNLIKELY(csound->oparms->msglevel & WARNMSG)) {
90         csound->Warning(csound, Str("strset index conflict at %d"), ndx);
91         csound->Warning(csound, Str("previous value: '%s', replaced with '%s'"),
92                                 csound->strsets[ndx], s);
93       }
94       csound->Free(csound, csound->strsets[ndx]);
95     }
96     csound->strsets[ndx] = (char*) csound->Malloc(csound, strlen(s) + 1);
97     strcpy(csound->strsets[ndx], s);
98     if ((csound->oparms->msglevel & 7) == 7)
99       csound->Message(csound, "Strsets[%d]: '%s'\n", ndx, s);
100 }
101 
strset_init(CSOUND * csound,STRSET_OP * p)102 int32_t strset_init(CSOUND *csound, STRSET_OP *p)
103 {
104     str_set(csound, (int32_t) MYFLT2LRND(*p->indx), p->str->data);
105     return OK;
106 }
107 
108 /* for argdecode.c */
109 
strset_option(CSOUND * csound,char * s)110 void strset_option(CSOUND *csound, char *s)
111 {
112     int32_t indx = 0;
113 
114     if (UNLIKELY(!isdigit(*s))) {
115        csound->Warning(csound, Str("--strset: invalid format"));
116        return;
117     }
118     do {
119       indx = (indx * 10) + (int32_t) (*s++ - '0');
120     } while (isdigit(*s));
121     if (UNLIKELY(*s++ != '=')){
122       csound->Warning(csound, Str("--strset: invalid format"));
123       return;
124     }
125     str_set(csound, indx, s);
126 }
127 
strget_init(CSOUND * csound,STRGET_OP * p)128 int32_t strget_init(CSOUND *csound, STRGET_OP *p)
129 {
130     int32_t   indx;
131     if (csound->ISSTRCOD(*(p->indx))) {
132       char *ss = csound->init_event->strarg;
133       if (ss == NULL)
134         return OK;
135       ss = get_arg_string(csound, *p->indx);
136       if (p->r->data == NULL) {
137         p->r->data = cs_strdup(csound, ss);
138         p->r->size = strlen(ss)+1;
139        }
140       else if ((int32_t) strlen(ss) >= p->r->size) {
141         csound->Free(csound, p->r->data);
142         p->r->data = cs_strdup(csound, ss);
143         p->r->size = strlen(ss) + 1;
144       }
145       else {
146         int32_t n = strlen(ss)+1;
147         p->r->size = n;
148         strNcpy(p->r->data, ss, n);
149         //p->r->data[p->r->size - 1] = '\0';
150       }
151       return OK;
152     }
153     indx = (int32_t)((double)*(p->indx) + (*(p->indx) >= FL(0.0) ? 0.5 : -0.5));
154     if (indx < 0 || indx > (int32_t) csound->strsmax ||
155         csound->strsets == NULL || csound->strsets[indx] == NULL)
156       return OK;
157     if (UNLIKELY((int32_t) strlen(csound->strsets[indx]) >= p->r->size)){
158       int32_t size = strlen(csound->strsets[indx]);
159       p->r->data = csound->ReAlloc(csound, p->r->data, size + 1);
160       p->r->size = size + 1;
161     }
162     strcpy((char*) p->r->data, csound->strsets[indx]);
163     return OK;
164 }
165 
StrOp_ErrMsg(void * p,const char * msg)166 static CS_NOINLINE int32_t StrOp_ErrMsg(void *p, const char *msg)
167 {
168     CSOUND      *csound = ((OPDS*) p)->insdshead->csound;
169     const char  *opname = csound->GetOpcodeName(p);
170 
171     if (UNLIKELY(csound->ids != NULL && csound->ids->insdshead == csound->curip))
172       return csound->InitError(csound, "%s: %s", opname, Str(msg));
173     else if (UNLIKELY(((OPDS*) p)->insdshead->pds != NULL))
174       return csound->PerfError(csound, (OPDS*)p,
175                                "%s: %s", opname, Str(msg));
176     else
177       csound->Warning(csound, "%s: %s", opname, Str(msg));
178 
179     return NOTOK;
180 }
181 /* strcpy */
strcpy_opcode_S(CSOUND * csound,STRCPY_OP * p)182 int32_t strcpy_opcode_S(CSOUND *csound, STRCPY_OP *p)
183 {
184     char  *newVal = p->str->data;
185     if (p->r->data == NULL) {
186       p->r->data =  cs_strdup(csound, newVal);
187       p->r->size =  strlen(p->str->data) + 1;
188       //printf("NULL str:%p %p \n", p->r, p->r->data);
189         return OK;
190     }
191     if (p->r->data == p->str->data){
192       //printf("sameptr str:%p %p \n", p->r->data);
193       return OK;
194     }
195     if (UNLIKELY((int32_t) strlen(newVal) >= p->r->size)){
196         csound->Free(csound, p->r->data);
197         p->r->data = cs_strdup(csound, newVal);
198         p->r->size = strlen(newVal) + 1;
199         //printf("dup str:%p %p \n", p->r, p->r->data);
200     }
201     else {
202       strcpy((char*) p->r->data, newVal);
203       p->r->size = strlen(newVal) + 1;
204       //printf("str:%p %p \n", p->r, p->r->data);
205     }
206 
207     return OK;
208 }
209 
strassign_opcode_S(CSOUND * csound,STRCPY_OP * p)210 int32_t strassign_opcode_S(CSOUND *csound, STRCPY_OP *p)
211 {
212    IGN(csound);
213     p->r->data = p->str->data;
214     p->r->size = p->str->size;
215     return OK;
216 }
strassign_opcode_Sk(CSOUND * csound,STRCPY_OP * p)217 int32_t strassign_opcode_Sk(CSOUND *csound, STRCPY_OP *p)
218 {
219     IGN(csound);
220     if (strcmp(p->r->data, p->str->data)!=0){
221       p->r->data = p->str->data;
222       p->r->size = p->str->size;
223     }
224     //csound->Message(csound, p->r->data);
225     return OK;
226 }
227 
str_changed(CSOUND * csound,STRCHGD * p)228 int32_t str_changed(CSOUND *csound, STRCHGD *p)
229 {
230     if (p->mem != NULL)
231       csound->Free(csound, p->mem);
232     p->mem = cs_strdup(csound, p->str->data);
233     *p->r = 0;
234     return OK;
235 }
236 
str_changed_k(CSOUND * csound,STRCHGD * p)237 int32_t str_changed_k(CSOUND *csound, STRCHGD *p)
238 {
239   if (p->str->data && ( p->mem == NULL || strcmp(p->str->data, p->mem)!=0)) {
240       csound->Free(csound, p->mem);
241       p->mem = cs_strdup(csound, p->str->data);
242       *p->r = 1;
243     }
244     else *p->r = 0;
245     return OK;
246 }
247 
248 extern char* get_strarg(CSOUND *csound, MYFLT p, char *strarg);
strcpy_opcode_p(CSOUND * csound,STRGET_OP * p)249 int32_t strcpy_opcode_p(CSOUND *csound, STRGET_OP *p)
250 {
251     if (csound->ISSTRCOD(*p->indx)) {
252       char *ss;
253       ss = get_arg_string(csound, *p->indx);
254       if (ss == NULL){
255       if (UNLIKELY(((OPDS*) p)->insdshead->pds != NULL))
256         return csoundPerfError(csound, (OPDS*)p,
257                                Str("NULL string\n"));
258       else
259         return csoundInitError(csound, Str("NULL string\n"));
260     }
261       if (p->r->data == NULL) {
262         p->r->data = cs_strdup(csound, ss);
263         p->r->size = strlen(ss)+1;
264       }
265       else if ((int32_t) strlen(ss) >= p->r->size) {
266         csound->Free(csound, p->r->data);
267         p->r->data = cs_strdup(csound, ss);
268         p->r->size = strlen(ss) + 1;
269       }
270       else {
271         strcpy(p->r->data,ss);
272         p->r->size = strlen(ss) + 1;
273       }
274     }
275     else {
276       p->r->data = csound->strarg2name(csound, NULL, p->indx, "soundin.", 0);
277       p->r->size = strlen(p->r->data) + 1;
278     }
279 
280     return OK;
281 }
282 
283 
284 /* strcat */
strcat_opcode(CSOUND * csound,STRCAT_OP * p)285 int32_t strcat_opcode(CSOUND *csound, STRCAT_OP *p)
286 {
287     int32_t size;
288     char *str1 = cs_strdup(csound, p->str1->data),
289          *str2 = cs_strdup(csound, p->str2->data);
290 
291     if (str1 == NULL || str2 == NULL){
292       csound->Free(csound,str1);
293       csound->Free(csound,str2);
294       if (UNLIKELY(((OPDS*) p)->insdshead->pds != NULL))
295         return csoundPerfError(csound, (OPDS*)p, Str("NULL string\n"));
296       else return csoundInitError(csound, Str("NULL string\n"));
297     }
298 
299     size = strlen(str1) + strlen(str2);
300 
301     if (p->r->data == NULL) {
302         p->r->data = csound->Calloc(csound, size+1);
303         p->r->size = size+1;
304     }
305     else if (UNLIKELY((int32_t) size >= p->r->size)) {
306        char *nstr =  csound->ReAlloc(csound, p->r->data, size + 1);
307        if (p->r->data == p->str1->data){
308          p->str1->data = nstr;
309          p->str1->size = size + 1;
310        }
311        if (p->r->data == p->str2->data){
312          p->str2->data = nstr;
313          p->str2->size = size + 1;
314        }
315          p->r->data = nstr;
316          p->r->size = size + 1;
317     }
318 
319     strNcpy((char*) p->r->data,  str1, p->r->size-1);
320     strcat((char*) p->r->data, str2);
321 
322     csound->Free(csound, str2);                 /* not needed anymore */
323     csound->Free(csound, str1);
324     return OK;
325 }
326 
327 /* strcmp */
328 
strcmp_opcode(CSOUND * csound,STRCMP_OP * p)329 int32_t strcmp_opcode(CSOUND *csound, STRCMP_OP *p)
330 {
331     int32_t     i;
332     if (p->str1->data == NULL || p->str2->data == NULL){
333       if (UNLIKELY(((OPDS*) p)->insdshead->pds != NULL))
334         return csoundPerfError(csound, (OPDS*)p, Str("NULL string\n"));
335       else return csoundInitError(csound, Str("NULL string\n"));
336     }
337 
338     *(p->r) = FL(0.0);
339     if (p->str1 == p->str2)
340       return OK;
341     i = strcmp((char*) p->str1->data, (char*) p->str2->data);
342     if (i < 0)
343       *(p->r) = FL(-1.0);
344     else if (i > 0)
345       *(p->r) = FL(1.0);
346 
347     return OK;
348 }
349 
350 /* perform a sprintf-style format -- based on code by Matt J. Ingalls */
351 
352 static CS_NOINLINE int32_t
sprintf_opcode_(CSOUND * csound,void * p,STRINGDAT * str,const char * fmt,MYFLT ** kvals,int32_t numVals,int32_t strCode)353 sprintf_opcode_(CSOUND *csound,
354                     void *p,          /* opcode data structure pointer       */
355                     STRINGDAT *str,   /* pointer to space for output string  */
356                     const char *fmt,  /* format string                       */
357                     MYFLT **kvals,    /* array of argument pointers          */
358                     int32_t numVals,      /* number of arguments             */
359                     int32_t strCode)      /* bit mask for string arguments   */
360 {
361     int32_t     len = 0;
362     char    *strseg, *outstring = str->data;
363     MYFLT   *parm = NULL;
364     int32_t     i = 0, j = 0, n;
365     const char  *segwaiting = NULL;
366     int32_t     maxChars, siz = strlen(fmt) + numVals*7 + 1;
367 
368     for (i = 0; i < numVals; i++) {
369       if (UNLIKELY(IS_ASIG_ARG(kvals[i]))) {
370         return StrOp_ErrMsg(p, Str("a-rate argument not allowed"));
371       }
372     }
373 
374     if (UNLIKELY((int32_t) ((OPDS*) p)->optext->t.inArgCount > 31)){
375       StrOp_ErrMsg(p, Str("too many arguments"));
376       return NOTOK;
377     }
378     if (numVals==0) {
379       strcpy(str->data, fmt);
380       return OK;
381     }
382 
383     strseg = csound->Malloc(csound, siz);
384     i = 0;
385 
386     while (1) {
387       if (UNLIKELY(i >= siz)) {
388         // return StrOp_ErrMsg(p, "format string too long");
389         siz *= 2;
390         strseg = csound->ReAlloc(csound, strseg, siz);
391       }
392       if (*fmt != '%' && *fmt != '\0') {
393         strseg[i++] = *fmt++;
394         continue;
395       }
396       if (fmt[0] == '%' && fmt[1] == '%') {
397         strseg[i++] = *fmt++;   /* Odd code: %% is usually % and as we
398                                    know the value of *fmt the loads are
399                                    unnecessary */
400         strseg[i++] = *fmt++;
401 
402         continue;
403       }
404 
405       /* if already a segment waiting, then lets print it */
406       if (segwaiting != NULL) {
407 
408         maxChars = str->size - len;
409         strseg[i] = '\0';
410         if (UNLIKELY(numVals <= 0)) {
411           csound->Free(csound, strseg);
412           return StrOp_ErrMsg(p, Str("insufficient arguments for format"));
413         }
414         numVals--;
415         /* if (UNLIKELY((*segwaiting == 's' && !(strCode & 1)) || */
416         /*              (*segwaiting != 's' && (strCode & 1)))) { */
417         /*   return StrOp_ErrMsg(p, "argument type inconsistent with format"); */
418         /* } */
419         strCode >>= 1;
420         parm = kvals[j++];
421 
422         switch (*segwaiting) {
423         case 'd':
424         case 'i':
425         case 'o':
426         case 'x':
427         case 'X':
428         case 'u':
429         case 'c':
430 #ifdef HAVE_SNPRINTF
431           if ((int32_t)strlen(strseg) + 24 > (int32_t)maxChars) {
432             int32_t offs = outstring - str->data;
433             str->data = csound->ReAlloc(csound, str->data,
434                                  str->size  + 24);
435             if(str->data == NULL) {
436               return StrOp_ErrMsg(p, Str("memory allocation failure"));
437             }
438             str->size += 24;
439             maxChars += 24;
440             outstring = str->data + offs;
441             //printf("maxchars = %d  %s\n", maxChars, strseg);
442             //printf("size: %d \n",str->size);
443 
444           }
445           n = snprintf(outstring, maxChars, strseg, (int32_t) MYFLT2LRND(*parm));
446 #else
447           n = sprintf(outstring, strseg, (int32_t) MYFLT2LRND(*parm));
448 #endif
449           break;
450         case 'e':
451         case 'E':
452         case 'f':
453         case 'F':
454         case 'g':
455         case 'G':
456 #ifdef HAVE_SNPRINTF
457           //printf("%d %d \n", str->size, strlen(str->data));
458           if (strlen(strseg) + 24 > (uint32_t)maxChars) {
459             int32_t offs = outstring - str->data;
460             str->data = csound->ReAlloc(csound, str->data, str->size  + 13);
461             if(str->data == NULL) {
462               return StrOp_ErrMsg(p, Str("memory allocation failure"));
463             }
464             str->size += 24;
465             maxChars += 24;
466             outstring = str->data + offs;
467             //printf("maxchars = %d  %s\n", maxChars, strseg);
468           }
469           //printf("%d %d \n", str->size, strlen(str->data));
470           n = snprintf(outstring, maxChars, strseg, (double)*parm);
471 #else
472           n = sprintf(outstring, strseg, (double)*parm);
473 #endif
474           break;
475         case 's':
476           if (((STRINGDAT*)parm)->data == str->data) {
477             csound->Free(csound, strseg);
478             return StrOp_ErrMsg(p, Str("output argument may not be "
479                                        "the same as any of the input args"));
480           }
481           if ((((STRINGDAT*)parm)->size+strlen(strseg)) >= (uint32_t)maxChars) {
482             int32_t offs = outstring - str->data;
483             str->data = csound->ReAlloc(csound, str->data,
484                                         str->size  + ((STRINGDAT*)parm)->size +
485                                         strlen(strseg));
486            if(str->data == NULL){
487               return StrOp_ErrMsg(p, Str("memory allocation failure"));
488             }
489             str->size += ((STRINGDAT*)parm)->size + strlen(strseg);
490             maxChars += ((STRINGDAT*)parm)->size + strlen(strseg);
491             outstring = str->data + offs;
492           }
493           n = snprintf(outstring, maxChars, strseg, ((STRINGDAT*)parm)->data);
494           break;
495         default:
496           csound->Free(csound, strseg);
497           return StrOp_ErrMsg(p, Str("invalid format string"));
498         }
499         if (n < 0 || n >= maxChars) {
500           /* safely detected excess string length */
501             int32_t offs = outstring - str->data;
502             str->data = csound->ReAlloc(csound, str->data, maxChars*2);
503             if (str->data == NULL) {
504               return StrOp_ErrMsg(p, Str("memory allocation failure"));
505             }
506             outstring = str->data + offs;
507             str->size = maxChars*2;
508             // VL: Coverity says this is unused. (which is true)
509             // maxChars += str->size;
510 
511         }
512         outstring += n;
513         len += n;
514         i = 0;
515       }
516 
517       if (*fmt == '\0')
518         break;
519       /* copy the '%' */
520       strseg[i++] = *fmt++;
521       /* find the format code */
522       segwaiting = fmt;
523 
524       while (!isalpha(*segwaiting) && *segwaiting != '\0')
525         segwaiting++;
526     }
527     if (UNLIKELY(numVals > 0)) {
528       csound->Free(csound, strseg);
529       return StrOp_ErrMsg(p, Str("too many arguments for format"));
530     }
531     csound->Free(csound, strseg);
532     return OK;
533 }
534 
sprintf_opcode(CSOUND * csound,SPRINTF_OP * p)535 int32_t sprintf_opcode(CSOUND *csound, SPRINTF_OP *p)
536 {
537     int32_t size = p->sfmt->size+ 18*((int32_t) p->INOCOUNT);
538     //printf("%d %d \n", p->r->size, strlen(p->r->data));
539     if (p->r->data == NULL || p->r->size < size) {
540       /* this 10 is 1n incorrect guess which is OK with numbers*/
541       p->r->data = csound->Calloc(csound, size);
542       p->r->size = size;
543     }
544     if (UNLIKELY(sprintf_opcode_(csound, p, p->r,
545                                   (char*) p->sfmt->data, &(p->args[0]),
546                                   (int32_t) p->INOCOUNT - 1,0) == NOTOK)) {
547       ((char*) p->r->data)[0] = '\0';
548       return NOTOK;
549     }
550     return OK;
551 }
552 
printf_opcode_(CSOUND * csound,PRINTF_OP * p)553 static CS_NOINLINE int32_t printf_opcode_(CSOUND *csound, PRINTF_OP *p)
554 {
555     STRINGDAT buf;
556     int32_t   err;
557     buf.size = /*strlen(p->sfmt->data) +*/ 3072;
558     buf.data = csound->Calloc(csound, buf.size);
559 
560     err = sprintf_opcode_(csound, p, &buf, (char*) p->sfmt->data, &(p->args[0]),
561                           (int32_t) p->INOCOUNT - 2,0);
562     if (LIKELY(err == OK))
563       csound->MessageS(csound, CSOUNDMSG_ORCH, "%s", buf.data);
564     csound->Free(csound, buf.data);
565 
566     return err;
567 }
568 
printf_opcode_init(CSOUND * csound,PRINTF_OP * p)569 int32_t printf_opcode_init(CSOUND *csound, PRINTF_OP *p)
570 {
571     if (*p->ktrig > FL(0.0))
572       return (printf_opcode_(csound, p));
573     return OK;
574 }
575 
printf_opcode_set(CSOUND * csound,PRINTF_OP * p)576 int32_t printf_opcode_set(CSOUND *csound, PRINTF_OP *p)
577 {
578     (void) csound;
579     p->prv_ktrig = FL(0.0);
580     return OK;
581 }
582 
printf_opcode_perf(CSOUND * csound,PRINTF_OP * p)583 int32_t printf_opcode_perf(CSOUND *csound, PRINTF_OP *p)
584 {
585     MYFLT ktrig = *p->ktrig;
586     if (ktrig == p->prv_ktrig)
587       return OK;
588     p->prv_ktrig = ktrig;
589     if (ktrig > FL(0.0))
590       return (printf_opcode_(csound, p));
591     return OK;
592 }
593 
puts_opcode_init(CSOUND * csound,PUTS_OP * p)594 int32_t puts_opcode_init(CSOUND *csound, PUTS_OP *p)
595 {
596     p->noNewLine = (*p->no_newline == FL(0.0) ? 0 : 1);
597     if (*p->ktrig > FL(0.0)) {
598       if (!p->noNewLine)
599         csound->MessageS(csound, CSOUNDMSG_ORCH, "%s\n", (char*) p->str->data);
600       else
601         csound->MessageS(csound, CSOUNDMSG_ORCH, "%s", (char*) p->str->data);
602     }
603     p->prv_ktrig = *p->ktrig;
604 
605     return OK;
606 }
607 
puts_opcode_perf(CSOUND * csound,PUTS_OP * p)608 int32_t puts_opcode_perf(CSOUND *csound, PUTS_OP *p)
609 {
610     if (*p->ktrig != p->prv_ktrig && *p->ktrig > FL(0.0)) {
611       p->prv_ktrig = *p->ktrig;
612       if (!p->noNewLine)
613         csound->MessageS(csound, CSOUNDMSG_ORCH, "%s\n", (char*) p->str->data);
614       else
615         csound->MessageS(csound, CSOUNDMSG_ORCH, "%s", (char*) p->str->data);
616     }
617 
618     return OK;
619 }
620 
strtod_opcode_p(CSOUND * csound,STRTOD_OP * p)621 int32_t strtod_opcode_p(CSOUND *csound, STRTOD_OP *p)
622 {
623     char    *s = NULL, *tmp;
624     double  x;
625 
626     if (csound->ISSTRCOD(*p->str))
627       s = get_arg_string(csound, *p->str);
628     else {
629       int32_t ndx = (int32_t) MYFLT2LRND(*p->str);
630       if (ndx >= 0 && ndx <= (int32_t) csound->strsmax && csound->strsets != NULL)
631         s = csound->strsets[ndx];
632     }
633     if (UNLIKELY(s == NULL))
634       return StrOp_ErrMsg(p, Str("empty string"));
635     while (isblank(*s)) s++;
636     if (UNLIKELY(*s == '\0'))
637      return StrOp_ErrMsg(p, Str("empty string"));
638     x = cs_strtod(s, &tmp);
639     if (UNLIKELY(*tmp != '\0'))
640       return StrOp_ErrMsg(p, Str("invalid format"));
641     *p->indx = (MYFLT) x;
642 
643     return OK;
644 }
645 
strtod_opcode_S(CSOUND * csound,STRSET_OP * p)646 int32_t strtod_opcode_S(CSOUND *csound, STRSET_OP *p)
647 {
648     IGN(csound);
649     char    *s = NULL, *tmp;
650     double  x;
651     s = (char*) p->str->data;
652     while (isblank(*s)) s++;
653     if (UNLIKELY(*s == '\0'))
654      return StrOp_ErrMsg(p, Str("empty string"));
655     x = cs_strtod(s, &tmp);
656     if (UNLIKELY(*tmp != '\0'))
657      return StrOp_ErrMsg(p, Str("invalid format"));
658     *p->indx = (MYFLT) x;
659 
660     return OK;
661 }
662 
strtol_opcode_S(CSOUND * csound,STRSET_OP * p)663 int32_t strtol_opcode_S(CSOUND *csound, STRSET_OP *p)
664 {
665     IGN(csound);
666     char  *s = NULL;
667     int32_t   sgn = 0, radix = 10;
668     int32_t  x = 0;
669 
670     s = (char*) p->str->data;
671     while (isblank(*s)) s++;
672     if (UNLIKELY(*s == '\0'))
673       return StrOp_ErrMsg(p, Str("empty string"));
674     if (*s == '+') s++;
675     else if (*s == '-') sgn++, s++;
676     if (*s == '0') {
677       if (s[1] == 'x' || s[1] == 'X')
678         radix = 16, s += 2;
679       else if (s[1] != '\0')
680         radix = 8, s++;
681       else {
682         *p->indx = FL(0.0);
683         return OK;
684       }
685     }
686     if (UNLIKELY(*s == '\0'))
687       return StrOp_ErrMsg(p, Str("invalid format"));
688     switch (radix) {
689       case 8:
690         while (*s >= '0' && *s <= '7') x = (x * 8L) + (int32_t) (*s++ - '0');
691         break;
692       case 10:
693         while (isdigit(*s)) x = (x * 10L) + (int32_t) (*s++ - '0');
694         break;
695       default:
696         while (1) {
697           if (isdigit(*s))
698             x = (x * 16L) + (int32_t) (*s++ - '0');
699           else if (*s >= 'A' && *s <= 'F')
700             x = (x * 16L) + (int32_t) (*s++ - 'A') + 10L;
701           else if (*s >= 'a' && *s <= 'f')
702             x = (x * 16L) + (int32_t) (*s++ - 'a') + 10L;
703           else
704             break;
705         }
706     }
707     if (UNLIKELY(*s != '\0'))
708       return StrOp_ErrMsg(p, Str("invalid format"));
709     if (sgn) x = -x;
710     *p->indx = (MYFLT) x;
711 
712     return OK;
713 }
714 
715 
strtol_opcode_p(CSOUND * csound,STRTOD_OP * p)716 int32_t strtol_opcode_p(CSOUND *csound, STRTOD_OP *p)
717 {
718     char  *s = NULL;
719     int32_t   sgn = 0, radix = 10;
720     int32_t   x = 0L;
721 
722     if (csound->ISSTRCOD(*p->str))
723         s = get_arg_string(csound, *p->str);
724       else {
725         int32_t ndx = (int32_t) MYFLT2LRND(*p->str);
726         if (ndx >= 0 && ndx <= (int32_t) csound->strsmax && csound->strsets != NULL)
727           s = csound->strsets[ndx];
728       }
729       if (UNLIKELY(s == NULL))
730         return StrOp_ErrMsg(p, Str("empty string"));
731 
732       while (isblank(*s)) s++;
733     if (UNLIKELY(*s == '\0'))
734       return StrOp_ErrMsg(p, Str("empty string"));
735     if (*s == '+') s++;
736     else if (*s == '-') sgn++, s++;
737     if (*s == '0') {
738       if (s[1] == 'x' || s[1] == 'X')
739         radix = 16, s += 2;
740       else if (s[1] != '\0')
741         radix = 8, s++;
742       else {
743         *p->indx = FL(0.0);
744         return OK;
745       }
746     }
747     if (UNLIKELY(*s == '\0'))
748       return StrOp_ErrMsg(p, Str("invalid format"));
749     switch (radix) {
750       case 8:
751         while (*s >= '0' && *s <= '7') x = (x * 8L) + (int32_t) (*s++ - '0');
752         break;
753       case 10:
754         while (isdigit(*s)) x = (x * 10L) + (int32_t) (*s++ - '0');
755         break;
756       default:
757         while (1) {
758           if (isdigit(*s))
759             x = (x * 16L) + (int32_t) (*s++ - '0');
760           else if (*s >= 'A' && *s <= 'F')
761             x = (x * 16L) + (int32_t) (*s++ - 'A') + 10L;
762           else if (*s >= 'a' && *s <= 'f')
763             x = (x * 16L) + (int32_t) (*s++ - 'a') + 10L;
764           else
765             break;
766         }
767     }
768     if (UNLIKELY(*s != '\0'))
769       return StrOp_ErrMsg(p, Str("invalid format"));
770     if (sgn) x = -x;
771     *p->indx = (MYFLT) x;
772 
773     return OK;
774 }
775 
776 /**
777  * Sdst    strsub      Ssrc[, istart[, iend]]
778  * Sdst    strsubk     Ssrc, kstart, kend
779  *
780  * Extract a part of Ssrc, from istart to iend; if istart or iend is
781  * less than 0, or greater than the length of the source string, it is
782  * interpreted as the end of the source string. istart > iend will
783  * reverse the string. The default parameters are istart = 0, iend = -1.
784  */
785 
strsub_opcode(CSOUND * csound,STRSUB_OP * p)786 int32_t strsub_opcode(CSOUND *csound, STRSUB_OP *p)
787 {
788     const char  *src;
789     char        *dst;
790     int32_t         i, len, strt, end, rev = 0;
791 
792     if (p->Ssrc->data == NULL) return NOTOK;
793     if (p->Sdst->data == NULL || p->Sdst->size < p->Ssrc->size) {
794       int32_t size = p->Ssrc->size;
795       if (p->Sdst->data != NULL) csound->Free(csound, p->Sdst->data);
796       p->Sdst->data = csound->Calloc(csound, size);
797       p->Sdst->size = size;
798     }
799 
800     src = (char*) p->Ssrc->data;
801     dst = (char*) p->Sdst->data;
802     len = (int32_t) strlen(src);
803 #if defined(MSVC) || (defined(__GNUC__) && defined(__i386__))
804     strt = (int32_t) MYFLT2LRND(*(p->istart));
805     end = (int32_t) MYFLT2LRND(*(p->iend));
806 #else
807     strt = (int32_t) (*(p->istart) + FL(1.5)) - 1;
808     end = (int32_t) (*(p->iend) + FL(1.5)) - 1;
809 #endif
810     if (strt < 0 || strt > len)
811       strt = len;
812     if (end < 0 || end > len)
813       end = len;
814     if (strt == end) {
815       /* trivial case: empty output */
816       dst[0] = '\0';
817       return OK;
818     }
819     if (strt > end) {
820       int32_t   tmp = strt;
821       /* reverse output */
822       strt = end;
823       end = tmp;
824       rev = 1;
825     }
826 
827     src += strt;
828     len = end - strt;
829     if (UNLIKELY(len >=  p->Sdst->size)) {
830       p->Sdst->data = csound->ReAlloc(csound, p->Sdst->data, len+1);
831       p->Sdst->size = len+1;
832       dst = (char*) p->Sdst->data;
833     }
834     i = 0;
835     if (!rev || p->Sdst->data == p->Ssrc->data) {
836       /* copying in forward direction is safe */
837       /* even if Ssrc and Sdst are the same */
838       do {
839         dst[i] = src[i];
840       } while (++i < len);
841       dst[i] = '\0';
842       if (rev) {
843         int32_t   j;
844         /* if the destination string variable is the same as the source, */
845         /* reversing needs to be handled in a special way */
846         i = 0;
847         j = len - 1;
848         while (i < j) {
849           char  tmp = dst[i];
850           dst[i++] = dst[j];
851           dst[j--] = tmp;
852         }
853       }
854     }
855     else {
856       /* reverse string out of place (Ssrc and Sdst are not the same) */
857       int32_t   j = len;
858       do {
859         dst[i] = src[--j];
860       } while (++i < len);
861       dst[i] = '\0';
862     }
863     return OK;
864 }
865 
866 /**
867  * ichr    strchar     Sstr[, ipos]
868  * kchr    strchark    Sstr[, kpos]
869  *
870  * Return the ASCII code of the character in Sstr at ipos (defaults to 0).
871  * If ipos is out of range, 0 is returned.
872  */
873 
strchar_opcode(CSOUND * csound,STRCHAR_OP * p)874 int32_t strchar_opcode(CSOUND *csound, STRCHAR_OP *p)
875 {
876     int32_t     len = (int32_t) strlen((char*) p->Ssrc->data);
877 #if defined(MSVC) || (defined(__GNUC__) && defined(__i386__))
878     int32_t     pos = (int32_t) MYFLT2LRND(*(p->ipos));
879 #else
880     int32_t     pos = (int32_t) (*(p->ipos) + FL(1.5)) - 1;
881 #endif
882 
883     (void) csound;
884     if (pos < 0 || pos >= len)
885       *(p->ichr) = FL(0.0);
886     else
887       *(p->ichr) = (MYFLT) ((int32_t)((unsigned char)((char*) p->Ssrc->data)[pos]));
888 
889     return OK;
890 }
891 
892 /**
893  * ilen    strlen      Sstr
894  * klen    strlenk     Sstr
895  *
896  * Return the length of a string.
897  */
898 
strlen_opcode(CSOUND * csound,STRLEN_OP * p)899 int32_t strlen_opcode(CSOUND *csound, STRLEN_OP *p)
900 {
901     (void) csound;
902     if (p->Ssrc->size)
903       *(p->ilen) = (MYFLT) strlen(p->Ssrc->data);
904     else *(p->ilen) = FL(0.0);
905     return OK;
906 }
907 
908 /**
909  * Sdst    strupper    Ssrc
910  * Sdst    strupperk   Ssrc
911  * Sdst    strlower    Ssrc
912  * Sdst    strlowerk   Ssrc
913  *
914  * Convert a string to upper or lower case.
915  */
916 
strupper_opcode(CSOUND * csound,STRUPPER_OP * p)917 int32_t strupper_opcode(CSOUND *csound, STRUPPER_OP *p)
918 {
919     const char  *src;
920     char        *dst;
921     int32_t         i;
922     if (p->Ssrc->data == NULL) return NOTOK;
923     if (p->Sdst->data == NULL || p->Sdst->size < p->Ssrc->size) {
924       int32_t size = p->Ssrc->size;
925       if (p->Sdst->data != NULL) csound->Free(csound, p->Sdst->data);
926       p->Sdst->data = csound->Calloc(csound, size);
927       p->Sdst->size = size;
928     }
929 
930     (void) csound;
931     src = (char*) p->Ssrc->data;
932     dst = (char*) p->Sdst->data;
933     for (i = 0; src[i] != '\0'; i++) {
934       unsigned char   tmp;
935       tmp = (unsigned char) src[i];
936       dst[i] = (char) (islower(tmp) ? (unsigned char) toupper(tmp) : tmp);
937     }
938 
939     return OK;
940 }
941 
strlower_opcode(CSOUND * csound,STRUPPER_OP * p)942 int32_t strlower_opcode(CSOUND *csound, STRUPPER_OP *p)
943 {
944     const char  *src;
945     char        *dst;
946     int32_t         i;
947     if (p->Ssrc->data == NULL) return NOTOK;
948     if (p->Sdst->data == NULL || p->Sdst->size < p->Ssrc->size) {
949       int32_t size = p->Ssrc->size;
950       if (p->Sdst->data != NULL) csound->Free(csound,p->Sdst->data);
951       p->Sdst->data = csound->Calloc(csound, size);
952       p->Sdst->size = size;
953     }
954 
955     (void) csound;
956     src = (char*) p->Ssrc->data;
957     dst = (char*) p->Sdst->data;
958     for (i = 0; src[i] != '\0'; i++) {
959       unsigned char   tmp;
960       tmp = (unsigned char) src[i];
961       dst[i] = (char) (isupper(tmp) ? (unsigned char) tolower(tmp) : tmp);
962     }
963 
964     return OK;
965 }
966 
967 /**
968  * Sval    getcfg      iopt
969  *
970  * Returns the value of a global setting (e.g. input file name) as a
971  * string.
972  */
973 
getcfg_opcode(CSOUND * csound,GETCFG_OP * p)974 int32_t getcfg_opcode(CSOUND *csound, GETCFG_OP *p)
975 {
976     const char  *s;
977 #if defined(MSVC) || (defined(__GNUC__) && defined(__i386__))
978     int32_t         opt = (int32_t) MYFLT2LRND(*(p->iopt));
979 #else
980     int32_t         opt = (int32_t) (*(p->iopt) + FL(0.5));
981 #endif
982     char        buf[32];
983 
984     if (p->Sdst->size < 32){
985       csound->Free(csound, p->Sdst->data);
986       p->Sdst->data = csound->Calloc(csound,32);
987       p->Sdst->size = 32;
988     }
989     //((char*) p->Sdst->data)[0] = '\0';
990     buf[0] = '\0';
991     s = &(buf[0]);
992     switch (opt) {
993     case 1:             /* maximum length of variable */
994       snprintf(&(buf[0]), 32, "%d", (int32_t) p->Sdst->size - 1);
995       break;
996     case 2:             /* input sound file name */
997       s = (csound->oparms->sfread && !csound->initonly ?
998            csound->oparms->infilename : (char*) NULL);
999       break;
1000     case 3:             /* output sound file name */
1001       s = (csound->oparms->sfwrite && !csound->initonly ?
1002            csound->oparms->outfilename : (char*) NULL);
1003       break;
1004     case 4:             /* is real-time audio being used ? (0: no, 1: yes) */
1005       buf[0] = '0';
1006       buf[1] = '\0';
1007       if ((csound->oparms->sfread && !csound->initonly &&
1008            check_rtaudio_name(csound->oparms->infilename, NULL, 0) >= 0) ||
1009           (csound->oparms->sfwrite && !csound->initonly &&
1010            check_rtaudio_name(csound->oparms->outfilename, NULL, 1) >= 0))
1011         buf[0] = '1';
1012       break;
1013     case 5:             /* is beat mode being used ? (0: no, 1: yes) */
1014       buf[0] = (csound->oparms->Beatmode ? '1' : '0');
1015       buf[1] = '\0';
1016       break;
1017     case 6:             /* host OS name */
1018 #ifdef LINUX
1019       s = "Linux";
1020 #elif defined(WIN32)
1021       s = "Win32";
1022 #elif defined(MACOSX)
1023       s = "MacOSX";
1024 #else
1025       s = "unknown";
1026 #endif
1027       break;
1028     case 7:             /* is the channel I/O callback set ? (0: no, 1: yes) */
1029       buf[0] = 0;
1030       buf[1] = '\0';
1031       break;
1032     default:
1033       return csound->InitError(csound, Str("invalid option code: %g"),
1034                                        *(p->iopt));
1035     }
1036     if (s != NULL) {
1037 
1038       if (p->Sdst->data == NULL) {
1039         int32_t size = strlen(s) + 1;
1040         p->Sdst->data = csound->Calloc(csound, size);
1041         p->Sdst->size = size;
1042       }
1043       else if (UNLIKELY((int32_t) strlen(s) >=  p->Sdst->size)) {
1044         p->Sdst->data = csound->ReAlloc(csound, p->Sdst->data, strlen(s) + 1);
1045         p->Sdst->size = strlen(s) + 1;
1046       }
1047       strcpy((char*) p->Sdst->data, s);
1048     }
1049     return OK;
1050 }
1051 
1052 /**
1053  * ipos    strindex    Sstr1, Sstr2
1054  * kpos    strindexk   Sstr1, Sstr2
1055  *
1056  * Return the position of the first occurence of Sstr2 in Sstr1,
1057  * or -1 if not found. If Sstr2 is empty, 0 is returned.
1058  */
1059 
strindex_opcode(CSOUND * csound,STRINDEX_OP * p)1060 int32_t strindex_opcode(CSOUND *csound, STRINDEX_OP *p)
1061 {
1062     const char  *s1 = (char*) p->Ssrc1->data;
1063     const char  *s2 = (char*) p->Ssrc2->data;
1064     int32_t         i, j;
1065 
1066     (void) csound;
1067     /* search substring from left to right, */
1068     /* and return position of first match */
1069     i = j = 0;
1070     while (s2[j] != '\0') {
1071       if (s1[i] == '\0') {
1072         *(p->ipos) = -FL(1.0);
1073         return OK;
1074       }
1075       j = (s1[i] != s2[j] ? 0 : j + 1);
1076       i++;
1077     }
1078     *(p->ipos) = (MYFLT) (i - j);
1079 
1080     return OK;
1081 }
1082 
1083 /**
1084  * ipos    strrindex   Sstr1, Sstr2
1085  * kpos    strrindexk  Sstr1, Sstr2
1086  *
1087  * Return the position of the last occurence of Sstr2 in Sstr1,
1088  * or -1 if not found. If Sstr2 is empty, the length of Sstr1 is
1089  * returned.
1090  */
1091 
strrindex_opcode(CSOUND * csound,STRINDEX_OP * p)1092 int32_t strrindex_opcode(CSOUND *csound, STRINDEX_OP *p)
1093 {
1094     const char  *s1 = (char*) p->Ssrc1->data;
1095     const char  *s2 = (char*) p->Ssrc2->data;
1096     int32_t         i, j, k;
1097 
1098     (void) csound;
1099     /* search substring from left to right, */
1100     /* and return position of last match */
1101     i = j = 0;
1102     k = -1;
1103     while (1) {
1104       if (s2[j] == '\0') {
1105         k = i - j;
1106         j = 0;
1107       }
1108       if (s1[i] == '\0')
1109         break;
1110       j = (s1[i] != s2[j] ? 0 : j + 1);
1111       i++;
1112     }
1113     *(p->ipos) = (MYFLT) k;
1114 
1115     return OK;
1116 }
1117 
1118 #ifdef HAVE_CURL
str_from_url(CSOUND * csound,STRCPY_OP * p)1119 int32_t str_from_url(CSOUND *csound, STRCPY_OP *p)
1120 {
1121     char  *newVal = p->str->data;
1122     if (strstr(newVal, ":/")==NULL) return strcpy_opcode_S(csound, p);
1123     {
1124       CORFIL *mm = copy_url_corefile(csound, newVal,0);
1125       int32_t len = corfile_length(mm);
1126       if (p->r->data == NULL) {
1127         p->r->data =  cs_strdup(csound, corfile_body(mm));
1128         p->r->size =  len + 1;
1129         goto cleanup;
1130       }
1131       if (UNLIKELY(len >= p->r->size)) {
1132         csound->Free(csound, p->r->data);
1133         p->r->data = cs_strdup(csound, corfile_body(mm));
1134         p->r->size = len + 1;
1135       }
1136       else strcpy((char*) p->r->data, corfile_body(mm));
1137     cleanup:
1138       corfile_rm(csound, &mm);
1139       return OK;
1140     }
1141 }
1142 #endif
1143 
1144 #if !defined(HAVE_STRLCAT) && !defined(strlcat) && !defined(EMSCRIPTEN)
1145 /* Direct from BSD sources */
1146 /*
1147  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1148  *
1149  * Permission to use, copy, modify, and distribute this software for any
1150  * purpose with or without fee is hereby granted, provided that the above
1151  * copyright notice and this permission notice appear in all copies.
1152  */
1153 size_t
strlcat(char * dst,const char * src,size_t siz)1154 strlcat(char *dst, const char *src, size_t siz)
1155 {
1156     char *d = dst;
1157     const char *s = src;
1158     size_t n = siz;
1159     size_t dlen;
1160 
1161     /* Find the end of dst and adjust bytes left but don't go past end */
1162     while (n-- != 0 && *d != '\0')
1163       d++;
1164     dlen = d - dst;
1165     n = siz - dlen;
1166 
1167     if (n == 0)
1168       return (dlen + strlen(s));
1169     while (*s != '\0') {
1170       if (n != 1) {
1171         *d++ = *s;
1172         n--;
1173       }
1174       s++;
1175     }
1176     *d = '\0';
1177 
1178     return (dlen + (s - src));  /* count does not include NUL */
1179 }
1180 #endif
1181 
1182 /* Modified from BSD sources for strlcpy */
1183 /*
1184  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1185  *
1186  * Permission to use, copy, modify, and distribute this software for any
1187  * purpose with or without fee is hereby granted, provided that the above
1188  * copyright notice and this permission notice appear in all copies.
1189  */
1190 /* modifed for speed -- JPff */
1191 char *
strNcpy(char * dst,const char * src,size_t siz)1192 strNcpy(char *dst, const char *src, size_t siz)
1193 {
1194     char *d = dst;
1195     const char *s = src;
1196     size_t n = siz;
1197 
1198     /* Copy as many bytes as will fit or until NULL */
1199     if (n != 0) {
1200       while (--n != 0) {
1201         if ((*d++ = *s++) == '\0')
1202           break;
1203       }
1204     }
1205 
1206     /* Not enough room in dst, add NUL */
1207     if (n == 0) {
1208       if (siz != 0)
1209         *d = '\0';                /* NUL-terminate dst */
1210 
1211       //while (*s++) ;
1212     }
1213     return dst;        /* count does not include NUL */
1214 }
1215 
1216 /* Debugging opcode for testing runtime type identification */
print_type_opcode(CSOUND * csound,PRINT_TYPE_OP * p)1217 int32_t print_type_opcode(CSOUND* csound, PRINT_TYPE_OP* p) {
1218     char* ptr = (char*)p->inVar;
1219 
1220     CS_TYPE* varType = *(CS_TYPE**)(ptr - CS_VAR_TYPE_OFFSET);
1221     csound->Message(csound, "Variable Type: %s\n", varType->varTypeName);
1222 
1223     return OK;
1224 }
1225