Compare commits
7 Enmetoj
6ac7a6b535
...
72b5894823
Author | SHA1 | Date | |
---|---|---|---|
Jaidyn Ann | 72b5894823 | ||
Jaidyn Ann | 9b2eaa72d3 | ||
Jaidyn Ann | 9e1947b0f7 | ||
Jaidyn Ann | adaa561e59 | ||
Jaidyn Ann | 1b5e9d5b1f | ||
Jaidyn Ann | fbea00fb66 | ||
Jaidyn Ann | c799d7a37c |
|
@ -0,0 +1,73 @@
|
||||||
|
# xattr
|
||||||
|
## Interface for extended filesystem attributes
|
||||||
|
|
||||||
|
[Extended attributes](https://en.wikipedia.org/wiki/Extended_attributes) are a special type of file attribute that lets you associate key-value pairs with a file without actually modifying the file's contents. Commonly used for program settings, tagging, and file metadata (i.e., storing a file's author with a key like “author.name”) xattrs can be used for used for pretty much anything you can dream up. They're mega-cool and useful.
|
||||||
|
|
||||||
|
This is a simple interface for accessing them within [Chicken Scheme](https://call-cc.org/), a friendly Scheme.
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
### get-xattr
|
||||||
|
`get-xattr path attr`
|
||||||
|
|
||||||
|
Returns the string-value of a file's extended attribute.
|
||||||
|
If the file has no such attribute, #f is returned. In other cases (e.g., the file doesn't exist, permission not available, no filesystem support), exceptions are raised.
|
||||||
|
|
||||||
|
### set-xattr
|
||||||
|
`set-xattr path attr value`
|
||||||
|
|
||||||
|
Sets the string-value of a file's extended attribute.
|
||||||
|
The set value is returned, if successful.
|
||||||
|
|
||||||
|
### remove-xattr
|
||||||
|
`remove-xattr path attr`
|
||||||
|
|
||||||
|
Removes an extended attribute from a file.
|
||||||
|
The value of the removed attribute is returned, if successful.
|
||||||
|
|
||||||
|
### list-xattrs
|
||||||
|
`list-xattrs path`
|
||||||
|
|
||||||
|
Returns a list of the file's extended attribute names.
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
```
|
||||||
|
(import xattr (chicken io) (chicken file))
|
||||||
|
;; Create the test file
|
||||||
|
(call-with-output-file "testing.txt"
|
||||||
|
(lambda (out-port) (write-line "Just a test!" out-port)))
|
||||||
|
|
||||||
|
(set-xattr "testing.txt" "user.dublincore.author" "Migdal E. F. Lemmings")
|
||||||
|
(set-xattr "testing.txt" "user.dublincore.title" "A Treatise on Almonds")
|
||||||
|
|
||||||
|
(get-xattr "testing.txt" "user.dublincore.author")
|
||||||
|
;; Returns "Migdal E. F. Lemmings"
|
||||||
|
|
||||||
|
(list-xattrs "testing.txt")
|
||||||
|
;; Returns '("user.dublincore.author" "user.dublincore.title")
|
||||||
|
|
||||||
|
(remove-xattr "testing.txt" "user.dublincore.title")
|
||||||
|
;; Returns "A Treatise on Almonds"
|
||||||
|
|
||||||
|
(list-xattrs "testing.txt")
|
||||||
|
;; Now returns '("user.dublincore.author")
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## On GNU/Linux
|
||||||
|
Currently, this egg only supports LiGNUx, a platform that has a particularity: The name of each attribute must contain a “namespace,” separated from the actual name by a period. For example, with the attribute named `user.xdg.origin.url`, the namespace is `user` and the name is `xdg.origin.url`. In most cases, you won't stray from the `user` namespace.
|
||||||
|
|
||||||
|
Don't forget to prepend “user.” to your attribute names!
|
||||||
|
|
||||||
|
A handy list of somewhat-standard extended attributes is hosted on FreeDesktop's website, [here](https://www.freedesktop.org/wiki/CommonExtendedAttributes/).
|
||||||
|
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
This egg requires `srfi-1` and `srfi-12`.
|
||||||
|
|
||||||
|
|
||||||
|
## Meta
|
||||||
|
**Author:** Jaidyn Ann <jadedctrl@posteo.at>
|
||||||
|
**License:** GPLv3
|
||||||
|
**Source:** https://github.com/jadedctrl/xattr, https://notabug.org/jadedctrl/xattr
|
|
@ -1,9 +1,7 @@
|
||||||
;; -*- Scheme -*-
|
;; -*- Scheme -*-
|
||||||
((synopsis "Access to extended attributes")
|
((synopsis "Access to extended attributes")
|
||||||
(author "Jaidyn Ann")
|
(author "Jaidyn Ann")
|
||||||
(category net)
|
(category io)
|
||||||
(license "GPLv3")
|
(license "GPLv3")
|
||||||
|
(dependencies srfi-1)
|
||||||
(components (extension xattr)))
|
(components (extension xattr)))
|
||||||
;; (csc-options "-O3" "-d1" "-X" "bind" "-s" "-k" "-emit-link-file" "xattr.link" "-o" "xattr_ext.o"))
|
|
||||||
;; (c-object xattr_ext)))
|
|
||||||
|
|
103
xattr.scm
103
xattr.scm
|
@ -1,5 +1,5 @@
|
||||||
;;
|
;;
|
||||||
;; Copyright 2022, Jaidyn Levesque <jadedctrl@posteo.at>
|
;; Copyright 2024, Jaidyn Levesque <jadedctrl@posteo.at>
|
||||||
;;
|
;;
|
||||||
;; This program is free software: you can redistribute it and/or
|
;; This program is free software: you can redistribute it and/or
|
||||||
;; modify it under the terms of the GNU General Public License as
|
;; modify it under the terms of the GNU General Public License as
|
||||||
|
@ -16,20 +16,23 @@
|
||||||
;;
|
;;
|
||||||
|
|
||||||
(module xattr
|
(module xattr
|
||||||
(get-xattr set-xattr remove-xattr list-xattr)
|
(get-xattr set-xattr remove-xattr list-xattrs)
|
||||||
|
|
||||||
(import (chicken base) (chicken memory) srfi-1 scheme (chicken foreign) srfi-12)
|
(import scheme
|
||||||
|
(chicken base) (chicken condition) (chicken foreign) (chicken memory)
|
||||||
(foreign-declare "#include \"xattr_ext.c\"")
|
srfi-1)
|
||||||
|
|
||||||
|
|
||||||
;; The direct foreign binding for `get_xattr`
|
(foreign-declare "#include \"xattr_ext.c\"")
|
||||||
(define get-xattr-foreign
|
|
||||||
|
|
||||||
|
;; The direct foreign binding for `get_xattr`
|
||||||
|
(define get-xattr-foreign
|
||||||
(foreign-lambda c-string* "get_xattr" c-string c-string (c-pointer int)))
|
(foreign-lambda c-string* "get_xattr" c-string c-string (c-pointer int)))
|
||||||
|
|
||||||
|
|
||||||
;; Wrapper around get-xattr-foreign, which throws exceptions and such.
|
;; Wrapper around get-xattr-foreign, which throws exceptions and such.
|
||||||
(define (get-xattr path attr)
|
(define (get-xattr path attr)
|
||||||
(let-location ([error-code int])
|
(let-location ([error-code int])
|
||||||
(let ([attr-value (get-xattr-foreign path attr (location error-code))]
|
(let ([attr-value (get-xattr-foreign path attr (location error-code))]
|
||||||
[exception (or (getxattr-exception error-code)
|
[exception (or (getxattr-exception error-code)
|
||||||
|
@ -39,16 +42,16 @@
|
||||||
attr-value))))
|
attr-value))))
|
||||||
|
|
||||||
|
|
||||||
;; The direct foreign binding for `list_xattr`
|
;; The direct foreign binding for `list_xattr`
|
||||||
(define list-xattr-foreign
|
(define list-xattrs-foreign
|
||||||
(foreign-lambda (c-pointer char) "list_xattr" c-string (c-pointer ssize_t) (c-pointer int)))
|
(foreign-lambda (c-pointer char) "list_xattr" c-string (c-pointer ssize_t) (c-pointer int)))
|
||||||
|
|
||||||
|
|
||||||
;; Wrapper around list-xattr-foreign, which throws exceptions and such.
|
;; Wrapper around list-xattrs-foreign, which throws exceptions and such.
|
||||||
(define (list-xattr path)
|
(define (list-xattrs path)
|
||||||
(let-location ([error-code int]
|
(let-location ([error-code int]
|
||||||
[length ssize_t])
|
[length ssize_t])
|
||||||
(let ([list-pointer (list-xattr-foreign path (location length) (location error-code))]
|
(let ([list-pointer (list-xattrs-foreign path (location length) (location error-code))]
|
||||||
[exception (or (getxattr-exception error-code)
|
[exception (or (getxattr-exception error-code)
|
||||||
(stat-exception error-code))])
|
(stat-exception error-code))])
|
||||||
(if exception
|
(if exception
|
||||||
|
@ -57,13 +60,13 @@
|
||||||
(pointer->delimited-string-list list-pointer length 0)))))
|
(pointer->delimited-string-list list-pointer length 0)))))
|
||||||
|
|
||||||
|
|
||||||
;; The direct foreign binding for `set_xattr`
|
;; The direct foreign binding for `set_xattr`
|
||||||
(define set-xattr-foreign
|
(define set-xattr-foreign
|
||||||
(foreign-lambda int "set_xattr" c-string c-string c-string (c-pointer int)))
|
(foreign-lambda int "set_xattr" c-string c-string c-string (c-pointer int)))
|
||||||
|
|
||||||
|
|
||||||
;; Wrapper around set-xattr-foreign, throwing exceptions and all that jazz
|
;; Wrapper around set-xattr-foreign, throwing exceptions and all that jazz
|
||||||
(define (set-xattr path attr value)
|
(define (set-xattr path attr value)
|
||||||
(let-location ([error-code int])
|
(let-location ([error-code int])
|
||||||
(let ([return-code (set-xattr-foreign path attr value (location error-code))]
|
(let ([return-code (set-xattr-foreign path attr value (location error-code))]
|
||||||
[exception (or (setxattr-exception error-code)
|
[exception (or (setxattr-exception error-code)
|
||||||
|
@ -74,29 +77,28 @@
|
||||||
value))))
|
value))))
|
||||||
|
|
||||||
|
|
||||||
;; The direct foreign binding for `remove_xattr`
|
;; The direct foreign binding for `remove_xattr`
|
||||||
(define remove-xattr-foreign
|
(define remove-xattr-foreign
|
||||||
(foreign-lambda int "remove_xattr" c-string c-string))
|
(foreign-lambda int "remove_xattr" c-string c-string))
|
||||||
|
|
||||||
|
|
||||||
;; Wrapper around remove-xattr-foreign, blah blah
|
;; Wrapper around remove-xattr-foreign, blah blah
|
||||||
(define (remove-xattr path attr)
|
(define (remove-xattr path attr)
|
||||||
(let* ([error-code (remove-xattr-foreign path attr)]
|
(let* ([value (get-xattr path attr)]
|
||||||
|
[error-code (remove-xattr-foreign path attr)]
|
||||||
[exception (or (getxattr-exception error-code)
|
[exception (or (getxattr-exception error-code)
|
||||||
(stat-exception error-code))])
|
(stat-exception error-code))])
|
||||||
(if exception
|
(if exception
|
||||||
(signal exception)
|
(signal exception)
|
||||||
attr)))
|
value)))
|
||||||
|
|
||||||
|
|
||||||
;; TODO: These exception functions should be constructed with a macro and a simple
|
;; TODO: These exception functions should be constructed with a macro and a simple
|
||||||
;; list, like `((c-constant symbol error-message) ("E2BIG" 'e2big "The attribute value was too big."))
|
;; list, like `((c-constant symbol error-message) ("E2BIG" 'e2big "The attribute value was too big."))
|
||||||
;; Unfortunately, it looks like chicken's macros work a good bit differently from CLs?
|
|
||||||
;; orr I'm losing my mind
|
|
||||||
|
|
||||||
|
|
||||||
;; Return the exception associated with an error-code as per getxattr(2), if it exists
|
;; Return the exception associated with an error-code as per getxattr(2), if it exists
|
||||||
(define (getxattr-exception error-code)
|
(define (getxattr-exception error-code)
|
||||||
(cond
|
(cond
|
||||||
[(eq? error-code (foreign-value "E2BIG" int))
|
[(eq? error-code (foreign-value "E2BIG" int))
|
||||||
(build-exception 'e2big "The attribute value was too big.")]
|
(build-exception 'e2big "The attribute value was too big.")]
|
||||||
|
@ -108,8 +110,8 @@
|
||||||
#f]))
|
#f]))
|
||||||
|
|
||||||
|
|
||||||
;; Return the exception associated with a setxattr(2) error-code, if it exists.
|
;; Return the exception associated with a setxattr(2) error-code, if it exists.
|
||||||
(define (setxattr-exception error-code)
|
(define (setxattr-exception error-code)
|
||||||
(cond
|
(cond
|
||||||
[(eq? error-code (foreign-value "EDQUOT" int))
|
[(eq? error-code (foreign-value "EDQUOT" int))
|
||||||
(build-exception 'edquot "Setting this attribute would violate disk quota.")]
|
(build-exception 'edquot "Setting this attribute would violate disk quota.")]
|
||||||
|
@ -123,8 +125,8 @@
|
||||||
#f]))
|
#f]))
|
||||||
|
|
||||||
|
|
||||||
;; Return the exception associated with an error-code as per stat(2), if applicable
|
;; Return the exception associated with an error-code as per stat(2), if applicable
|
||||||
(define (stat-exception error-code)
|
(define (stat-exception error-code)
|
||||||
(cond
|
(cond
|
||||||
[(eq? error-code (foreign-value "EACCES" int))
|
[(eq? error-code (foreign-value "EACCES" int))
|
||||||
(build-exception 'file "Search permission denied for a parent directory.")]
|
(build-exception 'file "Search permission denied for a parent directory.")]
|
||||||
|
@ -150,25 +152,30 @@
|
||||||
#f]))
|
#f]))
|
||||||
|
|
||||||
|
|
||||||
;; Creates a generic exception with given symbol and message
|
;; Creates a generic exception with given symbol and message
|
||||||
(define (build-exception symbol message)
|
(define (build-exception symbol message)
|
||||||
(signal (make-property-condition 'exn 'location symbol 'message message)))
|
(signal (make-property-condition 'exn 'location symbol 'message message)))
|
||||||
|
|
||||||
|
|
||||||
;; Some C functions offer up a string-list that isn't a two-dimensional array, but a
|
;; Some C functions offer up a string-list that isn't a two-dimensional array, but a
|
||||||
;; one-dimensional array with given length and strings separated by a given delimeter.
|
;; one-dimensional array with given length and strings separated by a given delimeter.
|
||||||
;; This takes that sort of pointer and gives you a nice list of strings.
|
;; This takes that sort of pointer and gives you a nice list of strings.
|
||||||
(define (pointer->delimited-string-list pointer length delimiter)
|
(define (pointer->delimited-string-list pointer length delimiter)
|
||||||
(let* ([is-zero (lambda (num) (eq? num 0))]
|
(let* ([is-zero (lambda (num) (eq? num 0))]
|
||||||
[map-to-char (lambda (a) (map integer->char a))]
|
[map-to-char (lambda (a) (map integer->char a))]
|
||||||
[byte-list (drop-right (pointer->integers pointer length) 1)])
|
[pointer-ints (pointer->integers pointer length)]
|
||||||
|
[byte-list (when (list? pointer-ints)
|
||||||
|
(drop-right pointer-ints 1))])
|
||||||
|
(if (and (list? byte-list)
|
||||||
|
(not (null-list? byte-list)))
|
||||||
(map list->string
|
(map list->string
|
||||||
(map map-to-char
|
(map map-to-char
|
||||||
(split-list is-zero byte-list)))))
|
(split-list is-zero byte-list)))
|
||||||
|
'())))
|
||||||
|
|
||||||
|
|
||||||
;; Takes a pointer and returns a list of bytes of a given length
|
;; Takes a pointer and returns a list of bytes of a given length
|
||||||
(define (pointer->integers pointer length)
|
(define (pointer->integers pointer length)
|
||||||
(let ([byte (pointer-s8-ref pointer)])
|
(let ([byte (pointer-s8-ref pointer)])
|
||||||
(if (eq? length 0)
|
(if (eq? length 0)
|
||||||
byte
|
byte
|
||||||
|
@ -176,8 +183,8 @@
|
||||||
(pointer->integers (pointer+ pointer 1) (- length 1))))))
|
(pointer->integers (pointer+ pointer 1) (- length 1))))))
|
||||||
|
|
||||||
|
|
||||||
;; Split a list into sublists, with items passing `test` being the delimiters
|
;; Split a list into sublists, with items passing `test` being the delimiters
|
||||||
(define (split-list test list)
|
(define (split-list test list)
|
||||||
(let ([before (take-while (compose not test) list)]
|
(let ([before (take-while (compose not test) list)]
|
||||||
[after (drop-while (compose not test) list)])
|
[after (drop-while (compose not test) list)])
|
||||||
(cond
|
(cond
|
||||||
|
@ -186,6 +193,4 @@
|
||||||
[(null-list? before)
|
[(null-list? before)
|
||||||
(split-list test (cdr after))]
|
(split-list test (cdr after))]
|
||||||
[#t
|
[#t
|
||||||
(append `(,before) (split-list test (cdr after)))])))
|
(append `(,before) (split-list test (cdr after)))]))))
|
||||||
|
|
||||||
)
|
|
||||||
|
|
Ŝarĝante…
Reference in New Issue