1: /*
2: * $Id: strings.c,v 1.8 2025/05/01 17:02:30 snw Exp $
3: * freem string library
4: *
5: *
6: * Author: Serena Willis <snw@coherent-logic.com>
7: * Copyright (C) 1998 MUG Deutschland
8: * Copyright (C) 2020, 2025 Coherent Logic Development LLC
9: *
10: *
11: * This file is part of FreeM.
12: *
13: * FreeM is free software: you can redistribute it and/or modify
14: * it under the terms of the GNU Affero Public License as published by
15: * the Free Software Foundation, either version 3 of the License, or
16: * (at your option) any later version.
17: *
18: * FreeM is distributed in the hope that it will be useful,
19: * but WITHOUT ANY WARRANTY; without even the implied warranty of
20: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21: * GNU Affero Public License for more details.
22: *
23: * You should have received a copy of the GNU Affero Public License
24: * along with FreeM. If not, see <https://www.gnu.org/licenses/>.
25: *
26: * $Log: strings.c,v $
27: * Revision 1.8 2025/05/01 17:02:30 snw
28: * Further debugging improvements
29: *
30: * Revision 1.7 2025/04/15 02:24:43 snw
31: * Improve FreeM logging capabilities
32: *
33: * Revision 1.6 2025/04/13 04:22:43 snw
34: * Fix snprintf calls
35: *
36: * Revision 1.5 2025/04/10 01:24:39 snw
37: * Remove C++ style comments
38: *
39: * Revision 1.4 2025/04/09 19:52:02 snw
40: * Eliminate as many warnings as possible while building with -Wall
41: *
42: * Revision 1.3 2025/03/09 19:50:47 snw
43: * Second phase of REUSE compliance and header reformat
44: *
45: *
46: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
47: * SPDX-License-Identifier: AGPL-3.0-or-later
48: **/
49:
50: #include "mpsdef.h"
51: #include <ctype.h>
52: #include <stdarg.h>
53: #include <string.h>
54: #include <stdlib.h>
55: #include <stdarg.h>
56:
57: /* length of 'source' string in bytes */
58: long int stlen (const char *source)
59: {
60: register int length = 0;
61:
62: while (*source++ != EOL) length++;
63:
64: return length;
65: }
66:
67: long int stnlen (const char *source, size_t siz)
68: {
69: register int length = 0;
70:
71: while ((*source++ != EOL) && (length < siz)) length++;
72:
73: return length;
74: }
75:
76: /* copy string from 'source' to 'dest' */
77: long int stcpy (char *dest, const char *source)
78: {
79: register int count = 0;
80:
81: while ((*dest++ = *source++) != EOL) count++;
82:
83: return count;
84: }
85:
86: long int stncpy (char *dest, const char *source, size_t siz)
87: {
88: register int count = 0;
89:
90: while (((*dest++ = *source++) != EOL) && (count < siz)) count++;
91:
92: return count;
93: }
94:
95:
96: /* copy exactly 'length' characters from source to dest */
97: void stcpy0 (char *dest, const char *source, long length)
98: {
99: while (length-- > 0) *dest++ = *source++;
100:
101: return;
102: }
103:
104:
105: /* copy exactly 'length' characters from source to dest*/
106: void stcpy1 (char *dest, const char *source, long length)
107: {
108: while (length-- > 0) *dest-- = *source--;
109:
110: return;
111: }
112:
113:
114: /* concatenate string from 'source' to the end of 'dest' */
115: short int stcat (char *dest, const char *source)
116: {
117: register int i = 0;
118:
119: while (dest[i++] != EOL);
120:
121: i--;
122:
123: while ((dest[i] = *source++) != EOL) {
124:
125: if (i++ >= STRLEN) {
126: dest[--i] = EOL;
127: return FALSE;
128: }
129:
130: }
131:
132: return TRUE;
133: }
134:
135: /* compare str1 and str2 */
136: short int stcmp (char *str1, char *str2)
137: {
138: while (*str1 == *str2) {
139:
140: if (*str1 == EOL) return 0;
141:
142: str1++;
143: str2++;
144:
145: }
146:
147: return *str1 - *str2;
148: }
149:
150: /* trim whitespace from string 's' */
151: char *trim (char *s)
152: {
153:
154: char *t = strdup (s);
155: char *end;
156: char *result;
157: int final_len;
158:
159: if (t == NULL) return NULL;
160:
161: while (isspace ((unsigned char) *t)) t++;
162:
163: if (*t == 0) return t;
164:
165: end = t + strlen (t) - 1;
166:
167: while (end > t && isspace ((unsigned char) *end)) end--;
168:
169: end[1] = '\0';
170:
171: /* recover waste ('t' still occupies the same heap
172: * as it did before the whitespace was stripped)
173: */
174:
175: final_len = strlen (t);
176: result = (char *) malloc ((final_len + 1) * sizeof (char));
177:
178: if (result == NULL) return NULL;
179:
180: strcpy (result, t);
181: free (t);
182:
183: return result;
184:
185: }
186:
187: /*
188: int stnprintf(char *dst, int size, char *fmt, ...)
189: {
190: va_list ptr;
191: va_start (ptr, fmt);
192:
193: char ch;
194: char typ;
195:
196: register int i;
197: register int k;
198:
199: k = 0;
200:
201: for (i = 0; fmt[i] != '\201'; i++) {
202: ch = fmt[i];
203:
204: if ((k + 1) == size) {
205: dst[k + 1] = '\201';
206: return k;
207: }
208:
209: if (ch == '%') {
210: typ = fmt[++i];
211:
212: switch (typ) {
213:
214: case '%':
215: dst[k++] = '%';
216: break;
217:
218: case 's':
219:
220:
221: }
222: }
223: else if (ch == '\') {
224:
225: }
226: }
227: }
228: */
229:
230: /* convert EOL-terminated string 'mstr' to NUL-terminated string in-place */
231: void stcnv_m2c(char *mstr)
232: {
233: mstr[stlen(mstr)] = NUL;
234: }
235:
236: void stncnv_m2c(char *mstr, size_t siz)
237: {
238: mstr[stnlen (mstr, siz)] = NUL;
239: }
240:
241: /* convert NUL-terminated string 'cstr' to EOL-terminated string in-place */
242: void stcnv_c2m(char *cstr)
243: {
244: register int i;
245:
246: for(i = 0; i < 256; i++) {
247:
248: if(cstr[i] == '\0') {
249: cstr[i] = '\201';
250:
251: return;
252: }
253:
254: }
255: }
256:
257: void stncnv_c2m(char *cstr, size_t siz)
258: {
259: register int i;
260:
261: for (i = 0; i < siz; i++) {
262:
263: if (cstr[i] == NUL) {
264: cstr[i] = EOL;
265: return;
266: }
267:
268: }
269:
270: return;
271: }
272:
273: /* convert at most 'count' characters of *key into human-readable format in *buf */
274: size_t key_to_name(char *buf, const char *key, size_t count)
275: {
276: size_t i;
277: int j = 0;
278: int first = 1;
279: int has_subs = 0;
280: int in_strlit = 0;
281: char c;
282: char next;
283:
284: if (key[0] == NUL) {
285: buf[0] = NUL;
286:
287: return 0;
288: }
289:
290: buf[0] = '^';
291:
292: for (i = 0; i < count; i++) {
293:
294: c = key[i];
295: next = key[i + 1];
296:
297: switch (c) {
298:
299: case EOL:
300:
301: if (first == 0) {
302:
303: if (has_subs == 1) {
304:
305: if (!in_strlit) {
306: buf[j++] = ')';
307: buf[j] = NUL;
308: }
309: else {
310: buf[j++] = '\"';
311: buf[j++] = ')';
312: buf[j] = NUL;
313: }
314:
315: }
316: else {
317: buf[j] = NUL;
318: }
319:
320: }
321: else {
322: buf[j] = NUL;
323: }
324:
325: return i;
326:
327:
328: case DELIM:
329:
330: if (first == 1) {
331:
332: buf[j] = '(';
333: first = 0;
334: has_subs = 1;
335:
336: }
337: else {
338:
339: if (!in_strlit) {
340: buf[j] = ',';
341: }
342: else {
343:
344: buf[j++] = '\"';
345: buf[j] = ',';
346:
347: in_strlit = 0;
348:
349: }
350:
351: }
352:
353: if (isalpha(next) && !in_strlit) {
354: in_strlit = 1;
355: buf[++j] = '\"';
356: }
357: else if (in_strlit) {
358: in_strlit = 0;
359: buf[++j] = '\"';
360: }
361:
362: break;
363:
364:
365: default:
366: buf[j] = key[i];
367: break;
368: }
369:
370: j++;
371: }
372:
373: return count;
374:
375: }
376:
377: size_t name_to_key(char *buf, const char *name, size_t count)
378: {
379: size_t i;
380: size_t j = 0;
381:
382: short insubs = FALSE;
383: short instr = FALSE;
384:
385: char ch;
386:
387: for (i = 0; i < count; i++) buf[i] = NUL;
388:
389: for (i = 0; i < count; i++) {
390:
391: ch = name[i];
392:
393: switch (ch) {
394:
395: case EOL:
396: buf[j] = ch;
397: goto n_to_k_done;
398:
399: case '(':
400: if (insubs == FALSE && instr == FALSE) {
401: insubs = TRUE;
402: buf[j++] = DELIM;
403: }
404: else {
405: if (instr == TRUE) {
406: buf[j++] = ch;
407: }
408: }
409: break;
410:
411:
412: case ')':
413: if (insubs == TRUE && instr == FALSE) {
414: buf[j] = EOL;
415:
416: goto n_to_k_done;
417: }
418: else {
419: if (insubs == TRUE && instr == TRUE) {
420: buf[j++] = ch;
421: }
422: }
423: break;
424:
425:
426: case ',':
427: if (insubs == TRUE && instr == FALSE) {
428: if (buf[j - 1] != DELIM) {
429: buf[j++] = DELIM;
430: }
431: }
432: else if (insubs == TRUE && instr == TRUE) {
433: buf[j++] = ch;
434: }
435:
436: break;
437:
438:
439: case '"':
440:
441: if (insubs == TRUE && instr == FALSE) {
442: instr = TRUE;
443:
444: if (buf[j - 1] != DELIM) {
445: buf[j++] = DELIM;
446: }
447:
448: break;
449: }
450:
451: if (instr == TRUE) {
452: instr = FALSE;
453: buf[j++] = DELIM;
454: }
455:
456: break;
457:
458:
459: default:
460: buf[j++] = ch;
461: break;
462: }
463:
464: }
465:
466: n_to_k_done:
467: buf[j] = '\201';
468:
469: return j;
470:
471: }
472:
473: void create_var_key (char *buf, int subct, char *nam, ...)
474: {
475: int i;
476: va_list args;
477:
478: strcat (buf, nam);
479: strcat (buf, "\202");
480:
481: va_start (args, nam);
482:
483: for (i = 0; i < subct; i++) {
484:
485: strcat (buf, va_arg (args, char *));
486:
487: if (i < (subct - 1)) strcat (buf, "\202");
488:
489: }
490:
491: va_end (args);
492:
493: strcat (buf, "\201");
494: }
495:
496: void trim_decimal (char *s)
497: {
498: register int i;
499:
500: for (i = stlen (s) - 1; s[i] == '0'; i--) s[i] = EOL;
501:
502: if (s[i] == '.') s[i] = EOL;
503: }
504:
505: void uuid_v4 (char *buf)
506: {
507:
508: char *chars = "0123456789abcdef";
509: int seg3num;
510: int seg4num;
511: int i;
512:
513: char seg1[9];
514: char seg2[5];
515: char seg3[5];
516: char seg4[5];
517: char seg5[13];
518:
519: seg3num = (rand () % 4095) + 16384;
520: seg4num = (rand () % 16383) + 32768;
521:
522: for (i = 0; i < 9; i++) {
523: seg1[i] = chars[rand () % 16];
524: }
525:
526: seg1[8] = '\0';
527:
528: for (i = 0; i < 4; i++) {
529: seg2[i] = chars[rand () % 16];
530: }
531:
532: seg2[4] = '\0';
533:
534: snprintf (seg3, sizeof (seg3), "%04x", seg3num);
535: snprintf (seg4, sizeof (seg4), "%04x", seg4num);
536:
537: for (i = 0; i < 12; i++) {
538: seg5[i] = chars[rand () % 16];
539: }
540:
541: seg5[12] = '\0';
542:
543: sprintf (buf, "%s-%s-%s-%s-%s", seg1, seg2, seg3, seg4, seg5);
544:
545: return;
546:
547: }
548:
549:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>