Coverage Report

Created: 2025-07-18 06:43

/src/libwebsockets/lib/core/libwebsockets.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * libwebsockets - small server side websockets and web server implementation
3
 *
4
 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to
8
 * deal in the Software without restriction, including without limitation the
9
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10
 * sell copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
 * IN THE SOFTWARE.
23
 */
24
25
#include "private-lib-core.h"
26
27
#ifdef LWS_HAVE_SYS_TYPES_H
28
#include <sys/types.h>
29
#endif
30
#include <signal.h>
31
32
void
33
lws_ser_wu16be(uint8_t *b, uint16_t u)
34
0
{
35
0
  *b++ = (uint8_t)(u >> 8);
36
0
  *b = (uint8_t)u;
37
0
}
38
39
void
40
lws_ser_wu32be(uint8_t *b, uint32_t u32)
41
0
{
42
0
  *b++ = (uint8_t)(u32 >> 24);
43
0
  *b++ = (uint8_t)(u32 >> 16);
44
0
  *b++ = (uint8_t)(u32 >> 8);
45
0
  *b = (uint8_t)u32;
46
0
}
47
48
void
49
lws_ser_wu64be(uint8_t *b, uint64_t u64)
50
0
{
51
0
  lws_ser_wu32be(b, (uint32_t)(u64 >> 32));
52
0
  lws_ser_wu32be(b + 4, (uint32_t)u64);
53
0
}
54
55
uint16_t
56
lws_ser_ru16be(const uint8_t *b)
57
0
{
58
0
  return (uint16_t)((b[0] << 8) | b[1]);
59
0
}
60
61
uint32_t
62
lws_ser_ru32be(const uint8_t *b)
63
0
{
64
0
  return (unsigned int)((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
65
0
}
66
67
uint64_t
68
lws_ser_ru64be(const uint8_t *b)
69
0
{
70
0
  return (((uint64_t)lws_ser_ru32be(b)) << 32) | lws_ser_ru32be(b + 4);
71
0
}
72
73
int
74
lws_vbi_encode(uint64_t value, void *buf)
75
0
{
76
0
  uint8_t *p = (uint8_t *)buf, b;
77
78
0
  if (value > 0xfffffff) {
79
0
    assert(0);
80
0
    return -1;
81
0
  }
82
83
0
  do {
84
0
    b = value & 0x7f;
85
0
    value >>= 7;
86
0
    if (value)
87
0
      *p++ = (0x80 | b);
88
0
    else
89
0
      *p++ = b;
90
0
  } while (value);
91
92
0
  return lws_ptr_diff(p, buf);
93
0
}
94
95
int
96
lws_vbi_decode(const void *buf, uint64_t *value, size_t len)
97
0
{
98
0
  const uint8_t *p = (const uint8_t *)buf, *end = p + len;
99
0
  uint64_t v = 0;
100
0
  int s = 0;
101
102
0
  while (p < end) {
103
0
    v |= (((uint64_t)(*p)) & 0x7f) << s;
104
0
    if (*p & 0x80) {
105
0
      *value = v;
106
107
0
      return lws_ptr_diff(p, buf);
108
0
    }
109
0
    s += 7;
110
0
    if (s >= 64)
111
0
      return 0;
112
0
    p++;
113
0
  }
114
115
0
  return 0;
116
0
}
117
118
signed char char_to_hex(const char c)
119
0
{
120
0
  if (c >= '0' && c <= '9')
121
0
    return (signed char)(c - '0');
122
123
0
  if (c >= 'a' && c <= 'f')
124
0
    return (signed char)(c - 'a' + 10);
125
126
0
  if (c >= 'A' && c <= 'F')
127
0
    return (signed char)(c - 'A' + 10);
128
129
0
  return (signed char)-1;
130
0
}
131
132
int
133
lws_hex_len_to_byte_array(const char *h, size_t hlen, uint8_t *dest, int max)
134
0
{
135
0
  uint8_t *odest = dest;
136
137
0
  while (max-- && hlen > 1) {
138
0
    int t = char_to_hex(*h++), t1;
139
140
0
    if (!*h || t < 0)
141
0
      return -1;
142
143
0
    t1 = char_to_hex(*h++);
144
0
    if (t1 < 0)
145
0
      return -1;
146
147
0
    *dest++ = (uint8_t)((t << 4) | t1);
148
0
    hlen -= 2;
149
0
  }
150
151
0
  if (max < -1)
152
0
    return -1;
153
154
0
  return lws_ptr_diff(dest, odest);
155
0
}
156
157
int
158
lws_hex_to_byte_array(const char *h, uint8_t *dest, int max)
159
0
{
160
0
  return lws_hex_len_to_byte_array(h, strlen(h), dest, max);
161
0
}
162
163
static char *hexch = "0123456789abcdef";
164
165
void
166
lws_hex_from_byte_array(const uint8_t *src, size_t slen, char *dest, size_t len)
167
0
{
168
0
  char *end = &dest[len - 1];
169
170
0
  while (slen-- && dest != end) {
171
0
    uint8_t b = *src++;
172
0
    *dest++ = hexch[b >> 4];
173
0
    if (dest == end)
174
0
      break;
175
0
    *dest++ = hexch[b & 0xf];
176
0
  }
177
178
0
  *dest = '\0';
179
0
}
180
181
int
182
lws_hex_random(struct lws_context *context, char *dest, size_t len)
183
0
{
184
0
  size_t n = ((len - 1) / 2) + 1;
185
0
  uint8_t b, *r = (uint8_t *)dest + len - n;
186
187
0
  if (lws_get_random(context, r, n) != n)
188
0
    return 1;
189
190
0
  while (len >= 3) {
191
0
    b = *r++;
192
0
    *dest++ = hexch[b >> 4];
193
0
    *dest++ = hexch[b & 0xf];
194
0
    len -= 2;
195
0
  }
196
197
0
  if (len == 2)
198
0
    *dest++ = hexch[(*r) >> 4];
199
200
0
  *dest = '\0';
201
202
0
  return 0;
203
0
}
204
205
#if defined(_DEBUG)
206
void
207
lws_assert_fourcc(uint32_t fourcc, uint32_t expected)
208
0
{
209
0
  if (fourcc == expected)
210
0
    return;
211
212
0
  lwsl_err("%s: fourcc mismatch, expected %c%c%c%c, saw %c%c%c%c\n",
213
0
      __func__, (int)(expected >> 24), (int)((expected >> 16) & 0xff),
214
0
      (int)((expected >> 8) & 0xff),(int)( expected & 0xff),
215
0
      (int)(fourcc >> 24), (int)((fourcc >> 16) & 0xff),
216
0
      (int)((fourcc >> 8) & 0xff), (int)(fourcc & 0xff));
217
218
0
  assert(0);
219
0
}
220
#endif
221
222
#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_BAREMETAL)
223
224
#if defined(LWS_WITH_FILE_OPS)
225
int lws_open(const char *__file, int __oflag, ...)
226
0
{
227
0
  va_list ap;
228
0
  int n;
229
230
0
  va_start(ap, __oflag);
231
0
  if (((__oflag & O_CREAT) == O_CREAT)
232
#if defined(O_TMPFILE)
233
    || ((__oflag & O_TMPFILE) == O_TMPFILE)
234
#endif
235
0
  )
236
#if defined(WIN32)
237
    /* last arg is really a mode_t.  But windows... */
238
    n = open(__file, __oflag, va_arg(ap, uint32_t));
239
#else
240
    /* ... and some other toolchains...
241
     *
242
     * error: second argument to 'va_arg' is of promotable type 'mode_t'
243
     * (aka 'unsigned short'); this va_arg has undefined behavior because
244
     * arguments will be promoted to 'int'
245
     */
246
0
    n = open(__file, __oflag, (mode_t)va_arg(ap, unsigned int));
247
0
#endif
248
0
  else
249
0
    n = open(__file, __oflag);
250
0
  va_end(ap);
251
252
0
  if (n != -1 && lws_plat_apply_FD_CLOEXEC(n)) {
253
0
    close(n);
254
255
0
    return -1;
256
0
  }
257
258
0
  return n;
259
0
}
260
#endif
261
#endif
262
263
int
264
lws_pthread_self_to_tsi(struct lws_context *context)
265
0
{
266
#if defined(LWS_WITH_NETWORK) && LWS_MAX_SMP > 1
267
  pthread_t ps = pthread_self();
268
  struct lws_context_per_thread *pt = &context->pt[0];
269
  int n;
270
271
  /* case that we have SMP build, but don't use it */
272
  if (context->count_threads == 1)
273
    return 0;
274
275
  for (n = 0; n < context->count_threads; n++) {
276
    if (pthread_equal(ps, pt->self))
277
      return n;
278
    pt++;
279
  }
280
281
  return -1;
282
#else
283
0
  return 0;
284
0
#endif
285
0
}
286
287
void *
288
lws_context_user(struct lws_context *context)
289
0
{
290
0
  return context->user_space;
291
0
}
292
293
void
294
lws_explicit_bzero(void *p, size_t len)
295
0
{
296
0
  volatile uint8_t *vp = p;
297
298
0
  while (len--)
299
0
    *vp++ = 0;
300
0
}
301
302
#if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK))
303
304
/**
305
 * lws_now_secs() - seconds since 1970-1-1
306
 *
307
 */
308
unsigned long
309
lws_now_secs(void)
310
0
{
311
0
  struct timeval tv;
312
313
0
  gettimeofday(&tv, NULL);
314
315
0
  return (unsigned long)tv.tv_sec;
316
0
}
317
318
#endif
319
320
#if defined(LWS_WITH_SERVER)
321
const char *
322
lws_canonical_hostname(struct lws_context *context)
323
0
{
324
0
  return (const char *)context->canonical_hostname;
325
0
}
326
#endif
327
328
int
329
lws_get_count_threads(struct lws_context *context)
330
0
{
331
0
  return context->count_threads;
332
0
}
333
334
static const unsigned char e0f4[] = {
335
  0xa0 | ((2 - 1) << 2) | 1, /* e0 */
336
  0x80 | ((4 - 1) << 2) | 1, /* e1 */
337
  0x80 | ((4 - 1) << 2) | 1, /* e2 */
338
  0x80 | ((4 - 1) << 2) | 1, /* e3 */
339
  0x80 | ((4 - 1) << 2) | 1, /* e4 */
340
  0x80 | ((4 - 1) << 2) | 1, /* e5 */
341
  0x80 | ((4 - 1) << 2) | 1, /* e6 */
342
  0x80 | ((4 - 1) << 2) | 1, /* e7 */
343
  0x80 | ((4 - 1) << 2) | 1, /* e8 */
344
  0x80 | ((4 - 1) << 2) | 1, /* e9 */
345
  0x80 | ((4 - 1) << 2) | 1, /* ea */
346
  0x80 | ((4 - 1) << 2) | 1, /* eb */
347
  0x80 | ((4 - 1) << 2) | 1, /* ec */
348
  0x80 | ((2 - 1) << 2) | 1, /* ed */
349
  0x80 | ((4 - 1) << 2) | 1, /* ee */
350
  0x80 | ((4 - 1) << 2) | 1, /* ef */
351
  0x90 | ((3 - 1) << 2) | 2, /* f0 */
352
  0x80 | ((4 - 1) << 2) | 2, /* f1 */
353
  0x80 | ((4 - 1) << 2) | 2, /* f2 */
354
  0x80 | ((4 - 1) << 2) | 2, /* f3 */
355
  0x80 | ((1 - 1) << 2) | 2, /* f4 */
356
357
  0,         /* s0 */
358
  0x80 | ((4 - 1) << 2) | 0, /* s2 */
359
  0x80 | ((4 - 1) << 2) | 1, /* s3 */
360
};
361
362
int
363
lws_check_byte_utf8(unsigned char state, unsigned char c)
364
0
{
365
0
  unsigned char s = state;
366
367
0
  if (!s) {
368
0
    if (c >= 0x80) {
369
0
      if (c < 0xc2 || c > 0xf4)
370
0
        return -1;
371
0
      if (c < 0xe0)
372
0
        return 0x80 | ((4 - 1) << 2);
373
0
      else
374
0
        return e0f4[c - 0xe0];
375
0
    }
376
377
0
    return s;
378
0
  }
379
0
  if (c < (s & 0xf0) || c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30))
380
0
    return -1;
381
382
0
  return e0f4[21 + (s & 3)];
383
0
}
384
385
int
386
lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len)
387
0
{
388
0
  unsigned char s = *state;
389
390
0
  while (len--) {
391
0
    unsigned char c = *buf++;
392
393
0
    if (!s) {
394
0
      if (c >= 0x80) {
395
0
        if (c < 0xc2 || c > 0xf4)
396
0
          return 1;
397
0
        if (c < 0xe0)
398
0
          s = 0x80 | ((4 - 1) << 2);
399
0
        else
400
0
          s = e0f4[c - 0xe0];
401
0
      }
402
0
    } else {
403
0
      if (c < (s & 0xf0) ||
404
0
          c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30))
405
0
        return 1;
406
0
      s = e0f4[21 + (s & 3)];
407
0
    }
408
0
  }
