<commit_before>package main

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"

	flag "github.com/ogier/pflag"
)

func main() {
	// debug := flag.BoolP("debug", "d", false, "Print ")
	useUDP := flag.BoolP("udp", "u", false, "Send via UDP (will ignore TLS)")
	useTLS := flag.BoolP("tls", "s", true, "Connect with TLS")
	doTee := flag.BoolP("tee", "t", true, "Tee stdin to stdout")
	command := "forward [OPTIONS] host:port"
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "%s\n", command)
		fmt.Fprintf(os.Stderr, "Options:\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	if len(flag.Args()) == 0 || !validDestination(flag.Arg(0)) {
		flag.Usage()
		return
	}

	destination := flag.Arg(0)

	var conn net.Conn
	var err error
	switch {
	case *useUDP:
		conn, err = net.Dial("udp", destination)
	case !*useTLS:
		conn, err = net.Dial("tcp", destination)
	default:
		conn, err = tls.Dial("tcp", destination, &tls.Config{})
	}
	connected := false
	if err != nil {
		fmt.Fprintf(os.Stderr, "Could not connect to %s: %v\n", destination, err)
	} else {
		connected = true
		defer conn.Close()
	}

	writers := make([]*io.PipeWriter, 0, 2)
	wg := sync.WaitGroup{}
	if *doTee {
		stdOutReader, stdOutWriter := io.Pipe()
		writers = append(writers, stdOutWriter)
		go func() {
			wg.Add(1)
			io.Copy(os.Stdout, stdOutReader)
			wg.Done()
		}()
	}
	if connected {
		netReader, netWriter := io.Pipe()
		writers = append(writers, netWriter)
		go func() {
			wg.Add(1)
			reader := bufio.NewReader(netReader)
			for {
				data, err := reader.ReadBytes('\n')
				if err != nil {
					break
				}
				conn.Write(data)
			}
			io.Copy(ioutil.Discard, netReader)
			wg.Done()
		}()
	}

	if len(writers) == 0 {
		io.Copy(ioutil.Discard, os.Stdin)
		return
	}

	writeOnly := make([]io.Writer, 0, len(writers))
	for _, w := range writers {
		writeOnly = append(writeOnly, w)
	}
	mw := io.MultiWriter(writeOnly...)
	io.Copy(mw, os.Stdin)
	for _, w := range writers {
		w.Close()
	}

	wg.Wait()
}

// Basic validation test
func validDestination(d string) bool {
	if len(d) == 0 {
		return false
	}

	s := strings.Split(d, ":")
	if len(s) != 2 || len(s[0]) == 0 || len(s[1]) == 0 {
		return false
	}

	if _, err := strconv.Atoi(s[1]); err != nil {
		return false
	}

	return true
}<commit_msg>Changed flag package, added basic syslog wrapping<commit_after>package main

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/codegangsta/cli"
)

var (
	useUDP bool
	useTLS bool
	doTee  bool

	syslog         bool
	syslogHostname string
	syslogApp      string
)

func main() {
	app := cli.NewApp()
	app.Name = "forward"
	app.Usage = "Transport StdIn lines to a remote destination over UDP, TCP, or TCP+TLS"
	app.UsageText = "forward [global options] [syslog [syslog options]] address:port"
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "udp, u",
			Usage:       "Send via UDP (will ignore TLS)",
			Destination: &useUDP,
		},
		cli.BoolTFlag{
			Name:        "tls, s",
			Usage:       "TLS-secured TCP connection",
			Destination: &useTLS,
		},
		cli.BoolTFlag{
			Name:        "tee, t",
			Usage:       "Tee stdin to stdout",
			Destination: &doTee,
		},
	}
	app.Action = func(c *cli.Context) {
		forward(c.Args().First())
	}

	h, _ := os.Hostname()
	app.Commands = []cli.Command{
		{
			Name:      "syslog",
			Aliases:   []string{"log"},
			Usage:     "Wrap lines in RFC-5424 Syslog format",
			ArgsUsage: "address:port",
			Action: func(c *cli.Context) {
				syslog = true
				forward(c.Args().First())
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:        "hostname, n",
					Value:       h,
					Destination: &syslogHostname,
				},
				cli.StringFlag{
					Name:        "app, a",
					Value:       "app",
					Destination: &syslogApp,
				},
			},
		},
	}

	app.Run(os.Args)
}

func forward(destination string) {
	if !validDestination(destination) {
		return
	}

	var conn net.Conn
	var err error
	switch {
	case useUDP:
		conn, err = net.Dial("udp", destination)
	case !useTLS:
		conn, err = net.Dial("tcp", destination)
	default:
		conn, err = tls.Dial("tcp", destination, &tls.Config{})
	}
	connected := false
	if err != nil {
		fmt.Fprintf(os.Stderr, "Could not connect to %s: %v\n", destination, err)
	} else {
		connected = true
		defer conn.Close()
	}

	writers := make([]*io.PipeWriter, 0, 2)
	wg := sync.WaitGroup{}
	if doTee {
		stdOutReader, stdOutWriter := io.Pipe()
		writers = append(writers, stdOutWriter)
		go func() {
			io.Copy(os.Stdout, stdOutReader)
		}()
	}
	if connected {
		netReader, netWriter := io.Pipe()
		writers = append(writers, netWriter)
		go func() {
			wg.Add(1)
			reader := bufio.NewReader(netReader)
			byteBuffer := bytes.NewBuffer([]byte{})
			for {
				data, err := reader.ReadBytes('\n')
				if err != nil {
					break
				}

				if syslog {
					data = toSyslog(byteBuffer, data)
				}

				if _, err = conn.Write(data); err != nil {
					break
				}
				byteBuffer.Reset()
			}
			io.Copy(ioutil.Discard, netReader)
			wg.Done()
		}()
	}

	if len(writers) == 0 {
		io.Copy(ioutil.Discard, os.Stdin)
		return
	}

	writeOnly := make([]io.Writer, 0, len(writers))
	for _, w := range writers {
		writeOnly = append(writeOnly, w)
	}
	mw := io.MultiWriter(writeOnly...)
	io.Copy(mw, os.Stdin)
	for _, w := range writers {
		w.Close()
	}

	wg.Wait()
}

func toSyslog(b *bytes.Buffer, line []byte) []byte {
	//<22>1 2016-06-18T09:56:21Z sendername programname - - - the log message
	b.WriteString("<22>1 ")
	b.WriteString(time.Now().UTC().Format(time.RFC3339) + " ")
	b.WriteString(syslogHostname + " ")
	b.WriteString(syslogApp + " ")
	b.WriteString("- - - ")
	b.Write(line)

	return b.Bytes()
}

// Basic validation test
func validDestination(d string) bool {
	if len(d) == 0 {
		return false
	}

	s := strings.Split(d, ":")
	if len(s) != 2 || len(s[0]) == 0 || len(s[1]) == 0 {
		return false
	}

	if _, err := strconv.Atoi(s[1]); err != nil {
		return false
	}

	return true
}
{'fraction_non_alphanumeric': 0.1719344696342876, 'fraction_numerical': 0.009266920403772961, 'mean_word_length': 2.9920739762219286, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1702256', 'n_tokens_mistral': 2677, 'n_tokens_neox': 2373, 'n_words': 546}