Example #1
0
// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
func (file *File) Stat() (FileInfo, error) {
	if file == nil {
		return nil, ErrInvalid
	}
	if file == nil || file.fd < 0 {
		return nil, syscall.EINVAL
	}
	if file.isdir() {
		// I don't know any better way to do that for directory
		return Stat(file.dirinfo.path)
	}
	if file.name == DevNull {
		return &devNullStat, nil
	}

	ft, err := syscall.GetFileType(file.fd)
	if err != nil {
		return nil, &PathError{"GetFileType", file.name, err}
	}
	if ft == syscall.FILE_TYPE_PIPE {
		return &fileStat{name: basename(file.name), pipe: true}, nil
	}

	var d syscall.ByHandleFileInformation
	err = syscall.GetFileInformationByHandle(file.fd, &d)
	if err != nil {
		return nil, &PathError{"GetFileInformationByHandle", file.name, err}
	}
	return &fileStat{
		name: basename(file.name),
		sys: syscall.Win32FileAttributeData{
			FileAttributes: d.FileAttributes,
			CreationTime:   d.CreationTime,
			LastAccessTime: d.LastAccessTime,
			LastWriteTime:  d.LastWriteTime,
			FileSizeHigh:   d.FileSizeHigh,
			FileSizeLow:    d.FileSizeLow,
		},
		vol:   d.VolumeSerialNumber,
		idxhi: d.FileIndexHigh,
		idxlo: d.FileIndexLow,
		pipe:  false,
	}, nil
}
func main() {
	var ok int // return status

	outHandle := syscall.Handle(os.Stdout.Fd())
	outType, err := syscall.GetFileType(outHandle)
	if err != nil {
		fatal.Fatalln(err)
	}
	outBsize := 4096

	// catch (./cat) < /etc/group
	var args []string
	if flag.NArg() == 0 {
		args = []string{"-"}
	} else {
		args = flag.Args()
	}

	// the main loop
	var file *os.File
	for _, arg := range args {
		var inStat os.FileInfo

		if arg == "-" {
			file = os.Stdin
		} else {
			file, err = os.Open(arg)
			if err != nil {
				fatal.Fatalln(err)
			}

			inStat, err = file.Stat()
			if err != nil {
				fatal.Fatalln(err)
			}
			if inStat.IsDir() {
				fatal.Printf("%s: Is a directory\n", file.Name())
			}
		}

		inHandle := syscall.Handle(file.Fd())
		inBsize := 4096

		// See http://stackoverflow.com/q/29360969/2967113
		// for why this differs from the Unix versions.
		//
		// Make sure we're not catting a file to itself,
		// provided it's a regular file. Catting a non-reg
		// file to itself is cool, e.g. cat file > file
		if outType == syscall.FILE_TYPE_DISK {

			inPath := make([]byte, syscall.MAX_PATH)
			outPath := make([]byte, syscall.MAX_PATH)

			err = k32.GetFinalPathNameByHandleA(inHandle, inPath, 0)
			if err != nil {
				fatal.Fatalln(err)
			}

			err = k32.GetFinalPathNameByHandleA(outHandle, outPath, 0)
			if err != nil {
				fatal.Fatalln(err)
			}

			if string(inPath) == string(outPath) {
				if n, _ := file.Seek(0, os.SEEK_CUR); n < inStat.Size() {
					fatal.Fatalf("%s: input file is output file\n", file.Name())
				}
			}
		}

		if simple {
			outBuf := bufio.NewWriterSize(os.Stdout, 4096)
			ok ^= simpleCat(file, outBuf)

			// Flush because we don't have a chance to in
			// simpleCat() because we use io.Copy()
			outBuf.Flush()
		} else {
			// If you want to know why, exactly, I chose
			// outBsize -1 + inBsize*4 + 20, read GNU's cat
			// source code. The tl;dr is the 20 is the counter
			// buffer, inBsize*4 is from potentially prepending
			// the control characters (M-^), and outBsize is
			// due to new tests for newlines.
			size := outBsize - 1 + inBsize*4 + 20
			outBuf := bufio.NewWriterSize(os.Stdout, size)
			inBuf := make([]byte, inBsize+1)
			ok ^= cat(file, inBuf, outBuf)
		}

		file.Close()
	}

	os.Exit(ok)
}
Example #3
0
// IsTerminal returns false on Windows.
func IsTerminal(f *os.File) bool {
	ft, _ := syscall.GetFileType(syscall.Handle(f.Fd()))
	return ft == syscall.FILE_TYPE_CHAR
}