409
410
0
  *state = s;
411
412
0
  return 0;
413
0
}
414
415
416
char *
417
lws_strdup(const char *s)
418
0
{
419
0
  size_t l = strlen(s) + 1;
420
0
  char *d = lws_malloc(l, "strdup");
421
422
0
  if (d)
423
0
    memcpy(d, s, l);
424
425
0
  return d;
426
0
}
427
428
const char *
429
lws_nstrstr(const char *buf, size_t len, const char *name, size_t nl)
430
0
{
431
0
  const char *end = buf + len - nl + 1;
432
0
  size_t n;
433
434
0
  if (nl > len)
435
    /* it cannot be found if the needle is longer than the haystack */
436
0
    return NULL;
437
438
0
  while (buf < end) {
439
0
    if (*buf != name[0]) {
440
0
      buf++;
441
0
      continue;
442
0
    }
443
444
0
    if (nl == 1)
445
      /* single char match, we are done */
446
0
      return buf;
447
448
0
    if (buf[nl - 1] == name[nl - 1]) {
449
      /*
450
       * This is looking interesting then... the first
451
       * and last chars match, let's check the insides
452
       */
453
0
      n = 1;
454
0
      while (n < nl && buf[n] == name[n])
455
0
        n++;
456
457
0
      if (n == nl)
458
        /* it's a hit */
459
0
        return buf;
460
0
    }
461
462
0
    buf++;
463
0
  }
464
465
0
  return NULL;
466
0
}
467
468
/*
469
 * name wants to be something like "\"myname\":"
470
 */
471
472
const char *
473
lws_json_simple_find(const char *buf, size_t len, const char *name, size_t *alen)
474
0
{
475
0
  size_t nl = strlen(name);
476
0
  const char *np = lws_nstrstr(buf, len, name, nl),
477
0
       *end = buf + len, *as;
478
0
  int qu = 0;
479
480
0
  if (!np)
481
0
    return NULL;
482
483
0
  np += nl;
484
485
0
  while (np < end && (*np == ' ' || *np == '\t'))
486
0
    np++;
487
488
0
  if (np >= end)
489
0
    return NULL;
490
491
  /*
492
   * The arg could be lots of things after "name": with JSON, commonly a
493
   * string like "mystring", true, false, null, [...] or {...} ... we want
494
   * to handle common, simple cases cheaply with this; the user can choose
495
   * a full JSON parser like lejp if it's complicated.  So if no opening
496
   * quote, return until a terminator like , ] }.  If there's an opening
497
   * quote, return until closing quote, handling escaped quotes.
498
   */
499
500
0
  if (*np == '\"') {
501
0
    qu = 1;
502
0
    np++;
503
0
  }
504
505
0
  as = np;
506
0
  while (np < end &&
507
0
         (!qu || *np != '\"') && /* end quote is EOT if quoted */
508
0
         (qu || (*np != '}' && *np != ']' && *np != ',')) /* delimiters */
509
0
  ) {
510
0
    if (qu && *np == '\\') /* skip next char if quoted escape */
511
0
      np++;
512
0
    np++;
513
0
  }
514
515
0
  *alen = (unsigned int)lws_ptr_diff(np, as);
516
517
0
  return as;
518
0
}
519
520
int
521
lws_json_simple_strcmp(const char *buf, size_t len, const char *name,
522
           const char *comp)
