Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libast/misc/mime.c
1810 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1985-2011 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* David Korn <[email protected]> *
19
* Phong Vo <[email protected]> *
20
* *
21
***********************************************************************/
22
#pragma prototyped
23
24
/*
25
* Glenn Fowler
26
* AT&T Research
27
*
28
* mime/mailcap support library
29
*/
30
31
static const char id[] = "\n@(#)$Id: mime library (AT&T Research) 2002-10-29 $\0\n";
32
33
static const char lib[] = "libast:mime";
34
35
#include "mimelib.h"
36
37
typedef struct Att_s
38
{
39
struct Att_s* next;
40
char* name;
41
char* value;
42
} Att_t;
43
44
typedef struct Cap_s
45
{
46
struct Cap_s* next;
47
unsigned long flags;
48
Att_t att;
49
char* test;
50
char data[1];
51
} Cap_t;
52
53
typedef struct
54
{
55
Dtlink_t link;
56
Cap_t* cap;
57
Cap_t* pac;
58
char name[1];
59
} Ent_t;
60
61
typedef struct
62
{
63
char* data;
64
int size;
65
} String_t;
66
67
typedef struct
68
{
69
char* next;
70
String_t name;
71
String_t value;
72
} Parse_t;
73
74
typedef struct
75
{
76
const char* pattern;
77
int prefix;
78
Sfio_t* fp;
79
int hit;
80
} Walk_t;
81
82
/*
83
* convert c to lower case
84
*/
85
86
static int
87
lower(register int c)
88
{
89
return isupper(c) ? tolower(c) : c;
90
}
91
92
/*
93
* Ent_t case insensitive comparf
94
*/
95
96
static int
97
order(Dt_t* dt, void* a, void* b, Dtdisc_t* disc)
98
{
99
return strcasecmp(a, b);
100
}
101
102
/*
103
* Cap_t free
104
*/
105
106
static void
107
dropcap(register Cap_t* cap)
108
{
109
register Att_t* att;
110
111
while (att = cap->att.next)
112
{
113
cap->att.next = att->next;
114
free(att);
115
}
116
free(cap);
117
}
118
119
/*
120
* Ent_t freef
121
*/
122
123
static void
124
drop(Dt_t* dt, void* object, Dtdisc_t* disc)
125
{
126
register Ent_t* ent = (Ent_t*)object;
127
register Cap_t* cap;
128
129
while (cap = ent->cap)
130
{
131
ent->cap = cap->next;
132
dropcap(cap);
133
}
134
free(ent);
135
}
136
137
/*
138
* add mime type entry in s to mp
139
*/
140
141
int
142
mimeset(Mime_t* mp, register char* s, unsigned long flags)
143
{
144
register Ent_t* ent;
145
register Cap_t* cap;
146
register Att_t* att;
147
register char* t;
148
register char* v;
149
register char* k;
150
char* x;
151
Att_t* tta;
152
int q;
153
154
for (; isspace(*s); s++);
155
if (*s && *s != '#')
156
{
157
cap = 0;
158
for (v = s; *v && *v != ';'; v++)
159
if (isspace(*v) || *v == '/' && *(v + 1) == '*')
160
*v = 0;
161
if (*v)
162
{
163
*v++ = 0;
164
do
165
{
166
for (; isspace(*v); v++);
167
if (cap)
168
{
169
for (t = v; *t && !isspace(*t) && *t != '='; t++);
170
for (k = t; isspace(*t); t++);
171
if (!*t || *t == '=' || *t == ';')
172
{
173
if (*t)
174
while (isspace(*++t));
175
*k = 0;
176
k = v;
177
v = t;
178
}
179
else
180
k = 0;
181
}
182
if (*v == '"')
183
q = *v++;
184
else
185
q = 0;
186
for (t = v; *t; t++)
187
if (*t == '\\')
188
{
189
switch (*(t + 1))
190
{
191
case 0:
192
case '\\':
193
case '%':
194
*t = *(t + 1);
195
break;
196
default:
197
*t = ' ';
198
break;
199
}
200
if (!*++t)
201
break;
202
}
203
else if (*t == q)
204
{
205
*t = ' ';
206
q = 0;
207
}
208
else if (*t == ';' && !q)
209
{
210
*t = ' ';
211
break;
212
}
213
for (; t > v && isspace(*(t - 1)); t--);
214
if (t <= v && (!cap || !k))
215
break;
216
if (!cap)
217
{
218
if (!(cap = newof(0, Cap_t, 1, strlen(v) + 1)))
219
return -1;
220
if (*t)
221
*t++ = 0;
222
tta = &cap->att;
223
tta->name = "default";
224
x = strcopy(tta->value = cap->data, v) + 1;
225
}
226
else if (k)
227
{
228
if (*t)
229
*t++ = 0;
230
if (!(att = newof(0, Att_t, 1, 0)))
231
return -1;
232
x = strcopy(att->name = x, k) + 1;
233
x = strcopy(att->value = x, v) + 1;
234
tta = tta->next = att;
235
if (!strcasecmp(k, "test"))
236
cap->test = att->value;
237
}
238
} while (*(v = t));
239
}
240
ent = (Ent_t*)dtmatch(mp->cap, s);
241
if (cap)
242
{
243
if (ent)
244
{
245
register Cap_t* dup;
246
register Cap_t* pud;
247
248
for (pud = 0, dup = ent->cap; dup; pud = dup, dup = dup->next)
249
if (!cap->test && !dup->test || cap->test && dup->test && streq(cap->test, dup->test))
250
{
251
if (flags & MIME_REPLACE)
252
{
253
if (pud)
254
pud->next = cap;
255
else
256
ent->cap = cap;
257
if (!(cap->next = dup->next))
258
ent->pac = cap;
259
cap = dup;
260
}
261
dropcap(cap);
262
return 0;
263
}
264
ent->pac = ent->pac->next = cap;
265
}
266
else if (!(ent = newof(0, Ent_t, 1, strlen(s) + 1)))
267
return -1;
268
else
269
{
270
strcpy(ent->name, s);
271
ent->cap = ent->pac = cap;
272
dtinsert(mp->cap, ent);
273
}
274
}
275
else if (ent && (flags & MIME_REPLACE))
276
dtdelete(mp->cap, ent);
277
}
278
return 0;
279
}
280
281
/*
282
* load mime type files into mp
283
*/
284
285
int
286
mimeload(Mime_t* mp, const char* file, unsigned long flags)
287
{
288
register char* s;
289
register char* t;
290
register char* e;
291
register int n;
292
Sfio_t* fp;
293
294
if (!(s = (char*)file))
295
{
296
flags |= MIME_LIST;
297
if (!(s = getenv(MIME_FILES_ENV)))
298
s = MIME_FILES;
299
}
300
for (;;)
301
{
302
if (!(flags & MIME_LIST))
303
e = 0;
304
else if (e = strchr(s, ':'))
305
{
306
/*
307
* ok, so ~ won't work for the last list element
308
* we do it for MIME_FILES_ENV anyway
309
*/
310
311
if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME")))
312
{
313
sfputr(mp->buf, t, -1);
314
s += n - 1;
315
}
316
sfwrite(mp->buf, s, e - s);
317
if (!(s = sfstruse(mp->buf)))
318
return -1;
319
}
320
if (fp = tokline(s, SF_READ, NiL))
321
{
322
while (t = sfgetr(fp, '\n', 1))
323
if (mimeset(mp, t, flags))
324
break;
325
sfclose(fp);
326
}
327
else if (!(flags & MIME_LIST))
328
return -1;
329
if (!e)
330
break;
331
s = e + 1;
332
}
333
return 0;
334
}
335
336
/*
337
* mimelist walker
338
*/
339
340
static int
341
list(Dt_t* dt, void* object, void* context)
342
{
343
register Walk_t* wp = (Walk_t*)context;
344
register Ent_t* ent = (Ent_t*)object;
345
register Cap_t* cap;
346
register Att_t* att;
347
348
if (!wp->pattern || !strncasecmp(ent->name, wp->pattern, wp->prefix) && (!ent->name[wp->prefix] || ent->name[wp->prefix] == '/'))
349
{
350
wp->hit++;
351
for (cap = ent->cap; cap; cap = cap->next)
352
{
353
sfprintf(wp->fp, "%s", ent->name);
354
for (att = &cap->att; att; att = att->next)
355
{
356
sfprintf(wp->fp, "\n\t");
357
if (att != &cap->att)
358
{
359
sfprintf(wp->fp, "%s", att->name);
360
if (*att->value)
361
sfprintf(wp->fp, " = ");
362
}
363
sfputr(wp->fp, att->value, -1);
364
}
365
sfprintf(wp->fp, "\n");
366
}
367
}
368
return 0;
369
}
370
371
/*
372
* find entry matching type
373
* if exact match fails then left and right x- and right version number
374
* permutations are attempted
375
*/
376
377
static Ent_t*
378
find(Mime_t* mp, const char* type)
379
{
380
register char* lp;
381
register char* rp;
382
register char* rb;
383
register char* rv;
384
register int rc;
385
register int i;
386
char* s;
387
Ent_t* ent;
388
char buf[256];
389
390
static const char* prefix[] = { "", "", "x-", "x-", "" };
391
392
if ((ent = (Ent_t*)dtmatch(mp->cap, type)) ||
393
!(rp = strchr(lp = (char*)type, '/')) ||
394
strlen(lp) >= sizeof(buf))
395
return ent;
396
strcpy(buf, type);
397
rp = buf + (rp - lp);
398
*rp++ = 0;
399
if (*rp == 'x' && *(rp + 1) == '-')
400
rp += 2;
401
lp = buf;
402
if (*lp == 'x' && *(lp + 1) == '-')
403
lp += 2;
404
rb = rp;
405
for (rv = rp + strlen(rp); rv > rp && (isdigit(*(rv - 1)) || *(rv - 1) == '.'); rv--);
406
rc = *rv;
407
do
408
{
409
rp = rb;
410
do
411
{
412
for (i = 0; i < elementsof(prefix) - 1; i++)
413
{
414
sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp);
415
if (!(s = sfstruse(mp->buf)))
416
return 0;
417
if (ent = (Ent_t*)dtmatch(mp->cap, s))
418
return ent;
419
if (rc)
420
{
421
*rv = 0;
422
sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp);
423
if (!(s = sfstruse(mp->buf)))
424
return 0;
425
if (ent = (Ent_t*)dtmatch(mp->cap, s))
426
return ent;
427
*rv = rc;
428
}
429
}
430
while (*rp && *rp++ != '-');
431
} while (*rp);
432
while (*lp && *lp++ != '-');
433
} while (*lp);
434
return (Ent_t*)dtmatch(mp->cap, buf);
435
}
436
437
/*
438
* list mime <type,data> for pat on fp
439
*/
440
441
int
442
mimelist(Mime_t* mp, Sfio_t* fp, register const char* pattern)
443
{
444
Ent_t* ent;
445
Walk_t ws;
446
447
ws.fp = fp;
448
ws.hit = 0;
449
ws.prefix = 0;
450
if (ws.pattern = pattern)
451
{
452
while (*pattern && *pattern++ != '/');
453
if (!*pattern || *pattern == '*' && !*(pattern + 1))
454
ws.prefix = pattern - ws.pattern;
455
else if (ent = find(mp, ws.pattern))
456
{
457
ws.pattern = 0;
458
list(mp->cap, ent, &ws);
459
return ws.hit;
460
}
461
}
462
dtwalk(mp->cap, list, &ws);
463
return ws.hit;
464
}
465
466
/*
467
* get next arg in pp
468
* 0 returned if no more args
469
*/
470
471
static int
472
arg(register Parse_t* pp, int first)
473
{
474
register char* s;
475
register int c;
476
register int q;
477
int x;
478
479
for (s = pp->next; isspace(*s) && *s != '\n'; s++);
480
if (!*s || *s == '\n')
481
{
482
pp->next = s;
483
return 0;
484
}
485
pp->name.data = s;
486
pp->value.data = 0;
487
q = 0;
488
x = 0;
489
while ((c = *s++) && c != ';' && c != '\n')
490
{
491
if (c == '"')
492
{
493
q = 1;
494
if (pp->value.data)
495
{
496
pp->value.data = s;
497
if (x)
498
x = -1;
499
else
500
x = 1;
501
}
502
else if (!x && pp->name.data == (s - 1))
503
{
504
x = 1;
505
pp->name.data = s;
506
}
507
do
508
{
509
if (!(c = *s++) || c == '\n')
510
{
511
s--;
512
break;
513
}
514
} while (c != '"');
515
if (first < 0 || x > 0)
516
{
517
c = ';';
518
break;
519
}
520
}
521
else if (c == '=' && !first)
522
{
523
first = 1;
524
pp->name.size = s - pp->name.data - 1;
525
pp->value.data = s;
526
}
527
else if (first >= 0 && isspace(c))
528
break;
529
}
530
pp->next = s - (c != ';');
531
if (first >= 0 || !q)
532
for (s--; s > pp->name.data && isspace(*(s - 1)); s--);
533
if (pp->value.data)
534
pp->value.size = s - pp->value.data - (q && first < 0);
535
else
536
{
537
pp->value.size = 0;
538
pp->name.size = s - pp->name.data - (q && first < 0);
539
}
540
if (first >= 0 && pp->name.size > 0 && pp->name.data[pp->name.size - 1] == ':')
541
return 0;
542
return pp->name.size > 0;
543
}
544
545
/*
546
* low level for mimeview()
547
*/
548
549
static char*
550
expand(Mime_t* mp, register char* s, const char* name, const char* type, const char* opts)
551
{
552
register char* t;
553
register char* v;
554
register int c;
555
register int e;
556
register int n;
557
Parse_t pp;
558
559
mp->disc->flags |= MIME_PIPE;
560
for (;;)
561
{
562
switch (c = *s++)
563
{
564
case 0:
565
case '\n':
566
break;
567
case '%':
568
if ((c = *s++) == '{' && (e = '}') || c == '(' && (e = ')'))
569
{
570
for (t = s; *s && *s != e; s++);
571
n = s - t;
572
switch (*s)
573
{
574
case '}':
575
s++;
576
c = '{';
577
break;
578
case ')':
579
s++;
580
if (c = *s)
581
s++;
582
break;
583
}
584
}
585
else
586
t = 0;
587
switch (c)
588
{
589
case 's':
590
v = (char*)name;
591
mp->disc->flags &= ~MIME_PIPE;
592
break;
593
case 't':
594
v = (char*)type;
595
break;
596
case '{':
597
for (t = s; *s && *s != '}'; s++);
598
if (*s && (c = s++ - t) && (pp.next = (char*)opts))
599
while (arg(&pp, 0))
600
if (pp.name.size == c && !strncasecmp(pp.name.data, t, c))
601
{
602
if (pp.value.size)
603
sfwrite(mp->buf, pp.value.data, pp.value.size);
604
break;
605
}
606
continue;
607
default:
608
sfputc(mp->buf, c);
609
continue;
610
}
611
if (v && *v)
612
n = strlen(v);
613
else if (t)
614
v = t;
615
else
616
continue;
617
sfputr(mp->buf, fmtquote(v, 0, 0, n, FMT_SHELL), -1);
618
continue;
619
default:
620
sfputc(mp->buf, c);
621
continue;
622
}
623
break;
624
}
625
return sfstruse(mp->buf);
626
}
627
628
/*
629
* return expanded command/path/value for <view,name,type,opts>
630
* return value valid until next mime*() call
631
*/
632
633
char*
634
mimeview(Mime_t* mp, const char* view, const char* name, const char* type, const char* opts)
635
{
636
register Ent_t* ent;
637
register Cap_t* cap;
638
register Att_t* att;
639
register char* s;
640
int c;
641
642
if (ent = find(mp, type))
643
{
644
cap = ent->cap;
645
if (!view || strcasecmp(view, "test"))
646
while (s = cap->test)
647
{
648
if (s = expand(mp, s, name, type, opts))
649
{
650
Parse_t a1;
651
Parse_t a2;
652
Parse_t a3;
653
Parse_t a4;
654
655
/*
656
* try to do a few common cases here
657
* mailcap consistency is a winning
658
* strategy
659
*/
660
661
a1.next = s;
662
if (arg(&a1, -1))
663
{
664
if ((c = *a1.name.data == '!') && --a1.name.size <= 0 && !arg(&a1, -1))
665
goto lose;
666
if (a1.name.size == 6 && strneq(a1.name.data, "strcmp", 6) || a1.name.size == 10 && strneq(a1.name.data, "strcasecmp", 10))
667
{
668
a2.next = a1.next;
669
if (!arg(&a2, -1))
670
goto lose;
671
a3.next = a2.next;
672
if (!arg(&a3, -1))
673
goto lose;
674
if (a2.name.size != a3.name.size)
675
c ^= 0;
676
else c ^= (a1.name.size == 6 ? strncmp : strncasecmp)(a2.name.data, a3.name.data, a2.name.size) == 0;
677
if (c)
678
break;
679
goto skip;
680
}
681
else if (a1.name.size == 4 && strneq(a1.name.data, "test", 4))
682
{
683
if (!arg(&a1, -1))
684
goto lose;
685
a2.next = a1.next;
686
if (!arg(&a2, -1) || a2.name.size > 2 || a2.name.size == 1 && *a2.name.data != '=' || a2.name.size == 2 && (!strneq(a1.name.data, "!=", 2) || !strneq(a2.name.data, "==", 2)))
687
goto lose;
688
a3.next = a2.next;
689
if (!arg(&a3, -1))
690
goto lose;
691
if (*a3.name.data == '`' && *(a3.name.data + a3.name.size - 1) == '`')
692
{
693
a4 = a3;
694
a3 = a1;
695
a1 = a4;
696
}
697
if (*a1.name.data == '`' && *(a1.name.data + a1.name.size - 1) == '`')
698
{
699
a1.next = a1.name.data + 1;
700
if (!arg(&a1, -1) || a1.name.size != 4 || !strneq(a1.name.data, "echo", 4) || !arg(&a1, -1))
701
goto lose;
702
a4.next = a1.next;
703
if (!arg(&a4, 1) || a4.name.size < 21 || !strneq(a4.name.data, "| tr '[A-Z]' '[a-z]'`", 21))
704
goto lose;
705
}
706
else
707
a4.name.size = 0;
708
c = *a2.name.data == '!';
709
if (a1.name.size != a3.name.size)
710
c ^= 0;
711
else c ^= (a4.name.size ? strncasecmp : strncmp)(a1.name.data, a3.name.data, a1.name.size) == 0;
712
if (c)
713
break;
714
goto skip;
715
}
716
}
717
lose:
718
if (!system(s))
719
break;
720
}
721
skip:
722
if (!(cap = cap->next))
723
return 0;
724
}
725
att = &cap->att;
726
if (view && *view && !streq(view, "-"))
727
while (strcasecmp(view, att->name))
728
if (!(att = att->next))
729
return 0;
730
return expand(mp, att->value, name, type, opts);
731
}
732
return 0;
733
}
734
735
/*
736
* lower case identifier prefix strcmp
737
* if e!=0 then it will point to the next char after the match
738
*/
739
740
int
741
mimecmp(register const char* s, register const char* v, char** e)
742
{
743
register int n;
744
745
while (isalnum(*v) || *v == *s && (*v == '_' || *v == '-' || *v == '/'))
746
if (n = lower(*s++) - lower(*v++))
747
return n;
748
if (!isalnum(*s) && *s != '_' && *s != '-')
749
{
750
if (e)
751
*e = (char*)s;
752
return 0;
753
}
754
return lower(*s) - lower(*v);
755
}
756
757
/*
758
* parse mime headers in strsearch(tab,num,siz) from s
759
* return >0 if mime header consumed
760
*/
761
762
int
763
mimehead(Mime_t* mp, void* tab, size_t num, size_t siz, register char* s)
764
{
765
register void* p;
766
char* e;
767
Parse_t pp;
768
Mimevalue_f set;
769
770
set = mp->disc->valuef;
771
if (!strncasecmp(s, "original-", 9))
772
s += 9;
773
if (!strncasecmp(s, "content-", 8))
774
{
775
s += 8;
776
if ((p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, s, &e)) && *e == ':')
777
{
778
pp.next = e + 1;
779
if (arg(&pp, 1))
780
{
781
if ((*set)(mp, p, pp.name.data, pp.name.size, mp->disc))
782
return 0;
783
while (arg(&pp, 0))
784
if (pp.value.size &&
785
(p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, pp.name.data, &e)) &&
786
(*set)(mp, p, pp.value.data, pp.value.size, mp->disc))
787
return 0;
788
return 1;
789
}
790
}
791
else if (strchr(s, ':'))
792
return 1;
793
}
794
return !strncasecmp(s, "x-", 2);
795
}
796
797
/*
798
* open a mime library handle
799
*/
800
801
Mime_t*
802
mimeopen(Mimedisc_t* disc)
803
{
804
register Mime_t* mp;
805
806
if (!(mp = newof(0, Mime_t, 1, 0)))
807
return 0;
808
mp->id = lib;
809
mp->disc = disc;
810
mp->dict.key = offsetof(Ent_t, name);
811
mp->dict.comparf = order;
812
mp->dict.freef = drop;
813
if (!(mp->buf = sfstropen()) || !(mp->cap = dtopen(&mp->dict, Dtoset)))
814
{
815
mimeclose(mp);
816
return 0;
817
}
818
return mp;
819
}
820
821
/*
822
* close a mimeopen() handle
823
*/
824
825
int
826
mimeclose(Mime_t* mp)
827
{
828
if (mp)
829
{
830
if (mp->buf)
831
sfclose(mp->buf);
832
if (mp->cap)
833
dtclose(mp->cap);
834
if (mp->freef)
835
(*mp->freef)(mp);
836
free(mp);
837
}
838
return 0;
839
}
840
841