agoo 2.5.4 → 2.5.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of agoo might be problematic. Click here for more details.

Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/bin/agoo +3 -1
  4. data/ext/agoo/base64.h +3 -3
  5. data/ext/agoo/bind.c +1 -1
  6. data/ext/agoo/bind.h +3 -3
  7. data/ext/agoo/con.c +4 -2
  8. data/ext/agoo/con.h +3 -3
  9. data/ext/agoo/debug.c +7 -0
  10. data/ext/agoo/debug.h +13 -6
  11. data/ext/agoo/doc.c +159 -0
  12. data/ext/agoo/doc.h +34 -0
  13. data/ext/agoo/dtime.h +3 -3
  14. data/ext/agoo/err.h +3 -3
  15. data/ext/agoo/error_stream.h +3 -3
  16. data/ext/agoo/extconf.rb +1 -1
  17. data/ext/agoo/gqlintro.c +333 -0
  18. data/ext/agoo/gqlintro.h +10 -0
  19. data/ext/agoo/gqlvalue.c +1035 -0
  20. data/ext/agoo/gqlvalue.h +88 -0
  21. data/ext/agoo/graphql.c +1078 -0
  22. data/ext/agoo/graphql.h +198 -0
  23. data/ext/agoo/hook.c +2 -0
  24. data/ext/agoo/hook.h +4 -3
  25. data/ext/agoo/http.c +2 -1
  26. data/ext/agoo/http.h +3 -3
  27. data/ext/agoo/kinds.h +3 -3
  28. data/ext/agoo/log.h +3 -3
  29. data/ext/agoo/log_queue.h +3 -3
  30. data/ext/agoo/method.h +3 -3
  31. data/ext/agoo/page.h +3 -3
  32. data/ext/agoo/pub.h +3 -3
  33. data/ext/agoo/queue.h +3 -3
  34. data/ext/agoo/rack_logger.h +3 -3
  35. data/ext/agoo/req.c +2 -2
  36. data/ext/agoo/req.h +3 -3
  37. data/ext/agoo/request.h +3 -3
  38. data/ext/agoo/res.h +3 -3
  39. data/ext/agoo/response.h +3 -3
  40. data/ext/agoo/rhook.c +2 -2
  41. data/ext/agoo/rhook.h +4 -3
  42. data/ext/agoo/rlog.h +3 -3
  43. data/ext/agoo/rresponse.h +3 -3
  44. data/ext/agoo/rserver.c +64 -0
  45. data/ext/agoo/rserver.h +3 -3
  46. data/ext/agoo/rupgraded.c +1 -1
  47. data/ext/agoo/rupgraded.h +3 -3
  48. data/ext/agoo/sdl.c +334 -0
  49. data/ext/agoo/sdl.h +10 -0
  50. data/ext/agoo/seg.h +3 -3
  51. data/ext/agoo/server.c +3 -1
  52. data/ext/agoo/server.h +5 -4
  53. data/ext/agoo/sha1.h +3 -3
  54. data/ext/agoo/sse.h +3 -3
  55. data/ext/agoo/sub.h +3 -3
  56. data/ext/agoo/subject.h +3 -3
  57. data/ext/agoo/text.h +3 -3
  58. data/ext/agoo/upgraded.h +3 -3
  59. data/ext/agoo/websocket.h +3 -3
  60. data/lib/agoo/version.rb +1 -1
  61. data/lib/rack/handler/agoo.rb +3 -1
  62. metadata +12 -12
  63. data/ext/agoo/foo/agoo.c +0 -109
  64. data/ext/agoo/foo/con.c +0 -1220
  65. data/ext/agoo/foo/con.h +0 -65
  66. data/ext/agoo/foo/page.c +0 -699
  67. data/ext/agoo/foo/pub.c +0 -131
  68. data/ext/agoo/foo/pub.h +0 -40
  69. data/ext/agoo/foo/rserver.c +0 -1016
  70. data/ext/agoo/foo/server.c +0 -303
  71. data/ext/agoo/foo/server.h +0 -67
  72. data/ext/agoo/foo/upgraded.c +0 -182
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
2
 
