-
-
Save isocroft/0ed7efcc6d3e8cfdea60b14cbded644d to your computer and use it in GitHub Desktop.
Golang implementation for RFC 1342: Non-ASCII Mail Headers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "encoding/base64" | |
| "fmt" | |
| "io" | |
| "io/ioutil" | |
| "mime/quotedprintable" | |
| "regexp" | |
| "strings" | |
| "golang.org/x/text/encoding" | |
| "golang.org/x/text/encoding/charmap" | |
| "golang.org/x/text/encoding/unicode" | |
| ) | |
| func decoder(encoding string) (*encoding.Decoder, error) { | |
| if strings.ToUpper(encoding) == "UTF-8" { | |
| return unicode.UTF8.NewDecoder(), nil | |
| } else if strings.ToUpper(encoding) == "ISO-8859-1" { | |
| return charmap.ISO8859_1.NewDecoder(), nil | |
| } else { | |
| return nil, fmt.Errorf("Unknown encoding") | |
| } | |
| } | |
| func decodeHeader(str string) (string, error) { | |
| re := regexp.MustCompile(`\=\?(?P<charset>.*?)\?(?P<encoding>.*)\?(?P<body>.*?)\?(.*?)\=`) | |
| matches := re.FindAllStringSubmatch(str, -1) | |
| if len(matches) == 0 { | |
| return str, nil | |
| } | |
| for _, match := range matches { | |
| var r io.Reader = strings.NewReader(match[3]) | |
| if match[2] == "Q" { | |
| r = quotedprintable.NewReader(r) | |
| } else if match[2] == "B" { | |
| r = base64.NewDecoder(base64.StdEncoding, r) | |
| } | |
| if d, err := decoder(match[1]); err == nil { | |
| r = d.Reader(r) | |
| } | |
| if val, err := ioutil.ReadAll(r); err == nil { | |
| str = strings.Replace(str, match[0], string(val), -1) | |
| } else if err != nil { | |
| fmt.Println(err.Error()) | |
| continue | |
| } | |
| } | |
| return str, nil | |
| } | |
| func main() { | |
| fmt.Println(decodeHeader("=?UTF-8?Q?=F3=BE=AC=8D_?= ... Laten we ontmoeten! Ik woon in de buurt ..")) | |
| fmt.Println(decodeHeader("=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>")) | |
| } |
Author
assert function in Go until go implements it as a language feature
package debugging
import (
"log"
"fmt"
)
func Assert (condition bool, message string) {
if condition == false {
log.Errorf(message);
panic(fmt.Sprintf("assertion error: %s", message))
}
}
Author
package callgraph
import (
"fmt"
"os"
"path/filepath"
"strings"
sitter "github.com/smacker/go-tree-sitter"
"github.com/smacker/go-tree-sitter/javascript"
"github.com/smacker/go-tree-sitter/php"
"github.com/smacker/go-tree-sitter/python"
)
/*
Node represents a dependency tree node.
-- This is assumed to come from your dependency tree package. --
*/
type Node struct {
Path string
Dependencies []*Node
}
type FunctionNode struct {
ID string
// e.g. "main.py:doSomething"
// e.g. "datastore:seeder.py:run_with_schema"
// e.g. "events.emitters:broadcast.py:normalizeEgress"
// e.g. "system:root.go:Liftoff"
// e.g. "main.go:init"
// e.g. "index.js:collectInput"
// e.g. "root:index.php:array_push"
Callees []string
// e.g. ["print", "saveUser", "db.insert"]
Signature string
// e.g. lightweight non-cryptographic hash
// derived from function name + params
}
/*
Call map
*/
var callMap = make(map[string][]FunctionNode)
/*
CallGraph represents a directed graph:
Caller ---> Callee
*/
type CallGraph struct {
/*
Adjacency list:
FunctionName -> Set of Callees
*/
graph map[string]map[string]struct{}
/*
Set of all discovered function nodes
*/
nodes map[string]struct{}
}
/*
NewCallGraph creates and initializes
a new call graph.
*/
func NewCallGraph() *CallGraph {
return &CallGraph{
graph: make(map[string]map[string]struct{}),
nodes: make(map[string]struct{}),
}
}
/*
AddCall adds a directed edge:
caller ---> callee
*/
func (cg *CallGraph) AddCall(
caller string,
callee string,
) {
/*
Initialize caller adjacency set
if it does not exist.
*/
if _, exists := cg.graph[caller]; !exists {
cg.graph[caller] = make(map[string]struct{})
}
/*
Add callee into caller adjacency set
*/
cg.graph[caller][callee] = struct{}{}
/*
Register both nodes
*/
cg.nodes[caller] = struct{}{}
cg.nodes[callee] = struct{}{}
}
/*
GetSuccessorNodes returns all functions
called by the given function.
*/
func (cg *CallGraph) GetSuccessorNodes(
functionName string,
) []string {
callees, exists := cg.graph[functionName]
if !exists {
return []string{}
}
results := make([]string, 0, len(callees))
for callee := range callees {
results = append(results, callee)
}
return results
}
/*
HasNode checks whether a function exists
in the graph.
*/
func (cg *CallGraph) HasNode(
functionName string,
) bool {
_, exists := cg.nodes[functionName]
return exists
}
/*
GetAllNodes returns all graph nodes.
*/
func (cg *CallGraph) GetAllNodes() []string {
results := make([]string, 0, len(cg.nodes))
for node := range cg.nodes {
results = append(results, node)
}
return results
}
/*
String implements fmt.Stringer.
Equivalent to Python's __repr__.
*/
func (cg *CallGraph) String() string {
return fmt.Sprintf("%v", cg.graph)
}
/*
==== PYTHON PORT ====
from collections import defaultdict
class CallGraph:
def __init__(self):
# Adjacency list: Function -> Set of Callees
self.graph = defaultdict(set)
self.nodes = set()
def add_call(self, caller, callee):
"""Adds a directed edge from caller to callee."""
self.graph[caller].add(callee)
self.nodes.add(caller)
self.nodes.add(callee)
def get_successor_nodes(self, function_name):
"""
API: Returns a list of functions called by the given function.
"""
return list(self.graph.get(function_name, []))
def __repr__(self):
return str(dict(self.graph))
*/
/*
GetLanguage resolves a tree-sitter language grammar
from a source file extension.
*/
func getLanguage(extension string) (*sitter.Language, error) {
switch extension {
case ".js":
return javascript.GetLanguage(), nil
case ".py":
return python.GetLanguage(), nil
case ".php":
return php.GetLanguage(), nil
default:
return nil, fmt.Errorf(
"no matching source file extension: %s",
extension,
)
}
}
/*
readSourceFile reads source code contents.
*/
func readSourceFile(sourceFilePath string) ([]byte, error) {
content, err := os.ReadFile(sourceFilePath)
if err != nil {
return nil, fmt.Errorf(
"failed to read source file %s: %w",
sourceFilePath,
err,
)
}
return content, nil
}
/*
getNodeText extracts node text from source code.
*/
func getNodeText(node *sitter.Node, source []byte) string {
if node == nil {
return ""
}
return string(source[node.StartByte():node.EndByte()])
}
/*
findChildrenByType recursively searches for child nodes
of a given type.
*/
func findChildrenByType(
node *sitter.Node,
nodeType string,
results *[]*sitter.Node,
) {
if node == nil {
return
}
if node.Type() == nodeType {
*results = append(*results, node)
}
for i := 0; i < int(node.ChildCount()); i++ {
findChildrenByType(
node.Child(i),
nodeType,
results,
)
}
}
/*
extractFunctionName extracts function identifiers
from tree-sitter function definition nodes.
*/
func extractFunctionName(
node *sitter.Node,
source []byte,
) string {
if node == nil {
return ""
}
for i := 0; i < int(node.NamedChildCount()); i++ {
child := node.NamedChild(i)
if child == nil {
continue
}
switch child.Type() {
case "identifier", "name":
return getNodeText(child, source)
}
}
return "anonymous"
}
/*
extractCallName extracts callee names from call_expression nodes.
*/
func extractCallName(
node *sitter.Node,
source []byte,
) string {
if node == nil {
return ""
}
for i := 0; i < int(node.NamedChildCount()); i++ {
child := node.NamedChild(i)
if child == nil {
continue
}
switch child.Type() {
case "identifier", "member_expression", "attribute":
return getNodeText(child, source)
}
}
return ""
}
/*
extractModuleName extracts module/package namespaces
based on the source language.
Examples:
Python:
/path/to/pkg/utils/helpers.py
=> pkg.utils:helpers.py
PHP:
namespace Vendor\Module;
=> Vendor\Module:index.php
Go:
package name
=> name:start.go
*/
func extractModuleName(
sourceFilePath string,
sourceCode []byte,
extension string,
) string {
switch extension {
/*
Go module extraction
*/
case ".go":
/*
Matches:
package name;
*/
packageRegex := regexp.MustCompile(
`(?m)^\s*package\s+([a-zA-Z0-9_]+)\s*`,
)
match := packageRegex.FindSubmatch(sourceCode)
moduleName := "main"
if len(match) > 1 {
moduleName = string(match[1])
}
return moduleName + ":" + filepath.Base(sourceFilePath)
/*
Python module extraction
*/
case ".py":
normalizedPath := filepath.ToSlash(sourceFilePath)
/*
Remove base filename:
pkg/utils/helpers.py
=> pkg/utils
*/
withoutBase := strings.TrimSuffix(
normalizedPath,
filepath.Base(normalizedPath),
)
/*
Convert path separators into dots:
pkg/utils
=> pkg.utils
*/
moduleName := strings.ReplaceAll(
withoutBase,
"/",
".",
)
/*
Optionally remove leading dots
*/
moduleName = strings.TrimPrefix(
moduleName,
".",
)
return moduleName + ":" + filepath.Base(sourceFilePath)
/*
PHP namespace extraction
*/
case ".php":
/*
Matches:
namespace Vendor\Module;
*/
namespaceRegex := regexp.MustCompile(
`(?m)^\s*namespace\s+([a-zA-Z0-9_\\]+)\s*;`,
)
match := namespaceRegex.FindSubmatch(sourceCode)
nameSpace := "root"
if len(match) > 1 {
nameSpace = string(match[1])
}
moduleName := strings.ReplaceAll(
nameSpace,
"\\",
".",
)
/*
Optionally remove leading dots
*/
moduleName = strings.TrimPrefix(
moduleName,
".",
)
return moduleName + ":" + filepath.Base(sourceFilePath)
default:
return filepath.Base(sourceFilePath)
}
}
/*
extractFunctionParameters extracts the raw parameter
list from a function definition node.
*/
func extractFunctionParameters(
node *sitter.Node,
source []byte,
) string {
if node == nil {
return ""
}
for i := 0; i < int(node.NamedChildCount()); i++ {
child := node.NamedChild(i)
if child == nil {
continue
}
switch child.Type() {
/*
JavaScript:
formal_parameters
Python:
parameters
PHP:
formal_parameters
*/
case "formal_parameters",
"parameters":
return getNodeText(
child,
source,
)
}
}
return ""
}
/*
buildSignature creates a lightweight pseudo-signature.
*/
func buildSignature(functionName string, parameters string) string {
signatureSeed := fmt.Sprintf(
"%s(%s)_%x",
functionName,
strings.TrimSpace(parameters),
len(parameters)+len(strings.TrimSpace(functionName))
)
/*
@NOTE:
In production, use xxHash3 / SHA-1 / FNV-1a
*/
return fmt.Sprintf(
"sig_%s",
signatureSeed,
)
}
/*
scanForFunctionCalls:
scans each source file for all function calls and maps each
into a function-node struct.
*/
func scanForFunctionCalls(
sourceFilePath string,
) ([]FunctionNode, error) {
/*
@HINT
Initialize Tree-sitter parser
*/
parser := sitter.NewParser()
extension := filepath.Ext(sourceFilePath)
language, err := getLanguage(extension)
if err != nil {
return nil, err
}
parser.SetLanguage(language)
/*
@HINT
Parse file into a tree
*/
sourceCode, err := readSourceFile(sourceFilePath)
if err != nil {
return nil, err
}
tree, err := parser.ParseCtx(
nil,
nil,
sourceCode,
)
if err != nil {
return nil, fmt.Errorf(
"failed to parse %s: %w",
sourceFilePath,
err,
)
}
rootNode := tree.RootNode()
/*
@HINT:
Query the tree for 'call_expression',
'function_declaration' and 'function_definition'
*/
var functionNodes []*sitter.Node
var callNodes []*sitter.Node
findChildrenByType(
rootNode,
"function_definition",
&functionNodes,
)
findChildrenByType(
rootNode,
"function_declaration",
&functionNodes,
)
findChildrenByType(
rootNode,
"call_expression",
&callNodes,
)
results := make([]FunctionNode, 0)
/*
Map function definitions
*/
for _, fnNode := range functionNodes {
functionName := extractFunctionName(
fnNode,
sourceCode,
)
moduleName := extractModuleName(
sourceFilePath,
sourceCode,
extension,
)
functionID := fmt.Sprintf(
"%s:%s",
moduleName,
functionName,
)
parameters := extractFunctionParameters(
fnNode,
sourceCode,
)
functionEntry := FunctionNode{
ID: functionID,
Callees: make([]string, 0),
Signature: buildSignature(moduleName + ":" + functionName, strings.TrimPrefix(parameters, functionName+
"(")),
}
/*
Find calls inside the function body
*/
var nestedCalls []*sitter.Node
findChildrenByType(
fnNode,
"call_expression",
&nestedCalls,
)
for _, callNode := range nestedCalls {
callName := extractCallName(
callNode,
sourceCode,
)
if callName == "" {
continue
}
functionEntry.Callees = append(
functionEntry.Callees,
callName,
)
}
results = append(results, functionEntry)
}
/*
@INFO:
Handle loose/global calls
(not inside functions)
*/
if len(functionNodes) == 0 && len(callNodes) > 0 {
moduleName := extractModuleName(
sourceFilePath,
sourceCode,
extension,
)
parameters := extractFunctionParameters(
rootNode,
sourceCode,
)
globalNode := FunctionNode{
ID: fmt.Sprintf(
"%s:<global>",
moduleName,
),
Callees: make([]string, 0),
Signature: buildSignature(moduleName + ":global_scope", parameters),
}
for _, callNode := range callNodes {
callName := extractCallName(
callNode,
sourceCode,
)
if callName == "" {
continue
}
globalNode.Callees = append(
globalNode.Callees,
callName,
)
}
results = append(results, globalNode)
}
return results, nil
}
/*
walkDependencyTree recursively traverses the dependency tree
and generates function call mappings.
*/
func walkDependencyTree(
node *Node,
visited map[string]bool,
) error {
if node == nil {
return nil
}
absolutePath := node.Path
if visited[absolutePath] {
return nil
}
visited[absolutePath] = true
functionCalls, err := scanForFunctionCalls(
absolutePath,
)
if err != nil {
return err
}
callMap[absolutePath] = functionCalls
for _, dependency := range node.Dependencies {
err := walkDependencyTree(
dependency,
visited,
)
if err != nil {
return err
}
}
return nil
}
/*
GenerateAll:
generate the static call map using the dependency tree.
*/
func GenerateAll(
rootNode *Node,
) (map[string][]FunctionNode, error) {
if rootNode == nil {
return nil, fmt.Errorf(
"root dependency node cannot be nil",
)
}
visited := make(map[string]bool)
err := walkDependencyTree(
rootNode,
visited,
)
if err != nil {
return nil, err
}
return callMap, nil
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.