tmail 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/LICENSE +21 -0
  2. data/README +157 -0
  3. data/bat/changelog +19 -0
  4. data/bat/clobber/package +10 -0
  5. data/bat/compile +42 -0
  6. data/bat/config.yaml +8 -0
  7. data/bat/prepare +8 -0
  8. data/bat/publish +51 -0
  9. data/bat/rdoc +42 -0
  10. data/bat/release +12 -0
  11. data/bat/setup +1616 -0
  12. data/bat/stats +138 -0
  13. data/bat/tag +25 -0
  14. data/bat/test +25 -0
  15. data/ext/tmail/Makefile +25 -0
  16. data/ext/tmail/base64/MANIFEST +4 -0
  17. data/ext/tmail/base64/base64.c +264 -0
  18. data/ext/tmail/base64/depend +1 -0
  19. data/ext/tmail/base64/extconf.rb +38 -0
  20. data/ext/tmail/scanner_c/MANIFEST +4 -0
  21. data/ext/tmail/scanner_c/depend +1 -0
  22. data/ext/tmail/scanner_c/extconf.rb +38 -0
  23. data/ext/tmail/scanner_c/scanner_c.c +582 -0
  24. data/lib/tmail.rb +4 -0
  25. data/lib/tmail/Makefile +19 -0
  26. data/lib/tmail/address.rb +245 -0
  27. data/lib/tmail/attachments.rb +47 -0
  28. data/lib/tmail/base64.rb +75 -0
  29. data/lib/tmail/compat.rb +39 -0
  30. data/lib/tmail/config.rb +71 -0
  31. data/lib/tmail/core_extensions.rb +67 -0
  32. data/lib/tmail/encode.rb +524 -0
  33. data/lib/tmail/header.rb +931 -0
  34. data/lib/tmail/index.rb +8 -0
  35. data/lib/tmail/interface.rb +540 -0
  36. data/lib/tmail/loader.rb +1 -0
  37. data/lib/tmail/mail.rb +507 -0
  38. data/lib/tmail/mailbox.rb +435 -0
  39. data/lib/tmail/mbox.rb +1 -0
  40. data/lib/tmail/net.rb +282 -0
  41. data/lib/tmail/obsolete.rb +137 -0
  42. data/lib/tmail/parser.rb +1475 -0
  43. data/lib/tmail/parser.y +381 -0
  44. data/lib/tmail/port.rb +379 -0
  45. data/lib/tmail/quoting.rb +142 -0
  46. data/lib/tmail/require_arch.rb +56 -0
  47. data/lib/tmail/scanner.rb +44 -0
  48. data/lib/tmail/scanner_r.rb +263 -0
  49. data/lib/tmail/stringio.rb +279 -0
  50. data/lib/tmail/tmail.rb +1 -0
  51. data/lib/tmail/utils.rb +281 -0
  52. data/lib/tmail/version.rb +38 -0
  53. data/meta/icli.yaml +16 -0
  54. data/meta/tmail-1.1.1.roll +24 -0
  55. data/sample/data/multipart +23 -0
  56. data/sample/data/normal +29 -0
  57. data/sample/data/sendtest +5 -0
  58. data/sample/data/simple +14 -0
  59. data/sample/data/test +27 -0
  60. data/sample/extract-attachements.rb +33 -0
  61. data/sample/from-check.rb +26 -0
  62. data/sample/multipart.rb +26 -0
  63. data/sample/parse-bench.rb +68 -0
  64. data/sample/parse-test.rb +19 -0
  65. data/sample/sendmail.rb +94 -0
  66. data/test/extctrl.rb +6 -0
  67. data/test/fixtures/raw_base64_decoded_string +0 -0
  68. data/test/fixtures/raw_base64_email +83 -0
  69. data/test/fixtures/raw_base64_encoded_string +1 -0
  70. data/test/fixtures/raw_email +14 -0
  71. data/test/fixtures/raw_email10 +20 -0
  72. data/test/fixtures/raw_email11 +34 -0
  73. data/test/fixtures/raw_email12 +32 -0
  74. data/test/fixtures/raw_email13 +29 -0
  75. data/test/fixtures/raw_email2 +114 -0
  76. data/test/fixtures/raw_email3 +70 -0
  77. data/test/fixtures/raw_email4 +59 -0
  78. data/test/fixtures/raw_email5 +19 -0
  79. data/test/fixtures/raw_email6 +20 -0
  80. data/test/fixtures/raw_email7 +66 -0
  81. data/test/fixtures/raw_email8 +47 -0
  82. data/test/fixtures/raw_email9 +28 -0
  83. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  84. data/test/fixtures/raw_email_simple +11 -0
  85. data/test/fixtures/raw_email_with_illegal_boundary +58 -0
  86. data/test/fixtures/raw_email_with_multipart_mixed_quoted_boundary +50 -0
  87. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  88. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  89. data/test/fixtures/raw_email_with_quoted_illegal_boundary +58 -0
  90. data/test/kcode.rb +14 -0
  91. data/test/test_address.rb +1128 -0
  92. data/test/test_attachments.rb +35 -0
  93. data/test/test_base64.rb +63 -0
  94. data/test/test_encode.rb +77 -0
  95. data/test/test_header.rb +885 -0
  96. data/test/test_helper.rb +2 -0
  97. data/test/test_mail.rb +623 -0
  98. data/test/test_mbox.rb +126 -0
  99. data/test/test_port.rb +430 -0
  100. data/test/test_scanner.rb +209 -0
  101. data/test/test_utils.rb +37 -0
  102. metadata +205 -0
