diff --git a/.gitignore b/.gitignore
index 4ded78e..38a5a3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
chm2docset
+_fixtures/Sample.docset/Contents/Resources/docSet.dsidx
diff --git a/README.md b/README.md
index 6dd3248..2488931 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ How to use
```sh
brew install chmlib
+go get -u go github.com/ngs/chm2docset
go install github.com/ngs/chm2docset
chm2docset -platform docset-platform -out /path/to/MyRef.docset /path/to/MyReference.chm
```
diff --git a/_fixtures/Sample.docset/Contents/Resources/Documents/sub/test4.htm b/_fixtures/Sample.docset/Contents/Resources/Documents/sub/test4.htm
new file mode 100644
index 0000000..d55add6
--- /dev/null
+++ b/_fixtures/Sample.docset/Contents/Resources/Documents/sub/test4.htm
@@ -0,0 +1,11 @@
+
+
+ test
+
+ 4
+
+
+
+ test4
+
+
diff --git a/_fixtures/Sample.docset/Contents/Resources/Documents/test1.htm b/_fixtures/Sample.docset/Contents/Resources/Documents/test1.htm
new file mode 100644
index 0000000..ad4d54d
--- /dev/null
+++ b/_fixtures/Sample.docset/Contents/Resources/Documents/test1.htm
@@ -0,0 +1,9 @@
+
+
+ test 1
+
+
+
+ test1
+
+
diff --git a/_fixtures/Sample.docset/Contents/Resources/Documents/test2.htm b/_fixtures/Sample.docset/Contents/Resources/Documents/test2.htm
new file mode 100644
index 0000000..64824e6
--- /dev/null
+++ b/_fixtures/Sample.docset/Contents/Resources/Documents/test2.htm
@@ -0,0 +1,10 @@
+
+
+ test 2
+yo
+
+
+
+ test2
+
+
diff --git a/_fixtures/Sample.docset/Contents/Resources/Documents/test3.htm b/_fixtures/Sample.docset/Contents/Resources/Documents/test3.htm
new file mode 100644
index 0000000..74740dd
--- /dev/null
+++ b/_fixtures/Sample.docset/Contents/Resources/Documents/test3.htm
@@ -0,0 +1,7 @@
+
+
+
+
+ test3
+
+
diff --git a/_fixtures/bin/extract_chmLib b/_fixtures/bin/extract_chmLib
new file mode 100755
index 0000000..81647ab
--- /dev/null
+++ b/_fixtures/bin/extract_chmLib
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+[ -d tmp ] || mkdir tmp
+echo $@ > tmp/fixtureinput.txt
diff --git a/chm2docset.go b/chm2docset.go
index ee1bc18..e79ba64 100644
--- a/chm2docset.go
+++ b/chm2docset.go
@@ -16,6 +16,8 @@ import (
_ "github.com/mattn/go-sqlite3"
)
+var logFatal = log.Fatal
+
func usage() {
fmt.Fprintf(os.Stderr, "usage: %s [inputfile]\n", os.Args[0])
flag.PrintDefaults()
@@ -24,7 +26,7 @@ func usage() {
func failOnError(err error) {
if err != nil {
- log.Fatal(err)
+ logFatal(err)
}
}
@@ -128,7 +130,7 @@ func (opts *Options) PlistContent() string {
// WritePlist writes plist file
func (opts *Options) WritePlist() error {
- return ioutil.WriteFile(opts.PlistPath(), []byte(opts.PlistContent()), 644)
+ return ioutil.WriteFile(opts.PlistPath(), []byte(opts.PlistContent()), 0644)
}
// Clean removes existing output
@@ -136,18 +138,22 @@ func (opts *Options) Clean() error {
return os.RemoveAll(opts.DocsetPath())
}
+// CreateDirectory creates directory
+func (opts *Options) CreateDirectory() error {
+ return os.MkdirAll(opts.ContentPath(), 0755)
+}
+
// ExtractSource extracts source to destination
func (opts *Options) ExtractSource() error {
- if err := os.MkdirAll(opts.ContentPath(), 0755); err != nil {
- return err
- }
cmd := exec.Command("extract_chmLib", opts.SourcePath, opts.ContentPath())
return cmd.Run()
}
// CreateDatabase creates database
func (opts *Options) CreateDatabase() error {
+ os.Remove(opts.DatabasePath())
titleRE := regexp.MustCompile("([^<]+)")
+ spacesRE := regexp.MustCompile("[\\s\\t]+")
db, err := sql.Open("sqlite3", opts.DatabasePath())
if err != nil {
return err
@@ -178,7 +184,10 @@ func (opts *Options) CreateDatabase() error {
content := string(b)
res := titleRE.FindAllStringSubmatch(content, -1)
if len(res) >= 1 && len(res[0]) >= 2 {
- _, err := stmt.Exec(res[0][1], "Guide", strings.TrimPrefix(path, opts.ContentPath()))
+ ttl := strings.Replace(res[0][1], "\n", " ", -1)
+ ttl = spacesRE.ReplaceAllString(ttl, " ")
+ ttl = strings.TrimSpace(ttl)
+ _, err := stmt.Exec(ttl, "Guide", strings.TrimPrefix(path, opts.ContentPath()))
if err != nil {
return err
}
@@ -194,6 +203,7 @@ func (opts *Options) CreateDatabase() error {
func main() {
opts := NewOptions()
opts.Clean()
+ failOnError(opts.CreateDirectory())
failOnError(opts.ExtractSource())
failOnError(opts.CreateDatabase())
failOnError(opts.WritePlist())
diff --git a/chm2docset_test.go b/chm2docset_test.go
index 339aaf6..f20524a 100644
--- a/chm2docset_test.go
+++ b/chm2docset_test.go
@@ -1,14 +1,22 @@
package main
import (
+ "database/sql"
+ "io"
+ "io/ioutil"
+ "log"
"os"
"reflect"
"testing"
)
+func cleanTmp() {
+ os.RemoveAll("tmp")
+}
+
type Test struct {
- expected interface{}
actual interface{}
+ expected interface{}
}
func (test Test) Compare(t *testing.T) {
@@ -23,6 +31,89 @@ func (test Test) DeepEqual(t *testing.T) {
}
}
+// Copies file source to destination dest.
+func CopyFile(source string, dest string) (err error) {
+ sf, err := os.Open(source)
+ if err != nil {
+ return err
+ }
+ defer sf.Close()
+ df, err := os.Create(dest)
+ if err != nil {
+ return err
+ }
+ defer df.Close()
+ _, err = io.Copy(df, sf)
+ if err == nil {
+ if si, e := os.Stat(source); e != nil {
+ err = os.Chmod(dest, si.Mode())
+ }
+ }
+
+ return err
+}
+
+// Recursively copies a directory tree, attempting to preserve permissions.
+// Source directory must exist, destination directory must *not* exist.
+func CopyDir(source string, dest string) (err error) {
+
+ // get properties of source dir
+ fi, err := os.Stat(source)
+ if err != nil {
+ return err
+ }
+
+ if !fi.IsDir() {
+ return &CustomError{"Source is not a directory"}
+ }
+
+ // ensure dest dir does not already exist
+
+ _, err = os.Open(dest)
+ if !os.IsNotExist(err) {
+ return &CustomError{"Destination already exists"}
+ }
+
+ // create dest dir
+
+ err = os.MkdirAll(dest, fi.Mode())
+ if err != nil {
+ return err
+ }
+
+ entries, err := ioutil.ReadDir(source)
+
+ for _, entry := range entries {
+
+ sfp := source + "/" + entry.Name()
+ dfp := dest + "/" + entry.Name()
+ if entry.IsDir() {
+ err = CopyDir(sfp, dfp)
+ if err != nil {
+ log.Println(err)
+ }
+ } else {
+ // perform copy
+ err = CopyFile(sfp, dfp)
+ if err != nil {
+ log.Println(err)
+ }
+ }
+
+ }
+ return
+}
+
+// A struct for returning custom error messages
+type CustomError struct {
+ What string
+}
+
+// Returns the error message defined in What as a string
+func (e *CustomError) Error() string {
+ return e.What
+}
+
func TestNewOptionsSourceUnspecified(t *testing.T) {
os.Args = []string{"chm2docset"}
opts := NewOptions()
@@ -125,3 +216,91 @@ func TestPlistContent(t *testing.T) {
`}.Compare(t)
}
+
+func TestWritePlist(t *testing.T) {
+ opts := &Options{
+ SourcePath: "/foo/bar/baz.chm",
+ Outdir: "tmp/foo.docset",
+ }
+ opts.CreateDirectory()
+ err := opts.WritePlist()
+ if err != nil {
+ t.Errorf("Expected nil but got %v", err)
+ }
+ b, err := ioutil.ReadFile("tmp/foo.docset/Contents/Info.plist")
+ if err != nil {
+ t.Errorf("Expected nil but got %v", err)
+ }
+ Test{string(b), `
+
+
+
+ dashIndexFilePath
+ Welcome.htm
+ CFBundleIdentifier
+ io.ngs.documentation.baz
+ CFBundleName
+ baz
+ DocSetPlatformFamily
+
+ isDashDocset
+
+
+`}.Compare(t)
+ opts.Clean()
+ if stat, err := os.Stat("tmp/foo.docset"); os.IsExist(err) {
+ t.Errorf("Expect not exist but exists %v", stat)
+ }
+ cleanTmp()
+}
+
+func TestExtractSource(t *testing.T) {
+ opts := &Options{
+ SourcePath: "/foo/bar/baz.chm",
+ Outdir: "tmp/foo.docset",
+ }
+ opts.CreateDirectory()
+ os.Setenv("PATH", "_fixtures/bin:"+os.Getenv("PATH"))
+ err := opts.ExtractSource()
+ if err != nil {
+ t.Errorf("Expected nil but got %v", err)
+ }
+ b, err := ioutil.ReadFile("tmp/fixtureinput.txt")
+ if err != nil {
+ t.Errorf("Expected nil but got %v", err)
+ }
+ Test{string(b), "/foo/bar/baz.chm tmp/foo.docset/Contents/Resources/Documents\n"}.Compare(t)
+ cleanTmp()
+}
+
+func TestCreateDatabase(t *testing.T) {
+ opts := &Options{
+ SourcePath: "/foo/bar/baz.chm",
+ Outdir: "tmp/Sample.docset",
+ }
+ opts.Clean()
+ CopyDir("_fixtures/Sample.docset", "tmp/Sample.docset")
+ opts.CreateDatabase()
+ db, _ := sql.Open("sqlite3", opts.DatabasePath())
+ rows, _ := db.Query("SELECT * FROM searchIndex")
+ columns, _ := rows.Columns()
+ Test{columns, []string{"id", "name", "type", "path"}}.DeepEqual(t)
+ grid := [][]string{}
+ for rows.Next() {
+ var id string
+ var name string
+ var indexType string
+ var path string
+ err := rows.Scan(&id, &name, &indexType, &path)
+ if err != nil {
+ t.Errorf("Got errror %v", err)
+ }
+ grid = append(grid, []string{id, name, indexType, path})
+ }
+ Test{grid, [][]string{
+ {"1", "test 4", "Guide", "/sub/test4.htm"},
+ {"2", "test 1", "Guide", "/test1.htm"},
+ {"3", "test 2 yo", "Guide", "/test2.htm"},
+ }}.DeepEqual(t)
+ cleanTmp()
+}