Annotation of mandoc/mlg.c, Revision 1.14
1.14 ! kristaps 1: /* $Id: mlg.c,v 1.13 2008/12/06 13:18:44 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <assert.h>
20: #include <ctype.h>
21: #include <err.h>
1.13 kristaps 22: #include <stdarg.h>
1.1 kristaps 23: #include <stdlib.h>
24: #include <stdio.h>
25: #include <string.h>
26:
27: #include "libmdocml.h"
28: #include "private.h"
29: #include "ml.h"
30:
31: /* TODO: literal tokens. */
32:
33: #define COLUMNS 72
34: #define INDENT 4
1.2 kristaps 35: #define MAXINDENT 10
1.1 kristaps 36:
37: enum md_tok {
38: MD_TEXT,
39: MD_INLINE_IN,
40: MD_INLINE_OUT,
41: MD_BLK_IN,
42: MD_BLK_OUT,
43: };
44:
45: struct md_mlg {
46: const struct md_args *args;
47: const struct md_rbuf *rbuf;
48:
49: struct md_mbuf *mbuf;
50: struct rofftree *tree;
51: size_t indent;
52: size_t pos;
53: enum md_tok last;
54: void *arg;
1.11 kristaps 55: struct ml_cbs cbs;
1.1 kristaps 56: int flags;
57: #define ML_OVERRIDE_ONE (1 << 0)
58: #define ML_OVERRIDE_ALL (1 << 1)
1.10 kristaps 59: void *data;
1.1 kristaps 60: };
61:
62:
63: static void mlg_roffmsg(void *arg, enum roffmsg,
64: const char *, const char *, char *);
1.4 kristaps 65: static int mlg_roffhead(void *, const struct tm *,
66: const char *, const char *,
67: const char *, const char *);
1.1 kristaps 68: static int mlg_rofftail(void *);
69: static int mlg_roffin(void *, int, int *, char **);
1.5 kristaps 70: static int mlg_roffdata(void *, int,
71: const char *, char *);
1.1 kristaps 72: static int mlg_roffout(void *, int);
73: static int mlg_roffblkin(void *, int, int *, char **);
74: static int mlg_roffblkout(void *, int);
1.8 kristaps 75: static int mlg_roffspecial(void *, int,
76: const char *, char **);
1.5 kristaps 77: static int mlg_roffblkheadin(void *, int,
78: int *, char **);
1.2 kristaps 79: static int mlg_roffblkheadout(void *, int);
1.5 kristaps 80: static int mlg_roffblkbodyin(void *, int,
81: int *, char **);
1.2 kristaps 82: static int mlg_roffblkbodyout(void *, int);
83:
1.1 kristaps 84: static int mlg_begintag(struct md_mlg *, enum md_ns,
85: int, int *, char **);
86: static int mlg_endtag(struct md_mlg *, enum md_ns, int);
87: static int mlg_indent(struct md_mlg *);
88: static int mlg_newline(struct md_mlg *);
89: static void mlg_mode(struct md_mlg *, enum md_tok);
1.5 kristaps 90: static int mlg_data(struct md_mlg *, int,
91: const char *, char *);
92: static void mlg_err(struct md_mlg *, const char *,
1.13 kristaps 93: const char *, const char *, ...);
1.5 kristaps 94: static void mlg_warn(struct md_mlg *, const char *,
1.13 kristaps 95: const char *, const char *, ...);
1.5 kristaps 96: static void mlg_msg(struct md_mlg *, enum roffmsg,
97: const char *, const char *, char *);
1.13 kristaps 98: static void mlg_vmsg(struct md_mlg *, enum roffmsg,
99: const char *, const char *,
100: const char *, va_list);
1.1 kristaps 101:
102: #ifdef __linux__
103: extern size_t strlcat(char *, const char *, size_t);
104: extern size_t strlcpy(char *, const char *, size_t);
105: #endif
106:
107:
108: static int
109: mlg_begintag(struct md_mlg *p, enum md_ns ns, int tok,
110: int *argc, char **argv)
111: {
112: ssize_t res;
113:
1.8 kristaps 114: assert(MD_NS_DEFAULT != ns);
115:
116: switch (ns) {
117: case (MD_NS_INLINE):
118: if ( ! (ML_OVERRIDE_ONE & p->flags) &&
119: ! (ML_OVERRIDE_ALL & p->flags) &&
1.9 kristaps 120: p->pos + 11 >= COLUMNS)
1.8 kristaps 121: if ( ! mlg_newline(p))
122: return(0);
123: if (0 != p->pos && (MD_TEXT == p->last ||
124: MD_INLINE_OUT == p->last)
125: && ! (ML_OVERRIDE_ONE & p->flags)
126: && ! (ML_OVERRIDE_ALL & p->flags))
127: if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
128: return(0);
129: if (0 == p->pos && ! mlg_indent(p))
130: return(0);
131: mlg_mode(p, MD_INLINE_IN);
132: break;
133: default:
134: if (0 != p->pos) {
135: if ( ! mlg_newline(p))
136: return(0);
137: if ( ! mlg_indent(p))
138: return(0);
139: } else if ( ! mlg_indent(p))
140: return(0);
141: p->indent++;
142: mlg_mode(p, MD_BLK_IN);
143: break;
144: }
1.1 kristaps 145:
146: if ( ! ml_nputs(p->mbuf, "<", 1, &p->pos))
147: return(0);
148:
1.11 kristaps 149: res = (*p->cbs.ml_begintag)(p->mbuf, p->data, p->args, ns, tok,
1.1 kristaps 150: argc, (const char **)argv);
151: if (-1 == res)
152: return(0);
153:
154: assert(res >= 0);
155: p->pos += (size_t)res;
156:
1.8 kristaps 157: if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
158: return(0);
159:
160: switch (ns) {
161: case (MD_NS_INLINE):
162: break;
163: default:
164: if ( ! mlg_newline(p))
165: return(0);
166: break;
167: }
1.1 kristaps 168:
1.8 kristaps 169: return(1);
1.1 kristaps 170: }
171:
172:
173: static int
174: mlg_endtag(struct md_mlg *p, enum md_ns ns, int tok)
175: {
176: ssize_t res;
177:
1.9 kristaps 178: assert(MD_NS_DEFAULT != ns);
179:
180: switch (ns) {
181: case (MD_NS_INLINE):
182: break;
183: default:
184: p->indent--;
185: if (0 != p->pos) {
186: if ( ! mlg_newline(p))
187: return(0);
188: if ( ! mlg_indent(p))
189: return(0);
190: } else if ( ! mlg_indent(p))
191: return(0);
192: break;
193: }
1.1 kristaps 194:
195: if ( ! ml_nputs(p->mbuf, "</", 2, &p->pos))
196: return(0);
197:
1.11 kristaps 198: res = (*p->cbs.ml_endtag)(p->mbuf, p->data, p->args, ns, tok);
1.1 kristaps 199: if (-1 == res)
200: return(0);
201:
202: assert(res >= 0);
203: p->pos += (size_t)res;
204:
1.9 kristaps 205: if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
206: return(0);
207:
208: switch (ns) {
209: case (MD_NS_INLINE):
210: mlg_mode(p, MD_INLINE_OUT);
211: break;
212: default:
213: mlg_mode(p, MD_BLK_OUT);
214: break;
215: }
1.1 kristaps 216:
1.9 kristaps 217: return(1);
1.1 kristaps 218: }
219:
220:
221: static int
222: mlg_indent(struct md_mlg *p)
223: {
224: size_t count;
225:
1.9 kristaps 226: count = p->indent > MAXINDENT ?
227: (size_t)MAXINDENT : p->indent;
1.1 kristaps 228: count *= INDENT;
229:
230: assert(0 == p->pos);
231: return(ml_putchars(p->mbuf, ' ', count, &p->pos));
232: }
233:
234:
235: static int
236: mlg_newline(struct md_mlg *p)
237: {
238:
239: p->pos = 0;
1.11 kristaps 240: return(ml_nputs(p->mbuf, "\n", 1, NULL));
1.1 kristaps 241: }
242:
243:
244: static void
245: mlg_mode(struct md_mlg *p, enum md_tok ns)
246: {
1.3 kristaps 247:
1.1 kristaps 248: p->flags &= ~ML_OVERRIDE_ONE;
249: p->last = ns;
250: }
251:
252:
253: static int
1.5 kristaps 254: mlg_data(struct md_mlg *p, int space, const char *start, char *buf)
1.1 kristaps 255: {
256: size_t sz;
1.5 kristaps 257: int c;
1.1 kristaps 258:
259: assert(p->mbuf);
260: assert(0 != p->indent);
261:
262: if (ML_OVERRIDE_ONE & p->flags ||
263: ML_OVERRIDE_ALL & p->flags)
264: space = 0;
265:
1.8 kristaps 266: sz = strlen(buf);
1.1 kristaps 267:
1.8 kristaps 268: if (0 == p->pos) {
269: if ( ! mlg_indent(p))
270: return(0);
1.1 kristaps 271:
1.8 kristaps 272: c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
1.1 kristaps 273:
1.8 kristaps 274: if (0 == c) {
275: mlg_err(p, start, buf, "bad char sequence");
276: return(0);
277: } else if (c > 1) {
278: mlg_warn(p, start, buf, "bogus char sequence");
279: return(0);
280: } else if (-1 == c)
281: return(0);
1.1 kristaps 282:
1.8 kristaps 283: if (p->indent * INDENT + sz >= COLUMNS)
284: if ( ! mlg_newline(p))
1.1 kristaps 285: return(0);
1.5 kristaps 286:
1.8 kristaps 287: return(1);
288: }
1.5 kristaps 289:
1.8 kristaps 290: if (space && sz + p->pos >= COLUMNS) {
291: if ( ! mlg_newline(p))
1.5 kristaps 292: return(0);
1.8 kristaps 293: if ( ! mlg_indent(p))
1.5 kristaps 294: return(0);
1.8 kristaps 295: } else if (space) {
296: if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
1.1 kristaps 297: return(0);
1.8 kristaps 298: }
1.1 kristaps 299:
1.8 kristaps 300: c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
301:
302: if (0 == c) {
303: mlg_err(p, start, buf, "bad char sequence");
304: return(0);
305: } else if (c > 1) {
306: mlg_warn(p, start, buf, "bogus char sequence");
307: return(0);
308: } else if (-1 == c)
309: return(0);
1.1 kristaps 310:
311: return(1);
312: }
313:
314:
315: int
316: mlg_line(struct md_mlg *p, char *buf)
317: {
318:
319: return(roff_engine(p->tree, buf));
320: }
321:
322:
323: int
324: mlg_exit(struct md_mlg *p, int flush)
325: {
326: int c;
327:
328: c = roff_free(p->tree, flush);
329: free(p);
1.11 kristaps 330:
331: (*p->cbs.ml_free)(p->data);
332:
1.1 kristaps 333: return(c);
334: }
335:
336:
337: struct md_mlg *
1.11 kristaps 338: mlg_alloc(const struct md_args *args,
1.1 kristaps 339: const struct md_rbuf *rbuf,
340: struct md_mbuf *mbuf,
1.11 kristaps 341: const struct ml_cbs *cbs)
1.1 kristaps 342: {
343: struct roffcb cb;
344: struct md_mlg *p;
345:
346: cb.roffhead = mlg_roffhead;
347: cb.rofftail = mlg_rofftail;
348: cb.roffin = mlg_roffin;
349: cb.roffout = mlg_roffout;
350: cb.roffblkin = mlg_roffblkin;
1.2 kristaps 351: cb.roffblkheadin = mlg_roffblkheadin;
352: cb.roffblkheadout = mlg_roffblkheadout;
353: cb.roffblkbodyin = mlg_roffblkbodyin;
354: cb.roffblkbodyout = mlg_roffblkbodyout;
1.1 kristaps 355: cb.roffblkout = mlg_roffblkout;
356: cb.roffspecial = mlg_roffspecial;
357: cb.roffmsg = mlg_roffmsg;
358: cb.roffdata = mlg_roffdata;
359:
360: if (NULL == (p = calloc(1, sizeof(struct md_mlg))))
361: err(1, "calloc");
362:
363: p->args = args;
364: p->mbuf = mbuf;
365: p->rbuf = rbuf;
366:
1.11 kristaps 367: (void)memcpy(&p->cbs, cbs, sizeof(struct ml_cbs));
368:
369: if (NULL == (p->tree = roff_alloc(&cb, p)))
370: free(p);
371: else if ( ! (*p->cbs.ml_alloc)(&p->data))
1.1 kristaps 372: free(p);
1.11 kristaps 373: else
374: return(p);
1.1 kristaps 375:
1.11 kristaps 376: return(NULL);
1.1 kristaps 377: }
378:
379:
380: static int
1.4 kristaps 381: mlg_roffhead(void *arg, const struct tm *tm, const char *os,
382: const char *title, const char *sec, const char *vol)
1.1 kristaps 383: {
384: struct md_mlg *p;
385:
386: assert(arg);
387: p = (struct md_mlg *)arg;
388:
389: mlg_mode(p, MD_BLK_IN);
1.11 kristaps 390:
391: if ( ! (*p->cbs.ml_begin)(p->mbuf, p->args, tm, os, title, sec, vol))
1.1 kristaps 392: return(0);
393:
394: p->indent++;
395: return(mlg_newline(p));
396: }
397:
398:
399: static int
400: mlg_rofftail(void *arg)
401: {
402: struct md_mlg *p;
403:
404: assert(arg);
405: p = (struct md_mlg *)arg;
406:
1.11 kristaps 407: if (0 != p->pos)
408: if ( ! mlg_newline(p))
409: return(0);
410:
411: if ( ! (*p->cbs.ml_end)(p->mbuf, p->args))
1.1 kristaps 412: return(0);
413:
414: mlg_mode(p, MD_BLK_OUT);
415:
416: return(mlg_newline(p));
417: }
418:
419:
420: static int
1.8 kristaps 421: mlg_roffspecial(void *arg, int tok, const char *start, char **more)
1.1 kristaps 422: {
423: struct md_mlg *p;
424:
425: assert(arg);
426: p = (struct md_mlg *)arg;
427:
428: switch (tok) {
1.14 ! kristaps 429: case (ROFF_Bt):
! 430: assert(NULL == *more);
! 431: if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
! 432: return(0);
! 433: if ( ! ml_puts(p->mbuf, "is currently in beta "
! 434: "test.", &p->pos))
! 435: return(0);
! 436: if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
! 437: return(0);
! 438: break;
! 439:
1.8 kristaps 440: case (ROFF_Xr):
441: if ( ! *more) {
1.9 kristaps 442: mlg_err(p, start, start, "missing argument");
1.8 kristaps 443: return(0);
444: }
445: if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
446: return(0);
447: if ( ! ml_puts(p->mbuf, *more++, &p->pos))
448: return(0);
449: if (*more) {
450: if ( ! ml_nputs(p->mbuf, "(", 1, &p->pos))
451: return(0);
1.9 kristaps 452: if ( ! ml_puts(p->mbuf, *more++, &p->pos))
1.8 kristaps 453: return(0);
454: if ( ! ml_nputs(p->mbuf, ")", 1, &p->pos))
455: return(0);
456: }
457: if (*more) {
458: mlg_err(p, start, start, "too many arguments");
459: return(0);
460: }
461: if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
462: return(0);
463: break;
1.13 kristaps 464:
1.12 kristaps 465: case (ROFF_Nm):
466: assert(*more);
467: if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
468: return(0);
469: if ( ! ml_puts(p->mbuf, *more++, &p->pos))
470: return(0);
471: assert(NULL == *more);
472: if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
473: return(0);
1.8 kristaps 474: break;
1.13 kristaps 475:
1.1 kristaps 476: case (ROFF_Ns):
477: p->flags |= ML_OVERRIDE_ONE;
478: break;
1.13 kristaps 479:
1.1 kristaps 480: case (ROFF_Sm):
481: assert(*more);
482: if (0 == strcmp(*more, "on"))
483: p->flags |= ML_OVERRIDE_ALL;
484: else
485: p->flags &= ~ML_OVERRIDE_ALL;
486: break;
1.13 kristaps 487:
488: case (ROFF_Ex):
489: assert(*more);
490: if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
491: return(0);
492: if ( ! ml_puts(p->mbuf, "The ", &p->pos))
493: return(0);
494: if ( ! mlg_begintag(p, MD_NS_INLINE, ROFF_Xr, NULL, NULL))
495: return(0);
496: if ( ! ml_puts(p->mbuf, *more++, &p->pos))
497: return(0);
498: if ( ! mlg_endtag(p, MD_NS_INLINE, ROFF_Xr))
499: return(0);
500: if ( ! ml_puts(p->mbuf, " utility exits 0 on success, "
501: "and >0 if an error "
502: "occurs.", &p->pos))
503: return(0);
504: assert(NULL == *more);
505: if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
506: return(0);
507: break;
508:
1.14 ! kristaps 509: case (ROFF_Ud):
! 510: assert(NULL == *more);
! 511: if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
! 512: return(0);
! 513: if ( ! ml_puts(p->mbuf, "currently under "
! 514: "development.", &p->pos))
! 515: return(0);
! 516: if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
! 517: return(0);
! 518: break;
! 519:
1.1 kristaps 520: default:
1.13 kristaps 521: mlg_err(p, start, start, "`%s' not yet supported",
522: toknames[tok]);
523: return(0);
1.1 kristaps 524: }
525:
526: return(1);
527: }
528:
529:
530: static int
531: mlg_roffblkin(void *arg, int tok, int *argc, char **argv)
532: {
533:
1.8 kristaps 534: return(mlg_begintag((struct md_mlg *)arg,
1.2 kristaps 535: MD_NS_BLOCK, tok, argc, argv));
536: }
537:
538:
539: static int
540: mlg_roffblkout(void *arg, int tok)
541: {
542:
1.9 kristaps 543: return(mlg_endtag((struct md_mlg *)arg, MD_NS_BLOCK, tok));
1.2 kristaps 544: }
545:
546:
547: static int
548: mlg_roffblkbodyin(void *arg, int tok, int *argc, char **argv)
549: {
550:
1.8 kristaps 551: return(mlg_begintag((struct md_mlg *)arg,
1.2 kristaps 552: MD_NS_BODY, tok, argc, argv));
553: }
1.1 kristaps 554:
555:
1.2 kristaps 556: static int
557: mlg_roffblkbodyout(void *arg, int tok)
558: {
1.1 kristaps 559:
1.9 kristaps 560: return(mlg_endtag((struct md_mlg *)arg, MD_NS_BODY, tok));
1.1 kristaps 561: }
562:
563:
564: static int
1.2 kristaps 565: mlg_roffblkheadin(void *arg, int tok, int *argc, char **argv)
1.1 kristaps 566: {
567:
1.8 kristaps 568: return(mlg_begintag((struct md_mlg *)arg,
1.2 kristaps 569: MD_NS_HEAD, tok, argc, argv));
570: }
1.1 kristaps 571:
572:
1.2 kristaps 573: static int
574: mlg_roffblkheadout(void *arg, int tok)
575: {
1.1 kristaps 576:
1.9 kristaps 577: return(mlg_endtag((struct md_mlg *)arg, MD_NS_HEAD, tok));
1.1 kristaps 578: }
579:
580:
581: static int
582: mlg_roffin(void *arg, int tok, int *argc, char **argv)
583: {
584:
1.8 kristaps 585: return(mlg_begintag((struct md_mlg *)arg,
586: MD_NS_INLINE, tok, argc, argv));
1.1 kristaps 587: }
588:
589:
590: static int
591: mlg_roffout(void *arg, int tok)
592: {
593:
1.9 kristaps 594: return(mlg_endtag((struct md_mlg *)arg, MD_NS_INLINE, tok));
1.1 kristaps 595: }
596:
597:
598: static void
599: mlg_roffmsg(void *arg, enum roffmsg lvl,
600: const char *buf, const char *pos, char *msg)
601: {
1.5 kristaps 602:
603: mlg_msg((struct md_mlg *)arg, lvl, buf, pos, msg);
604: }
605:
606:
607: static int
608: mlg_roffdata(void *arg, int space, const char *start, char *buf)
609: {
1.1 kristaps 610: struct md_mlg *p;
611:
612: assert(arg);
613: p = (struct md_mlg *)arg;
614:
1.5 kristaps 615: if ( ! mlg_data(p, space, start, buf))
616: return(0);
617:
618: mlg_mode(p, MD_TEXT);
1.11 kristaps 619:
1.5 kristaps 620: return(1);
621: }
622:
623:
624: static void
1.13 kristaps 625: mlg_vmsg(struct md_mlg *p, enum roffmsg lvl, const char *start,
626: const char *pos, const char *fmt, va_list ap)
1.5 kristaps 627: {
1.13 kristaps 628: char buf[128];
1.5 kristaps 629:
1.13 kristaps 630: (void)vsnprintf(buf, sizeof(buf), fmt, ap);
631: mlg_msg(p, lvl, start, pos, buf);
1.5 kristaps 632: }
633:
634:
635: static void
1.13 kristaps 636: mlg_warn(struct md_mlg *p, const char *start,
637: const char *pos, const char *fmt, ...)
1.5 kristaps 638: {
1.13 kristaps 639: va_list ap;
1.5 kristaps 640:
1.13 kristaps 641: va_start(ap, fmt);
642: mlg_vmsg(p, ROFF_WARN, start, pos, fmt, ap);
643: va_end(ap);
644: }
645:
646:
647: static void
648: mlg_err(struct md_mlg *p, const char *start,
649: const char *pos, const char *fmt, ...)
650: {
651: va_list ap;
652:
653: va_start(ap, fmt);
654: mlg_vmsg(p, ROFF_ERROR, start, pos, fmt, ap);
655: va_end(ap);
1.5 kristaps 656: }
657:
658:
659: static void
660: mlg_msg(struct md_mlg *p, enum roffmsg lvl,
661: const char *buf, const char *pos, char *msg)
662: {
663: char *level;
664:
1.1 kristaps 665: switch (lvl) {
666: case (ROFF_WARN):
667: if ( ! (MD_WARN_ALL & p->args->warnings))
668: return;
669: level = "warning";
670: break;
671: case (ROFF_ERROR):
672: level = "error";
673: break;
674: default:
675: abort();
676: }
677:
678: if (pos)
679: (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
680: p->rbuf->name, p->rbuf->line, level,
681: msg, pos - buf);
682: else
683: (void)fprintf(stderr, "%s: %s: %s\n",
684: p->rbuf->name, level, msg);
685: }
CVSweb