<commit_before>package shell

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/user"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/zetamatta/nyagos/nodos"
	"github.com/zetamatta/nyagos/texts"
)

var NoClobber = false

func isSpace(c rune) bool {
	return strings.IndexRune(" \t\n\r\v\f", c) >= 0
}

type StatementT struct {
	Args     []string
	RawArgs  []string
	Term     string
	Redirect []func([]*os.File) (func(), error)
}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return fmt.Sprintf("%d", LastErrorLevel)
	},
	"DATE": func() string {
		layout, err := nodos.OsDateLayout()
		if err != nil {
			return err.Error()
		}
		return time.Now().Format(layout)
	},
	"TIME": func() string {
		return time.Now().Format("15:04:05.00")
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

var rxSubstitute = regexp.MustCompile(`^([^\:]+)\:([^\=]+)=(.*)$`)

var rxPercent = regexp.MustCompile(`%\w+%`)

func ourGetenvSub(name string) (string, bool) {
	m := rxSubstitute.FindStringSubmatch(name)
	if m != nil {
		base, ok := OurGetEnv(m[1])
		if ok {
			return texts.ReplaceIgnoreCase(base, m[2], m[3]), true
		} else {
			return "", false
		}
	} else {
		return OurGetEnv(name)
	}
}

func OurGetEnv(name string) (string, bool) {
	value := os.Getenv(name)
	if value != "" {
		return value, true
	} else if m := rxUnicode.FindStringSubmatch(name); m != nil {
		ucode, _ := strconv.ParseInt(m[1], 16, 32)
		return fmt.Sprintf("%c", rune(ucode)), true
	} else if f, ok := PercentFunc[strings.ToUpper(name)]; ok {
		return f(), true
	} else {<commit_msg>Implement: environment variable substring like %ENV:~10,5%<commit_after>package shell

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/user"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/zetamatta/nyagos/nodos"
	"github.com/zetamatta/nyagos/texts"
)

var NoClobber = false

func isSpace(c rune) bool {
	return strings.IndexRune(" \t\n\r\v\f", c) >= 0
}

type StatementT struct {
	Args     []string
	RawArgs  []string
	Term     string
	Redirect []func([]*os.File) (func(), error)
}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return fmt.Sprintf("%d", LastErrorLevel)
	},
	"DATE": func() string {
		layout, err := nodos.OsDateLayout()
		if err != nil {
			return err.Error()
		}
		return time.Now().Format(layout)
	},
	"TIME": func() string {
		return time.Now().Format("15:04:05.00")
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

var rxSubstitute = regexp.MustCompile(`^([^\:]+)\:([^\=]+)=(.*)$`)

var rxSubstring = regexp.MustCompile(`^([^\:]+)\:\~(\-?\d+)(?:,(\-?\d+))?$`)

var rxPercent = regexp.MustCompile(`%\w+%`)

func minusPosToAbs(s string, pos int) int {
	if pos < 0 {
		pos = len(s) + pos
	}
	if pos < 0 {
		pos = 0
	}
	if pos > len(s) {
		pos = len(s)
	}
	return pos
}

func ourGetenvSub(name string) (string, bool) {
	m := rxSubstitute.FindStringSubmatch(name)
	if m != nil {
		base, ok := OurGetEnv(m[1])
		if !ok {
			return "", false
		}
		return texts.ReplaceIgnoreCase(base, m[2], m[3]), true
	}
	m = rxSubstring.FindStringSubmatch(name)
	if m != nil {
		base, ok := OurGetEnv(m[1])
		if !ok {
			return "", false
		}
		pos, _ := strconv.Atoi(m[2])
		pos = minusPosToAbs(base, pos)
		base = base[pos:]
		if len(m) >= 4 {
			if pos, err := strconv.Atoi(m[3]); err == nil {
				pos = minusPosToAbs(base, pos)
				base = base[:pos]
			}
		}
		return base, true
	}
	return OurGetEnv(name)
}

func OurGetEnv(name string) (string, bool) {
	value := os.Getenv(name)
	if value != "" {
		return value, true
	} else if m := rxUnicode.FindStringSubmatch(name); m != nil {
		ucode, _ := strconv.ParseInt(m[1], 16, 32)
		return fmt.Sprintf("%c", rune(ucode)), true
	} else if f, ok := PercentFunc[strings.ToUpper(name)]; ok {
		return f(), true
	} else {
{'fraction_non_alphanumeric': 0.1977914110429448, 'fraction_numerical': 0.01177914110429448, 'mean_word_length': 3.554189944134078, 'pattern_counts': {'":': 8, '<': 5, '<?xml version=': 0, '>': 7, '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': '6294604', 'n_tokens_mistral': 1791, 'n_tokens_neox': 1659, 'n_words': 372}