Compare commits
20 Commits
Author | SHA1 | Date |
---|---|---|
兔子 | 0b6373e9e3 | 2 months ago |
兔子 | 0e7072d72a | 2 months ago |
兔子 | c1b5b0c2c5 | 7 months ago |
兔子 | a1fdeb62fc | 7 months ago |
兔子 | bae1b83843 | 7 months ago |
兔子 | 4190d59159 | 7 months ago |
兔子 | 42ad19e798 | 8 months ago |
兔子 | 232bc3835e | 2 years ago |
兔子 | 1333eb85bb | 2 years ago |
兔子 | dae84c0a85 | 2 years ago |
兔子 | ed85eb7616 | 3 years ago |
兔子 | d8c0d86ca0 | 3 years ago |
兔子 | bbd85885df | 3 years ago |
兔子 | c615c4bb00 | 3 years ago |
兔子 | 5353429b8c | 3 years ago |
兔子 | cf453821ef | 3 years ago |
兔子 | 3501166592 | 3 years ago |
Starainrt | d981a03b14 | 3 years ago |
Starainrt | fd5cf2c1f6 | 4 years ago |
Starainrt | c3ca057b96 | 4 years ago |
@ -0,0 +1,2 @@
|
||||
.idea
|
||||
.vscode
|
@ -0,0 +1,28 @@
|
||||
// +build darwin
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultFreq - frequency, in Hz, middle A
|
||||
DefaultFreq = 0.0
|
||||
// DefaultDuration - duration in milliseconds
|
||||
DefaultDuration = 0
|
||||
)
|
||||
|
||||
// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker).
|
||||
func Beep(freq float64, duration int) error {
|
||||
osa, err := exec.LookPath("osascript")
|
||||
if err != nil {
|
||||
// Output the only beep we can
|
||||
_, err = os.Stdout.Write([]byte{7})
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command(osa, "-e", `beep`)
|
||||
return cmd.Run()
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
// +build linux
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Constants
|
||||
const (
|
||||
// This number represents the fixed frequency of the original PC XT's timer chip, which is approximately 1.193 MHz. This number
|
||||
// is divided with the desired frequency to obtain a counter value, that is subsequently fed into the timer chip, tied to the PC speaker.
|
||||
clockTickRate = 1193180
|
||||
|
||||
// linux/kd.h, start sound generation (0 for off)
|
||||
kiocsound = 0x4B2F
|
||||
|
||||
// linux/input-event-codes.h
|
||||
evSnd = 0x12 // Event type
|
||||
sndTone = 0x02 // Sound
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultFreq - frequency, in Hz, middle A
|
||||
DefaultFreq = 440.0
|
||||
// DefaultDuration - duration in milliseconds
|
||||
DefaultDuration = 200
|
||||
)
|
||||
|
||||
// inputEvent represents linux/input.h event structure.
|
||||
type inputEvent struct {
|
||||
Time syscall.Timeval // time in seconds since epoch at which event occurred
|
||||
Type uint16 // event type
|
||||
Code uint16 // event code related to the event type
|
||||
Value int32 // event value related to the event type
|
||||
}
|
||||
|
||||
// ioctl system call manipulates the underlying device parameters of special files.
|
||||
func ioctl(fd, name, data uintptr) error {
|
||||
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, name, data)
|
||||
if e != 0 {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker).
|
||||
//
|
||||
// On Linux it needs permission to access `/dev/tty0` or `/dev/input/by-path/platform-pcspkr-event-spkr` files for writing,
|
||||
// and `pcspkr` module must be loaded. User must be in correct groups, usually `input` and/or `tty`.
|
||||
//
|
||||
// If it can not open device files, it will fallback to sending Bell character (https://en.wikipedia.org/wiki/Bell_character).
|
||||
// For bell character in X11 terminals you can enable bell with `xset b on`. For console check `setterm` and `--blength` or `--bfreq` options.
|
||||
//
|
||||
// On macOS this just sends bell character. Enable `Audible bell` in Terminal --> Preferences --> Settings --> Advanced.
|
||||
//
|
||||
// On Windows it uses Beep function via syscall.
|
||||
//
|
||||
// On Web it plays hard coded beep sound.
|
||||
func Beep(freq float64, duration int) error {
|
||||
if freq == 0 {
|
||||
freq = DefaultFreq
|
||||
} else if freq > 20000 {
|
||||
freq = 20000
|
||||
} else if freq < 0 {
|
||||
freq = DefaultFreq
|
||||
}
|
||||
|
||||
if duration == 0 {
|
||||
duration = DefaultDuration
|
||||
}
|
||||
|
||||
period := int(float64(clockTickRate) / freq)
|
||||
|
||||
var evdev bool
|
||||
|
||||
f, err := os.OpenFile("/dev/tty0", os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
e := err
|
||||
f, err = os.OpenFile("/dev/input/by-path/platform-pcspkr-event-spkr", os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
e = errors.New("beeep: " + e.Error() + "; " + err.Error())
|
||||
|
||||
// Output the only beep we can
|
||||
_, err = os.Stdout.Write([]byte{7})
|
||||
if err != nil {
|
||||
return errors.New(e.Error() + "; " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
evdev = true
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
if evdev { // Use Linux evdev API
|
||||
ev := inputEvent{}
|
||||
ev.Type = evSnd
|
||||
ev.Code = sndTone
|
||||
ev.Value = int32(freq)
|
||||
|
||||
d := *(*[unsafe.Sizeof(ev)]byte)(unsafe.Pointer(&ev))
|
||||
|
||||
// Start beep
|
||||
f.Write(d[:])
|
||||
|
||||
time.Sleep(time.Duration(duration) * time.Millisecond)
|
||||
|
||||
ev.Value = 0
|
||||
d = *(*[unsafe.Sizeof(ev)]byte)(unsafe.Pointer(&ev))
|
||||
|
||||
// Stop beep
|
||||
f.Write(d[:])
|
||||
} else { // Use ioctl
|
||||
// Start beep
|
||||
err = ioctl(f.Fd(), kiocsound, uintptr(period))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(duration) * time.Millisecond)
|
||||
|
||||
// Stop beep
|
||||
err = ioctl(f.Fd(), kiocsound, uintptr(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package staros
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
rat float64 = 1.059463094 //2^(1/12)
|
||||
C float64 = 493.8833013 * rat
|
||||
CU = C * rat * rat
|
||||
D = CU * rat
|
||||
DU = D * rat
|
||||
E = DU * rat
|
||||
F = E * rat
|
||||
FU = F * rat
|
||||
G = FU * rat
|
||||
GU = G * rat
|
||||
A = GU * rat
|
||||
AU = A * rat
|
||||
B = AU * rat
|
||||
)
|
||||
|
||||
func beepMusic(qual ...float64) {
|
||||
for _, v := range qual {
|
||||
fmt.Println(v)
|
||||
Beep(v, 700)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Music(t *testing.T) {
|
||||
beepMusic(G, D, A, AU, A, G, F, D, DU, D, C, D, AU/2, C, G/2, C, D)
|
||||
time.Sleep(time.Second * 3)
|
||||
beepMusic(D,AU,A,G,A,D*2,F*2,G*2,F*2,D*2,D*2,C*2,D*2,DU*2,D*2,AU,A,E,G,FU)
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
// +build windows
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultFreq - frequency, in Hz, middle A
|
||||
DefaultFreq = 587.0
|
||||
// DefaultDuration - duration in milliseconds
|
||||
DefaultDuration = 500
|
||||
)
|
||||
|
||||
// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker).
|
||||
func Beep(freq float64, duration int) error {
|
||||
if freq == 0 {
|
||||
freq = DefaultFreq
|
||||
} else if freq > 32767 {
|
||||
freq = 32767
|
||||
} else if freq < 37 {
|
||||
freq = DefaultFreq
|
||||
}
|
||||
|
||||
if duration == 0 {
|
||||
duration = DefaultDuration
|
||||
}
|
||||
|
||||
kernel32, _ := syscall.LoadLibrary("kernel32.dll")
|
||||
beep32, _ := syscall.GetProcAddress(kernel32, "Beep")
|
||||
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
|
||||
_, _, e := syscall.Syscall(uintptr(beep32), uintptr(2), uintptr(int(freq)), uintptr(duration), 0)
|
||||
if e != 0 {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
//+build darwin
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"b612.me/stario"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FileLock struct {
|
||||
fd int
|
||||
filepath string
|
||||
}
|
||||
|
||||
func (f *FileLock) openFileForLock() error {
|
||||
fd, err := syscall.Open(f.filepath, syscall.O_CREAT|syscall.O_RDONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.filepath = f.filepath
|
||||
f.fd = fd
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileLock) Lock(Exclusive bool) error {
|
||||
var lockType int
|
||||
if Exclusive {
|
||||
lockType = syscall.LOCK_EX
|
||||
} else {
|
||||
lockType = syscall.LOCK_SH
|
||||
}
|
||||
if err := f.openFileForLock(); err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.Flock(f.fd, lockType)
|
||||
}
|
||||
|
||||
func (f *FileLock) LockNoBlocking(Exclusive bool) error {
|
||||
var lockType int
|
||||
if Exclusive {
|
||||
lockType = syscall.LOCK_EX
|
||||
} else {
|
||||
lockType = syscall.LOCK_SH
|
||||
}
|
||||
if err := f.openFileForLock(); err != nil {
|
||||
return err
|
||||
}
|
||||
err := syscall.Flock(f.fd, lockType|syscall.LOCK_NB)
|
||||
if err != nil {
|
||||
syscall.Close(f.fd)
|
||||
if err == syscall.EWOULDBLOCK {
|
||||
return ERR_ALREADY_LOCKED
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *FileLock) Unlock() error {
|
||||
err := syscall.Flock(f.fd, syscall.LOCK_UN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.Close(f.fd)
|
||||
}
|
||||
|
||||
func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error {
|
||||
return stario.WaitUntilTimeout(tm, func(tmout chan struct{}) error {
|
||||
err := f.Lock(Exclusive)
|
||||
select {
|
||||
case <-tmout:
|
||||
f.Unlock()
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func timespecToTime(ts syscall.Timespec) time.Time {
|
||||
return time.Unix(int64(ts.Sec), int64(ts.Nsec))
|
||||
}
|
||||
|
||||
func GetFileCreationTime(fileinfo os.FileInfo) time.Time {
|
||||
return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Ctimespec)
|
||||
}
|
||||
|
||||
func GetFileAccessTime(fileinfo os.FileInfo) time.Time {
|
||||
return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Atimespec)
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package staros
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_FileLock(t *testing.T) {
|
||||
filename := "./test.file"
|
||||
lock := NewFileLock(filename)
|
||||
lock2 := NewFileLock(filename)
|
||||
fmt.Println("lock1", lock.LockNoBlocking(false))
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("lock2", lock2.LockWithTimeout(time.Second*5, false))
|
||||
fmt.Println("unlock1", lock.Unlock())
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("unlock2", lock2.Unlock())
|
||||
fmt.Println("lock2", lock2.LockNoBlocking(true))
|
||||
fmt.Println("unlock2", lock2.Unlock())
|
||||
os.Remove(filename)
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
module b612.me/staros
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
b612.me/stario v0.0.10
|
||||
b612.me/win32api v0.0.2
|
||||
b612.me/wincmd v0.0.4
|
||||
golang.org/x/sys v0.24.0
|
||||
)
|
@ -0,0 +1,75 @@
|
||||
b612.me/stario v0.0.10 h1:+cIyiDCBCjUfodMJDp4FLs+2E1jo7YENkN+sMEe6550=
|
||||
b612.me/stario v0.0.10/go.mod h1:1Owmu9jzKWgs4VsmeI8YWlGwLrCwPNM/bYpxkyn+MMk=
|
||||
b612.me/win32api v0.0.2 h1:5PwvPR5fYs3a/v+LjYdtRif+5Q04zRGLTVxmCYNjCpA=
|
||||
b612.me/win32api v0.0.2/go.mod h1:sj66sFJDKElEjOR+0YhdSW6b4kq4jsXu4T5/Hnpyot0=
|
||||
b612.me/wincmd v0.0.4 h1:fv9p1V8mw2HdUjaoZBWZy0T41JftueyLxAuch1MgtdI=
|
||||
b612.me/wincmd v0.0.4/go.mod h1:o3yPoE+DpVPHGKl/q1WT1C8OaIVwHEnpeNgMFqzlwD8=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,23 @@
|
||||
#hosts This file describes a number of hostname-to-address
|
||||
#mappings for the TCP/IP subsystem. It is mostly
|
||||
#used at boot time, when no name servers are running.
|
||||
#On small systems, this file can be used instead of a
|
||||
#"named" name server.
|
||||
#Syntax:
|
||||
#IP-Address Full-Qualifie
|
||||
#special IPv6 addre
|
||||
127.0.0.1 localhost
|
||||
127.0.0.1 b612
|
||||
8.8.8.8 ssh.b612.me
|
||||
#special IPv6 addresses
|
||||
::1 localhost ipv6-localhost ipv6-loopback
|
||||
fe00::0 ipv6-localnet
|
||||
ff00::0 ipv6-mcastprefix
|
||||
ff02::1 ipv6-allnodes
|
||||
ff02::2 ipv6-allrouters
|
||||
ff02::3 ipv6-allhosts
|
||||
1.2.3.4 ssh.b612.me
|
||||
4.5.6.7 dns.b612.me
|
||||
8.9.10.11 release-ftpd
|
||||
11.22.33.44 test.dns.set.b612.me remove.b612.me
|
||||
4.5.6.7 game.b612.me
|
@ -0,0 +1,26 @@
|
||||
// +build windows
|
||||
|
||||
package staros
|
||||
|
||||
import "b612.me/win32api"
|
||||
|
||||
// Memory 系统内存信息
|
||||
func Memory() (MemStatus, error) {
|
||||
var mem MemStatus
|
||||
ram := new(win32api.MEMORYSTATUSEX)
|
||||
_, err := win32api.GlobalMemoryStatusEx(ram)
|
||||
if err != nil {
|
||||
return mem, err
|
||||
}
|
||||
mem.All = uint64(ram.UllTotalPhys)
|
||||
mem.Free = uint64(ram.UllAvailPhys)
|
||||
mem.Available = uint64(ram.UllAvailPhys)
|
||||
mem.Used = uint64(mem.All - mem.Free)
|
||||
mem.SwapAll = uint64(ram.UllTotalPageFile)
|
||||
mem.SwapFree = uint64(ram.UllAvailPageFile)
|
||||
mem.SwapUsed = mem.SwapAll - mem.SwapFree
|
||||
mem.VirtualAll = uint64(mem.VirtualAll)
|
||||
mem.VirtualAvail = uint64(mem.VirtualAvail)
|
||||
mem.VirtualUsed = mem.VirtualAll - mem.VirtualUsed
|
||||
return mem, nil
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NetUsage() ([]NetAdapter, error) {
|
||||
data, err := ioutil.ReadFile("/proc/net/dev")
|
||||
if err != nil {
|
||||
return []NetAdapter{}, err
|
||||
}
|
||||
sps := strings.Split(strings.TrimSpace(string(data)), "\n")
|
||||
if len(sps) < 3 {
|
||||
return []NetAdapter{}, errors.New("No Adaptor")
|
||||
}
|
||||
var res []NetAdapter
|
||||
netLists := sps[2:]
|
||||
for _, v := range netLists {
|
||||
v = strings.ReplaceAll(v, " ", " ")
|
||||
for strings.Contains(v, " ") {
|
||||
v = strings.ReplaceAll(v, " ", " ")
|
||||
}
|
||||
v = strings.TrimSpace(v)
|
||||
card := strings.Split(v, " ")
|
||||
name := strings.ReplaceAll(card[0], ":", "")
|
||||
recvBytes, _ := strconv.Atoi(card[1])
|
||||
sendBytes, _ := strconv.Atoi(card[9])
|
||||
res = append(res, NetAdapter{name, uint64(recvBytes), uint64(sendBytes)})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NetUsageByname(name string) (NetAdapter, error) {
|
||||
ada, err := NetUsage()
|
||||
if err != nil {
|
||||
return NetAdapter{}, err
|
||||
}
|
||||
for _, v := range ada {
|
||||
if v.Name == name {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
return NetAdapter{}, errors.New("Not Found")
|
||||
}
|
||||
|
||||
func NetSpeeds(duration time.Duration) ([]NetSpeed, error) {
|
||||
list1, err := NetUsage()
|
||||
if err != nil {
|
||||
return []NetSpeed{}, err
|
||||
}
|
||||
time.Sleep(duration)
|
||||
list2, err := NetUsage()
|
||||
if err != nil {
|
||||
return []NetSpeed{}, err
|
||||
}
|
||||
if len(list1) > len(list2) {
|
||||
return []NetSpeed{}, errors.New("NetWork Adaptor Num Not ok")
|
||||
}
|
||||
var res []NetSpeed
|
||||
for k, v := range list1 {
|
||||
recv := float64(list2[k].RecvBytes-v.RecvBytes) / duration.Seconds()
|
||||
send := float64(list2[k].SendBytes-v.SendBytes) / duration.Seconds()
|
||||
res = append(res, NetSpeed{v.Name, recv, send})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NetSpeedsByName(duration time.Duration, name string) (NetSpeed, error) {
|
||||
ada, err := NetSpeeds(duration)
|
||||
if err != nil {
|
||||
return NetSpeed{}, err
|
||||
}
|
||||
for _, v := range ada {
|
||||
if v.Name == name {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
return NetSpeed{}, errors.New("Not Found")
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package staros
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_TrimSpace(t *testing.T) {
|
||||
|
||||
}
|
@ -0,0 +1,326 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NetUsage() ([]NetAdapter, error) {
|
||||
data, err := ioutil.ReadFile("/proc/net/dev")
|
||||
if err != nil {
|
||||
return []NetAdapter{}, err
|
||||
}
|
||||
sps := strings.Split(strings.TrimSpace(string(data)), "\n")
|
||||
if len(sps) < 3 {
|
||||
return []NetAdapter{}, errors.New("No Adaptor")
|
||||
}
|
||||
var res []NetAdapter
|
||||
netLists := sps[2:]
|
||||
for _, v := range netLists {
|
||||
v = strings.ReplaceAll(v, " ", " ")
|
||||
for strings.Contains(v, " ") {
|
||||
v = strings.ReplaceAll(v, " ", " ")
|
||||
}
|
||||
v = strings.TrimSpace(v)
|
||||
card := strings.Split(v, " ")
|
||||
name := strings.ReplaceAll(card[0], ":", "")
|
||||
recvBytes, _ := strconv.Atoi(card[1])
|
||||
sendBytes, _ := strconv.Atoi(card[9])
|
||||
res = append(res, NetAdapter{name, uint64(recvBytes), uint64(sendBytes)})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NetUsageByname(name string) (NetAdapter, error) {
|
||||
ada, err := NetUsage()
|
||||
if err != nil {
|
||||
return NetAdapter{}, err
|
||||
}
|
||||
for _, v := range ada {
|
||||
if v.Name == name {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
return NetAdapter{}, errors.New("Not Found")
|
||||
}
|
||||
|
||||
func NetSpeeds(duration time.Duration) ([]NetSpeed, error) {
|
||||
list1, err := NetUsage()
|
||||
if err != nil {
|
||||
return []NetSpeed{}, err
|
||||
}
|
||||
time.Sleep(duration)
|
||||
list2, err := NetUsage()
|
||||
if err != nil {
|
||||
return []NetSpeed{}, err
|
||||
}
|
||||
if len(list1) > len(list2) {
|
||||
return []NetSpeed{}, errors.New("NetWork Adaptor Num Not ok")
|
||||
}
|
||||
var res []NetSpeed
|
||||
for k, v := range list1 {
|
||||
recv := float64(list2[k].RecvBytes-v.RecvBytes) / duration.Seconds()
|
||||
send := float64(list2[k].SendBytes-v.SendBytes) / duration.Seconds()
|
||||
res = append(res, NetSpeed{
|
||||
Name: v.Name,
|
||||
RecvSpeeds: recv,
|
||||
SendSpeeds: send,
|
||||
RecvBytes: list2[k].RecvBytes,
|
||||
SendBytes: list2[k].SendBytes,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NetSpeedsByName(duration time.Duration, name string) (NetSpeed, error) {
|
||||
ada, err := NetSpeeds(duration)
|
||||
if err != nil {
|
||||
return NetSpeed{}, err
|
||||
}
|
||||
for _, v := range ada {
|
||||
if v.Name == name {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
return NetSpeed{}, errors.New("Not Found")
|
||||
}
|
||||
|
||||
// NetConnections return all TCP/UDP/UNIX DOMAIN SOCKET Connections
|
||||
// if your uid != 0 ,and analysePid==true ,you should have CAP_SYS_PRTACE and CAP_DAC_OVERRIDE/CAP_DAC_READ_SEARCH Caps
|
||||
func NetConnections(analysePid bool, types string) ([]NetConn, error) {
|
||||
var result []NetConn
|
||||
var inodeMap map[string]int64
|
||||
var err error
|
||||
var fileList []string
|
||||
if types == "" || strings.Contains(strings.ToLower(types), "all") {
|
||||
fileList = []string{
|
||||
"/proc/net/tcp",
|
||||
"/proc/net/tcp6",
|
||||
"/proc/net/udp",
|
||||
"/proc/net/udp6",
|
||||
"/proc/net/unix",
|
||||
}
|
||||
}
|
||||
if strings.Contains(strings.ToLower(types), "tcp") {
|
||||
fileList = append(fileList, "/proc/net/tcp", "/proc/net/tcp6")
|
||||
}
|
||||
if strings.Contains(strings.ToLower(types), "udp") {
|
||||
fileList = append(fileList, "/proc/net/udp", "/proc/net/udp6")
|
||||
}
|
||||
if strings.Contains(strings.ToLower(types), "unix") {
|
||||
fileList = append(fileList, "/proc/net/unix")
|
||||
}
|
||||
if analysePid {
|
||||
inodeMap, err = GetInodeMap()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
for _, file := range fileList {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
tmpRes, err := analyseNetFiles(data, inodeMap, file[strings.LastIndex(file, "/")+1:])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = append(result, tmpRes...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetInodeMap() (map[string]int64, error) {
|
||||
res := make(map[string]int64)
|
||||
paths, err := ioutil.ReadDir("/proc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range paths {
|
||||
if v.IsDir() && Exists("/proc/"+v.Name()+"/fd") {
|
||||
fds, err := ioutil.ReadDir("/proc/" + v.Name() + "/fd")
|
||||
if err != nil && Exists("/proc/"+v.Name()+"/fd") {
|
||||
return nil, err
|
||||
}
|
||||
for _, fd := range fds {
|
||||
socket, err := os.Readlink("/proc/" + v.Name() + "/fd/" + fd.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(socket, "socket") {
|
||||
continue
|
||||
}
|
||||
start := strings.Index(socket, "[")
|
||||
if start < 0 {
|
||||
continue
|
||||
}
|
||||
pid, err := strconv.ParseInt(v.Name(), 10, 64)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
res[socket[start+1:len(socket)-1]] = pid
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func analyseNetFiles(data []byte, inodeMap map[string]int64, typed string) ([]NetConn, error) {
|
||||
if typed == "unix" {
|
||||
return analyseUnixFiles(data, inodeMap, typed)
|
||||
}
|
||||
var result []NetConn
|
||||
strdata := strings.TrimSpace(string(data))
|
||||
strdata = remainOne(strdata, " ", " ")
|
||||
csvData := strings.Split(strdata, "\n")
|
||||
pidMap := make(map[int64]*Process)
|
||||
for line, lineData := range csvData {
|
||||
if line == 0 {
|
||||
continue
|
||||
}
|
||||
v := strings.Split(strings.TrimSpace(lineData), " ")
|
||||
var res NetConn
|
||||
ip, port, err := parseHexIpPort(v[1])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.LocalAddr = ip
|
||||
res.LocalPort = port
|
||||
ip, port, err = parseHexIpPort(v[2])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.RemoteAddr = ip
|
||||
res.RemotePort = port
|
||||
//connection state
|
||||
if strings.Contains(typed, "tcp") {
|
||||
state, err := strconv.ParseInt(strings.TrimSpace(v[3]), 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.Status = TCP_STATE[state]
|
||||
}
|
||||
txrx_queue := strings.Split(strings.TrimSpace(v[4]), ":")
|
||||
if len(txrx_queue) != 2 {
|
||||
return result, errors.New("not a valid net file")
|
||||
}
|
||||
tx_queue, err := strconv.ParseInt(txrx_queue[0], 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.TX_Queue = tx_queue
|
||||
rx_queue, err := strconv.ParseInt(txrx_queue[1], 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.RX_Queue = rx_queue
|
||||
timer := strings.Split(strings.TrimSpace(v[5]), ":")
|
||||
if len(timer) != 2 {
|
||||
return result, errors.New("not a valid net file")
|
||||
}
|
||||
switch timer[0] {
|
||||
case "00":
|
||||
res.TimerActive = "NO_TIMER"
|
||||
case "01":
|
||||
//重传定时器
|
||||
res.TimerActive = "RETRANSMIT"
|
||||
case "02":
|
||||
//连接定时器、FIN_WAIT_2定时器或TCP保活定时器
|
||||
res.TimerActive = "KEEPALIVE"
|
||||
case "03":
|
||||
//TIME_WAIT定时器
|
||||
res.TimerActive = "TIME_WAIT"
|
||||
case "04":
|
||||
//持续定时器
|
||||
res.TimerActive = "ZERO_WINDOW_PROBE"
|
||||
default:
|
||||
res.TimerActive = "UNKNOWN"
|
||||
}
|
||||
timerJif, err := strconv.ParseInt(timer[1], 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.TimerJiffies = timerJif
|
||||
timerCnt, err := strconv.ParseInt(strings.TrimSpace(v[6]), 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.RtoTimer = timerCnt
|
||||
res.Uid, err = strconv.ParseInt(v[7], 10, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.Inode = v[9]
|
||||
if inodeMap != nil && len(inodeMap) > 0 {
|
||||
var ok bool
|
||||
res.Pid, ok = inodeMap[res.Inode]
|
||||
if !ok {
|
||||
res.Pid = -1
|
||||
} else {
|
||||
_, ok := pidMap[res.Pid]
|
||||
if !ok {
|
||||
tmp, err := FindProcessByPid(res.Pid)
|
||||
if err != nil {
|
||||
pidMap[res.Pid] = nil
|
||||
} else {
|
||||
pidMap[res.Pid] = &tmp
|
||||
}
|
||||
}
|
||||
res.Process = pidMap[res.Pid]
|
||||
}
|
||||
}
|
||||
res.Typed = typed
|
||||
result = append(result, res)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func analyseUnixFiles(data []byte, inodeMap map[string]int64, typed string) ([]NetConn, error) {
|
||||
var result []NetConn
|
||||
strdata := strings.TrimSpace(string(data))
|
||||
strdata = remainOne(strdata, " ", " ")
|
||||
csvData := strings.Split(strdata, "\n")
|
||||
pidMap := make(map[int64]*Process)
|
||||
for line, lineData := range csvData {
|
||||
if line == 0 {
|
||||
continue
|
||||
}
|
||||
v := strings.Split(strings.TrimSpace(lineData), " ")
|
||||
var res NetConn
|
||||
res.Inode = v[6]
|
||||
if len(v) == 8 {
|
||||
res.Socket = v[7]
|
||||
}
|
||||
if inodeMap != nil && len(inodeMap) > 0 {
|
||||
var ok bool
|
||||
res.Pid, ok = inodeMap[res.Inode]
|
||||
if !ok {
|
||||
res.Pid = -1
|
||||
} else {
|
||||
_, ok := pidMap[res.Pid]
|
||||
if !ok || pidMap[res.Pid] == nil {
|
||||
tmp, err := FindProcessByPid(res.Pid)
|
||||
if err != nil {
|
||||
pidMap[res.Pid] = nil
|
||||
} else {
|
||||
pidMap[res.Pid] = &tmp
|
||||
}
|
||||
}
|
||||
if pidMap[res.Pid] != nil {
|
||||
res.Uid = int64(pidMap[res.Pid].RUID)
|
||||
res.Process = pidMap[res.Pid]
|
||||
}
|
||||
}
|
||||
}
|
||||
res.Typed = typed
|
||||
result = append(result, res)
|
||||
}
|
||||
return result, nil
|
||||
}
|
@ -0,0 +1,261 @@
|
||||
package sysconf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CSV struct {
|
||||
header []string
|
||||
text [][]string
|
||||
}
|
||||
|
||||
type CSVRow struct {
|
||||
header []string
|
||||
data []string
|
||||
}
|
||||
|
||||
type CSVValue struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
|
||||
func ParseCSV(data []byte, hasHeader bool) (csv CSV, err error) {
|
||||
strData := strings.Split(string(bytes.TrimSpace(data)), "\n")
|
||||
if len(strData) < 1 {
|
||||
err = fmt.Errorf("cannot parse data,invalid data format")
|
||||
}
|
||||
var header []string
|
||||
var text [][]string
|
||||
if hasHeader {
|
||||
header = csvAnalyse(strData[0])
|
||||
strData = strData[1:]
|
||||
} else {
|
||||
num := len(csvAnalyse(strData[0]))
|
||||
for i := 0; i < num; i++ {
|
||||
header = append(header, strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
for k, v := range strData {
|
||||
tmpData := csvAnalyse(v)
|
||||
if len(tmpData) != len(header) {
|
||||
err = fmt.Errorf("cannot parse data line %d,got %d values but need %d", k, len(tmpData), len(header))
|
||||
return
|
||||
}
|
||||
text = append(text, tmpData)
|
||||
}
|
||||
csv.header = header
|
||||
csv.text = text
|
||||
return
|
||||
}
|
||||
|
||||
func (csv *CSV) Header() []string {
|
||||
return csv.header
|
||||
}
|
||||
|
||||
func (csv *CSV) Data() [][]string {
|
||||
return csv.text
|
||||
}
|
||||
|
||||
func (csv *CSV) Row(row int) *CSVRow {
|
||||
if row >= len(csv.Data()) {
|
||||
return nil
|
||||
}
|
||||
return &CSVRow{
|
||||
header: csv.Header(),
|
||||
data: csv.Data()[row],
|
||||
}
|
||||
}
|
||||
|
||||
func (csv *CSVRow) Get(key string) *CSVValue {
|
||||
for k, v := range csv.header {
|
||||
if v == key {
|
||||
return &CSVValue{
|
||||
key: key,
|
||||
value: csv.data[k],
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (csv *CSVRow) Col(key int) *CSVValue {
|
||||
if key >= len(csv.header) {
|
||||
return nil
|
||||
}
|
||||
return &CSVValue{
|
||||
key: csv.header[key],
|
||||
value: csv.data[key],
|
||||
}
|
||||
}
|
||||
|
||||
func (csv *CSVRow) Header() []string {
|
||||
return csv.header
|
||||
}
|
||||
|
||||
func (csv *CSV) MapData() []map[string]string {
|
||||
var result []map[string]string
|
||||
for _, v := range csv.text {
|
||||
tmp := make(map[string]string)
|
||||
for k, v2 := range csv.header {
|
||||
tmp[v2] = v[k]
|
||||
}
|
||||
result = append(result, tmp)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func CsvAnalyse(data string) []string {
|
||||
return csvAnalyse(data)
|
||||
}
|
||||
|
||||
func csvAnalyse(data string) []string {
|
||||
var segStart bool = false
|
||||
var segReady bool = false
|
||||
var segSign string = ""
|
||||
var dotReady bool = false
|
||||
data = strings.TrimSpace(data)
|
||||
var result []string
|
||||
var seg string
|
||||
for k, v := range []rune(data) {
|
||||
if k == 0 && v != []rune(`"`)[0] {
|
||||
dotReady = true
|
||||
}
|
||||
if v != []rune(`,`)[0] && dotReady {
|
||||
segSign = `,`
|
||||
segStart = true
|
||||
dotReady = false
|
||||
if v == []rune(`"`)[0] {
|
||||
segSign = `"`
|
||||
continue
|
||||
}
|
||||
}
|
||||
if dotReady && v == []rune(`,`)[0] {
|
||||
//dotReady = false
|
||||
result = append(result, "")
|
||||
continue
|
||||
}
|
||||
|
||||
if v == []rune(`"`)[0] && segStart {
|
||||
if !segReady {
|
||||
segReady = true
|
||||
continue
|
||||
}
|
||||
seg += `"`
|
||||
segReady = false
|
||||
continue
|
||||
}
|
||||
if segReady && segSign == `"` && segStart {
|
||||
segReady = false
|
||||
segStart = false
|
||||
result = append(result, seg)
|
||||
segSign = ``
|
||||
seg = ""
|
||||
}
|
||||
|
||||
if v == []rune(`"`)[0] && !segStart {
|
||||
segStart = true
|
||||
segReady = false
|
||||
segSign = `"`
|
||||
continue
|
||||
}
|
||||
if v == []rune(`,`)[0] && !segStart {
|
||||
dotReady = true
|
||||
}
|
||||
if v == []rune(`,`)[0] && segStart && segSign == "," {
|
||||
segStart = false
|
||||
result = append(result, seg)
|
||||
dotReady = true
|
||||
segSign = ``
|
||||
seg = ""
|
||||
}
|
||||
if segStart {
|
||||
seg = string(append([]rune(seg), v))
|
||||
}
|
||||
}
|
||||
if len(data) != 0 && len(result) == 0 && seg == "" {
|
||||
result = append(result, data)
|
||||
} else {
|
||||
result = append(result, seg)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func MarshalCSV(header []string, ins interface{}) ([]byte, error) {
|
||||
var result [][]string
|
||||
t := reflect.TypeOf(ins)
|
||||
v := reflect.ValueOf(ins)
|
||||
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
|
||||
return nil, errors.New("not a Slice or Array")
|
||||
}
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
subT := reflect.TypeOf(v.Index(i).Interface())
|
||||
subV := reflect.ValueOf(v.Index(i).Interface())
|
||||
if subV.Kind() == reflect.Slice || subV.Kind() == reflect.Array {
|
||||
if subT.Kind() == reflect.Ptr {
|
||||
subV = subV.Elem()
|
||||
}
|
||||
var tmp []string
|
||||
for j := 0; j < subV.Len(); j++ {
|
||||
tmp = append(tmp, fmt.Sprint(reflect.ValueOf(subV.Index(j))))
|
||||
}
|
||||
result = append(result, tmp)
|
||||
}
|
||||
if subV.Kind() == reflect.Struct {
|
||||
var tmp []string
|
||||
if subT.Kind() == reflect.Ptr {
|
||||
subV = subV.Elem()
|
||||
}
|
||||
for i := 0; i < subV.NumField(); i++ {
|
||||
tmp = append(tmp, fmt.Sprint(subV.Field(i)))
|
||||
}
|
||||
result = append(result, tmp)
|
||||
}
|
||||
}
|
||||
|
||||
return buildCSV(header,result)
|
||||
}
|
||||
|
||||
func buildCSV(header []string, data [][]string) ([]byte, error) {
|
||||
var result []string
|
||||
var length int
|
||||
build := func(slc []string) string {
|
||||
for k, v := range slc {
|
||||
if strings.Index(v, `"`) >= 0 {
|
||||
v = strings.ReplaceAll(v, `"`, `""`)
|
||||
}
|
||||
if strings.Index(v,"\n")>=0 {
|
||||
v=strings.ReplaceAll(v,"\n",`\n`)
|
||||
}
|
||||
if strings.Index(v,"\r")>=0 {
|
||||
v=strings.ReplaceAll(v,"\r",`\r`)
|
||||
}
|
||||
v = `"` + v + `"`
|
||||
slc[k] = v
|
||||
}
|
||||
return strings.Join(slc, ",")
|
||||
}
|
||||
if len(header) != 0 {
|
||||
result = append(result, build(header))
|
||||
length = len(header)
|
||||
} else {
|
||||
length = len(data[0])
|
||||
}
|
||||
for k, v := range data {
|
||||
if len(v) != length {
|
||||
return nil, fmt.Errorf("line %d got length %d ,but need %d", k, len(v), length)
|
||||
}
|
||||
result = append(result, build(v))
|
||||
}
|
||||
return []byte(strings.Join(result, "\n")), nil
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package sysconf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_csv(t *testing.T) {
|
||||
//var test Sqlplus
|
||||
var text=`
|
||||
姓名,班级,性别,年龄
|
||||
张三,"我,不""知道",boy,23
|
||||
"里斯","哈哈",girl,23
|
||||
`
|
||||
fmt.Println(csvAnalyse(`请求权,lkjdshck,dsvdsv,"sdvkjsdv,",=dsvdsv,"=,dsvsdv"`))
|
||||
a,b:=ParseCSV([]byte(text),true)
|
||||
fmt.Println(b)
|
||||
fmt.Println(a.Row(0).Col(3).MustInt())
|
||||
}
|
||||
|
||||
type csvtest struct {
|
||||
A string
|
||||
B int
|
||||
}
|
||||
func Test_Masharl(t *testing.T) {
|
||||
//var test Sqlplus
|
||||
/*
|
||||
var a []csvtest = []csvtest{
|
||||
{"lala",1},
|
||||
{"haha",34},
|
||||
}
|
||||
*/
|
||||
var a [][]string
|
||||
a=append(a,[]string{"a","b","c"})
|
||||
a=append(a,[]string{"1",`s"s"d`,"3"})
|
||||
b,_:=MarshalCSV([]string{},a)
|
||||
fmt.Println(string(b))
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
package sysconf
|
||||
|
||||
import "strconv"
|
||||
|
||||
func (csv *CSVValue)Key()string {
|
||||
return csv.key
|
||||
}
|
||||
|
||||
func (csv *CSVValue)Int()(int,error) {
|
||||
tmp,err:=strconv.Atoi(csv.value)
|
||||
return tmp,err
|
||||
}
|
||||
|
||||
func (csv *CSVValue)MustInt()int {
|
||||
tmp,err:=csv.Int()
|
||||
if err!=nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
func (csv *CSVValue)Int64()(int64,error) {
|
||||
tmp,err:=strconv.ParseInt(csv.value,10,64)
|
||||
return tmp,err
|
||||
}
|
||||
|
||||
func (csv *CSVValue)MustInt64()int64 {
|
||||
tmp,err:=csv.Int64()
|
||||
if err!=nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
func (csv *CSVValue)Int32()(int32,error) {
|
||||
tmp,err:=strconv.ParseInt(csv.value,10,32)
|
||||
return int32(tmp),err
|
||||
}
|
||||
|
||||
func (csv *CSVValue)MustInt32()int32 {
|
||||
tmp,err:=csv.Int32()
|
||||
if err!=nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
func (csv *CSVValue)Uint64()(uint64,error) {
|
||||
tmp,err:=strconv.ParseUint(csv.value,10,64)
|
||||
return tmp,err
|
||||
}
|
||||
|
||||
func (csv *CSVValue)MustUint64()uint64 {
|
||||
tmp,err:=csv.Uint64()
|
||||
if err!=nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
func (csv *CSVValue)Uint32()(uint32,error) {
|
||||
tmp,err:=strconv.ParseUint(csv.value,10,32)
|
||||
return uint32(tmp),err
|
||||
}
|
||||
|
||||
func (csv *CSVValue)MustUint32()uint32 {
|
||||
tmp,err:=csv.Uint32()
|
||||
if err!=nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
func (csv *CSVValue)String()string {
|
||||
return csv.value
|
||||
}
|
||||
|
||||
func (csv *CSVValue)Byte()[]byte {
|
||||
return []byte(csv.value)
|
||||
}
|
||||
|
||||
|
||||
func (csv *CSVValue)Bool()(bool,error) {
|
||||
tmp,err:=strconv.ParseBool(csv.value)
|
||||
return tmp,err
|
||||
}
|
||||
|
||||
func (csv *CSVValue)MustBool()bool {
|
||||
tmp,err:=csv.Bool()
|
||||
if err!=nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
func (csv *CSVValue)Float64()(float64,error) {
|
||||
tmp,err:=strconv.ParseFloat(csv.value,64)
|
||||
return tmp,err
|
||||
}
|
||||
|
||||
func (csv *CSVValue)MustFloat64()float64 {
|
||||
tmp,err:=csv.Float64()
|
||||
if err!=nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
func (csv *CSVValue)Float32()(float32,error) {
|
||||
tmp,err:=strconv.ParseFloat(csv.value,32)
|
||||
return float32(tmp),err
|
||||
}
|
||||
|
||||
func (csv *CSVValue)MustFloat32()float32 {
|
||||
tmp,err:=csv.Float32()
|
||||
if err!=nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmp
|
||||
}
|
Loading…
Reference in New Issue