github.com/gotranspile/cxgo@v0.3.7/runtime/stdio/scan.go (about)

     1  package stdio
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"os"
    10  
    11  	"github.com/gotranspile/cxgo/runtime/libc"
    12  )
    13  
    14  func FscanfGo(r io.Reader, format string, args ...interface{}) (int, error) {
    15  	left := args
    16  	var (
    17  		goformat bytes.Buffer
    18  		goargs   []interface{}
    19  		post     []func() error
    20  	)
    21  	popArg := func() (interface{}, error) {
    22  		if len(left) == 0 {
    23  			return nil, errors.New("not enough arguments to scan")
    24  		}
    25  		v := left[0]
    26  		left = left[1:]
    27  		return v, nil
    28  	}
    29  	processArg := func(typ string) error {
    30  		switch typ {
    31  		case "%%":
    32  			goformat.WriteString("%%")
    33  			return nil
    34  		case "%s":
    35  			p, err := popArg()
    36  			if err != nil {
    37  				return err
    38  			}
    39  			goformat.WriteString("%s")
    40  			var v string
    41  			goargs = append(goargs, &v)
    42  
    43  			rv, err := libc.AsPtr(p)
    44  			if err != nil {
    45  				panic(err)
    46  			}
    47  			pv := (*byte)(rv)
    48  			post = append(post, func() error {
    49  				libc.StrCpyGoZero(pv, []byte(v))
    50  				return nil
    51  			})
    52  			return nil
    53  		case "%d":
    54  			p, err := popArg()
    55  			if err != nil {
    56  				return err
    57  			}
    58  			goformat.WriteString("%d")
    59  			var v int
    60  			goargs = append(goargs, &v)
    61  			rv, err := libc.AsPtr(p)
    62  			if err != nil {
    63  				panic(err)
    64  			}
    65  			pv := (*int)(rv)
    66  			post = append(post, func() error {
    67  				*pv = v
    68  				return nil
    69  			})
    70  			return nil
    71  		case "%f":
    72  			p, err := popArg()
    73  			if err != nil {
    74  				return err
    75  			}
    76  			goformat.WriteString("%f")
    77  			var v float32
    78  			goargs = append(goargs, &v)
    79  			rv, err := libc.AsPtr(p)
    80  			if err != nil {
    81  				panic(err)
    82  			}
    83  			pv := (*float32)(rv)
    84  			post = append(post, func() error {
    85  				*pv = v
    86  				return nil
    87  			})
    88  			return nil
    89  		case "%*s":
    90  			goformat.WriteString("%s")
    91  			var v string
    92  			goargs = append(goargs, &v)
    93  			// ignore
    94  			return nil
    95  		default:
    96  			panic(typ)
    97  		}
    98  	}
    99  	for _, w := range parseFormat(format) {
   100  		if !w.Verb {
   101  			goformat.WriteString(w.Str)
   102  			continue
   103  		}
   104  		if err := processArg(w.Str); err != nil {
   105  			log.Printf("fscanf(%q, %+v): process: %v", format, args, err)
   106  			return 0, err
   107  		}
   108  	}
   109  	r = &spaceReader{r: r}
   110  	_, err := fmt.Fscanf(r, goformat.String(), goargs...)
   111  	if err != nil {
   112  		log.Printf("fscanf(%q, %+v): scan: %v", format, args, err)
   113  		return 0, err
   114  	}
   115  	for _, fnc := range post {
   116  		if err := fnc(); err != nil {
   117  			log.Printf("fscanf(%q, %+v): post: %v", format, args, err)
   118  			return 0, err
   119  		}
   120  	}
   121  	return len(args), nil
   122  }
   123  
   124  // spaceReader replaces new line characters with spaces.
   125  // It's needed because scanf in C interprets new lines as spaces, while Go's fmt.Scanf requires new lines to be included
   126  // in the format string.
   127  type spaceReader struct {
   128  	r io.Reader
   129  }
   130  
   131  func (r *spaceReader) Read(p []byte) (int, error) {
   132  	n, err := r.r.Read(p)
   133  	for i := range p[:n] {
   134  		switch p[i] {
   135  		case '\n', '\r':
   136  			p[i] = ' '
   137  		}
   138  	}
   139  	return n, err
   140  }
   141  
   142  func Scanf(format string, args ...interface{}) int {
   143  	n, err := FscanfGo(os.Stdin, format, args...)
   144  	if err != nil {
   145  		libc.SetErr(err)
   146  		return -1
   147  	}
   148  	return n
   149  }
   150  
   151  func Vscanf(format string, args libc.ArgList) int {
   152  	return Scanf(format, args.Args()...)
   153  }
   154  
   155  func Sscanf(buf *byte, format string, args ...interface{}) int {
   156  	str := libc.GoBytes(buf)
   157  	n, err := FscanfGo(bytes.NewReader(str), format, args...)
   158  	if err != nil {
   159  		libc.SetErr(err)
   160  		return -1
   161  	}
   162  	return n
   163  }
   164  
   165  func Vsscanf(buf *byte, format string, args libc.ArgList) int {
   166  	return Sscanf(buf, format, args.Args()...)
   167  }