浏览代码

moar code gengit st

Tobias Waldekranz.com 8 年之前
父节点
当前提交
7d0344b16d
共有 10 个文件被更改,包括 251 次插入62 次删除
  1. 130 21
      global.c
  2. 90 5
      ir.c
  3. 7 16
      ir.h
  4. 1 1
      kprobe.c
  5. 2 9
      node.c
  6. 1 2
      node.h
  7. 5 5
      ply.c
  8. 10 0
      type.c
  9. 2 0
      type.h
  10. 3 3
      utils.c

+ 130 - 21
global.c

14
 
14
 
15
 /* . */
15
 /* . */
16
 
16
 
17
+static int global_dot_ir_pre(const struct func *func, struct node *n,
18
+				struct prog *prog)
19
+{
20
+	struct node *sou = n->expr.args;
21
+
22
+	if (node_is(sou, ":deref")) {
23
+		/* (*ptr).member, if *ptr is not already loaded let it
24
+		 * know that we're only interested in one member */
25
+		sou->sym->irs.hint.dot = 1;
26
+
27
+		/* this also means we need to put ourselves on the
28
+		 * stack since data will be loaded via probe_read */
29
+		n->sym->irs.hint.stack = 1;
30
+	}
31
+	return 0;
32
+}
33
+
34
+static int global_dot_ir_post(const struct func *func, struct node *n,
35
+				struct prog *prog)
36
+{
37
+	struct node *sou, *member;
38
+	struct irstate *dst;
39
+	ssize_t offset;
40
+
41
+	sou = n->expr.args;
42
+	member = sou->next;
43
+	dst = &n->sym->irs;
44
+	
45
+	ir_init_sym(prog->ir, n->sym);
46
+
47
+	offset = type_offsetof(type_base(sou->sym->type), member->string.data);
48
+	assert(offset >= 0);
49
+
50
+	if (!sou->sym->irs.loc) {
51
+		/* sou is a :deref which wasn't loaded by child, just
52
+		 * read the member we're interested in. */
53
+		struct node *ptr = sou->expr.args;
54
+
55
+		ir_emit_sym_to_reg(prog->ir, BPF_REG_3, ptr->sym);
56
+		ir_emit_insn(prog->ir, ALU_IMM(BPF_ADD, 0, offset), BPF_REG_3, 0);
57
+		goto probe_read;
58
+	}
59
+
60
+	offset += sou->sym->irs.stack;
61
+
62
+	if (dst->loc == LOC_REG) {
63
+		switch (dst->size) {
64
+		case 1:
65
+			ir_emit_insn(prog->ir, LDXB(0, offset, 0),
66
+				     dst->reg, BPF_REG_BP);
67
+			break;
68
+		case 2:
69
+			ir_emit_insn(prog->ir, LDXH(0, offset, 0),
70
+				     dst->reg, BPF_REG_BP);
71
+			break;
72
+		case 4:
73
+			ir_emit_insn(prog->ir, LDXW(0, offset, 0),
74
+				     dst->reg, BPF_REG_BP);
75
+			break;
76
+		case 8:
77
+			ir_emit_insn(prog->ir, LDXDW(0, offset, 0),
78
+				     dst->reg, BPF_REG_BP);
79
+			break;
80
+		default:
81
+			assert(0);
82
+		}
83
+
84
+		return 0;
85
+	}
86
+
87
+	ir_emit_insn(prog->ir, ALU_IMM(BPF_ADD, 0, offset), BPF_REG_3, 0);
88
+probe_read:
89
+	ir_emit_insn(prog->ir, MOV_IMM(0, (int32_t)dst->size), BPF_REG_2, 0);
90
+	ir_emit_insn(prog->ir, MOV(0, 0), BPF_REG_1, BPF_REG_BP);
91
+	ir_emit_insn(prog->ir, ALU_IMM(BPF_ADD, 0, dst->stack), BPF_REG_1, 0);
92
+	ir_emit_insn(prog->ir, CALL(BPF_FUNC_probe_read), 0, 0);
93
+	/* TODO if (r0) exit(r0); */
94
+	return 0;
95
+}
96
+
17
 static int global_dot_type_infer(const struct func *func, struct node *n)
97
 static int global_dot_type_infer(const struct func *func, struct node *n)
