Browse Source

Initial commit

Piotr Czajkowski 3 years ago
commit
f8d46ad872
10 changed files with 442 additions and 0 deletions
  1. 57 0
      detectLanguage.go
  2. 42 0
      detectLanguage_test.go
  3. 70 0
      generateHTML.go
  4. 5 0
      go.mod
  5. 24 0
      go.sum
  6. 25 0
      html/result.html
  7. 126 0
      server.go
  8. 28 0
      testFiles/c.txt
  9. 40 0
      testFiles/csharp.txt
  10. 25 0
      testFiles/go.txt

+ 57 - 0
detectLanguage.go

@@ -0,0 +1,57 @@
+package main
+
+import (
+	"bufio"
+	"strings"
+)
+
+var checks map[string][]string
+
+func init() {
+	checks = make(map[string][]string)
+	checks["c"] = []string{"printf(", "malloc(", "realloc(", "free(", "#include", "#define", "sizeof", "typeof"}
+	checks["go"] = []string{"fmt.", "package ", ":= range", "make(map[", "if err != nil"}
+	checks["csharp"] = []string{"using", "namespace", ".Where(", ".Select(", "public ", "private ", "readonly ", "List<", " async ", "await "}
+}
+
+func removeString(array []string, index int) []string {
+	return append(array[:index], array[index+1:]...)
+}
+
+func check(content string, keywords []string) float64 {
+	keywordsToCheck := make([]string, len(keywords))
+	copy(keywordsToCheck, keywords)
+	certainty := 0.0
+
+	scanner := bufio.NewScanner(strings.NewReader(content))
+	for scanner.Scan() {
+		if certainty >= 1.0 || len(keywordsToCheck) == 0 {
+			break
+		}
+
+		line := scanner.Text()
+		for index, keyword := range keywordsToCheck {
+			if strings.Contains(line, keyword) {
+				certainty += 0.1
+				keywordsToCheck = removeString(keywordsToCheck, index)
+			}
+		}
+	}
+
+	return certainty
+}
+
+func detectLanguage(content string) string {
+	currentLanguage := ""
+	currentBest := 0.0
+
+	for key, value := range checks {
+		result := check(content, value)
+		if result > currentBest {
+			currentLanguage = key
+			currentBest = result
+		}
+	}
+
+	return currentLanguage
+}

+ 42 - 0
detectLanguage_test.go

@@ -0,0 +1,42 @@
+package main
+
+import (
+	"io/ioutil"
+	"testing"
+)
+
+type testCase struct {
+	language string
+	code     string
+	score    float64
+}
+
+var testCases []testCase
+
+func init() {
+	testCases = []testCase{
+		{"c", "testFiles/c.txt", 0.4},
+		{"csharp", "testFiles/csharp.txt", 0.6},
+		{"go", "testFiles/go.txt", 0.5},
+	}
+}
+
+func TestCheckAndDetection(t *testing.T) {
+	for _, test := range testCases {
+		content, err := ioutil.ReadFile(test.code)
+		if err != nil {
+			t.Errorf("Error reading file %s: %s", test.code, err)
+		}
+		code := string(content)
+
+		result := check(code, checks[test.language])
+		if result < test.score {
+			t.Errorf("Certainty should be at least %.1f, but is %.1f", test.score, result)
+		}
+
+		language := detectLanguage(code)
+		if language != test.language {
+			t.Errorf("Language should be %s, but is %s", test.language, language)
+		}
+	}
+}

+ 70 - 0
generateHTML.go

