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

Last change on this file since 1299 was 1179, checked in by Maciej Komosinski, 2 years ago

"eof" quoting and unquoting functions (used in client-server communication)

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