1
0
mirror of https://github.com/rancher/os.git synced 2025-09-03 07:44:21 +00:00

Cleanup ./vendor dir

This commit is contained in:
Ivan Mikushin
2015-12-09 20:52:47 +05:00
parent f3afb076f7
commit ea2d8b30e3
397 changed files with 0 additions and 55783 deletions

View File

@@ -1,16 +0,0 @@
###### Notice
*This is the official list of **Terminal** authors for copyright purposes.*
*This file is distinct from the CONTRIBUTORS file. See the latter for an
explanation.*
*Names should be added to this file as: `Organization`;
`[Name](web address)` or `Name <email>` for individuals*
*Please keep the list sorted.*
* * *
[Jonas mg](https://github.com/kless)

View File

@@ -1,26 +0,0 @@
###### Notice
*This is the official list of people who can contribute (and typically have
contributed) code to the **Terminal** repository.*
*The AUTHORS file lists the copyright holders; this file lists people. For
example, the employees of an organization are listed here but not in AUTHORS,
because the organization holds the copyright.*
*Names should be added to this file as: `[Name](web address)` or `Name <email>`*
*Please keep the list sorted.*
* * *
### Initial author
[Jonas mg](https://github.com/kless)
### Maintainer
### Other authors

View File

@@ -1,374 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@@ -1,15 +0,0 @@
###### Notice
*This file documents the changes in **Terminal** versions that are listed below.*
*Items should be added to this file as:*
### YYYY-MM-DD Release
+ Additional changes.
+ More changes.
* * *

View File

@@ -1,65 +0,0 @@
// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// The references about ANSI Escape sequences have been got from
// http://ascii-table.com/ansi-escape-sequences.php and
// http://www.termsys.demon.co.uk/vtansi.htm
package readline
// ANSI terminal escape controls
const (
// Cursor control
ANSI_CURSOR_UP = "\033[A" // Up
ANSI_CURSOR_DOWN = "\033[B" // Down
ANSI_CURSOR_FORWARD = "\033[C" // Forward
ANSI_CURSOR_BACKWARD = "\033[D" // Backward
ANSI_NEXT_LINE = "\033[E" // To next line
ANSI_PREV_LINE = "\033[F" // To previous line
// Erase
ANSI_DEL_LINE = "\033[2K" // Erase line
// Graphics mode
ANSI_SET_BOLD = "\033[1m" // Bold on
ANSI_SET_OFF = "\033[0m" // All attributes off
)
// ANSI terminal escape controls
var (
// Cursor control
CursorUp = []byte(ANSI_CURSOR_UP)
CursorDown = []byte(ANSI_CURSOR_DOWN)
CursorForward = []byte(ANSI_CURSOR_FORWARD)
CursorBackward = []byte(ANSI_CURSOR_BACKWARD)
ToNextLine = []byte(ANSI_NEXT_LINE)
ToPreviousLine = []byte(ANSI_PREV_LINE)
// Erase Text
DelScreenToUpper = []byte("\033[2J\033[0;0H") // Erase the screen; move upper
DelToRight = []byte("\033[0K") // Erase to right
DelLine_CR = []byte("\033[2K\r") // Erase line; carriage return
DelLine_cursorUp = []byte("\033[2K\033[A") // Erase line; cursor up
//DelChar = []byte("\033[1X") // Erase character
DelChar = []byte("\033[P") // Delete character, from current position
DelBackspace = []byte("\033[D\033[P")
// Misc.
//InsertChar = []byte("\033[@") // Insert CHaracter
//SetLineWrap = []byte("\033[?7h") // Enable Line Wrap
)
// Characters
var (
CR = []byte{13} // Carriage return -- \r
CRLF = []byte{13, 10} // CR+LF is used for a new line in raw mode -- \r\n
CtrlC = []rune("^C")
CtrlD = []rune("^D")
)

View File

@@ -1,380 +0,0 @@
// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package readline
import (
"fmt"
"unicode/utf8"
"github.com/kless/term"
)
// Buffer size
var (
BufferCap = 4096
BufferLen = 64 // Initial length
)
// == Init
/*var lines, columns int
func init() {
lines, columns = term.SizeInChar()
}*/
// == Type
// A buffer represents the line buffer.
type buffer struct {
columns int // Number of columns for actual window
promptLen int
pos int // Pointer position into buffer
size int // Amount of characters added
data []rune // Text buffer
}
func newBuffer(promptLen, columns int) *buffer {
b := new(buffer)
b.columns = columns
b.promptLen = promptLen
b.data = make([]rune, BufferLen, BufferCap)
return b
}
// == Output
// insertRune inserts a character in the cursor position.
func (b *buffer) insertRune(r rune) error {
var useRefresh bool
b.grow(b.size + 1) // Check if there is free space for one more character
// Avoid a full update of the line.
if b.pos == b.size {
char := make([]byte, utf8.UTFMax)
utf8.EncodeRune(char, r)
if _, err := term.Output.Write(char); err != nil {
return outputError(err.Error())
}
} else {
useRefresh = true
copy(b.data[b.pos+1:b.size+1], b.data[b.pos:b.size])
}
b.data[b.pos] = r
b.pos++
b.size++
if useRefresh {
return b.refresh()
}
return nil
}
// insertRunes inserts several characters.
func (b *buffer) insertRunes(runes []rune) error {
for _, r := range runes {
if err := b.insertRune(r); err != nil {
return err
}
}
return nil
}
// toBytes returns a slice of the contents of the buffer.
func (b *buffer) toBytes() []byte {
chars := make([]byte, b.size*utf8.UTFMax)
var end, runeLen int
// == Each character (as integer) is encoded to []byte
for i := 0; i < b.size; i++ {
if i != 0 {
runeLen = utf8.EncodeRune(chars[end:], b.data[i])
end += runeLen
} else {
runeLen = utf8.EncodeRune(chars, b.data[i])
end = runeLen
}
}
return chars[:end]
}
// toString returns the contents of the buffer as a string.
func (b *buffer) toString() string { return string(b.data[b.promptLen:b.size]) }
// refresh refreshes the line.
func (b *buffer) refresh() (err error) {
lastLine, _ := b.pos2xy(b.size)
posLine, posColumn := b.pos2xy(b.pos)
// To the first line.
for ln := posLine; ln > 0; ln-- {
if _, err = term.Output.Write(ToPreviousLine); err != nil {
return outputError(err.Error())
}
}
// == Write the line
if _, err = term.Output.Write(CR); err != nil {
return outputError(err.Error())
}
if _, err = term.Output.Write(b.toBytes()); err != nil {
return outputError(err.Error())
}
if _, err = term.Output.Write(DelToRight); err != nil {
return outputError(err.Error())
}
// == Move cursor to original position.
for ln := lastLine; ln > posLine; ln-- {
if _, err = term.Output.Write(ToPreviousLine); err != nil {
return outputError(err.Error())
}
}
if _, err = fmt.Fprintf(term.Output, "\r\033[%dC", posColumn); err != nil {
return outputError(err.Error())
}
return nil
}
// == Movement
// start moves the cursor at the start.
func (b *buffer) start() (err error) {
if b.pos == b.promptLen {
return
}
for ln, _ := b.pos2xy(b.pos); ln > 0; ln-- {
if _, err = term.Output.Write(CursorUp); err != nil {
return outputError(err.Error())
}
}
if _, err = fmt.Fprintf(term.Output, "\r\033[%dC", b.promptLen); err != nil {
return outputError(err.Error())
}
b.pos = b.promptLen
return
}
// end moves the cursor at the end.
// Returns the number of lines that fill in the data.
func (b *buffer) end() (lines int, err error) {
if b.pos == b.size {
return
}
lastLine, lastColumn := b.pos2xy(b.size)
for ln, _ := b.pos2xy(b.pos); ln < lastLine; ln++ {
if _, err = term.Output.Write(CursorDown); err != nil {
return 0, outputError(err.Error())
}
}
if _, err = fmt.Fprintf(term.Output, "\r\033[%dC", lastColumn); err != nil {
return 0, outputError(err.Error())
}
b.pos = b.size
return lastLine, nil
}
// backward moves the cursor one character backward.
// Returns a boolean to know if the cursor is at the beginning of the line.
func (b *buffer) backward() (start bool, err error) {
if b.pos == b.promptLen {
return true, nil
}
b.pos--
// If position is on the same line.
if _, col := b.pos2xy(b.pos); col != 0 {
if _, err = term.Output.Write(CursorBackward); err != nil {
return false, outputError(err.Error())
}
} else {
if _, err = term.Output.Write(CursorUp); err != nil {
return false, outputError(err.Error())
}
if _, err = fmt.Fprintf(term.Output, "\033[%dC", b.columns); err != nil {
return false, outputError(err.Error())
}
}
return
}
// forward moves the cursor one character forward.
// Returns a boolean to know if the cursor is at the end of the line.
func (b *buffer) forward() (end bool, err error) {
if b.pos == b.size {
return true, nil
}
b.pos++
if _, col := b.pos2xy(b.pos); col != 0 {
if _, err = term.Output.Write(CursorForward); err != nil {
return false, outputError(err.Error())
}
} else {
if _, err = term.Output.Write(ToNextLine); err != nil {
return false, outputError(err.Error())
}
}
return
}
// swap swaps the actual character by the previous one. If it is the end of the
// line then it is swapped the 2nd previous by the previous one.
func (b *buffer) swap() error {
if b.pos == b.promptLen {
return nil
}
if b.pos < b.size {
aux := b.data[b.pos-1]
b.data[b.pos-1] = b.data[b.pos]
b.data[b.pos] = aux
b.pos++
// End of line
} else {
aux := b.data[b.pos-2]
b.data[b.pos-2] = b.data[b.pos-1]
b.data[b.pos-1] = aux
}
return b.refresh()
}
// wordBackward moves the cursor one word backward.
func (b *buffer) wordBackward() (err error) {
for start := false; ; {
start, err = b.backward()
if start == true || err != nil || b.data[b.pos-1] == 32 {
return
}
}
}
// wordForward moves the cursor one word forward.
func (b *buffer) wordForward() (err error) {
for end := false; ; {
end, err = b.forward()
if end == true || err != nil || b.data[b.pos] == 32 {
return
}
}
}
// == Delete
// deleteChar deletes the character in cursor.
func (b *buffer) deleteChar() (err error) {
if b.pos == b.size {
return
}
copy(b.data[b.pos:], b.data[b.pos+1:b.size])
b.size--
if lastLine, _ := b.pos2xy(b.size); lastLine == 0 {
if _, err = term.Output.Write(DelChar); err != nil {
return outputError(err.Error())
}
return nil
}
return b.refresh()
}
// deleteCharPrev deletes the previous character from cursor.
func (b *buffer) deleteCharPrev() (err error) {
if b.pos == b.promptLen {
return
}
copy(b.data[b.pos-1:], b.data[b.pos:b.size])
b.pos--
b.size--
if lastLine, _ := b.pos2xy(b.size); lastLine == 0 {
if _, err = term.Output.Write(DelBackspace); err != nil {
return outputError(err.Error())
}
return nil
}
return b.refresh()
}
// deleteToRight deletes from current position until to end of line.
func (b *buffer) deleteToRight() (err error) {
if b.pos == b.size {
return
}
lastLine, _ := b.pos2xy(b.size)
posLine, _ := b.pos2xy(b.pos)
// To the last line.
for ln := posLine; ln < lastLine; ln++ {
if _, err = term.Output.Write(CursorDown); err != nil {
return outputError(err.Error())
}
}
// Delete all lines until the cursor position.
for ln := lastLine; ln > posLine; ln-- {
if _, err = term.Output.Write(DelLine_cursorUp); err != nil {
return outputError(err.Error())
}
}
if _, err = term.Output.Write(DelToRight); err != nil {
return outputError(err.Error())
}
b.size = b.pos
return nil
}
// deleteLine deletes full line.
func (b *buffer) deleteLine() error {
lines, err := b.end()
if err != nil {
return err
}
for lines > 0 {
if _, err = term.Output.Write(DelLine_cursorUp); err != nil {
return outputError(err.Error())
}
lines--
}
return nil
}
// == Utility
// grow grows buffer to guarantee space for n more byte.
func (b *buffer) grow(n int) {
for n > len(b.data) {
b.data = b.data[:len(b.data)+BufferLen]
}
}
// pos2xy returns the coordinates of a position for a line of size given in
// columns.
func (b *buffer) pos2xy(pos int) (line, column int) {
if pos < b.columns {
return 0, pos
}
line = pos / b.columns
column = pos - (line * b.columns) //- 1
return
}

View File

@@ -1,50 +0,0 @@
// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
/*
Package readline provides simple functions for both line and screen editing.
Features:
Unicode support
History
Multi-line editing
List of key sequences enabled (just like in GNU Readline):
Backspace / Ctrl+h
Delete
Home / Ctrl+a
End / Ctrl+e
Left arrow / Ctrl+b
Right arrow / Ctrl+f
Up arrow / Ctrl+p
Down arrow / Ctrl+n
Ctrl+left arrow
Ctrl+right arrow
Ctrl+t : swap actual character by the previous one
Ctrl+k : delete from current to end of line
Ctrl+u : delete the whole line
Ctrl+l : clear screen
Ctrl+c
Ctrl+d : exit
Note that There are several default values:
+ For the buffer: BufferCap, BufferLen.
+ For the history file: HistoryCap, HistoryPerm.
Important: the TTY is set in "raw mode" so there is to use CR+LF ("\r\n") for
writing a new line.
Note: the values for the input and output are got from the package base "term".
*/
package readline

View File

@@ -1,25 +0,0 @@
// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package readline
import "errors"
var ErrCtrlD = errors.New("Interrumpted (Ctrl+d)")
// An inputError represents a failure on input.
type inputError string
func (e inputError) Error() string {
return "could not read from input: " + string(e)
}
// An outputError represents a failure in output.
type outputError string
func (e outputError) Error() string {
return "could not write to output: " + string(e)
}

View File

@@ -1,196 +0,0 @@
// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package readline
import (
"bufio"
"container/list"
"errors"
"io"
"log"
"os"
"strconv"
"strings"
)
// Values by default
var (
HistoryCap = 500 // Capacity
HistoryPerm os.FileMode = 0600 // History file permission
)
var (
ErrEmptyHist = errors.New("history: empty")
ErrNilElement = errors.New("history: no more elements")
)
// == Type
type history struct {
Cap int
filename string
file *os.File
mark *list.Element // Pointer to the last element added.
li *list.List
}
// _baseHistory is the base to create an history file.
func _baseHistory(fname string, size int) (*history, error) {
file, err := os.OpenFile(fname, os.O_CREATE|os.O_RDWR, HistoryPerm)
if err != nil {
return nil, err
}
h := new(history)
h.Cap = size
h.filename = fname
h.file = file
h.li = list.New()
return h, nil
}
// NewHistory creates a new history using the maximum length by default.
func NewHistory(filename string) (*history, error) {
return _baseHistory(filename, HistoryCap)
}
// NewHistoryOfSize creates a new history whose buffer has the specified size,
// which must be greater than zero.
func NewHistoryOfSize(filename string, size int) (*history, error) {
if size <= 0 {
return nil, errors.New("wrong history size: " + strconv.Itoa(size))
}
return _baseHistory(filename, size)
}
// == Access to file
// Load loads the history from the file.
func (h *history) Load() {
in := bufio.NewReader(h.file)
for {
line, err := in.ReadString('\n')
if err == io.EOF {
break
}
h.li.PushBack(strings.TrimRight(line, "\n"))
}
h.mark = h.li.Back() // Point to an element.
}
// Save saves all lines to the text file, excep when:
// + it starts with some space
// + it is an empty line
func (h *history) Save() (err error) {
if _, err = h.file.Seek(0, 0); err != nil {
return
}
out := bufio.NewWriter(h.file)
element := h.li.Front() // Get the first element.
for i := 0; i < h.li.Len(); i++ {
line := element.Value.(string)
if strings.HasPrefix(line, " ") {
goto _next
}
if line = strings.TrimSpace(line); line == "" {
goto _next
}
if _, err = out.WriteString(line + "\n"); err != nil {
log.Println("history.Save:", err)
break
}
_next:
if element = element.Next(); element == nil {
continue
}
}
if err = out.Flush(); err != nil {
log.Println("history.Save:", err)
}
h.close()
return
}
// close Close the file descriptor.
func (h *history) close() {
h.file.Close()
}
// openFile opens again the file.
/*func (h *history) openFile() {
file, err := os.Open(fname, os.O_CREATE|os.O_RDWR, HistoryPerm)
if err != nil {
log.Println("history.openFile:", err)
return
}
h.file = file
}*/
// Add adds a new line to the buffer.
func (h *history) Add(line string) {
if h.li.Len() <= h.Cap {
h.mark = h.li.PushBack(line)
} else {
// TODO: overwrite lines
}
}
// _baseNextPrev is the base to move between lines.
func (h *history) _baseNextPrev(c byte) (line []rune, err error) {
if h.li.Len() <= 0 {
return line, ErrEmptyHist
}
new := new(list.Element)
if c == 'p' {
new = h.mark.Prev()
} else if c == 'n' {
new = h.mark.Next()
} else {
panic("history._baseNextPrev: wrong character choice")
}
if new != nil {
h.mark = new
} else {
return nil, ErrNilElement
}
return []rune(new.Value.(string)), nil
}
// Next returns the next line.
func (h *history) Next() (line []rune, err error) {
return h._baseNextPrev('n')
}
// Prev returns the previous line.
func (h *history) Prev() (line []rune, err error) {
return h._baseNextPrev('p')
}
// == Utility
// hasHistory checks whether has an history file.
func hasHistory(h *history) bool {
if h == nil {
return false
}
return true
}

View File

@@ -1,71 +0,0 @@
// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// +build !lookup
package readline
import (
"os"
"path"
"strings"
"testing"
)
var (
historyFile = path.Join(os.TempDir(), "test_history")
historyLen int
)
func TestHistSave(t *testing.T) {
hist, err := NewHistoryOfSize(historyFile, 10)
if err != nil {
t.Error("could not create history", err)
}
if hist.li.Len() > hist.Cap {
t.Error("bad capacity size")
}
hist.Add("1 line with trailing spaces ")
hist.Add("2 line without trailing spaces")
hist.Add("3 line without trailing spaces")
hist.Add("4 with trailing tabulator\t")
hist.Add("5 with trailing new line\n")
hist.Add(" ") // Not saved to file
hist.Add(" leading space") // Idem
hist.Add("") // Idem
hist.Add("9 line without trailing spaces")
hist.Add("10 line number 6")
hist.Save()
historyLen = hist.li.Len() - 3 // 3 lines should not be saved
}
func TestHistLoad(t *testing.T) {
hist, err := NewHistoryOfSize(historyFile, 10)
if err != nil {
t.Error("could not load history", err)
}
hist.Load()
e := hist.li.Front()
for i := 0; i < hist.li.Len(); i++ {
line := e.Value.(string)
if strings.HasSuffix(line, "\n") || strings.HasSuffix(line, "\t") ||
strings.HasSuffix(line, " ") {
t.Error("line saved with any trailing Unicode space")
}
}
if hist.li.Len() != historyLen {
t.Error("length doesn't match with values saved")
}
os.Remove(historyFile)
}

View File

@@ -1,86 +0,0 @@
// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package readline
import "github.com/kless/term"
// Default values for prompts.
const (
PS1 = "$ "
PS2 = "> "
)
// keyAction represents the action to run for a key or sequence of keys pressed.
type keyAction int
const (
_ keyAction = iota
_LEFT
_RIGHT
_UP
_DOWN
_HOME
_END
)
// To detect if has been pressed CTRL-C
var ChanCtrlC = make(chan byte)
// To detect if has been pressed CTRL-D
var ChanCtrlD = make(chan byte)
// A Line represents a line in the term.
type Line struct {
ter *term.Terminal
buf *buffer // Text buffer
hist *history // History file
ps1 string // Primary prompt
ps2 string // Command continuations
lenPS1 int // Size of primary prompt
useHistory bool
}
// NewDefaultLine returns a line type using the prompt by default, and setting
// the terminal to raw mode.
// If the history is nil then it is not used.
func NewDefaultLine(hist *history) (*Line, error) {
ter, err := term.New()
if err != nil {
return nil, err
}
if err = ter.RawMode(); err != nil {
return nil, err
}
_, col, err := ter.GetSize()
if err != nil {
return nil, err
}
buf := newBuffer(len(PS1), col)
buf.insertRunes([]rune(PS1))
return &Line{
ter: ter,
buf: buf,
hist: hist,
ps1: PS1,
ps2: PS2,
lenPS1: len(PS1),
useHistory: hasHistory(hist),
}, nil
}
// Restore restores the terminal settings, so it is disabled the raw mode.
func (ln *Line) Restore() error {
return ln.ter.Restore()
}

View File

@@ -1,345 +0,0 @@
// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// +build !plan9,!windows
package readline
import (
"bufio"
"fmt"
"strings"
"github.com/kless/term"
"github.com/kless/term/sys"
)
func init() {
if !term.SupportANSI() {
panic("Your terminal does not support ANSI")
}
}
// NewLine returns a line using both prompts ps1 and ps2, and setting the given
// terminal to raw mode, if were necessary.
// lenAnsi is the length of ANSI codes that the prompt ps1 could have.
// If the history is nil then it is not used.
func NewLine(ter *term.Terminal, ps1, ps2 string, lenAnsi int, hist *history) (*Line, error) {
if ter.Mode()&term.RawMode == 0 { // the raw mode is not set
if err := ter.RawMode(); err != nil {
return nil, err
}
}
lenPS1 := len(ps1) - lenAnsi
_, col, err := ter.GetSize()
if err != nil {
return nil, err
}
buf := newBuffer(lenPS1, col)
buf.insertRunes([]rune(ps1))
return &Line{
ter: ter,
buf: buf,
hist: hist,
ps1: ps1,
ps2: ps2,
lenPS1: lenPS1,
useHistory: hasHistory(hist),
}, nil
}
// Prompt prints the primary prompt.
func (ln *Line) Prompt() (err error) {
if _, err = term.Output.Write(DelLine_CR); err != nil {
return outputError(err.Error())
}
if _, err = fmt.Fprint(term.Output, ln.ps1); err != nil {
return outputError(err.Error())
}
ln.buf.pos, ln.buf.size = ln.lenPS1, ln.lenPS1
return
}
// Read reads charactes from input to write them to output, enabling line editing.
// The errors that could return are to indicate if Ctrl+D was pressed, and for
// both input/output errors.
func (ln *Line) Read() (line string, err error) {
var anotherLine []rune // For lines got from history.
var isHistoryUsed bool // If the history has been accessed.
var action keyAction
in := bufio.NewReader(term.Input) // Read input.
esc := make([]byte, 2) // For escape sequences.
extEsc := make([]byte, 3) // Extended escape sequences.
// Print the primary prompt.
if err = ln.Prompt(); err != nil {
return "", err
}
// == Detect change of window size.
winSize := term.DetectWinSize()
go func() {
for {
select {
case <-winSize.Change: // Wait for.
_, col, err := ln.ter.GetSize()
if err != nil {
ln.buf.columns = col
ln.buf.refresh()
}
}
}
}()
defer winSize.Close()
for ; ; action = 0 {
char, _, err := in.ReadRune()
if err != nil {
return "", inputError(err.Error())
}
_S:
switch char {
default:
if err = ln.buf.insertRune(char); err != nil {
return "", err
}
continue
case sys.K_RETURN:
line = ln.buf.toString()
if ln.useHistory {
ln.hist.Add(line)
}
if _, err = term.Output.Write(CRLF); err != nil {
return "", outputError(err.Error())
}
return strings.TrimSpace(line), nil
case sys.K_TAB:
// TODO: disabled by now
continue
case sys.K_BACK, sys.K_CTRL_H:
if err = ln.buf.deleteCharPrev(); err != nil {
return "", err
}
continue
case sys.K_CTRL_C:
if err = ln.buf.insertRunes(CtrlC); err != nil {
return "", err
}
if _, err = term.Output.Write(CRLF); err != nil {
return "", outputError(err.Error())
}
ChanCtrlC <- 1 //TODO: is really necessary?
if err = ln.Prompt(); err != nil {
return "", err
}
continue
case sys.K_CTRL_D:
if err = ln.buf.insertRunes(CtrlD); err != nil {
return "", err
}
if _, err = term.Output.Write(CRLF); err != nil {
return "", outputError(err.Error())
}
ln.Restore()
ChanCtrlD <- 1
return "", ErrCtrlD
// Escape sequence
case sys.K_ESCAPE: // Ctrl+[ ("\x1b" in hexadecimal, "033" in octal)
if _, err = in.Read(esc); err != nil {
return "", inputError(err.Error())
}
if esc[0] == 79 { // 'O'
switch esc[1] {
case 72: // Home: "\x1b O H"
action = _HOME
break _S
case 70: // End: "\x1b O F"
action = _END
break _S
}
}
if esc[0] == 91 { // '['
switch esc[1] {
case 65: // Up: "\x1b [ A"
if !ln.useHistory {
continue
}
action = _UP
break _S
case 66: // Down: "\x1b [ B"
if !ln.useHistory {
continue
}
action = _DOWN
break _S
case 68: // "\x1b [ D"
action = _LEFT
break _S
case 67: // "\x1b [ C"
action = _RIGHT
break _S
}
// Extended escape.
if esc[1] > 48 && esc[1] < 55 {
if _, err = in.Read(extEsc); err != nil {
return "", inputError(err.Error())
}
if extEsc[0] == 126 { // '~'
switch esc[1] {
//case 50: // Insert: "\x1b [ 2 ~"
case 51: // Delete: "\x1b [ 3 ~"
if err = ln.buf.deleteChar(); err != nil {
return "", err
}
continue
//case 53: // RePag: "\x1b [ 5 ~"
//case 54: // AvPag: "\x1b [ 6 ~"
}
}
if esc[1] == 49 && extEsc[0] == 59 && extEsc[1] == 53 { // "1;5"
switch extEsc[2] {
case 68: // Ctrl+left arrow: "\x1b [ 1 ; 5 D"
// move to last word
if err = ln.buf.wordBackward(); err != nil {
return "", err
}
continue
case 67: // Ctrl+right arrow: "\x1b [ 1 ; 5 C"
// move to next word
if err = ln.buf.wordForward(); err != nil {
return "", err
}
continue
}
}
}
}
continue
case sys.K_CTRL_T: // Swap actual character by the previous one.
if err = ln.buf.swap(); err != nil {
return "", err
}
continue
case sys.K_CTRL_L: // Clear screen.
if _, err = term.Output.Write(DelScreenToUpper); err != nil {
return "", err
}
if err = ln.Prompt(); err != nil {
return "", err
}
continue
case sys.K_CTRL_U: // Delete the whole line.
if err = ln.buf.deleteLine(); err != nil {
return "", err
}
if err = ln.Prompt(); err != nil {
return "", err
}
continue
case sys.K_CTRL_K: // Delete from current to end of line.
if err = ln.buf.deleteToRight(); err != nil {
return "", err
}
continue
case sys.K_CTRL_P: // Up
if !ln.useHistory {
continue
}
action = _UP
case sys.K_CTRL_N: // Down
if !ln.useHistory {
continue
}
action = _DOWN
case sys.K_CTRL_B: // Left
action = _LEFT
case sys.K_CTRL_F: // Right
action = _RIGHT
case sys.K_CTRL_A: // Start of line.
action = _HOME
case sys.K_CTRL_E: // End of line.
action = _END
}
switch action {
case _UP, _DOWN: // Up and down arrow: history
if action == _UP {
anotherLine, err = ln.hist.Prev()
} else {
anotherLine, err = ln.hist.Next()
}
if err != nil {
continue
}
// Update the current history entry before to overwrite it with
// the next one.
// TODO: it has to be removed before of to be saved the history
if !isHistoryUsed {
ln.hist.Add(ln.buf.toString())
}
isHistoryUsed = true
ln.buf.grow(len(anotherLine))
ln.buf.size = len(anotherLine) + ln.buf.promptLen
copy(ln.buf.data[ln.lenPS1:], anotherLine)
if err = ln.buf.refresh(); err != nil {
return "", err
}
continue
case _LEFT:
if _, err = ln.buf.backward(); err != nil {
return "", err
}
continue
case _RIGHT:
if _, err = ln.buf.forward(); err != nil {
return "", err
}
continue
case _HOME:
if err = ln.buf.start(); err != nil {
return "", err
}
continue
case _END:
if _, err = ln.buf.end(); err != nil {
return "", err
}
continue
}
}
}

View File

@@ -1,347 +0,0 @@
// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package test checks the functions that depend of the standard input,
// which is changed by `go test` to the standard error.
//
// Flags:
//
// -dbg-key=false: debug: print the decimal code at pressing a key
// -dbg-winsize=false: debug: to know how many signals are sent at maximizing a window
// -iact=false: interactive mode
// -t=2: time in seconds to wait to write in automatic mode
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/kless/term"
"github.com/kless/term/readline"
)
var (
IsInteractive = flag.Bool("iact", false, "interactive mode")
Time = flag.Uint("t", 2, "time in seconds to wait to write in automatic mode")
DebugWinSize = flag.Bool("dbg-winsize", false, "debug: to know how many signals are sent at maximizing a window")
DebugKey = flag.Bool("dbg-key", false, "debug: print the decimal code at pressing a key")
pr *io.PipeReader
pw *io.PipeWriter
)
func main() {
flag.Parse()
log.SetFlags(0)
log.SetPrefix("--- FAIL: ")
if *DebugKey {
Lookup()
return
}
if *DebugWinSize {
win := term.DetectWinSize()
defer win.Close()
fmt.Println("[Resize the window: should print a number every time]")
for i := 0; i < 7; i++ {
select {
case <-win.Change:
fmt.Printf("%d ", i)
case <-time.After(13 * time.Second):
fmt.Print("\ntimed out\n")
return
}
}
return
}
if !*IsInteractive {
pr, pw = io.Pipe()
term.Input = pr
}
TestCharMode()
TestEchoMode()
if *IsInteractive {
TestPassword()
}
TestEditLine()
if *IsInteractive {
TestDetectSize()
}
}
// TestCharMode tests terminal set to single character mode.
func TestCharMode() {
fmt.Print("\n=== RUN TestCharMode\n")
ter, _ := term.New()
defer func() {
if err := ter.Restore(); err != nil {
log.Print(err)
}
}()
if err := ter.CharMode(); err != nil {
log.Print("expected to set character mode:", err)
return
}
buf := bufio.NewReaderSize(term.Input, 4)
reply := []string{"a", "€", "~"}
if !*IsInteractive {
go func() {
for _, r := range reply {
time.Sleep(time.Duration(*Time) * time.Second)
fmt.Fprint(pw, r)
}
}()
}
for i := 1; ; i++ {
fmt.Print(" Press key: ")
rune, _, err := buf.ReadRune()
if err != nil {
log.Print(err)
return
}
fmt.Printf("\n pressed: %q\n", string(rune))
if *IsInteractive || i == len(reply) {
break
}
}
}
func TestEchoMode() {
fmt.Print("\n=== RUN TestEchoMode\n")
ter, _ := term.New()
defer func() {
if err := ter.Restore(); err != nil {
log.Print(err)
}
}()
if err := ter.EchoMode(false); err != nil {
log.Print("expected to set echo mode:", err)
return
}
fmt.Print(" + Mode to echo off\n")
buf := bufio.NewReader(term.Input)
if !*IsInteractive {
go func() {
time.Sleep(time.Duration(*Time) * time.Second)
fmt.Fprint(pw, "Karma\n")
}()
}
fmt.Print(" Write (enter to finish): ")
line, err := buf.ReadString('\n')
if err != nil {
log.Print(err)
return
}
fmt.Printf("\n entered: %q\n", line)
ter.EchoMode(true)
fmt.Print("\n + Mode to echo on\n")
if !*IsInteractive {
go func() {
time.Sleep(time.Duration(*Time) * time.Second)
fmt.Fprint(pw, "hotel\n")
}()
}
fmt.Print(" Write (enter to finish): ")
line, _ = buf.ReadString('\n')
if !*IsInteractive {
fmt.Println()
}
fmt.Printf(" entered: %q\n", line)
}
func TestPassword() {
fmt.Print("\n=== RUN TestPassword\n")
fmt.Print(" Password (no echo): ")
pass := make([]byte, 8)
n, err := term.ReadPassword(pass)
if err != nil {
log.Print(err)
return
}
fmt.Printf(" entered: %q\n number: %d\n", pass, n)
term.PasswordShadowed = true
fmt.Print("\n Password (shadow character): ")
pass = make([]byte, 8)
n, err = term.ReadPassword(pass)
if err != nil {
log.Print(err)
return
}
fmt.Printf(" entered: %q\n number: %d\n", pass, n)
}
func TestDetectSize() {
fmt.Print("\n=== RUN TestDetectSize\n")
ter, _ := term.New()
defer func() {
if err := ter.Restore(); err != nil {
log.Print(err)
}
}()
row, col, err := ter.GetSize()
if err != nil {
panic(err)
}
winSize := term.DetectWinSize()
fmt.Println("[Change the size of the terminal]")
// I want to finish the test.
go func() {
time.Sleep(10 * time.Second)
winSize.Change <- true
}()
<-winSize.Change
winSize.Close()
row2, col2, err := ter.GetSize()
if err != nil {
panic(err)
}
if row == row2 && col == col2 {
log.Print("the terminal size got the same value")
return
}
}
// Package readline
func TestEditLine() {
fmt.Println("\n=== RUN TestEditLine")
tempHistory := filepath.Join(os.TempDir(), "test_readline")
hist, err := readline.NewHistory(tempHistory)
if err != nil {
log.Print(err)
return
}
defer func() {
if err = os.Remove(tempHistory); err != nil {
log.Print(err)
}
}()
hist.Load()
fmt.Printf("Press ^D to exit\n\n")
ln, err := readline.NewDefaultLine(hist)
if err != nil {
log.Print(err)
return
}
defer func() {
if err = ln.Restore(); err != nil {
log.Print(err)
}
}()
if !*IsInteractive {
reply := []string{
"I have heard that the night is all magic",
"and that a goblin invites you to dream",
}
go func() {
for _, r := range reply {
time.Sleep(time.Duration(*Time) * time.Second)
fmt.Fprintf(pw, "%s\r\n", r)
}
time.Sleep(time.Duration(*Time) * time.Second)
pw.Write([]byte{4}) // Ctrl+D
}()
}
for {
if _, err = ln.Read(); err != nil {
if err == readline.ErrCtrlD {
hist.Save()
err = nil
} else {
log.Print(err)
return
}
break
}
}
}
// Lookup prints the decimal code at pressing a key.
func Lookup() {
ter, err := term.New()
if err != nil {
log.Print(err)
return
}
defer func() {
if err = ter.Restore(); err != nil {
log.Print(err)
}
}()
if err = ter.RawMode(); err != nil {
log.Print(err)
return
} else {
buf := bufio.NewReader(term.Input)
runes := make([]int32, 0)
chars := make([]string, 0)
fmt.Print("[Press Enter to exit]\r\n")
fmt.Print("> ")
L:
for {
rune_, _, err := buf.ReadRune()
if err != nil {
log.Print(err)
continue
}
switch rune_ {
default:
fmt.Print(rune_, " ")
runes = append(runes, rune_)
char := strconv.QuoteRune(rune_)
chars = append(chars, char[1:len(char)-1])
continue
case 13:
fmt.Printf("\r\n\r\n%v\r\n\"%s\"\r\n", runes, strings.Join(chars, " "))
break L
}
}
}
}