• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 syzkaller project authors. All rights reserved.
2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3
4package prog
5
6import (
7	"bufio"
8	"bytes"
9	"encoding/hex"
10	"fmt"
11	"strconv"
12	"strings"
13)
14
15// String generates a very compact program description (mostly for debug output).
16func (p *Prog) String() string {
17	buf := new(bytes.Buffer)
18	for i, c := range p.Calls {
19		if i != 0 {
20			fmt.Fprintf(buf, "-")
21		}
22		fmt.Fprintf(buf, "%v", c.Meta.Name)
23	}
24	return buf.String()
25}
26
27func (p *Prog) Serialize() []byte {
28	p.debugValidate()
29	ctx := &serializer{
30		target: p.Target,
31		buf:    new(bytes.Buffer),
32		vars:   make(map[*ResultArg]int),
33	}
34	for _, c := range p.Calls {
35		ctx.call(c)
36	}
37	return ctx.buf.Bytes()
38}
39
40type serializer struct {
41	target *Target
42	buf    *bytes.Buffer
43	vars   map[*ResultArg]int
44	varSeq int
45}
46
47func (ctx *serializer) printf(text string, args ...interface{}) {
48	fmt.Fprintf(ctx.buf, text, args...)
49}
50
51func (ctx *serializer) allocVarID(arg *ResultArg) int {
52	id := ctx.varSeq
53	ctx.varSeq++
54	ctx.vars[arg] = id
55	return id
56}
57
58func (ctx *serializer) call(c *Call) {
59	if c.Ret != nil && len(c.Ret.uses) != 0 {
60		ctx.printf("r%v = ", ctx.allocVarID(c.Ret))
61	}
62	ctx.printf("%v(", c.Meta.Name)
63	for i, a := range c.Args {
64		if IsPad(a.Type()) {
65			continue
66		}
67		if i != 0 {
68			ctx.printf(", ")
69		}
70		ctx.arg(a)
71	}
72	ctx.printf(")\n")
73}
74
75func (ctx *serializer) arg(arg Arg) {
76	if arg == nil {
77		ctx.printf("nil")
78		return
79	}
80	arg.serialize(ctx)
81}
82
83func (a *ConstArg) serialize(ctx *serializer) {
84	ctx.printf("0x%x", a.Val)
85}
86
87func (a *PointerArg) serialize(ctx *serializer) {
88	if a.IsNull() {
89		ctx.printf("0x0")
90		return
91	}
92	target := ctx.target
93	ctx.printf("&%v", target.serializeAddr(a))
94	if a.Res != nil && isDefault(a.Res) && !target.isAnyPtr(a.Type()) {
95		return
96	}
97	ctx.printf("=")
98	if target.isAnyPtr(a.Type()) {
99		ctx.printf("ANY=")
100	}
101	ctx.arg(a.Res)
102}
103
104func (a *DataArg) serialize(ctx *serializer) {
105	if a.Type().Dir() == DirOut {
106		ctx.printf("\"\"/%v", a.Size())
107		return
108	}
109	data := a.Data()
110	if !a.Type().Varlen() {
111		// Statically typed data will be padded with 0s during
112		// deserialization, so we can strip them here for readability.
113		for len(data) >= 2 && data[len(data)-1] == 0 && data[len(data)-2] == 0 {
114			data = data[:len(data)-1]
115		}
116	}
117	serializeData(ctx.buf, data)
118}
119
120func (a *GroupArg) serialize(ctx *serializer) {
121	var delims []byte
122	switch a.Type().(type) {
123	case *StructType:
124		delims = []byte{'{', '}'}
125	case *ArrayType:
126		delims = []byte{'[', ']'}
127	default:
128		panic("unknown group type")
129	}
130	ctx.buf.WriteByte(delims[0])
131	lastNonDefault := len(a.Inner) - 1
132	if a.fixedInnerSize() {
133		for ; lastNonDefault >= 0; lastNonDefault-- {
134			if !isDefault(a.Inner[lastNonDefault]) {
135				break
136			}
137		}
138	}
139	for i := 0; i <= lastNonDefault; i++ {
140		arg1 := a.Inner[i]
141		if arg1 != nil && IsPad(arg1.Type()) {
142			continue
143		}
144		if i != 0 {
145			ctx.printf(", ")
146		}
147		ctx.arg(arg1)
148	}
149	ctx.buf.WriteByte(delims[1])
150}
151
152func (a *UnionArg) serialize(ctx *serializer) {
153	ctx.printf("@%v", a.Option.Type().FieldName())
154	if isDefault(a.Option) {
155		return
156	}
157	ctx.printf("=")
158	ctx.arg(a.Option)
159}
160
161func (a *ResultArg) serialize(ctx *serializer) {
162	if len(a.uses) != 0 {
163		ctx.printf("<r%v=>", ctx.allocVarID(a))
164	}
165	if a.Res == nil {
166		ctx.printf("0x%x", a.Val)
167		return
168	}
169	id, ok := ctx.vars[a.Res]
170	if !ok {
171		panic("no result")
172	}
173	ctx.printf("r%v", id)
174	if a.OpDiv != 0 {
175		ctx.printf("/%v", a.OpDiv)
176	}
177	if a.OpAdd != 0 {
178		ctx.printf("+%v", a.OpAdd)
179	}
180}
181
182func (target *Target) Deserialize(data []byte) (prog *Prog, err error) {
183	prog = &Prog{
184		Target: target,
185	}
186	p := newParser(data)
187	vars := make(map[string]*ResultArg)
188	comment := ""
189	for p.Scan() {
190		if p.EOF() {
191			if comment != "" {
192				prog.Comments = append(prog.Comments, comment)
193				comment = ""
194			}
195			continue
196		}
197		if p.Char() == '#' {
198			if comment != "" {
199				prog.Comments = append(prog.Comments, comment)
200			}
201			comment = strings.TrimSpace(p.s[p.i+1:])
202			continue
203		}
204		name := p.Ident()
205		r := ""
206		if p.Char() == '=' {
207			r = name
208			p.Parse('=')
209			name = p.Ident()
210
211		}
212		meta := target.SyscallMap[name]
213		if meta == nil {
214			return nil, fmt.Errorf("unknown syscall %v", name)
215		}
216		c := &Call{
217			Meta:    meta,
218			Ret:     MakeReturnArg(meta.Ret),
219			Comment: comment,
220		}
221		prog.Calls = append(prog.Calls, c)
222		p.Parse('(')
223		for i := 0; p.Char() != ')'; i++ {
224			if i >= len(meta.Args) {
225				eatExcessive(p, false)
226				break
227			}
228			typ := meta.Args[i]
229			if IsPad(typ) {
230				return nil, fmt.Errorf("padding in syscall %v arguments", name)
231			}
232			arg, err := target.parseArg(typ, p, vars)
233			if err != nil {
234				return nil, err
235			}
236			c.Args = append(c.Args, arg)
237			if p.Char() != ')' {
238				p.Parse(',')
239			}
240		}
241		p.Parse(')')
242		p.SkipWs()
243		if !p.EOF() {
244			if p.Char() != '#' {
245				return nil, fmt.Errorf("tailing data (line #%v)", p.l)
246			}
247			if c.Comment != "" {
248				prog.Comments = append(prog.Comments, c.Comment)
249			}
250			c.Comment = strings.TrimSpace(p.s[p.i+1:])
251		}
252		for i := len(c.Args); i < len(meta.Args); i++ {
253			c.Args = append(c.Args, meta.Args[i].makeDefaultArg())
254		}
255		if len(c.Args) != len(meta.Args) {
256			return nil, fmt.Errorf("wrong call arg count: %v, want %v", len(c.Args), len(meta.Args))
257		}
258		if r != "" && c.Ret != nil {
259			vars[r] = c.Ret
260		}
261		comment = ""
262	}
263	if comment != "" {
264		prog.Comments = append(prog.Comments, comment)
265	}
266	if err := p.Err(); err != nil {
267		return nil, err
268	}
269	// This validation is done even in non-debug mode because deserialization
270	// procedure does not catch all bugs (e.g. mismatched types).
271	// And we can receive bad programs from corpus and hub.
272	if err := prog.validate(); err != nil {
273		return nil, err
274	}
275	for _, c := range prog.Calls {
276		target.SanitizeCall(c)
277	}
278	return
279}
280
281func (target *Target) parseArg(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) {
282	r := ""
283	if p.Char() == '<' {
284		p.Parse('<')
285		r = p.Ident()
286		p.Parse('=')
287		p.Parse('>')
288	}
289	arg, err := target.parseArgImpl(typ, p, vars)
290	if err != nil {
291		return nil, err
292	}
293	if arg == nil {
294		if typ != nil {
295			arg = typ.makeDefaultArg()
296		} else if r != "" {
297			return nil, fmt.Errorf("named nil argument")
298		}
299	}
300	if r != "" {
301		if res, ok := arg.(*ResultArg); ok {
302			vars[r] = res
303		}
304	}
305	return arg, nil
306}
307
308func (target *Target) parseArgImpl(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) {
309	switch p.Char() {
310	case '0':
311		return target.parseArgInt(typ, p)
312	case 'r':
313		return target.parseArgRes(typ, p, vars)
314	case '&':
315		return target.parseArgAddr(typ, p, vars)
316	case '"', '\'':
317		return target.parseArgString(typ, p)
318	case '{':
319		return target.parseArgStruct(typ, p, vars)
320	case '[':
321		return target.parseArgArray(typ, p, vars)
322	case '@':
323		return target.parseArgUnion(typ, p, vars)
324	case 'n':
325		p.Parse('n')
326		p.Parse('i')
327		p.Parse('l')
328		return nil, nil
329
330	default:
331		return nil, fmt.Errorf("failed to parse argument at %v (line #%v/%v: %v)",
332			int(p.Char()), p.l, p.i, p.s)
333	}
334}
335
336func (target *Target) parseArgInt(typ Type, p *parser) (Arg, error) {
337	val := p.Ident()
338	v, err := strconv.ParseUint(val, 0, 64)
339	if err != nil {
340		return nil, fmt.Errorf("wrong arg value '%v': %v", val, err)
341	}
342	switch typ.(type) {
343	case *ConstType, *IntType, *FlagsType, *ProcType, *LenType, *CsumType:
344		return MakeConstArg(typ, v), nil
345	case *ResourceType:
346		return MakeResultArg(typ, nil, v), nil
347	case *PtrType, *VmaType:
348		if typ.Optional() {
349			return MakeNullPointerArg(typ), nil
350		}
351		return typ.makeDefaultArg(), nil
352	default:
353		eatExcessive(p, true)
354		return typ.makeDefaultArg(), nil
355	}
356}
357
358func (target *Target) parseArgRes(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) {
359	id := p.Ident()
360	var div, add uint64
361	if p.Char() == '/' {
362		p.Parse('/')
363		op := p.Ident()
364		v, err := strconv.ParseUint(op, 0, 64)
365		if err != nil {
366			return nil, fmt.Errorf("wrong result div op: '%v'", op)
367		}
368		div = v
369	}
370	if p.Char() == '+' {
371		p.Parse('+')
372		op := p.Ident()
373		v, err := strconv.ParseUint(op, 0, 64)
374		if err != nil {
375			return nil, fmt.Errorf("wrong result add op: '%v'", op)
376		}
377		add = v
378	}
379	v := vars[id]
380	if v == nil {
381		return typ.makeDefaultArg(), nil
382	}
383	arg := MakeResultArg(typ, v, 0)
384	arg.OpDiv = div
385	arg.OpAdd = add
386	return arg, nil
387}
388
389func (target *Target) parseArgAddr(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) {
390	var typ1 Type
391	switch t1 := typ.(type) {
392	case *PtrType:
393		typ1 = t1.Type
394	case *VmaType:
395	default:
396		eatExcessive(p, true)
397		return typ.makeDefaultArg(), nil
398	}
399	p.Parse('&')
400	addr, vmaSize, err := target.parseAddr(p)
401	if err != nil {
402		return nil, err
403	}
404	var inner Arg
405	if p.Char() == '=' {
406		p.Parse('=')
407		if p.Char() == 'A' {
408			p.Parse('A')
409			p.Parse('N')
410			p.Parse('Y')
411			p.Parse('=')
412			typ = target.makeAnyPtrType(typ.Size(), typ.FieldName())
413			typ1 = target.any.array
414		}
415		inner, err = target.parseArg(typ1, p, vars)
416		if err != nil {
417			return nil, err
418		}
419	}
420	if typ1 == nil {
421		return MakeVmaPointerArg(typ, addr, vmaSize), nil
422	}
423	if inner == nil {
424		inner = typ1.makeDefaultArg()
425	}
426	return MakePointerArg(typ, addr, inner), nil
427}
428
429func (target *Target) parseArgString(typ Type, p *parser) (Arg, error) {
430	if _, ok := typ.(*BufferType); !ok {
431		eatExcessive(p, true)
432		return typ.makeDefaultArg(), nil
433	}
434	data, err := deserializeData(p)
435	if err != nil {
436		return nil, err
437	}
438	size := ^uint64(0)
439	if p.Char() == '/' {
440		p.Parse('/')
441		sizeStr := p.Ident()
442		size, err = strconv.ParseUint(sizeStr, 0, 64)
443		if err != nil {
444			return nil, fmt.Errorf("failed to parse buffer size: %q", sizeStr)
445		}
446	}
447	if !typ.Varlen() {
448		size = typ.Size()
449	} else if size == ^uint64(0) {
450		size = uint64(len(data))
451	}
452	if typ.Dir() == DirOut {
453		return MakeOutDataArg(typ, size), nil
454	}
455	if diff := int(size) - len(data); diff > 0 {
456		data = append(data, make([]byte, diff)...)
457	}
458	data = data[:size]
459	return MakeDataArg(typ, data), nil
460}
461
462func (target *Target) parseArgStruct(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) {
463	p.Parse('{')
464	t1, ok := typ.(*StructType)
465	if !ok {
466		eatExcessive(p, false)
467		p.Parse('}')
468		return typ.makeDefaultArg(), nil
469	}
470	var inner []Arg
471	for i := 0; p.Char() != '}'; i++ {
472		if i >= len(t1.Fields) {
473			eatExcessive(p, false)
474			break
475		}
476		fld := t1.Fields[i]
477		if IsPad(fld) {
478			inner = append(inner, MakeConstArg(fld, 0))
479		} else {
480			arg, err := target.parseArg(fld, p, vars)
481			if err != nil {
482				return nil, err
483			}
484			inner = append(inner, arg)
485			if p.Char() != '}' {
486				p.Parse(',')
487			}
488		}
489	}
490	p.Parse('}')
491	for len(inner) < len(t1.Fields) {
492		inner = append(inner, t1.Fields[len(inner)].makeDefaultArg())
493	}
494	return MakeGroupArg(typ, inner), nil
495}
496
497func (target *Target) parseArgArray(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) {
498	p.Parse('[')
499	t1, ok := typ.(*ArrayType)
500	if !ok {
501		eatExcessive(p, false)
502		p.Parse(']')
503		return typ.makeDefaultArg(), nil
504	}
505	var inner []Arg
506	for i := 0; p.Char() != ']'; i++ {
507		arg, err := target.parseArg(t1.Type, p, vars)
508		if err != nil {
509			return nil, err
510		}
511		inner = append(inner, arg)
512		if p.Char() != ']' {
513			p.Parse(',')
514		}
515	}
516	p.Parse(']')
517	if t1.Kind == ArrayRangeLen && t1.RangeBegin == t1.RangeEnd {
518		for uint64(len(inner)) < t1.RangeBegin {
519			inner = append(inner, t1.Type.makeDefaultArg())
520		}
521		inner = inner[:t1.RangeBegin]
522	}
523	return MakeGroupArg(typ, inner), nil
524}
525
526func (target *Target) parseArgUnion(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) {
527	t1, ok := typ.(*UnionType)
528	if !ok {
529		eatExcessive(p, true)
530		return typ.makeDefaultArg(), nil
531	}
532	p.Parse('@')
533	name := p.Ident()
534	var optType Type
535	for _, t2 := range t1.Fields {
536		if name == t2.FieldName() {
537			optType = t2
538			break
539		}
540	}
541	if optType == nil {
542		eatExcessive(p, true)
543		return typ.makeDefaultArg(), nil
544	}
545	var opt Arg
546	if p.Char() == '=' {
547		p.Parse('=')
548		var err error
549		opt, err = target.parseArg(optType, p, vars)
550		if err != nil {
551			return nil, err
552		}
553	} else {
554		opt = optType.makeDefaultArg()
555	}
556	return MakeUnionArg(typ, opt), nil
557}
558
559// Eats excessive call arguments and struct fields to recover after description changes.
560func eatExcessive(p *parser, stopAtComma bool) {
561	paren, brack, brace := 0, 0, 0
562	for !p.EOF() && p.e == nil {
563		ch := p.Char()
564		switch ch {
565		case '(':
566			paren++
567		case ')':
568			if paren == 0 {
569				return
570			}
571			paren--
572		case '[':
573			brack++
574		case ']':
575			if brack == 0 {
576				return
577			}
578			brack--
579		case '{':
580			brace++
581		case '}':
582			if brace == 0 {
583				return
584			}
585			brace--
586		case ',':
587			if stopAtComma && paren == 0 && brack == 0 && brace == 0 {
588				return
589			}
590		case '\'', '"':
591			p.Parse(ch)
592			for !p.EOF() && p.Char() != ch {
593				p.Parse(p.Char())
594			}
595			if p.EOF() {
596				return
597			}
598		}
599		p.Parse(ch)
600	}
601}
602
603const (
604	encodingAddrBase = 0x7f0000000000
605	maxLineLen       = 1 << 20
606)
607
608func (target *Target) serializeAddr(arg *PointerArg) string {
609	ssize := ""
610	if arg.VmaSize != 0 {
611		ssize = fmt.Sprintf("/0x%x", arg.VmaSize)
612	}
613	return fmt.Sprintf("(0x%x%v)", encodingAddrBase+arg.Address, ssize)
614}
615
616func (target *Target) parseAddr(p *parser) (uint64, uint64, error) {
617	p.Parse('(')
618	pstr := p.Ident()
619	addr, err := strconv.ParseUint(pstr, 0, 64)
620	if err != nil {
621		return 0, 0, fmt.Errorf("failed to parse addr: %q", pstr)
622	}
623	if addr < encodingAddrBase {
624		return 0, 0, fmt.Errorf("address without base offset: %q", pstr)
625	}
626	addr -= encodingAddrBase
627	// This is not used anymore, but left here to parse old programs.
628	if p.Char() == '+' || p.Char() == '-' {
629		minus := false
630		if p.Char() == '-' {
631			minus = true
632			p.Parse('-')
633		} else {
634			p.Parse('+')
635		}
636		ostr := p.Ident()
637		off, err := strconv.ParseUint(ostr, 0, 64)
638		if err != nil {
639			return 0, 0, fmt.Errorf("failed to parse addr offset: %q", ostr)
640		}
641		if minus {
642			off = -off
643		}
644		addr += off
645	}
646	maxMem := target.NumPages * target.PageSize
647	var vmaSize uint64
648	if p.Char() == '/' {
649		p.Parse('/')
650		pstr := p.Ident()
651		size, err := strconv.ParseUint(pstr, 0, 64)
652		if err != nil {
653			return 0, 0, fmt.Errorf("failed to parse addr size: %q", pstr)
654		}
655		addr = addr & ^(target.PageSize - 1)
656		vmaSize = (size + target.PageSize - 1) & ^(target.PageSize - 1)
657		if vmaSize == 0 {
658			vmaSize = target.PageSize
659		}
660		if vmaSize > maxMem {
661			vmaSize = maxMem
662		}
663		if addr > maxMem-vmaSize {
664			addr = maxMem - vmaSize
665		}
666	}
667	p.Parse(')')
668	return addr, vmaSize, nil
669}
670
671func serializeData(buf *bytes.Buffer, data []byte) {
672	readable := true
673	for _, v := range data {
674		if v >= 0x20 && v < 0x7f {
675			continue
676		}
677		switch v {
678		case 0, '\a', '\b', '\f', '\n', '\r', '\t', '\v':
679			continue
680		}
681		readable = false
682		break
683	}
684	if !readable || len(data) == 0 {
685		fmt.Fprintf(buf, "\"%v\"", hex.EncodeToString(data))
686		return
687	}
688	buf.WriteByte('\'')
689	for _, v := range data {
690		switch v {
691		case 0:
692			buf.Write([]byte{'\\', 'x', '0', '0'})
693		case '\a':
694			buf.Write([]byte{'\\', 'a'})
695		case '\b':
696			buf.Write([]byte{'\\', 'b'})
697		case '\f':
698			buf.Write([]byte{'\\', 'f'})
699		case '\n':
700			buf.Write([]byte{'\\', 'n'})
701		case '\r':
702			buf.Write([]byte{'\\', 'r'})
703		case '\t':
704			buf.Write([]byte{'\\', 't'})
705		case '\v':
706			buf.Write([]byte{'\\', 'v'})
707		case '\'':
708			buf.Write([]byte{'\\', '\''})
709		case '\\':
710			buf.Write([]byte{'\\', '\\'})
711		default:
712			buf.WriteByte(v)
713		}
714	}
715	buf.WriteByte('\'')
716}
717
718func deserializeData(p *parser) ([]byte, error) {
719	var data []byte
720	if p.Char() == '"' {
721		p.Parse('"')
722		val := ""
723		if p.Char() != '"' {
724			val = p.Ident()
725		}
726		p.Parse('"')
727		var err error
728		data, err = hex.DecodeString(val)
729		if err != nil {
730			return nil, fmt.Errorf("data arg has bad value %q", val)
731		}
732	} else {
733		if p.consume() != '\'' {
734			return nil, fmt.Errorf("data arg does not start with \" nor with '")
735		}
736		for p.Char() != '\'' && p.Char() != 0 {
737			v := p.consume()
738			if v != '\\' {
739				data = append(data, v)
740				continue
741			}
742			v = p.consume()
743			switch v {
744			case 'x':
745				hi := p.consume()
746				lo := p.consume()
747				if lo != '0' || hi != '0' {
748					return nil, fmt.Errorf(
749						"invalid \\x%c%c escape sequence in data arg", hi, lo)
750				}
751				data = append(data, 0)
752			case 'a':
753				data = append(data, '\a')
754			case 'b':
755				data = append(data, '\b')
756			case 'f':
757				data = append(data, '\f')
758			case 'n':
759				data = append(data, '\n')
760			case 'r':
761				data = append(data, '\r')
762			case 't':
763				data = append(data, '\t')
764			case 'v':
765				data = append(data, '\v')
766			case '\'':
767				data = append(data, '\'')
768			case '\\':
769				data = append(data, '\\')
770			default:
771				return nil, fmt.Errorf("invalid \\%c escape sequence in data arg", v)
772			}
773		}
774		p.Parse('\'')
775	}
776	return data, nil
777}
778
779type parser struct {
780	r *bufio.Scanner
781	s string
782	i int
783	l int
784	e error
785}
786
787func newParser(data []byte) *parser {
788	p := &parser{r: bufio.NewScanner(bytes.NewReader(data))}
789	p.r.Buffer(nil, maxLineLen)
790	return p
791}
792
793func (p *parser) Scan() bool {
794	if p.e != nil {
795		return false
796	}
797	if !p.r.Scan() {
798		p.e = p.r.Err()
799		return false
800	}
801	p.s = p.r.Text()
802	p.i = 0
803	p.l++
804	return true
805}
806
807func (p *parser) Err() error {
808	return p.e
809}
810
811func (p *parser) Str() string {
812	return p.s
813}
814
815func (p *parser) EOF() bool {
816	return p.i == len(p.s)
817}
818
819func (p *parser) Char() byte {
820	if p.e != nil {
821		return 0
822	}
823	if p.EOF() {
824		p.failf("unexpected eof")
825		return 0
826	}
827	return p.s[p.i]
828}
829
830func (p *parser) Parse(ch byte) {
831	if p.e != nil {
832		return
833	}
834	if p.EOF() {
835		p.failf("want %s, got EOF", string(ch))
836		return
837	}
838	if p.s[p.i] != ch {
839		p.failf("want '%v', got '%v'", string(ch), string(p.s[p.i]))
840		return
841	}
842	p.i++
843	p.SkipWs()
844}
845
846func (p *parser) consume() byte {
847	if p.e != nil {
848		return 0
849	}
850	if p.EOF() {
851		p.failf("unexpected eof")
852		return 0
853	}
854	v := p.s[p.i]
855	p.i++
856	return v
857}
858
859func (p *parser) SkipWs() {
860	for p.i < len(p.s) && (p.s[p.i] == ' ' || p.s[p.i] == '\t') {
861		p.i++
862	}
863}
864
865func (p *parser) Ident() string {
866	i := p.i
867	for p.i < len(p.s) &&
868		(p.s[p.i] >= 'a' && p.s[p.i] <= 'z' ||
869			p.s[p.i] >= 'A' && p.s[p.i] <= 'Z' ||
870			p.s[p.i] >= '0' && p.s[p.i] <= '9' ||
871			p.s[p.i] == '_' || p.s[p.i] == '$') {
872		p.i++
873	}
874	if i == p.i {
875		p.failf("failed to parse identifier at pos %v", i)
876		return ""
877	}
878	s := p.s[i:p.i]
879	p.SkipWs()
880	return s
881}
882
883func (p *parser) failf(msg string, args ...interface{}) {
884	p.e = fmt.Errorf("%v\nline #%v: %v", fmt.Sprintf(msg, args...), p.l, p.s)
885}
886
887// CallSet returns a set of all calls in the program.
888// It does very conservative parsing and is intended to parse paste/future serialization formats.
889func CallSet(data []byte) (map[string]struct{}, error) {
890	calls := make(map[string]struct{})
891	s := bufio.NewScanner(bytes.NewReader(data))
892	s.Buffer(nil, maxLineLen)
893	for s.Scan() {
894		ln := s.Bytes()
895		if len(ln) == 0 || ln[0] == '#' {
896			continue
897		}
898		bracket := bytes.IndexByte(ln, '(')
899		if bracket == -1 {
900			return nil, fmt.Errorf("line does not contain opening bracket")
901		}
902		call := ln[:bracket]
903		if eq := bytes.IndexByte(call, '='); eq != -1 {
904			eq++
905			for eq < len(call) && call[eq] == ' ' {
906				eq++
907			}
908			call = call[eq:]
909		}
910		if len(call) == 0 {
911			return nil, fmt.Errorf("call name is empty")
912		}
913		calls[string(call)] = struct{}{}
914	}
915	if err := s.Err(); err != nil {
916		return nil, err
917	}
918	if len(calls) == 0 {
919		return nil, fmt.Errorf("program does not contain any calls")
920	}
921	return calls, nil
922}
923