1// +build !cgo
2// +build upgrade,ignore
3
4package main
5
6import (
7	"archive/zip"
8	"bufio"
9	"bytes"
10	"fmt"
11	"io"
12	"io/ioutil"
13	"log"
14	"net/http"
15	"os"
16	"path"
17	"path/filepath"
18	"strings"
19	"time"
20
21	"github.com/PuerkitoBio/goquery"
22)
23
24func download(prefix string) (url string, content []byte, err error) {
25	year := time.Now().Year()
26
27	site := "https://www.sqlite.org/download.html"
28	//fmt.Printf("scraping %v\n", site)
29	doc, err := goquery.NewDocument(site)
30	if err != nil {
31		log.Fatal(err)
32	}
33
34	doc.Find("a").Each(func(_ int, s *goquery.Selection) {
35		if strings.HasPrefix(s.Text(), prefix) {
36			url = fmt.Sprintf("https://www.sqlite.org/%d/", year) + s.Text()
37		}
38	})
39
40	if url == "" {
41		return "", nil, fmt.Errorf("Unable to find prefix '%s' on sqlite.org", prefix)
42	}
43
44	fmt.Printf("Downloading %v\n", url)
45	resp, err := http.Get(url)
46	if err != nil {
47		log.Fatal(err)
48	}
49
50	// Ready Body Content
51	content, err = ioutil.ReadAll(resp.Body)
52	defer resp.Body.Close()
53	if err != nil {
54		return "", nil, err
55	}
56
57	return url, content, nil
58}
59
60func mergeFile(src string, dst string) error {
61	defer func() error {
62		fmt.Printf("Removing: %s\n", src)
63		err := os.Remove(src)
64
65		if err != nil {
66			return err
67		}
68
69		return nil
70	}()
71
72	// Open destination
73	fdst, err := os.OpenFile(dst, os.O_APPEND|os.O_WRONLY, 0666)
74	if err != nil {
75		return err
76	}
77	defer fdst.Close()
78
79	// Read source content
80	content, err := ioutil.ReadFile(src)
81	if err != nil {
82		return err
83	}
84
85	// Add Additional newline
86	if _, err := fdst.WriteString("\n"); err != nil {
87		return err
88	}
89
90	fmt.Printf("Merging: %s into %s\n", src, dst)
91	if _, err = fdst.Write(content); err != nil {
92		return err
93	}
94
95	return nil
96}
97
98func main() {
99	fmt.Println("Go-SQLite3 Upgrade Tool")
100
101	// Download Amalgamation
102	_, amalgamation, err := download("sqlite-amalgamation-")
103	if err != nil {
104		fmt.Println("Failed to download: sqlite-amalgamation; %s", err)
105	}
106
107	// Download Source
108	_, source, err := download("sqlite-src-")
109	if err != nil {
110		fmt.Println("Failed to download: sqlite-src; %s", err)
111	}
112
113	// Create Amalgamation Zip Reader
114	rAmalgamation, err := zip.NewReader(bytes.NewReader(amalgamation), int64(len(amalgamation)))
115	if err != nil {
116		log.Fatal(err)
117	}
118
119	// Create Source Zip Reader
120	rSource, err := zip.NewReader(bytes.NewReader(source), int64(len(source)))
121	if err != nil {
122		log.Fatal(err)
123	}
124
125	// Extract Amalgamation
126	for _, zf := range rAmalgamation.File {
127		var f *os.File
128		switch path.Base(zf.Name) {
129		case "sqlite3.c":
130			f, err = os.Create("sqlite3-binding.c")
131		case "sqlite3.h":
132			f, err = os.Create("sqlite3-binding.h")
133		case "sqlite3ext.h":
134			f, err = os.Create("sqlite3ext.h")
135		default:
136			continue
137		}
138		if err != nil {
139			log.Fatal(err)
140		}
141		zr, err := zf.Open()
142		if err != nil {
143			log.Fatal(err)
144		}
145
146		_, err = io.WriteString(f, "#ifndef USE_LIBSQLITE3\n")
147		if err != nil {
148			zr.Close()
149			f.Close()
150			log.Fatal(err)
151		}
152		scanner := bufio.NewScanner(zr)
153		for scanner.Scan() {
154			text := scanner.Text()
155			if text == `#include "sqlite3.h"` {
156				text = `#include "sqlite3-binding.h"`
157			}
158			_, err = fmt.Fprintln(f, text)
159			if err != nil {
160				break
161			}
162		}
163		err = scanner.Err()
164		if err != nil {
165			zr.Close()
166			f.Close()
167			log.Fatal(err)
168		}
169		_, err = io.WriteString(f, "#else // USE_LIBSQLITE3\n // If users really want to link against the system sqlite3 we\n// need to make this file a noop.\n #endif")
170		if err != nil {
171			zr.Close()
172			f.Close()
173			log.Fatal(err)
174		}
175		zr.Close()
176		f.Close()
177		fmt.Printf("Extracted: %v\n", filepath.Base(f.Name()))
178	}
179
180	//Extract Source
181	for _, zf := range rSource.File {
182		var f *os.File
183		switch path.Base(zf.Name) {
184		case "userauth.c":
185			f, err = os.Create("userauth.c")
186		case "sqlite3userauth.h":
187			f, err = os.Create("userauth.h")
188		default:
189			continue
190		}
191		if err != nil {
192			log.Fatal(err)
193		}
194		zr, err := zf.Open()
195		if err != nil {
196			log.Fatal(err)
197		}
198
199		_, err = io.Copy(f, zr)
200		if err != nil {
201			log.Fatal(err)
202		}
203
204		zr.Close()
205		f.Close()
206		fmt.Printf("extracted %v\n", filepath.Base(f.Name()))
207	}
208
209	// Merge SQLite User Authentication into amalgamation
210	if err := mergeFile("userauth.c", "sqlite3-binding.c"); err != nil {
211		log.Fatal(err)
212	}
213	if err := mergeFile("userauth.h", "sqlite3-binding.h"); err != nil {
214		log.Fatal(err)
215	}
216
217	os.Exit(0)
218}
219