523
0
{
524
0
  size_t al;
525
0
  const char *hit = lws_json_simple_find(buf, len, name, &al);
526
527
0
  if (!hit)
528
0
    return -1;
529
530
0
  if (al != strlen(comp))
531
0
    return -1;
532
533
0
  return strncmp(hit, comp, al);
534
0
}
535
536
static const char *hex = "0123456789ABCDEF";
537
538
const char *
539
lws_sql_purify(char *escaped, const char *string, size_t len)
540
0
{
541
0
  const char *p = string;
542
0
  char *q = escaped;
543
544
0
  while (*p && len-- > 2) {
545
0
    if (*p == '\'') {
546
0
      *q++ = '\'';
547
0
      *q++ = '\'';
548
0
      len --;
549
0
      p++;
550
0
    } else
551
0
      *q++ = *p++;
552
0
  }
553
0
  *q = '\0';
554
555
0
  return escaped;
556
0
}
557
558
int
559
lws_sql_purify_len(const char *p)
560
0
{
561
0
  int olen = 0;
562
563
0
  while (*p) {
564
0
    if (*p++ == '\'')
565
0
      olen++;
566
0
    olen++;
567
0
  }
568
569
0
  return olen;
570
0
}
571
572
const char *
573
lws_json_purify(char *escaped, const char *string, int len, int *in_used)
574
0
{
575
0
  const char *p = string;
576
0
  char *q = escaped;
577
578
0
  if (!p) {
579
0
    escaped[0] = '\0';
580
0
    return escaped;
581
0
  }
582
583
0
  while (*p && len-- > 6) {
584
0
    if (*p == '\t') {
585
0
      p++;
586
0
      *q++ = '\\';
587
0
      *q++ = 't';
588
0
      continue;
589
0
    }
590
591
0
    if (*p == '\n') {
592
0
      p++;
593
0
      *q++ = '\\';
594
0
      *q++ = 'n';
595
0
      continue;
596
0
    }
597
598
0
    if (*p == '\r') {
599
0
      p++;
600
0
      *q++ = '\\';
601
0
      *q++ = 'r';
602
0
      continue;
603
0
    }
604
605
0
    if (*p == '\\') {
606
0
      p++;
607
0
      *q++ = '\\';
608
0
      *q++ = '\\';
609
0
      continue;
610
0
    }
611
612
0
    if (*p == '\"' || *p < 0x20) {
613
0
      *q++ = '\\';
614
0
      *q++ = 'u';
615
0
      *q++ = '0';
616
0
      *q++ = '0';
617
0
      *q++ = hex[((*p) >> 4) & 15];
618
0
      *q++ = hex[(*p) & 15];
619
0
      len -= 5;
620
0
      p++;
621
0
    } else
622
0
      *q++ = *p++;
623
0
  }
624
0
  *q = '\0';
625
626
0
  if (in_used)
627
0
    *in_used = lws_ptr_diff(p, string);
628
629
0
  return escaped;
630
0
}
631
632
int
633
lws_json_purify_len(const char *string)
634
0
{
635
0
  int len = 0;
636
0
  const char *p = string;
637
638
0
  while (*p) {
639
0
    if (*p == '\t' || *p == '\n' || *p == '\r') {
640
0
      p++;
641
0
      len += 2;
642
0
      continue;
643
0
    }
644
645
0
    if (*p == '\"' || *p == '\\' || *p < 0x20) {
646
0
      len += 6;
647
0
      p++;
648
0
      continue;
649
0
    }
650
0
    p++;
651
0
    len++;
652
0
  }
653
654
0
  return len;
655
0
}
656
657
void
658
lws_filename_purify_inplace(char *filename)
659
0
{
660
0
  while (*filename) {
661
662
0
    if (*filename == '.' && filename[1] == '.') {
663
0
      *filename = '_';
664
0
      filename[1] = '_';
665
0
    }
666
667
0
    if (*filename == ':' ||
668
0
#if !defined(WIN32)
669
0
        *filename == '\\' ||
670
0
#endif
671
0
        *filename == '$' ||
672
0
        *filename == '%')
673
0
      *filename = '_';
674
675
0
    filename++;
676
0
  }
677
0
}
678
679
const char *
680
lws_urlencode(char *escaped, const char *string, int len)
681
0
{
682
0
  const char *p = string;
683
0
  char *q = escaped;
684
685
0
  while (*p && len-- > 3) {
686
0
    if (*p == ' ') {
687
0
      *q++ = '+';
688
0
      p++;
689
0
      continue;
690
0
    }
691
0
    if ((*p >= '0' && *p <= '9') ||
692
0
        (*p >= 'A' && *p <= 'Z') ||
693
0
        (*p >= 'a' && *p <= 'z')) {
694
0
      *q++ = *p++;
695
0
      continue;
696
0
    }
697
0
    *q++ = '%';
698
0
    *q++ = hex[(*p >> 4) & 0xf];
699
0
    *q++ = hex[*p & 0xf];
700
701
0
    len -= 2;
702
0
    p++;
703
0
  }
704
0
  *q = '\0';
705
706
0
  return escaped;
707
0
}
708
709
int
710
lws_urldecode(char *string, const char *escaped, int len)
711
0
{
712
0
  int state = 0, n;
713
0
  char sum = 0;
714
715
0
  while (*escaped && len) {
716
0
    switch (state) {
717
0
    case 0:
718
0
      if (*escaped == '%') {
719
0
        state++;
720
0
        escaped++;
721
0
        continue;
722
0
      }
723
0
      if (*escaped == '+') {
724
0
        escaped++;
725
0
        *string++ = ' ';
726
0
        len--;
727
0
        continue;
728
0
      }
729
0
      *string++ = *escaped++;
730
0
      len--;
731
0
      break;
732
0
    case 1:
733
0
      n = char_to_hex(*escaped);
734
0
      if (n < 0)
735
0
        return -1;
736
0
      escaped++;
737
0
      sum = (char)(n << 4);
738
0
      state++;
739
0
      break;
740
741
0
    case 2:
742
0
      n = char_to_hex(*escaped);
743
0
      if (n < 0)
744
0
        return -1;
745
0
      escaped++;
746
0
      *string++ = (char)(sum | n);
747
0
      len--;
748
0
      state = 0;
749
0
      break;
750
0
    }
751
752
0
  }
753
0
  *string = '\0';
754
755
0
  return 0;
756
0
}
757
758
/*
759
 * Copy the url formof rel into dest, using base to fill in missing context
760
 *
761
 * If base is https://x.com/y/z.html
762
 *
763
 *   a.html               -> https://x.com/y/a/html
764
 *   ../b.html            -> https://x.com/b.html
765
 *   /c.html              -> https://x.com/c.html
766
 *   https://y.com/a.html -> https://y.com/a.html
767
 */
