...

Source file src/code.rocketnine.space/tslocum/gophast/pkg/download/controlfile.go

Documentation: code.rocketnine.space/tslocum/gophast/pkg/download

     1  package download
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"os"
     8  	"regexp"
     9  	"strconv"
    10  
    11  	"github.com/pkg/errors"
    12  	"gitlab.com/tslocum/gophast/pkg/log"
    13  	"gitlab.com/tslocum/gophast/pkg/utils"
    14  )
    15  
    16  type ControlFile struct {
    17  	URLs   []string
    18  	Ranges []*ByteRange
    19  }
    20  
    21  func ParseControlFile(filePath string) (*ControlFile, error) {
    22  	f, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
    23  	if err != nil {
    24  		return nil, errors.Errorf("failed to open control file: %v", err)
    25  	}
    26  	defer f.Close()
    27  
    28  	urlRegexp, err := regexp.Compile(`^https?://[\S]+$`)
    29  	if err != nil {
    30  		return nil, errors.Errorf("failed to compile URL regexp: %v", err)
    31  	}
    32  
    33  	urlRange, err := regexp.Compile(`^([0-9]+),([0-9]+),([0-9]+)$`)
    34  	if err != nil {
    35  		return nil, errors.Errorf("failed to compile range regexp: %v", err)
    36  	}
    37  
    38  	var (
    39  		cf                = &ControlFile{}
    40  		readURLs          bool
    41  		readRanges        bool
    42  		b                 []byte
    43  		m                 [][][]byte
    44  		start, end, wrote int64
    45  	)
    46  
    47  	log.Verbose("Parsing control file...")
    48  
    49  	scanner := bufio.NewScanner(f)
    50  	for scanner.Scan() {
    51  		b = scanner.Bytes()
    52  		if urlRegexp.Match(b) {
    53  			if readRanges {
    54  				return nil, errors.Errorf("unexpected URL after range: %s", b)
    55  			}
    56  
    57  			readURLs = true
    58  
    59  			cf.URLs = append(cf.URLs, string(b))
    60  		} else {
    61  			m = urlRange.FindAllSubmatch(b, -1)
    62  			if len(m) == 1 && len(m[0]) == 4 {
    63  				if !readURLs {
    64  					return nil, errors.Errorf("unexpected range before URL: %s", b)
    65  				}
    66  
    67  				readRanges = true
    68  
    69  				start, err = strconv.ParseInt(string(m[0][1]), 10, 64)
    70  				if err != nil {
    71  					return nil, errors.Errorf("failed to parse range start: %v", err)
    72  				}
    73  
    74  				end, err = strconv.ParseInt(string(m[0][2]), 10, 64)
    75  				if err != nil {
    76  					return nil, errors.Errorf("failed to parse range end: %v", err)
    77  				}
    78  
    79  				utils.ReverseBytes(m[0][3])
    80  				wrote, err = strconv.ParseInt(string(m[0][3]), 10, 64)
    81  				if err != nil {
    82  					return nil, errors.Errorf("failed to parse range wrote: %v", err)
    83  				}
    84  
    85  				if start < 0 || end < 0 || wrote < 0 || end < start || wrote > (end-start+1) {
    86  					return nil, errors.New(fmt.Sprintf("failed to parse invalid range: %s", NewByteRange(start, end, wrote)))
    87  				}
    88  
    89  				cf.Ranges = append(cf.Ranges, NewByteRange(start, end, wrote))
    90  			} else if len(bytes.TrimSpace(b)) > 0 {
    91  				return nil, errors.Errorf("failed to parse line: %s", b)
    92  			}
    93  		}
    94  	}
    95  	if err := scanner.Err(); err != nil {
    96  		return nil, errors.Errorf("failed to read file: %v", err)
    97  	}
    98  
    99  	return cf, nil
   100  }
   101  
   102  func (cf *ControlFile) String() string {
   103  	return fmt.Sprintf("{URLs: %s, Ranges: %s}", cf.URLs, cf.Ranges)
   104  }
   105  

View as plain text