From c1618deb838e89cd645ff358d295a2c5aa90e1bf Mon Sep 17 00:00:00 2001 From: "John K. Luebs" Date: Mon, 27 Apr 2020 12:34:56 -0400 Subject: [PATCH] checkpoint for real --- API.cs | 18 +- AlpmOld.cs | 532 +------------------------------- Backup.cs | 8 + CachePackage.cs | 19 ++ CollectionWrapper`2.cs | 94 ++++-- Database.cs | 95 +++++- Depend.cs | 34 ++ DotNetAlpm.csproj | 2 +- ErrNo.cs | 81 ++++- Exception.cs | 18 +- File.cs | 15 + FileList.cs | 23 ++ Folder.DotSettings | 4 + Handle.cs | 75 ++++- IAlpmItemsAccessor`1.cs | 3 +- IAlpmReadOnlyItemsAccessor`1.cs | 7 - IPackageData.cs | 64 ++++ IReadOnlyItemsAccessor`1.cs | 11 + Junk.cs | 346 ++++++++++++++++++++- Package.cs | 68 ++++ Program.cs | 21 +- ReadOnlyCollectionWrapper`2.cs | 47 ++- SafeAlpmHandle.cs | 21 +- SafeCachePackageHandle.cs | 52 ++++ SafeDatabaseHandle.cs | 53 +++- SafeListHandle.cs | 39 +-- SafePackageHandle.cs | 20 ++ UTF8InMarshaler.cs | 22 +- UTF8OutMarshaler.cs | 35 ++- Wrapper.cs | 6 +- alpm.cs | 222 ++++++++++++- 31 files changed, 1429 insertions(+), 626 deletions(-) create mode 100644 Backup.cs create mode 100644 CachePackage.cs create mode 100644 Depend.cs create mode 100644 File.cs create mode 100644 FileList.cs delete mode 100644 IAlpmReadOnlyItemsAccessor`1.cs create mode 100644 IPackageData.cs create mode 100644 IReadOnlyItemsAccessor`1.cs create mode 100644 Package.cs create mode 100644 SafeCachePackageHandle.cs create mode 100644 SafePackageHandle.cs diff --git a/API.cs b/API.cs index 9b781fd..62c1f54 100644 --- a/API.cs +++ b/API.cs @@ -1,4 +1,18 @@ -$HEADER$namespace $NAMESPACE$ +using System; + +namespace Foodsoft.Alpm { - public class $CLASS$ {$END$} + public static class API + { + public static readonly string Version = alpm.alpm_version(); + + internal static void WrapError(SafeAlpmHandle h, Func f) + { + var err = f(); + if (err != 0) + { + throw new Exception(alpm.alpm_errno(h)); + } + } + } } \ No newline at end of file diff --git a/AlpmOld.cs b/AlpmOld.cs index 16cd984..61122ce 100644 --- a/AlpmOld.cs +++ b/AlpmOld.cs @@ -1,10 +1,8 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; -using System.Runtime.Serialization; // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnassignedReadonlyField @@ -12,538 +10,12 @@ using System.Runtime.Serialization; // ReSharper disable InconsistentNaming -namespace Alpm +namespace Foodsoft.Alpm { - - - - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct alpm_pkg_p { private IntPtr ptr; } - - [StructLayout(LayoutKind.Sequential)] - public struct alpm_list_t - { - public IntPtr data; - public unsafe alpm_list_t* prev; - public unsafe alpm_list_t* next; - } - - public interface IAlpmItemsAccessor - { - SafeAlpmHandle Handle { get; } - public IntPtr GetItems(); - public int SetItems(IntPtr list); - public int AddItem(TElement item); - public int RemoveItem(TElement item); - public IntPtr FindItem(TElement item); - public TElement PtrToItem(IntPtr p); - } - - - public class SafeAlpmHandle : SafeHandle - { - private SafeAlpmHandle() : base(IntPtr.Zero, true) { } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - protected override bool ReleaseHandle() - { - return alpm.alpm_release(handle) == 0; - } - - public override bool IsInvalid => handle == IntPtr.Zero; - } - - public sealed class Handle : IDisposable - { - private readonly SafeAlpmHandle _handle; - - public Handle(string root, string dbpath) - { - var err = ErrNo.ERR_OK; - _handle = alpm.alpm_initialize(root, dbpath, ref err); - if (_handle.IsInvalid) - { - throw new Exception(err); - } - } - - public void Dispose() - { - _handle.Dispose(); - } - - private Database DatabaseAccess(IntPtr ptr) - { - if (ptr == IntPtr.Zero) - { - throw new Exception(_handle); - } - - Database db; - RuntimeHelpers.PrepareConstrainedRegions(); - try { } - finally - { - // adding this ref and getting the Database object the handle must not interrupted - var addedRef = false; - _handle.DangerousAddRef(ref addedRef); - if (!addedRef) - throw new ObjectDisposedException(GetType().FullName); - db = new Database(_handle, new DatabaseHandle(ptr)); - } - return db; - } - - public Database LocalDB => DatabaseAccess(alpm.alpm_get_localdb(_handle)); - - public Database RegisterSyncDB(string treename, SigLevel sigLevel) => - DatabaseAccess(alpm.alpm_register_syncdb(_handle, treename, sigLevel)); - - public readonly struct CacheDirsAccessor : IAlpmItemsAccessor - { - public CacheDirsAccessor(SafeAlpmHandle handle) => Handle = handle; - public SafeAlpmHandle Handle { get; } - public IntPtr GetItems() => alpm.alpm_option_get_cachedirs(Handle); - public int SetItems(IntPtr list) => alpm.alpm_option_set_cachedirs(Handle, list); - public int AddItem(string item) => alpm.alpm_option_add_cachedir(Handle, item); - public int RemoveItem(string item) => alpm.alpm_option_remove_cachedir(Handle, item); - public IntPtr FindItem(string item) => alpm.alpm_list_find_str(GetItems(), item); - public string PtrToItem(IntPtr p) => Marshal.PtrToStringUTF8(p)!; - } - - #nullable enable - public struct CollectionWrapper : ICollection where TImpl : IAlpmItemsAccessor, - ICollection - { - private TImpl _impl; - - public CollectionWrapper(TImpl impl) - { - _impl = impl; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return (IEnumerator) GetEnumerator(); - } - - public IEnumerator GetEnumerator() - { - var result = new List(); - var intPtr = _impl.GetItems(); - unsafe - { - for (var list = (alpm_list_t*) intPtr; list != null; list = alpm.alpm_list_next(list)) - { - result.Add(_impl.PtrToItem(list->data)); - } - } - - return result.GetEnumerator(); - } - - - public void Add(TElement item) - { - _impl.AddItem(item); - } - - public void Clear() - { - _impl.SetItems(IntPtr.Zero); - } - - - public bool Contains(TElement item) => - _impl.FindItem(item) != IntPtr.Zero; - - public void CopyTo(TElement[] array, int arrayIndex) - { - var intPtr = _impl.GetItems(); - unsafe - { - for (var list = (alpm_list_t*) intPtr; list != null; list = alpm.alpm_list_next(list)) - { - array[arrayIndex++] = _impl.PtrToItem(list->data); - } - } - } - - public bool Remove(TElement item) - { - var err = _impl.RemoveItem(item); - if (err < 0) - { - throw new Exception(alpm.alpm_errno(_impl.Handle)); - } - - return err == 0; - } - - public int Count => (int) alpm.alpm_list_count(_impl.GetItems()); - public bool IsReadOnly => false; - } - - private readonly struct CacheDirCollection : ICollection - { - private readonly SafeAlpmHandle _handle; - - public CacheDirCollection(SafeAlpmHandle handle) - { - _handle = handle; - } - - public IEnumerator GetEnumerator() - { - var result = new List(); - var intPtr = alpm.alpm_option_get_cachedirs(_handle); - unsafe - { - for (var list = (alpm_list_t*) intPtr; list != null; list = alpm.alpm_list_next(list)) - { - result.Add(Marshal.PtrToStringUTF8(list->data)); - } - } - - return result.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public void Add(string item) - { - var handle = _handle; - Alpm.ErrWrap(_handle, () => alpm.alpm_option_add_cachedir(handle, item)); - } - - public void Clear() - { - var handle = _handle; - Alpm.ErrWrap(_handle, () => alpm.alpm_option_set_cachedirs(handle, IntPtr.Zero)); - } - - public bool Contains(string item) => - alpm.alpm_list_find_str(alpm.alpm_option_get_cachedirs(_handle), item) != IntPtr.Zero; - - public void CopyTo(string[] array, int arrayIndex) - { - var intPtr = alpm.alpm_option_get_cachedirs(_handle); - unsafe - { - for (var list = (alpm_list_t*) intPtr; list != null; list = alpm.alpm_list_next(list)) - { - array[arrayIndex++] = Marshal.PtrToStringUTF8(list->data); - } - } - } - - public bool Remove(string item) - { - var err = alpm.alpm_option_remove_cachedir(_handle, item); - if (err < 0) - { - throw new Exception(alpm.alpm_errno(_handle)); - } - - return err == 0; - } - - public int Count => (int) alpm.alpm_list_count(alpm.alpm_option_get_cachedirs(_handle)); - public bool IsReadOnly => false; - } - - public ICollection CacheDirs - { - get => new CacheDirCollection(_handle); - set - { - foreach (var s in value) - { - Alpm.ErrWrap(_handle, () => alpm.alpm_option_add_cachedir(_handle, s)); - } - } - } - } - - public enum caps - { - ALPM_CAPABILITY_NLS = (1 << 0), - ALPM_CAPABILITY_DOWNLOADER = (1 << 1), - ALPM_CAPABILITY_SIGNATURES = (1 << 2) - } - - [Serializable()] - public class Exception : System.Exception - { - public Exception() { } - public Exception(SafeAlpmHandle handle) : base(Alpm.StrError(alpm.alpm_errno(handle))) { } - public Exception(ErrNo errno) : base(Alpm.StrError(errno)) { } - public Exception(ErrNo errno, System.Exception inner) : base(Alpm.StrError(errno), inner) { } - - protected Exception(SerializationInfo info, - StreamingContext context) : base(info, context) { } - } - - public class DatabaseHandle : SafeHandle - { - private DatabaseHandle() : base(IntPtr.Zero, true) { } - - public DatabaseHandle(IntPtr h) : this() - { - SetHandle(h); - } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - protected override bool ReleaseHandle() - { - return alpm.alpm_db_unregister(handle) == 0; - } - - public override bool IsInvalid => handle == IntPtr.Zero; - } - - public sealed class Database : IDisposable - { - private SafeAlpmHandle _handle; - private DatabaseHandle _db; - - public Database(SafeAlpmHandle handle, DatabaseHandle db) - { - _handle = handle; - _db = db; - } - - private readonly struct ServerCollection : ICollection - { - private readonly SafeAlpmHandle _handle; - private readonly DatabaseHandle _db; - - public ServerCollection(SafeAlpmHandle handle, DatabaseHandle db) - { - _handle = handle; - _db = db; - } - - public IEnumerator GetEnumerator() - { - var result = new List(); - var intPtr = alpm.alpm_db_get_servers(_db); - unsafe - { - for (var list = (alpm_list_t*) intPtr; list != null; list = alpm.alpm_list_next(list)) - { - result.Add(Marshal.PtrToStringUTF8(list->data)); - } - } - - return result.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public void Add(string item) - { - var db = _db; - Alpm.ErrWrap(_handle, () => alpm.alpm_db_add_server(db, item)); - } - - public void Clear() - { - var db = _db; - Alpm.ErrWrap(_handle, () => alpm.alpm_db_set_servers(db, IntPtr.Zero)); - } - - public bool Contains(string item) => - alpm.alpm_list_find_str(alpm.alpm_db_get_servers(_db), item) != IntPtr.Zero; - - public void CopyTo(string[] array, int arrayIndex) - { - var intPtr = alpm.alpm_db_get_servers(_db); - unsafe - { - for (var list = (alpm_list_t*) intPtr; list != null; list = alpm.alpm_list_next(list)) - { - array[arrayIndex++] = Marshal.PtrToStringUTF8(list->data); - } - } - } - - public bool Remove(string item) - { - var err = alpm.alpm_db_remove_server(_db, item); - if (err < 0) - { - throw new Exception(alpm.alpm_errno(_handle)); - } - - return err == 0; - } - - - public int Count => (int) alpm.alpm_list_count(alpm.alpm_db_get_servers(_db)); - public bool IsReadOnly => false; - } - - public ICollection Servers - { - get => new ServerCollection(_handle, _db); - set - { - foreach (var s in value) - { - Alpm.ErrWrap(_handle, () => alpm.alpm_db_add_server(_db, s)); - } - } - } - - public string Name => Marshal.PtrToStringUTF8(alpm.alpm_db_get_name(_db)); - - public SigLevel SigLevel => alpm.alpm_db_get_siglevel(_db); - - public ErrNo Valid => alpm.alpm_db_get_valid(_db) != 0 ? alpm.alpm_errno(_handle) : ErrNo.ERR_OK; - - public bool Update(bool force) - { - var err = alpm.alpm_db_update(force ? 1 : 0, _db); - if (err < 0) - { - throw new Exception(_handle); - } - - return err == 0; - } - - public void Dispose() - { - _db.Dispose(); - RuntimeHelpers.PrepareConstrainedRegions(); - try { } - finally - { - _handle.DangerousRelease(); - } - } - } - - - public static class alpm - { - [DllImport(nameof(alpm))] - public static extern IntPtr alpm_option_get_cachedirs(SafeAlpmHandle handle); - - [DllImport(nameof(alpm))] - public static extern int alpm_option_set_cachedirs(SafeAlpmHandle handle, IntPtr cachedirs); - - [DllImport(nameof(alpm), CharSet = CharSet.Ansi)] - public static extern int alpm_option_add_cachedir(SafeAlpmHandle handle, string cachedir); - - [DllImport(nameof(alpm), CharSet = CharSet.Ansi)] - public static extern int alpm_option_remove_cachedir(SafeAlpmHandle handle, string cachedir); - - [DllImport(nameof(alpm))] - public static extern ErrNo alpm_errno(SafeAlpmHandle handle); - - [DllImport(nameof(alpm))] - public static extern IntPtr alpm_strerror(ErrNo err); - - [DllImport(nameof(alpm))] - public static extern IntPtr alpm_get_localdb(SafeAlpmHandle handle); - - [DllImport(nameof(alpm))] - public static extern unsafe alpm_list_t* alpm_option_get_syncdbs(); - - [DllImport(nameof(alpm), CharSet = CharSet.Ansi)] - public static extern IntPtr alpm_register_syncdb(SafeAlpmHandle handle, string treename, SigLevel sigLevel); - - [DllImport(nameof(alpm))] - public static extern int alpm_db_unregister_all_syncdbs(SafeAlpmHandle handle); - - [DllImport(nameof(alpm))] - public static extern unsafe alpm_list_t* alpm_list_next(alpm_list_t* list); - - [DllImport(nameof(alpm))] - public static extern UIntPtr alpm_list_count(IntPtr list); - - [DllImport(nameof(alpm))] - public static extern void alpm_list_free(IntPtr list); - - [DllImport(nameof(alpm), CharSet = CharSet.Ansi)] - public static extern IntPtr alpm_list_find_str(IntPtr list, string needle); - - [DllImport(nameof(alpm))] - public static extern int alpm_unlock(Handle handle); - - [DllImport(nameof(alpm))] - public static extern IntPtr alpm_version(); - - [DllImport(nameof(alpm))] - public static extern caps alpm_capabilities(); - - [DllImport(nameof(alpm), CharSet = CharSet.Ansi)] - public static extern SafeAlpmHandle alpm_initialize(string root, string dbpath, ref ErrNo err); - - [DllImport(nameof(alpm))] - public static extern int alpm_release(IntPtr handle); - - [DllImport(nameof(alpm))] - public static extern int alpm_db_remove_server(DatabaseHandle db, string url); - - [DllImport(nameof(alpm))] - public static extern int alpm_db_unregister(IntPtr db); - - [DllImport(nameof(alpm))] - public static extern IntPtr alpm_db_get_name(DatabaseHandle db); - - [DllImport(nameof(alpm))] - public static extern SigLevel alpm_db_get_siglevel(DatabaseHandle db); - - [DllImport(nameof(alpm))] - public static extern int alpm_db_get_valid(DatabaseHandle db); - - [DllImport(nameof(alpm))] - public static extern int alpm_db_update(int force, DatabaseHandle db); - - [DllImport(nameof(alpm))] - public static extern IntPtr alpm_db_get_servers(DatabaseHandle db); - - [DllImport(nameof(alpm))] - public static extern int alpm_db_set_servers(DatabaseHandle db, IntPtr list); - - [DllImport(nameof(alpm), CharSet = CharSet.Ansi)] - public static extern int alpm_db_add_server(DatabaseHandle db, string url); - } - - internal static class Alpm - { - - public static string Version() - { - return Marshal.PtrToStringUTF8(alpm.alpm_version()); - } - - public static string StrError(ErrNo err) - { - return Marshal.PtrToStringUTF8(alpm.alpm_strerror(err)); - } - - internal static void ErrWrap(SafeAlpmHandle h, Func f) - { - var err = f(); - if (err != 0) - { - throw new Exception(alpm.alpm_errno(h)); - } - } - } + } \ No newline at end of file diff --git a/Backup.cs b/Backup.cs new file mode 100644 index 0000000..bc16d37 --- /dev/null +++ b/Backup.cs @@ -0,0 +1,8 @@ +namespace Foodsoft.Alpm +{ + public struct Backup + { + private string _name; + private string _hash; + } +} \ No newline at end of file diff --git a/CachePackage.cs b/CachePackage.cs new file mode 100644 index 0000000..ebed529 --- /dev/null +++ b/CachePackage.cs @@ -0,0 +1,19 @@ +namespace Foodsoft.Alpm +{ + public class CachePackage : Package + { + // ReSharper disable once SuggestBaseTypeForParameter + internal CachePackage(SafeCachePackageHandle handle, Database db) : base(handle) + { + DB = db; + } + + public override Database? DB { get; } + + public void SetInstallReason(InstallReason reason) + { + if (alpm.alpm_pkg_set_reason(Handle, reason) != 0) + throw new Exception(alpm.alpm_errno(((SafeCachePackageHandle) Handle).SafeAlpmHandle)); + } + } +} \ No newline at end of file diff --git a/CollectionWrapper`2.cs b/CollectionWrapper`2.cs index f3b5db8..1c7f7a3 100644 --- a/CollectionWrapper`2.cs +++ b/CollectionWrapper`2.cs @@ -1,12 +1,12 @@ using System; using System.Collections; using System.Collections.Generic; -using Foodsoft.Alpm; +using System.Runtime.CompilerServices; namespace Foodsoft.Alpm { - internal struct CollectionWrapper : ICollection where TImpl : struct, - IAlpmItemsAccessor + internal class CollectionWrapper : ICollection where TImpl : struct, + IItemsAccessor { private TImpl _impl; @@ -15,26 +15,31 @@ namespace Foodsoft.Alpm _impl = impl; } - IEnumerator IEnumerable.GetEnumerator() + private static unsafe IntPtr ListNext(IntPtr list) => ((alpm_list_t*) list)->next; + private static unsafe IntPtr ListData(IntPtr list) => ((alpm_list_t*) list)->data; + public IEnumerator GetEnumerator() { - return (IEnumerator) GetEnumerator(); - } + var safeAlpmHandle = _impl.SafeAlpmHandle; + var release = false; - public IEnumerator GetEnumerator() - { - var result = new List(); - var intPtr = _impl.GetItems(); - unsafe + RuntimeHelpers.PrepareConstrainedRegions(); + try { - for (var list = (alpm_list_t*) intPtr; list != null; list = alpm.alpm_list_next(list)) + safeAlpmHandle.DangerousAddRef(ref release); + if (!release) throw new ObjectDisposedException(_impl.GetType().FullName); + for (var list = _impl.GetItems(); list != IntPtr.Zero; list = ListNext(list)) { - result.Add(_impl.PtrToItem(list->data)); + yield return _impl.PtrToItem(ListData(list)); } } - - return result.GetEnumerator(); + finally + { + if (release) + safeAlpmHandle.DangerousRelease(); + } } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public void Add(TElement item) { @@ -47,19 +52,45 @@ namespace Foodsoft.Alpm } - public bool Contains(TElement item) => - _impl.FindItem(item) != IntPtr.Zero; + public bool Contains(TElement item) + { + var safeAlpmHandle = _impl.SafeAlpmHandle; + var release = false; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + safeAlpmHandle.DangerousAddRef(ref release); + return _impl.FindItem(item) != IntPtr.Zero; + } + finally + { + if (release) + safeAlpmHandle.DangerousRelease(); + } + } public void CopyTo(TElement[] array, int arrayIndex) { - var intPtr = _impl.GetItems(); - unsafe + var safeAlpmHandle = _impl.SafeAlpmHandle; + var release = false; + RuntimeHelpers.PrepareConstrainedRegions(); + try { - for (var list = (alpm_list_t*) intPtr; list != null; list = alpm.alpm_list_next(list)) + safeAlpmHandle.DangerousAddRef(ref release); + var intPtr = _impl.GetItems(); + unsafe { - array[arrayIndex++] = _impl.PtrToItem(list->data); + for (var list = (alpm_list_t*) intPtr; list != null; list = alpm.alpm_list_next(list)) + { + array[arrayIndex++] = _impl.PtrToItem(list->data); + } } } + finally + { + if (release) + safeAlpmHandle.DangerousRelease(); + } } public bool Remove(TElement item) @@ -73,7 +104,26 @@ namespace Foodsoft.Alpm return err == 0; } - public int Count => (int) alpm.alpm_list_count(_impl.GetItems()); + public int Count + { + get + { + var safeAlpmHandle = _impl.SafeAlpmHandle; + var release = false; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + safeAlpmHandle.DangerousAddRef(ref release); + return (int) alpm.alpm_list_count(_impl.GetItems()); + } + finally + { + if (release) + safeAlpmHandle.DangerousRelease(); + } + } + } + public bool IsReadOnly => false; } } \ No newline at end of file diff --git a/Database.cs b/Database.cs index 9b781fd..a494cc2 100644 --- a/Database.cs +++ b/Database.cs @@ -1,4 +1,95 @@ -$HEADER$namespace $NAMESPACE$ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Foodsoft.Alpm { - public class $CLASS$ {$END$} + public sealed class Database : IDisposable + { + private readonly SafeDatabaseHandle _handle; + + internal Database(SafeDatabaseHandle handle) => _handle = handle; + + private readonly struct ServersAccessor : IItemsAccessor + { + private readonly SafeDatabaseHandle _handle; + + internal ServersAccessor(SafeDatabaseHandle handle) + { + _handle = handle; + } + + public SafeAlpmHandle SafeAlpmHandle => _handle.SafeAlpmHandle; + public IntPtr GetItems() => alpm.alpm_db_get_servers(_handle); + public int SetItems(IntPtr list) => alpm.alpm_db_set_servers(_handle, list); + public int AddItem(string item) => alpm.alpm_db_add_server(_handle, item); + public int RemoveItem(string item) => alpm.alpm_db_remove_server(_handle, item); + public IntPtr FindItem(string item) => alpm.alpm_list_find_str(GetItems(), item); + public string PtrToItem(IntPtr p) => Marshal.PtrToStringUTF8(p)!; + } + + public ICollection Servers + { + get => new CollectionWrapper(new ServersAccessor(_handle)); + set + { + foreach (var s in value) + { + API.WrapError(_handle.SafeAlpmHandle, () => alpm.alpm_db_add_server(_handle, s)); + } + } + } + + + public string Name => alpm.alpm_db_get_name(_handle); + + public SigLevel SigLevel => alpm.alpm_db_get_siglevel(_handle); + + public ErrNo Valid => + alpm.alpm_db_get_valid(_handle) != 0 ? alpm.alpm_errno(_handle.SafeAlpmHandle) : ErrNo.ERR_OK; + + public bool Update(bool force) + { + var err = alpm.alpm_db_update(force ? 1 : 0, _handle); + if (err < 0) + { + throw new Exception(_handle.SafeAlpmHandle); + } + + return err == 0; + } + + public Package? GetPackage(string name) + { + var pkgPtr = alpm.alpm_pkg_get_pkg(_handle, name); + if (pkgPtr == IntPtr.Zero) + { + var err = alpm.alpm_errno(_handle.SafeAlpmHandle); + if (err == ErrNo.ERR_PKG_NOT_FOUND) + return null; + throw new Exception(err); + } + return new CachePackage(new SafeCachePackageHandle(pkgPtr, _handle), this); + } + + private readonly struct PkgCacheAccessor : IReadOnlyItemsAccessor + { + private readonly SafeDatabaseHandle _handle; + public SafeAlpmHandle SafeAlpmHandle => _handle.SafeAlpmHandle; + public IntPtr GetItems() + { + throw new NotImplementedException(); + } + + public Package PtrToItem(IntPtr p) + { + throw new NotImplementedException(); + } + } + + public void Dispose() + { + _handle.Dispose(); + } + } } \ No newline at end of file diff --git a/Depend.cs b/Depend.cs new file mode 100644 index 0000000..c80f05a --- /dev/null +++ b/Depend.cs @@ -0,0 +1,34 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Foodsoft.Alpm +{ + + + [StructLayout(LayoutKind.Sequential)] + [SuppressMessage("ReSharper", "ConvertToConstant.Local")] + public class Depend + { + public enum ModType + { + Any = 1, + Equal, + GreaterThanOrEqual, + LessThanOrEqual, + GreaterThan, + LessThan + } + + [field: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public string Name { get; } = null!; + + [field: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public string Version { get; } = null!; + + [field: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public string Description { get; } = null!; + + public ulong NameHash { get; } = 0; + public ModType Mod { get; } = ModType.Any; + } +} \ No newline at end of file diff --git a/DotNetAlpm.csproj b/DotNetAlpm.csproj index 71399ab..928485e 100644 --- a/DotNetAlpm.csproj +++ b/DotNetAlpm.csproj @@ -5,7 +5,7 @@ netcoreapp3.1 true enable - nalpm + Foodsoft.Alpm diff --git a/ErrNo.cs b/ErrNo.cs index 22b9f06..9be718b 100644 --- a/ErrNo.cs +++ b/ErrNo.cs @@ -1,4 +1,81 @@ -$HEADER$namespace $NAMESPACE$ +namespace Foodsoft.Alpm { - public enum $ENUM$ {$END$} + public enum ErrNo + { + ERR_OK = 0, + ERR_MEMORY, + ERR_SYSTEM, + ERR_BADPERMS, + ERR_NOT_A_FILE, + ERR_NOT_A_DIR, + ERR_WRONG_ARGS, + ERR_DISK_SPACE, + + /* Interface */ + ERR_HANDLE_NULL, + ERR_HANDLE_NOT_NULL, + ERR_HANDLE_LOCK, + + /* Databases */ + ERR_DB_OPEN, + ERR_DB_CREATE, + ERR_DB_NULL, + ERR_DB_NOT_NULL, + ERR_DB_NOT_FOUND, + ERR_DB_INVALID, + ERR_DB_INVALID_SIG, + ERR_DB_VERSION, + ERR_DB_WRITE, + ERR_DB_REMOVE, + + /* Servers */ + ERR_SERVER_BAD_URL, + ERR_SERVER_NONE, + + /* Transactions */ + ERR_TRANS_NOT_NULL, + ERR_TRANS_NULL, + ERR_TRANS_DUP_TARGET, + ERR_TRANS_NOT_INITIALIZED, + ERR_TRANS_NOT_PREPARED, + ERR_TRANS_ABORT, + ERR_TRANS_TYPE, + ERR_TRANS_NOT_LOCKED, + ERR_TRANS_HOOK_FAILED, + + /* Packages */ + ERR_PKG_NOT_FOUND, + ERR_PKG_IGNORED, + ERR_PKG_INVALID, + ERR_PKG_INVALID_CHECKSUM, + ERR_PKG_INVALID_SIG, + ERR_PKG_MISSING_SIG, + ERR_PKG_OPEN, + ERR_PKG_CANT_REMOVE, + ERR_PKG_INVALID_NAME, + ERR_PKG_INVALID_ARCH, + ERR_PKG_REPO_NOT_FOUND, + + /* Signatures */ + ERR_SIG_MISSING, + ERR_SIG_INVALID, + + /* Dependencies */ + ERR_UNSATISFIED_DEPS, + ERR_CONFLICTING_DEPS, + ERR_FILE_CONFLICTS, + + /* Misc */ + ERR_RETRIEVE, + ERR_INVALID_REGEX, + + /* External library errors */ + ERR_LIBARCHIVE, + ERR_LIBCURL, + ERR_EXTERNAL_DOWNLOAD, + ERR_GPGME, + + /* Missing compile-time features */ + ERR_MISSING_CAPABILITY_SIGNATURES + } } \ No newline at end of file diff --git a/Exception.cs b/Exception.cs index 9b781fd..daa1325 100644 --- a/Exception.cs +++ b/Exception.cs @@ -1,4 +1,18 @@ -$HEADER$namespace $NAMESPACE$ +using System; +using System.Runtime.Serialization; +using Foodsoft.Alpm; + +namespace Foodsoft.Alpm { - public class $CLASS$ {$END$} + [Serializable()] + public class Exception : System.Exception + { + public Exception() { } + internal Exception(SafeAlpmHandle handle) : base(alpm.alpm_strerror(alpm.alpm_errno(handle))) { } + public Exception(ErrNo errno) : base(alpm.alpm_strerror(errno)) { } + public Exception(ErrNo errno, System.Exception inner) : base(alpm.alpm_strerror(errno), inner) { } + + protected Exception(SerializationInfo info, + StreamingContext context) : base(info, context) { } + } } \ No newline at end of file diff --git a/File.cs b/File.cs new file mode 100644 index 0000000..77d3bb8 --- /dev/null +++ b/File.cs @@ -0,0 +1,15 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Foodsoft.Alpm +{ + [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")] + public readonly struct File + { + [field: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public string Name { get; } + + public long Size { get; } + public uint Mode { get; } + } +} \ No newline at end of file diff --git a/FileList.cs b/FileList.cs new file mode 100644 index 0000000..07b8260 --- /dev/null +++ b/FileList.cs @@ -0,0 +1,23 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Foodsoft.Alpm +{ + public class FileList : IReadOnlyList + { + + public IEnumerator GetEnumerator() + { + throw new System.NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public int Count { get; } + + public File this[int index] => throw new System.NotImplementedException(); + } +} \ No newline at end of file diff --git a/Folder.DotSettings b/Folder.DotSettings index 1639e72..1c8e73a 100644 --- a/Folder.DotSettings +++ b/Folder.DotSettings @@ -1,6 +1,10 @@  True + True + True + True True + True True True True diff --git a/Handle.cs b/Handle.cs index 9b781fd..ded90b4 100644 --- a/Handle.cs +++ b/Handle.cs @@ -1,4 +1,75 @@ -$HEADER$namespace $NAMESPACE$ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Foodsoft.Alpm { - public class $CLASS$ {$END$} + public sealed class Handle : IDisposable + { + private readonly SafeAlpmHandle _handle; + + public Handle(string root, string dbpath) + { + _handle = alpm.alpm_initialize(root, dbpath, out var err); + if (_handle.IsInvalid) + { + throw new Exception(err); + } + } + + public void Dispose() + { + _handle.Dispose(); + } + + private Database ToDatabase(IntPtr ptr) + { + // It's ok that the database pointer is kept outside atomic section, because the resource is actually + // managed by the alpm handle. + if (ptr == IntPtr.Zero) + { + throw new Exception(_handle); + } + + return new Database(new SafeDatabaseHandle(ptr, _handle)); + } + + public Database LocalDB => ToDatabase(alpm.alpm_get_localdb(_handle)); + + public Database RegisterSyncDB(string treename, SigLevel sigLevel) => + ToDatabase(alpm.alpm_register_syncdb(_handle, treename, sigLevel)); + + private readonly struct CacheDirsAccessor : IItemsAccessor + { + internal CacheDirsAccessor(SafeAlpmHandle safeAlpmHandle) + { + SafeAlpmHandle = safeAlpmHandle; + } + + public SafeAlpmHandle SafeAlpmHandle { get; } + public IntPtr GetItems() => alpm.alpm_option_get_cachedirs(SafeAlpmHandle); + public int SetItems(IntPtr list) => alpm.alpm_option_set_cachedirs(SafeAlpmHandle, list); + public int AddItem(string item) => alpm.alpm_option_add_cachedir(SafeAlpmHandle, item); + public int RemoveItem(string item) => alpm.alpm_option_remove_cachedir(SafeAlpmHandle, item); + public IntPtr FindItem(string item) => alpm.alpm_list_find_str(GetItems(), item); + public string PtrToItem(IntPtr p) => Marshal.PtrToStringUTF8(p)!; + } + + public ICollection CacheDirs + { + get => new CollectionWrapper(new CacheDirsAccessor(_handle)); + set + { + foreach (var s in value) + { + API.WrapError(_handle, () => alpm.alpm_option_add_cachedir(_handle, s)); + } + } + } + + public bool ShouldIgnorePackage(Package pkg) + { + return alpm.alpm_pkg_should_ignore(_handle, pkg.Handle) == 0; + } + } } \ No newline at end of file diff --git a/IAlpmItemsAccessor`1.cs b/IAlpmItemsAccessor`1.cs index 5b69a12..3cad288 100644 --- a/IAlpmItemsAccessor`1.cs +++ b/IAlpmItemsAccessor`1.cs @@ -2,7 +2,7 @@ using System; namespace Foodsoft.Alpm { - internal interface IAlpmItemsAccessor + internal interface IItemsAccessor { SafeAlpmHandle SafeAlpmHandle { get; } public IntPtr GetItems(); @@ -12,5 +12,4 @@ namespace Foodsoft.Alpm public IntPtr FindItem(TElement item); public TElement PtrToItem(IntPtr p); } - } \ No newline at end of file diff --git a/IAlpmReadOnlyItemsAccessor`1.cs b/IAlpmReadOnlyItemsAccessor`1.cs deleted file mode 100644 index 34a14a9..0000000 --- a/IAlpmReadOnlyItemsAccessor`1.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Foodsoft.Alpm -{ - public class IAlpmReadOnlyItemsAccessor - { - - } -} \ No newline at end of file diff --git a/IPackageData.cs b/IPackageData.cs new file mode 100644 index 0000000..9c4f62c --- /dev/null +++ b/IPackageData.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; + +namespace Foodsoft.Alpm +{ + public enum PackageOrigin + { + File = 1, + LocalDB, + SyncDB + } + + public enum InstallReason + { + Explicit = 0, + Depend = 1 + } + + [Flags] + public enum ValidationType + { + Unknown = 0, + None = (1 << 0), + MD5Sum = (1 << 1), + SHA256Sum = (1 << 2), + Signature = (1 << 3) + } + + public interface IPackageData + { + public string Filename { get; } + public string Base { get; } + public string Name { get; } + public string Version { get; } + public PackageOrigin Origin { get; } + public string Description { get; } + public string Url { get; } + public DateTimeOffset BuildDate { get; } + public DateTimeOffset InstallDate { get; } + public string Packager { get; } + public string MD5Sum { get; } + public string SHA256Sum { get; } + public string Arch { get; } + public long Size { get; } + public long InstalledSize { get; } + public InstallReason InstallReason { get; } + public IReadOnlyCollection Licenses { get; } + public IReadOnlyCollection Groups { get; } + public IReadOnlyCollection Depends { get; } + public IReadOnlyCollection OptDepends { get; } + public IReadOnlyCollection CheckDepends { get; } + public IReadOnlyCollection MakeDepends { get; } + public IReadOnlyCollection Conflicts { get; } + public IReadOnlyCollection Provides { get; } + public IReadOnlyCollection Replaces { get; } + public IReadOnlyList FileList { get; } + public IReadOnlyList Backup { get; } + public string Base64Signature { get; } + public ValidationType Validation { get; } + public bool HasScriptlet { get; } + public long DownloadSize { get; } + public Database? DB { get; } + } +} \ No newline at end of file diff --git a/IReadOnlyItemsAccessor`1.cs b/IReadOnlyItemsAccessor`1.cs new file mode 100644 index 0000000..38744e3 --- /dev/null +++ b/IReadOnlyItemsAccessor`1.cs @@ -0,0 +1,11 @@ +using System; + +namespace Foodsoft.Alpm +{ + internal interface IReadOnlyItemsAccessor + { + SafeAlpmHandle SafeAlpmHandle { get; } + public IntPtr GetItems(); + public TElement PtrToItem(IntPtr p); + } +} \ No newline at end of file diff --git a/Junk.cs b/Junk.cs index 0f48790..1946f95 100644 --- a/Junk.cs +++ b/Junk.cs @@ -1,7 +1,345 @@ -namespace Alpm -{ - public class Junk +using System; +using System.Runtime.InteropServices; + +namespace Foodsoft.Alpm +{ + + /* + * Hooks + */ + + enum alpm_hook_when_t { - + HOOK_PRE_TRANSACTION = 1, + HOOK_POST_TRANSACTION } + +/* + * Logging facilities + */ + + /** Logging Levels */ + enum _alpm_loglevel_t + { + LOG_ERROR = 1, + LOG_WARNING = (1 << 1), + LOG_DEBUG = (1 << 2), + LOG_FUNCTION = (1 << 3) + } + + + /** Location a package object was loaded from. */ + enum alpm_pkgfrom_t + { + PKG_FROM_FILE = 1, + PKG_FROM_LOCALDB, + PKG_FROM_SYNCDB + } + + /** Method used to validate a package. */ + enum alpm_pkgvalidation_t + { + PKG_VALIDATION_UNKNOWN = 0, + PKG_VALIDATION_NONE = (1 << 0), + PKG_VALIDATION_MD5SUM = (1 << 1), + PKG_VALIDATION_SHA256SUM = (1 << 2), + PKG_VALIDATION_SIGNATURE = (1 << 3) + } + + /** Types of version constraints in dependency specs. */ + public enum alpm_depmod_t + { + /** No version constraint */ + DEP_MOD_ANY = 1, + + /** Test version equality (package=x.y.z) */ + DEP_MOD_EQ, + + /** Test for at least a version (package>=x.y.z) */ + DEP_MOD_GE, + + /** Test for at most a version (package<=x.y.z) */ + DEP_MOD_LE, + + /** Test for greater than some version (package>x.y.z) */ + DEP_MOD_GT, + + /** Test for less than some version (package(ptr); + } + } + + /** Missing dependency */ + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct alpm_depmissing_t + { + public string target; + + public AlpmDependPtr depend; + + /* this is used only in the case of a remove dependency error */ + public string causingpkg; + } + + [StructLayout(LayoutKind.Sequential)] + /** Conflict */ + public struct alpm_conflict_t + { + public ulong package1_hash; + public ulong package2_hash; + public string package1; + public string package2; + public AlpmDependPtr reason; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + /** File conflict */ + struct alpm_fileconflict_t + { + public string target; + public alpm_fileconflicttype_t type; + public string file; + public string ctarget; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + /** Package group */ + public readonly struct alpm_group_t + { + /** group name */ + public readonly string name; + + /** list of alpm_pkg_t packages */ + private readonly unsafe alpm_list_t* packages; + } + + /* TODO: alpm_pkg_get_files - need to wrap */ + /** File in a package */ + public readonly struct alpm_file_t + { + public readonly string name; + public readonly UIntPtr size; + public readonly uint mode; + } + + public enum caps + { + ALPM_CAPABILITY_NLS = (1 << 0), + ALPM_CAPABILITY_DOWNLOADER = (1 << 1), + ALPM_CAPABILITY_SIGNATURES = (1 << 2) + } + + /** Package filelist container */ + public struct _alpm_filelist_t + { + public UIntPtr count; + private IntPtr files; + + /* FIXME: This is broken as shit */ + public alpm_file_t[] Unmarshal() + { + var iCount = (int) this.count; + var byteCount = Marshal.SizeOf() * iCount; + var byteBuffer = new byte[byteCount]; + Marshal.Copy(files, byteBuffer, 0, byteBuffer.Length); + var result = new alpm_file_t[iCount]; + + Buffer.BlockCopy(byteBuffer, 0, result, 0, byteBuffer.Length); + return result; + } + } + + /** Local package or package file backup entry */ + public struct _alpm_backup_t + { + string name; + string hash; + } + + public class alpm_pgpkey_t + { + public byte[] data; + public string fingerprint; + public string uid; + public string name; + public string email; + public long created; + public long expires; + public uint revoked; + public char pubkey_algo; + } + + public class PgpKeyInMarshaler : ICustomMarshaler + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + private class NativePgpKey + { + public IntPtr data; + public string fingerprint; + public string uid; + public string name; + public string email; + public long created; + public long expires; + public uint length; + public uint revoked; + public char pubkey_algo; + } + + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() + { + throw new NotImplementedException(); + } + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + throw new NotImplementedException(); + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + var raw = Marshal.PtrToStructure(pNativeData); + var managed = new alpm_pgpkey_t() + { + data = new byte[raw.length], + fingerprint = raw.fingerprint, + uid = raw.uid, + name = raw.name, + email = raw.email, + created = raw.created, + expires = raw.expires, + revoked = raw.revoked, + pubkey_algo = raw.pubkey_algo + }; + Marshal.Copy(raw.data, managed.data, 0, (int) raw.length); + return managed; + } + } + + /** + * Signature result. Contains the key, status, and validity of a given + * signature. + */ + public readonly struct alpm_sigresult_t + { + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PgpKeyInMarshaler))] + readonly alpm_pgpkey_t key; + + readonly alpm_sigstatus_t status; + readonly alpm_sigvalidity_t validity; + } + + /** + * Signature list. Contains the number of signatures found and a pointer to an + * array of results. The array is of size count. + */ + public class SigListInMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() + { + throw new NotImplementedException(); + } + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + throw new NotImplementedException(); + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + UIntPtr count; + IntPtr data; + unsafe + { + count = *(UIntPtr*) pNativeData; + data = *(IntPtr*) (pNativeData + sizeof(UIntPtr)); + } + + var iCount = (int) count; + var result = new alpm_sigresult_t[iCount]; + // NOTE: I expect this to fail cuz i didn't implement the above GetNativeDataSize + for (var i = 0; i < iCount; ++i, data += Marshal.SizeOf()) + { + result[i] = Marshal.PtrToStructure(data); + } + + return result; + } + } + + } \ No newline at end of file diff --git a/Package.cs b/Package.cs new file mode 100644 index 0000000..00e208d --- /dev/null +++ b/Package.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; + +namespace Foodsoft.Alpm +{ + public class Package : IPackageData + { + internal SafePackageHandle Handle { get; } + + internal Package(SafePackageHandle handle) + { + Handle = handle; + } + + public string Filename => alpm.alpm_pkg_get_filename(Handle); + public string Base => alpm.alpm_pkg_get_base(Handle); + public string Name => alpm.alpm_pkg_get_name(Handle); + public string Version => alpm.alpm_pkg_get_version(Handle); + public PackageOrigin Origin => alpm.alpm_pkg_get_origin(Handle); + public string Description => alpm.alpm_pkg_get_desc(Handle); + public string Url => alpm.alpm_pkg_get_url(Handle); + + public DateTimeOffset BuildDate => DateTimeOffset.FromUnixTimeSeconds(alpm.alpm_pkg_get_builddate(Handle)); + public DateTimeOffset InstallDate => DateTimeOffset.FromUnixTimeSeconds(alpm.alpm_pkg_get_installdate(Handle)); + public string Packager => alpm.alpm_pkg_get_packager(Handle); + public string MD5Sum => alpm.alpm_pkg_get_md5sum(Handle); + public string SHA256Sum => alpm.alpm_pkg_get_sha256sum(Handle); + public string Arch => alpm.alpm_pkg_get_arch(Handle); + public long Size => alpm.alpm_pkg_get_size(Handle); + public long InstalledSize => alpm.alpm_pkg_get_isize(Handle); + public InstallReason InstallReason => alpm.alpm_pkg_get_reason(Handle); + + public IReadOnlyCollection Licenses { get; } + public IReadOnlyCollection Groups { get; } + public IReadOnlyCollection Depends { get; } + public IReadOnlyCollection OptDepends { get; } + public IReadOnlyCollection CheckDepends { get; } + public IReadOnlyCollection MakeDepends { get; } + public IReadOnlyCollection Conflicts { get; } + public IReadOnlyCollection Provides { get; } + public IReadOnlyCollection Replaces { get; } + public IReadOnlyList FileList { get; } + public IReadOnlyList Backup { get; } + public string Base64Signature { get; } + public ValidationType Validation { get; } + public bool HasScriptlet { get; } + public long DownloadSize { get; } + public bool CheckMD5Sum() + { + throw new NotImplementedException(); + } + + public virtual Database? DB => null; + + public IEnumerator ComputeRequiredBy() + { + throw new NotImplementedException(); + } + + public IEnumerator ComputeOptionalFor() + { + throw new NotImplementedException(); + } + + + } + +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index e3df67f..63e596f 100644 --- a/Program.cs +++ b/Program.cs @@ -1,24 +1,27 @@ using System; using System.Collections.Generic; -using System.Data.Common; +using Foodsoft.Alpm; -namespace nalpm +namespace Foodsoft.Alpm { class Program { static void Main(string[] args) { - errno_t err = errno_t.ERR_OK; + ErrNo err = ErrNo.ERR_OK; using var h = new Handle("/", "/home/luebsj/db"); using var db = h.RegisterSyncDB("jkl-repo", SigLevel.SIG_PACKAGE_OPTIONAL | SigLevel.SIG_USE_DEFAULT); - db.Servers = new List {"http://a.b.c", "http://d.e.f", "http://arch.johnluebs.com/x86_64"}; - var a = new string[3]; - db.Servers.CopyTo(a, 0); - Console.WriteLine("Hello World! {0} {1} {2}", db.Name, Alpm.Version(), - a[0]); - + db.Servers = new List {"http://a.b.c", "http://d.e.f", "http://arch.johnluebs.com/x86_64"}; + foreach (var a in db.Servers) + { + Console.WriteLine("Hello {0}", a); + } + var b = new string[3]; + db.Servers.CopyTo(b, 0); + Console.WriteLine("Hello World! {0} {1} {2}", db.Name, API.Version, + b[0]); } } } diff --git a/ReadOnlyCollectionWrapper`2.cs b/ReadOnlyCollectionWrapper`2.cs index 9b781fd..1d08dde 100644 --- a/ReadOnlyCollectionWrapper`2.cs +++ b/ReadOnlyCollectionWrapper`2.cs @@ -1,4 +1,47 @@ -$HEADER$namespace $NAMESPACE$ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Foodsoft.Alpm { - public class $CLASS$ {$END$} + internal class ReadOnlyCollectionWrapper : IReadOnlyCollection where TImpl : struct, + IReadOnlyItemsAccessor + { + private TImpl _impl; + private SafeAlpmHandle SafeAlpmHandle => _impl.SafeAlpmHandle; + + public ReadOnlyCollectionWrapper(TImpl impl) + { + _impl = impl; + } + + private static unsafe IntPtr ListNext(IntPtr list) => ((alpm_list_t*) list)->next; + private static unsafe IntPtr ListData(IntPtr list) => ((alpm_list_t*) list)->data; + public IEnumerator GetEnumerator() + { + var safeAlpmHandle = _impl.SafeAlpmHandle; + var release = false; + + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + safeAlpmHandle.DangerousAddRef(ref release); + if (!release) throw new ObjectDisposedException(_impl.GetType().FullName); + for (var list = _impl.GetItems(); list != IntPtr.Zero; list = ListNext(list)) + { + yield return _impl.PtrToItem(ListData(list)); + } + } + finally + { + if (release) + safeAlpmHandle.DangerousRelease(); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public int Count => (int) alpm.alpm_list_count(_impl.GetItems()); + } } \ No newline at end of file diff --git a/SafeAlpmHandle.cs b/SafeAlpmHandle.cs index 9b781fd..2d4b59b 100644 --- a/SafeAlpmHandle.cs +++ b/SafeAlpmHandle.cs @@ -1,4 +1,21 @@ -$HEADER$namespace $NAMESPACE$ +using System; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using Foodsoft.Alpm; + +namespace Foodsoft.Alpm { - public class $CLASS$ {$END$} + internal sealed class SafeAlpmHandle : SafeHandle + { + private SafeAlpmHandle() : base(IntPtr.Zero, true) { } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), PrePrepareMethod] + protected override bool ReleaseHandle() => alpm.alpm_release(handle) == 0; + + public override bool IsInvalid + { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), PrePrepareMethod] + get => handle == IntPtr.Zero; + } + } } \ No newline at end of file diff --git a/SafeCachePackageHandle.cs b/SafeCachePackageHandle.cs new file mode 100644 index 0000000..c47a030 --- /dev/null +++ b/SafeCachePackageHandle.cs @@ -0,0 +1,52 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; + +namespace Foodsoft.Alpm +{ + internal sealed class SafeCachePackageHandle : SafePackageHandle + { + internal SafeDatabaseHandle SafeDatabaseHandle + { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [PrePrepareMethod] + get; + } = null!; + + internal SafeAlpmHandle SafeAlpmHandle => SafeDatabaseHandle.SafeAlpmHandle; + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + [PrePrepareMethod] + internal SafeCachePackageHandle(IntPtr pkgPtr, SafeDatabaseHandle dbHandle) + : base() + { + var success = false; + RuntimeHelpers.PrepareConstrainedRegions(); + try { } + finally + { + dbHandle.DangerousAddRef(ref success); + if (success) + { + handle = pkgPtr; + SafeDatabaseHandle = dbHandle; + } + } + + if (!success) throw new ObjectDisposedException(GetType().FullName); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), PrePrepareMethod] + protected override bool ReleaseHandle() + { + SafeDatabaseHandle.DangerousRelease(); + return true; + } + + public override bool IsInvalid + { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), PrePrepareMethod] + get => handle == IntPtr.Zero; + } + } +} \ No newline at end of file diff --git a/SafeDatabaseHandle.cs b/SafeDatabaseHandle.cs index 9b781fd..a1b249a 100644 --- a/SafeDatabaseHandle.cs +++ b/SafeDatabaseHandle.cs @@ -1,4 +1,53 @@ -$HEADER$namespace $NAMESPACE$ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; + +namespace Foodsoft.Alpm { - public class $CLASS$ {$END$} + internal sealed class SafeDatabaseHandle : SafeHandle + { + internal SafeAlpmHandle SafeAlpmHandle + { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [PrePrepareMethod] + get; + } = null!; // we always throw in the constructor on failure + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + [PrePrepareMethod] + internal SafeDatabaseHandle(IntPtr dbPtr, SafeAlpmHandle safeAlpmHandle) + : base(IntPtr.Zero, true) + { + var success = false; + + RuntimeHelpers.PrepareConstrainedRegions(); + try { } + finally + { + safeAlpmHandle.DangerousAddRef(ref success); + if (success) + { + SafeAlpmHandle = safeAlpmHandle; + handle = dbPtr; + } + } + + if (!success) throw new ObjectDisposedException(GetType().FullName); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), PrePrepareMethod] + protected override bool ReleaseHandle() + { + var ret = alpm.alpm_db_unregister(handle) == 0; + SafeAlpmHandle.DangerousRelease(); + return ret; + } + + public override bool IsInvalid + { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), PrePrepareMethod] + get => handle == IntPtr.Zero; + } + } } \ No newline at end of file diff --git a/SafeListHandle.cs b/SafeListHandle.cs index c5ec06a..911f1c5 100644 --- a/SafeListHandle.cs +++ b/SafeListHandle.cs @@ -1,46 +1,49 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; namespace Foodsoft.Alpm { - internal struct ListRef : CriticalFinalIDisposable + internal sealed class SafeListHandle : SafeHandle { public SafeAlpmHandle SafeAlpmHandle { [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [PrePrepareMethod] get; - } + } = null!; - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), PrePrepareMethod] - public ListRef(SafeAlpmHandle handle) + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + [PrePrepareMethod] + internal SafeListHandle(SafeAlpmHandle safeAlpmHandle) + : base(IntPtr.Zero, true) { - SafeAlpmHandle = null!; + var success = false; RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { - SafeAlpmHandle = handle; - var success = false; - handle.DangerousAddRef(ref success); + safeAlpmHandle.DangerousAddRef(ref success); if (success) - SafeAlpmHandle = handle; - else - { - SafeAlpmHandle = null!; - throw new ObjectDisposedException(GetType().FullName); - } + SafeAlpmHandle = safeAlpmHandle; } + + if (!success) + throw new ObjectDisposedException(GetType().FullName); } - ~ListRef() + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), PrePrepareMethod] + protected override bool ReleaseHandle() { - + SafeAlpmHandle.DangerousRelease(); + return true; } - - public void Dispose() + + public override bool IsInvalid { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), PrePrepareMethod] + get => SafeAlpmHandle.IsInvalid; } } } \ No newline at end of file diff --git a/SafePackageHandle.cs b/SafePackageHandle.cs new file mode 100644 index 0000000..33f1cc9 --- /dev/null +++ b/SafePackageHandle.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; + +namespace Foodsoft.Alpm +{ + internal class SafePackageHandle : SafeHandle + { + protected SafePackageHandle() : base(IntPtr.Zero, true) { } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), PrePrepareMethod] + protected override bool ReleaseHandle() => alpm.alpm_pkg_free(handle) == 0; + + public override bool IsInvalid + { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), PrePrepareMethod] + get => handle == IntPtr.Zero; + } + } +} \ No newline at end of file diff --git a/UTF8InMarshaler.cs b/UTF8InMarshaler.cs index 3653351..48dc3e0 100644 --- a/UTF8InMarshaler.cs +++ b/UTF8InMarshaler.cs @@ -4,16 +4,10 @@ using System.Text; namespace Foodsoft.Alpm { - internal readonly struct UTF8Marshaler : ICustomMarshaler + internal readonly struct UTF8InMarshaler : ICustomMarshaler { - private static readonly UTF8Marshaler _instance = default; - - // ReSharper disable once UnusedMember.Local - // ReSharper disable once UnusedParameter.Local - private static ICustomMarshaler GetInstance(string cookie) - { - return _instance; - } + private static readonly UTF8InMarshaler _instance = default; + public void CleanUpManagedData(object managedObj) { } @@ -35,13 +29,14 @@ namespace Foodsoft.Alpm var nb = Encoding.UTF8.GetMaxByteCount(s.Length); var pMem = Marshal.AllocHGlobal(nb + 1); - var pbMem = (byte*) pMem; + var pbMem = (byte*) pMem; int nbWritten; fixed (char* firstChar = s) { nbWritten = Encoding.UTF8.GetBytes(firstChar, s.Length, pbMem, nb); } + pbMem[nbWritten] = 0; return pMem; } @@ -50,5 +45,12 @@ namespace Foodsoft.Alpm { return Marshal.PtrToStringUTF8(pNativeData)!; } + + // ReSharper disable once UnusedMember.Local + // ReSharper disable once UnusedParameter.Local + private static ICustomMarshaler GetInstance(string cookie) + { + return _instance; + } } } \ No newline at end of file diff --git a/UTF8OutMarshaler.cs b/UTF8OutMarshaler.cs index 9b781fd..1ecbf60 100644 --- a/UTF8OutMarshaler.cs +++ b/UTF8OutMarshaler.cs @@ -1,4 +1,35 @@ -$HEADER$namespace $NAMESPACE$ +namespace Foodsoft.Alpm { - public class $CLASS$ {$END$} + using System; + using System.Runtime.InteropServices; + using System.Text; + + + internal readonly struct UTF8OutMarshaler : ICustomMarshaler + { + private static readonly UTF8OutMarshaler _instance = default; + + // ReSharper disable once UnusedMember.Local + // ReSharper disable once UnusedParameter.Local + private static ICustomMarshaler GetInstance(string cookie) + { + return _instance; + } + + public void CleanUpManagedData(object managedObj) { } + + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => -1; + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + return Marshal.PtrToStringUTF8(pNativeData)!; + } + + public IntPtr MarshalManagedToNative(object managedObj) + { + throw new NotImplementedException(); + } + } } \ No newline at end of file diff --git a/Wrapper.cs b/Wrapper.cs index b5d91dc..30607ee 100644 --- a/Wrapper.cs +++ b/Wrapper.cs @@ -1,7 +1,7 @@ +using System; + namespace Foodsoft.Alpm { public class Wrapper - { - - } + { } } \ No newline at end of file diff --git a/alpm.cs b/alpm.cs index adfa77b..30f61ed 100644 --- a/alpm.cs +++ b/alpm.cs @@ -1,7 +1,225 @@ -namespace Alpm +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; +using Foodsoft.Alpm; + +namespace Foodsoft.Alpm { - public class alpm + // ReSharper disable once InconsistentNaming + [StructLayout(LayoutKind.Sequential)] + internal struct alpm_list_t { + internal IntPtr data; + internal readonly IntPtr prev; + internal readonly IntPtr next; + } + + // ReSharper disable once InconsistentNaming + internal static class alpm + { + [DllImport(nameof(alpm))] + public static extern int alpm_pkg_should_ignore(SafeAlpmHandle handle, SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_filename(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_base(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_name(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_version(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + public static extern PackageOrigin alpm_pkg_get_origin(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_desc(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_url(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + public static extern long alpm_pkg_get_builddate(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + public static extern long alpm_pkg_get_installdate(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_packager(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_md5sum(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_sha256sum(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_arch(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + public static extern long alpm_pkg_get_size(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + public static extern long alpm_pkg_get_isize(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + public static extern InstallReason alpm_pkg_get_reason(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_licenses(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_groups(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_depends(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_optdepends(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_checkdepends(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_makedepends(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_conflicts(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_pkg_get_provides(SafePackageHandle pkg); + + [DllImport(nameof(alpm))] + public static extern int alpm_pkg_set_reason(SafePackageHandle pkg, InstallReason reason); + + [DllImport(nameof(alpm))] + public static extern int alpm_pkg_free(IntPtr ptr); + + [DllImport(nameof(alpm))] + public static extern IntPtr alpm_option_get_cachedirs(SafeAlpmHandle handle); + + [DllImport(nameof(alpm))] + public static extern int alpm_option_set_cachedirs(SafeAlpmHandle handle, IntPtr cachedirs); + + [DllImport(nameof(alpm))] + public static extern int alpm_option_add_cachedir(SafeAlpmHandle handle, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8InMarshaler))] string cachedir); + + [DllImport(nameof(alpm))] + public static extern int alpm_option_remove_cachedir(SafeAlpmHandle handle, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8InMarshaler))] string cachedir); + + [DllImport(nameof(alpm))] + public static extern ErrNo alpm_errno(SafeAlpmHandle handle); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_strerror(ErrNo err); + + [DllImport(nameof(alpm))] + public static extern IntPtr alpm_get_localdb(SafeAlpmHandle handle); + + [DllImport(nameof(alpm))] + public static extern unsafe alpm_list_t* alpm_option_get_syncdbs(); + + [DllImport(nameof(alpm))] + public static extern IntPtr alpm_register_syncdb(SafeAlpmHandle handle, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8InMarshaler))] string treename, SigLevel sigLevel); + + [DllImport(nameof(alpm))] + public static extern int alpm_db_unregister_all_syncdbs(SafeAlpmHandle handle); + + [DllImport(nameof(alpm))] + public static extern unsafe alpm_list_t* alpm_list_next(alpm_list_t* list); + + [DllImport(nameof(alpm))] + public static extern UIntPtr alpm_list_count(IntPtr list); + + [DllImport(nameof(alpm))] + public static extern void alpm_list_free(IntPtr list); + + [DllImport(nameof(alpm))] + public static extern IntPtr alpm_list_find_str(IntPtr list, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8InMarshaler))] string needle); + + [DllImport(nameof(alpm))] + public static extern int alpm_unlock(Handle handle); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_version(); + + [DllImport(nameof(alpm))] + public static extern caps alpm_capabilities(); + + [DllImport(nameof(alpm))] + public static extern SafeAlpmHandle alpm_initialize( + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8InMarshaler))] + string root, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8InMarshaler))] + string dbpath, out ErrNo err); + + [DllImport(nameof(alpm))] + public static extern int alpm_release(IntPtr handle); + + [DllImport(nameof(alpm))] + public static extern int alpm_db_remove_server(SafeDatabaseHandle db, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8InMarshaler))] + string url); + + [DllImport(nameof(alpm))] + public static extern int alpm_db_unregister(IntPtr db); + + [DllImport(nameof(alpm))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8OutMarshaler))] + public static extern string alpm_db_get_name(SafeDatabaseHandle db); + + [DllImport(nameof(alpm))] + public static extern SigLevel alpm_db_get_siglevel(SafeDatabaseHandle db); + + [DllImport(nameof(alpm))] + public static extern int alpm_db_get_valid(SafeDatabaseHandle db); + + [DllImport(nameof(alpm))] + public static extern int alpm_db_update(int force, SafeDatabaseHandle db); + + [DllImport(nameof(alpm))] + public static extern IntPtr alpm_db_get_servers(SafeDatabaseHandle db); + + [DllImport(nameof(alpm))] + public static extern int alpm_db_set_servers(SafeDatabaseHandle db, IntPtr list); + + [DllImport(nameof(alpm))] + public static extern int alpm_db_add_server(SafeDatabaseHandle db, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8InMarshaler))] + string url); + + [DllImport(nameof(alpm))] + public static extern IntPtr alpm_pkg_get_pkg(SafeDatabaseHandle db, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8InMarshaler))] + string name); + + [DllImport(nameof(alpm))] + public static extern IntPtr alpm_pkg_get_pkgcache(SafeDatabaseHandle db); + } } \ No newline at end of file