768
769
int
770
lws_http_rel_to_url(char *dest, size_t len, const char *base, const char *rel)
771
0
{
772
0
  size_t n = 0, ps = 0;
773
0
  char d = 0;
774
775
  // lwsl_err("%s: base %s, rel %s\n", __func__, base, rel);
776
777
0
  if (!strncmp(rel, "https://", 8) ||
778
0
      !strncmp(rel, "http://", 7) ||
779
0
      !strncmp(rel, "file://", 7)) {
780
    /* rel is already a full url, just copy it */
781
0
    lws_strncpy(dest, rel, len);
782
0
    return 0;
783
0
  }
784
785
  /* we're going to be using the first part of base at least */
786
787
0
  while (n < len - 2 && base[n]) {
788
0
    dest[n] = base[n];
789
0
    if (d && base[n] == '/') {
790
0
      n++;
791
0
      ps = n;
792
      //if (rel[0] == '/') {
793
0
        break;
794
      //}
795
0
    }
796
0
    if (n && base[n] == '/' && base[n - 1] == '/')
797
0
      d = 1;
798
0
    n++;
799
0
  }
800
801
0
  if (!n || n >= len - 2)
802
0
    return 1;
803
804
  /* if we did not have a '/' after the hostname, add one */
805
0
  if (dest[n - 1] != '/') {
806
0
    ps = n;
807
0
    dest[n++] = '/';
808
0
  }
809
810
  /* is rel an absolute path we should just use with the hostname? */
811
0
  if (rel[0] != '/') {
812
813
    /*
814
     * Apply the rest of the basename, without the file part,
815
     * end with last / if any
816
     */
817
818
0
    ps = n;
819
0
    while (n < len - 2 && base[n]) {
820
0
      dest[n] = base[n];
821
0
      n++;
822
0
      if (base[n] == '/')
823
0
        ps = n;
824
0
    }
825
826
0
    n = ps;
827
828
0
    if (n >= len - 2)
829
0
      return 1;
830
831
    /* if we did not have a '/' after the base path, add one */
832
0
    if (dest[n - 1] != '/')
833
0
      dest[n++] = '/';
834
0
  }
835
836
  /* append rel */
837
838
0
  if (len - n < strlen(rel) + 2)
839
0
    return 1;
840
841
0
  lws_strncpy(dest + n, rel, len - n);
842
843
0
  return 0;
844
0
}
845
846
int
847
lws_finalize_startup(struct lws_context *context)
848
0
{
849
0
  if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
850
0
    if (lws_plat_drop_app_privileges(context, 1))
851
0
      return 1;
852
853
0
  return 0;
854
0
}
855
856
#if !defined(LWS_PLAT_FREERTOS)
857
void
858
lws_get_effective_uid_gid(struct lws_context *context, uid_t *uid, gid_t *gid)
859
0
{
860
0
  *uid = context->uid;
861
0
  *gid = context->gid;
862
0
}
863
#endif
864
865
int
866
lws_snprintf(char *str, size_t size, const char *format, ...)
867
30
{
868
30
  va_list ap;
869
30
  int n;
870
871
30
  if (!str || !size)
872
0
    return 0;
873
874
30
  va_start(ap, format);
875
30
  n = vsnprintf(str, size, format, ap);
876
30
  va_end(ap);
877
878
30
  if (n >= (int)size)
879
0
    return (int)size;
880
881
30
  return n;
882
30
}
883
884
char *
885
lws_strncpy(char *dest, const char *src, size_t size)
886
0
{
887
0
  strncpy(dest, src, size - 1);
888
0
  dest[size - 1] = '\0';
889
890
0
  return dest;
891
0
}
892
893
int
894
lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len)
895
0
{
896
0
  const uint8_t *pa = a, *pb = b;
897
0
  uint8_t sum = 0;
898
899
0
  while (len--)
900
0
    sum |= (uint8_t)(*pa++ ^ *pb++);
901
902
0
  return sum;
903
0
}
904
905
lws_tokenize_elem
906
lws_tokenize(struct lws_tokenize *ts)
907
0
{
908
0
  const char *rfc7230_delims = "(),/:;<=>?@[\\]{}";
909
0
  char c, flo = 0, d_minus = '-', d_dot = '.', d_star = '*', s_minus = '\0',
910
0
       s_dot = '\0', s_star = '\0', d_eq = '=', s_eq = '\0', skipping = 0;
911
0
  signed char num = (ts->flags & LWS_TOKENIZE_F_NO_INTEGERS) ? 0 : -1;
912
0
  int utf8 = 0;
913
914
  /* for speed, compute the effect of the flags outside the loop */
915
916
0
  if (ts->flags & LWS_TOKENIZE_F_MINUS_NONTERM) {
917
0
    d_minus = '\0';
918
0
    s_minus = '-';
919
0
  }
920
0
  if (ts->flags & LWS_TOKENIZE_F_DOT_NONTERM) {
921
0
    d_dot = '\0';
922
0
    s_dot = '.';
923
0
  }
924
0
  if (ts->flags & LWS_TOKENIZE_F_ASTERISK_NONTERM) {
925
0
    d_star = '\0';
926
0
    s_star = '*';
927
0
  }
928
0
  if (ts->flags & LWS_TOKENIZE_F_EQUALS_NONTERM) {
929
0
    d_eq = '\0';
930
0
    s_eq = '=';
931
0
  }
932
933
0
  if (!ts->dry)
934
0
    ts->token = ts->collect;
935
0
  ts->dry = 0;
936
937
0
  if (ts->reset_token) {
938
0
    ts->effline = ts->line;
939
0
    ts->state = LWS_TOKZS_LEADING_WHITESPACE;
940
0
    ts->token_len = 0;
941
0
    ts->reset_token = 0;
942
0
  }
943
944
0
  while (ts->len) {
945
0
    c = *ts->start++;
946
0
    ts->len--;
947
948
0
    utf8 = lws_check_byte_utf8((unsigned char)utf8, (unsigned char)c);
949
0
    if (utf8 < 0)
950
0
      return LWS_TOKZE_ERR_BROKEN_UTF8;
951
952
0
    if (!c)
953
0
      break;
954
955
0
    if (skipping) {
956
0
      if (c != '\r' && c != '\n')
957
0
        continue;
958
0
      else
959
0
        skipping = 0;
960
0
    }
961
962
    /* comment */
963
964
0
    if (ts->flags & LWS_TOKENIZE_F_HASH_COMMENT &&
965
0
        ts->state != LWS_TOKZS_QUOTED_STRING &&
966
0
        c == '#') {
967
0
      skipping = 1;
968
0
      continue;
969
0
    }
970
971
    /* whitespace */
972
973
0
    if (c == ' ' || c == '\t' || c == '\n' || c == '\r' ||
974
0
        c == '\f') {
975
0
      if (c == '\r' && !ts->crlf)
976
0
        ts->line++;
977
0
      if (c == '\n') {
978
0
        ts->line++;
979
0
        ts->crlf = 1;
980
0
      }
981
0
      switch (ts->state) {
982
0
      case LWS_TOKZS_LEADING_WHITESPACE:
983
0
      case LWS_TOKZS_TOKEN_POST_TERMINAL:
984
0
        continue;
985
0
      case LWS_TOKZS_QUOTED_STRING:
986
0
        goto agg;
987
0
      case LWS_TOKZS_TOKEN:
988
        /* we want to scan forward to look for = */
989
990
0
        ts->state = LWS_TOKZS_TOKEN_POST_TERMINAL;
991
0
        continue;
992
0
      }
993
0
    } else
994
0
      ts->crlf = 0;
995
996
    /* quoted string */
997
998
0
    if (c == '\"') {
999
0
      if (ts->state == LWS_TOKZS_QUOTED_STRING) {
1000
0
        ts->reset_token = 1;
1001
1002
0
        return LWS_TOKZE_QUOTED_STRING;
1003
0
      }
1004
1005
      /* starting a quoted string */
1006
1007
0
      if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
1008
0
        if (ts->delim == LWSTZ_DT_NEED_DELIM)
1009
0
          return LWS_TOKZE_ERR_COMMA_LIST;
1010
0
        ts->delim = LWSTZ_DT_NEED_DELIM;
1011
0
      }
1012
1013
0
      ts->state = LWS_TOKZS_QUOTED_STRING;
1014
0
      ts->token = ts->collect;
1015
0
      ts->token_len = 0;
1016
1017
0
      continue;
1018
0
    }
1019
1020
    /* token= aggregation */
1021
1022
0
    if (!(ts->flags & LWS_TOKENIZE_F_EQUALS_NONTERM) &&
1023
0
        c == '=' && (ts->state == LWS_TOKZS_TOKEN_POST_TERMINAL ||
1024
0
         ts->state == LWS_TOKZS_TOKEN)) {
1025
1026
0
      ts->reset_token = 1;
1027
1028
0
      if (num == 1)
1029
0
        return LWS_TOKZE_ERR_NUM_ON_LHS;
1030
      /* swallow the = */
1031
0
      return LWS_TOKZE_TOKEN_NAME_EQUALS;
1032
0
    }
1033
1034
    /* optional token: aggregation */
1035
1036
0
    if ((ts->flags & LWS_TOKENIZE_F_AGG_COLON) && c == ':' &&
1037
0
        (ts->state == LWS_TOKZS_TOKEN_POST_TERMINAL ||
1038
0
         ts->state == LWS_TOKZS_TOKEN)) {
1039
0
      ts->reset_token = 1;
1040
1041
      /* swallow the : */
1042
0
      return LWS_TOKZE_TOKEN_NAME_COLON;
1043
0
    }
1044
1045
    /* aggregate . in a number as a float */
1046
1047
0
    if (c == '.' && !(ts->flags & LWS_TOKENIZE_F_NO_FLOATS) &&
1048
0
        ts->state == LWS_TOKZS_TOKEN && num == 1) {
1049
0
      if (flo)
1050
0
        return LWS_TOKZE_ERR_MALFORMED_FLOAT;
1051
0
      flo = 1;
1052
0
      goto agg;
1053
0
    }
1054
1055
    /*
1056
     * Delimiter... by default anything that:
1057
     *
1058
     *  - isn't matched earlier, or
1059
     *  - is [A-Z, a-z, 0-9, _], and
1060
     *  - is not a partial utf8 char
1061
     *
1062
     * is a "delimiter", it marks the end of a token and is itself
1063
     * reported as a single LWS_TOKZE_DELIMITER each time.
1064
     *
1065
     * However with LWS_TOKENIZE_F_RFC7230_DELIMS flag, tokens may
1066
     * contain any noncontrol character that isn't defined in
1067
     * rfc7230_delims, and only characters listed there are treated
1068
     * as delimiters.
1069
     */
1070
1071
0
    if (!utf8 &&
1072
0
         ((ts->flags & LWS_TOKENIZE_F_RFC7230_DELIMS &&
1073
0
           strchr(rfc7230_delims, c) && c > 32) ||
1074
0
           ((!(ts->flags & LWS_TOKENIZE_F_RFC7230_DELIMS) &&
1075
0
            (c < '0' || c > '9') && (c < 'A' || c > 'Z') &&
1076
0
            (c < 'a' || c > 'z') && c != '_') &&
1077
0
            c != s_minus && c != s_dot && c != s_star && c != s_eq) ||
1078
0
            c == d_minus ||
1079
0
      c == d_dot ||
1080
0
      c == d_star ||
1081
0
      c == d_eq
1082
0
        ) &&
1083
0
        !((ts->flags & LWS_TOKENIZE_F_COLON_NONTERM) && c == ':') &&
1084
0
        !((ts->flags & LWS_TOKENIZE_F_SLASH_NONTERM) && c == '/')) {
1085
0
      switch (ts->state) {
1086
0
      case LWS_TOKZS_LEADING_WHITESPACE:
1087
0
        if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
1088
0
          if (c != ',' ||
1089
0
              ts->delim != LWSTZ_DT_NEED_DELIM)
1090
0
            return LWS_TOKZE_ERR_COMMA_LIST;
1091
0
          ts->delim = LWSTZ_DT_NEED_NEXT_CONTENT;
1092
0
        }
1093
1094
0
        ts->token = ts->start - 1;
1095
0
        ts->token_len = 1;
1096
0
        ts->reset_token = 1;
1097
1098
0
        return LWS_TOKZE_DELIMITER;
1099
1100
0
      case LWS_TOKZS_QUOTED_STRING:
1101
0
agg:
1102
0
        ts->collect[ts->token_len++] = c;
1103
0
        if (ts->token_len == sizeof(ts->collect) - 1)
1104
0
          return LWS_TOKZE_TOO_LONG;
1105
0
        ts->collect[ts->token_len] = '\0';
1106
0
        continue;
1107
1108
0
      case LWS_TOKZS_TOKEN_POST_TERMINAL:
1109
0
      case LWS_TOKZS_TOKEN:
1110
        /* report the delimiter next time */
1111
0
        ts->start--;
1112
0
        ts->len++;
1113
0
        goto token_or_numeric;
1114
0
      }
1115
0
    }
1116
1117
    /* anything that's not whitespace or delimiter is payload */
