source: cpp/frams/util/sstringutils.cpp @ 1341

Last change on this file since 1341 was 1341, checked in by Maciej Komosinski, 2 days ago

Cosmetic

  • Property svn:eol-style set to native
File size: 10.4 KB
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
[1341]2// Copyright (C) 1999-2025  Maciej Komosinski and Szymon Ulatowski.
[286]3// See LICENSE.txt for details.
[109]4
[1341]5#include <inttypes.h> //PRIu64
[109]6#include "sstringutils.h"
[382]7#include <common/virtfile/virtfile.h>
[375]8#include <common/log.h>
[109]9#include <common/nonstd.h>
[691]10#include <common/Convert.h>
[1179]11#include <common/nonstd_span.h>
[109]12
[529]13int loadSString(const char* filename, SString& s, const char* framsgmodule, const char* error, bool remove_cr)
[109]14{
[257]15        VirtFILE *f;
16        int ret = 0;
17        if (f = Vfopen(filename, FOPEN_READ_BINARY))
[109]18        {
[529]19                loadSString(f, s, remove_cr);
[257]20                ret = 1;
[523]21                delete f;
[109]22        }
[257]23        else if (framsgmodule)
[691]24                logPrintf(framsgmodule, "loadSString", LOG_WARN, error ? error : "Can't open file \"%s\"", filename);
[257]25        return ret;
[109]26}
27
[529]28void loadSString(VirtFILE *f, SString& s, bool remove_cr)
[109]29{
[257]30        char buf[1024];
31        int len;
32        while (!f->Veof())
[109]33        {
[1179]34                len = (int)f->Vread(buf, 1, sizeof(buf));
[257]35                s.append(buf, len);
[109]36        }
[529]37        if (remove_cr)
38                removeCR(s);
[109]39}
40
41//load single line, discarding any \r or \n found at the end, return false if nothing could be loaded (error or eof)
[257]42bool loadSStringLine(VirtFILE* f, SString& s)
[109]43{
[257]44        char buf[100];
45        bool eolfound = false;
46        bool ret = false;
47        s = SString::empty();
48        while (!eolfound)
[109]49        {
[523]50                char *r = f->Vgets(buf, sizeof(buf));
[257]51                if (r == NULL) break;
52                ret = true;
[1179]53                int d = (int)strlen(r);
[257]54                if (d > 0)
[109]55                {
[257]56                        if (r[d - 1] == '\n') { d--; eolfound = true; }
57                        if (d > 0) if (r[d - 1] == '\r') d--;
58                        s += SString(r, d);
[109]59                }
60        }
[257]61        return ret;
[109]62}
63
64//////////////////////////
65
66/** "x~xx~xxx" -> "x\~xx\~xxx"  */
67int quoteTilde(SString &target)
68{
[348]69        const char* x = target.c_str();
[257]70        SString tmp;
71        char *f;
72        while (1)
[109]73        {
[257]74                f = strchr((char*)x, '~');
75                if (f)
[109]76                {
[257]77                        tmp.append(x, f - x);
78                        tmp += "\\~";
79                        x = f + 1;
[109]80                }
[257]81                else
[109]82                {
[973]83                        if (tmp.length() == 0) return 0; // nothing was changed!
[257]84                        tmp += x;
85                        target = tmp;
86                        return 1;
[109]87                }
88        }
89}
90
91/** "x\~xx\~xxx" -> "x~xx~xxx"  */
92int unquoteTilde(SString &target)
93{
[348]94        const char* x = target.c_str();
[257]95        SString tmp;
96        char *f;
97        while (1)
[109]98        {
[257]99                f = strchr((char*)x, '\\');
100                if (f)
[109]101                {
[257]102                        tmp.append(x, f - x);
103                        if (f[1] == '~')
[109]104                        {
[257]105                                tmp += '~';
106                                x = f + 2;
[109]107                        }
[257]108                        else
[109]109                        {
[257]110                                tmp += "\\";
111                                x = f + 1;
[109]112                        }
113                }
[257]114                else
[109]115                {
[973]116                        if (tmp.length() == 0) return 0; // nothing was changed!
[257]117                        tmp += x;
118                        target = tmp;
119                        return 1;
[109]120                }
121        }
122}
123
124/////////////////
125
[257]126bool strContainsOneOf(const char* str, const char* chars)
[109]127{
[257]128        while (*str)
[109]129        {
[257]130                if (strchr(chars, *str)) return 1;
131                str++;
[109]132        }
[257]133        return 0;
[109]134}
135
136//////////////
137
138bool sstringQuote(SString& target)
139{
[348]140        const char* x = target.c_str();
[257]141        bool changed = 0;
142        SString tmp;
[973]143        tmp.reserve(target.length());
[257]144        while (*x)
[109]145        {
[257]146                switch (*x)
[109]147                {
[257]148                case '\n': tmp += "\\n"; changed = 1; break;
149                case '\r': tmp += "\\r"; changed = 1; break;
150                case '\t': tmp += "\\t"; changed = 1; break;
151                case '\"': tmp += "\\\""; changed = 1; break;
152                case '\\': tmp += "\\\\"; changed = 1; break;
153                default: tmp += *x;
[109]154                }
[257]155                x++;
[109]156        }
[257]157        if (changed) target = tmp;
158        return changed;
[109]159}
160
[786]161SString sstringDelimitAndShorten(const SString &in, int maxlen, bool show_length, const SString& before, const SString& after)
[785]162{
163        SString out;
[973]164        if (in.length() > maxlen)
165                out = in.substr(0, maxlen / 2) + "..." + in.substr(in.length() - maxlen + maxlen / 2);
[785]166        else
[786]167        {
168                out = in; show_length = false;
169        }
[785]170        sstringQuote(out);
[786]171        out = before + out + after;
172        if (show_length)
[973]173                out += SString::sprintf(" (length %d)", in.length());
[785]174        return out;
175}
176
[109]177const char* skipQuoteString(const char* txt, const char* limit)
178{
[257]179        while (*txt)
[109]180        {
[257]181                if (*txt == '\"') return txt;
182                if (*txt == '\\') txt++;
183                txt++;
184                if (txt == limit) break;
[109]185        }
[257]186        return txt;
[109]187}
188
189int sstringUnquote(SString &target)
190{
[348]191        const char* x = target.c_str();
[257]192        SString tmp;
193        char *f;
194        while (1)
[109]195        {
[257]196                f = strchr((char*)x, '\\');
197                if (f)
[109]198                {
[257]199                        tmp.append(x, f - x);
200                        switch (f[1])
[109]201                        {
[257]202                        case 'n': tmp += '\n'; break;
203                        case 'r': tmp += '\r'; break;
204                        case 't': tmp += '\t'; break;
205                        case '\"': tmp += '\"'; break;
206                        default: tmp += f[1];
[109]207                        }
[257]208                        x = f + 2;
[109]209                }
[257]210                else
[109]211                {
[973]212                        if (tmp.length() == 0) return 0; // nothing was changed!
[257]213                        tmp += x;
214                        target = tmp;
215                        return 1;
[109]216                }
217        }
218}
219
[257]220int strFindField(const SString& txt, const SString& name, int &end)
[109]221{
[348]222        const char* t = txt.c_str(), *n;
[257]223        int pos = 0;
224        while (1)
[109]225        {
[257]226                n = strchr(t + pos, ',');
[973]227                if ((!strncmp(t + pos, name.c_str(), name.length())) && (t[pos + name.length()] == '='))
[109]228                {
[973]229                        if (n) end = n - t; else end = txt.length();
[257]230                        return pos;
[109]231                }
[257]232                if (n) pos = n - t + 1; else break;
[109]233        }
[257]234        return -1;
[109]235}
236
[257]237SString strGetField(const SString& txt, const SString& name)
[109]238{
[257]239        int p, e;
240        p = strFindField(txt, name, e);
241        if (p < 0) return SString();
[973]242        p += name.length() + 1;
[257]243        return SString(txt.substr(p, e - p));
[109]244}
245
[257]246void strSetField(SString& txt, const SString& name, const SString& value)
[109]247{
[257]248        int p, e;
249        p = strFindField(txt, name, e);
250        if (p < 0)
[109]251        {
[973]252                if (!value.length()) return;
253                char *t = txt.directAppend(1 + name.length() + value.length());
[257]254                char *b = t;
[973]255                if (txt.length()) *(t++) = ',';
256                strcpy(t, name.c_str()); t += name.length();
[257]257                *(t++) = '=';
[973]258                strcpy(t, value.c_str()); t += value.length();
[257]259                txt.endAppend(t - b);
[109]260        }
[257]261        else
[109]262        {
[973]263                if (!value.length())
[109]264                {
[973]265                        if (p > 0) p--; else if (e < txt.length()) e++;
[257]266                        char *t = txt.directWrite(0);
[973]267                        memmove(t + p, t + e, txt.length() - e);
268                        txt.endWrite(txt.length() + value.length() - (e - p));
[109]269                }
[257]270                else
[109]271                {
[973]272                        p += name.length() + 1;
273                        char *t = txt.directWrite(txt.length() + value.length() - (e - p));
274                        memmove(t + p + value.length(), t + e, txt.length() - e);
275                        memmove(t + p, value.c_str(), value.length());
276                        txt.endWrite(txt.length() + value.length() - (e - p));
[109]277                }
278        }
279}
280
[512]281SString trim(const SString& s)
[109]282{
[348]283        const unsigned char*b = (const unsigned char*)s.c_str();
[973]284        const unsigned char*e = b + s.length();
[257]285        while ((b < e) && (*b <= ' ')) b++;
286        while ((b < e) && (e[-1] <= ' ')) e--;
[973]287        if ((e - b) == s.length()) return s;
[257]288        SString newstring;
289        char* t = newstring.directWrite(e - b);
290        memmove(t, b, e - b);
291        newstring.endWrite(e - b);
292        return newstring;
[109]293}
294
[973]295SString concatPath(const SString& in1, const SString& in2)
[904]296{
[973]297        SString out = in1;
298        if (out.length() > 0 && out[out.length() - 1] != PATH_SEPARATOR_CHAR)
299                out += PATH_SEPARATOR_CHAR;
300        out += in2;
[904]301        return out;
302}
303
[109]304bool removeCR(SString& s)
305{
[348]306        const char* p = s.c_str();
[257]307        const char* cr = strchr(p, '\r');
308        if (!cr) return false;
309        char* begin = s.directWrite();
310        char* src = begin + (cr - p), *dst = src;
311        while (*src)
312                if (*src == '\r')
313                        src++;
314                else
315                        *(dst++) = *(src++);
316        s.endWrite(dst - begin);
317        return true;
[109]318}
[210]319
[257]320bool matchWildcard(const SString& word, const SString& pattern)
[210]321{
[973]322        if (pattern.length() == 0)
323                return word.length() == 0;
[257]324        int aster = pattern.indexOf('*');
325        if (aster >= 0)
[210]326        {
[257]327                SString before = pattern.substr(0, aster);
328                SString after = pattern.substr(aster + 1);
[973]329                if (!word.length()) return false;
330                if (before.length()) if (!word.startsWith(before.c_str())) return false;
331                if (after.length())
332                        if ((word.length() < after.length())
333                                || (strcmp(after.c_str(), word.c_str() + word.length() - after.length())))
[257]334                                return false;
335                return true;
[210]336        }
[257]337        else
338                return word == pattern;
[210]339}
340
[257]341bool matchWildcardList(const SString& word, const SString& patterns)
[210]342{
[973]343        if (patterns.length() == 0)
344                return word.length() == 0;
[257]345        int pos = 0;
346        SString pattern;
347        while (patterns.getNextToken(pos, pattern, ','))
348                if (matchWildcard(word, pattern))
349                        return true;
350        return false;
[210]351}
352
[691]353SString getUIDString(uint64_t uid, char prefix)
354{
[1341]355        return SString::sprintf("%c%" PRIu64, prefix, uid);
[691]356}
357
358bool parseUIDString(const char* str, char prefix, uint64_t &uid, bool err)
359{
360        if ((str[0] == prefix) && (isdigit(str[1])))
361        {
362                char* end;
363                uid = strtoull(str + 1, &end, 10);
364                if (end == (str + 1 + strlen(str + 1)))
365                        return true;
366        }
367        if (err)
368                logPrintf("SString", "parseUIDString", LOG_ERROR, "Invalid uid: '%s'", str);
369        return false;
370}
[1156]371
372bool sstringURLEncode(SString& target)
373{
374        const char* x = target.c_str();
375        bool changed = 0;
376        SString tmp;
377        tmp.reserve(target.length());
378        static constexpr const char* ALLOWED_CHARS = "-_.~";
379        for (; *x; x++)
380        {
381                if (!(isalnum(*x) || strchr(ALLOWED_CHARS, *x)))
382                {
383                        tmp += "%";
384                        tmp += SString::sprintf("%02x", *x);
385                        changed = 1;
386                }
387                else
388                        tmp += *x;
389        }
390        if (changed) target = tmp;
391        return changed;
392}
393
394bool sstringURLDecode(SString &target)
395{
396        const char* x = target.c_str();
397        SString tmp;
398        char *f;
399        while (1)
400        {
401                f = strchr((char*)x, '%');
402                if (f)
403                {
404                        tmp.append(x, f - x);
405                        char hex[3] = { f[1],f[2],0 };
406                        char* after;
407                        unsigned long intvalue = strtoul(hex, &after, 16);
408                        tmp += (char)intvalue;
409                        x = f + 3;
410                }
411                else
412                {
413                        if (tmp.length() == 0) return false; // nothing was changed!
414                        tmp += x;
415                        target = tmp;
416                        return true;
417                }
418        }
419}
[1179]420
421static span<char> findEof(span<char> field, const char* pos) // 'eof' includes any number of leading '\'s and is only detected when there is nothing else in the line
422{
423        const char* eof = strstr(pos, "eof");
424        if (!eof)
425                return span<char>();
426
427        if (!(((eof + 3) == field.end()) || (eof[3] == '\n')))
428                return span<char>();
429
430        const char* begin = eof - 1;
431        for (; begin >= field.begin(); begin--)
432        {
433                if (*begin == '\n')
434                        break;
435                if (*begin != '\\')
436                        return span<char>();
437        }
438
439        return span<char>((char*)(begin + 1), (eof + 3) - (begin + 1));
440}
441
442bool sstringQuoteEOF(SString& target)
443{
444        span<char> field((char*)target.c_str(), target.size());
445        const char* pos = field.begin();
446
447        bool changed = false;
448
449        SString tmp;
450
451        while (true)
452        {
453                auto eof = findEof(field, pos);
454                if (eof.size())
455                {
456                        if (!changed)
457                        {
458                                tmp.reserve(target.length() + 10);
459                                changed = true;
460                        }
461                        tmp.append(pos, eof.begin() - pos);
462                        tmp += "\\";
463                        tmp.append(eof.begin(), (int)eof.size());
464                        pos = eof.end();
465                }
466                else
467                {
468                        if (!changed)
469                                return false;
470                        tmp.append(pos, field.end() - pos);
471                        break;
472                }
473        }
474
475        target = tmp;
476        return true;
477}
478
479bool sstringUnquoteEOF(SString& target)
480{
481        span<char> field((char*)target.c_str(), target.size());
482        const char* pos = field.begin();
483
484        bool changed = false;
485
486        SString tmp;
487
488        while (true)
489        {
490                auto eof = findEof(field, pos);
491                if (eof.size() > 3)
492                {
493                        if (!changed)
494                        {
495                                tmp.reserve(target.length());
496                                changed = true;
497                        }
498                        tmp.append(pos, eof.begin() - pos);
499                        tmp.append(eof.begin() + 1, (int)eof.size() - 1);
500                        pos = eof.end();
501                }
502                else
503                {
504                        if (eof.size() == 3) //size()==3 means bare 'eof', which cannot be unquoted (and means something went wrong somewhere else)
505                                logPrintf("String", "unquoteEof", LOG_WARN, "Unexpected bare \"eof\" line");
506                        if (!changed)
507                                return false;
508                        tmp.append(pos, field.end() - pos);
509                        break;
510                }
511        }
512
513        target = tmp;
514        return true;
515}
Note: See TracBrowser for help on using the repository browser.