@@ -0,0 +1 @@
1
+ tmbase64.o: tmbase64.c $(hdrdir)/ruby.h $(topdir)/config.h $(hdrdir)/defines.h Makefile
@@ -0,0 +1,38 @@
1
+ require 'mkmf'
2
+ require 'rbconfig'
3
+
4
+ extension_name = 'base64'
5
+
6
+ arch = Config::CONFIG['arch']
7
+
8
+ windows = (/mswin/ =~ arch) #RUBY_PLATFORM)
9
+
10
+ if (ENV['NORUBYEXT'] == 'true') || windows # TEMPORARILY ADD WINDOWS HERE
11
+ #ENV['make'] = 'echo' # THIS DOESN"T GET TO THE PARENT PROCESS!!!
12
+ # LETS TRY FAKING IT OUT.
13
+ if windows
14
+ File.open('make.bat', 'w') do |f|
15
+ f << 'echo Native extension will be omitted.'
16
+ end
17
+ File.open('nmake.bat', 'w') do |f|
18
+ f << 'echo Native extension will be omitted.'
19
+ end
20
+ #File.chmod(0755, "make.bat", "nmake.bat") # need?
21
+ end
22
+ File.open('Makefile', 'w') do |f|
23
+ f << "all:\n"
24
+ f << "install:\n"
25
+ end
26
+ else
27
+ if windows && ENV['make'].nil?
28
+ $LIBS += " msvcprt.lib"
29
+ #dir_config(extension_name)
30
+ #create_makefile(extension_name, "tmail")
31
+ create_makefile(extension_name, "tmail/#{arch}")
32
+ else
33
+ $CFLAGS += " -D_FILE_OFFSET_BITS=64" #???
34
+ #dir_config(extension_name)
35
+ #create_makefile(extension_name, "tmail")
36
+ create_makefile(extension_name, "tmail/#{arch}")
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ MANIFEST
2
+ mails.c
3
+ extconf.rb
4
+ depend
@@ -0,0 +1 @@
1
+ mails.o: mails.c $(hdrdir)/ruby.h $(topdir)/config.h $(hdrdir)/defines.h Makefile
@@ -0,0 +1,38 @@
1
+ require 'mkmf'
2
+ require 'rbconfig'
3
+
4
+ extension_name = 'scanner_c'
5
+
6
+ arch = Config::CONFIG['arch']
7
+
8
+ windows = (/mswin/ =~ arch) #RUBY_PLATFORM)
9
+
10
+ if (ENV['NORUBYEXT'] == 'true') || windows # TEMPORARILY ADD WINDOWS HERE
11
+ #ENV['make'] = 'echo' # THIS DOESN"T GET TO THE PARENT PROCESS!!!
12
+ # LETS TRY FAKING IT OUT.
13
+ if windows
14
+ File.open('make.bat', 'w') do |f|
15
+ f << 'echo Native extension will be omitted.'
16
+ end
17
+ File.open('nmake.bat', 'w') do |f|
18
+ f << 'echo Native extension will be omitted.'
19
+ end
20
+ #File.chmod(0755, "make.bat", "nmake.bat") # need?
21
+ end
22
+ File.open('Makefile', 'w') do |f|
23
+ f << "all:\n"
24
+ f << "install:\n"
25
+ end
26
+ else
27
+ if windows && ENV['make'].nil?
28
+ $LIBS += " msvcprt.lib"
29
+ #dir_config(extension_name)
30
+ #create_makefile(extension_name, "tmail")
31
+ create_makefile(extension_name, "tmail/#{arch}")
32
+ else
33
+ $CFLAGS += " -D_FILE_OFFSET_BITS=64" #???
34
+ #dir_config(extension_name)
35
+ #create_makefile(extension_name, "tmail")
36
+ create_makefile(extension_name, "tmail/#{arch}")
37
+ end
38
+ end
@@ -0,0 +1,582 @@
1
+ /*
2
+
3
+ scanner_c.c
4
+
5
+ Copyright (c) 1998-2007 Minero Aoki
6
+
7
+ This program is free software.
8
+ You can distribute/modify this program under the terms of
9
+ the GNU Lesser General Public License version 2.1.
10
+
11
+ */
12
+
13
+ #include <stdio.h>
14
+ #ifdef __STDC__
15
+ # include <stdlib.h>
16
+ #endif
17
+
18
+ #include "ruby.h"
19
+ #include "re.h"
20
+
21
+
22
+ #define TMAIL_VERSION "0.10.8"
23
+
24
+ static VALUE MailScanner;
25
+ static VALUE ScanError;
26
+
27
+ struct scanner
28
+ {
29
+ char *pbeg;
30
+ char *p;
31
+ char *pend;
32
+ unsigned int flags;
33
+ VALUE comments;
34
+ };
35
+
36
+ #define MODE_MIME (1 << 0)
37
+ #define MODE_RECV (1 << 1)
38
+ #define MODE_ISO2022 (1 << 2)
39
+ #define MODE_DEBUG (1 << 4)
40
+
41
+ #define MIME_MODE_P(s) ((s)->flags & MODE_MIME)
42
+ #define RECV_MODE_P(s) ((s)->flags & MODE_RECV)
43
+ #define ISO2022_MODE_P(s) ((s)->flags & MODE_ISO2022)
44
+
45
+ #define GET_SCANNER(val, s) Data_Get_Struct(val, struct scanner, s)
46
+
47
+
48
+ static void
49
+ mails_free(sc)
50
+ struct scanner *sc;
51
+ {
52
+ free(sc);
53
+ }
54
+
55
+ #ifndef StringValue
56
+ # define StringValue(s) Check_Type(str, T_STRING);
57
+ #endif
58
+
59
+ /*
60
+ * Document-method: mails_s_new
61
+ *
62
+ * Creates a new mail
63
+ *
64
+ */
65
+ static VALUE
66
+ mails_s_new(klass, str, ident, cmt)
67
+ VALUE klass, str, ident, cmt;
68
+ {
69
+ struct scanner *sc;
70
+ const char *tmp;
71
+
72
+ sc = ALLOC_N(struct scanner, 1);
73
+
74
+ StringValue(str);
75
+ sc->pbeg = RSTRING(str)->ptr;
76
+ sc->p = sc->pbeg;
77
+ sc->pend = sc->p + RSTRING(str)->len;
78
+
79
+ sc->flags = 0;
80
+ Check_Type(ident, T_SYMBOL);
81
+ tmp = rb_id2name(SYM2ID(ident));
82
+ if (strcmp(tmp, "RECEIVED") == 0) sc->flags |= MODE_RECV;
83
+ else if (strcmp(tmp, "CTYPE") == 0) sc->flags |= MODE_MIME;
84
+ else if (strcmp(tmp, "CENCODING") == 0) sc->flags |= MODE_MIME;
85
+ else if (strcmp(tmp, "CDISPOSITION") == 0) sc->flags |= MODE_MIME;
86
+
87
+ tmp = rb_get_kcode();
88
+ if (strcmp(tmp, "EUC") == 0 || strcmp(tmp, "SJIS") == 0) {
89
+ sc->flags |= MODE_ISO2022;
90
+ }
91
+
92
+ sc->comments = Qnil;
93
+ if (! NIL_P(cmt)) {
94
+ Check_Type(cmt, T_ARRAY);
95
+ sc->comments = cmt;
96
+ }
97
+
98
+ return Data_Wrap_Struct(MailScanner, 0, mails_free, sc);
99
+ }
100
+
101
+ /*
102
+ * Document-method: mails_debug_get
103
+ *
104
+ * TODO: Documentation needed
105
+ *
106
+ */
107
+ static VALUE
108
+ mails_debug_get(self)
109
+ VALUE self;
110
+ {
111
+ struct scanner *sc;
112
+
113
+ GET_SCANNER(self, sc);
114
+ if (sc->flags & MODE_DEBUG)
115
+ return Qtrue;
116
+ else
117
+ return Qfalse;
118
+ }
119
+
120
+ /*
121
+ * Document-method: mails_debug_set
122
+ *
123
+ * TODO: Documentation needed
124
+ *
125
+ */
126
+ static VALUE
127
+ mails_debug_set(self, flag)
128
+ VALUE self, flag;
129
+ {
130
+ struct scanner *sc;
131
+
132
+ GET_SCANNER(self, sc);
133
+ if (RTEST(flag))
134
+ sc->flags |= MODE_DEBUG;
135
+ else
136
+ sc->flags &= ~MODE_DEBUG;
137
+ return Qnil;
138
+ }
139
+
140
+
141
+ /*
142
+ ----------------------------------------------------------------------
143
+ scanning routines
144
+ ----------------------------------------------------------------------
145
+ */
146
+
147
+ #define ESC '\033'
148
+ #define ATOM_SYMBOLS "_#!$%&'`*+-{|}~^/=?"
149
+ #define TOKEN_SYMBOLS "_#!$%&'`*+-{|}~^."
150
+ #define ATOM_SPECIAL "()<>[]@,;:\"\\."
151
+ #define TOKEN_SPECIAL "()<>[]@,;:\"\\/?="
152
+ #define LWSP " \t\r\n"
153
+
154
+ #define IS_ALPHA(ch) (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'))
155
+ #define IS_UPPER(ch) ('A' <= ch && ch <= 'Z')
156
+ #define TO_LOWER(ch) (IS_UPPER(ch) ? ch + 32 : ch)
157
+ #define IS_LWSP(ch) (strchr(LWSP, ch))
158
+ #define IS_DIGIT(ch) ('0' <= ch && ch <= '9')
159
+ #define IS_WORDCHAR(ch, symlist) \
160
+ (IS_ALPHA(ch) || IS_DIGIT(ch) || strchr(symlist, ch))
161
+ #define IS_ATOMCHAR(ch) IS_WORDCHAR(ch, ATOM_SYMBOLS)
162
+ #define IS_TOKENCHAR(ch) IS_WORDCHAR(ch, TOKEN_SYMBOLS)
163
+ #define IS_JCHAR(ch) ismbchar(ch)
164
+
165
+
166
+ /* I know this implement is ugly, but usually useful. */
167
+
168
+ /* skip until "\e(B" (us-ascii) */
169
+ static void
170
+ skip_iso2022jp_string(sc)
171
+ struct scanner *sc;
172
+ {
173
+ for (; sc->p < sc->pend; sc->p++) {
174
+ if (*sc->p == ESC) {
175
+ if (strncmp(sc->p, "\033(B", 3) == 0) {
176
+ sc->p += 3;
177
+ return;
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ static void
184
+ skip_japanese_string(sc)
185
+ struct scanner *sc;
186
+ {
187
+ while (sc->p < sc->pend) {
188
+ if (! ismbchar(*sc->p)) return;
189
+ sc->p += mbclen(*sc->p);
190
+ }
191
+ }
192
+
193
+
194
+ #define scan_atom(sc) scan_word(sc, ATOM_SYMBOLS)
195
+ #define scan_token(sc) scan_word(sc, TOKEN_SYMBOLS)
196
+
197
+ static VALUE
198
+ scan_word(sc, syms)
199
+ struct scanner *sc;
200
+ char *syms;
201
+ {
202
+ char *beg = sc->p;
203
+
204
+ while (sc->p < sc->pend) {
205
+ if (ISO2022_MODE_P(sc) && *sc->p == ESC) {
206
+ skip_iso2022jp_string(sc);
207
+ }
208
+ else if (IS_JCHAR(*sc->p)) {
209
+ skip_japanese_string(sc);
210
+ }
211
+ else if (IS_WORDCHAR(*sc->p, syms)) {
212
+ sc->p++;
213
+ }
214
+ else {
215
+ break;
216
+ }
217
+ }
218
+
219
+ return rb_str_new(beg, sc->p - beg);
220
+ }
221
+
222
+
223
+ #define BUFSIZE 256
224
+
225
+ static VALUE
226
+ scan_quoted_word(sc)
227
+ struct scanner *sc;
228
+ {
229
+ char buf[BUFSIZE];
230
+ char *p;
231
+ char *save;
232
+ VALUE result = rb_str_new("", 0);
233
+
234
+ sc->p++; /* discard first dquote */
235
+ p = buf;
236
+ while (sc->p < sc->pend) {
237
+ if (*sc->p == '"') {
238
+ sc->p++; /* discard terminal dquote */
239
+ rb_str_cat(result, buf, p - buf);
240
+ return result;
241
+ }
242
+ if (ISO2022_MODE_P(sc) && *sc->p == ESC) {
243
+ save = sc->p;
244
+ skip_iso2022jp_string(sc);
245
+ while (save < sc->p) {
246
+ *p++ = *save++;
247
+ if (p >= buf + BUFSIZE) {
248
+ /* flush buffer */
249
+ rb_str_cat(result, buf, BUFSIZE);
250
+ p = buf;
251
+ }
252
+ }
253
+ continue;
254
+ }
255
+
256
+ if (*sc->p == '\\')
257
+ sc->p++; /* discard quoting backslash */
258
+ *p++ = *sc->p++;
259
+ if (p >= buf + BUFSIZE) {
260
+ /* flush buffer */
261
+ rb_str_cat(result, buf, BUFSIZE);
262
+ p = buf;
263
+ }
264
+ }
265
+
266
+ rb_raise(ScanError, "unterminated quoted-word");
267
+ return Qnil;
268
+ }
269
+
270
+ static VALUE
271
+ scan_domain_literal(sc)
272
+ struct scanner *sc;
273
+ {
274
+ char buf[BUFSIZE];
275
+ char *p;
276
+ VALUE result = rb_str_new("", 0);
277
+
278
+ p = buf;
279
+ while (sc->p < sc->pend) {
280
+ if (*sc->p == ']') {
281
+ *p++ = *sc->p++;
282
+ rb_str_cat(result, buf, p - buf);
283
+ return result;
284
+ }
285
+
286
+ if (*sc->p == '\\')
287
+ sc->p++; /* discard backslash */
288
+ *p++ = *sc->p++;
289
+ if (p >= buf + BUFSIZE) {
290
+ /* flush buffer */
291
+ rb_str_cat(result, buf, BUFSIZE);
292
+ p = buf;
293
+ }
294
+ }
295
+
296
+ rb_raise(ScanError, "unterminated domain literal");
297
+ return Qnil;
298
+ }
299
+
300
+
301
+ static VALUE
302
+ scan_comment(sc)
303
+ struct scanner *sc;
304
+ {
305
+ int nest = 1;
306
+ char *p;
307
+ VALUE ret = rb_str_new("", 0);
308
+
309
+ sc->p++;
310
+ p = sc->p;
311
+ while (sc->p < sc->pend) {
312
+ if (ISO2022_MODE_P(sc) && *sc->p == ESC) {
313
+ skip_iso2022jp_string(sc);
314
+ }
315
+ else if (IS_JCHAR(*sc->p)) {
316
+ skip_japanese_string(sc);
317
+ }
318
+ else {
319
+ switch (*sc->p) {
320
+ case '(':
321
+ nest++;
322
+ break;
323
+ case ')':
324
+ nest--;
325
+ if (nest == 0) {
326
+ rb_str_cat(ret, p, sc->p - p);
327
+ sc->p++;
328
+ return ret;
329
+ }
330
+ break;
331
+ case '\\':
332
+ rb_str_cat(ret, p, sc->p - p);
333
+ sc->p++;
334
+ if (sc->p == sc->pend)
335
+ rb_raise(ScanError, "incomplete char quote");
336
+ p = sc->p;
337
+ break;
338
+ default:
339
+ break;
340
+ }
341
+ sc->p++;
342
+ }
343
+ }
344
+
345
+ rb_raise(ScanError, "unterminated comment");
346
+ return Qnil;
347
+ }
348
+
349
+
350
+ static void
351
+ skip_lwsp(sc)
352
+ struct scanner *sc;
353
+ {
354
+ while (sc->p < sc->pend) {
355
+ if (IS_LWSP(*sc->p)) sc->p++;
356
+ else break;
357
+ }
358
+ }
359
+
360
+ static int
361
+ nccmp(a, b)
362
+ char *a, *b;
363
+ {
364
+ while (*a && *b) {
365
+ if ((*a != *b) && (TO_LOWER(*a) != TO_LOWER(*b)))
366
+ return 0;
367
+ a++; b++;
368
+ }
369
+ return (*a == *b);
370
+ }
371
+
372
+ static int
373
+ digit_p(str)
374
+ VALUE str;
375
+ {
376
+ char *p;
377
+ int i;
378
+
379
+ p = RSTRING(str)->ptr;
380
+ for (i = 0; i < RSTRING(str)->len; i++) {
381
+ if (! IS_DIGIT(RSTRING(str)->ptr[i]))
382
+ return 0;
383
+ }
384
+ return 1;
385
+ }
386
+
387
+ static VALUE tok_atom, tok_digit, tok_token, tok_quoted, tok_domlit;
388
+ static VALUE tok_from, tok_by, tok_via, tok_with, tok_id, tok_for;
389
+
390
+ static VALUE
391
+ atomsym(sc, str)
392
+ struct scanner *sc;
393
+ VALUE str;
394
+ {
395
+ if (digit_p(str)) {
396
+ return tok_digit;
397
+ }
398
+ else if (RECV_MODE_P(sc)) {
399
+ char *p = RSTRING(str)->ptr;
400
+ if (nccmp(p, "from")) return tok_from;
401
+ else if (nccmp(p, "by")) return tok_by;
402
+ else if (nccmp(p, "via")) return tok_via;
403
+ else if (nccmp(p, "with")) return tok_with;
404
+ else if (nccmp(p, "id")) return tok_id;
405
+ else if (nccmp(p, "for")) return tok_for;
406
+ }
407
+ return tok_atom;
408
+ }
409
+
410
+ static void
411
+ debug_print(sc, sym, val)
412
+ struct scanner *sc;
413
+ VALUE sym, val;
414
+ {
415
+ VALUE s;
416
+
417
+ s = rb_funcall(sym, rb_intern("inspect"), 0),
418
+ printf("%7ld %-10s token=<%s>\n",
419
+ (unsigned long)(sc->pend - sc->p),
420
+ RSTRING(s)->ptr,
421
+ RSTRING(val)->ptr);
422
+ }
423
+
424
+ #define D(expr) do {\
425
+ if (sc->flags & MODE_DEBUG) {expr;}\
426
+ } while (0)
427
+
428
+ static void
429
+ pass_token(sc, sym, tok, arr)
430
+ struct scanner *sc;
431
+ VALUE sym, tok, arr;
432
+ {
433
+ D(debug_print(sc, sym, tok));
434
+ rb_ary_store(arr, 0, sym);
435
+ rb_ary_store(arr, 1, tok);
436
+ rb_yield(arr);
437
+ }
438
+
439
+ /*
440
+ * Document-method: mails_scan
441
+ *
442
+ * TODO: Documentation needed
443
+ *
444
+ */
445
+ static VALUE
446
+ mails_scan(self)
447
+ VALUE self;
448
+ {
449
+ struct scanner *sc;
450
+ VALUE arr;
451
+
452
+ #define PASS(s,v) pass_token(sc,s,v,arr)
453
+ GET_SCANNER(self, sc);
454
+ if (!sc->p) {
455
+ rb_raise(ScanError, "Mails#scan called before reset");
456
+ }
457
+ arr = rb_assoc_new(Qnil, Qnil);
458
+
459
+ while (sc->p < sc->pend) {
460
+ D(puts("new loop"));
461
+ D(printf("char='%c'\n", *sc->p));
462
+ if (IS_LWSP(*sc->p)) {
463
+ D(puts("lwsp"));
464
+ skip_lwsp(sc);
465
+ if (sc->p >= sc->pend)
466
+ break;
467
+ }
468
+
469
+ if (MIME_MODE_P(sc)) {
470
+ if (IS_TOKENCHAR(*sc->p) ||
471
+ (ISO2022_MODE_P(sc) && (*sc->p == ESC)) ||
472
+ IS_JCHAR(*sc->p)) {
473
+ D(puts("token"));
474
+ PASS(tok_token, scan_token(sc));
475
+ continue;
476
+ }
477
+ }
478
+ else {
479
+ if (IS_ATOMCHAR(*sc->p) ||
480
+ (ISO2022_MODE_P(sc) && (*sc->p == ESC)) ||
481
+ IS_JCHAR(*sc->p)) {
482
+ VALUE tmp;
483
+ D(puts("atom"));
484
+ tmp = scan_atom(sc);
485
+ PASS(atomsym(sc, tmp), tmp);
486
+ continue;
487
+ }
488
+ }
489
+
490
+ if (*sc->p == '"') {
491
+ D(puts("quoted"));
492
+ PASS(tok_quoted, scan_quoted_word(sc));
493
+ D(puts("quoted"));
494
+ }
495
+ else if (*sc->p == '(') {
496
+ VALUE c;
497
+ D(puts("comment"));
498
+ c = scan_comment(sc);
499
+ if (! NIL_P(sc->comments))
500
+ rb_ary_push(sc->comments, c);
501
+ }
502
+ else if (*sc->p == '[') {
503
+ D(puts("domlit"));
504
+ PASS(tok_domlit, scan_domain_literal(sc));
505
+ }
506
+ else {
507
+ VALUE ch;
508
+ D(puts("char"));
509
+ ch = rb_str_new(sc->p, 1);
510
+ sc->p++;
511
+ PASS(ch, ch);
512
+ }
513
+ }
514
+
515
+ PASS(Qfalse, rb_str_new("$", 1));
516
+ return Qnil;
517
+ }
518
+
519
+
520
+ /*
521
+ ------------------------------------------------------------------
522
+ ruby interface
523
+ ------------------------------------------------------------------
524
+ */
525
+
526
+ static VALUE
527
+ cstr2symbol(str)
528
+ char *str;
529
+ {
530
+ ID tmp;
531
+
532
+ tmp = rb_intern(str);
533
+ #ifdef ID2SYM
534
+ return ID2SYM(tmp);
535
+ #else
536
+ return INT2FIX(tmp);
537
+ #endif
538
+ }
539
+
540
+ void
541
+ Init_scanner_c()
542
+ {
543
+ VALUE TMail;
544
+ VALUE tmp;
545
+
546
+ if (rb_const_defined(rb_cObject, rb_intern("TMail"))) {
547
+ TMail = rb_const_get(rb_cObject, rb_intern("TMail"));
548
+ }
549
+ else {
550
+ TMail = rb_define_module("TMail");
551
+ }
552
+ MailScanner = rb_define_class_under(TMail, "Scanner_C", rb_cObject);
553
+
554
+ tmp = rb_str_new2(TMAIL_VERSION);
555
+ rb_obj_freeze(tmp);
556
+ rb_define_const(MailScanner, "Version", tmp);
557
+
558
+ rb_define_singleton_method(MailScanner, "new", mails_s_new, 3);
559
+ rb_define_method(MailScanner, "scan", mails_scan, 0);
560
+ rb_define_method(MailScanner, "debug", mails_debug_get, 0);
561
+ rb_define_method(MailScanner, "debug=", mails_debug_set, 1);
562
+
563
+ if (rb_const_defined(TMail, rb_intern("SyntaxError"))) {
564
+ ScanError = rb_const_get(rb_cObject, rb_intern("SyntaxError"));
565
+ }
566
+ else {
567
+ ScanError = rb_define_class_under(TMail, "SyntaxError", rb_eStandardError);
568
+ }
569
+
570
+ tok_atom = cstr2symbol("ATOM");
571
+ tok_digit = cstr2symbol("DIGIT");
572
+ tok_token = cstr2symbol("TOKEN");
573
+ tok_quoted = cstr2symbol("QUOTED");
574
+ tok_domlit = cstr2symbol("DOMLIT");
575
+
576
+ tok_from = cstr2symbol("FROM");
577
+ tok_by = cstr2symbol("BY");
578
+ tok_via = cstr2symbol("VIA");
579
+ tok_with = cstr2symbol("WITH");
580
+ tok_id = cstr2symbol("ID");
581
+ tok_for = cstr2symbol("FOR");
582
+ }