1118
1119
0
    switch (ts->state) {
1120
0
    case LWS_TOKZS_LEADING_WHITESPACE:
1121
1122
0
      if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
1123
0
        if (ts->delim == LWSTZ_DT_NEED_DELIM) {
1124
0
          ts->reset_token = 1;
1125
1126
0
          return LWS_TOKZE_ERR_COMMA_LIST;
1127
0
        }
1128
0
        ts->delim = LWSTZ_DT_NEED_DELIM;
1129
0
      }
1130
1131
0
      ts->state = LWS_TOKZS_TOKEN;
1132
0
      ts->reset_token = 1;
1133
1134
0
      ts->token = ts->collect; //ts->start - 1;
1135
0
      ts->collect[0] = c;
1136
0
      ts->token_len = 1;
1137
0
      goto checknum;
1138
1139
0
    case LWS_TOKZS_QUOTED_STRING:
1140
0
    case LWS_TOKZS_TOKEN:
1141
0
      ts->collect[ts->token_len++] = c;
1142
0
      if (ts->token_len == sizeof(ts->collect) - 1)
1143
0
        return LWS_TOKZE_TOO_LONG;
1144
0
      ts->collect[ts->token_len] = '\0';
1145
0
checknum:
1146
0
      if (!(ts->flags & LWS_TOKENIZE_F_NO_INTEGERS)) {
1147
0
        if (c < '0' || c > '9')
1148
0
          num = 0;
1149
0
        else
1150
0
          if (num < 0)
1151
0
            num = 1;
1152
0
      }
1153
0
      continue;
1154
1155
0
    case LWS_TOKZS_TOKEN_POST_TERMINAL:
1156
      /* report the new token next time */
1157
0
      ts->start--;
1158
0
      ts->len++;
1159
0
      goto token_or_numeric;
1160
0
    }
1161
0
  }
1162
1163
  /* we ran out of content */
1164
1165
0
  if (ts->flags & LWS_TOKENIZE_F_EXPECT_MORE) {
1166
0
    ts->reset_token = 0;
1167
0
    ts->dry = 1;
1168
0
    return LWS_TOKZE_WANT_READ;
1169
0
  }
1170
1171
0
  if (utf8) /* ended partway through a multibyte char */
1172
0
    return LWS_TOKZE_ERR_BROKEN_UTF8;
1173
1174
0
  if (ts->state == LWS_TOKZS_QUOTED_STRING)
1175
0
    return LWS_TOKZE_ERR_UNTERM_STRING;
1176
1177
0
  if (ts->state != LWS_TOKZS_TOKEN_POST_TERMINAL &&
1178
0
      ts->state != LWS_TOKZS_TOKEN) {
1179
0
    if ((ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) &&
1180
0
         ts->delim == LWSTZ_DT_NEED_NEXT_CONTENT)
1181
0
      return LWS_TOKZE_ERR_COMMA_LIST;
1182
1183
0
    return LWS_TOKZE_ENDED;
1184
0
  }
1185
1186
  /* report the pending token */
1187
1188
0
token_or_numeric:
1189
1190
0
  ts->reset_token = 1;
1191
1192
0
  if (num != 1)
1193
0
    return LWS_TOKZE_TOKEN;
1194
0
  if (flo)
1195
0
    return LWS_TOKZE_FLOAT;
1196
1197
0
  return LWS_TOKZE_INTEGER;
1198
0
}
1199
1200
1201
int
1202
lws_tokenize_cstr(struct lws_tokenize *ts, char *str, size_t max)
1203
0
{
1204
0
  if (ts->token_len + 1 >= max)
1205
0
    return 1;
1206
1207
0
  memcpy(str, ts->token, ts->token_len);
1208
0
  str[ts->token_len] = '\0';
1209
1210
0
  return 0;
1211
0
}
1212
1213
void
1214
lws_tokenize_init(struct lws_tokenize *ts, const char *start, int flags)
1215
0
{
1216
0
  ts->start = start;
1217
0
  ts->len = 0x7fffffff;
1218
0
  ts->flags = (uint16_t)(unsigned int)flags;
1219
0
  ts->delim = LWSTZ_DT_NEED_FIRST_CONTENT;
1220
0
  ts->token = NULL;
1221
0
  ts->token_len = 0;
1222
0
  ts->line = 0;
1223
0
  ts->effline = 0;
1224
0
  ts->dry = 0;
1225
0
  ts->reset_token = 0;
1226
0
  ts->crlf = 0;
1227
0
  ts->state = LWS_TOKZS_LEADING_WHITESPACE;
1228
0
}
1229
1230
1231
typedef enum {
1232
  LWS_EXPS_LITERAL,
1233
  LWS_EXPS_OPEN_OR_LIT,
1234
  LWS_EXPS_NAME_OR_CLOSE,
1235
  LWS_EXPS_DRAIN,
1236
} lws_strexp_state;
1237
1238
void
1239
lws_strexp_init(lws_strexp_t *exp, void *priv, lws_strexp_expand_cb cb,
1240
     char *out, size_t olen)
1241
0
{
1242
0
  memset(exp, 0, sizeof(*exp));
1243
0
  exp->cb = cb;
1244
0
  exp->out = out;
1245
0
  exp->olen = olen;
1246
0
  exp->state = LWS_EXPS_LITERAL;
1247
0
  exp->priv = priv;
1248
0
}
1249
1250
void
1251
lws_strexp_reset_out(lws_strexp_t *exp, char *out, size_t olen)
1252
0
{
1253
0
  exp->out = out;
1254
0
  exp->olen = olen;
1255
0
  exp->pos = 0;
1256
0
}
1257
1258
int
1259
lws_strexp_expand(lws_strexp_t *exp, const char *in, size_t len,
1260
      size_t *pused_in, size_t *pused_out)
1261
0
{
1262
0
  size_t used = 0;
1263
0
  int n;
1264
1265
0
  while (used < len) {
1266
1267
0
    switch (exp->state) {
1268
0
    case LWS_EXPS_LITERAL:
1269
0
      if (*in == '$') {
1270
0
        exp->state = LWS_EXPS_OPEN_OR_LIT;
1271
0
        break;
1272
0
      }
1273
1274
0
      if (exp->out)
1275
0
        exp->out[exp->pos] = *in;
1276
0
      exp->pos++;
1277
0
      if (exp->olen - exp->pos < 1) {
1278
0
        *pused_in = used + 1;
1279
0
        *pused_out = exp->pos;
1280
0
        return LSTRX_FILLED_OUT;
1281
0
      }
1282
0
      break;
1283
1284
0
    case LWS_EXPS_OPEN_OR_LIT:
1285
0
      if (*in == '{') {
1286
0
        exp->state = LWS_EXPS_NAME_OR_CLOSE;
1287
0
        exp->name_pos = 0;
1288
0
        exp->exp_ofs = 0;
1289
0
        break;
1290
0
      }
1291
      /* treat as a literal */
1292
0
      if (exp->olen - exp->pos < 3)
1293
0
        return -1;
1294
1295
0
      if (exp->out) {
1296
0
        exp->out[exp->pos++] = '$';
1297
0
        exp->out[exp->pos++] = *in;
1298
0
      } else
1299
0
        exp->pos += 2;
1300
0
      if (*in != '$')
1301
0
        exp->state = LWS_EXPS_LITERAL;
1302
0
      break;
1303
1304
0
    case LWS_EXPS_NAME_OR_CLOSE:
1305
0
      if (*in == '}') {
1306
0
        exp->name[exp->name_pos] = '\0';
1307
0
        exp->state = LWS_EXPS_DRAIN;
1308
0
        goto drain;
1309
0
      }
1310
0
      if (exp->name_pos >= sizeof(exp->name) - 1)
1311
0
        return LSTRX_FATAL_NAME_TOO_LONG;
1312
1313
0
      exp->name[exp->name_pos++] = *in;
1314
0
      break;
1315
1316
0
    case LWS_EXPS_DRAIN:
1317
0
drain:
1318
0
      *pused_in = used;
1319
0
      n = exp->cb(exp->priv, exp->name, exp->out, &exp->pos,
1320
0
            exp->olen, &exp->exp_ofs);
1321
0
      *pused_out = exp->pos;
1322
0
      if (n == LSTRX_FILLED_OUT ||
1323
0
          n == LSTRX_FATAL_NAME_UNKNOWN)
1324
0
        return n;
1325
1326
0
      exp->state = LWS_EXPS_LITERAL;
1327
0
      break;
1328
0
    }
1329
1330
0
    used++;
1331
0
    in++;
1332
0
  }
1333
1334
0
  if (exp->out)
1335
0
    exp->out[exp->pos] = '\0';
1336
0
  *pused_in = used;
1337
0
  *pused_out = exp->pos;
1338
1339
0
  return LSTRX_DONE;
1340
0
}
1341
1342
int
1343
lws_strcmp_wildcard(const char *wildcard, size_t wlen, const char *check,
1344
        size_t clen)
