Source file src/syscall/syscall_solaris.go

     1  // Copyright 2009 The Go 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  // Solaris system calls.
     6  // This file is compiled as ordinary Go code,
     7  // but it is also input to mksyscall,
     8  // which parses the //sys lines and generates system call stubs.
     9  // Note that sometimes we use a lowercase //sys name and wrap
    10  // it in our own nicer implementation, either here or in
    11  // syscall_solaris.go or syscall_unix.go.
    12  
    13  package syscall
    14  
    15  import "unsafe"
    16  
    17  func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
    18  func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
    19  func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
    20  func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
    21  
    22  const _F_DUP2FD_CLOEXEC = F_DUP2FD_CLOEXEC
    23  
    24  // Implemented in asm_solaris_amd64.s.
    25  func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
    26  func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
    27  
    28  type SockaddrDatalink struct {
    29  	Family uint16
    30  	Index  uint16
    31  	Type   uint8
    32  	Nlen   uint8
    33  	Alen   uint8
    34  	Slen   uint8
    35  	Data   [244]int8
    36  	raw    RawSockaddrDatalink
    37  }
    38  
    39  func direntIno(buf []byte) (uint64, bool) {
    40  	return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
    41  }
    42  
    43  func direntReclen(buf []byte) (uint64, bool) {
    44  	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
    45  }
    46  
    47  func direntNamlen(buf []byte) (uint64, bool) {
    48  	reclen, ok := direntReclen(buf)
    49  	if !ok {
    50  		return 0, false
    51  	}
    52  	return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
    53  }
    54  
    55  func Pipe(p []int) (err error) {
    56  	return Pipe2(p, 0)
    57  }
    58  
    59  //sysnb	pipe2(p *[2]_C_int, flags int) (err error)
    60  
    61  func Pipe2(p []int, flags int) error {
    62  	if len(p) != 2 {
    63  		return EINVAL
    64  	}
    65  	var pp [2]_C_int
    66  	err := pipe2(&pp, flags)
    67  	if err == nil {
    68  		p[0] = int(pp[0])
    69  		p[1] = int(pp[1])
    70  	}
    71  	return err
    72  }
    73  
    74  //sys   accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) = libsocket.accept4
    75  
    76  func Accept4(fd int, flags int) (int, Sockaddr, error) {
    77  	var rsa RawSockaddrAny
    78  	var addrlen _Socklen = SizeofSockaddrAny
    79  	nfd, err := accept4(fd, &rsa, &addrlen, flags)
    80  	if err != nil {
    81  		return 0, nil, err
    82  	}
    83  	if addrlen > SizeofSockaddrAny {
    84  		panic("RawSockaddrAny too small")
    85  	}
    86  	sa, err := anyToSockaddr(&rsa)
    87  	if err != nil {
    88  		Close(nfd)
    89  		return 0, nil, err
    90  	}
    91  	return nfd, sa, nil
    92  }
    93  
    94  func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
    95  	if sa.Port < 0 || sa.Port > 0xFFFF {
    96  		return nil, 0, EINVAL
    97  	}
    98  	sa.raw.Family = AF_INET
    99  	p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
   100  	p[0] = byte(sa.Port >> 8)
   101  	p[1] = byte(sa.Port)
   102  	sa.raw.Addr = sa.Addr
   103  	return unsafe.Pointer(&sa.raw), SizeofSockaddrInet4, nil
   104  }
   105  
   106  func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) {
   107  	if sa.Port < 0 || sa.Port > 0xFFFF {
   108  		return nil, 0, EINVAL
   109  	}
   110  	sa.raw.Family = AF_INET6
   111  	p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
   112  	p[0] = byte(sa.Port >> 8)
   113  	p[1] = byte(sa.Port)
   114  	sa.raw.Scope_id = sa.ZoneId
   115  	sa.raw.Addr = sa.Addr
   116  	return unsafe.Pointer(&sa.raw), SizeofSockaddrInet6, nil
   117  }
   118  
   119  func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) {
   120  	name := sa.Name
   121  	n := len(name)
   122  	if n >= len(sa.raw.Path) {
   123  		return nil, 0, EINVAL
   124  	}
   125  	sa.raw.Family = AF_UNIX
   126  	for i := 0; i < n; i++ {
   127  		sa.raw.Path[i] = int8(name[i])
   128  	}
   129  	// length is family (uint16), name, NUL.
   130  	sl := _Socklen(2)
   131  	if n > 0 {
   132  		sl += _Socklen(n) + 1
   133  	}
   134  	if sa.raw.Path[0] == '@' {
   135  		sa.raw.Path[0] = 0
   136  		// Don't count trailing NUL for abstract address.
   137  		sl--
   138  	}
   139  
   140  	return unsafe.Pointer(&sa.raw), sl, nil
   141  }
   142  
   143  func Getsockname(fd int) (sa Sockaddr, err error) {
   144  	var rsa RawSockaddrAny
   145  	var len _Socklen = SizeofSockaddrAny
   146  	if err = getsockname(fd, &rsa, &len); err != nil {
   147  		return
   148  	}
   149  	return anyToSockaddr(&rsa)
   150  }
   151  
   152  const ImplementsGetwd = true
   153  
   154  //sys	Getcwd(buf []byte) (n int, err error)
   155  
   156  func Getwd() (wd string, err error) {
   157  	var buf [PathMax]byte
   158  	// Getcwd will return an error if it failed for any reason.
   159  	_, err = Getcwd(buf[0:])
   160  	if err != nil {
   161  		return "", err
   162  	}
   163  	n := clen(buf[:])
   164  	if n < 1 {
   165  		return "", EINVAL
   166  	}
   167  	return string(buf[:n]), nil
   168  }
   169  
   170  /*
   171   * Wrapped
   172   */
   173  
   174  //sysnb	getgroups(ngid int, gid *_Gid_t) (n int, err error)
   175  //sysnb	setgroups(ngid int, gid *_Gid_t) (err error)
   176  
   177  func Getgroups() (gids []int, err error) {
   178  	n, err := getgroups(0, nil)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	if n == 0 {
   183  		return nil, nil
   184  	}
   185  
   186  	// Sanity check group count. Max is 16 on BSD.
   187  	if n < 0 || n > 1000 {
   188  		return nil, EINVAL
   189  	}
   190  
   191  	a := make([]_Gid_t, n)
   192  	n, err = getgroups(n, &a[0])
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  	gids = make([]int, n)
   197  	for i, v := range a[0:n] {
   198  		gids[i] = int(v)
   199  	}
   200  	return
   201  }
   202  
   203  func Setgroups(gids []int) (err error) {
   204  	if len(gids) == 0 {
   205  		return setgroups(0, nil)
   206  	}
   207  
   208  	a := make([]_Gid_t, len(gids))
   209  	for i, v := range gids {
   210  		a[i] = _Gid_t(v)
   211  	}
   212  	return setgroups(len(a), &a[0])
   213  }
   214  
   215  func ReadDirent(fd int, buf []byte) (n int, err error) {
   216  	// Final argument is (basep *uintptr) and the syscall doesn't take nil.
   217  	// TODO(rsc): Can we use a single global basep for all calls?
   218  	return Getdents(fd, buf, new(uintptr))
   219  }
   220  
   221  // Wait status is 7 bits at bottom, either 0 (exited),
   222  // 0x7F (stopped), or a signal number that caused an exit.
   223  // The 0x80 bit is whether there was a core dump.
   224  // An extra number (exit code, signal causing a stop)
   225  // is in the high bits.
   226  
   227  type WaitStatus uint32
   228  
   229  const (
   230  	mask  = 0x7F
   231  	core  = 0x80
   232  	shift = 8
   233  
   234  	exited  = 0
   235  	stopped = 0x7F
   236  )
   237  
   238  func (w WaitStatus) Exited() bool { return w&mask == exited }
   239  
   240  func (w WaitStatus) ExitStatus() int {
   241  	if w&mask != exited {
   242  		return -1
   243  	}
   244  	return int(w >> shift)
   245  }
   246  
   247  func (w WaitStatus) Signaled() bool { return w&mask != stopped && w&mask != 0 }
   248  
   249  func (w WaitStatus) Signal() Signal {
   250  	sig := Signal(w & mask)
   251  	if sig == stopped || sig == 0 {
   252  		return -1
   253  	}
   254  	return sig
   255  }
   256  
   257  func (w WaitStatus) CoreDump() bool { return w.Signaled() && w&core != 0 }
   258  
   259  func (w WaitStatus) Stopped() bool { return w&mask == stopped && Signal(w>>shift) != SIGSTOP }
   260  
   261  func (w WaitStatus) Continued() bool { return w&mask == stopped && Signal(w>>shift) == SIGSTOP }
   262  
   263  func (w WaitStatus) StopSignal() Signal {
   264  	if !w.Stopped() {
   265  		return -1
   266  	}
   267  	return Signal(w>>shift) & 0xFF
   268  }
   269  
   270  func (w WaitStatus) TrapCause() int { return -1 }
   271  
   272  func wait4(pid uintptr, wstatus *WaitStatus, options uintptr, rusage *Rusage) (wpid uintptr, err uintptr)
   273  
   274  func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
   275  	r0, e1 := wait4(uintptr(pid), wstatus, uintptr(options), rusage)
   276  	if e1 != 0 {
   277  		err = Errno(e1)
   278  	}
   279  	return int(r0), err
   280  }
   281  
   282  func gethostname() (name string, err uintptr)
   283  
   284  func Gethostname() (name string, err error) {
   285  	name, e1 := gethostname()
   286  	if e1 != 0 {
   287  		err = Errno(e1)
   288  	}
   289  	return name, err
   290  }
   291  
   292  func UtimesNano(path string, ts []Timespec) error {
   293  	if len(ts) != 2 {
   294  		return EINVAL
   295  	}
   296  	return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
   297  }
   298  
   299  //sys	fcntl(fd int, cmd int, arg int) (val int, err error)
   300  
   301  // FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
   302  func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
   303  	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_fcntl)), 3, uintptr(fd), uintptr(cmd), uintptr(unsafe.Pointer(lk)), 0, 0, 0)
   304  	if e1 != 0 {
   305  		return e1
   306  	}
   307  	return nil
   308  }
   309  
   310  func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) {
   311  	switch rsa.Addr.Family {
   312  	case AF_UNIX:
   313  		pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa))
   314  		sa := new(SockaddrUnix)
   315  		// Assume path ends at NUL.
   316  		// This is not technically the Solaris semantics for
   317  		// abstract Unix domain sockets -- they are supposed
   318  		// to be uninterpreted fixed-size binary blobs -- but
   319  		// everyone uses this convention.
   320  		n := 0
   321  		for n < len(pp.Path) && pp.Path[n] != 0 {
   322  			n++
   323  		}
   324  		bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
   325  		sa.Name = string(bytes)
   326  		return sa, nil
   327  
   328  	case AF_INET:
   329  		pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa))
   330  		sa := new(SockaddrInet4)
   331  		p := (*[2]byte)(unsafe.Pointer(&pp.Port))
   332  		sa.Port = int(p[0])<<8 + int(p[1])
   333  		sa.Addr = pp.Addr
   334  		return sa, nil
   335  
   336  	case AF_INET6:
   337  		pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa))
   338  		sa := new(SockaddrInet6)
   339  		p := (*[2]byte)(unsafe.Pointer(&pp.Port))
   340  		sa.Port = int(p[0])<<8 + int(p[1])
   341  		sa.ZoneId = pp.Scope_id
   342  		sa.Addr = pp.Addr
   343  		return sa, nil
   344  	}
   345  	return nil, EAFNOSUPPORT
   346  }
   347  
   348  //sys	accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) = libsocket.accept
   349  
   350  func Accept(fd int) (nfd int, sa Sockaddr, err error) {
   351  	var rsa RawSockaddrAny
   352  	var len _Socklen = SizeofSockaddrAny
   353  	nfd, err = accept(fd, &rsa, &len)
   354  	if err != nil {
   355  		return
   356  	}
   357  	sa, err = anyToSockaddr(&rsa)
   358  	if err != nil {
   359  		Close(nfd)
   360  		nfd = 0
   361  	}
   362  	return
   363  }
   364  
   365  func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
   366  	var msg Msghdr
   367  	msg.Name = (*byte)(unsafe.Pointer(rsa))
   368  	msg.Namelen = uint32(SizeofSockaddrAny)
   369  	var iov Iovec
   370  	if len(p) > 0 {
   371  		iov.Base = (*int8)(unsafe.Pointer(&p[0]))
   372  		iov.SetLen(len(p))
   373  	}
   374  	var dummy int8
   375  	if len(oob) > 0 {
   376  		// receive at least one normal byte
   377  		if len(p) == 0 {
   378  			iov.Base = &dummy
   379  			iov.SetLen(1)
   380  		}
   381  		msg.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
   382  		msg.Accrightslen = int32(len(oob))
   383  	}
   384  	msg.Iov = &iov
   385  	msg.Iovlen = 1
   386  	if n, err = recvmsg(fd, &msg, flags); err != nil {
   387  		return
   388  	}
   389  	oobn = int(msg.Accrightslen)
   390  	return
   391  }
   392  
   393  //sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_sendmsg
   394  
   395  func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
   396  	var msg Msghdr
   397  	msg.Name = (*byte)(unsafe.Pointer(ptr))
   398  	msg.Namelen = uint32(salen)
   399  	var iov Iovec
   400  	if len(p) > 0 {
   401  		iov.Base = (*int8)(unsafe.Pointer(&p[0]))
   402  		iov.SetLen(len(p))
   403  	}
   404  	var dummy int8
   405  	if len(oob) > 0 {
   406  		// send at least one normal byte
   407  		if len(p) == 0 {
   408  			iov.Base = &dummy
   409  			iov.SetLen(1)
   410  		}
   411  		msg.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
   412  		msg.Accrightslen = int32(len(oob))
   413  	}
   414  	msg.Iov = &iov
   415  	msg.Iovlen = 1
   416  	if n, err = sendmsg(fd, &msg, flags); err != nil {
   417  		return 0, err
   418  	}
   419  	if len(oob) > 0 && len(p) == 0 {
   420  		n = 0
   421  	}
   422  	return n, nil
   423  }
   424  
   425  /*
   426   * Exposed directly
   427   */
   428  //sys	Access(path string, mode uint32) (err error)
   429  //sys	Adjtime(delta *Timeval, olddelta *Timeval) (err error)
   430  //sys	Chdir(path string) (err error)
   431  //sys	Chmod(path string, mode uint32) (err error)
   432  //sys	Chown(path string, uid int, gid int) (err error)
   433  //sys	Chroot(path string) (err error)
   434  //sys	Close(fd int) (err error)
   435  //sys	Dup(fd int) (nfd int, err error)
   436  //sys	Fchdir(fd int) (err error)
   437  //sys	Fchmod(fd int, mode uint32) (err error)
   438  //sys	Fchown(fd int, uid int, gid int) (err error)
   439  //sys	Fpathconf(fd int, name int) (val int, err error)
   440  //sys	Fstat(fd int, stat *Stat_t) (err error)
   441  //sys	Getdents(fd int, buf []byte, basep *uintptr) (n int, err error)
   442  //sysnb	Getgid() (gid int)
   443  //sysnb	Getpid() (pid int)
   444  //sys	Geteuid() (euid int)
   445  //sys	Getegid() (egid int)
   446  //sys	Getppid() (ppid int)
   447  //sys	Getpriority(which int, who int) (n int, err error)
   448  //sysnb	Getrlimit(which int, lim *Rlimit) (err error)
   449  //sysnb	Getrusage(who int, rusage *Rusage) (err error)
   450  //sysnb	Gettimeofday(tv *Timeval) (err error)
   451  //sysnb	Getuid() (uid int)
   452  //sys	Kill(pid int, signum Signal) (err error)
   453  //sys	Lchown(path string, uid int, gid int) (err error)
   454  //sys	Link(path string, link string) (err error)
   455  //sys	Listen(s int, backlog int) (err error) = libsocket.__xnet_listen
   456  //sys	Lstat(path string, stat *Stat_t) (err error)
   457  //sys	Mkdir(path string, mode uint32) (err error)
   458  //sys	Mknod(path string, mode uint32, dev int) (err error)
   459  //sys	Nanosleep(time *Timespec, leftover *Timespec) (err error)
   460  //sys	Open(path string, mode int, perm uint32) (fd int, err error)
   461  //sys	Pathconf(path string, name int) (val int, err error)
   462  //sys	pread(fd int, p []byte, offset int64) (n int, err error)
   463  //sys	pwrite(fd int, p []byte, offset int64) (n int, err error)
   464  //sys	read(fd int, p []byte) (n int, err error)
   465  //sys	Readlink(path string, buf []byte) (n int, err error)
   466  //sys	Rename(from string, to string) (err error)
   467  //sys	Rmdir(path string) (err error)
   468  //sys	Seek(fd int, offset int64, whence int) (newoffset int64, err error) = lseek
   469  //sys	sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = libsendfile.sendfile
   470  //sysnb	Setegid(egid int) (err error)
   471  //sysnb	Seteuid(euid int) (err error)
   472  //sysnb	Setgid(gid int) (err error)
   473  //sysnb	Setpgid(pid int, pgid int) (err error)
   474  //sys	Setpriority(which int, who int, prio int) (err error)
   475  //sysnb	Setregid(rgid int, egid int) (err error)
   476  //sysnb	Setreuid(ruid int, euid int) (err error)
   477  //sysnb	Setrlimit(which int, lim *Rlimit) (err error)
   478  //sysnb	Setsid() (pid int, err error)
   479  //sysnb	Setuid(uid int) (err error)
   480  //sys	Shutdown(s int, how int) (err error) = libsocket.shutdown
   481  //sys	Stat(path string, stat *Stat_t) (err error)
   482  //sys	Symlink(path string, link string) (err error)
   483  //sys	Sync() (err error)
   484  //sys	Truncate(path string, length int64) (err error)
   485  //sys	Fsync(fd int) (err error)
   486  //sys	Ftruncate(fd int, length int64) (err error)
   487  //sys	Umask(newmask int) (oldmask int)
   488  //sys	Unlink(path string) (err error)
   489  //sys	utimes(path string, times *[2]Timeval) (err error)
   490  //sys	bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) = libsocket.__xnet_bind
   491  //sys	connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) = libsocket.__xnet_connect
   492  //sys	mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
   493  //sys	munmap(addr uintptr, length uintptr) (err error)
   494  //sys	sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) = libsocket.__xnet_sendto
   495  //sys	socket(domain int, typ int, proto int) (fd int, err error) = libsocket.__xnet_socket
   496  //sysnb	socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) = libsocket.__xnet_socketpair
   497  //sys	write(fd int, p []byte) (n int, err error)
   498  //sys	getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) = libsocket.__xnet_getsockopt
   499  //sysnb	getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) = libsocket.getpeername
   500  //sys	getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) = libsocket.getsockname
   501  //sys	setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) = libsocket.setsockopt
   502  //sys	recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) = libsocket.recvfrom
   503  //sys	recvmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_recvmsg
   504  //sys	getexecname() (path unsafe.Pointer, err error) = libc.getexecname
   505  //sys	utimensat(dirfd int, path string, times *[2]Timespec, flag int) (err error)
   506  
   507  func Getexecname() (path string, err error) {
   508  	ptr, err := getexecname()
   509  	if err != nil {
   510  		return "", err
   511  	}
   512  	bytes := (*[1 << 29]byte)(ptr)[:]
   513  	for i, b := range bytes {
   514  		if b == 0 {
   515  			return string(bytes[:i]), nil
   516  		}
   517  	}
   518  	panic("unreachable")
   519  }
   520  
   521  func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
   522  	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_read)), 3, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf), 0, 0, 0)
   523  	n = int(r0)
   524  	if e1 != 0 {
   525  		err = e1
   526  	}
   527  	return
   528  }
   529  
   530  func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
   531  	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_write)), 3, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf), 0, 0, 0)
   532  	n = int(r0)
   533  	if e1 != 0 {
   534  		err = e1
   535  	}
   536  	return
   537  }
   538  
   539  func Utimes(path string, tv []Timeval) error {
   540  	if len(tv) != 2 {
   541  		return EINVAL
   542  	}
   543  	return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
   544  }
   545  

View as plain text