Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 57 additions & 39 deletions .github/workflows/build-clipboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,10 @@ jobs:

- name: Run a script
run: |
vcpkg install openssl:x64-windows
$env:PATH = "$env:VCPKG_ROOT;$env:PATH"
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
cmake --build . --config Release -j 3
- uses: actions/upload-artifact@v4
with:
Expand All @@ -176,96 +178,112 @@ jobs:
vcpkg install openssl:arm64-windows
$env:PATH = "$env:VCPKG_ROOT;$env:PATH"
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_GENERATOR_PLATFORM=ARM64
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_GENERATOR_PLATFORM=ARM64 -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
cmake --build . --config Release -j 3
- uses: actions/upload-artifact@v4
with:
name: clipboard-windows-arm64
path: build/output
freebsd-amd64:
runs-on: ubuntu-latest
defaults:
run:
shell: cpa.sh {0}

steps:
- uses: actions/checkout@v4

- name: Run a script
uses: cross-platform-actions/action@v0.13.0
- name: Start VM
uses: cross-platform-actions/action@v1.2.0
with:
operating_system: freebsd
architecture: x86-64
version: '13.1'
shell: bash
run: |
sudo pkg install -y cmake llvm15 xorg wayland
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang15 -DCMAKE_CXX_COMPILER=clang++15 -DNO_X11=1
cmake --build . -j 2
version: '14.2'

- name: Build
run: |
sudo pkg install -y cmake llvm15 xorg wayland
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang15 -DCMAKE_CXX_COMPILER=clang++15 -DNO_X11=1
cmake --build . -j 2
- uses: actions/upload-artifact@v4
with:
name: clipboard-freebsd-amd64
path: build/output
openbsd-amd64:
runs-on: ubuntu-latest
defaults:
run:
shell: cpa.sh {0}

steps:
- uses: actions/checkout@v4

- name: Run a script
uses: cross-platform-actions/action@v0.13.0
- name: Start VM
uses: cross-platform-actions/action@v1.2.0
with:
operating_system: openbsd
architecture: x86-64
version: '7.2'
shell: bash
run: |
sudo pkg_add cmake
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DNO_X11=1
cmake --build . -j 2
version: '7.6'

- name: Build
run: |
sudo pkg_add cmake
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DNO_X11=1
cmake --build . -j 2
- uses: actions/upload-artifact@v4
with:
name: clipboard-openbsd-amd64
path: build/output
openbsd-arm64:
runs-on: ubuntu-latest
defaults:
run:
shell: cpa.sh {0}

steps:
- uses: actions/checkout@v4

- name: Run a script
uses: cross-platform-actions/action@v0.13.0
- name: Start VM
uses: cross-platform-actions/action@v1.2.0
with:
operating_system: openbsd
architecture: arm64
version: '7.2'
shell: bash
run: |
sudo pkg_add cmake
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DNO_X11=1
cmake --build . -j 2
version: '7.6'

- name: Build
run: |
sudo pkg_add cmake
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DNO_X11=1
cmake --build . -j 2
- uses: actions/upload-artifact@v4
with:
name: clipboard-openbsd-arm64
path: build/output
netbsd-amd64:
runs-on: ubuntu-latest
defaults:
run:
shell: cpa.sh {0}

steps:
- uses: actions/checkout@v4

- name: Run a script
uses: cross-platform-actions/action@v0.13.0
- name: Start VM
uses: cross-platform-actions/action@v1.2.0
with:
operating_system: netbsd
architecture: x86_64
version: '9.2'
shell: bash
run: |
sudo pkgin -y install cmake gcc12
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc12/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc12/bin/g++
cmake --build . -j 2
architecture: x86-64
version: '10.1'

- name: Build
run: |
sudo pkgin -y install cmake gcc12
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc12/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc12/bin/g++
cmake --build . -j 2
- uses: actions/upload-artifact@v4
with:
name: clipboard-netbsd-amd64
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ documentation/website/public/

gmon.out

