agoo 2.5.7 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of agoo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +38 -0
- data/ext/agoo/agoo.c +11 -2
- data/ext/agoo/bind.c +15 -20
- data/ext/agoo/con.c +32 -25
- data/ext/agoo/debug.c +225 -162
- data/ext/agoo/debug.h +31 -51
- data/ext/agoo/doc.c +278 -5
- data/ext/agoo/doc.h +6 -1
- data/ext/agoo/err.c +1 -0
- data/ext/agoo/err.h +1 -0
- data/ext/agoo/error_stream.c +3 -6
- data/ext/agoo/gqlcobj.c +12 -0
- data/ext/agoo/gqlcobj.h +25 -0
- data/ext/agoo/gqleval.c +520 -0
- data/ext/agoo/gqleval.h +49 -0
- data/ext/agoo/gqlintro.c +1237 -97
- data/ext/agoo/gqlintro.h +8 -0
- data/ext/agoo/gqljson.c +460 -0
- data/ext/agoo/gqljson.h +15 -0
- data/ext/agoo/gqlvalue.c +679 -136
- data/ext/agoo/gqlvalue.h +29 -7
- data/ext/agoo/graphql.c +841 -362
- data/ext/agoo/graphql.h +180 -90
- data/ext/agoo/hook.c +8 -16
- data/ext/agoo/http.c +3 -4
- data/ext/agoo/log.c +22 -25
- data/ext/agoo/log.h +1 -0
- data/ext/agoo/page.c +24 -40
- data/ext/agoo/pub.c +23 -21
- data/ext/agoo/queue.c +2 -4
- data/ext/agoo/ready.c +9 -9
- data/ext/agoo/req.c +80 -5
- data/ext/agoo/req.h +2 -0
- data/ext/agoo/res.c +1 -3
- data/ext/agoo/rgraphql.c +753 -0
- data/ext/agoo/rresponse.c +9 -15
- data/ext/agoo/rserver.c +18 -17
- data/ext/agoo/sdl.c +1264 -120
- data/ext/agoo/sdl.h +8 -1
- data/ext/agoo/sectime.c +136 -0
- data/ext/agoo/sectime.h +19 -0
- data/ext/agoo/server.c +1 -3
- data/ext/agoo/subject.c +2 -4
- data/ext/agoo/text.c +124 -18
- data/ext/agoo/text.h +5 -1
- data/ext/agoo/upgraded.c +2 -4
- data/lib/agoo/version.rb +1 -1
- data/test/base_handler_test.rb +43 -40
- data/test/bind_test.rb +49 -48
- data/test/graphql_test.rb +1019 -0
- data/test/hijack_test.rb +1 -1
- data/test/rack_handler_test.rb +40 -34
- data/test/static_test.rb +33 -32
- metadata +17 -6
data/ext/agoo/graphql.h
CHANGED
@@ -5,11 +5,15 @@
|
|
5
5
|
|
6
6
|
#include <stdbool.h>
|
7
7
|
#include <stdint.h>
|
8
|
+
#include <stdlib.h>
|
8
9
|
|
9
10
|
#include "err.h"
|
11
|
+
#include "gqlcobj.h"
|
12
|
+
#include "gqleval.h"
|
10
13
|
#include "text.h"
|
11
14
|
|
12
15
|
typedef enum {
|
16
|
+
GQL_UNDEF = (int8_t)0, // not defined yet
|
13
17
|
GQL_OBJECT = (int8_t)1,
|
14
18
|
GQL_FRAG = (int8_t)2,
|
15
19
|
GQL_INPUT = (int8_t)3,
|
@@ -17,10 +21,32 @@ typedef enum {
|
|
17
21
|
GQL_INTERFACE = (int8_t)5,
|
18
22
|
GQL_ENUM = (int8_t)6,
|
19
23
|
GQL_SCALAR = (int8_t)7,
|
20
|
-
|
21
|
-
|
24
|
+
GQL_LIST = (int8_t)8,
|
25
|
+
GQL_NON_NULL = (int8_t)9,
|
22
26
|
} gqlKind;
|
23
27
|
|
28
|
+
typedef enum {
|
29
|
+
GQL_SCALAR_UNDEF = (int8_t)0, // not defined yet
|
30
|
+
GQL_SCALAR_NULL = (int8_t)1,
|
31
|
+
GQL_SCALAR_BOOL = (int8_t)2,
|
32
|
+
GQL_SCALAR_INT = (int8_t)3,
|
33
|
+
GQL_SCALAR_I64 = (int8_t)4,
|
34
|
+
GQL_SCALAR_FLOAT = (int8_t)5,
|
35
|
+
GQL_SCALAR_STRING = (int8_t)6,
|
36
|
+
GQL_SCALAR_TOKEN = (int8_t)7,
|
37
|
+
GQL_SCALAR_VAR = (int8_t)8,
|
38
|
+
GQL_SCALAR_TIME = (int8_t)9,
|
39
|
+
GQL_SCALAR_UUID = (int8_t)10,
|
40
|
+
GQL_SCALAR_LIST = (int8_t)11,
|
41
|
+
GQL_SCALAR_OBJECT = (int8_t)12,
|
42
|
+
} gqlScalarKind;
|
43
|
+
|
44
|
+
typedef enum {
|
45
|
+
GQL_QUERY = (int8_t)'Q',
|
46
|
+
GQL_MUTATION = (int8_t)'M',
|
47
|
+
GQL_SUBSCRIPTION = (int8_t)'S',
|
48
|
+
} gqlOpKind;
|
49
|
+
|
24
50
|
struct _agooCon;
|
25
51
|
struct _agooReq;
|
26
52
|
struct _gqlDirUse;
|
@@ -30,8 +56,6 @@ struct _gqlLink;
|
|
30
56
|
struct _gqlType;
|
31
57
|
struct _gqlValue;
|
32
58
|
|
33
|
-
// Used for references to implemenation entities.
|
34
|
-
typedef void* gqlRef;
|
35
59
|
typedef struct _gqlQuery {
|
36
60
|
struct _gqlQuery *next;
|
37
61
|
struct _gqlValue *result;
|
@@ -39,11 +63,6 @@ typedef struct _gqlQuery {
|
|
39
63
|
// TBD data about a query, mutation, or subscription
|
40
64
|
} *gqlQuery;
|
41
65
|
|
42
|
-
typedef struct _gqlKeyVal {
|
43
|
-
const char *key;
|
44
|
-
struct _gqlValue *value;
|
45
|
-
} *gqlKeyVal;
|
46
|
-
|
47
66
|
typedef struct _gqlStrLink {
|
48
67
|
struct _gqlStrLink *next;
|
49
68
|
char *str;
|
@@ -51,24 +70,20 @@ typedef struct _gqlStrLink {
|
|
51
70
|
|
52
71
|
typedef struct _gqlTypeLink {
|
53
72
|
struct _gqlTypeLink *next;
|
54
|
-
char *name;
|
55
73
|
struct _gqlType *type;
|
56
74
|
} *gqlTypeLink;
|
57
75
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
typedef void (*gqlIterCb)(gqlRef ref, gqlQuery query);
|
65
|
-
typedef void (*gqlIterateFunc)(gqlRef ref, gqlIterCb cb, gqlQuery query);
|
76
|
+
typedef struct _gqlEnumVal {
|
77
|
+
struct _gqlEnumVal *next;
|
78
|
+
const char *value;
|
79
|
+
const char *desc;
|
80
|
+
struct _gqlDirUse *dir;
|
81
|
+
} *gqlEnumVal;
|
66
82
|
|
67
83
|
typedef struct _gqlArg {
|
68
84
|
struct _gqlArg *next;
|
69
85
|
const char *name;
|
70
86
|
const char *desc;
|
71
|
-
const char *type_name;
|
72
87
|
struct _gqlType *type;
|
73
88
|
struct _gqlValue *default_value;
|
74
89
|
struct _gqlDirUse *dir;
|
@@ -80,14 +95,10 @@ typedef struct _gqlField {
|
|
80
95
|
const char *name;
|
81
96
|
struct _gqlType *type; // return type
|
82
97
|
const char *desc;
|
83
|
-
const char *reason; // deprecationReason
|
84
98
|
gqlArg args;
|
85
99
|
struct _gqlDirUse *dir;
|
86
|
-
|
100
|
+
struct _gqlValue *default_value;
|
87
101
|
bool required;
|
88
|
-
bool list;
|
89
|
-
bool not_empty; // list can not be empty, member is required
|
90
|
-
bool deprecated;
|
91
102
|
} *gqlField;
|
92
103
|
|
93
104
|
typedef struct _gqlDir {
|
@@ -96,104 +107,183 @@ typedef struct _gqlDir {
|
|
96
107
|
const char *desc;
|
97
108
|
gqlArg args;
|
98
109
|
gqlStrLink locs; // location names
|
99
|
-
bool
|
110
|
+
bool defined;
|
111
|
+
bool core;
|
100
112
|
} *gqlDir;
|
101
113
|
|
102
114
|
typedef struct _gqlDirUse {
|
115
|
+
struct _gqlDirUse *next;
|
103
116
|
gqlDir dir;
|
104
117
|
struct _gqlLink *args;
|
105
118
|
} *gqlDirUse;
|
106
119
|
|
107
120
|
typedef struct _gqlType {
|
108
|
-
const char
|
109
|
-
const char
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
bool
|
114
|
-
bool core;
|
121
|
+
const char *name;
|
122
|
+
const char *desc;
|
123
|
+
gqlDirUse dir;
|
124
|
+
gqlKind kind;
|
125
|
+
gqlScalarKind scalar_kind;
|
126
|
+
bool core;
|
115
127
|
union {
|
116
|
-
struct { // Objects
|
128
|
+
struct { // Objects and interfaces
|
117
129
|
gqlField fields;
|
118
|
-
|
119
|
-
struct _gqlType **interfaces; // Objects, null terminated array if not NULL.
|
120
|
-
struct _gqlType *on; // Fragment
|
121
|
-
};
|
130
|
+
gqlTypeLink interfaces; // Types
|
122
131
|
};
|
123
|
-
gqlTypeLink types;
|
124
|
-
|
125
|
-
//
|
126
|
-
struct {
|
127
|
-
|
132
|
+
gqlTypeLink types; // Union
|
133
|
+
gqlEnumVal choices; // Enums
|
134
|
+
gqlArg args; // InputObject
|
135
|
+
struct { // scalar
|
136
|
+
agooText (*to_sdl)(agooText text, struct _gqlValue *value, int indent, int depth);
|
137
|
+
agooText (*to_json)(agooText text, struct _gqlValue *value, int indent, int depth);
|
128
138
|
void (*destroy)(struct _gqlValue *value);
|
129
139
|
};
|
140
|
+
struct { // List types
|
141
|
+
struct _gqlType *base;
|
142
|
+
bool not_empty;
|
143
|
+
};
|
130
144
|
};
|
131
145
|
} *gqlType;
|
132
146
|
|
147
|
+
// Execution Definition types
|
148
|
+
struct _gqlFrag;
|
149
|
+
|
150
|
+
typedef struct _gqlVar {
|
151
|
+
struct _gqlVar *next;
|
152
|
+
const char *name;
|
153
|
+
gqlType type;
|
154
|
+
struct _gqlValue *value;
|
155
|
+
} *gqlVar;
|
156
|
+
|
157
|
+
typedef struct _gqlSelArg {
|
158
|
+
struct _gqlSelArg *next;
|
159
|
+
const char *name;
|
160
|
+
gqlVar var;
|
161
|
+
struct _gqlValue *value;
|
162
|
+
} *gqlSelArg;
|
163
|
+
|
164
|
+
typedef struct _gqlSel {
|
165
|
+
struct _gqlSel *next;
|
166
|
+
const char *alias;
|
167
|
+
const char *name;
|
168
|
+
gqlType type; // set with validation
|
169
|
+
gqlDirUse dir;
|
170
|
+
gqlSelArg args;
|
171
|
+
struct _gqlSel *sels;
|
172
|
+
const char *frag;
|
173
|
+
struct _gqlFrag *inline_frag;
|
174
|
+
} *gqlSel;
|
175
|
+
|
176
|
+
typedef struct _gqlOp {
|
177
|
+
struct _gqlOp *next;
|
178
|
+
const char *name;
|
179
|
+
gqlVar vars;
|
180
|
+
gqlDirUse dir;
|
181
|
+
gqlSel sels;
|
182
|
+
gqlOpKind kind;
|
183
|
+
} *gqlOp;
|
184
|
+
|
185
|
+
typedef struct _gqlFrag {
|
186
|
+
struct _gqlFrag *next;
|
187
|
+
const char *name;
|
188
|
+
gqlDirUse dir;
|
189
|
+
gqlType on;
|
190
|
+
gqlSel sels;
|
191
|
+
} *gqlFrag;
|
192
|
+
|
193
|
+
typedef struct _gqlFuncs {
|
194
|
+
gqlResolveFunc resolve; // TBD change
|
195
|
+
gqlTypeFunc type;
|
196
|
+
} *gqlFuncs;
|
197
|
+
|
198
|
+
typedef struct _gqlDoc {
|
199
|
+
gqlOp ops;
|
200
|
+
gqlVar vars;
|
201
|
+
gqlFrag frags;
|
202
|
+
gqlOp op; // the op to execute
|
203
|
+
struct _gqlFuncs funcs;
|
204
|
+
} *gqlDoc;
|
205
|
+
|
133
206
|
extern int gql_init(agooErr err);
|
134
207
|
extern void gql_destroy(); // clear out all
|
135
208
|
|
136
|
-
extern gqlType gql_type_create(agooErr err, const char *name, const char *desc,
|
137
|
-
extern gqlType
|
138
|
-
extern
|
139
|
-
|
209
|
+
extern gqlType gql_type_create(agooErr err, const char *name, const char *desc, size_t dlen, gqlTypeLink interfaces);
|
210
|
+
extern gqlType gql_assure_type(agooErr err, const char *name);
|
211
|
+
extern void gql_type_directive_use(gqlType type, gqlDirUse use);
|
212
|
+
|
213
|
+
extern gqlType gql_input_create(agooErr err, const char *name, const char *desc, size_t dlen);
|
214
|
+
extern gqlType gql_interface_create(agooErr err, const char *name, const char *desc, size_t dlen);
|
140
215
|
|
141
216
|
extern gqlField gql_type_field(agooErr err,
|
142
217
|
gqlType type,
|
143
218
|
const char *name,
|
144
219
|
gqlType return_type,
|
220
|
+
struct _gqlValue *default_value,
|
145
221
|
const char *desc,
|
146
|
-
|
147
|
-
bool
|
148
|
-
bool not_empty,
|
149
|
-
gqlResolveFunc resolve);
|
222
|
+
size_t dlen,
|
223
|
+
bool required);
|
150
224
|
|
151
225
|
extern gqlArg gql_field_arg(agooErr err,
|
152
226
|
gqlField field,
|
153
227
|
const char *name,
|
154
|
-
gqlType
|
228
|
+
gqlType type,
|
155
229
|
const char *desc,
|
230
|
+
size_t dlen,
|
156
231
|
struct _gqlValue *def_value,
|
157
232
|
bool required);
|
158
233
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
extern
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
extern
|
175
|
-
|
176
|
-
extern gqlType
|
177
|
-
extern int
|
178
|
-
|
179
|
-
extern gqlType
|
180
|
-
extern
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
extern
|
185
|
-
extern
|
186
|
-
|
187
|
-
extern
|
188
|
-
|
189
|
-
extern agooText
|
190
|
-
|
191
|
-
extern agooText
|
192
|
-
|
193
|
-
extern agooText
|
194
|
-
extern agooText
|
195
|
-
|
196
|
-
|
197
|
-
extern
|
234
|
+
extern gqlType gql_scalar_create(agooErr err, const char *name, const char *desc, size_t dlen);
|
235
|
+
|
236
|
+
extern gqlDir gql_directive_create(agooErr err, const char *name, const char *desc, size_t dlen);
|
237
|
+
extern gqlArg gql_dir_arg(agooErr err,
|
238
|
+
gqlDir dir,
|
239
|
+
const char *name,
|
240
|
+
gqlType type,
|
241
|
+
const char *desc,
|
242
|
+
size_t dlen,
|
243
|
+
struct _gqlValue *def_value,
|
244
|
+
bool required);
|
245
|
+
extern int gql_directive_on(agooErr err, gqlDir d, const char *on, int len);
|
246
|
+
extern gqlDir gql_directive_get(const char *name);
|
247
|
+
|
248
|
+
extern gqlDirUse gql_dir_use_create(agooErr err, const char *name);
|
249
|
+
extern int gql_dir_use_arg(agooErr err, gqlDirUse use, const char *key, struct _gqlValue *value);
|
250
|
+
|
251
|
+
extern gqlType gql_union_create(agooErr err, const char *name, const char *desc, size_t dlen);
|
252
|
+
extern int gql_union_add(agooErr err, gqlType type, gqlType member);
|
253
|
+
|
254
|
+
extern gqlType gql_enum_create(agooErr err, const char *name, const char *desc, size_t dlen);
|
255
|
+
extern gqlEnumVal gql_enum_append(agooErr err, gqlType type, const char *value, size_t len, const char *desc, size_t dlen);
|
256
|
+
|
257
|
+
extern gqlType gql_assure_list(agooErr err, gqlType base, bool not_empty);
|
258
|
+
|
259
|
+
extern int gql_type_set(agooErr err, gqlType type);
|
260
|
+
extern gqlType gql_type_get(const char *name);
|
261
|
+
extern void gql_type_destroy(gqlType type);
|
262
|
+
extern void gql_type_iterate(void (*fun)(gqlType type, void *ctx), void *ctx);
|
263
|
+
|
264
|
+
extern agooText gql_type_sdl(agooText text, gqlType type, bool comments);
|
265
|
+
extern agooText gql_directive_sdl(agooText text, gqlDir dir, bool comments);
|
266
|
+
extern agooText gql_schema_sdl(agooText text, bool with_desc, bool all);
|
267
|
+
|
268
|
+
extern agooText gql_object_to_graphql(agooText text, struct _gqlValue *value, int indent, int depth);
|
269
|
+
extern agooText gql_union_to_text(agooText text, struct _gqlValue *value, int indent, int depth);
|
270
|
+
extern agooText gql_enum_to_text(agooText text, struct _gqlValue *value, int indent, int depth);
|
271
|
+
|
272
|
+
extern gqlDoc gql_doc_create(agooErr err);
|
273
|
+
extern void gql_doc_destroy(gqlDoc doc);
|
274
|
+
|
275
|
+
extern gqlOp gql_op_create(agooErr err, const char *name, gqlOpKind kind);
|
276
|
+
extern gqlFrag gql_fragment_create(agooErr err, const char *name, gqlType on);
|
277
|
+
extern agooText gql_doc_sdl(gqlDoc doc, agooText text);
|
278
|
+
|
279
|
+
extern void gql_dump_hook(struct _agooReq *req);
|
280
|
+
extern void gql_eval_get_hook(struct _agooReq *req);
|
281
|
+
extern void gql_eval_post_hook(struct _agooReq *req);
|
282
|
+
|
283
|
+
extern int gql_validate(agooErr err);
|
284
|
+
|
285
|
+
extern gqlField gql_type_get_field(gqlType type, const char *field);
|
286
|
+
|
287
|
+
extern gqlDir gql_directives; // linked list
|
198
288
|
|
199
289
|
#endif // AGOO_GRAPHQL_H
|
data/ext/agoo/hook.c
CHANGED
@@ -10,23 +10,20 @@
|
|
10
10
|
|
11
11
|
agooHook
|
12
12
|
agoo_hook_create(agooMethod method, const char *pattern, void *handler, agooHookType type, agooQueue q) {
|
13
|
-
agooHook hook = (agooHook)
|
13
|
+
agooHook hook = (agooHook)AGOO_MALLOC(sizeof(struct _agooHook));
|
14
14
|
|
15
15
|
if (NULL != hook) {
|
16
16
|
char *pat = NULL;
|
17
17
|
|
18
|
-
DEBUG_ALLOC(mem_hook, hook);
|
19
18
|
if (NULL == pattern) {
|
20
19
|
if (AGOO_NONE != method) {
|
21
|
-
pat =
|
20
|
+
pat = AGOO_STRDUP("");
|
22
21
|
}
|
23
22
|
} else {
|
24
|
-
pat =
|
23
|
+
pat = AGOO_STRDUP(pattern);
|
25
24
|
}
|
26
25
|
hook->pattern = pat;
|
27
|
-
|
28
26
|
hook->next = NULL;
|
29
|
-
DEBUG_ALLOC(mem_hook_pattern, hook->pattern)
|
30
27
|
hook->method = method;
|
31
28
|
hook->handler = handler;
|
32
29
|
hook->type = type;
|
@@ -38,23 +35,20 @@ agoo_hook_create(agooMethod method, const char *pattern, void *handler, agooHook
|
|
38
35
|
|
39
36
|
agooHook
|
40
37
|
agoo_hook_func_create(agooMethod method, const char *pattern, void (*func)(agooReq req), agooQueue q) {
|
41
|
-
agooHook hook = (agooHook)
|
38
|
+
agooHook hook = (agooHook)AGOO_MALLOC(sizeof(struct _agooHook));
|
42
39
|
|
43
40
|
if (NULL != hook) {
|
44
41
|
char *pat = NULL;
|
45
42
|
|
46
|
-
DEBUG_ALLOC(mem_hook, hook);
|
47
43
|
if (NULL == pattern) {
|
48
44
|
if (AGOO_NONE != method) {
|
49
|
-
pat =
|
45
|
+
pat = AGOO_STRDUP("");
|
50
46
|
}
|
51
47
|
} else {
|
52
|
-
pat =
|
48
|
+
pat = AGOO_STRDUP(pattern);
|
53
49
|
}
|
54
50
|
hook->pattern = pat;
|
55
|
-
|
56
51
|
hook->next = NULL;
|
57
|
-
DEBUG_ALLOC(mem_hook_pattern, hook->pattern)
|
58
52
|
hook->method = method;
|
59
53
|
hook->func = func;
|
60
54
|
hook->type = FUNC_HOOK;
|
@@ -67,11 +61,9 @@ agoo_hook_func_create(agooMethod method, const char *pattern, void (*func)(agooR
|
|
67
61
|
void
|
68
62
|
agoo_hook_destroy(agooHook hook) {
|
69
63
|
if (NULL != hook->pattern) {
|
70
|
-
|
71
|
-
free(hook->pattern);
|
64
|
+
AGOO_FREE(hook->pattern);
|
72
65
|
}
|
73
|
-
|
74
|
-
free(hook);
|
66
|
+
AGOO_FREE(hook);
|
75
67
|
}
|
76
68
|
|
77
69
|
bool
|
data/ext/agoo/http.c
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
3
|
#include <stdint.h>
|
4
|
+
#include <stdio.h>
|
4
5
|
#include <stdlib.h>
|
5
6
|
#include <string.h>
|
6
7
|
|
@@ -469,8 +470,7 @@ key_set(const char *key) {
|
|
469
470
|
Slot *bucket = get_bucketp(h);
|
470
471
|
Slot s;
|
471
472
|
|
472
|
-
if (NULL != (s = (Slot)
|
473
|
-
DEBUG_ALLOC(mem_http_slot, s)
|
473
|
+
if (NULL != (s = (Slot)AGOO_MALLOC(sizeof(struct _slot)))) {
|
474
474
|
s->hash = h;
|
475
475
|
s->klen = len;
|
476
476
|
s->key = key;
|
@@ -499,8 +499,7 @@ agoo_http_cleanup() {
|
|
499
499
|
for (i = BUCKET_SIZE; 0 < i; i--, sp++) {
|
500
500
|
for (s = *sp; NULL != s; s = n) {
|
501
501
|
n = s->next;
|
502
|
-
|
503
|
-
free(s);
|
502
|
+
AGOO_FREE(s);
|
504
503
|
}
|
505
504
|
*sp = NULL;
|
506
505
|
}
|