mirror of
https://github.com/rwinkhart/cmutton.git
synced 2026-08-27 20:36:31 -04:00
Use Pascal strings in place of null-termination; update documentation
This commit is contained in:
@@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
typedef struct {
|
||||||
|
char* data;
|
||||||
|
int len;
|
||||||
|
} PascalString;
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// getPascalString returns a pascal
|
||||||
|
// string struct for the input Go string.
|
||||||
|
func getPascalString(goString string) C.PascalString {
|
||||||
|
goStringBytes := []byte(goString)
|
||||||
|
goStringPtr := C.CBytes(goStringBytes)
|
||||||
|
return C.PascalString{
|
||||||
|
data: (*C.char)(goStringPtr),
|
||||||
|
len: C.int(len(goStringBytes)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPascalStringFromBytes returns a pascal
|
||||||
|
// string struct for the input Go byte slice.
|
||||||
|
func getPascalStringFromBytes(goBytes []byte) C.PascalString {
|
||||||
|
goBytesPtr := C.CBytes(goBytes)
|
||||||
|
return C.PascalString{
|
||||||
|
data: (*C.char)(goBytesPtr),
|
||||||
|
len: C.int(len(goBytes)),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2025 Randall Winkhart
|
Copyright (c) 2025-2026 Randall Winkhart
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,8 +1,48 @@
|
|||||||
# cmutton
|
# cmutton
|
||||||
Official C bindings for [libmutton](https://github.com/rwinkhart/libmutton).
|
Official C bindings for [libmutton](https://github.com/rwinkhart/libmutton).
|
||||||
|
|
||||||
|
This repository contains what is needed to generate a C library+headers for interfacing with libmutton
|
||||||
|
in native code. The bindings were written by hand and cover all libmutton functionality that is possible
|
||||||
|
to export to C.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
Build the C library + headers w/`go build -buildmode=c-archive`.
|
### Building
|
||||||
|
`go build -buildmode=c-archive`.
|
||||||
|
### Functions
|
||||||
|
All relevant exported libmutton functions have C functions of the same name.
|
||||||
|
Functions with multiple return values all have CGO-generated structs to store the return values.
|
||||||
|
These structs are named `<FunctionName>_return`, as per CGO.
|
||||||
|
All functions continue to perform the same basic operations, with a couple caveats:
|
||||||
|
- Anything that would normally return a Go error now returns a null-terminated *C.char containing the error string.
|
||||||
|
These are safe to null-terminate because their values are much more predictable than other strings.
|
||||||
|
Errors are always the *first* return value, so they can always be referenced with `<result>.r0`.
|
||||||
|
- Anything that would normally return a Go string or byte slice now returns a C.PascalString struct.
|
||||||
|
This is to avoid bugs with null-terminated strings.
|
||||||
|
|
||||||
|
Additionally, since comments tend to fall out of date, please rely on the [Go documentation for libmutton](https://pkg.go.dev/github.com/rwinkhart/libmutton). Documentation for the CGO bindings present in this repo only specify return values.
|
||||||
|
### Example (libmutton's global.DirInit)
|
||||||
|
Build the following example with `gcc <filename> ./cmutton.a`.
|
||||||
|
```c
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "cmutton.h" // import cmutton
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// use CGO-generated struct to get multiple return values
|
||||||
|
struct DirInit_return result = DirInit(1);
|
||||||
|
|
||||||
|
// familiar error handling pattern
|
||||||
|
if (result.r0 != NULL) {
|
||||||
|
printf("Error: %s\n", result.r0);
|
||||||
|
exit(1); // result.r0 not freed since program exits
|
||||||
|
}
|
||||||
|
|
||||||
|
// print Pascal string using "%.*s" with printf and supplying both length and data
|
||||||
|
printf("Old device ID: %.*s\n", result.r1.len, result.r1.data);
|
||||||
|
|
||||||
|
// be sure to free the data!
|
||||||
|
free(result.r1.data);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Progress
|
# Progress
|
||||||
- [ ] age
|
- [ ] age
|
||||||
@@ -33,14 +73,17 @@ Build the C library + headers w/`go build -buildmode=c-archive`.
|
|||||||
- [ ] DecryptFileToSlice(realPath string) ([]string, error)
|
- [ ] DecryptFileToSlice(realPath string) ([]string, error)
|
||||||
- [ ] EncryptBytes(decBytes []byte) []byte
|
- [ ] EncryptBytes(decBytes []byte) []byte
|
||||||
- [ ] RCWDArgument()
|
- [ ] RCWDArgument()
|
||||||
|
- [ ] VAR: Daemonize bool
|
||||||
|
- [ ] VAR: RetryPassword bool
|
||||||
- [ ] global
|
- [ ] global
|
||||||
- [X] DirInit(preserveOldCfgDir bool) (string, error)
|
- [X] DirInit(preserveOldCfgDir bool) (string, error)
|
||||||
- [ ] ~~GenDeviceIDList() ([]fs.DirEntry, error)~~
|
- [ ] ~~GenDeviceIDList() ([]fs.DirEntry, error)~~ (not for use outside of libmutton)
|
||||||
- [X] GetCurrentDeviceID() (string, error)
|
- [X] GetCurrentDeviceID() (string, error)
|
||||||
- [X] GetRealAgePath(vanityPath string) string
|
- [X] GetRealAgePath(vanityPath string) string
|
||||||
- [X] GetRealPath(vanityPath string) string
|
- [X] GetRealPath(vanityPath string) string
|
||||||
- [ ] ~~GetSysProcAttr() *syscall.SysProcAttr~~
|
- [ ] ~~GetSysProcAttr() *syscall.SysProcAttr~~ (not for use outside of libmutton)
|
||||||
- [X] GetVanityPath(realPath string) string
|
- [X] GetVanityPath(realPath string) string
|
||||||
|
- [ ] VAR (CB func): GetPassword
|
||||||
- [ ] syncclient
|
- [ ] syncclient
|
||||||
- [ ] AddFolderRemote(vanityPath string) error
|
- [ ] AddFolderRemote(vanityPath string) error
|
||||||
- [ ] GenDeviceID(oldDeviceID, prefix string) (string, string, bool, error)
|
- [ ] GenDeviceID(oldDeviceID, prefix string) (string, string, bool, error)
|
||||||
|
|||||||
@@ -2,62 +2,68 @@ package main
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
typedef struct {
|
||||||
|
char* data;
|
||||||
|
int len;
|
||||||
|
} PascalString;
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"github.com/rwinkhart/libmutton/global"
|
"github.com/rwinkhart/libmutton/global"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO Use a nil pointer for blank deviceIDs, not FSMisc (requires changes to libmutton)
|
||||||
|
|
||||||
// DirInit returns:
|
// DirInit returns:
|
||||||
// r0: oldDeviceID (FSMisc if none)
|
// r0: err
|
||||||
//
|
//
|
||||||
// r1: err
|
// r1: oldDeviceID (FSMisc if none)
|
||||||
//
|
//
|
||||||
//export DirInit
|
//export DirInit
|
||||||
func DirInit(preserveOldCfgDir bool) (*C.char, *C.char) {
|
func DirInit(preserveOldCfgDir bool) (*C.char, C.PascalString) {
|
||||||
oldDeviceID, err := global.DirInit(preserveOldCfgDir)
|
oldDeviceID, err := global.DirInit(preserveOldCfgDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, C.CString(err.Error())
|
return C.CString(err.Error()), C.PascalString{data: nil, len: 0}
|
||||||
}
|
}
|
||||||
return C.CString(oldDeviceID), nil
|
return nil, getPascalString(oldDeviceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDeviceID returns:
|
// GetCurrentDeviceID returns:
|
||||||
// r0: currentDeviceID (FSMisc if none)
|
// r0: err
|
||||||
//
|
//
|
||||||
// r1: err
|
// r1: currentDeviceID (FSMisc if none)
|
||||||
//
|
//
|
||||||
//export GetCurrentDeviceID
|
//export GetCurrentDeviceID
|
||||||
func GetCurrentDeviceID() (*C.char, *C.char) {
|
func GetCurrentDeviceID() (*C.char, C.PascalString) {
|
||||||
currentDeviceID, err := global.GetCurrentDeviceID()
|
currentDeviceID, err := global.GetCurrentDeviceID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, C.CString(err.Error())
|
return C.CString(err.Error()), C.PascalString{data: nil, len: 0}
|
||||||
}
|
}
|
||||||
return C.CString(currentDeviceID), nil
|
return nil, getPascalString(currentDeviceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRealAgePath returns:
|
// GetRealAgePath returns:
|
||||||
// realAgePath
|
// realAgePath
|
||||||
//
|
//
|
||||||
//export GetRealAgePath
|
//export GetRealAgePath
|
||||||
func GetRealAgePath(vanityPath *C.char) *C.char {
|
func GetRealAgePath(vanityPath *C.char) C.PascalString {
|
||||||
return C.CString(global.GetRealAgePath(C.GoString(vanityPath)))
|
return getPascalString(global.GetRealAgePath(C.GoString(vanityPath)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRealPath returns:
|
// GetRealPath returns:
|
||||||
// realPath
|
// realPath
|
||||||
//
|
//
|
||||||
//export GetRealPath
|
//export GetRealPath
|
||||||
func GetRealPath(vanityPath *C.char) *C.char {
|
func GetRealPath(vanityPath *C.char) C.PascalString {
|
||||||
return C.CString(global.GetRealPath(C.GoString(vanityPath)))
|
return getPascalString(global.GetRealPath(C.GoString(vanityPath)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVanityPath returns:
|
// GetVanityPath returns:
|
||||||
// vanityPath
|
// vanityPath
|
||||||
//
|
//
|
||||||
//export GetVanityPath
|
//export GetVanityPath
|
||||||
func GetVanityPath(realPath *C.char) *C.char {
|
func GetVanityPath(realPath *C.char) C.PascalString {
|
||||||
return C.CString(global.GetVanityPath(C.GoString(realPath)))
|
return getPascalString(global.GetVanityPath(C.GoString(realPath)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {}
|
func main() {}
|
||||||
|
|||||||
Reference in New Issue
Block a user