@@ -0,0 +1,70 @@
+package main
+
+import (
+	"fmt"
+	"github.com/alecthomas/chroma"
+	"github.com/alecthomas/chroma/formatters/html"
+	"github.com/alecthomas/chroma/lexers"
+	"github.com/alecthomas/chroma/styles"
+	"io/ioutil"
+	"log"
+	"os"
+)
+
+func readFile(path string) (string, error) {
+	content, err := ioutil.ReadFile(path)
+	if err != nil {
+		return "", fmt.Errorf("Error reading file %s: %s", path, err)
+	}
+
+	return string(content), nil
+}
+
+func createHTML(content string, file *os.File) error {
+	language := detectLanguage(content)
+	lexer := lexers.Get(language)
+	if lexer == nil {
+		lexer = lexers.Fallback
+	}
+	lexer = chroma.Coalesce(lexer)
+
+	style := styles.Get("dracula")
+	if style == nil {
+		style = styles.Fallback
+	}
+
+	formatter := html.New(html.Standalone(true), html.WithLineNumbers(true), html.LinkableLineNumbers(true, ""), html.LineNumbersInTable(true))
+	iterator, err := lexer.Tokenise(nil, content)
+
+	err = formatter.Format(file, style, iterator)
+	if err != nil {
+		return fmt.Errorf("Error writing HTML: %s", err)
+	}
+
+	return nil
+}
+
+func convert(files paths) error {
+	content, err := readFile(files.textFile)
+	if err != nil {
+		return fmt.Errorf("Error reading file %s: %s", files.textFile, err)
+	}
+
+	file, err := os.Create(files.htmlFile)
+	if err != nil {
+		return fmt.Errorf("Error creating file: %s", err)
+	}
+	defer func() {
+		err := file.Close()
+		if err != nil {
+			log.Printf("Error closing file: %s", err)
+		}
+	}()
+
+	err = createHTML(content, file)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

+ 5 - 0
go.mod

@@ -0,0 +1,5 @@
+module codeHighlight
+
+go 1.15
+
+require github.com/alecthomas/chroma v0.8.2

+ 24 - 0
go.sum

@@ -0,0 +1,24 @@
+github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
+github.com/alecthomas/chroma v0.8.2 h1:x3zkuE2lUk/RIekyAJ3XRqSCP4zwWDfcw/YJCuCAACg=
+github.com/alecthomas/chroma v0.8.2/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
+github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
+github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
+github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
+github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
+github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
+github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

+ 25 - 0
html/result.html

@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+	<head>
+		<meta charset="utf-8">
+		<script async src="https://cdn.ampproject.org/v0.js"></script>
+		<title>Something went wrong</title>
+		<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
+		<script type="application/ld+json">
+			{
+				"@context": "http://schema.org",
+				"@type": "NewsArticle",
+				"headline": "Open-source framework for publishing content",
+				"datePublished": "2015-10-07T12:02:41Z",
+				"image": [
+				"logo.jpg"
+				]
+			}
+		</script>
+		<link rel="stylesheet" href="https://newcss.net/lite.css">
+		<link rel="stylesheet" href="https://newcss.net/theme/night.css">
+	</head>
+	<body>
+		<div>{{.}}</div>
+	</body>
+</html>

+ 126 - 0
server.go

@@ -0,0 +1,126 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"html/template"
+	"log"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+var host = flag.String("h", "localhost", "Host on which to serve")
+var port = flag.String("p", "9090", "Port on which to serve")
+var mainFolder = flag.String("f", "", "Absolute path to where files are kept")
+var logFilePath = flag.String("l", "", "Path to log file")
+
+type paths struct {
+	textFile string
+	htmlFile string
+}
+
+func getPaths(folder string) (paths, error) {
+	destination := filepath.Join(*mainFolder, folder)
+	if !strings.HasPrefix(destination, *mainFolder) {
+		return paths{}, fmt.Errorf("Wrong destination: %s", destination)
+	}
+
+	if _, err := os.Stat(destination); os.IsNotExist(err) {
+		return paths{}, fmt.Errorf("Destination folder doesn't exist at %s!", destination)
+	}
+
+	textFile := filepath.Join(destination, "index.txt")
+	if _, err := os.Stat(textFile); os.IsNotExist(err) {
+		return paths{}, fmt.Errorf("Text file doesn't exist at %s!", textFile)
+	}
+
+	return paths{textFile, filepath.Join(destination, "index.html")}, nil
+}
+
+type params struct {
+	folder string
+	option string
+}
+
+func getParameters(path string) (params, error) {
+	if path == "" {
+		return params{}, fmt.Errorf("Empty string!")
+	}
+
+	parts := strings.Split(path, "/")
+	partsCount := len(parts)
+	if partsCount > 2 {
+		return params{}, fmt.Errorf("Too many parameters: %s", parts)
+	}
+
+	if partsCount == 2 {
+		return params{parts[0], parts[1]}, nil
+	}
+
+	return params{parts[0], ""}, nil
+}
+
+func serve(w http.ResponseWriter, r *http.Request) {
+	pathFromURL := strings.TrimPrefix(r.URL.Path, "/")
+	t := template.Must(template.ParseFiles("./html/result.html"))
+
+	parameters, err := getParameters(pathFromURL)
+	if err != nil {
+		log.Println(err)
+		t.Execute(w, "Bad parameters!")
+		return
+	}
+
+	paths, err := getPaths(parameters.folder)
+	if err != nil {
+		log.Println(err)
+		w.WriteHeader(http.StatusNotFound)
+		t.Execute(w, "Not found!")
+		return
+	}
+
+	if parameters.option == "t" {
+		http.ServeFile(w, r, paths.textFile)
+		return
+	}
+
+	if _, err := os.Stat(paths.htmlFile); os.IsNotExist(err) {
+		err := convert(paths)
+		if err != nil {
+			log.Println(err)
+			http.ServeFile(w, r, paths.textFile)
+			return
+		}
+	}
+
+	http.ServeFile(w, r, paths.htmlFile)
+}
+
+func setLog() *os.File {
+	if *logFilePath == "" {
+		return nil
+	}
+
+	file, err := os.OpenFile(*logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+	if err != nil {
+		log.Fatalf("Can't open logs file: %s", err)
+	}
+
+	log.SetOutput(file)
+	return file
+}
+
+func main() {
+	flag.Parse()
+
+	logFile := setLog()
+	if logFile != nil {
+		defer logFile.Close()
+	}
+
+	hostname := *host + ":" + *port
+	http.HandleFunc("/", serve)
+	log.Fatal(http.ListenAndServe(hostname, nil))
+}

+ 28 - 0
testFiles/c.txt

@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int arraySize = 0;
+
+int **readInput(FILE *fp) {
+	int **array = NULL;
+	int index = 0;
+
+	while (1) {
+		if (feof(fp)) break;
+
+		int *p = malloc(sizeof(int));
+		if (!p) return NULL;
+
+		if (1 != fscanf(fp, "%d\n", p))
+			return NULL;
+
+		arraySize++;
+		int **newArray = realloc(array, sizeof(int*)*arraySize);
+		if (!newArray) return NULL;
+		array = newArray;
+		array[index] = p;
+		index++;
+	}
+
+	return array;
+}

+ 40 - 0
testFiles/csharp.txt

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using System.Xml.Serialization;
+using Newtonsoft.Json;
+
+namespace PSDText
+{
+    public class PSDText
+    {
+        private readonly string _xmlData;
+        private readonly XmlNamespaceManager _ns = new XmlNamespaceManager(new NameTable());
+        public List<TextData> TextData;
+        private string Readxmpmeta(string path)
+        {
+            var sb = new StringBuilder(1000);
+            using (var sr = new StreamReader(path))
+            {
+                var read = false;
+                while (true)
+                {
+                    var line = sr.ReadLine();
+                    if (string.IsNullOrWhiteSpace(line))
+                        return sb.ToString();
+
+                    if (line.StartsWith("<x:xmpmeta"))
+                        read = true;
+
+                    if (read) sb.Append(line);
+
+                    if (line.StartsWith("</x:xmpmeta>"))
+                        break;
+                }
+            }
+
+            return sb.ToString();
+        }

+ 25 - 0
testFiles/go.txt

@@ -0,0 +1,25 @@
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"strings"
+)
+
+func readNumbers(startingNumbers string) (int, map[int][2]int) {
+	lastNumber := 0
+	numbersSpoken := make(map[int][2]int)
+	for i, item := range strings.Split(string(startingNumbers), ",") {
+		var number int
+		n, err := fmt.Sscanf(item, "%d", &number)
+		if err != nil || n < 1 {
+			log.Fatal(err)
+		}
+
+		lastNumber = number
+		numbersSpoken[number] = [2]int{i + 1, 0}
+	}
+
+	return lastNumber, numbersSpoken
+}