1345
0
{
1346
0
  const char *match[3], *wc[3], *wc_end = wildcard + wlen,
1347
0
       *cend = check + clen;
1348
0
  int sp = 0;
1349
1350
0
  do {
1351
1352
0
    if (wildcard == wc_end) {
1353
      /*
1354
       * We reached the end of wildcard, but not of check,
1355
       * and the last thing in wildcard was not a * or we
1356
       * would have completed already... if we can rewind,
1357
       * let's try that...
1358
       */
1359
0
      if (sp) {
1360
0
        wildcard = wc[sp - 1];
1361
0
        check = match[--sp];
1362
1363
0
        continue;
1364
0
      }
1365
1366
      /* otherwise it's the end of the road for this one */
1367
1368
0
      return 1;
1369
0
    }
1370
1371
0
    if (*wildcard == '*') {
1372
1373
0
      if (++wildcard == wc_end)
1374
         /*
1375
          * Wildcard ended on a *, so we know we will
1376
          * match unconditionally
1377
          */
1378
0
        return 0;
1379
1380
      /*
1381
       * Now we need to stick wildcard here and see if there
1382
       * is any remaining match exists, for eg b of "a*b"
1383
       */
1384
1385
0
      if (sp == LWS_ARRAY_SIZE(match)) {
1386
0
        lwsl_err("%s: exceeds * stack\n", __func__);
1387
0
        return 1; /* we can't deal with it */
1388
0
      }
1389
1390
0
      wc[sp] = wildcard;
1391
      /* if we ever pop and come back here, pick up from +1 */
1392
0
      match[sp++] = check + 1;
1393
0
      continue;
1394
0
    }
1395
1396
0
    if (*(check++) == *wildcard) {
1397
1398
0
      if (wildcard == wc_end)
1399
0
        return 0;
1400
      /*
1401
       * We're still compatible with wildcard... keep going
1402
       */
1403
0
      wildcard++;
1404
1405
0
      continue;
1406
0
    }
1407
1408
0
    if (!sp)
1409
      /*
1410
       * We're just trying to match literals, and failed...
1411
       */
1412
0
      return 1;
1413
1414
    /* we're looking for a post-* match... keep looking... */
1415
1416
0
  } while (check < cend);
1417
1418
  /*
1419
   * We reached the end of check, if also at end of wildcard we're OK
1420
   */
1421
1422
0
  return wildcard != wc_end;
1423
0
}
1424
1425
#if defined(LWS_WITH_NETWORK) && LWS_MAX_SMP > 1
1426
1427
void
1428
lws_mutex_refcount_init(struct lws_mutex_refcount *mr)
1429
{
1430
  pthread_mutex_init(&mr->lock, NULL);
1431
  mr->last_lock_reason = NULL;
1432
  mr->lock_depth = 0;
1433
  mr->metadata = 0;
1434
#ifdef __PTW32_H
1435
  /* If we use implementation of PThreads for Win that is
1436
   * distributed by VCPKG */
1437
  memset(&mr->lock_owner, 0, sizeof(pthread_t));
1438
#else
1439
  mr->lock_owner = 0;
1440
#endif
1441
}
1442
1443
void
1444
lws_mutex_refcount_destroy(struct lws_mutex_refcount *mr)
1445
{
1446
  pthread_mutex_destroy(&mr->lock);
1447
}
1448
1449
void
1450
lws_mutex_refcount_lock(struct lws_mutex_refcount *mr, const char *reason)
1451
{
1452
  /* if true, this sequence is atomic because our thread has the lock
1453
   *
1454
   *  - if true, only guy who can race to make it untrue is our thread,
1455
   *    and we are here.
1456
   *
1457
   *  - if false, only guy who could race to make it true is our thread,
1458
   *    and we are here
1459
   *
1460
   *  - it can be false and change to a different tid that is also false
1461
   */
1462
#ifdef __PTW32_H
1463
  /* If we use implementation of PThreads for Win that is
1464
   * distributed by VCPKG */
1465
  if (pthread_equal(mr->lock_owner, pthread_self()))
1466
#else
1467
  if (mr->lock_owner == pthread_self())
1468
#endif
1469
  {
1470
    /* atomic because we only change it if we own the lock */
1471
    mr->lock_depth++;
1472
    return;
1473
  }
1474
1475
  pthread_mutex_lock(&mr->lock);
1476
  /* atomic because only we can have the lock */
1477
  mr->last_lock_reason = reason;
1478
  mr->lock_owner = pthread_self();
1479
  mr->lock_depth = 1;
1480
  //lwsl_notice("tid %d: lock %s\n", mr->tid, reason);
1481
}
1482
1483
void
1484
lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr)
1485
{
1486
  if (--mr->lock_depth)
1487
    /* atomic because only thread that has the lock can unlock */
1488
    return;
1489
1490
  mr->last_lock_reason = "free";
1491
#ifdef __PTW32_H
1492
  /* If we use implementation of PThreads for Win that is
1493
   * distributed by VCPKG */
1494
  memset(&mr->lock_owner, 0, sizeof(pthread_t));
1495
#else
1496
  mr->lock_owner = 0;
1497
#endif
1498
  // lwsl_notice("tid %d: unlock %s\n", mr->tid, mr->last_lock_reason);
1499
  pthread_mutex_unlock(&mr->lock);
1500
}
1501
1502
void
1503
lws_mutex_refcount_assert_held(struct lws_mutex_refcount *mr)
1504
{
1505
#ifdef __PTW32_H
1506
  /* If we use implementation of PThreads for Win that is
1507
   * distributed by VCPKG */
1508
  assert(pthread_equal(mr->lock_owner, pthread_self()) && mr->lock_depth);
1509
#else
1510
  assert(mr->lock_owner == pthread_self() && mr->lock_depth);
1511
#endif
1512
}
1513
1514
#endif /* SMP */
1515
1516
1517
const char *
1518
lws_cmdline_option(int argc, const char **argv, const char *val)
1519
0
{
1520
0
  size_t n = strlen(val);
1521
0
  int c = argc;
1522
1523
0
  while (--c > 0) {
1524
1525
0
    if (!strncmp(argv[c], val, n)) {
1526
0
      if (c < argc - 1 && !*(argv[c] + n)) {
1527
        /* coverity treats unchecked argv as "tainted" */
1528
0
        if (!argv[c + 1] || strlen(argv[c + 1]) > 1024)
1529
0
          return NULL;
1530
0
        return argv[c + 1];
1531
0
      }
1532
1533
0
      if (argv[c][n] == '=')
1534
0
        return &argv[c][n + 1];
1535
0
      return argv[c] + n;
1536
0
    }
1537
0
  }
1538
1539
0
  return NULL;
1540
0
}
1541
1542
static const char * const builtins[] = {
1543
  "-d",
1544
  "--fault-injection",
1545
  "--fault-seed",
1546
  "--ignore-sigterm",
1547
  "--ssproxy-port",
1548
  "--ssproxy-iface",
1549
  "--ssproxy-ads",
1550
};
1551
1552
enum opts {
1553
  OPT_DEBUGLEVEL,
1554
  OPT_FAULTINJECTION,
1555
  OPT_FAULT_SEED,
1556
  OPT_IGNORE_SIGTERM,
1557
  OPT_SSPROXY_PORT,
1558
  OPT_SSPROXY_IFACE,
1559
  OPT_SSPROXY_ADS,
1560
};
1561
1562
#if !defined(LWS_PLAT_FREERTOS)
1563
static void
1564
lws_sigterm_catch(int sig)
1565
0
{
1566
0
}
1567
#endif
1568
1569
void
1570
_lws_context_info_defaults(struct lws_context_creation_info *info,
1571
        const char *sspol)
1572
0
{
1573
0
  memset(info, 0, sizeof *info);
1574
0
        info->fd_limit_per_thread = 1 + 6 + 1;
1575
0
#if defined(LWS_WITH_NETWORK)
1576
0
        info->port = CONTEXT_PORT_NO_LISTEN;
1577
0
#endif
1578
0
#if defined(LWS_WITH_SECURE_STREAMS) && !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
1579
0
        info->pss_policies_json = sspol;
1580
0
#endif
1581
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
1582
        if (!sspol)
1583
          info->protocols = lws_sspc_protocols;
1584
#endif
1585
0
        info->options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
1586
0
          LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW |
1587
0
          LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
1588
0
}
1589
1590
void
1591
lws_default_loop_exit(struct lws_context *cx)
1592
0
{
1593
0
  if (cx) {
1594
0
    cx->interrupted = 1;
1595
0
#if defined(LWS_WITH_NETWORK)
1596
0
    lws_cancel_service(cx);
1597
0
#endif
1598
0
  }
1599
0
}
1600
1601
#if defined(LWS_WITH_NETWORK)
1602
void
1603
lws_context_default_loop_run_destroy(struct lws_context *cx)
1604
0
{
1605
        /* the default event loop, since we didn't provide an alternative one */
1606
1607
0
        while (!cx->interrupted && lws_service(cx, 0) >= 0)
1608
0
          ;
1609
1610
0
        lws_context_destroy(cx);
1611
0
}
1612
#endif
1613
1614
int
1615
lws_cmdline_passfail(int argc, const char **argv, int actual)
1616
0
{
1617
0
  int expected = 0;
1618
0
  const char *p;
1619
1620
0
  if ((p = lws_cmdline_option(argc, argv, "--expected-exit")))
1621
0
    expected = atoi(p);
1622
1623
0
  if (actual == expected) {
1624
0
    lwsl_user("Completed: OK (seen expected %d)\n", actual);
1625
1626
0
    return 0;
1627
0
  }
1628
1629
0
  lwsl_err("Completed: failed: exit %d, expected %d\n", actual, expected);
1630
1631
0
  return 1;
1632
0
}
1633
1634
void
1635
lws_cmdline_option_handle_builtin(int argc, const char **argv,
1636
          struct lws_context_creation_info *info)
