mirror of
https://github.com/mainflux/mainflux.git
synced 2025-05-04 22:17:59 +08:00

* Use normalizer as stream source Renamed 'writer' service to 'normalizer' and dropped Cassandra facilities from it. Extracted the common dependencies to 'mainflux' package for easier sharing. Fixed the API docs and unified environment variables. Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Use docker build arguments to specify build Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Remove cassandra libraries Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Update go-kit version to 0.6.0 Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Fix manager configuration Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Refactor docker-compose Merged individual compose files and dropped external links. Remove CoAP container since it is not referenced from NginX config at the moment. Update port mapping in compose and nginx.conf. Dropped bin scripts. Updated service documentation. Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Drop content-type check Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Implement users data access layer in PostgreSQL Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Bump version to 0.1.0 Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Use go-kit logger everywhere (except CoAP) Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Improve factory methods naming Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Implement clients data access layer on PostgreSQL Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Make tests stateless All tests are refactored to use map-based table-driven tests. No cross-tests dependencies is present anymore. Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Remove gitignore Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Fix nginx proxying Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Mark client-user FK explicit Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Update API documentation Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Update channel model Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Add channel PostgreSQL repository tests Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Implement PostgreSQL channels DAO Replaced update queries with raw SQL. Explicitly defined M2M table due to difficulties of ensuring the referential integrity through GORM. Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Expose connection endpoints Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Fix swagger docs and remove DB logging Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Fix nested query remarks Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Add unique indices Signed-off-by: Dejan Mijic <dejan@mainflux.com>
239 lines
6.4 KiB
Go
239 lines
6.4 KiB
Go
// Copyright 2012 Neal van Veen. All rights reserved.
|
|
// Usage of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Gotty is a Go-package for reading and parsing the terminfo database
|
|
package gotty
|
|
|
|
// TODO add more concurrency to name lookup, look for more opportunities.
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// Open a terminfo file by the name given and construct a TermInfo object.
|
|
// If something went wrong reading the terminfo database file, an error is
|
|
// returned.
|
|
func OpenTermInfo(termName string) (*TermInfo, error) {
|
|
var term *TermInfo
|
|
var err error
|
|
// Find the environment variables
|
|
termloc := os.Getenv("TERMINFO")
|
|
if len(termloc) == 0 {
|
|
// Search like ncurses
|
|
locations := []string{os.Getenv("HOME") + "/.terminfo/", "/etc/terminfo/",
|
|
"/lib/terminfo/", "/usr/share/terminfo/"}
|
|
var path string
|
|
for _, str := range locations {
|
|
// Construct path
|
|
path = str + string(termName[0]) + "/" + termName
|
|
// Check if path can be opened
|
|
file, _ := os.Open(path)
|
|
if file != nil {
|
|
// Path can open, fall out and use current path
|
|
file.Close()
|
|
break
|
|
}
|
|
}
|
|
if len(path) > 0 {
|
|
term, err = readTermInfo(path)
|
|
} else {
|
|
err = errors.New(fmt.Sprintf("No terminfo file(-location) found"))
|
|
}
|
|
}
|
|
return term, err
|
|
}
|
|
|
|
// Open a terminfo file from the environment variable containing the current
|
|
// terminal name and construct a TermInfo object. If something went wrong
|
|
// reading the terminfo database file, an error is returned.
|
|
func OpenTermInfoEnv() (*TermInfo, error) {
|
|
termenv := os.Getenv("TERM")
|
|
return OpenTermInfo(termenv)
|
|
}
|
|
|
|
// Return an attribute by the name attr provided. If none can be found,
|
|
// an error is returned.
|
|
func (term *TermInfo) GetAttribute(attr string) (stacker, error) {
|
|
// Channel to store the main value in.
|
|
var value stacker
|
|
// Add a blocking WaitGroup
|
|
var block sync.WaitGroup
|
|
// Keep track of variable being written.
|
|
written := false
|
|
// Function to put into goroutine.
|
|
f := func(ats interface{}) {
|
|
var ok bool
|
|
var v stacker
|
|
// Switch on type of map to use and assign value to it.
|
|
switch reflect.TypeOf(ats).Elem().Kind() {
|
|
case reflect.Bool:
|
|
v, ok = ats.(map[string]bool)[attr]
|
|
case reflect.Int16:
|
|
v, ok = ats.(map[string]int16)[attr]
|
|
case reflect.String:
|
|
v, ok = ats.(map[string]string)[attr]
|
|
}
|
|
// If ok, a value is found, so we can write.
|
|
if ok {
|
|
value = v
|
|
written = true
|
|
}
|
|
// Goroutine is done
|
|
block.Done()
|
|
}
|
|
block.Add(3)
|
|
// Go for all 3 attribute lists.
|
|
go f(term.boolAttributes)
|
|
go f(term.numAttributes)
|
|
go f(term.strAttributes)
|
|
// Wait until every goroutine is done.
|
|
block.Wait()
|
|
// If a value has been written, return it.
|
|
if written {
|
|
return value, nil
|
|
}
|
|
// Otherwise, error.
|
|
return nil, fmt.Errorf("Erorr finding attribute")
|
|
}
|
|
|
|
// Return an attribute by the name attr provided. If none can be found,
|
|
// an error is returned. A name is first converted to its termcap value.
|
|
func (term *TermInfo) GetAttributeName(name string) (stacker, error) {
|
|
tc := GetTermcapName(name)
|
|
return term.GetAttribute(tc)
|
|
}
|
|
|
|
// A utility function that finds and returns the termcap equivalent of a
|
|
// variable name.
|
|
func GetTermcapName(name string) string {
|
|
// Termcap name
|
|
var tc string
|
|
// Blocking group
|
|
var wait sync.WaitGroup
|
|
// Function to put into a goroutine
|
|
f := func(attrs []string) {
|
|
// Find the string corresponding to the name
|
|
for i, s := range attrs {
|
|
if s == name {
|
|
tc = attrs[i+1]
|
|
}
|
|
}
|
|
// Goroutine is finished
|
|
wait.Done()
|
|
}
|
|
wait.Add(3)
|
|
// Go for all 3 attribute lists
|
|
go f(BoolAttr[:])
|
|
go f(NumAttr[:])
|
|
go f(StrAttr[:])
|
|
// Wait until every goroutine is done
|
|
wait.Wait()
|
|
// Return the termcap name
|
|
return tc
|
|
}
|
|
|
|
// This function takes a path to a terminfo file and reads it in binary
|
|
// form to construct the actual TermInfo file.
|
|
func readTermInfo(path string) (*TermInfo, error) {
|
|
// Open the terminfo file
|
|
file, err := os.Open(path)
|
|
defer file.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// magic, nameSize, boolSize, nrSNum, nrOffsetsStr, strSize
|
|
// Header is composed of the magic 0432 octal number, size of the name
|
|
// section, size of the boolean section, the amount of number values,
|
|
// the number of offsets of strings, and the size of the string section.
|
|
var header [6]int16
|
|
// Byte array is used to read in byte values
|
|
var byteArray []byte
|
|
// Short array is used to read in short values
|
|
var shArray []int16
|
|
// TermInfo object to store values
|
|
var term TermInfo
|
|
|
|
// Read in the header
|
|
err = binary.Read(file, binary.LittleEndian, &header)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// If magic number isn't there or isn't correct, we have the wrong filetype
|
|
if header[0] != 0432 {
|
|
return nil, errors.New(fmt.Sprintf("Wrong filetype"))
|
|
}
|
|
|
|
// Read in the names
|
|
byteArray = make([]byte, header[1])
|
|
err = binary.Read(file, binary.LittleEndian, &byteArray)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
term.Names = strings.Split(string(byteArray), "|")
|
|
|
|
// Read in the booleans
|
|
byteArray = make([]byte, header[2])
|
|
err = binary.Read(file, binary.LittleEndian, &byteArray)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
term.boolAttributes = make(map[string]bool)
|
|
for i, b := range byteArray {
|
|
if b == 1 {
|
|
term.boolAttributes[BoolAttr[i*2+1]] = true
|
|
}
|
|
}
|
|
// If the number of bytes read is not even, a byte for alignment is added
|
|
if len(byteArray)%2 != 0 {
|
|
err = binary.Read(file, binary.LittleEndian, make([]byte, 1))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Read in shorts
|
|
shArray = make([]int16, header[3])
|
|
err = binary.Read(file, binary.LittleEndian, &shArray)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
term.numAttributes = make(map[string]int16)
|
|
for i, n := range shArray {
|
|
if n != 0377 && n > -1 {
|
|
term.numAttributes[NumAttr[i*2+1]] = n
|
|
}
|
|
}
|
|
|
|
// Read the offsets into the short array
|
|
shArray = make([]int16, header[4])
|
|
err = binary.Read(file, binary.LittleEndian, &shArray)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Read the actual strings in the byte array
|
|
byteArray = make([]byte, header[5])
|
|
err = binary.Read(file, binary.LittleEndian, &byteArray)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
term.strAttributes = make(map[string]string)
|
|
// We get an offset, and then iterate until the string is null-terminated
|
|
for i, offset := range shArray {
|
|
if offset > -1 {
|
|
r := offset
|
|
for ; byteArray[r] != 0; r++ {
|
|
}
|
|
term.strAttributes[StrAttr[i*2+1]] = string(byteArray[offset:r])
|
|
}
|
|
}
|
|
return &term, nil
|
|
}
|