3
- #ifndef __AGOO_RESPONSE_H__
4
- #define __AGOO_RESPONSE_H__
3
+ #ifndef AGOO_RESPONSE_H
4
+ #define AGOO_RESPONSE_H
5
5
 
6
6
  #include <stdatomic.h>
7
7
  #include <stdbool.h>
@@ -25,4 +25,4 @@ typedef struct _Response {
25
25
  extern int response_len(Response res);
26
26
  extern void response_fill(Response res, char *buf);
27
27
 
28
- #endif // __AGOO_RESPONSE_H__
28
+ #endif // AGOO_RESPONSE_H
@@ -22,7 +22,7 @@ resolve_classname(VALUE mod, const char *classname) {
22
22
  return clas;
23
23
  }
24
24
 
25
- static VALUE
25
+ VALUE
26
26
  resolve_classpath(const char *name, size_t len) {
27
27
  char class_name[1024];
28
28
  VALUE clas;
@@ -55,7 +55,7 @@ resolve_classpath(const char *name, size_t len) {
55
55
 
56
56
  Hook
57
57
  rhook_create(Method method, const char *pattern, VALUE handler, Queue q) {
58
- Hook hook = hook_create(method,pattern, NULL, RACK_HOOK, q);
58
+ Hook hook = hook_create(method, pattern, NULL, RACK_HOOK, q);
59
59
 
60
60
  if (NULL != hook) {
61
61
  if (T_STRING == rb_type(handler)) {
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
2
 
3
- #ifndef __AGOO_RHOOK_H__
4
- #define __AGOO_RHOOK_H__
3
+ #ifndef AGOO_RHOOK_H
4
+ #define AGOO_RHOOK_H
5
5
 
6
6
  #include <ruby.h>
7
7
 
@@ -9,5 +9,6 @@
9
9
  #include "method.h"
10
10
 
11
11
  extern Hook rhook_create(Method method, const char *pattern, VALUE handler, Queue q);
12
+ extern VALUE resolve_classpath(const char *name, size_t len);
12
13
 
13
- #endif // __AGOO_RHOOK_H__
14
+ #endif // AGOO_RHOOK_H
@@ -1,11 +1,11 @@
1
1
  // Copyright 2018 by Peter Ohler, All Rights Reserved
2
2
 
3
- #ifndef __AGOO_RLOG_H__
4
- #define __AGOO_RLOG_H__
3
+ #ifndef AGOO_RLOG_H
4
+ #define AGOO_RLOG_H
5
5
 
6
6
  #include <ruby.h>
7
7
  #include "log.h"
8
8
 
9
9
  extern void rlog_init(VALUE mod);
10
10
 
11
- #endif /* __AGOO_RLOG_H__ */
11
+ #endif /* AGOO_RLOG_H */
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
2
 
3
- #ifndef __AGOO_RRESPONSE_H__
4
- #define __AGOO_RRESPONSE_H__
3
+ #ifndef AGOO_RRESPONSE_H
4
+ #define AGOO_RRESPONSE_H
5
5
 
6
6
  #include <ruby.h>
7
7
 
@@ -11,4 +11,4 @@ extern void response_init(VALUE mod);
11
11
  extern VALUE response_new();
12
12
  extern Text response_text(VALUE self);
13
13
 
14
- #endif // __AGOO_RRESPONSE_H__
14
+ #endif // AGOO_RRESPONSE_H
@@ -15,6 +15,7 @@
15
15
  #include "debug.h"
16
16
  #include "dtime.h"
17
17
  #include "err.h"
18
+ #include "graphql.h"
18
19
  #include "http.h"
19
20
  #include "log.h"
20
21
  #include "page.h"
@@ -173,6 +174,32 @@ configure(Err err, int port, const char *root, VALUE options) {
173
174
  break;
174
175
  }
175
176
  }
177
+ if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("graphql"))))) {
178
+ const char *path;
179
+ Hook hook;
180
+ char schema_path[256];
181
+ long plen;
182
+
183
+ rb_check_type(v, T_STRING);
184
+ if (ERR_OK != gql_init(err)) {
185
+ return err->code;
186
+ }
187
+ path = StringValuePtr(v);
188
+ plen = (long)RSTRING_LEN(v);
189
+ if ((int)sizeof(schema_path) - 8 < plen) {
190
+ rb_raise(rb_eArgError, "A graphql schema path is limited to %d characters.", (int)(sizeof(schema_path) - 8));
191
+ }
192
+ memcpy(schema_path, path, plen);
193
+ memcpy(schema_path + plen, "/schema", 8);
194
+
195
+ hook = hook_func_create(GET, schema_path, gql_dump_hook, &the_server.eval_queue);
196
+ hook->next = the_server.hooks;
197
+ the_server.hooks = hook;
198
+
199
+ hook = hook_func_create(GET, path, gql_eval_hook, &the_server.eval_queue);
200
+ hook->next = the_server.hooks;
201
+ the_server.hooks = hook;
202
+ }
176
203
  if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("quiet"))))) {
177
204
  if (Qtrue == v) {
178
205
  info_cat.on = false;
@@ -838,6 +865,7 @@ handle(VALUE self, VALUE method, VALUE pattern, VALUE handler) {
838
865
  Hook hook;
839
866
  Method meth = ALL;
840
867
  const char *pat;
868
+ ID static_id = rb_intern("static?");
841
869
 
842
870
  rb_check_type(pattern, T_STRING);
843
871
  pat = StringValuePtr(pattern);
@@ -861,6 +889,42 @@ handle(VALUE self, VALUE method, VALUE pattern, VALUE handler) {
861
889
  } else {
862
890
  rb_raise(rb_eArgError, "invalid method");
863
891
  }
892
+ if (T_STRING == rb_type(handler)) {
893
+ handler = resolve_classpath(StringValuePtr(handler), RSTRING_LEN(handler));
894
+ }
895
+ if (rb_respond_to(handler, static_id)) {
896
+ if (Qtrue == rb_funcall(handler, static_id, 0, Qnil)) {
897
+ VALUE res = rb_funcall(handler, call_id, 1, Qnil);
898
+ VALUE bv;
899
+
900
+ rb_check_type(res, T_ARRAY);
901
+ if (3 != RARRAY_LEN(res)) {
902
+ rb_raise(rb_eArgError, "a rack call() response must be an array of 3 members.");
903
+ }
904
+ bv = rb_ary_entry(res, 2);
905
+ if (T_ARRAY == rb_type(bv)) {
906
+ int i;
907
+ int bcnt = (int)RARRAY_LEN(bv);
908
+ Text t = text_allocate(1024);
909
+ struct _Err err = ERR_INIT;
910
+ VALUE v;
911
+
912
+ if (NULL == t) {
913
+ rb_raise(rb_eArgError, "failed to allocate response.");
914
+ }
915
+ for (i = 0; i < bcnt; i++) {
916
+ v = rb_ary_entry(bv, i);
917
+ t = text_append(t, StringValuePtr(v), (int)RSTRING_LEN(v));
918
+ }
919
+ if (NULL == page_immutable(&err, pat, t->text, t->len)) {
920
+ rb_raise(rb_eArgError, "%s", err.msg);
921
+ }
922
+ text_release(t);
923
+
924
+ return Qnil;
925
+ }
926
+ }
927
+ }
864
928
  if (NULL == (hook = rhook_create(meth, pat, handler, &the_server.eval_queue))) {
865
929
  rb_raise(rb_eStandardError, "out of memory.");
866
930
  } else {
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
2
 
3
- #ifndef __AGOO_RSERVER_H__
4
- #define __AGOO_RSERVER_H__
3
+ #ifndef AGOO_RSERVER_H
4
+ #define AGOO_RSERVER_H
5
5
 
6
6
  #include <ruby.h>
7
7
 
@@ -18,4 +18,4 @@ extern struct _RServer the_rserver;
18
18
  extern void server_init(VALUE mod);
19
19
  extern VALUE rserver_shutdown(VALUE self);
20
20
 
21
- #endif // __AGOO_RSERVER_H__
21
+ #endif // AGOO_RSERVER_H
@@ -47,7 +47,7 @@ extract_subject(VALUE subject, int *slen) {
47
47
  break;
48
48
  case T_SYMBOL:
49
49
  subj = rb_id2name(rb_sym2id(subject));
50
- *slen = strlen(subj);
50
+ *slen = (int)strlen(subj);
51
51
  break;
52
52
  default:
53
53
  subject = rb_funcall(subject, to_s_id, 0);
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
2
 
3
- #ifndef __AGOO_RUPGRADED_H__
4
- #define __AGOO_RUPGRADED_H__
3
+ #ifndef AGOO_RUPGRADED_H
4
+ #define AGOO_RUPGRADED_H
5
5
 
6
6
  #include <ruby.h>
7
7
 
@@ -14,4 +14,4 @@ extern Upgraded rupgraded_create(struct _Con *c, VALUE obj, VALUE env);
14
14
 
15
15
  extern const char* extract_subject(VALUE subject, int *slen);
16
16
 
17
- #endif // __AGOO_RUPGRADED_H__
17
+ #endif // AGOO_RUPGRADED_H
@@ -0,0 +1,334 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include <stdio.h>
4
+ #include <string.h>
5
+
6
+ #include "doc.h"
7
+ #include "gqlvalue.h"
8
+ #include "graphql.h"
9
+ #include "sdl.h"
10
+
11
+ static int
12
+ extract_name(Err err, Doc doc, const char *key, int klen, char *name, size_t max) {
13
+ const char *start;
14
+ size_t nlen;
15
+
16
+ if (0 != strncmp(doc->cur, key, klen)) {
17
+ return doc_err(doc, err, "Expected %s key word", key);
18
+ }
19
+ doc->cur += klen;
20
+ if (0 == doc_skip_white(doc)) {
21
+ return doc_err(doc, err, "Expected %s key word", key);
22
+ }
23
+ start = doc->cur;
24
+ doc_read_token(doc);
25
+ if (doc->cur == start) {
26
+ return doc_err(doc, err, "Name not provided");
27
+ }
28
+ nlen = doc->cur - start;
29
+ if (max <= nlen) {
30
+ return doc_err(doc, err, "Name too long");
31
+ }
32
+ strncpy(name, start, nlen);
33
+ name[nlen] = '\0';
34
+
35
+ return ERR_OK;
36
+ }
37
+
38
+ static int
39
+ make_scalar(Err err, Doc doc, const char *desc, int len) {
40
+ char name[256];
41
+
42
+ if (ERR_OK != extract_name(err, doc, "scalar", 6, name, sizeof(name))) {
43
+ return err->code;
44
+ }
45
+ gql_scalar_create(err, name, desc, len, false);
46
+
47
+ return ERR_OK;
48
+ }
49
+
50
+ static int
51
+ make_enum(Err err, Doc doc, const char *desc, int len) {
52
+ char name[256];
53
+ const char *start;
54
+ gqlType type;
55
+
56
+ if (ERR_OK != extract_name(err, doc, "enum", 4, name, sizeof(name))) {
57
+ return err->code;
58
+ }
59
+ doc_skip_white(doc);
60
+ if ('{' != *doc->cur) {
61
+ return doc_err(doc, err, "Expected '{'");
62
+ }
63
+ doc->cur++;
64
+
65
+ if (NULL == (type = gql_enum_create(err, name, desc, len, false))) {
66
+ return err->code;
67
+ }
68
+ while (doc->cur < doc->end) {
69
+ doc_skip_white(doc);
70
+ start = doc->cur;
71
+ doc_read_token(doc);
72
+ if (doc->cur == start) {
73
+ if ('}' == *doc->cur) {
74
+ doc->cur++;
75
+ break;
76
+ }
77
+ return doc_err(doc, err, "Invalid Enum value");
78
+ }
79
+ if (ERR_OK != gql_enum_add(err, type, start, (int)(doc->cur - start))) {
80
+ return err->code;
81
+ }
82
+ }
83
+ return ERR_OK;
84
+ }
85
+
86
+ static int
87
+ make_union(Err err, Doc doc, const char *desc, int len) {
88
+ char name[256];
89
+ const char *start;
90
+ gqlType type;
91
+
92
+ if (ERR_OK != extract_name(err, doc, "union", 5, name, sizeof(name))) {
93
+ return err->code;
94
+ }
95
+ doc_skip_white(doc);
96
+ if ('=' != *doc->cur) {
97
+ return doc_err(doc, err, "Expected '='");
98
+ }
99
+ doc->cur++;
100
+ doc_skip_white(doc);
101
+
102
+ if (NULL == (type = gql_union_create(err, name, desc, len, false))) {
103
+ return err->code;
104
+ }
105
+ while (doc->cur < doc->end) {
106
+ doc_skip_white(doc);
107
+ start = doc->cur;
108
+ doc_read_token(doc);
109
+ if (ERR_OK != gql_union_add(err, type, start, (int)(doc->cur - start))) {
110
+ return err->code;
111
+ }
112
+ doc_skip_white(doc);
113
+ if ('|' != *doc->cur) {
114
+ break;
115
+ }
116
+ doc->cur++; // skip |
117
+ }
118
+ return ERR_OK;
119
+ }
120
+
121
+ static int
122
+ make_arg(Err err, Doc doc, gqlDir dir) {
123
+ char name[256];
124
+ char type_name[256];
125
+ const char *start;
126
+ const char *desc = NULL;
127
+ const char *desc_end = NULL;
128
+ size_t nlen;
129
+ bool required = false;
130
+ gqlValue dv = NULL;
131
+
132
+ doc_skip_white(doc);
133
+ if ('"' == *doc->cur) {
134
+ desc = doc->cur + 1;
135
+ if (ERR_OK != doc_read_string(err, doc)) {
136
+ return err->code;
137
+ }
138
+ if ('"' == *desc) { // must be a """
139
+ desc += 2;
140
+ desc_end = doc->cur - 3;
141
+ } else {
142
+ desc_end = doc->cur - 1;
143
+ }
144
+ }
145
+ doc_skip_white(doc);
146
+ start = doc->cur;
147
+ doc_read_token(doc);
148
+ if (doc->cur == start) {
149
+ return doc_err(doc, err, "Argument name not provided");
150
+ }
151
+ if (':' != *doc->cur) {
152
+ return doc_err(doc, err, "Expected ':'");
153
+ }
154
+ nlen = doc->cur - start;
155
+ if (sizeof(name) <= nlen) {
156
+ return doc_err(doc, err, "Name too long");
157
+ }
158
+ strncpy(name, start, nlen);
159
+ name[nlen] = '\0';
160
+ doc->cur++;
161
+
162
+ // read type
163
+ doc_skip_white(doc);
164
+ start = doc->cur;
165
+ doc_read_token(doc);
166
+ if (doc->cur == start) {
167
+ return doc_err(doc, err, "Argument type not provided");
168
+ }
169
+ nlen = doc->cur - start;
170
+ if (sizeof(type_name) <= nlen) {
171
+ return doc_err(doc, err, "Type name too long");
172
+ }
173
+ strncpy(type_name, start, nlen);
174
+ type_name[nlen] = '\0';
175
+
176
+ doc_skip_white(doc);
177
+ if ('!' == *doc->cur) {
178
+ required = true;
179
+ } else if ('=' == *doc->cur) {
180
+ if (NULL == (dv = doc_read_value(err, doc))) {
181
+ return err->code;
182
+ }
183
+ }
184
+ doc_skip_white(doc);
185
+ if ('@' == *doc->cur) {
186
+ // TBD directive
187
+ }
188
+ if (NULL == gql_dir_arg(err, dir, name, type_name, desc, (int)(desc_end - desc), dv, required)) {
189
+ return err->code;
190
+ }
191
+ return ERR_OK;
192
+ }
193
+
194
+ static int
195
+ make_directive(Err err, Doc doc, const char *desc, int len) {
196
+ char name[256];
197
+ const char *start;
198
+ gqlDir dir;
199
+ size_t nlen;
200
+
201
+ if (0 != strncmp(doc->cur, "directive", 9)) {
202
+ return doc_err(doc, err, "Expected directive key word");
203
+ }
204
+ doc->cur += 9;
205
+ if (0 == doc_skip_white(doc)) {
206
+ return doc_err(doc, err, "Expected directive key word");
207
+ }
208
+ if ('@' != *doc->cur) {
209
+ return doc_err(doc, err, "Expected '@'");
210
+ }
211
+ doc->cur++;
212
+ start = doc->cur;
213
+ doc_read_token(doc);
214
+ if (doc->cur == start) {
215
+ return doc_err(doc, err, "Name not provided");
216
+ }
217
+ nlen = doc->cur - start;
218
+ if (sizeof(name) <= nlen) {
219
+ return doc_err(doc, err, "Name too long");
220
+ }
221
+ strncpy(name, start, nlen);
222
+ name[nlen] = '\0';
223
+ if (NULL == (dir = gql_directive_create(err, name, desc, len, false))) {
224
+ return err->code;
225
+ }
226
+ if ('(' == *doc->cur) {
227
+ while (doc->cur < doc->end) {
228
+ if (ERR_OK != make_arg(err, doc, dir)) {
229
+ return err->code;
230
+ }
231
+ doc_skip_white(doc);
232
+ if (')' == *doc->cur) {
233
+ doc->cur++;
234
+ break;
235
+ }
236
+ }
237
+ }
238
+ doc_skip_white(doc);
239
+ start = doc->cur;
240
+ doc_read_token(doc);
241
+
242
+ if (2 != doc->cur - start || 0 != strncmp(start, "on", 2)) {
243
+ return doc_err(doc, err, "Expected 'on'");
244
+ }
245
+ while (doc->cur < doc->end) {
246
+ doc_skip_white(doc);
247
+ start = doc->cur;
248
+ doc_read_token(doc);
249
+ if (ERR_OK != gql_directive_on(err, dir, start, (int)(doc->cur - start))) {
250
+ return err->code;
251
+ }
252
+ doc_skip_white(doc);
253
+ if ('|' != *doc->cur) {
254
+ break;
255
+ }
256
+ doc->cur++; // skip |
257
+ }
258
+ return ERR_OK;
259
+ }
260
+
261
+ int
262
+ sdl_parse(Err err, const char *str, int len) {
263
+ struct _Doc doc;
264
+ const char *desc = NULL;
265
+ const char *desc_end = NULL;
266
+
267
+ doc_init(&doc, str, len);
268
+
269
+ while (doc.cur < doc.end) {
270
+ doc_next_token(&doc);
271
+ switch (*doc.cur) {
272
+ case '"':
273
+ desc = doc.cur + 1;
274
+ if (ERR_OK != doc_read_string(err, &doc)) {
275
+ return err->code;
276
+ }
277
+ if ('"' == *desc) { // must be a """
278
+ desc += 2;
279
+ desc_end = doc.cur - 3;
280
+ } else {
281
+ desc_end = doc.cur - 1;
282
+ }
283
+ break;
284
+ case 's': // scalar
285
+ if (ERR_OK != make_scalar(err, &doc, desc, (int)(desc_end - desc))) {
286
+ return err->code;
287
+ }
288
+ break;
289
+ case 'e': // enum, and extend interface or type
290
+ if (4 < (doc.end - doc.cur)) {
291
+ if ('n' == doc.cur[1]) {
292
+ if (ERR_OK != make_enum(err, &doc, desc, (int)(desc_end - desc))) {
293
+ return err->code;
294
+ }
295
+ break;
296
+ } else {
297
+ // TBD extend
298
+ break;
299
+ }
300
+ }
301
+ return doc_err(&doc, err, "Unknown directive");
302
+ case 'u': // union
303
+ if (ERR_OK != make_union(err, &doc, desc, (int)(desc_end - desc))) {
304
+ return err->code;
305
+ }
306
+ break;
307
+ case 'd': // directive
308
+ if (ERR_OK != make_directive(err, &doc, desc, (int)(desc_end - desc))) {
309
+ return err->code;
310
+ }
311
+ break;
312
+ case 't': // type
313
+ break;
314
+ case 'i': // interface, input
315
+ if (5 < (doc.end - doc.cur) && 'n' == doc.cur[1]) {
316
+ if ('p' == doc.cur[2]) {
317
+ // input
318
+ break;
319
+ } else {
320
+ // TBD interface
321
+ break;
322
+ }
323
+ }
324
+ return doc_err(&doc, err, "Unknown directive");
325
+ case 'f': // fragment
326
+ break;
327
+ case '\0':
328
+ return ERR_OK;
329
+ default:
330
+ return doc_err(&doc, err, "Unknown directive");
331
+ }
332
+ }
333
+ return ERR_OK;
334
+ }