1637
0
{
1638
0
  const char *p;
1639
0
  int n, m, logs = info->default_loglevel ? info->default_loglevel :
1640
0
        LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
1641
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
1642
  uint64_t seed = (uint64_t)lws_now_usecs();
1643
#endif
1644
1645
0
  for (n = 0; n < (int)LWS_ARRAY_SIZE(builtins); n++) {
1646
0
    p = lws_cmdline_option(argc, argv, builtins[n]);
1647
0
    if (!p)
1648
0
      continue;
1649
1650
0
    m = atoi(p);
1651
1652
0
    switch (n) {
1653
0
    case OPT_DEBUGLEVEL:
1654
0
      logs = m;
1655
0
      break;
1656
1657
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
1658
    case OPT_SSPROXY_PORT:
1659
      /* connect to ssproxy via UDS by default, else via
1660
       * tcp connection to this port */
1661
      info->ss_proxy_port = (uint16_t)atoi(p);
1662
      break;
1663
1664
    case OPT_SSPROXY_IFACE:
1665
      /* UDS "proxy.ss.lws" in abstract namespace, else this socket
1666
       * path; when -p given this can specify the network interface
1667
       * to bind to */
1668
      info->ss_proxy_bind = p;
1669
      break;
1670
1671
    case OPT_SSPROXY_ADS:
1672
      info->ss_proxy_address = p;
1673
      break;
1674
#endif
1675
1676
0
    case OPT_FAULTINJECTION:
1677
0
#if !defined(LWS_WITH_SYS_FAULT_INJECTION)
1678
0
      lwsl_err("%s: FAULT_INJECTION not built\n", __func__);
1679
0
#endif
1680
0
      lws_fi_deserialize(&info->fic, p);
1681
0
      break;
1682
1683
0
    case OPT_FAULT_SEED:
1684
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
1685
      seed = (uint64_t)atoll(p);
1686
#endif
1687
0
      break;
1688
1689
0
    case OPT_IGNORE_SIGTERM:
1690
0
#if !defined(LWS_PLAT_FREERTOS)
1691
0
      signal(SIGTERM, lws_sigterm_catch);
1692
0
#endif
1693
0
      break;
1694
0
    }
1695
0
  }
1696
1697
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
1698
  lws_xos_init(&info->fic.xos, seed);
1699
#endif
1700
0
  lws_set_log_level(logs, NULL);
1701
1702
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
1703
  if (info->fic.fi_owner.count)
1704
    lwsl_notice("%s: Fault Injection seed %llu\n", __func__,
1705
        (unsigned long long)seed);
1706
#endif
1707
0
}
1708
1709
1710
const lws_humanize_unit_t humanize_schema_si[] = {
1711
  { "Pi", LWS_PI }, { "Ti", LWS_TI }, { "Gi", LWS_GI },
1712
  { "Mi", LWS_MI }, { "Ki", LWS_KI }, { "", 1 },
1713
  { NULL, 0 }
1714
};
1715
const lws_humanize_unit_t humanize_schema_si_bytes[] = {
1716
  { "PiB", LWS_PI }, { "TiB", LWS_TI }, { "GiB", LWS_GI },
1717
  { "MiB", LWS_MI }, { "KiB", LWS_KI }, { "B", 1 },
1718
  { NULL, 0 }
1719
};
1720
const lws_humanize_unit_t humanize_schema_us[] = {
1721
  { "y",  (uint64_t)365 * 24 * 3600 * LWS_US_PER_SEC },
1722
  { "d",  (uint64_t)24 * 3600 * LWS_US_PER_SEC },
1723
  { "hr", (uint64_t)3600 * LWS_US_PER_SEC },
1724
  { "min", 60 * LWS_US_PER_SEC },
1725
  { "s", LWS_US_PER_SEC },
1726
  { "ms", LWS_US_PER_MS },
1727
#if defined(WIN32)
1728
  { "us", 1 },
1729
#else
1730
  { "μs", 1 },
1731
#endif
1732
  { NULL, 0 }
1733
};
1734
1735
/* biggest ull is 18446744073709551615 (20 chars) */
1736
1737
static int
1738
decim(char *r, uint64_t v, char chars, char leading)
1739
0
{
1740
0
  uint64_t q = 1;
1741
0
  char *ro = r;
1742
0
  int n = 1;
1743
1744
0
  while ((leading || v > (q * 10) - 1) && n < 20 && n < chars) {
1745
0
    q = q * 10;
1746
0
    n++;
1747
0
  }
1748
1749
  /* n is how many chars needed */
1750
1751
0
  while (n--) {
1752
0
    *r++ = (char)('0' + (char)((v / q) % 10));
1753
0
    q = q / 10;
1754
0
  }
1755
1756
0
  *r = '\0';
1757
1758
0
  return lws_ptr_diff(r, ro);
1759
0
}
1760
1761
int
1762
lws_humanize(char *p, size_t len, uint64_t v, const lws_humanize_unit_t *schema)
1763
0
{
1764
0
  char *obuf = p, *end = p + len;
1765
1766
0
  do {
1767
0
    if (v >= schema->factor || schema->factor == 1) {
1768
0
      if (schema->factor == 1) {
1769
0
        p += decim(p, v, 4, 0);
1770
0
        p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
1771
0
                "%s", schema->name);
1772
0
        return lws_ptr_diff(p, obuf);
1773
0
      }
1774
1775
0
      p += decim(p, v / schema->factor, 4, 0);
1776
0
      *p++ = '.';
1777
0
      p += decim(p, (v % schema->factor) /
1778
0
          (schema->factor / 1000), 3, 1);
1779
1780
0
      p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
1781
0
              "%s", schema->name);
1782
0
      return lws_ptr_diff(p, obuf);
1783
0
    }
1784
0
    schema++;
1785
0
  } while (schema->name);
1786
1787
0
  assert(0);
1788
0
  strncpy(p, "unknown value", len);
1789
1790
0
  return 0;
1791
0
}
1792
1793
/*
1794
 * -1 = fail
1795
 *  0 = continue
1796
 *  1 = hit
1797
 */