/temp/*
/temp/*BUG_FIXES.md
51 changes: 51 additions & 0 deletions BUG_FIXES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Clipboard 项目 Bug 修复记录

以下是我在项目目录中发现并修复的 Bug 列表。

## Bug 1: PipeFd 移动赋值操作符问题

- **文件**: `src/cbwayland/src/fd.cpp`
- **问题**: `PipeFd::operator=` 方法错误地显式调用了析构函数 (`PipeFd::~PipeFd();`) 而不是在交换文件描述符之前正确关闭它们。
- **修复**: 替换为正确的关闭操作:
```cpp
PipeFd& PipeFd::operator=(PipeFd&& other) noexcept {
closeRead();
closeWrite();
std::swap(m_readFd, other.m_readFd);
std::swap(m_writeFd, other.m_writeFd);
return *this;
}
```

## Bug 2: 缺失 SSIZE_MAX 定义

- **文件**: `src/cbwayland/src/fd.cpp`
- **问题**: `FdBuffer::constrainSize` 函数使用了 `SSIZE_MAX` 但未包含定义它的头文件。
- **修复**: 添加缺失的头文件包含:
```cpp
#include <sys/types.h>
#include <limits.h> // 添加此行
```

## Bug 3: Note 功能未正确处理尾部换行符

- **文件**: `src/cb/src/actions/note.cpp`
- **问题**: `notePipe` 函数保留了管道输入中的尾部换行符,导致测试失败。当用户通过 `echo "Foobar" | cb note` 管道输入时,测试期望的note内容是 "Foobar"(不含换行),但实际保存的是 "Foobar\n"。
- **修复**: 添加尾部换行符和回车符的去除逻辑:
```cpp
void notePipe() {
std::string content(pipedInContent());
// 去除尾部的换行符和回车符
while (!content.empty() &&
(content.back() == '\n' || content.back() == '\r')) {
content.pop_back();
}
writeToFile(path.metadata.notes, content);
if (output_silent || confirmation_silent) return;
stopIndicator();
fprintf(stderr, formatColors("[success][inverse] ✔ [noinverse] Saved note \"%s\"[blank]\n").data(), content.data());
exit(EXIT_SUCCESS);
}
```

所有修复后,项目能够成功编译并通过所有测试用例。
2 changes: 1 addition & 1 deletion src/cb/src/actions/clear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void clear() {
clipboards_cleared++;
}
stopIndicator();
fprintf(stderr, formatColors("[success][inverse] ✔ [noinverse] Cleared %d clipboard%s[blank]\n").data(), clipboards_cleared, clipboards_cleared == 1 ? "" : "s");
fprintf(stderr, formatColors("[success][inverse] ✔ [noinverse] Cleared %lu clipboard%s[blank]\n").data(), clipboards_cleared, clipboards_cleared == 1 ? "" : "s");
}
} else {
if (copying.items.size() >= 1) {
Expand Down
4 changes: 2 additions & 2 deletions src/cb/src/actions/history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ void history() {
constexpr size_t batchInterval = 65536;
#endif

auto longestEntryLength = numberLength(path.entryIndex.size() - 1);
auto longestEntryLength = path.entryIndex.empty() ? 1 : numberLength(path.entryIndex.size() - 1);

stopIndicator();
auto available = thisTerminalSize();
Expand Down Expand Up @@ -182,7 +182,7 @@ void history() {
size_t longestDateLength = atomicLongestDateLength.load(std::memory_order_relaxed);
size_t longestSizeLength = atomicLongestSizeLength.load(std::memory_order_relaxed);

for (long entry = path.entryIndex.size() - 1; entry >= 0; entry--) {
for (long entry = static_cast<long>(path.entryIndex.size()) - 1; entry >= 0; entry--) {
path.setEntry(entry);

if (batchedMessage.size() - offset > batchInterval) {
Expand Down
22 changes: 21 additions & 1 deletion src/cb/src/actions/note.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,27 @@ void noteText() {
}

void notePipe() {
std::string content(pipedInContent());
std::string rawContent(pipedInContent());
// Strip trailing newlines and carriage returns
std::string content(rawContent);
while (!content.empty() &&
(content.back() == '\n' || content.back() == '\r')) {
content.pop_back();
}
if (rawContent.empty()) {
if (fs::is_regular_file(path.metadata.notes)) {
std::string noteContent(fileContents(path.metadata.notes).value());
if (is_tty.out) {
stopIndicator();
printf(formatColors("[info]┃ Note for this clipboard: %s[blank]\n").data(), noteContent.data());
} else
printf(formatColors("%s").data(), noteContent.data());
} else {
stopIndicator();
fprintf(stderr, "%s", formatColors("[info]┃ There is no note for this clipboard.[blank]\n").data());
}
return;
}
writeToFile(path.metadata.notes, content);
if (output_silent || confirmation_silent) return;
stopIndicator();
Expand Down
6 changes: 3 additions & 3 deletions src/cb/src/actions/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ void displaySearchResults(const std::vector<Result>& results) {

void displaySearchJSON(const std::vector<Result>& results) {
printf("[\n");
for (size_t i = results.size() - 1; i > 0; i--) {
for (size_t i = 0; i < results.size(); i++) {
printf(" {\n");
printf(" \"clipboard\": \"%s\",\n", results.at(i).clipboard.data());
printf(" \"entry\": %lu,\n", results.at(i).entry);
printf(" \"preview\": \"%s\",\n", JSONescape(results.at(i).preview).data());
printf(" \"score\": %lu\n", results.at(i).score);
printf(" }%s\n", i == 1 ? "" : ",");
printf(" }%s\n", i == results.size() - 1 ? "" : ",");
}
printf("]\n");
}
Expand Down Expand Up @@ -150,7 +150,7 @@ void searchInternal(std::function<void(const std::vector<Result>&)> nextStep) {
};

for (auto& clipboard : targets) {
for (auto entry = 0; entry < clipboard.entryIndex.size(); entry++) {
for (size_t entry = 0; entry < clipboard.entryIndex.size(); entry++) {
auto adjustScoreByEntryPosition = [&](Result& result) {
float multiplier = 1.0f - (static_cast<float>(entry) / (20.0f * static_cast<float>(clipboard.entryIndex.size())));
float newScore = static_cast<float>(result.score) * multiplier;
Expand Down
13 changes: 8 additions & 5 deletions src/cb/src/clipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,14 @@ std::deque<unsigned long> Clipboard::generatedEntryIndex() {
fs::create_directories(entriesDir);
#if defined(UNIX_OR_UNIX_LIKE)
auto dirptr = opendir(entriesDir.string().data());
errno = 0;
for (auto* dir = readdir(dirptr); dir != nullptr; dir = readdir(dirptr), errno = 0) {
pathNames.emplace_back(0);
if (auto [ptr, ec] = std::from_chars(dir->d_name, dir->d_name + strlen(dir->d_name), pathNames.back()); ec != std::errc()) [[unlikely]]
pathNames.pop_back();
if (dirptr != nullptr) {
errno = 0;
for (auto* dir = readdir(dirptr); dir != nullptr; dir = readdir(dirptr), errno = 0) {
pathNames.emplace_back(0);
if (auto [ptr, ec] = std::from_chars(dir->d_name, dir->d_name + strlen(dir->d_name), pathNames.back()); ec != std::errc()) [[unlikely]]
pathNames.pop_back();
}
closedir(dirptr);
}
#else
for (const auto& entry : fs::directory_iterator(entriesDir))
Expand Down
12 changes: 7 additions & 5 deletions src/cb/src/externalclipboards.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ ClipboardContent getRemoteClipboard() {

// remove terminal control characters
response = response.substr(response.find_last_of(';') + 1);
response = response.substr(0, response.size() - 2); // remove the \007 character and something before it
if (response.size() >= 2)
response = response.substr(0, response.size() - 2); // remove the \007 character and something before it
else
response.clear();

if (response.empty()) return {};

Expand All @@ -83,7 +86,7 @@ ClipboardContent getRemoteClipboard() {
constexpr std::string_view convertToChar("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
std::string output;
output.reserve(content.size() * 3 / 4);
for (size_t i = 0; i < content.size(); i += 4) {
for (size_t i = 0; i + 1 < content.size(); i += 4) {
auto first = content.at(i);
auto second = content.at(i + 1);
auto byte = static_cast<char>((convertToChar.find(first) << 2) | (convertToChar.find(second) >> 4));
Expand Down Expand Up @@ -131,8 +134,7 @@ void convertFromGUIClipboard(const ClipboardPaths& clipboard) {
auto regexes = path.ignoreRegexes();
auto paths = clipboard.paths();
for (const auto& regex : regexes)
for (auto&& path : paths)
if (std::regex_match(path.filename().string(), regex)) paths.erase(std::find(paths.begin(), paths.end(), path));
paths.erase(std::remove_if(paths.begin(), paths.end(), [&regex](auto& path) { return std::regex_match(path.filename().string(), regex); }), paths.end());

// Only clear the temp directory if all files in the clipboard are outside the temp directory
// This avoids the situation where we delete the very files we're trying to copy
Expand Down Expand Up @@ -369,7 +371,7 @@ void setupGUIClipboardDaemon() {
close(STDOUT_FILENO);
close(STDERR_FILENO);

#elif defined(_WIN32) | defined(_WIN64)
#elif defined(_WIN32) || defined(_WIN64)

#endif

Expand Down
2 changes: 1 addition & 1 deletion src/cb/src/utils/formatting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ std::string generatedEndbar() {
std::string repeatString(const std::string_view& character, const size_t& length) {
std::string repeated;
repeated.reserve(character.size() * length);
for (int i = 0; i < length; i++)
for (size_t i = 0; i < length; i++)
repeated += character;
return repeated;
}
Expand Down
Loading
Loading