sauce

package module
v1.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 27, 2024 License: MIT Imports: 7 Imported by: 2

README

Go Reference   Go Report Card Coverage

Package sauce

Package sauce is a Go module that parses SAUCE (Standard Architecture for Universal Comment Extensions) metadata.

The Standard Architecture for Universal Comment Extensions or SAUCE as it is more commonly known, is an architecture or protocol for attaching meta data or comments to files. Mainly intended for ANSI art files, SAUCE has always had provisions for many different file types.

For the complete specification see:
http://www.acid.org/info/sauce/sauce.htm
https://github.com/radman1/sauce

Quick usage

Go Package with docs and examples.

// open a file
file, err := os.Open("artwork.ans")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

// read the file and create a SAUCE record
sr, err := sauce.Read(file)
if err != nil {
    log.Println(err)
    return
}

// print specific SAUCE fields
fmt.Printf("%q\n", sr.Title)
fmt.Printf("Author:\t%s.\n", sr.Author)
fmt.Printf("Group:\t%s.\n", sr.Group)
fmt.Printf("Date:\t%s.\n", sr.Date.Time.Format(time.ANSIC))

// print the SAUCE data as indented JSON
js, err := sr.JSONIndent("  ")
if err != nil {
    log.Println(err)
    return
}
fmt.Printf("%s", js)

SAUCE as an API reference

  • id
    SAUCE identification. This should be equal to SAUCE.

  • version
    SAUCE version number, should be 00.

  • title
    Title of the file.

  • author
    The nick, name or handle of the creator of the file.

  • group
    The name of the group or company the creator is employed by.

  • date - The date the file was created.

    • value - SAUCE date format, CCYYMMDD (century, year, month, day).
    • iso - value as an ISO 8601 date.
    • epoch- value as Unix time, the number of seconds since 1 Jan 1970.
  • fileSize

    • bytes - The reported file size not including the SAUCE information.
    • decimal - bytes returned as a base 10 value (kilo, mega...).
    • binary - bytes returned as a base 2 value (kibi, mebi...).
  • dataType - Type of data.

    • type - DataType value.
    • name - DataType name.
  • fileType - Type of file.

    • type - FileType value.
    • name - FileType name.
  • typeInfo - Type dependant information, see http://www.acid.org/info/sauce/sauce.htm#FileType.

    • 1
      • value - Value of TInfo1.
      • info - Human readable description of the value.
    • 2
      • value - Value of TInfo2.
      • info - Human readable description of the value.
    • 3
      • value - Value of TInfo3.
      • info - Human readable description of the value.
    • flags - Type dependant flags.
      • decimal - Value as an unsigned integer.
      • binary - Value in binary notation.
      • nonBlinkMode - Request ANSi non-blink mode (iCE Color).
        • flag - Value as binary bit boolean ("0" or "1").
        • interpretation - Human readable description of the value.
      • letterSpacing - ANSi letter-spacing to request 8 or 9 pixel font selection.
        • flag - Value as a 2-bit binary string ("00", "01", "10").
        • interpretation - Human readable description of the value.
      • aspectRatio - ANSi aspect ratio to request LCD square or CRT monitor style pixels.
        • flag - Value as a 2-bit binary string ("00", "01", "10").
        • interpretation - Human readable description of the value.
  • comments - Comments or notes from the creator.
    • id - SAUCE comment block identification, this should be "COMNT"
    • count- The reported number of lines in the SAUCE comment block.
    • lines - Lines of text, each line should comprise of 64 characters.

Similar projects and languages

Documentation

Overview

Package sauce is a Go module that parses SAUCE metadata.

What is SAUCE?

The Standard Architecture for Universal Comment Extensions is an architecture and protocol for attaching meta data and comments to files. While intended for ANSI art files, SAUCE has always had provisions for many different file types.

Why SAUCE?

From the original SAUCE specification:

In the early 1990s there was a growing popularity in ANSI artwork. The ANSI art groups regularly released the works of their members over a certain period. Some of the bigger groups also included specialised viewers in each ‘artpack’. One of the problems with these artpacks was a lack of standardized way to provide meta data to the art, such as the title of the artwork, the author, the group, ... Some of the specialised viewers provided such information for a specific artpack either by encoding it as part of the executable, or by having some sort of database or list. However every viewer did it their own way. This meant you either had to use the viewer included with the artpack, or had to make do without the extra info. SAUCE was created to address that need. So if you wanted to, you could use your preferred viewer to view the art in a certain artpack, or even store the art files you liked in a separate folder while retaining the meta data.