18
 {
98
 {
19
 	struct node *sou, *member;
99
 	struct node *sou, *member;
37
 		return -EINVAL;
117
 		return -EINVAL;
38
 	}
118
 	}
39
 
119
 
40
-	tfields_foreach(f, t->sou.fields) {
41
-		if (!strcmp(f->name, member->string.data)) {
42
-			/* given `sou.member` where sou is a
43
-			 * struct/union, infer that the expression's
44
-			 * type is equal to member's type. */
45
-			n->sym->type = f->type;
46
-			return 0;
47
-		}
120
+	f = tfields_get(t->sou.fields, member->string.data);
121
+	if (!f) {
122
+		_e("%#N: type '%T' has no member named %N.\n", n, t, member);
123
+		return -EINVAL;
48
 	}
124
 	}
49
 
125
 
50
-	_e("%#N: type '%T' has no member named %N.\n", n, t, member);
51
-	return -EINVAL;
126
+	/* given `sou.member` where sou is a struct/union, infer that
127
+	 * the expression's type is equal to member's type. */
128
+	n->sym->type = f->type;
129
+	return 0;
52
 }
130
 }
53
 
131
 
54
 
132
 
69
 		 * from our argument */
147
 		 * from our argument */
70
 		return 0;
148
 		return 0;
71
 
149
 
72
-	if (dst->loc)
73
-		return 0;
150
+	ir_init_sym(prog->ir, n->sym);
74
 
151
 
75
-	size = type_sizeof(type_base(ptr->sym->type)->ptr.type);
76
-	/* parent might have already allocated stack for us. typically
77
-	 * when the value is one part of a map key or value. */
78
-	if (!dst->hint.stack)
79
-		irs_alloc_stack(dst, prog->ir, size);
152
+	if (dst->hint.lval)
153
+		/* *ptr = val, whatever is in our storage now it will
154
+                    be overwritten, so skip the load. */
155
+		return 0;
80
 
156
 
81
 	ir_emit_insn(prog->ir, MOV(0, 0), BPF_REG_1, BPF_REG_BP);
157
 	ir_emit_insn(prog->ir, MOV(0, 0), BPF_REG_1, BPF_REG_BP);
82
 	ir_emit_insn(prog->ir, ALU_IMM(BPF_ADD, 0, dst->stack), BPF_REG_1, 0);
158
 	ir_emit_insn(prog->ir, ALU_IMM(BPF_ADD, 0, dst->stack), BPF_REG_1, 0);
83
-	ir_emit_insn(prog->ir, MOV_IMM(0, (int32_t)size), BPF_REG_2, 0);
84
-	ir_emit_mov_irs(prog->ir, BPF_REG_3, &ptr->sym->irs);
159
+	ir_emit_insn(prog->ir, MOV_IMM(0, (int32_t)dst->size), BPF_REG_2, 0);
160
+	ir_emit_sym_to_reg(prog->ir, BPF_REG_3, ptr->sym);
85
 	ir_emit_insn(prog->ir, CALL(BPF_FUNC_probe_read), 0, 0);
161
 	ir_emit_insn(prog->ir, CALL(BPF_FUNC_probe_read), 0, 0);
162
+	/* TODO if (r0) exit(r0); */
86
 	return 0;
163
 	return 0;
87
 }
164
 }
88
 
165
 
208
 		/* TODO do we need assignment expressions? */
285
 		/* TODO do we need assignment expressions? */
209
 		n->sym->type = &t_void;
286
 		n->sym->type = &t_void;
210
 		
287
 		
211
-		if (node_is_map(lval))
288
+		if (node_is(lval, "{}"))
212
 			return global_assign_type_infer_map(lval);
289
 			return global_assign_type_infer_map(lval);
213
 
290
 
214
 		return 0;
291
 		return 0;
229
 
306
 
230
 	lval = n->expr.args;
307
 	lval = n->expr.args;
231
 
308
 
232
-	if (node_is_map(lval) || (lval->ntype == N_IDENT))
309
+	if (node_is(lval, "{}") || (lval->ntype == N_IDENT))
233
 		return 0;
310
 		return 0;
234
 
311
 
235
 	_e("%#N: can't assign a value to %N.\n", n, lval);
312
 	_e("%#N: can't assign a value to %N.\n", n, lval);
285
 
362
 
286
 /* pid */
363
 /* pid */
287
 
364
 
