Compare commits
8
Commits
0e15bfe4bf
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d8e4f3625 | ||
|
|
add2138321 | ||
|
|
05aff91858 | ||
|
|
e0ecd63dfd | ||
|
|
33ae6c57a2 | ||
|
|
07882506c0 | ||
|
|
9af4b778eb | ||
|
|
2f89d5cfdc |
+49
-12
@@ -16,7 +16,7 @@ Config file and package tracking utility.
|
|||||||
|
|
||||||
If you are using *Arch Linux*, you can install the AUR package [[https://aur.archlinux.org/packages/manafiles-git/][manafiles-git]].
|
If you are using *Arch Linux*, you can install the AUR package [[https://aur.archlinux.org/packages/manafiles-git/][manafiles-git]].
|
||||||
|
|
||||||
Alternatively, you will have to [[#building][build from source]].
|
Alternatively, you will have to [[#compiling][build from source]].
|
||||||
|
|
||||||
*** Configuration
|
*** Configuration
|
||||||
|
|
||||||
@@ -86,21 +86,29 @@ or uncomments them depending on the machine's configuration. Such a block looks
|
|||||||
like this:
|
like this:
|
||||||
|
|
||||||
#+BEGIN_SRC shell-script
|
#+BEGIN_SRC shell-script
|
||||||
# >>> distro=arch hostname=arch-desktop user=anon
|
# >>> distro=arch hostname=arch-desktop user=anon session=xorg
|
||||||
export PATH="$PATH:$HOME/.local/bin"
|
export PATH="$PATH:$HOME/.local/bin"
|
||||||
# <<<
|
# <<<
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
This block uses all of the supported configurations, ~distro~, ~hostname~ and
|
This block uses all of the supported configurations, ~distro~, ~hostname~,
|
||||||
~user~. These can be used in any combination and order.
|
~user~ and ~session~. These can be used in any combination and order.
|
||||||
|
|
||||||
If *all* of the given configurations match, manafiles will make sure that the
|
If *all* of the given configurations match, manafiles will make sure that the
|
||||||
contents of the block are uncommented. If *any* of them do *not* match, then
|
contents of the block are uncommented. If *any* of them do *not* match, then
|
||||||
manafiles will make sure that the contents of the block are commented.
|
manafiles will make sure that the contents of the block are commented.
|
||||||
|
|
||||||
** Building
|
***** Usable values for the configurations
|
||||||
|
|
||||||
*** Build dependencies
|
| distro | session |
|
||||||
|
|--------+---------|
|
||||||
|
| arch | xorg |
|
||||||
|
| debian | wayland |
|
||||||
|
| ubuntu | |
|
||||||
|
|
||||||
|
** Compiling
|
||||||
|
|
||||||
|
*** Dependencies
|
||||||
|
|
||||||
- ~gcc-libs~
|
- ~gcc-libs~
|
||||||
- (make) ~cmake~
|
- (make) ~cmake~
|
||||||
@@ -112,17 +120,46 @@ manafiles will make sure that the contents of the block are commented.
|
|||||||
|
|
||||||
The optional requirements are for the package tracking functionality.
|
The optional requirements are for the package tracking functionality.
|
||||||
|
|
||||||
*** Build compilation
|
*** Building
|
||||||
|
|
||||||
|
To get the sources from the repository, run the following commands:
|
||||||
|
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
$ git clone https://github.com/riyyi/manafiles
|
||||||
|
$ cd manafiles
|
||||||
|
$ git submodule init
|
||||||
|
$ git submodule update
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
$ git clone --recursive https://github.com/riyyi/manafiles
|
||||||
|
$ cd manafiles
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
**** Compiling
|
||||||
|
|
||||||
|
To compile, run the following commands:
|
||||||
|
|
||||||
#+BEGIN_SRC shell-script
|
#+BEGIN_SRC shell-script
|
||||||
$ mkdir build
|
$ mkdir build
|
||||||
$ cd build
|
$ cd build
|
||||||
$ cmake ..
|
$ cmake ..
|
||||||
$ make
|
$ make
|
||||||
#+END_SRC
|
# Optional. This will install the manafiles executable in /usr/bin
|
||||||
|
|
||||||
*** Build installation
|
|
||||||
|
|
||||||
#+BEGIN_SRC shell-script
|
|
||||||
$ sudo make install
|
$ sudo make install
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
*** Uninstalling
|
||||||
|
|
||||||
|
To uninstall, run the following commands:
|
||||||
|
|
||||||
|
*Warning*: ~make uninstall~ will try to delete all files listed in the
|
||||||
|
~install_manifest.txt~ file inside your ~build~ folder. Make sure you first
|
||||||
|
check that it won't delete any important files.
|
||||||
|
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
$ cd build
|
||||||
|
$ sudo make uninstall
|
||||||
|
#+END_SRC
|
||||||
|
|||||||
+6
-4
@@ -1,4 +1,4 @@
|
|||||||
.TH MANAFILES 1 "2022-01-29" "manafiles 0.5" "User Commands"
|
.TH MANAFILES 1 "2025-02-02" "manafiles 0.6" "User Commands"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
manafiles \- config file and package tracking utility
|
manafiles \- config file and package tracking utility
|
||||||
@@ -31,7 +31,7 @@ See File Options below.
|
|||||||
.TP
|
.TP
|
||||||
.BR \-P ", " \-\-package
|
.BR \-P ", " \-\-package
|
||||||
Operate on packages. \
|
Operate on packages. \
|
||||||
This operation allows you to track installed packages and reinstall them. \
|
This operation allows tracking installed packages and reinstalling them. \
|
||||||
In the first case, if no package names are provided in the command line, all packages will be selected. \
|
In the first case, if no package names are provided in the command line, all packages will be selected. \
|
||||||
See Package Options below.
|
See Package Options below.
|
||||||
|
|
||||||
@@ -67,8 +67,10 @@ Install all AUR packages of the stored list.
|
|||||||
Install all official packages of the stored list.
|
Install all official packages of the stored list.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR \-s ", " \-\-store
|
.BR \-s ", " \-\-search
|
||||||
Stores a list of all installed packages on the system.
|
Search each locally-installed package for names via a partial match. \
|
||||||
|
When including multiple search terms, all packages with names matching any of those terms are returned. \
|
||||||
|
If the \fB-s\fR option is ommited, packages names are filtered via a full match.
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
Usage examples:
|
Usage examples:
|
||||||
|
|||||||
+53
-19
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2021-2022 Riyyi
|
* Copyright (C) 2021-2022,2025 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -15,11 +15,12 @@
|
|||||||
#include <unistd.h> // geteuid, getlogin, setegid, seteuid
|
#include <unistd.h> // geteuid, getlogin, setegid, seteuid
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "ruc/file.h"
|
||||||
|
#include "ruc/meta/assert.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "dotfile.h"
|
#include "dotfile.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
#include "ruc/file.h"
|
|
||||||
#include "ruc/meta/assert.h"
|
|
||||||
|
|
||||||
Dotfile::Dotfile(s)
|
Dotfile::Dotfile(s)
|
||||||
{
|
{
|
||||||
@@ -114,7 +115,12 @@ bool Dotfile::match(const std::string& path, const std::vector<std::string>& pat
|
|||||||
std::string pathString = path.substr(cutFrom);
|
std::string pathString = path.substr(cutFrom);
|
||||||
|
|
||||||
for (const auto& pattern : patterns) {
|
for (const auto& pattern : patterns) {
|
||||||
|
// A dot matches everything in the current working directory
|
||||||
|
if (pattern == ".") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exact match is obviously true
|
||||||
if (pathString == pattern) {
|
if (pathString == pattern) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -375,39 +381,58 @@ void Dotfile::selectivelyCommentOrUncomment(const std::string& path)
|
|||||||
{
|
{
|
||||||
ruc::File dotfile(path);
|
ruc::File dotfile(path);
|
||||||
|
|
||||||
const std::string search[3] = {
|
const std::string search[4] = {
|
||||||
"distro=",
|
"distro=",
|
||||||
"hostname=",
|
"hostname=",
|
||||||
"user=",
|
"user=",
|
||||||
|
"session=",
|
||||||
};
|
};
|
||||||
|
|
||||||
// State of the loop
|
// State of the loop
|
||||||
bool isFiltering = false;
|
bool isFiltering = false;
|
||||||
std::string filter[3];
|
std::string filter[4];
|
||||||
std::string commentCharacter;
|
std::string commentCharacter;
|
||||||
std::string commentTerminationCharacter;
|
std::string commentTerminationCharacter;
|
||||||
size_t positionInFile = 0;
|
size_t positionInFile = 0;
|
||||||
|
|
||||||
auto commentOrUncommentLine = [&](std::string& line, bool addComment) {
|
auto commentOrUncommentLine = [&](std::string& line, bool addComment) {
|
||||||
|
size_t indentation = line.find_first_not_of(" \t");
|
||||||
|
|
||||||
|
// Empty lines are skipped
|
||||||
|
if (line.empty() || indentation == std::string::npos) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
size_t lineLength = line.size();
|
size_t lineLength = line.size();
|
||||||
size_t whiteSpaceBeforeComment = line.find_first_not_of(" \t");
|
size_t commentStart = line.find(commentCharacter, indentation);
|
||||||
size_t contentAfterComment = line.find_first_not_of(" \t" + commentCharacter);
|
size_t commentEnd = line.rfind(commentTerminationCharacter);
|
||||||
// NOTE: The +1 is needed to take the newline into account
|
bool hasComment = commentStart != std::string::npos && (commentTerminationCharacter.empty() || commentEnd != std::string::npos);
|
||||||
size_t contentLength = lineLength + 1 - contentAfterComment - (lineLength - line.find_last_not_of(" \t" + commentTerminationCharacter));
|
|
||||||
|
|
||||||
// If there was no comment, grab whitespace correctly
|
// Lines that have a comment at the *end* of the line aren't considered commented lines
|
||||||
if (whiteSpaceBeforeComment == std::string::npos) {
|
if (hasComment && line.find_first_not_of(" \t") < commentStart) {
|
||||||
whiteSpaceBeforeComment = contentAfterComment;
|
hasComment = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!addComment) {
|
// Uncomment line
|
||||||
line = line.substr(0, whiteSpaceBeforeComment)
|
if (hasComment && !addComment) {
|
||||||
+ line.substr(contentAfterComment, contentLength);
|
size_t contentStart = line.find_first_not_of(" \t", commentStart + commentCharacter.size());
|
||||||
|
size_t contentLength = commentEnd - contentStart;
|
||||||
|
|
||||||
|
// Trim trailing whitespace
|
||||||
|
std::string content = line.substr(contentStart, contentLength);
|
||||||
|
content = content.substr(0, content.find_last_not_of(" \t") + 1);
|
||||||
|
|
||||||
|
line = line.substr(0, indentation)
|
||||||
|
+ content;
|
||||||
}
|
}
|
||||||
else {
|
// Comment line
|
||||||
line = line.substr(0, whiteSpaceBeforeComment)
|
else if (!hasComment && addComment) {
|
||||||
|
size_t contentStart = line.find_first_not_of(" \t");
|
||||||
|
size_t contentLength = line.size() - contentStart;
|
||||||
|
|
||||||
|
line = line.substr(0, indentation)
|
||||||
+ commentCharacter + ' '
|
+ commentCharacter + ' '
|
||||||
+ line.substr(contentAfterComment, contentLength)
|
+ line.substr(contentStart, contentLength)
|
||||||
+ (!commentTerminationCharacter.empty() ? ' ' + commentTerminationCharacter : "");
|
+ (!commentTerminationCharacter.empty() ? ' ' + commentTerminationCharacter : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,7 +445,7 @@ void Dotfile::selectivelyCommentOrUncomment(const std::string& path)
|
|||||||
if (line.find(">>>") != std::string::npos) {
|
if (line.find(">>>") != std::string::npos) {
|
||||||
// Find machine info
|
// Find machine info
|
||||||
size_t find = 0;
|
size_t find = 0;
|
||||||
for (size_t i = 0; i < 3; ++i) {
|
for (size_t i = 0; i < 4; ++i) {
|
||||||
find = line.find(search[i]) + search[i].size();
|
find = line.find(search[i]) + search[i].size();
|
||||||
if (find < search[i].size()) {
|
if (find < search[i].size()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -435,6 +460,10 @@ void Dotfile::selectivelyCommentOrUncomment(const std::string& path)
|
|||||||
if (i > 0 && commentCharacter.at(i - 1) == '/' && commentCharacter.at(i) == '*') {
|
if (i > 0 && commentCharacter.at(i - 1) == '/' && commentCharacter.at(i) == '*') {
|
||||||
commentTerminationCharacter = "*/";
|
commentTerminationCharacter = "*/";
|
||||||
}
|
}
|
||||||
|
// Support for <!-- XMl comments -->
|
||||||
|
if (i > 0 && commentCharacter.at(i - 1) == '<' && commentCharacter.at(i) == '!' && commentCharacter.at(i + 1) == '-' && commentCharacter.at(i + 2) == '-') {
|
||||||
|
commentTerminationCharacter = "-->";
|
||||||
|
}
|
||||||
// NOTE: Modification of the string should be at the end of the iteration to prevent 'out of range' errors
|
// NOTE: Modification of the string should be at the end of the iteration to prevent 'out of range' errors
|
||||||
if (commentCharacter.at(i) == ' ' || commentCharacter.at(i) == '\t') {
|
if (commentCharacter.at(i) == ' ' || commentCharacter.at(i) == '\t') {
|
||||||
commentCharacter.erase(i, 1);
|
commentCharacter.erase(i, 1);
|
||||||
@@ -450,6 +479,7 @@ void Dotfile::selectivelyCommentOrUncomment(const std::string& path)
|
|||||||
filter[0] = "";
|
filter[0] = "";
|
||||||
filter[1] = "";
|
filter[1] = "";
|
||||||
filter[2] = "";
|
filter[2] = "";
|
||||||
|
filter[3] = "";
|
||||||
commentCharacter.clear();
|
commentCharacter.clear();
|
||||||
commentTerminationCharacter.clear();
|
commentTerminationCharacter.clear();
|
||||||
continue;
|
continue;
|
||||||
@@ -471,6 +501,10 @@ void Dotfile::selectivelyCommentOrUncomment(const std::string& path)
|
|||||||
commentOrUncommentLine(line, true);
|
commentOrUncommentLine(line, true);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else if (filter[3] != Machine::the().session() && !filter[3].empty()) {
|
||||||
|
commentOrUncommentLine(line, true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
commentOrUncommentLine(line, false);
|
commentOrUncommentLine(line, false);
|
||||||
}
|
}
|
||||||
|
|||||||
+86
-2
@@ -1,21 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022,2025 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cstdint> // int8_t
|
||||||
|
#include <filesystem> // std::filesystem::path
|
||||||
#include <pwd.h> // getpwnam
|
#include <pwd.h> // getpwnam
|
||||||
#include <sstream> // istringstream
|
#include <sstream> // istringstream
|
||||||
#include <unistd.h> // gethostname, getlogin
|
#include <unistd.h> // gethostname, getlogin
|
||||||
|
|
||||||
#include "machine.h"
|
|
||||||
#include "ruc/file.h"
|
#include "ruc/file.h"
|
||||||
|
|
||||||
|
#include "machine.h"
|
||||||
|
|
||||||
Machine::Machine(s)
|
Machine::Machine(s)
|
||||||
{
|
{
|
||||||
fetchDistro();
|
fetchDistro();
|
||||||
fetchHostname();
|
fetchHostname();
|
||||||
fetchUsername();
|
fetchUsername();
|
||||||
|
fetchSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
Machine::~Machine()
|
Machine::~Machine()
|
||||||
@@ -61,3 +65,83 @@ void Machine::fetchUsername()
|
|||||||
perror("\033[31;1mError:\033[0m getpwnam");
|
perror("\033[31;1mError:\033[0m getpwnam");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Machine::fetchSession()
|
||||||
|
{
|
||||||
|
// Determine if this is an Xorg or Wayland session
|
||||||
|
int8_t likelyWayland = 0;
|
||||||
|
|
||||||
|
// Detect via environment variable
|
||||||
|
const char* env;
|
||||||
|
env = std::getenv("XDG_SESSION_TYPE");
|
||||||
|
if (env != nullptr) {
|
||||||
|
auto session = std::string(env);
|
||||||
|
if (session == "wayland") {
|
||||||
|
likelyWayland++;
|
||||||
|
}
|
||||||
|
else if (session == "x11") {
|
||||||
|
likelyWayland--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env = std::getenv("WAYLAND_DISPLAY");
|
||||||
|
if (env != nullptr) {
|
||||||
|
auto display = std::string(env);
|
||||||
|
if (display.find("wayland-", 0) == 0) {
|
||||||
|
likelyWayland++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect via Wayland socket
|
||||||
|
auto socket = std::filesystem::path("/run/user") / std::to_string(uid());
|
||||||
|
if (std::filesystem::exists(socket) && std::filesystem::is_directory(socket)) {
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(socket)) {
|
||||||
|
if (entry.path().filename().string().find("wayland-", 0) == 0) {
|
||||||
|
likelyWayland++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect via Xorg socket
|
||||||
|
if (std::filesystem::exists("/tmp/.X11-unix")) {
|
||||||
|
likelyWayland--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect via running processes, /proc/<id>/comm
|
||||||
|
std::filesystem::path processes = "/proc";
|
||||||
|
if (std::filesystem::exists(processes) && std::filesystem::is_directory(processes)) {
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(processes)) {
|
||||||
|
if (std::filesystem::is_directory(entry)) {
|
||||||
|
|
||||||
|
std::filesystem::path comm = entry.path() / "comm";
|
||||||
|
if (!std::filesystem::exists(comm)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the contents of the "comm" file
|
||||||
|
std::ifstream stream(comm);
|
||||||
|
std::string command;
|
||||||
|
std::getline(stream, command);
|
||||||
|
|
||||||
|
if (command == "Xwayland" || command == "sway" || command == "hyprland") {
|
||||||
|
likelyWayland++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command == "Xorg" || command == "xinit" || command == "i3" || command == "bspwm") {
|
||||||
|
likelyWayland--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we detected at least 2 ways, we can be fairly certain
|
||||||
|
m_session = "";
|
||||||
|
if (likelyWayland <= -2) {
|
||||||
|
m_session = "xorg";
|
||||||
|
}
|
||||||
|
else if (likelyWayland >= 2) {
|
||||||
|
m_session = "wayland";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+10
-6
@@ -17,21 +17,25 @@ public:
|
|||||||
Machine(s);
|
Machine(s);
|
||||||
virtual ~Machine();
|
virtual ~Machine();
|
||||||
|
|
||||||
const std::string& distroId() { return m_distroId; }
|
const std::string& distroId() const { return m_distroId; }
|
||||||
const std::string& distroIdLike() { return m_distroIdLike; }
|
const std::string& distroIdLike() const { return m_distroIdLike; }
|
||||||
const std::string& hostname() { return m_hostname; }
|
const std::string& hostname() const { return m_hostname; }
|
||||||
|
|
||||||
std::string username() { return m_passwd->pw_name; }
|
std::string username() const { return std::string(m_passwd->pw_name); }
|
||||||
uint32_t uid() { return m_passwd->pw_uid; }
|
uint32_t uid() const { return m_passwd->pw_uid; }
|
||||||
uint32_t gid() { return m_passwd->pw_gid; }
|
uint32_t gid() const { return m_passwd->pw_gid; }
|
||||||
|
|
||||||
|
const std::string& session() const { return m_session; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void fetchDistro();
|
void fetchDistro();
|
||||||
void fetchHostname();
|
void fetchHostname();
|
||||||
void fetchUsername();
|
void fetchUsername();
|
||||||
|
void fetchSession();
|
||||||
|
|
||||||
std::string m_distroId;
|
std::string m_distroId;
|
||||||
std::string m_distroIdLike;
|
std::string m_distroIdLike;
|
||||||
std::string m_hostname;
|
std::string m_hostname;
|
||||||
|
std::string m_session;
|
||||||
passwd* m_passwd { nullptr };
|
passwd* m_passwd { nullptr };
|
||||||
};
|
};
|
||||||
|
|||||||
+14
-16
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2021-2022 Riyyi
|
* Copyright (C) 2021-2022,2025 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -8,11 +8,12 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "ruc/argparser.h"
|
||||||
|
#include "ruc/timer.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "dotfile.h"
|
#include "dotfile.h"
|
||||||
#include "package.h"
|
#include "package.h"
|
||||||
#include "ruc/argparser.h"
|
|
||||||
#include "ruc/timer.h"
|
|
||||||
|
|
||||||
int main(int argc, const char* argv[])
|
int main(int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
@@ -23,7 +24,7 @@ int main(int argc, const char* argv[])
|
|||||||
bool addOrAur = false;
|
bool addOrAur = false;
|
||||||
bool install = false;
|
bool install = false;
|
||||||
bool pull = false;
|
bool pull = false;
|
||||||
bool pushOrStore = false;
|
bool pushOrSearch = false;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
|
||||||
std::vector<std::string> targets {};
|
std::vector<std::string> targets {};
|
||||||
@@ -36,7 +37,7 @@ int main(int argc, const char* argv[])
|
|||||||
argParser.addOption(addOrAur, 'a', "add", nullptr, nullptr);
|
argParser.addOption(addOrAur, 'a', "add", nullptr, nullptr);
|
||||||
argParser.addOption(install, 'i', "install", nullptr, nullptr);
|
argParser.addOption(install, 'i', "install", nullptr, nullptr);
|
||||||
argParser.addOption(pull, 'l', "pull", nullptr, nullptr);
|
argParser.addOption(pull, 'l', "pull", nullptr, nullptr);
|
||||||
argParser.addOption(pushOrStore, 's', "push", nullptr, nullptr);
|
argParser.addOption(pushOrSearch, 's', "push", nullptr, nullptr);
|
||||||
argParser.addOption(verbose, 'v', "verbose", nullptr, nullptr);
|
argParser.addOption(verbose, 'v', "verbose", nullptr, nullptr);
|
||||||
|
|
||||||
argParser.addArgument(targets, "targets", nullptr, nullptr, ruc::ArgParser::Required::No);
|
argParser.addArgument(targets, "targets", nullptr, nullptr, ruc::ArgParser::Required::No);
|
||||||
@@ -60,32 +61,29 @@ int main(int argc, const char* argv[])
|
|||||||
if (pull) {
|
if (pull) {
|
||||||
Dotfile::the().pull(targets);
|
Dotfile::the().pull(targets);
|
||||||
}
|
}
|
||||||
if (pushOrStore) {
|
if (pushOrSearch) {
|
||||||
Dotfile::the().push(targets);
|
Dotfile::the().push(targets);
|
||||||
}
|
}
|
||||||
if (!addOrAur && !pull && !pushOrStore) {
|
if (!addOrAur && !pull && !pushOrSearch) {
|
||||||
Dotfile::the().list(targets);
|
Dotfile::the().list(targets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (packageOperation) {
|
else if (packageOperation) {
|
||||||
if (addOrAur) {
|
if (addOrAur) {
|
||||||
Package::the().aurInstall();
|
Package::the().aurInstall(targets);
|
||||||
}
|
}
|
||||||
if (install) {
|
if (install) {
|
||||||
Package::the().install();
|
Package::the().install(targets);
|
||||||
}
|
}
|
||||||
if (pushOrStore) {
|
if (!addOrAur && !install) {
|
||||||
Package::the().store();
|
Package::the().list(targets, pushOrSearch);
|
||||||
}
|
|
||||||
if (!addOrAur && !install && !pushOrStore) {
|
|
||||||
Package::the().list(targets);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (helpOperation) {
|
else if (helpOperation) {
|
||||||
// TODO open manpage
|
// TODO: open manpage
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// TODO open manpage
|
// TODO: open manpage
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|||||||
+24
-28
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2021-2022 Riyyi
|
* Copyright (C) 2021-2022,2025 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -14,12 +14,12 @@
|
|||||||
#include <string> // getline
|
#include <string> // getline
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "machine.h"
|
|
||||||
#include "package.h"
|
|
||||||
#include "ruc/file.h"
|
|
||||||
#include "ruc/shell.h"
|
#include "ruc/shell.h"
|
||||||
#include "ruc/system.h"
|
#include "ruc/system.h"
|
||||||
|
|
||||||
|
#include "machine.h"
|
||||||
|
#include "package.h"
|
||||||
|
|
||||||
Package::Package(s)
|
Package::Package(s)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -30,17 +30,27 @@ Package::~Package()
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
void Package::aurInstall()
|
void Package::aurInstall(const std::vector<std::string>& targets)
|
||||||
{
|
{
|
||||||
installOrAurInstall(InstallType::AurInstall);
|
if (targets.size() > 1) {
|
||||||
|
fprintf(stderr, "\033[31;1mPackage:\033[0m only 1 file can be read packages from at a time\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
installOrAurInstall(InstallType::AurInstall, targets.size() != 0 ? targets.front() : PACKAGE_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Package::install()
|
void Package::install(const std::vector<std::string>& targets)
|
||||||
{
|
{
|
||||||
installOrAurInstall(InstallType::Install);
|
if (targets.size() > 1) {
|
||||||
|
fprintf(stderr, "\033[31;1mPackage:\033[0m only 1 file can be read packages from at a time\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
installOrAurInstall(InstallType::Install, targets.size() != 0 ? targets.front() : PACKAGE_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Package::list(const std::vector<std::string>& targets)
|
void Package::list(const std::vector<std::string>& targets, bool partialMatch)
|
||||||
{
|
{
|
||||||
auto packagesOrEmpty = getPackageList();
|
auto packagesOrEmpty = getPackageList();
|
||||||
|
|
||||||
@@ -62,7 +72,7 @@ void Package::list(const std::vector<std::string>& targets)
|
|||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(stream, line)) {
|
while (std::getline(stream, line)) {
|
||||||
for (const auto& target : targets) {
|
for (const auto& target : targets) {
|
||||||
if (line.find(target) != std::string::npos) {
|
if (line == target || (partialMatch && line.find(target) != std::string::npos)) {
|
||||||
packages.append(line + '\n');
|
packages.append(line + '\n');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -72,20 +82,6 @@ void Package::list(const std::vector<std::string>& targets)
|
|||||||
printf("%s", packages.c_str());
|
printf("%s", packages.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Package::store()
|
|
||||||
{
|
|
||||||
auto packagesOrEmpty = getPackageList();
|
|
||||||
|
|
||||||
if (!packagesOrEmpty.has_value()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto packageFile = ruc::File::create("./packages");
|
|
||||||
packageFile.clear();
|
|
||||||
packageFile.append(packagesOrEmpty.value());
|
|
||||||
packageFile.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
std::optional<std::string> Package::fetchAurHelper()
|
std::optional<std::string> Package::fetchAurHelper()
|
||||||
@@ -105,7 +101,7 @@ std::optional<std::string> Package::fetchAurHelper()
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Package::installOrAurInstall(InstallType type)
|
void Package::installOrAurInstall(InstallType type, const std::string& file)
|
||||||
{
|
{
|
||||||
distroDetect();
|
distroDetect();
|
||||||
distroDependencies();
|
distroDependencies();
|
||||||
@@ -129,12 +125,12 @@ void Package::installOrAurInstall(InstallType type)
|
|||||||
ruc::System $;
|
ruc::System $;
|
||||||
if (m_distro == Distro::Arch) {
|
if (m_distro == Distro::Arch) {
|
||||||
// Grab everything off enabled official repositories that is in the list
|
// Grab everything off enabled official repositories that is in the list
|
||||||
auto repoList = $("pacman -Ssq") | $("grep -xf ./packages");
|
auto repoList = $("pacman -Ssq") | $("grep -xf " + file);
|
||||||
|
|
||||||
if (type == InstallType::AurInstall) {
|
if (type == InstallType::AurInstall) {
|
||||||
// Determine which packages in the list are from the AUR
|
// Determine which packages in the list are from the AUR
|
||||||
// NOTE: ruc::System does not support commands with newlines
|
// NOTE: ruc::System does not support commands with newlines
|
||||||
auto aurList = ruc::Shell()("grep -vx '" + repoList.output() + "' ./packages");
|
auto aurList = ruc::Shell()("grep -vx '" + repoList.output() + "' " + file);
|
||||||
command = aurHelper.value() + " -Sy --devel --needed --noconfirm " + aurList.output();
|
command = aurHelper.value() + " -Sy --devel --needed --noconfirm " + aurList.output();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -143,7 +139,7 @@ void Package::installOrAurInstall(InstallType type)
|
|||||||
}
|
}
|
||||||
else if (m_distro == Distro::Debian) {
|
else if (m_distro == Distro::Debian) {
|
||||||
// Grab everything off enabled official repositories that is in the list
|
// Grab everything off enabled official repositories that is in the list
|
||||||
auto repoList = $("apt-cache search .").cut(1, ' ') | $("grep -xf ./packages");
|
auto repoList = $("apt-cache search .").cut(1, ' ') | $("grep -xf " + file);
|
||||||
command = "apt install " + repoList.output();
|
command = "apt install " + repoList.output();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-6
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2021-2022 Riyyi
|
* Copyright (C) 2021-2022,2025 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
#include "ruc/singleton.h"
|
#include "ruc/singleton.h"
|
||||||
|
|
||||||
|
#define PACKAGE_FILE "./packages"
|
||||||
|
|
||||||
class Package : public ruc::Singleton<Package> {
|
class Package : public ruc::Singleton<Package> {
|
||||||
public:
|
public:
|
||||||
Package(s);
|
Package(s);
|
||||||
@@ -29,14 +31,13 @@ public:
|
|||||||
AurInstall,
|
AurInstall,
|
||||||
};
|
};
|
||||||
|
|
||||||
void aurInstall();
|
void aurInstall(const std::vector<std::string>& targets = {});
|
||||||
void install();
|
void install(const std::vector<std::string>& targets = {});
|
||||||
void list(const std::vector<std::string>& targets = {});
|
void list(const std::vector<std::string>& targets = {}, bool partialMatch = false);
|
||||||
void store();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<std::string> fetchAurHelper();
|
std::optional<std::string> fetchAurHelper();
|
||||||
void installOrAurInstall(InstallType type);
|
void installOrAurInstall(InstallType type, const std::string& file);
|
||||||
|
|
||||||
bool findDependency(const std::string& search);
|
bool findDependency(const std::string& search);
|
||||||
bool distroDetect();
|
bool distroDetect();
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022,2025 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint32_t
|
|
||||||
#include <cstdio> // stderr
|
#include <cstdio> // stderr
|
||||||
#include <filesystem> // path
|
#include <filesystem> // path
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -13,12 +12,12 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "ruc/file.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "dotfile.h"
|
#include "dotfile.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "ruc/file.h"
|
|
||||||
#include "ruc/system.h"
|
|
||||||
#include "testcase.h"
|
#include "testcase.h"
|
||||||
#include "testsuite.h"
|
#include "testsuite.h"
|
||||||
|
|
||||||
@@ -28,7 +27,7 @@ const size_t homeDirectorySize = homeDirectory.string().size();
|
|||||||
|
|
||||||
void createTestDotfiles(const std::vector<std::string>& fileNames, const std::vector<std::string>& fileContents, bool asRoot = false)
|
void createTestDotfiles(const std::vector<std::string>& fileNames, const std::vector<std::string>& fileContents, bool asRoot = false)
|
||||||
{
|
{
|
||||||
EXPECT(fileNames.size() == fileContents.size(), return );
|
EXPECT(fileNames.size() == fileContents.size(), return);
|
||||||
|
|
||||||
if (root && !asRoot) {
|
if (root && !asRoot) {
|
||||||
setegid(Machine::the().gid());
|
setegid(Machine::the().gid());
|
||||||
@@ -503,7 +502,7 @@ TEST_CASE(PushDotfilesWithIgnorePattern)
|
|||||||
TEST_CASE(PushDotfilesSelectivelyComment)
|
TEST_CASE(PushDotfilesSelectivelyComment)
|
||||||
{
|
{
|
||||||
std::vector<std::string> fileNames;
|
std::vector<std::string> fileNames;
|
||||||
for (size_t i = 0; i < 36; ++i) {
|
for (size_t i = 0; i < 44; ++i) {
|
||||||
fileNames.push_back("__test-file-" + std::to_string(i + 1));
|
fileNames.push_back("__test-file-" + std::to_string(i + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,6 +677,43 @@ test data /**/ comment
|
|||||||
/* test data /**/ uncomment */
|
/* test data /**/ uncomment */
|
||||||
/* <<< */
|
/* <<< */
|
||||||
)",
|
)",
|
||||||
|
|
||||||
|
// Comment <!---->
|
||||||
|
"<!-- >>> distro=" + placeholder + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
test data <!----> comment
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + distro + " hostname=" + placeholder + " user=" + username + " -->" + R"(
|
||||||
|
test data <!----> comment
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + distro + " hostname=" + hostname + " user=" + placeholder + " -->" + R"(
|
||||||
|
test data <!----> comment
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + placeholder + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
test data <!----> comment
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
|
||||||
|
// Uncomment <!---->
|
||||||
|
"<!-- >>> distro=" + distro + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
<!-- test data <!----> uncomment -->
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro="
|
||||||
|
+ distro + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
<!-- test data <!----> uncomment -->
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + distro + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
<!-- test data <!----> uncomment -->
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + distro + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
<!-- test data <!----> uncomment -->
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::string> pushedFileContents = {
|
std::vector<std::string> pushedFileContents = {
|
||||||
@@ -846,6 +882,42 @@ test data /**/ uncomment
|
|||||||
test data /**/ uncomment
|
test data /**/ uncomment
|
||||||
/* <<< */
|
/* <<< */
|
||||||
)",
|
)",
|
||||||
|
|
||||||
|
// Comment <!---->
|
||||||
|
"<!-- >>> distro=" + placeholder + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
<!-- test data <!----> comment -->
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + distro + " hostname=" + placeholder + " user=" + username + " -->" + R"(
|
||||||
|
<!-- test data <!----> comment -->
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + distro + " hostname=" + hostname + " user=" + placeholder + " -->" + R"(
|
||||||
|
<!-- test data <!----> comment -->
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + placeholder + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
<!-- test data <!----> comment -->
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
|
||||||
|
// Uncomment <!---->
|
||||||
|
"<!-- >>> distro=" + distro + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
test data <!----> uncomment
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + distro + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
test data <!----> uncomment
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + distro + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
test data <!----> uncomment
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
|
" <!-- >>> distro=" + distro + " hostname=" + hostname + " user=" + username + " -->" + R"(
|
||||||
|
test data <!----> uncomment
|
||||||
|
<!-- <<< -->
|
||||||
|
)",
|
||||||
};
|
};
|
||||||
|
|
||||||
createTestDotfiles(fileNames, fileContents);
|
createTestDotfiles(fileNames, fileContents);
|
||||||
@@ -866,7 +938,7 @@ test data /**/ uncomment
|
|||||||
|
|
||||||
TEST_CASE(AddSystemDotfiles)
|
TEST_CASE(AddSystemDotfiles)
|
||||||
{
|
{
|
||||||
EXPECT(geteuid() == 0, return );
|
EXPECT(geteuid() == 0, return);
|
||||||
|
|
||||||
Config::the().setSystemPatterns({ "/etc/", "/usr/lib/" });
|
Config::the().setSystemPatterns({ "/etc/", "/usr/lib/" });
|
||||||
Dotfile::the().add({ "/etc/group", "/usr/lib/os-release" });
|
Dotfile::the().add({ "/etc/group", "/usr/lib/os-release" });
|
||||||
@@ -881,7 +953,7 @@ TEST_CASE(AddSystemDotfiles)
|
|||||||
|
|
||||||
TEST_CASE(PullSystemDotfiles)
|
TEST_CASE(PullSystemDotfiles)
|
||||||
{
|
{
|
||||||
EXPECT(geteuid() == 0, return );
|
EXPECT(geteuid() == 0, return);
|
||||||
|
|
||||||
createTestDotfiles({ "etc/group" }, { "" }, true);
|
createTestDotfiles({ "etc/group" }, { "" }, true);
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
Submodule vendor/ruc updated: d3de1fb635...243442a3b6
Reference in New Issue
Block a user