gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

util.go (11142B)


      1 // Copyright 2020 The CCGO Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // generator.go helpers
      6 
      7 package ccgo // import "modernc.org/ccgo/v3/lib"
      8 
      9 import (
     10 	"archive/tar"
     11 	"bufio"
     12 	"bytes"
     13 	"compress/gzip"
     14 	"fmt"
     15 	"io"
     16 	"io/ioutil"
     17 	"os"
     18 	"os/exec"
     19 	"path/filepath"
     20 	"runtime/debug"
     21 	"strings"
     22 )
     23 
     24 // CopyFile copies src to dest, preserving permissions and times where/when
     25 // possible. If canOverwrite is not nil, it is consulted whether a destination
     26 // file can be overwritten. If canOverwrite is nil then destination is
     27 // overwritten if permissions allow that, otherwise the function fails.
     28 //
     29 // Src and dst must be in the slash form.
     30 func CopyFile(dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) (n int64, rerr error) {
     31 	dst = filepath.FromSlash(dst)
     32 	dstDir := filepath.Dir(dst)
     33 	di, err := os.Stat(dstDir)
     34 	switch {
     35 	case err != nil:
     36 		if !os.IsNotExist(err) {
     37 			return 0, err
     38 		}
     39 
     40 		if err := os.MkdirAll(dstDir, 0770); err != nil {
     41 			return 0, err
     42 		}
     43 	case err == nil:
     44 		if !di.IsDir() {
     45 			return 0, fmt.Errorf("cannot create directory, file exists: %s", dst)
     46 		}
     47 	}
     48 
     49 	src = filepath.FromSlash(src)
     50 	si, err := os.Stat(src)
     51 	if err != nil {
     52 		return 0, err
     53 	}
     54 
     55 	if si.IsDir() {
     56 		return 0, fmt.Errorf("cannot copy a directory: %s", src)
     57 	}
     58 
     59 	di, err = os.Stat(dst)
     60 	switch {
     61 	case err != nil && !os.IsNotExist(err):
     62 		return 0, err
     63 	case err == nil:
     64 		if di.IsDir() {
     65 			return 0, fmt.Errorf("cannot overwite a directory: %s", dst)
     66 		}
     67 
     68 		if canOverwrite != nil && !canOverwrite(dst, di) {
     69 			return 0, fmt.Errorf("cannot overwite: %s", dst)
     70 		}
     71 	}
     72 
     73 	s, err := os.Open(src)
     74 	if err != nil {
     75 		return 0, err
     76 	}
     77 
     78 	defer s.Close()
     79 	r := bufio.NewReader(s)
     80 
     81 	d, err := os.Create(dst)
     82 
     83 	defer func() {
     84 		if err := d.Close(); err != nil && rerr == nil {
     85 			rerr = err
     86 			return
     87 		}
     88 
     89 		if err := os.Chmod(dst, si.Mode()); err != nil && rerr == nil {
     90 			rerr = err
     91 			return
     92 		}
     93 
     94 		if err := os.Chtimes(dst, si.ModTime(), si.ModTime()); err != nil && rerr == nil {
     95 			rerr = err
     96 			return
     97 		}
     98 	}()
     99 
    100 	w := bufio.NewWriter(d)
    101 
    102 	defer func() {
    103 		if err := w.Flush(); err != nil && rerr == nil {
    104 			rerr = err
    105 		}
    106 	}()
    107 
    108 	return io.Copy(w, r)
    109 }
    110 
    111 // MustCopyFile is like CopyFile but it executes Fatal(stackTrace, err) if it fails.
    112 func MustCopyFile(stackTrace bool, dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) int64 {
    113 	n, err := CopyFile(dst, src, canOverwrite)
    114 	if err != nil {
    115 		Fatal(stackTrace, err)
    116 	}
    117 
    118 	return n
    119 }
    120 
    121 // CopyDir recursively copies src to dest, preserving permissions and times
    122 // where/when possible. If canOverwrite is not nil, it is consulted whether a
    123 // destination file can be overwritten. If canOverwrite is nil then destination
    124 // is overwritten if permissions allow that, otherwise the function fails.
    125 //
    126 // Src and dst must be in the slash form.
    127 func CopyDir(dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) (files int, bytes int64, rerr error) {
    128 	dst = filepath.FromSlash(dst)
    129 	src = filepath.FromSlash(src)
    130 	si, err := os.Stat(src)
    131 	if err != nil {
    132 		return 0, 0, err
    133 	}
    134 
    135 	if !si.IsDir() {
    136 		return 0, 0, fmt.Errorf("cannot copy a file: %s", src)
    137 	}
    138 
    139 	return files, bytes, filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
    140 		if err != nil {
    141 			return err
    142 		}
    143 
    144 		rel, err := filepath.Rel(src, path)
    145 		if err != nil {
    146 			return err
    147 		}
    148 
    149 		if info.IsDir() {
    150 			return os.MkdirAll(filepath.Join(dst, rel), 0770)
    151 		}
    152 
    153 		n, err := CopyFile(filepath.Join(dst, rel), path, canOverwrite)
    154 		if err != nil {
    155 			return err
    156 		}
    157 
    158 		files++
    159 		bytes += n
    160 		return nil
    161 	})
    162 }
    163 
    164 // MustCopyDir is like CopyDir, but it executes Fatal(stackTrace, errĂº if it fails.
    165 func MustCopyDir(stackTrace bool, dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) (files int, bytes int64) {
    166 	file, bytes, err := CopyDir(dst, src, canOverwrite)
    167 	if err != nil {
    168 		Fatal(stackTrace, err)
    169 	}
    170 
    171 	return file, bytes
    172 }
    173 
    174 // UntarFile extracts a named tar.gz archive into dst. If canOverwrite is not
    175 // nil, it is consulted whether a destination file can be overwritten. If
    176 // canOverwrite is nil then destination is overwritten if permissions allow
    177 // that, otherwise the function fails.
    178 //
    179 // Src and dst must be in the slash form.
    180 func UntarFile(dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) error {
    181 	f, err := os.Open(filepath.FromSlash(src))
    182 	if err != nil {
    183 		return err
    184 	}
    185 
    186 	defer f.Close()
    187 
    188 	return Untar(dst, bufio.NewReader(f), canOverwrite)
    189 }
    190 
    191 // MustUntarFile is like UntarFile but it executes Fatal(stackTrace, err) if it fails.
    192 func MustUntarFile(stackTrace bool, dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) {
    193 	if err := UntarFile(dst, src, canOverwrite); err != nil {
    194 		Fatal(stackTrace, err)
    195 	}
    196 }
    197 
    198 // Untar extracts a tar.gz archive into dst. If canOverwrite is not nil, it is
    199 // consulted whether a destination file can be overwritten. If canOverwrite is
    200 // nil then destination is overwritten if permissions allow that, otherwise the
    201 // function fails.
    202 //
    203 // Dst must be in the slash form.
    204 func Untar(dst string, r io.Reader, canOverwrite func(fn string, fi os.FileInfo) bool) error {
    205 	dst = filepath.FromSlash(dst)
    206 	gr, err := gzip.NewReader(r)
    207 	if err != nil {
    208 		return err
    209 	}
    210 
    211 	tr := tar.NewReader(gr)
    212 	for {
    213 		hdr, err := tr.Next()
    214 		if err != nil {
    215 			if err != io.EOF {
    216 				return err
    217 			}
    218 
    219 			return nil
    220 		}
    221 
    222 		switch hdr.Typeflag {
    223 		case tar.TypeDir:
    224 			dir := filepath.Join(dst, hdr.Name)
    225 			if err = os.MkdirAll(dir, 0770); err != nil {
    226 				return err
    227 			}
    228 		case tar.TypeSymlink, tar.TypeXGlobalHeader:
    229 			// skip
    230 		case tar.TypeReg, tar.TypeRegA:
    231 			dir := filepath.Dir(filepath.Join(dst, hdr.Name))
    232 			if _, err := os.Stat(dir); err != nil {
    233 				if !os.IsNotExist(err) {
    234 					return err
    235 				}
    236 
    237 				if err = os.MkdirAll(dir, 0770); err != nil {
    238 					return err
    239 				}
    240 			}
    241 
    242 			fn := filepath.Join(dst, hdr.Name)
    243 			f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode))
    244 			if err != nil {
    245 				return err
    246 			}
    247 
    248 			w := bufio.NewWriter(f)
    249 			if _, err = io.Copy(w, tr); err != nil {
    250 				return err
    251 			}
    252 
    253 			if err := w.Flush(); err != nil {
    254 				return err
    255 			}
    256 
    257 			if err := f.Close(); err != nil {
    258 				return err
    259 			}
    260 
    261 			if err := os.Chtimes(fn, hdr.AccessTime, hdr.ModTime); err != nil {
    262 				return err
    263 			}
    264 		default:
    265 			return fmt.Errorf("unexpected tar header typeflag %#02x", hdr.Typeflag)
    266 		}
    267 	}
    268 
    269 }
    270 
    271 // MustUntar is like Untar but it executes Fatal(stackTrace, err) if it fails.
    272 func MustUntar(stackTrace bool, dst string, r io.Reader, canOverwrite func(fn string, fi os.FileInfo) bool) {
    273 	if err := Untar(dst, r, canOverwrite); err != nil {
    274 		Fatal(stackTrace, err)
    275 	}
    276 }
    277 
    278 // Fatalf prints a formatted message to os.Stderr and performs os.Exit(1). A
    279 // stack trace is added if stackTrace is true.
    280 func Fatalf(stackTrace bool, s string, args ...interface{}) {
    281 	if stackTrace {
    282 		fmt.Fprintf(os.Stderr, "%s\n", debug.Stack())
    283 	}
    284 	fmt.Fprintln(os.Stderr, strings.TrimSpace(fmt.Sprintf(s, args...)))
    285 	os.Exit(1)
    286 }
    287 
    288 // Fatal prints its argumenst to os.Stderr and performs os.Exit(1). A
    289 // stack trace is added if stackTrace is true.
    290 func Fatal(stackTrace bool, args ...interface{}) {
    291 	if stackTrace {
    292 		fmt.Fprintf(os.Stderr, "%s\n", debug.Stack())
    293 	}
    294 	fmt.Fprintln(os.Stderr, strings.TrimSpace(fmt.Sprint(args...)))
    295 	os.Exit(1)
    296 }
    297 
    298 // Mkdirs will create all paths. Paths must be in slash form.
    299 func Mkdirs(paths ...string) error {
    300 	for _, path := range paths {
    301 		path = filepath.FromSlash(path)
    302 		if err := os.MkdirAll(path, 0770); err != nil {
    303 			return err
    304 		}
    305 	}
    306 
    307 	return nil
    308 }
    309 
    310 // MustMkdirs is like Mkdir but if executes Fatal(stackTrace, err) if it fails.
    311 func MustMkdirs(stackTrace bool, paths ...string) {
    312 	if err := Mkdirs(paths...); err != nil {
    313 		Fatal(stackTrace, err)
    314 	}
    315 }
    316 
    317 // InDir executes f in dir. Dir must be in slash form.
    318 func InDir(dir string, f func() error) (err error) {
    319 	var cwd string
    320 	if cwd, err = os.Getwd(); err != nil {
    321 		return err
    322 	}
    323 
    324 	defer func() {
    325 		if err2 := os.Chdir(cwd); err2 != nil {
    326 			err = err2
    327 		}
    328 	}()
    329 
    330 	if err = os.Chdir(filepath.FromSlash(dir)); err != nil {
    331 		return err
    332 	}
    333 
    334 	return f()
    335 }
    336 
    337 // MustInDir is like InDir but it executes Fatal(stackTrace, err) if it fails.
    338 func MustInDir(stackTrace bool, dir string, f func() error) {
    339 	if err := InDir(dir, f); err != nil {
    340 		Fatal(stackTrace, err)
    341 	}
    342 }
    343 
    344 type echoWriter struct {
    345 	w bytes.Buffer
    346 }
    347 
    348 func (w *echoWriter) Write(b []byte) (int, error) {
    349 	os.Stdout.Write(b)
    350 	return w.w.Write(b)
    351 }
    352 
    353 // Shell echoes and executes cmd with args and returns the combined output if the command.
    354 func Shell(cmd string, args ...string) ([]byte, error) {
    355 	cmd, err := exec.LookPath(cmd)
    356 	if err != nil {
    357 		return nil, err
    358 	}
    359 
    360 	wd, err := AbsCwd()
    361 	if err != nil {
    362 		return nil, err
    363 	}
    364 
    365 	fmt.Printf("execute %s %q in %s\n", cmd, args, wd)
    366 	var b echoWriter
    367 	c := exec.Command(cmd, args...)
    368 	c.Stdout = &b
    369 	c.Stderr = &b
    370 	err = c.Run()
    371 	return b.w.Bytes(), err
    372 }
    373 
    374 // MustShell is like Shell but it executes Fatal(stackTrace, err) if it fails.
    375 func MustShell(stackTrace bool, cmd string, args ...string) []byte {
    376 	b, err := Shell(cmd, args...)
    377 	if err != nil {
    378 		Fatalf(stackTrace, "%v %s\noutput: %s\nerr: %s", cmd, args, b, err)
    379 	}
    380 
    381 	return b
    382 }
    383 
    384 // Compile executes Shell with cmd set to "ccgo".
    385 func Compile(args ...string) ([]byte, error) { return Shell("ccgo", args...) }
    386 
    387 // MustCompile is like Compile but if executes Fatal(stackTrace, err) if it fails.
    388 func MustCompile(stackTrace bool, args ...string) []byte {
    389 	return MustShell(stackTrace, "ccgo", args...)
    390 }
    391 
    392 // Run is like Compile, but executes in-process.
    393 func Run(args ...string) ([]byte, error) {
    394 	var b bytes.Buffer
    395 	t := NewTask(append([]string{"ccgo"}, args...), &b, &b)
    396 	err := t.Main()
    397 	return b.Bytes(), err
    398 }
    399 
    400 // MustRun is like Run but if executes Fatal(stackTrace, err) if it fails.
    401 func MustRun(stackTrace bool, args ...string) []byte {
    402 	var b bytes.Buffer
    403 	args = append([]string{"ccgo"}, args...)
    404 	t := NewTask(args, &b, &b)
    405 	if err := t.Main(); err != nil {
    406 		Fatalf(stackTrace, "%v\noutput: %s\nerr: %s", args, b.Bytes(), err)
    407 	}
    408 
    409 	return b.Bytes()
    410 }
    411 
    412 // AbsCwd returns the absolute working directory.
    413 func AbsCwd() (string, error) {
    414 	wd, err := os.Getwd()
    415 	if err != nil {
    416 		return "", err
    417 	}
    418 
    419 	if wd, err = filepath.Abs(wd); err != nil {
    420 		return "", err
    421 	}
    422 
    423 	return wd, nil
    424 }
    425 
    426 // MustAbsCwd is like AbsCwd but executes Fatal(stackTrace, err) if it fails.
    427 func MustAbsCwd(stackTrace bool) string {
    428 	s, err := AbsCwd()
    429 	if err != nil {
    430 		Fatal(stackTrace, err)
    431 	}
    432 
    433 	return s
    434 }
    435 
    436 // Env returns the value of environmental variable key of dflt otherwise.
    437 func Env(key, dflt string) string {
    438 	if s := os.Getenv(key); s != "" {
    439 		return s
    440 	}
    441 
    442 	return dflt
    443 }
    444 
    445 // MustTempDir is like ioutil.TempDir but executes Fatal(stackTrace, err) if it
    446 // fails. The returned path is absolute.
    447 func MustTempDir(stackTrace bool, dir, name string) string {
    448 	s, err := ioutil.TempDir(dir, name)
    449 	if err != nil {
    450 		Fatal(stackTrace, err)
    451 	}
    452 
    453 	if s, err = filepath.Abs(s); err != nil {
    454 		Fatal(stackTrace, err)
    455 	}
    456 
    457 	return s
    458 }