365
+static int global_pid_ir_post(const struct func *func, struct node *n,
366
+				struct prog *prog)
367
+{
368
+	struct node *ptr = n->expr.args;
369
+
370
+	ir_init_sym(prog->ir, n->sym);
371
+
372
+	ir_emit_insn(prog->ir, CALL(BPF_FUNC_get_current_pid_tgid), 0, 0);
373
+	ir_emit_insn(prog->ir, ALU64_IMM(BPF_RSH, 0, 32), BPF_REG_0, 0);
374
+	ir_emit_reg_to_sym(prog->ir, n->sym, BPF_REG_0);
375
+	return 0;
376
+}
377
+
288
 struct type t_pid = {
378
 struct type t_pid = {
289
 	.ttype = T_TYPEDEF,
379
 	.ttype = T_TYPEDEF,
290
 
380
 
299
 
389
 
300
 /* time */
390
 /* time */
301
 
391
 
392
+static int global_time_ir_post(const struct func *func, struct node *n,
393
+			       struct prog *prog)
394
+{
395
+	struct node *ptr = n->expr.args;
396
+
397
+	ir_init_sym(prog->ir, n->sym);
398
+
399
+	ir_emit_insn(prog->ir, CALL(BPF_FUNC_ktime_get_ns), 0, 0);
400
+	ir_emit_reg_to_sym(prog->ir, n->sym, BPF_REG_0);
401
+	return 0;
402
+}
403
+
302
 struct type t_time = {
404
 struct type t_time = {
303
 	.ttype = T_TYPEDEF,	/* TODO: should be a T_FUNC with a static
405
 	.ttype = T_TYPEDEF,	/* TODO: should be a T_FUNC with a static
304
 				 * signature */
406
 				 * signature */
379
 		.name = ".",
481
 		.name = ".",
380
 		.type = &t_dot_func,
482
 		.type = &t_dot_func,
381
 		.type_infer = global_dot_type_infer,
483
 		.type_infer = global_dot_type_infer,
484
+
485
+		.ir_pre  = global_dot_ir_pre,
486
+		.ir_post = global_dot_ir_post,
382
 	},
487
 	},
383
 	{
488
 	{
384
 		.name = ":deref",
489
 		.name = ":deref",
416
 		.name = "pid",
521
 		.name = "pid",
417
 		.type = &t_pid_func,
522
 		.type = &t_pid_func,
418
 		.static_ret = 1,
523
 		.static_ret = 1,
524
+
525
+		.ir_post = global_pid_ir_post,
419
 	},
526
 	},
420
 	{
527
 	{
421
 		.name = "time",
528
 		.name = "time",
422
 		.type = &t_time_func,
529
 		.type = &t_time_func,
423
 		.static_ret = 1,
530
 		.static_ret = 1,
531
+
532
+		.ir_post = global_time_ir_post,
424
 	},
533
 	},
425
 
534
 
426
 	{
535
 	{

+ 90 - 5
ir.c

7
 
7
 
8
 #include "ir.h"
8
 #include "ir.h"
9
 #include "sym.h"
9
 #include "sym.h"
10
+#include "type.h"
10
 
11
 
11
 const uint16_t vreg_base = 0x8000;
12
 const uint16_t vreg_base = 0x8000;
12
 
13
 
331
 /* 	ir_emit(ir, &vi); */
332
 /* 	ir_emit(ir, &vi); */
332
 /* } */
333
 /* } */
333
 
334
 
334
-void ir_emit_mov_irs(struct ir *ir, uint16_t dst, struct irstate *src)
335
+void ir_emit_sym_to_reg(struct ir *ir, uint16_t dst, struct sym *src)
335
 {
336
 {
336
-	switch (src->loc) {
337
+	struct irstate *irs = &src->irs;
338
+
339
+	switch (irs->loc) {
337
 	case LOC_IMM:
340
 	case LOC_IMM:
338
-		ir_emit_insn(ir, MOV_IMM(0, src->imm), dst, 0);
341
+		ir_emit_insn(ir, MOV_IMM(0, irs->imm), dst, 0);
339
 		break;
342
 		break;
340
 	case LOC_REG:
343
 	case LOC_REG:
341
-		ir_emit_insn(ir, MOV(0, 0), dst, src->reg);
344
+		ir_emit_insn(ir, MOV(0, 0), dst, irs->reg);
342
 		break;
345
 		break;
343
 	case LOC_STACK:
346
 	case LOC_STACK:
344
-		ir_emit_insn(ir, LDX(0, src->stack, 0), dst, BPF_REG_BP);
347
+		switch (irs->size) {
348
+		case 1:
349
+			ir_emit_insn(ir, LDXB(0, irs->stack, 0),
350
+				     dst, BPF_REG_BP);
351
+			break;
352
+		case 2:
353
+			ir_emit_insn(ir, LDXH(0, irs->stack, 0),
354
+				     dst, BPF_REG_BP);
355
+			break;
356
+		case 4:
357
+			ir_emit_insn(ir, LDXW(0, irs->stack, 0),
358
+				     dst, BPF_REG_BP);
359
+			break;
360
+		case 8:
361
+			ir_emit_insn(ir, LDXDW(0, irs->stack, 0),
362
+				     dst, BPF_REG_BP);
363
+			break;
364
+		default:
365
+			assert(0);
366
+		}
345
 		break;
367
 		break;
368
+	default:
369
+		assert(0);
370
+	}
371
+}
346
 
372
 
373
+void ir_emit_reg_to_sym(struct ir *ir, struct sym *dst, uint16_t src)
374
+{
375
+	struct irstate *irs = &dst->irs;
376
+
377
+	switch (irs->loc) {
378
+	case LOC_REG:
379
+		ir_emit_insn(ir, MOV(0, 0), irs->reg, src);
380
+		break;
381
+	case LOC_STACK:
382
+		switch (irs->size) {
383
+		case 1:
384
+			ir_emit_insn(ir, STXB(0, irs->stack, 0),
385
+				     BPF_REG_BP, src);
386
+			break;
387
+		case 2:
388
+			ir_emit_insn(ir, STXH(0, irs->stack, 0),
389
+				     BPF_REG_BP, src);
390
+			break;
391
+		case 4:
392
+			ir_emit_insn(ir, STXW(0, irs->stack, 0),
393
+				     BPF_REG_BP, src);
394
+			break;
395
+		case 8:
396
+			ir_emit_insn(ir, STXDW(0, irs->stack, 0),
397
+				     BPF_REG_BP, src);
398
+			break;
399
+		default:
400
+			assert(0);
401
+		}
402
+		break;
347
 	default:
403
 	default:
348
 		assert(0);
404
 		assert(0);
349
 	}
405
 	}
350
 }
406
 }
351
 
407
 
408
+/* void ir_emit_xfer(struct ir *ir,  */
409
+
352
 int16_t ir_alloc_label (struct ir *ir)
410
 int16_t ir_alloc_label (struct ir *ir)
353
 {
411
 {
354
 	return ir->next_label--;
412
 	return ir->next_label--;
367
 	return ir->sp;
425
 	return ir->sp;
368
 }
426
 }
369
 
427
 
428
+void ir_init_sym(struct ir *ir, struct sym *sym)
429
+{
430
+	struct irstate *irs = &sym->irs;
431
+	struct type *t = type_base(sym->type);
432
+
433
+	if (irs->loc)
434
+		return;
435
+
436
+	irs->size = type_sizeof(t);
437
+
438
+	if (irs->hint.stack)
439
+		goto alloc_stack;
440
+
441
+	switch (t->ttype) {
442
+	case T_SCALAR:
443
+	case T_POINTER:
444
+		irs->loc = LOC_REG;
445
+		irs->reg = ir_alloc_register(ir);
446
+		return;
447
+
448
+	default:
449
+	alloc_stack:
450
+		irs->loc = LOC_STACK;
451
+		irs->stack = ir_alloc_stack(ir, irs->size);
452
+	}
453
+}
454
+
370
 struct ir *ir_new(void)
455
 struct ir *ir_new(void)
371
 {
456
 {
372
 	struct ir *ir;
457
 	struct ir *ir;

+ 7 - 16
ir.h

106
 struct irstate {
106
 struct irstate {
107
 	int loc;
107
 	int loc;
108
 
108
 
109
-	uint16_t reg;
109
+	size_t   size;
110
 	int32_t  stack;
110
 	int32_t  stack;
111
 	int32_t  imm;
111
 	int32_t  imm;
112
+	uint16_t reg;
112
 
113
 
113
 	struct {
114
 	struct {
114
 		int dot:1;
115
 		int dot:1;
116
+		int lval:1;
115
 		int stack:1;
117
 		int stack:1;
116
 	} hint;
118
 	} hint;
117
 };
119
 };
124
 uint16_t ir_alloc_register(struct ir *ir);
126
 uint16_t ir_alloc_register(struct ir *ir);
125
 ssize_t  ir_alloc_stack   (struct ir *ir, size_t size);
127
 ssize_t  ir_alloc_stack   (struct ir *ir, size_t size);
126
 
128
 
129
+void ir_init_sym(struct ir *ir, struct sym *sym);
130
+
127
 void ir_emit_insn   (struct ir *ir, struct bpf_insn bpf, uint16_t dst, uint16_t src);
131
 void ir_emit_insn   (struct ir *ir, struct bpf_insn bpf, uint16_t dst, uint16_t src);
128
 void ir_emit_ldmap  (struct ir *ir, uint16_t dst, struct sym *map);
132
 void ir_emit_ldmap  (struct ir *ir, uint16_t dst, struct sym *map);
129
 void ir_emit_label  (struct ir *ir, int16_t label);
133
 void ir_emit_label  (struct ir *ir, int16_t label);
130
 /* void ir_emit_reg_get(struct ir *ir, uint16_t reg); */
134
 /* void ir_emit_reg_get(struct ir *ir, uint16_t reg); */
131
 /* void ir_emit_reg_put(struct ir *ir, uint16_t reg); */
135
 /* void ir_emit_reg_put(struct ir *ir, uint16_t reg); */
132
-void ir_emit_mov_irs(struct ir *ir, uint16_t dst, struct irstate *src);
136
+void ir_emit_sym_to_reg(struct ir *ir, uint16_t dst, struct sym *src);
137
+void ir_emit_reg_to_sym(struct ir *ir, struct sym *dst, uint16_t src);
133
 
138
 
134
 struct ir *ir_new(void);
139
 struct ir *ir_new(void);
135
 
140
 
136
-
137
-static inline void irs_alloc_register(struct irstate *irs, struct ir *ir)
138
-{
139
-	irs->loc = LOC_REG;
140
-	irs->reg = ir_alloc_register(ir);
141
-}
142
-
143
-static inline void irs_alloc_stack(struct irstate *irs, struct ir *ir,
144
-				   size_t size)
145
-{
146
-	irs->loc = LOC_STACK;
147
-	irs->stack = ir_alloc_stack(ir, size);
148
-}
149
-
150
 #endif	/* _PLY_IR_H */
141
 #endif	/* _PLY_IR_H */

+ 1 - 1
kprobe.c

88
 
88
 
89
 	symtab_foreach(prog->locals, sym) {
89
 	symtab_foreach(prog->locals, sym) {
90
 		if ((*sym)->name && !strcmp((*sym)->name, "regs")) {
90
 		if ((*sym)->name && !strcmp((*sym)->name, "regs")) {
91
-			irs_alloc_register(&(*sym)->irs, prog->ir);
91
+			ir_init_sym(prog->ir, *sym);
92
 
92
 
93
 			/* kernel sets r1 to the address of the
93
 			/* kernel sets r1 to the address of the
94
 			 * pt_regs struct, which ply denotes as
94
 			 * pt_regs struct, which ply denotes as

+ 2 - 9
node.c

97
 
97
 
98
 /* helpers */
98
 /* helpers */
99
 
99
 
100
-int node_is_block(struct node *n)
100
+int node_is(struct node *n, const char *func)
101
 {
101
 {
102
 	if (!n || (n->ntype != N_EXPR))
102
 	if (!n || (n->ntype != N_EXPR))
103
 		return 0;
103
 		return 0;
104
 
104
 
105
-	return !strcmp(n->expr.func, ":block");
106
-}
107
-
108
-int node_is_map(struct node *n)
109
-{
110
-	if (!n || (n->ntype != N_EXPR))
111
-		return 0;
105
+	return !strcmp(n->expr.func, func);
112
 
106
 
113
-	return !strcmp(n->expr.func, "{}");
114
 }
107
 }
115
 
108
 
116
 
109
 

+ 1 - 2
node.h

71
 	return nargs;
71
 	return nargs;
72
 }
72
 }
73
 
73
 
74
-int node_is_block(struct node *n);
75
-int node_is_map  (struct node *n);
74
+int node_is(struct node *n, const char *func);
76
 
75
 
77
 #define node_expr_foreach(_expr, _arg) \
76
 #define node_expr_foreach(_expr, _arg) \
78
 	for ((_arg) = (_expr)->expr.args; (_arg); (_arg) = (_arg)->next)
77
 	for ((_arg) = (_expr)->expr.args; (_arg); (_arg) = (_arg)->next)

+ 5 - 5
ply.c

71
 	prog->probe = "k:SyS_read";
71
 	prog->probe = "k:SyS_read";
72
 
72
 
73
 	/* { 
73
 	/* { 
74
-	 * 	us = pid();
74
+	 * 	# us = pid();
75
 	 * 	t[0, pid()] = time();
75
 	 * 	t[0, pid()] = time();
76
 	 * 	reads[pid()] = quantize(arg2);
76
 	 * 	reads[pid()] = quantize(arg2);
77
 	 * }
77
 	 * }
78
 	 */
78
 	 */
79
 	prog->ast =
79
 	prog->ast =
80
 		node_expr(":block",
80
 		node_expr(":block",
81
-			  node_expr("=",
82
-				    node_ident("us"),
83
-				    node_expr("pid", NULL),
84
-				    NULL),
81
+			  /* node_expr("=", */
82
+			  /* 	    node_ident("us"), */
83
+			  /* 	    node_expr("pid", NULL), */
84
+			  /* 	    NULL), */
85
 			  node_expr("=",
85
 			  node_expr("=",
86
 				    node_expr("{}",
86
 				    node_expr("{}",
87
 					      node_ident("t"),
87
 					      node_ident("t"),

+ 10 - 0
type.c

327
 	return -EINVAL;
327
 	return -EINVAL;
328
 }
328
 }
329
 
329
 
330
+struct tfield *tfields_get(struct tfield *fields, const char *name)
331
+{
332
+	struct tfield *f;
330
 
333
 
334
+	tfields_foreach(f, fields) {
335
+		if (!strcmp(f->name, name))
336
+			return f;
337
+	}
338
+
339
+	return NULL;
340
+}
331
 
341
 
332
 int all_types_cmp(const void *_a, const void *_b)
342
 int all_types_cmp(const void *_a, const void *_b)
333
 {
343
 {

+ 2 - 0
type.h

38
 #define tfields_foreach(_f, _fields) \
38
 #define tfields_foreach(_f, _fields) \
39
 	for ((_f) = (_fields); (_f)->type; (_f)++)
39
 	for ((_f) = (_fields); (_f)->type; (_f)++)
40
 
40
 
41
+struct tfield *tfields_get(struct tfield *fields, const char *name);
42
+
41
 struct tstruct {
43
 struct tstruct {
42
 	char *name;
44
 	char *name;
43
 	struct tfield *fields;
45
 	struct tfield *fields;

+ 3 - 3
utils.c

19
 	if (n->prev)
19
 	if (n->prev)
20
 		fputc(' ', info->fp);
20
 		fputc(' ', info->fp);
21
 
21
 
22
-	if (node_is_block(n->up)) {
22
+	if (node_is(n->up, ":block")) {
23
 		info->indent += 4;
23
 		info->indent += 4;
24
 		fprintf(info->fp, "\n%*s", info->indent, "");
24
 		fprintf(info->fp, "\n%*s", info->indent, "");
25
 	}
25
 	}
41
 	struct ast_fprint_info *info = _info;
41
 	struct ast_fprint_info *info = _info;
42
 	struct node *c;
42
 	struct node *c;
43
 
43
 
44
-	if (node_is_block(n))
44
+	if (node_is(n, ":block"))
45
 		fprintf(info->fp, "\n%*s", info->indent, "");
45
 		fprintf(info->fp, "\n%*s", info->indent, "");
46
 
46
 
47
 	if (n->ntype == N_EXPR)
47
 	if (n->ntype == N_EXPR)
48
 		fputc(')', info->fp);
48
 		fputc(')', info->fp);
49
 
49
 
50
-	if (node_is_block(n->up))
50
+	if (node_is(n->up, ":block"))
51
 		info->indent -= 4;
51
 		info->indent -= 4;
52
 	
52
 	
53
 	return 0;
53
 	return 0;