diff options
Diffstat (limited to 'widgets')
-rw-r--r-- | widgets/battery.go | 94 | ||||
-rw-r--r-- | widgets/batterygauge.go | 83 | ||||
-rw-r--r-- | widgets/cpu.go | 112 | ||||
-rw-r--r-- | widgets/disk.go | 169 | ||||
-rw-r--r-- | widgets/drawille-go/LICENSE.md | 661 | ||||
-rw-r--r-- | widgets/drawille-go/README.md | 24 | ||||
-rw-r--r-- | widgets/drawille-go/drawille.go | 275 | ||||
-rw-r--r-- | widgets/entry.go | 113 | ||||
-rw-r--r-- | widgets/gauge.go | 22 | ||||
-rw-r--r-- | widgets/linegraph.go | 228 | ||||
-rw-r--r-- | widgets/linegraph_test.go | 60 | ||||
-rw-r--r-- | widgets/mem.go | 76 | ||||
-rw-r--r-- | widgets/metrics.go | 19 | ||||
-rw-r--r-- | widgets/net.go | 134 | ||||
-rw-r--r-- | widgets/proc.go | 29 | ||||
-rw-r--r-- | widgets/proc_freebsd.go | 2 | ||||
-rw-r--r-- | widgets/proc_linux.go | 3 | ||||
-rw-r--r-- | widgets/proc_windows.go | 4 | ||||
-rw-r--r-- | widgets/sparkline.go | 106 | ||||
-rw-r--r-- | widgets/statusbar.go | 14 | ||||
-rw-r--r-- | widgets/table.go | 221 | ||||
-rw-r--r-- | widgets/temp.go | 93 | ||||
-rw-r--r-- | widgets/widgets.go | 13 |
23 files changed, 1979 insertions, 576 deletions
diff --git a/widgets/battery.go b/widgets/battery.go index 4a8dcbf..0fcf874 100644 --- a/widgets/battery.go +++ b/widgets/battery.go @@ -2,61 +2,38 @@ package widgets import ( "fmt" - "log" "math" "strconv" "time" - "github.com/VictoriaMetrics/metrics" - "github.com/distatus/battery" - - ui "github.com/xxxserxxx/gotop/v4/termui" + "github.com/xxxserxxx/gotop/v4/devices" ) type BatteryWidget struct { - *ui.LineGraph + *LineGraph updateInterval time.Duration + d []*devices.Batteries } func NewBatteryWidget(horizontalScale int) *BatteryWidget { - self := &BatteryWidget{ - LineGraph: ui.NewLineGraph(), + bw := &BatteryWidget{ + LineGraph: NewLineGraph(), updateInterval: time.Minute, + d: make([]*devices.Batteries, 0), } - self.Title = tr.Value("widget.label.battery") - self.HorizontalScale = horizontalScale + bw.Title = tr.Value("widget.label.battery") + bw.HorizontalScale = horizontalScale + + return bw +} +func (b *BatteryWidget) Attach(n *devices.Batteries) { + b.d = append(b.d, n) // intentional duplicate // adds 2 datapoints to the graph, otherwise the dot is difficult to see - self.update() - self.update() + b.Update() + b.Update() - go func() { - for range time.NewTicker(self.updateInterval).C { - self.Lock() - self.update() - self.Unlock() - } - }() - - return self -} - -func (b *BatteryWidget) EnableMetric() { - bats, err := battery.GetAll() - if err != nil { - log.Printf(tr.Value("error.metricsetup", "batt", err.Error())) - return - } - for i, _ := range bats { - id := makeID(i) - metrics.NewGauge(makeName("battery", i), func() float64 { - if ds, ok := b.Data[id]; ok { - return ds[len(ds)-1] - } - return 0.0 - }) - } } func makeID(i int) string { @@ -67,38 +44,17 @@ func (b *BatteryWidget) Scale(i int) { b.LineGraph.HorizontalScale = i } -func (b *BatteryWidget) update() { - batteries, err := battery.GetAll() - if err != nil { - switch errt := err.(type) { - case battery.ErrFatal: - log.Printf(tr.Value("error.fatalfetch", "batt", err.Error())) - return - case battery.Errors: - batts := make([]*battery.Battery, 0) - for i, e := range errt { - if e == nil { - batts = append(batts, batteries[i]) - } else { - log.Printf(tr.Value("error.recovfetch"), "batt", e.Error()) - } +func (b *BatteryWidget) Update() { + for _, bats := range b.d { + for i, battery := range bats.Data { + if battery.Full == 0.0 { + continue } - if len(batts) < 1 { - log.Print(tr.Value("error.nodevfound", "batt")) - return - } - batteries = batts - } - } - for i, battery := range batteries { - if battery.Full == 0.0 { - continue + id := makeID(i) + perc := battery.Current / battery.Full + percentFull := math.Abs(perc) * 100.0 + b.Data[id] = append(b.Data[id], percentFull) + b.Labels[id] = fmt.Sprintf("%3.0f%% %.0f/%.0f", percentFull, math.Abs(battery.Current), math.Abs(battery.Full)) } - id := makeID(i) - perc := battery.Current / battery.Full - percentFull := math.Abs(perc) * 100.0 - // TODO: look into this sort of thing; doesn't the array grow forever? Is the widget library truncating it? - b.Data[id] = append(b.Data[id], percentFull) - b.Labels[id] = fmt.Sprintf("%3.0f%% %.0f/%.0f", percentFull, math.Abs(battery.Current), math.Abs(battery.Full)) } } diff --git a/widgets/batterygauge.go b/widgets/batterygauge.go index 3a89590..ddbbac6 100644 --- a/widgets/batterygauge.go +++ b/widgets/batterygauge.go @@ -2,77 +2,60 @@ package widgets import ( "fmt" - "log" - "time" - "github.com/VictoriaMetrics/metrics" - "github.com/distatus/battery" - - "github.com/xxxserxxx/gotop/v4/termui" + "github.com/xxxserxxx/gotop/v4/devices" ) +// FIXME gauge isn't updating. + type BatteryGauge struct { - *termui.Gauge + *Gauge + d []*devices.Batteries } +// NewBatteryGauge creates a gauge widget representing the percentage of how +// full power a battery is. func NewBatteryGauge() *BatteryGauge { - self := &BatteryGauge{Gauge: termui.NewGauge()} + self := &BatteryGauge{ + Gauge: NewGauge(), + d: make([]*devices.Batteries, 0), + } self.Title = tr.Value("widget.label.gauge") - self.update() - - go func() { - for range time.NewTicker(time.Second).C { - self.Lock() - self.update() - self.Unlock() - } - }() - return self } -func (b *BatteryGauge) EnableMetric() { - metrics.NewGauge(makeName("battery", "total"), func() float64 { - return float64(b.Percent) - }) +func (g *BatteryGauge) Attach(b *devices.Batteries) { + g.d = append(g.d, b) } // Only report battery errors once. var errLogged = false -func (b *BatteryGauge) update() { - bats, err := battery.GetAll() - if err != nil { - if !errLogged { - log.Printf("error setting up batteries: %v", err) - errLogged = true - } - } - if len(bats) < 1 { - b.Label = fmt.Sprintf("N/A") - return - } +func (b *BatteryGauge) Update() { mx := 0.0 cu := 0.0 - charging := "%d%% ⚡%s" - rate := 0.0 - for _, bat := range bats { - if bat.Full == 0.0 { - continue - } - mx += bat.Full - cu += bat.Current - if rate < bat.ChargeRate { - rate = bat.ChargeRate - } - if bat.State == battery.Charging { - charging = "%d%% 🔌%s" + // default: discharging + formatString := "%d%% ⚡%s" + var rate time.Duration + for _, bats := range b.d { + for _, bat := range bats.Data { + if bat.Full == 0.0 { + continue + } + mx += bat.Full + cu += bat.Current + if bat.Charging { + fullTime := (mx - cu) / bat.ChargeRate + rate, _ = time.ParseDuration(fmt.Sprintf("%fh", fullTime)) + formatString = "%d%% 🔌%s" + } else { + runTime := cu / bat.ChargeRate + rate, _ = time.ParseDuration(fmt.Sprintf("%fh", runTime)) + } } } - tn := (mx - cu) / rate - d, _ := time.ParseDuration(fmt.Sprintf("%fh", tn)) b.Percent = int((cu / mx) * 100.0) - b.Label = fmt.Sprintf(charging, b.Percent, d.Truncate(time.Minute)) + b.Label = fmt.Sprintf(formatString, b.Percent, rate.Truncate(time.Minute)) } diff --git a/widgets/cpu.go b/widgets/cpu.go index bb72866..b497754 100644 --- a/widgets/cpu.go +++ b/widgets/cpu.go @@ -4,31 +4,32 @@ import ( "fmt" "time" - "github.com/VictoriaMetrics/metrics" "github.com/VividCortex/ewma" "github.com/xxxserxxx/gotop/v4/devices" "github.com/gizak/termui/v3" - ui "github.com/xxxserxxx/gotop/v4/termui" ) +const AVRG = "AVRG" + // TODO Maybe group CPUs in columns if space permits +// TODO add CPU freq type CPUWidget struct { - *ui.LineGraph - CPUCount int + *LineGraph + cpuCount int ShowAverageLoad bool ShowPerCPULoad bool updateInterval time.Duration cpuLoads map[string]float64 average ewma.MovingAverage + cpus []*devices.CPUs + keys [][]string } -var cpuLabels []string - func NewCPUWidget(updateInterval time.Duration, horizontalScale int, showAverageLoad bool, showPerCPULoad bool) *CPUWidget { self := &CPUWidget{ - LineGraph: ui.NewLineGraph(), - CPUCount: len(cpuLabels), + LineGraph: NewLineGraph(), + cpuCount: len(cpuLabels), updateInterval: updateInterval, ShowAverageLoad: showAverageLoad, ShowPerCPULoad: showPerCPULoad, @@ -40,7 +41,7 @@ func NewCPUWidget(updateInterval time.Duration, horizontalScale int, showAverage self.HorizontalScale = horizontalScale if !(self.ShowAverageLoad || self.ShowPerCPULoad) { - if self.CPUCount <= 8 { + if self.cpuCount <= 8 { self.ShowPerCPULoad = true } else { self.ShowAverageLoad = true @@ -51,41 +52,32 @@ func NewCPUWidget(updateInterval time.Duration, horizontalScale int, showAverage self.Data[AVRG] = []float64{0} } - if self.ShowPerCPULoad { - cpus := make(map[string]int) - devices.UpdateCPU(cpus, self.updateInterval, self.ShowPerCPULoad) - for k, v := range cpus { - self.Data[k] = []float64{float64(v)} - } - } - - self.update() - - go func() { - for range time.NewTicker(self.updateInterval).C { - self.update() - } - }() - return self } -const AVRG = "AVRG" +var cpuLabels []string -func (cpu *CPUWidget) EnableMetric() { - if cpu.ShowAverageLoad { - metrics.NewGauge(makeName("cpu", " avg"), func() float64 { - return cpu.cpuLoads[AVRG] - }) - } else { - cpus := make(map[string]int) - devices.UpdateCPU(cpus, cpu.updateInterval, cpu.ShowPerCPULoad) - for key, perc := range cpus { - kc := key - cpu.cpuLoads[key] = float64(perc) - metrics.NewGauge(makeName("cpu", key), func() float64 { - return cpu.cpuLoads[kc] - }) +func (cpu *CPUWidget) Attach(cs *devices.CPUs) { + cpu.cpus = append(cpu.cpus, cs) + + cpu.cpuCount = 0 + cpu.keys = make([][]string, len(cpu.cpus)) + if cpu.ShowPerCPULoad { + cpu.Data = make(map[string][]float64) + for _, c := range cpu.cpus { + cpu.cpuCount += len(c.Data) + } + formatString := "%s%1d" + if cpu.cpuCount > 10 { + formatString = "%s%02d" + } + for i, ci := range cpu.cpus { + cpu.keys[i] = make([]string, len(ci.Data)) + for j, _ := range ci.Data { + key := fmt.Sprintf(formatString, ci.Name, j) + cpu.Data[key] = make([]float64, 0) + cpu.keys[i][j] = key + } } } } @@ -94,28 +86,30 @@ func (cpu *CPUWidget) Scale(i int) { cpu.LineGraph.HorizontalScale = i } -func (cpu *CPUWidget) update() { - go func() { - cpus := make(map[string]int) - devices.UpdateCPU(cpus, cpu.updateInterval, true) - cpu.Lock() - defer cpu.Unlock() - // AVG = ((AVG*i)+n)/(i+1) - var sum int - for key, percent := range cpus { - sum += percent - if cpu.ShowPerCPULoad { +func (cpu *CPUWidget) Update() { + cpus := make(map[string]int) + // AVG = ((AVG*i)+n)/(i+1) + var sum float64 + if cpu.ShowPerCPULoad { + for i, c := range cpu.cpus { + sum += c.Average + for j, percent := range c.Data { + key := cpu.keys[i][j] cpu.Data[key] = append(cpu.Data[key], float64(percent)) - cpu.Labels[key] = fmt.Sprintf("%3d%%", percent) + cpu.Labels[key] = fmt.Sprintf("%3d%%", int(percent)) cpu.cpuLoads[key] = float64(percent) } } - if cpu.ShowAverageLoad { - cpu.average.Add(float64(sum) / float64(len(cpus))) - avg := cpu.average.Value() - cpu.Data[AVRG] = append(cpu.Data[AVRG], avg) - cpu.Labels[AVRG] = fmt.Sprintf("%3.0f%%", avg) - cpu.cpuLoads[AVRG] = avg + } else { + for _, c := range cpu.cpus { + sum += c.Average } - }() + } + if cpu.ShowAverageLoad { + cpu.average.Add(sum / float64(len(cpus))) + avg := cpu.average.Value() + cpu.Data[AVRG] = append(cpu.Data[AVRG], avg) + cpu.Labels[AVRG] = fmt.Sprintf("%3.0f%%", avg) + cpu.cpuLoads[AVRG] = avg + } } diff --git a/widgets/disk.go b/widgets/disk.go index 9df3c3b..e0aa4fe 100644 --- a/widgets/disk.go +++ b/widgets/disk.go @@ -2,40 +2,25 @@ package widgets import ( "fmt" - "log" "sort" "strings" "time" - "github.com/VictoriaMetrics/metrics" - psDisk "github.com/shirou/gopsutil/disk" - - ui "github.com/xxxserxxx/gotop/v4/termui" + "github.com/xxxserxxx/gotop/v4/devices" "github.com/xxxserxxx/gotop/v4/utils" ) -type Partition struct { - Device string - MountPoint string - BytesRead uint64 - BytesWritten uint64 - BytesReadRecently string - BytesWrittenRecently string - UsedPercent uint32 - Free string -} - type DiskWidget struct { - *ui.Table - updateInterval time.Duration - Partitions map[string]*Partition + *Table + parts datas + factor uint64 } -func NewDiskWidget() *DiskWidget { +func NewDiskWidget(refresh time.Duration) *DiskWidget { self := &DiskWidget{ - Table: ui.NewTable(), - updateInterval: time.Second, - Partitions: make(map[string]*Partition), + Table: NewTable(), + parts: datas{make([]string, 0), make([]*devices.Partition, 0)}, + factor: uint64(refresh / time.Second), } self.Table.Tr = tr self.Title = tr.Value("widget.label.disk") @@ -49,124 +34,46 @@ func NewDiskWidget() *DiskWidget { } } - self.update() - - go func() { - for range time.NewTicker(self.updateInterval).C { - self.Lock() - self.update() - self.Unlock() - } - }() - return self } -func (disk *DiskWidget) EnableMetric() { - for key, part := range disk.Partitions { - pc := part - metrics.NewGauge(makeName("disk", strings.ReplaceAll(key, "/", ":")), func() float64 { - return float64(pc.UsedPercent) / 100.0 - }) +func (disk *DiskWidget) Attach(d devices.Disk) { + for name, part := range d { + disk.parts.names = append(disk.parts.names, name) + disk.parts.parts = append(disk.parts.parts, part) } + sort.Sort(disk.parts) } -func (disk *DiskWidget) update() { - partitions, err := psDisk.Partitions(false) - if err != nil { - log.Printf(tr.Value("error.setup", "disk-partitions", err.Error())) - return - } - - // add partition if it's new - for _, partition := range partitions { - // don't show loop devices - if strings.HasPrefix(partition.Device, "/dev/loop") { - continue - } - // don't show docker container filesystems - if strings.HasPrefix(partition.Mountpoint, "/var/lib/docker/") { - continue - } - // check if partition doesn't already exist in our list - if _, ok := disk.Partitions[partition.Device]; !ok { - disk.Partitions[partition.Device] = &Partition{ - Device: partition.Device, - MountPoint: partition.Mountpoint, - } - } - } - - // delete a partition if it no longer exists - toDelete := []string{} - for device := range disk.Partitions { - exists := false - for _, partition := range partitions { - if device == partition.Device { - exists = true - break - } - } - if !exists { - toDelete = append(toDelete, device) - } - } - for _, device := range toDelete { - delete(disk.Partitions, device) - } - - // updates partition info. We add 0.5 to all values to make sure the truncation rounds - for _, partition := range disk.Partitions { - usage, err := psDisk.Usage(partition.MountPoint) - if err != nil { - log.Printf(tr.Value("error.recovfetch", "partition-"+partition.MountPoint+"-usage", err.Error())) - continue - } - partition.UsedPercent = uint32(usage.UsedPercent + 0.5) - bytesFree, magnitudeFree := utils.ConvertBytes(usage.Free) - partition.Free = fmt.Sprintf("%3d%s", uint64(bytesFree+0.5), magnitudeFree) - - ioCounters, err := psDisk.IOCounters(partition.Device) - if err != nil { - log.Printf(tr.Value("error.recovfetch", "partition-"+partition.Device+"-rw", err.Error())) - continue - } - ioCounter := ioCounters[strings.Replace(partition.Device, "/dev/", "", -1)] - bytesRead, bytesWritten := ioCounter.ReadBytes, ioCounter.WriteBytes - if partition.BytesRead != 0 { // if this isn't the first update - bytesReadRecently := bytesRead - partition.BytesRead - bytesWrittenRecently := bytesWritten - partition.BytesWritten +func (disk *DiskWidget) Update() { + // converts self.Partitions into self.Rows which is a [][]String + disk.Rows = make([][]string, len(disk.parts.names)) - readFloat, readMagnitude := utils.ConvertBytes(bytesReadRecently) - writeFloat, writeMagnitude := utils.ConvertBytes(bytesWrittenRecently) - bytesReadRecently, bytesWrittenRecently = uint64(readFloat+0.5), uint64(writeFloat+0.5) - partition.BytesReadRecently = fmt.Sprintf("%d%s", bytesReadRecently, readMagnitude) - partition.BytesWrittenRecently = fmt.Sprintf("%d%s", bytesWrittenRecently, writeMagnitude) - } else { - partition.BytesReadRecently = fmt.Sprintf("%d%s", 0, "B") - partition.BytesWrittenRecently = fmt.Sprintf("%d%s", 0, "B") - } - partition.BytesRead, partition.BytesWritten = bytesRead, bytesWritten + for i, part := range disk.parts.parts { + disk.Rows[i] = make([]string, 6) + disk.Rows[i][0] = strings.Replace(strings.Replace(part.Device, "/dev/", "", -1), "mapper/", "", -1) + disk.Rows[i][1] = part.MountPoint + disk.Rows[i][2] = fmt.Sprintf("%d%%", int(part.UsedPercent)) + disk.Rows[i][3] = fmt.Sprintf("%d", part.Free) + disk.Rows[i][4] = fmt.Sprintf("%d", part.BytesReadRecently/disk.factor) + disk.Rows[i][5] = fmt.Sprintf("%d", part.BytesWrittenRecently/disk.factor) } +} - // converts self.Partitions into self.Rows which is a [][]String +type datas struct { + names []string + parts []*devices.Partition +} - sortedPartitions := []string{} - for seriesName := range disk.Partitions { - sortedPartitions = append(sortedPartitions, seriesName) - } - sort.Strings(sortedPartitions) +func (d datas) Len() int { + return len(d.names) +} - disk.Rows = make([][]string, len(disk.Partitions)) +func (d datas) Less(i, j int) bool { + return d.names[i] < d.names[j] +} - for i, key := range sortedPartitions { - partition := disk.Partitions[key] - disk.Rows[i] = make([]string, 6) - disk.Rows[i][0] = strings.Replace(strings.Replace(partition.Device, "/dev/", "", -1), "mapper/", "", -1) - disk.Rows[i][1] = partition.MountPoint - disk.Rows[i][2] = fmt.Sprintf("%d%%", partition.UsedPercent) - disk.Rows[i][3] = partition.Free - disk.Rows[i][4] = partition.BytesReadRecently - disk.Rows[i][5] = partition.BytesWrittenRecently - } +func (d datas) Swap(i, j int) { + d.names[i], d.names[j] = d.names[j], d.names[i] + d.parts[i], d.parts[j] = d.parts[j], d.parts[i] } diff --git a/widgets/drawille-go/LICENSE.md b/widgets/drawille-go/LICENSE.md new file mode 100644 index 0000000..58777e3 --- /dev/null +++ b/widgets/drawille-go/LICENSE.md @@ -0,0 +1,661 @@ +GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no char |