The goal was simple, but the way to get there certainly was not. Logistically, we wanted as many art groups as possible to support it. Technically, we wanted a system that was easy to implement and – if at all possible – manage to provide this meta data while still being compatible with all the existing software such as ANSI viewers, and Bulletin Board Software.

Example
package main

import (
	"embed"
	"fmt"
	"log"
	"time"

	"github.com/bengarrett/sauce"
)

//go:embed static/*
var static embed.FS

func main() { //nolint:funlen
	// open a file
	file, err := static.Open("static/sauce.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// read the file and create a SAUCE record
	sr, err := sauce.Read(file)
	if err != nil {
		log.Println(err)
		return
	}
	if !sr.Valid() {
		fmt.Println("no sauce metadata found")
		return
	}

	// print specific SAUCE fields
	fmt.Printf("%q\n", sr.Title)
	fmt.Printf("Author,\t%s.\n", sr.Author)
	fmt.Printf("Group,\t%s.\n", sr.Group)
	fmt.Printf("Date,\t%s.\n", sr.Date.Time.Format(time.ANSIC))

	// return the SAUCE data as indented JSON
	js, err := sr.JSONIndent("    ")
	if err != nil {
		log.Println(err)
		return
	}
	fmt.Printf("%s", js)

}
Output:

"Sauce title"
Author,	Sauce author.
Group,	Sauce group.
Date,	Sat Nov 26 00:00:00 2016.
{
    "id": "SAUCE",
    "version": "00",
    "title": "Sauce title",
    "author": "Sauce author",
    "group": "Sauce group",
    "date": {
        "value": "20161126",
        "iso": "2016-11-26T00:00:00Z",
        "epoch": 1480118400
    },
    "filesize": {
        "bytes": 3741,
        "decimal": "3.7 kB",
        "binary": "3.7 KiB"
    },
    "dataType": {
        "type": 1,
        "name": "text or character stream"
    },
    "fileType": {
        "type": 0,
        "name": "ASCII text"
    },
    "typeInfo": {
        "1": {
            "value": 977,
            "info": "character width"
        },
        "2": {
            "value": 9,
            "info": "number of lines"
        },
        "3": {
            "value": 0,
            "info": ""
        },
        "flags": {
            "decimal": 19,
            "binary": "10011",
            "nonBlinkMode": {
                "flag": "1",
                "interpretation": "non-blink mode"
            },
            "letterSpacing": {
                "flag": "00",
                "interpretation": "no preference"
            },
            "aspectRatio": {
                "flag": "11",
                "interpretation": "invalid value"
            }
        },
        "fontName": "IBM VGA"
    },
    "comments": {
        "id": "COMNT",
        "count": 1,
        "lines": [
            "Any comments go here.                                           "
        ]
    }
}

Index

Examples

Constants

View Source
const (
	ID      = "SAUCE"
	Version = "00" // the version is always 00
)

SAUCE identifier and version.

View Source
const Date = "20060102"

Date format layout.

View Source
const EOF byte = 26

EOF is the end-of-file marker, otherwise known as SUB, the substitute character.

Variables

This section is empty.

Functions

func Contains

func Contains(b []byte) bool

Contains reports whether a valid SAUCE record is within b.

Example
b, err := static.ReadFile("static/sauce.txt")
if err != nil {
	fmt.Print(err)
}
fmt.Print(sauce.Contains(b))
Output:

true
Example (None)
package main

import (
	"fmt"

	"github.com/bengarrett/sauce"
)

func main() {
	b := []byte("This string of text does not contain any SAUCE.")
	fmt.Print(sauce.Contains(b))
}
Output:

false

func Index

func Index(b []byte) int

Index returns the index of the instance of the SAUCE ID in b, or -1 if it is not present in b.

Example
b, err := static.ReadFile("static/sauce.txt")
if err != nil {
	fmt.Print(err)
}
i := sauce.Index(b)
fmt.Printf("SAUCE metadata position index: %v", i)
Output:

SAUCE metadata position index: 1190

func Trim

func Trim(b []byte) []byte

Trim returns b without any SAUCE metadata and the optional end-of-file marker.

Example
b, err := static.ReadFile("static/sauce.txt")
if err != nil {
	fmt.Print(err)
}

t := sauce.Trim(b)

fmt.Printf("The original size of the file is %d bytes and contains sauce? %v\n", len(b), sauce.Contains(b))
fmt.Printf("The trimmed size of the file is %d bytes and contains sauce? %v\n", len(t), sauce.Contains(t))
Output:

The original size of the file is 1318 bytes and contains sauce? true
The trimmed size of the file is 1119 bytes and contains sauce? false
Example (Comnt)
b, err := static.ReadFile("static/sauce.txt")
if err != nil {
	fmt.Print(err)
}

// normalize line endings
b = bytes.ReplaceAll(b, []byte("\r\n"), []byte("\n"))

// print the raw sauce binary data
const index = 7
if x := bytes.Split(b, []byte("\n")); len(x) >= index+1 {
	fmt.Printf("\nSAUCE: %q\n\n", x[index])
}

// print the trimmed text
fmt.Printf("%s", string(sauce.Trim(b)))
Output:

SAUCE: "\x1aCOMNTAny comments go here.                                           SAUCE00Sauce title                        Sauce author        Sauce group         20161126\x9d\x0e\x00\x00\x01\x00\xd1\x03\t\x00\x00\x00\x00\x00\x01\x13IBM VGA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

File: static/sauce.txt

This must be opened and saved as a Windows 1252 document.

SAUCE package test with complete data and comment.

Cras sit amet purus urna. Phasellus in dapibus ex. Proin pretium eget leo ut gravida. Praesent egestas urna at tincidunt mollis. Vivamus nec urna lorem. Vestibulum molestie accumsan lectus, in egestas metus facilisis eget. Nam consectetur, metus et sodales aliquam, mi dui dapibus metus, non cursus libero felis ac nunc. Nulla euismod, turpis sed mollis faucibus, orci elit dapibus leo, vitae placerat diam eros sed velit. Fusce convallis, lorem ut vulputate suscipit, tortor risus rhoncus mauris, a mattis metus magna at lorem. Sed molestie velit ipsum, in vulputate metus consequat eget. Fusce quis dui lacinia, laoreet lectus et, luctus velit. Pellentesque ut nisi quis orci pulvinar placerat vel ac lorem. Maecenas finibus fermentum erat, a pulvinar augue dictum mattis. Aenean vulputate consectetur velit at dictum. Donec vehicula ante quis ante venenatis, eu ultrices lectus egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
Example (Nocomnt)
b, err := static.ReadFile("static/sauce-nocomnt.txt")
if err != nil {
	fmt.Print(err)
}

// normalize line endings
b = bytes.ReplaceAll(b, []byte("\r\n"), []byte("\n"))

// print the raw sauce binary data
const index = 7
if x := bytes.Split(b, []byte("\n")); len(x) >= index+1 {
	fmt.Printf("\nSAUCE: %q\n\n", x[index])
}

// print the trimmed text
fmt.Printf("%s", string(sauce.Trim(b)))
Output:

SAUCE: "\x1aSAUCE00Sauce title                        Sauce author        Sauce group         20161126\x9d\x0e\x00\x00\x01\x00\xd1\x03\t\x00\x00\x00\x00\x00\x01\x13IBM VGA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

File: static/sauce-nocomnt.txt

This must be opened and saved as a Windows 1252 document.

SAUCE package test with complete data and comment.

Cras sit amet purus urna. Phasellus in dapibus ex. Proin pretium eget leo ut gravida. Praesent egestas urna at tincidunt mollis. Vivamus nec urna lorem. Vestibulum molestie accumsan lectus, in egestas metus facilisis eget. Nam consectetur, metus et sodales aliquam, mi dui dapibus metus, non cursus libero felis ac nunc. Nulla euismod, turpis sed mollis faucibus, orci elit dapibus leo, vitae placerat diam eros sed velit. Fusce convallis, lorem ut vulputate suscipit, tortor risus rhoncus mauris, a mattis metus magna at lorem. Sed molestie velit ipsum, in vulputate metus consequat eget. Fusce quis dui lacinia, laoreet lectus et, luctus velit. Pellentesque ut nisi quis orci pulvinar placerat vel ac lorem. Maecenas finibus fermentum erat, a pulvinar augue dictum mattis. Aenean vulputate consectetur velit at dictum. Donec vehicula ante quis ante venenatis, eu ultrices lectus egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;

Types

type Record

type Record struct {
	ID       string         `json:"id" xml:"id,attr"`           // SAUCE identification.
	Version  string         `json:"version" xml:"version,attr"` // version must equal "00".
	Title    string         `json:"title" xml:"title"`          // title of the file.
	Author   string         `json:"author" xml:"author"`        // author of the file.
	Group    string         `json:"group" xml:"group"`          // author employer or membership.
	Date     layout.Dates   `json:"date" xml:"date"`            // date of creation or release.
	FileSize layout.Sizes   `json:"filesize" xml:"filesize"`    // size of file in bytes without SAUCE.
	Data     layout.Datas   `json:"dataType" xml:"data_type"`   // data type of file.
	File     layout.Files   `json:"fileType" xml:"file_type"`   // file type of file.
	Info     layout.Infos   `json:"typeInfo" xml:"type_info"`   // file type dependant information.
	Desc     string         `json:"-" xml:"-"`                  // description of the file.
	Comnt    layout.Comment `json:"comments" xml:"comments"`    // comment block or notes.
}

Record is the SAUCE data structure that corresponds with the SAUCE Layout fields.

func Decode

func Decode(b []byte) Record

Decode the SAUCE data contained within b.

Example
b, err := static.ReadFile("static/sauce.txt")
if err != nil {
	fmt.Print(err)
}

sr := sauce.Decode(b)
fmt.Printf("%+v", sr)
Output:

{ID:SAUCE Version:00 Title:Sauce title Author:Sauce author Group:Sauce group Date:{Value:20161126 Time:2016-11-26 00:00:00 +0000 UTC Epoch:1480118400} FileSize:{Bytes:3741 Decimal:3.7 kB Binary:3.7 KiB} Data:{Type:text or character stream Name:text or character stream} File:{Type:0 Name:ASCII text} Info:{Info1:{Value:977 Info:character width} Info2:{Value:9 Info:number of lines} Info3:{Value:0 Info:} Flags:{Decimal:19 Binary:10011 B:{Flag:non-blink mode Info:non-blink mode} LS:{Flag:no preference Info:no preference} AR:{Flag:invalid value Info:invalid value} Interpretations:} Font:IBM VGA} Desc:ASCII text file with no formatting codes or color codes. Comnt:{ID:COMNT Count:1 Index:1121 Comment:[Any comments go here.                                           ]}}
Example (None)
package main

import (
	"fmt"

	"github.com/bengarrett/sauce"
)

func main() {
	b := []byte("This string of text does not contain any SAUCE.")

	sr := sauce.Decode(b)
	fmt.Printf("%+v", sr)
}
Output:

{ID: Version: Title: Author: Group: Date:{Value: Time:0001-01-01 00:00:00 +0000 UTC Epoch:0} FileSize:{Bytes:0 Decimal: Binary:} Data:{Type:undefined Name:} File:{Type:0 Name:} Info:{Info1:{Value:0 Info:} Info2:{Value:0 Info:} Info3:{Value:0 Info:} Flags:{Decimal:0 Binary: B:{Flag:invalid value Info:} LS:{Flag:invalid value Info:} AR:{Flag:invalid value Info:} Interpretations:} Font:} Desc: Comnt:{ID: Count:0 Index:-1 Comment:[]}}

func NewRecord

func NewRecord(r io.Reader) (*Record, error)

NewRecord is deprecated, use Read.

func Read added in v1.2.1

func Read(r io.Reader) (*Record, error)

Read and return the SAUCE record in r.

Example
// open file
file, err := static.Open("static/sauce.txt")
if err != nil {
	log.Print(err)
	return
}
defer file.Close()

// create a new sauce record
sr, err := sauce.Read(file)
if err != nil {
	log.Print(err)
	return
}

// encode and print the sauce record as JSON
js, err := sr.JSON()
if err != nil {
	log.Print(err)
	return
}
fmt.Print(string(js))
Output:

{"id":"SAUCE","version":"00","title":"Sauce title","author":"Sauce author","group":"Sauce group","date":{"value":"20161126","iso":"2016-11-26T00:00:00Z","epoch":1480118400},"filesize":{"bytes":3741,"decimal":"3.7 kB","binary":"3.7 KiB"},"dataType":{"type":1,"name":"text or character stream"},"fileType":{"type":0,"name":"ASCII text"},"typeInfo":{"1":{"value":977,"info":"character width"},"2":{"value":9,"info":"number of lines"},"3":{"value":0,"info":""},"flags":{"decimal":19,"binary":"10011","nonBlinkMode":{"flag":"1","interpretation":"non-blink mode"},"letterSpacing":{"flag":"00","interpretation":"no preference"},"aspectRatio":{"flag":"11","interpretation":"invalid value"}},"fontName":"IBM VGA"},"comments":{"id":"COMNT","count":1,"lines":["Any comments go here.                                           "]}}
Example (None)
package main

import (
	"bytes"
	"fmt"
	"log"

	"github.com/bengarrett/sauce"
)

func main() {
	b := []byte("This string of text does not contain any SAUCE.")

	// create a new sauce record
	sr, err := sauce.Read(bytes.NewReader(b))
	if err != nil {
		fmt.Printf("error: %v", err)
		return
	}

	// encode and print the sauce record as JSON
	js, err := sr.JSON()
	if err != nil {
		log.Print(err)
		return
	}
	fmt.Print(string(js))
}
Output:

{"id":"","version":"","title":"","author":"","group":"","date":{"value":"","iso":"0001-01-01T00:00:00Z","epoch":0},"filesize":{"bytes":0,"decimal":"","binary":""},"dataType":{"type":0,"name":""},"fileType":{"type":0,"name":""},"typeInfo":{"1":{"value":0,"info":""},"2":{"value":0,"info":""},"3":{"value":0,"info":""},"flags":{"decimal":0,"binary":"","nonBlinkMode":{"flag":"","interpretation":""},"letterSpacing":{"flag":"","interpretation":""},"aspectRatio":{"flag":"","interpretation":""}},"fontName":""},"comments":{"id":"","count":0,"lines":[]}}

func (*Record) JSON

func (r *Record) JSON() ([]byte, error)

JSON returns the JSON encoding of the r SAUCE record.

Example
b, err := static.ReadFile("static/sauce.txt")
if err != nil {
	fmt.Print(err)
}

sr := sauce.Decode(b)
js, err := sr.JSON()
if err != nil {
	fmt.Print(err)
}
fmt.Print(string(js))
Output:

{"id":"SAUCE","version":"00","title":"Sauce title","author":"Sauce author","group":"Sauce group","date":{"value":"20161126","iso":"2016-11-26T00:00:00Z","epoch":1480118400},"filesize":{"bytes":3741,"decimal":"3.7 kB","binary":"3.7 KiB"},"dataType":{"type":1,"name":"text or character stream"},"fileType":{"type":0,"name":"ASCII text"},"typeInfo":{"1":{"value":977,"info":"character width"},"2":{"value":9,"info":"number of lines"},"3":{"value":0,"info":""},"flags":{"decimal":19,"binary":"10011","nonBlinkMode":{"flag":"1","interpretation":"non-blink mode"},"letterSpacing":{"flag":"00","interpretation":"no preference"},"aspectRatio":{"flag":"11","interpretation":"invalid value"}},"fontName":"IBM VGA"},"comments":{"id":"COMNT","count":1,"lines":["Any comments go here.                                           "]}}

func (*Record) JSONIndent

func (r *Record) JSONIndent(indent string) ([]byte, error)

JSONIndent is like JSON but applies Indent to format the output. Each JSON element in the output will begin on a new line beginning with one or more copies of indent according to the indentation nesting.

Example
{ //nolint:funlen
	b, err := static.ReadFile("static/sauce.txt")
	if err != nil {
		fmt.Print(err)
	}

	sr := sauce.Decode(b)

	const indent = "  "
	js, err := sr.JSONIndent(indent)
	if err != nil {
		fmt.Print(err)
	}
	fmt.Print(string(js))
	
Output:

{
  "id": "SAUCE",
  "version": "00",
  "title": "Sauce title",
  "author": "Sauce author",
  "group": "Sauce group",
  "date": {
    "value": "20161126",
    "iso": "2016-11-26T00:00:00Z",
    "epoch": 1480118400
  },
  "filesize": {
    "bytes": 3741,
    "decimal": "3.7 kB",
    "binary": "3.7 KiB"
  },
  "dataType": {
    "type": 1,
    "name": "text or character stream"
  },
  "fileType": {
    "type": 0,
    "name": "ASCII text"
  },
  "typeInfo": {
    "1": {
      "value": 977,
      "info": "character width"
    },
    "2": {
      "value": 9,
      "info": "number of lines"
    },
    "3": {
      "value": 0,
      "info": ""
    },
    "flags": {
      "decimal": 19,
      "binary": "10011",
      "nonBlinkMode": {
        "flag": "1",
        "interpretation": "non-blink mode"
      },
      "letterSpacing": {
        "flag": "00",
        "interpretation": "no preference"
      },
      "aspectRatio": {
        "flag": "11",
        "interpretation": "invalid value"
      }
    },
    "fontName": "IBM VGA"
  },
  "comments": {
    "id": "COMNT",
    "count": 1,
    "lines": [
      "Any comments go here.                                           "
    ]
  }
}

func (*Record) Valid

func (r *Record) Valid() bool

Valid reports the completeness of the r SAUCE record.

Example
b, err := static.ReadFile("static/sauce.txt")
if err != nil {
	fmt.Print(err)
}

sr := sauce.Decode(b)
fmt.Print(sr.Valid())
Output:

true
Example (Invalid)
package main

import (
	"fmt"

	"github.com/bengarrett/sauce"
)

func main() {
	b := []byte("This string of text does not contain any SAUCE.")
	sr := sauce.Decode(b)

	fmt.Print(sr.Valid())
}
Output:

false

func (*Record) XML

func (r *Record) XML() ([]byte, error)

XML returns the XML encoding of the r SAUCE record.

Example
b, err := static.ReadFile("static/sauce.txt")
if err != nil {
	fmt.Print(err)
}
sr := sauce.Decode(b)
xm, err := sr.XML()
if err != nil {
	fmt.Print(err)
}
fmt.Print(string(xm))
Output:

<Record id="SAUCE" version="00"><title>Sauce title</title><author>Sauce author</author><group>Sauce group</group><date epoch="1480118400"><value>20161126</value><date>2016-11-26T00:00:00Z</date></date><filesize decimal="3.7 kB" binary="3.7 KiB"><bytes>3741</bytes></filesize><data_type><type>1</type><name>text or character stream</name></data_type><file_type><type>0</type><name>ASCII text</name></file_type><type_info><type1 type="character width"><value>977</value></type1><type2 type="number of lines"><value>9</value></type2><type3 type=""><value>0</value></type3><flags decimal="19" binary="10011"><non_blink_mode interpretation="non-blink mode"><flag>1</flag></non_blink_mode><letter_spacing interpretation="no preference"><flag>00</flag></letter_spacing><aspect_ratio interpretation="invalid value"><flag>11</flag></aspect_ratio></flags><fontname>IBM VGA</fontname></type_info><comments id="COMNT" count="1"><line>Any comments go here.                                           </line></comments></Record>

func (*Record) XMLIndent

func (r *Record) XMLIndent(indent string) ([]byte, error)

XMLIndent is like XML but applies Indent to format the output. Each XML element in the output will begin on a new line beginning with one or more copies of indent according to the indentation nesting.

Example
b, err := static.ReadFile("static/sauce.txt")
if err != nil {
	fmt.Print(err)
}
sr := sauce.Decode(b)
const indent = "  "
xm, err := sr.XMLIndent(indent)
if err != nil {
	fmt.Print(err)
}
fmt.Print(string(xm))
Output:

<Record id="SAUCE" version="00">
  <title>Sauce title</title>
  <author>Sauce author</author>
  <group>Sauce group</group>
  <date epoch="1480118400">
    <value>20161126</value>
    <date>2016-11-26T00:00:00Z</date>
  </date>
  <filesize decimal="3.7 kB" binary="3.7 KiB">
    <bytes>3741</bytes>
  </filesize>
  <data_type>
    <type>1</type>
    <name>text or character stream</name>
  </data_type>
  <file_type>
    <type>0</type>
    <name>ASCII text</name>
  </file_type>
  <type_info>
    <type1 type="character width">
      <value>977</value>
    </type1>
    <type2 type="number of lines">
      <value>9</value>
    </type2>
    <type3 type="">
      <value>0</value>
    </type3>
    <flags decimal="19" binary="10011">
      <non_blink_mode interpretation="non-blink mode">
        <flag>1</flag>
      </non_blink_mode>
      <letter_spacing interpretation="no preference">
        <flag>00</flag>
      </letter_spacing>
      <aspect_ratio interpretation="invalid value">
        <flag>11</flag>
      </aspect_ratio>
    </flags>
    <fontname>IBM VGA</fontname>
  </type_info>
  <comments id="COMNT" count="1">
    <line>Any comments go here.                                           </line>
  </comments>
</Record>

Directories

Path Synopsis
Package humanize is parses some limited time and byte sizes data to human readable formats.
Package humanize is parses some limited time and byte sizes data to human readable formats.
internal
layout
ANSiFlags allow an author of ANSi and similar files to provide a clue to a viewer / editor how to render the image.
ANSiFlags allow an author of ANSi and similar files to provide a clue to a viewer / editor how to render the image.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL