...

Source file src/gitlab.com/tslocum/gmitohtml/pkg/gmitohtml/convert.go

Documentation: gitlab.com/tslocum/gmitohtml/pkg/gmitohtml

     1  package gmitohtml
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"html"
     9  	"net/url"
    10  	"path"
    11  	"strings"
    12  	"sync"
    13  )
    14  
    15  // ErrInvalidURL is the error returned when the URL is invalid.
    16  var ErrInvalidURL = errors.New("invalid URL")
    17  
    18  var daemonAddress string
    19  
    20  var assetLock sync.Mutex
    21  
    22  func rewriteURL(u string, loc *url.URL) string {
    23  	if daemonAddress != "" {
    24  		scheme := "gemini"
    25  		if strings.HasPrefix(loc.Path, "/file/") {
    26  			scheme = "file"
    27  		}
    28  
    29  		if strings.HasPrefix(u, "file://") {
    30  			if !allowFileAccess {
    31  				return "http://" + daemonAddress + "/?FileAccessNotAllowed"
    32  			}
    33  			return "http://" + daemonAddress + "/file/" + u[7:]
    34  		}
    35  
    36  		offset := 0
    37  		if strings.HasPrefix(u, "gemini://") {
    38  			offset = 9
    39  		}
    40  		firstSlash := strings.IndexRune(u[offset:], '/')
    41  		if firstSlash != -1 {
    42  			u = strings.ToLower(u[:firstSlash+offset]) + u[firstSlash+offset:]
    43  		}
    44  
    45  		if strings.HasPrefix(u, "gemini://") {
    46  			return "http://" + daemonAddress + "/gemini/" + u[9:]
    47  		} else if strings.Contains(u, "://") {
    48  			return u
    49  		} else if loc != nil && len(u) > 0 && !strings.HasPrefix(u, "//") {
    50  			if u[0] != '/' {
    51  				if loc.Path[len(loc.Path)-1] == '/' {
    52  					u = path.Join("/", loc.Path, u)
    53  				} else {
    54  					u = path.Join("/", path.Dir(loc.Path), u)
    55  				}
    56  			}
    57  			return "http://" + daemonAddress + "/" + scheme + "/" + strings.ToLower(loc.Host) + u
    58  		}
    59  		return "http://" + daemonAddress + "/" + scheme + "/" + u
    60  	}
    61  	return u
    62  }
    63  
    64  func newPage() []byte {
    65  	data := []byte(pageHeader)
    66  	if daemonAddress != "" {
    67  		data = append(data, navHeader...)
    68  	}
    69  	return append(data, contentHeader...)
    70  }
    71  
    72  // Convert converts text/gemini to text/html.
    73  func Convert(page []byte, u string) []byte {
    74  	var result []byte
    75  
    76  	var preformatted bool
    77  
    78  	parsedURL, err := url.Parse(u)
    79  	if err != nil {
    80  		parsedURL = nil
    81  		err = nil
    82  	}
    83  
    84  	scanner := bufio.NewScanner(bytes.NewReader(page))
    85  	for scanner.Scan() {
    86  		line := scanner.Bytes()
    87  		l := len(line)
    88  		if l >= 3 && string(line[0:3]) == "```" {
    89  			preformatted = !preformatted
    90  			if preformatted {
    91  				result = append(result, []byte("<pre>\n")...)
    92  			} else {
    93  				result = append(result, []byte("</pre>\n")...)
    94  			}
    95  			continue
    96  		}
    97  
    98  		if preformatted {
    99  			result = append(result, html.EscapeString(string(line))...)
   100  			result = append(result, []byte("\n")...)
   101  			continue
   102  		}
   103  
   104  		if l >= 6 && bytes.HasPrefix(line, []byte("=>")) {
   105  			splitStart := 2
   106  			if line[splitStart] == ' ' || line[splitStart] == '\t' {
   107  				splitStart++
   108  			}
   109  
   110  			var split [][]byte
   111  			firstSpace := bytes.IndexRune(line[splitStart:], ' ')
   112  			firstTab := bytes.IndexRune(line[splitStart:], '\t')
   113  			if firstSpace != -1 && (firstTab == -1 || firstSpace < firstTab) {
   114  				split = bytes.SplitN(line[splitStart:], []byte(" "), 2)
   115  			} else if firstTab != -1 {
   116  				split = bytes.SplitN(line[splitStart:], []byte("\t"), 2)
   117  			}
   118  
   119  			var linkURL []byte
   120  			var linkLabel []byte
   121  			if len(split) == 2 {
   122  				linkURL = split[0]
   123  				linkLabel = split[1]
   124  			} else {
   125  				linkURL = line[splitStart:]
   126  				linkLabel = line[splitStart:]
   127  			}
   128  
   129  			link := append([]byte(`<a href="`), html.EscapeString(rewriteURL(string(linkURL), parsedURL))...)
   130  			link = append(link, []byte(`">`)...)
   131  			link = append(link, html.EscapeString(string(linkLabel))...)
   132  			link = append(link, []byte(`</a>`)...)
   133  			result = append(result, link...)
   134  			result = append(result, []byte("<br>")...)
   135  			continue
   136  		}
   137  
   138  		heading := 0
   139  		for i := 0; i < l; i++ {
   140  			if line[i] == '#' {
   141  				heading++
   142  			} else {
   143  				break
   144  			}
   145  		}
   146  		if heading > 0 {
   147  			result = append(result, []byte(fmt.Sprintf("<h%d>%s</h%d>", heading, html.EscapeString(string(line[heading:])), heading))...)
   148  			continue
   149  		}
   150  
   151  		result = append(result, html.EscapeString(string(line))...)
   152  		result = append(result, []byte("<br>")...)
   153  	}
   154  
   155  	if preformatted {
   156  		result = append(result, []byte("</pre>\n")...)
   157  	}
   158  
   159  	data := newPage()
   160  	data = append(data, result...)
   161  	data = append(data, []byte(pageFooter)...)
   162  	return fillTemplateVariables(data, u, false)
   163  }
   164  

View as plain text