1798
1799
0
#define LWS_MINILEX_FAIL_CODING 8
1800
1801
int
1802
lws_minilex_parse(const uint8_t *lex, int16_t *ps, const uint8_t c, int *match)
1803
0
{
1804
0
  if (*ps == (int16_t)-1)
1805
0
    return LWS_MINILEX_FAIL;
1806
1807
0
  while (1) {
1808
0
    if (lex[*ps] & (1 << 7)) {
1809
      /* 1-byte, fail on mismatch */
1810
0
      if ((lex[*ps] & 0x7f) != c)
1811
0
        goto nope;
1812
1813
      /* go forward */
1814
0
      if (lex[++(*ps)] == LWS_MINILEX_FAIL_CODING)
1815
0
        goto nope;
1816
1817
0
      if (lex[*ps] < LWS_MINILEX_FAIL_CODING) {
1818
        /* this is a terminal marker */
1819
0
        *match = (int)lex[++(*ps)];
1820
0
        return LWS_MINILEX_MATCH;
1821
0
      }
1822
1823
0
      return LWS_MINILEX_CONTINUE;
1824
0
    }
1825
1826
0
    if (lex[*ps] == LWS_MINILEX_FAIL_CODING)
1827
0
      goto nope;
1828
1829
    /* b7 = 0, end or 3-byte */
1830
0
    if (lex[*ps] < LWS_MINILEX_FAIL_CODING) {
1831
      /* this is a terminal marker */
1832
0
      *match = (int)lex[++(*ps)];
1833
0
      return LWS_MINILEX_MATCH;
1834
0
    }
1835
1836
0
    if (lex[*ps] == c) { /* goto-on-match */
1837
0
      *ps = (int16_t)(*ps + (lex[(*ps) + 1]) +
1838
0
                (lex[(*ps) + 2] << 8));
1839
0
      return LWS_MINILEX_CONTINUE;
1840
0
    }
1841
1842
    /* fall thru to next */
1843
0
    *ps = (int16_t)((*ps) + 3);
1844
0
  }
1845
1846
0
nope:
1847
0
  *ps = (int16_t)-1;
1848
1849
0
  return LWS_MINILEX_FAIL;
1850
0
}
1851
1852
unsigned int
1853
lws_sigbits(uintptr_t u)
1854
0
{
1855
0
  uintptr_t mask = (uintptr_t)(0xffllu << ((sizeof(u) - 1) * 8)),
1856
0
      m1   = (uintptr_t)(0x80llu << ((sizeof(u) - 1) * 8));
1857
0
  unsigned int n;
1858
1859
0
  for (n = sizeof(u) * 8; n > 0; n -= 8) {
1860
0
    if (u & mask)
1861
0
      break;
1862
0
    mask >>= 8;
1863
0
    m1 >>= 8;
1864
0
  }
1865
1866
0
  if (!n)
1867
0
    return 1; /* not bits are set, we need at least 1 to represent */
1868
1869
0
  while (!(u & m1)) {
1870
0
    n--;
1871
0
    m1 >>= 1;
1872
0
  }
1873
1874
0
  return n;
1875
0
}
1876
1877
const lws_fx_t *
1878
lws_fx_add(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b)
1879
0
{
1880
0
  int32_t w, sf;
1881
1882
0
  w = a->whole + b->whole;
1883
0
  sf = a->frac + b->frac;
1884
0
  if (sf >= 100000000) {
1885
0
    w++;
1886
0
    r->frac = sf - 100000000;
1887
0
  } else if (sf < -100000000) {
1888
0
    w--;
1889
0
    r->frac = sf + 100000000;
1890
0
  } else
1891
0
    r->frac = sf;
1892
1893
0
  r->whole = w;
1894
1895
0
  return r;
1896
0
}
1897
1898
const lws_fx_t *
1899
lws_fx_sub(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b)
1900
0
{
1901
0
  int32_t w;
1902
1903
0
  if (a->whole >= b->whole) {
1904
0
    w = a->whole - b->whole;
1905
0
    if (a->frac >= b->frac)
1906
0
      r->frac = a->frac - b->frac;
1907
0
    else {
1908
0
      w--;
1909
0
      r->frac = (100000000 + a->frac) - b->frac;
1910
0
    }
1911
0
  } else {
1912
0
    w = -(b->whole - a->whole);
1913
0
    if (b->frac >= a->frac)
1914
0
      r->frac = b->frac - a->frac;
1915
0
    else {
1916
0
      w++;
1917
0
      r->frac = (100000000 + b->frac) - a->frac;
1918
0
    }
1919
0
  }
1920
0
  r->whole = w;
1921
1922
0
  return r;
1923
0
}
1924
1925
const lws_fx_t *
1926
lws_fx_mul(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b)
1927
0
{
1928
0
  int64_t _c1, _c2;
1929
0
  int32_t w, t;
1930
0
  char neg = 0;
1931
1932
0
  assert(a->frac < LWS_FX_FRACTION_MSD);
1933
0
  assert(b->frac < LWS_FX_FRACTION_MSD);
1934
1935
  /* we can't use r as a temp, because it may alias on to a, b */
1936
1937
0
  w = a->whole * b->whole;
1938
1939
0
  if (!lws_neg(a) && !lws_neg(b)) {
1940
0
    _c2 = (((int64_t)((int64_t)a->frac) * (int64_t)b->frac) /
1941
0
              LWS_FX_FRACTION_MSD);
1942
0
    _c1 = ((int64_t)a->frac * ((int64_t)b->whole)) +
1943
0
            (((int64_t)a->whole) * (int64_t)b->frac) + _c2;
1944
0
    w += (int32_t)(_c1 / LWS_FX_FRACTION_MSD);
1945
0
  } else
1946
0
    if (lws_neg(a) && !lws_neg(b)) {
1947
0
      _c2 = (((int64_t)((int64_t)-a->frac) * (int64_t)b->frac) /
1948
0
                LWS_FX_FRACTION_MSD);
1949
0
      _c1 = ((int64_t)-a->frac * (-(int64_t)b->whole)) +
1950
0
             (((int64_t)a->whole) * (int64_t)b->frac) - _c2;
1951
0
      w += (int32_t)(_c1 / LWS_FX_FRACTION_MSD);
1952
0
      neg = 1;
1953
0
    } else
1954
0
      if (!lws_neg(a) && lws_neg(b)) {
1955
0
        _c2 = (((int64_t)((int64_t)a->frac) * (int64_t)-b->frac) /
1956
0
                  LWS_FX_FRACTION_MSD);
1957
0
        _c1 = ((int64_t)a->frac * ((int64_t)b->whole)) -
1958
0
               (((int64_t)a->whole) * (int64_t)-b->frac) - _c2;
1959
0
        w += (int32_t)(_c1 / LWS_FX_FRACTION_MSD);
1960
0
        neg = 1;
1961
0
      } else {
1962
0
        _c2 = (((int64_t)((int64_t)-a->frac) * (int64_t)-b->frac) /
1963
0
                  LWS_FX_FRACTION_MSD);
1964
0
        _c1 = ((int64_t)-a->frac * ((int64_t)b->whole)) +
1965
0
               (((int64_t)a->whole) * (int64_t)-b->frac) - _c2;
1966
0
        w -= (int32_t)(_c1 / LWS_FX_FRACTION_MSD);
1967
0
      }
1968
1969
0
  t = (int32_t)(_c1 % LWS_FX_FRACTION_MSD);
1970
0
  r->whole = w; /* don't need a,b any further... now we can write to r */
1971
0
  if (neg ^ !!(t < 0))
1972
0
    r->frac = -t;
1973
0
  else
1974
0
    r->frac = t;
1975
1976
0
  return r;
1977
0
}
1978
1979
const lws_fx_t *
1980
lws_fx_div(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b)
1981
0
{
1982
0
  int64_t _a = lws_fix64_abs(a), _b = lws_fix64_abs(b), q = 0, d, m;
1983
1984
0
  if (!_b)
1985
0
    _a = 0;
1986
0
  else {
1987
0
    int c = 64 / 2 + 1;
1988
1989
0
    while (_a && c >= 0) {
1990
0
      d = _a / _b;
1991
0
      m = (_a % _b);
1992
0
      if (m < 0)
1993
0
        m = -m;
1994
0
      _a = m << 1;
1995
0
      q += d << (c--);
1996
0
    }
1997
0
    _a = q >> 1;
1998
0
  }
1999
2000
0
  if (lws_neg(a) ^ lws_neg(b)) {
2001
0
    r->whole = -(int32_t)(_a >> 32);
2002
0
    r->frac = -(int32_t)((100000000 * (_a & 0xffffffff)) >> 32);
2003
0
  } else {
2004
0
    r->whole = (int32_t)(_a >> 32);
2005
0
    r->frac = (int32_t)((100000000 * (_a & 0xffffffff)) >> 32);
2006
0
  }
2007
2008
0
  return r;
2009
0
}
2010
2011
const lws_fx_t *
2012
lws_fx_sqrt(lws_fx_t *r, const lws_fx_t *a)
2013
0
{
2014
0
  uint64_t t, q = 0, b = 1ull << 62, v = ((uint64_t)a->whole << 32) +
2015
0
         (((uint64_t)a->frac << 32) / LWS_FX_FRACTION_MSD);
2016
2017
0
  while (b > 0x40) {
2018
0
    t = q + b;
2019
0
    if (v >= t) {
2020
0
      v -= t;
2021
0
      q = t + b;
2022
0
    }
2023
0
    v <<= 1;
2024
0
    b >>= 1;
2025
0
  }
2026
2027
0
  r->whole = (int32_t)(q >> 48);
2028
0
  r->frac = (int32_t)((((q >> 16) & 0xffffffff) *
2029
0
          LWS_FX_FRACTION_MSD) >> 32);
2030
2031
0
  return r;
2032
0
}
2033
2034
/* returns < 0 if a < b, >0 if a > b, or 0 if exactly equal */
2035
2036
int
2037
lws_fx_comp(const lws_fx_t *a, const lws_fx_t *b)
2038
0
{
2039
0
  if (a->whole < b->whole)
2040
0
    return -1;
2041
0
  if (a->whole > b->whole)
2042
0
                return 1;
2043
2044
0
  if (a->frac < b->frac)
2045
0
    return -1;
2046
2047
0
  if (a->frac > b->frac)
2048
0
    return 1;
2049
2050
0
  return 0;
2051
0
}
2052
2053
int
2054
lws_fx_roundup(const lws_fx_t *a)
2055
0
{
2056
0
  if (!a->frac)
2057
0
    return a->whole;
2058
2059
0
  if (lws_neg(a))
2060
0
    return a->whole - 1;
2061
2062
0
  return a->whole + 1;
2063
0
}
2064
2065
LWS_VISIBLE LWS_EXTERN int
2066
lws_fx_rounddown(const lws_fx_t *a)
2067
0
{
2068
0
  return a->whole;
2069
0
}
2070
2071
LWS_VISIBLE LWS_EXTERN const char *
2072
lws_fx_string(const lws_fx_t *a, char *buf, size_t size)
2073
0
{
2074
0
  int n, m = 7;
2075
2076
0
  if (lws_neg(a))
2077
0
    n = lws_snprintf(buf, size - 1, "-%d.%08d",
2078
0
         (int)(a->whole < 0 ? -a->whole : a->whole),
2079
0
         (int)(a->frac < 0 ? -a->frac : a->frac));
2080
0
  else
2081
0
    n = lws_snprintf(buf, size - 1, "%d.%08d", (int)a->whole,
2082
0
         (int)a->frac);
2083
2084
0
  while (m-- && buf[n - 1] == '0')
2085
0
    n--;
2086
2087
0
  buf[n] = '\0';
2088
2089
0
  return buf;
2090
0
}