mirror of
https://github.com/tomasriveral/NoteWrapper.git
synced 2026-08-12 10:20:46 +02:00
Compare commits
117
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e666ca32de | ||
|
|
2a8392ac2e | ||
|
|
292688296e | ||
|
|
4c18c2dda4 | ||
|
|
1bfb7f82a5 | ||
|
|
a32f97d229 | ||
|
|
6555b7247e | ||
|
|
2b2a3455d2 | ||
|
|
d8a11bbc16 | ||
|
|
1d95983d5d | ||
|
|
30449ca473 | ||
|
|
7af79c76b1 | ||
|
|
27b8b310cb | ||
|
|
4d228fb911 | ||
|
|
34a2546990 | ||
|
|
7231741f2d | ||
|
|
e86d7e7e03 | ||
|
|
280c329339 | ||
|
|
f7c84136a5 | ||
|
|
f66371137a | ||
|
|
77b5148489 | ||
|
|
d537357802 | ||
|
|
503dddd9be | ||
|
|
fd77eaa4de | ||
|
|
d6fa89785f | ||
|
|
30616b1b6f | ||
|
|
004b536e05 | ||
|
|
c9ee6ca032 | ||
|
|
c8b5538e1c | ||
|
|
2a09d4ee46 | ||
|
|
52169ccbe4 | ||
|
|
31366aeb48 | ||
|
|
e370f98c5e | ||
|
|
5c4bd6ef16 | ||
|
|
6e6ddebb5e | ||
|
|
fcb853e36a | ||
|
|
91d6db6c74 | ||
|
|
833f8c1bd2 | ||
|
|
5328fdc91c | ||
|
|
f85a32f97f | ||
|
|
bc306ca630 | ||
|
|
89c686652e | ||
|
|
d1c19696ff | ||
|
|
8ee0096540 | ||
|
|
3fa0dbe0dd | ||
|
|
2d6787a91b | ||
|
|
2f688df3ec | ||
|
|
1f20eb6996 | ||
|
|
ebd1ea70e1 | ||
|
|
ffcc16c14f | ||
|
|
9aa8b075e9 | ||
|
|
9a865897ed | ||
|
|
5cdf27f7ed | ||
|
|
e98985fb5b | ||
|
|
dff2fb25aa | ||
|
|
56498b2732 | ||
|
|
73f892aff0 | ||
|
|
533afd0a4b | ||
|
|
986618a05b | ||
|
|
7bd39a08c6 | ||
|
|
37a0763e14 | ||
|
|
0bf3d4624e | ||
|
|
36189b4a9d | ||
|
|
feadf62632 | ||
|
|
a839b0da5b | ||
|
|
027038e49e | ||
|
|
0b2c499bd1 | ||
|
|
8720a17286 | ||
|
|
c46ff838c1 | ||
|
|
66ca150190 | ||
|
|
acd64a845b | ||
|
|
2a5b08b737 | ||
|
|
a142f92c79 | ||
|
|
4422a8d6fd | ||
|
|
fc9480e12e | ||
|
|
d1561ddc2e | ||
|
|
a33a94637f | ||
|
|
4d7c44c016 | ||
|
|
433aca0a58 | ||
|
|
f8bc4004ed | ||
|
|
bf32877f96 | ||
|
|
99e0f9c130 | ||
|
|
92caf3ffd7 | ||
|
|
a3ece38fc1 | ||
|
|
aa4fa234ff | ||
|
|
d5b5d07e64 | ||
|
|
3fb2a32b83 | ||
|
|
5c87ac0aa2 | ||
|
|
3233b330db | ||
|
|
b7ca3eca14 | ||
|
|
68991673c0 | ||
|
|
0d2ec12263 | ||
|
|
107519507f | ||
|
|
e2343dd401 | ||
|
|
08fffcb3d1 | ||
|
|
8cae6cf8ff | ||
|
|
495e8015bc | ||
|
|
d208ccaa20 | ||
|
|
ce4b5cd719 | ||
|
|
f6625e0ef1 | ||
|
|
ae168f92ce | ||
|
|
0eac451751 | ||
|
|
9d7e2efe17 | ||
|
|
0b92270e15 | ||
|
|
7db5bfb44a | ||
|
|
eceaf723f1 | ||
|
|
cb6ac9bce6 | ||
|
|
847f0dae5d | ||
|
|
411ed0939a | ||
|
|
83008d8da8 | ||
|
|
44f14803e0 | ||
|
|
3190da3cc5 | ||
|
|
adbc56fb36 | ||
|
|
9ad71fb4f5 | ||
|
|
5aee1f540c | ||
|
|
f131316fa7 | ||
|
|
7ced9e735f |
@@ -0,0 +1,3 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 4
|
||||||
|
ColumnLimit: 210
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
name: Release PR
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release_pr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Configure git
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
- uses: DeterminateSystems/nix-installer-action@v12
|
||||||
|
|
||||||
|
- name: Create release branch
|
||||||
|
run: |
|
||||||
|
git checkout -B release/${{ inputs.version }}
|
||||||
|
|
||||||
|
- name: Set version in flake.nix
|
||||||
|
run: |
|
||||||
|
sed -i "s/version = \".*\";/version = \"${{ inputs.version }}\";/" flake.nix
|
||||||
|
|
||||||
|
- name: Update flake inputs
|
||||||
|
run: nix flake update
|
||||||
|
|
||||||
|
- name: Commit changes
|
||||||
|
run: |
|
||||||
|
git add flake.nix flake.lock
|
||||||
|
git commit -m "chore(release): ${{ inputs.version }}"
|
||||||
|
|
||||||
|
- name: Push branch
|
||||||
|
run: |
|
||||||
|
git push origin release/${{ inputs.version }}
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
run: |
|
||||||
|
gh pr create \
|
||||||
|
--base main \
|
||||||
|
--head release/${{ inputs.version }} \
|
||||||
|
--title "[Automatic] Release PR ${{ inputs.version }}" \
|
||||||
|
--body "Automated release PR.\nAfter the test's succesfull run, create manually a release with a new tag matching ${{ inputs.version }}"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install clang
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y clang-format
|
||||||
|
- name: Check format
|
||||||
|
run: |
|
||||||
|
clang-format --version
|
||||||
|
find src -name "*.c" -o -name "*.h" | xargs clang-format --dry-run --Werror
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get install -y \
|
||||||
|
gcc \
|
||||||
|
make \
|
||||||
|
pkg-config \
|
||||||
|
libcjson-dev \
|
||||||
|
libncurses-dev \
|
||||||
|
git \
|
||||||
|
neovim
|
||||||
|
- name: Prepare environnement
|
||||||
|
run: mkdir -p ~/Documents/Notes
|
||||||
|
- name: Build
|
||||||
|
run: make
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
export TERM=xterm
|
||||||
|
make test
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
name: Weekly Flake Update
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 3 * * 1" # every Monday at 03:00 UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
flake_update:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: DeterminateSystems/nix-installer-action@v12
|
||||||
|
|
||||||
|
- name: Set branch name (date)
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
echo "branch=flake-update/$(date +%Y-%m-%d)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create update branch
|
||||||
|
run: |
|
||||||
|
git checkout -B ${{ steps.vars.outputs.branch }}
|
||||||
|
|
||||||
|
- name: Update flake inputs
|
||||||
|
run: |
|
||||||
|
nix flake update
|
||||||
|
|
||||||
|
- name: Commit changes
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add flake.lock
|
||||||
|
git commit -m "chore: weekly flake update" || echo "No changes to commit"
|
||||||
|
|
||||||
|
- name: Push branch
|
||||||
|
run: |
|
||||||
|
git push origin ${{ steps.vars.outputs.branch }} --force
|
||||||
|
|
||||||
|
- name: Install GitHub CLI
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y gh
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
run: |
|
||||||
|
gh pr create \
|
||||||
|
--base main \
|
||||||
|
--head ${{ steps.vars.outputs.branch }} \
|
||||||
|
--title "[Automated] Weekly flake update $(date +%Y-%m-%d)" \
|
||||||
|
--body "Automated weekly update of flake inputs for $(date +%Y-%m-%d)."
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -8,3 +8,4 @@ result
|
|||||||
massif.out.*
|
massif.out.*
|
||||||
*.out.*
|
*.out.*
|
||||||
vgcore.*
|
vgcore.*
|
||||||
|
.direnv/
|
||||||
|
|||||||
+20
-2
@@ -37,7 +37,7 @@ Format:
|
|||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
* `Editor (#1): add support for Microsoft Word`
|
* `Editor (#1): add support for Microsoft Word`
|
||||||
* `README.md: fix typos`
|
* `docs: fix typos in README.md`
|
||||||
* `Journal: improve entry parsing logic`
|
* `Journal: improve entry parsing logic`
|
||||||
|
|
||||||
Keep commit messages short and descriptive.
|
Keep commit messages short and descriptive.
|
||||||
@@ -58,7 +58,7 @@ To add support for a new editor, you must update both documentation and source c
|
|||||||
* Add the editor name to `supportedEditor[]`
|
* Add the editor name to `supportedEditor[]`
|
||||||
* Increment `numEditor`
|
* Increment `numEditor`
|
||||||
|
|
||||||
* If the editor executable name differs from its logical name (for example `neovim` → `nvim`), update `doesEditorExist()` in `src/utils.c`
|
* If the editor executable name differs from its logical name (for example `neovim` → `nvim`), update `isEditorValid()` in `src/utils.c`
|
||||||
|
|
||||||
* Modify `openEditor()` in `src/utils.c`:
|
* Modify `openEditor()` in `src/utils.c`:
|
||||||
|
|
||||||
@@ -67,6 +67,15 @@ To add support for a new editor, you must update both documentation and source c
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Changing default configuration
|
||||||
|
|
||||||
|
If you want to change the default configuration or add a new option:
|
||||||
|
1. Update [README's documentation](./README.md#configuration)
|
||||||
|
2. In `./src/utils.c`, update the function `initAppFilesAndDirs()` which creates a default configuration if `~/.config/notewrapper/config.json` or if neither `-c` nor `--config` is set.
|
||||||
|
3. In `./src/main.c` where the JSON is parsed, add the parsing logic. Do not forget the debugging information, errors when wrong type or when an important field is missing and add a default value if a less important field is missing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Before opening a pull request
|
## Before opening a pull request
|
||||||
|
|
||||||
Before submitting a pull request, ensure that:
|
Before submitting a pull request, ensure that:
|
||||||
@@ -76,3 +85,12 @@ Before submitting a pull request, ensure that:
|
|||||||
* `make`, or
|
* `make`, or
|
||||||
* `nix-build` (on NixOS)
|
* `nix-build` (on NixOS)
|
||||||
* There are **no warnings or errors**
|
* There are **no warnings or errors**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Continuous integration
|
||||||
|
|
||||||
|
All PR will need to pass these checks:
|
||||||
|
1. `clang-format`. Running `find src -name "*.c" -o -name "*.h" | xargs clang-format -i` will automatically apply those rules.
|
||||||
|
2. The program must build without warning or error.
|
||||||
|
3. The program must run for five seconds without crashing.
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# NoteWrapper
|
# NoteWrapper
|
||||||
|
|
||||||
|
Note: This project fullfills all my usecases for now, so my main source for features ideas dried out. So if you have good ideas, feel free to open an issue ;)
|
||||||
|
|
||||||
[](https://deepwiki.com/tomasriveral/notewrapper)
|
[](https://deepwiki.com/tomasriveral/notewrapper)
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
@@ -21,8 +23,8 @@ The goal was to create a terminal-based interface for accessing vaults and notes
|
|||||||
* Standard Markdown files (no custom format)
|
* Standard Markdown files (no custom format)
|
||||||
* Journal support with flexible entry formats
|
* Journal support with flexible entry formats
|
||||||
* Real-time Markdown rendering via Vivify
|
* Real-time Markdown rendering via Vivify
|
||||||
* Backup support using `rsync`
|
* Backup different directories into different destinations
|
||||||
* Supports multiple editors (vim, neovim, nano)
|
* Supports multiple editors
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -40,16 +42,41 @@ Before building NoteWrapper, you must install the following dependencies:
|
|||||||
* `ripgrep`
|
* `ripgrep`
|
||||||
* `fzf`
|
* `fzf`
|
||||||
|
|
||||||
You must also have a [supported editor](#editor-support) installed:
|
You must also have a [supported editor (and their associated plugin if needed)](#editor-support) installed:
|
||||||
|
|
||||||
* `vim`
|
* `helix`
|
||||||
* `neovim`
|
* `jed`
|
||||||
|
* `kakoune`
|
||||||
|
* `micro`
|
||||||
* `nano`
|
* `nano`
|
||||||
|
* `neovim`
|
||||||
|
* `vi`
|
||||||
|
* `vim`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## How to install
|
## How to install
|
||||||
|
|
||||||
|
### Nix flakes
|
||||||
|
|
||||||
|
Add this to your inputs in flake.nix:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
notewrapper = {
|
||||||
|
url = "github:tomasriveral/notewrapper";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
If your flake already inputs nixpkgs and/or flake-utils, you can follow it:
|
||||||
|
```nix
|
||||||
|
notewrapper = {
|
||||||
|
url = "github:tomasriveral/notewrapper;
|
||||||
|
inputs.nixpkgs.follow = "nixpkgs";
|
||||||
|
inputs.flake-utils.follow = "flake-utils";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building from source
|
||||||
|
|
||||||
1. Install all required dependencies listed above.
|
1. Install all required dependencies listed above.
|
||||||
2. Ensure you have a supported editor installed.
|
2. Ensure you have a supported editor installed.
|
||||||
3. Clone the repository:
|
3. Clone the repository:
|
||||||
@@ -73,7 +100,7 @@ nix-build
|
|||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Configure `./config.json` (it is automatically created on first launch if it does not already exist).
|
5. [Configure](#configuration) `~/.config/notewrapper/config.json` (it is automatically created on first launch if it does not already exist).
|
||||||
6. Run the program:
|
6. Run the program:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@@ -82,7 +109,7 @@ make
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Packaging
|
### Packaging
|
||||||
|
|
||||||
There is currently no official package available for NoteWrapper.
|
There is currently no official package available for NoteWrapper.
|
||||||
|
|
||||||
@@ -98,7 +125,6 @@ As a NixOS user, I will likely package it for **nixpkgs** in the future if the p
|
|||||||
Usage: notewrapper [options]
|
Usage: notewrapper [options]
|
||||||
Options:
|
Options:
|
||||||
-c, --config <path/to/config> Specify the config file.
|
-c, --config <path/to/config> Specify the config file.
|
||||||
-d, --directory <path/to/directory> Specify the vaults' directory.
|
|
||||||
-h, --help Display this message.
|
-h, --help Display this message.
|
||||||
-e, --editor Specify the editor to open.
|
-e, --editor Specify the editor to open.
|
||||||
-j, --jump Jump to the end of the file on opening.
|
-j, --jump Jump to the end of the file on opening.
|
||||||
@@ -139,11 +165,16 @@ The first two features depend on [Vivify's editor integration](https://github.co
|
|||||||
|
|
||||||
If your editor does not support these features, you can implement a plugin using [Vivify's API](https://github.com/jannis-baum/Vivify?tab=readme-ov-file#editor-support).
|
If your editor does not support these features, you can implement a plugin using [Vivify's API](https://github.com/jannis-baum/Vivify?tab=readme-ov-file#editor-support).
|
||||||
|
|
||||||
| Editor | Bufferless | Cursor | Jump to end | Plugin required |
|
| Editor | Bufferless | Cursor | Jump to end | Aditional requirements |
|
||||||
| ------ | ---------- | ------ | ----------- | ------------------------------------------------------- |
|
| --------- | ----------- | ------- | ------------ | ---------------------------------------------------------|
|
||||||
| Neovim | ✅ | ✅ | ✅ | [vivify-vim](https://github.com/jannis-baum/vivify.vim) |
|
| [Helix](https://helix-editor.com/) | ❌ | ❌ | ✅ | — |
|
||||||
| Vim | ✅ | ✅ | ✅ | [vivify-vim](https://github.com/jannis-baum/vivify.vim) |
|
| [Jed](https://www.jedsoft.org/jed/) | ❌ | ❌ | ✅ | — |
|
||||||
| Nano | ❌ | ❌ | ✅ | — |
|
| [Kakoune](http://kakoune.org/) | ❌ | ❌ | ✅ | — |
|
||||||
|
| [Micro](https://micro-editor.github.io/) | ❌ | ✅ | ✅ | [micro-vivify](https://codeberg.org/gibbert/micro-vivify) and [modifications to your `init.lua`](./docs/micro.md)|
|
||||||
|
| [Nano](https://www.nano-editor.org/) | ❌ | ❌ | ✅ | — |
|
||||||
|
| [Neovim](https://neovim.io/) | ✅ | ✅ | ✅ | [vivify-vim](https://github.com/jannis-baum/vivify.vim) |
|
||||||
|
| [Vi](https://en.wikipedia.org/wiki/Vi_(text_editor)) | ❌ | ❌ | ✅ | |
|
||||||
|
| [Vim](https://www.vim.org/) | ✅ | ✅ | ✅ | [vivify-vim](https://github.com/jannis-baum/vivify.vim) |
|
||||||
|
|
||||||
[How to add support for another editor](./CONTRIBUTING.md#adding-editor-support)
|
[How to add support for another editor](./CONTRIBUTING.md#adding-editor-support)
|
||||||
|
|
||||||
@@ -155,7 +186,7 @@ Edit `~/.config/notewrapper/config.json`. If it does not exist, it will be creat
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"directory": "~/Documents/Notes/",
|
"directory": ["~/Documents/Notes/", "/other/paths/"],
|
||||||
"render": true,
|
"render": true,
|
||||||
"jumpToEndOfFileOnLaunch": true,
|
"jumpToEndOfFileOnLaunch": true,
|
||||||
"editor": "neovim",
|
"editor": "neovim",
|
||||||
@@ -164,7 +195,10 @@ Edit `~/.config/notewrapper/config.json`. If it does not exist, it will be creat
|
|||||||
"newLineOnOpening": true,
|
"newLineOnOpening": true,
|
||||||
"backup": {
|
"backup": {
|
||||||
"enable": false,
|
"enable": false,
|
||||||
"directory": "/path/to/backup",
|
"directory": {
|
||||||
|
"~/Documents/Notes": "path/to/backup1",
|
||||||
|
"/other/paths/": "path/to/backup2"
|
||||||
|
},
|
||||||
"interval": "weekly",
|
"interval": "weekly",
|
||||||
"rsyncArgs": ["-Lqah", "--update"]
|
"rsyncArgs": ["-Lqah", "--update"]
|
||||||
}
|
}
|
||||||
@@ -173,18 +207,28 @@ Edit `~/.config/notewrapper/config.json`. If it does not exist, it will be creat
|
|||||||
|
|
||||||
### Fields
|
### Fields
|
||||||
|
|
||||||
* `directory`: root directory containing all vaults
|
* `directory`: Array of directories containing the vaults.
|
||||||
* `render`: enable/disable Vivify rendering
|
* `render`: enable/disable Vivify rendering
|
||||||
* `jumpToEndOfFileOnLaunch`: move cursor to end of file on open
|
* `jumpToEndOfFileOnLaunch`: move cursor to end of file on open
|
||||||
* `editor`: selected editor (must be supported)
|
* `editor`: selected editor (must be supported). If not set, it defaults to `$EDITOR`.
|
||||||
* `journalRegex`: regex used to detect journal files
|
* `journalRegex`: regex used to detect journal files
|
||||||
* `dateEntry`: format for journal entries (see `strftime`)
|
* `dateEntry`: format for journal entries (see `strftime`)
|
||||||
* `newLineOnOpening`: add a newline when opening a note
|
* `newLineOnOpening`: add a newline when opening a note
|
||||||
* `backup.enable`: enable automatic backups using `rsync`
|
* `backup.enable`: enable automatic backups using `rsync`
|
||||||
* `backup.directory`: destination directory for backups
|
* `backup.directory`: backup's destination for each directory
|
||||||
* `backup.interval`: backup frequency (`daily`, `weekly`, `monthly`, or integer)
|
* `backup.interval`: backup frequency (`daily`, `weekly`, `monthly`, or integer)
|
||||||
* `backup.rsyncArgs`: arguments passed to `rsync`
|
* `backup.rsyncArgs`: arguments passed to `rsync`
|
||||||
|
|
||||||
|
Note:
|
||||||
|
* Directories must end with `/`.
|
||||||
|
* All directories used must exist.
|
||||||
|
* Elements of `directory` and `backup.directory` must match exactly.
|
||||||
|
* Directories set in `directory` but not in `backup.directory` won't be backed up.
|
||||||
|
* Directories set in `backup.directory` but not in `directory` will be ignored.
|
||||||
|
* `journalRegex` must match if the file name ends with `.md` and if it doesn't.
|
||||||
|
* It is recommended to keep `-q` or `--quiet` flag in `rsyncArgs` to avoid interference with `ncurses`.
|
||||||
|
* If `rsync` fails, you will see it inside `ncurses`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Vivify configuration
|
## Vivify configuration
|
||||||
@@ -199,5 +243,7 @@ It is recommended to use a browser different from your main one for rendering.
|
|||||||
## Planned features
|
## Planned features
|
||||||
|
|
||||||
* [ ] A converter between journal types
|
* [ ] A converter between journal types
|
||||||
* [ ] Support multiple vault directories
|
* [x] Support multiple vault directories
|
||||||
* [ ] Port NoteWrapper to other editors
|
* [ ] Port NoteWrapper to other editors (non-exhaustive list of planned ports: `emacs -nw`, `ad`, flow-control, `ee`, `amp`, `dte`, `cano`, `mle`, `zee`, `ptext`, `kibi`, `ox`, `ne`, `dit`, `zile`, `moe`, `joe`, `pico`, `vis`)
|
||||||
|
* [x] Default to $EDITOR
|
||||||
|
* [ ] Refactoring the source code
|
||||||
|
|||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"directory": "~/Documents/Notes/",
|
|
||||||
"render": true,
|
|
||||||
"jumpToEndOfFileOnLaunch": true,
|
|
||||||
"editor": "neovim",
|
|
||||||
"journalRegex": ".*journal.*",
|
|
||||||
"dateEntry": "# %Y %m %d %a",
|
|
||||||
"newLineOnOpening": true,
|
|
||||||
"backup": {
|
|
||||||
"enable": false,
|
|
||||||
"directory": "/path/to/backup",
|
|
||||||
"interval": "weekly"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
{ pkgs ? import <nixpkgs> {} }:
|
|
||||||
|
|
||||||
pkgs.stdenv.mkDerivation {
|
|
||||||
pname = "notewrapper";
|
|
||||||
version = "1.0";
|
|
||||||
|
|
||||||
src = ./.;
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs.pkg-config
|
|
||||||
];
|
|
||||||
|
|
||||||
buildInputs = [
|
|
||||||
pkgs.ncurses
|
|
||||||
pkgs.cjson
|
|
||||||
pkgs.fzf
|
|
||||||
pkgs.gnused
|
|
||||||
pkgs.ripgrep
|
|
||||||
];
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
make
|
|
||||||
'';
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out/bin
|
|
||||||
cp notewrapper $out/bin/
|
|
||||||
'';
|
|
||||||
|
|
||||||
meta = with pkgs.lib; {
|
|
||||||
description = " Journaling and notetaking TUI wrapper on top of neovim, vim and nano.";
|
|
||||||
license = licenses.gpl3Only;
|
|
||||||
platforms = platforms.linux;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
You must add the following code to your `init.lua`:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local buffer = import("micro/buffer")
|
||||||
|
local shell = import("micro/shell")
|
||||||
|
|
||||||
|
function vivifyOpen(buf)
|
||||||
|
local cursor = buf:GetActiveCursor()
|
||||||
|
local line = cursor.Loc.Y + 1
|
||||||
|
|
||||||
|
shell.ExecCommand("viv", string.format("%s:%d", buf.AbsPath, line))
|
||||||
|
end
|
||||||
|
|
||||||
|
function onBufferOpen(buf)
|
||||||
|
if os.getenv("MICRO_VIVIFY") ~= "1" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if buf.Type.Kind == buffer.BTDefault then
|
||||||
|
vivifyOpen(buf)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
This enables Vivify to run automatically when a file is opened, effectively simulating “startup” behavior (since Micro does not provide a true startup command hook for this use case).
|
||||||
|
|
||||||
|
Big thanks to @Andriamanitra for answering [my questions](https://github.com/micro-editor/micro/discussions/4078).
|
||||||
|
|
||||||
Generated
+61
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1779560665,
|
||||||
|
"narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
description = "NoteWrapper";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils}:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
in {
|
||||||
|
packages.default = pkgs.stdenv.mkDerivation (finalAttrs: {
|
||||||
|
pname = "notewrapper";
|
||||||
|
version = "v2.2.1";
|
||||||
|
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
pkg-config
|
||||||
|
gnumake
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
ncurses
|
||||||
|
cjson
|
||||||
|
vivify
|
||||||
|
rsync
|
||||||
|
ripgrep
|
||||||
|
fzf
|
||||||
|
gnused
|
||||||
|
];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
make VERSION=${finalAttrs.version}
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp notewrapper $out/bin/
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
cjson
|
||||||
|
ncurses
|
||||||
|
pkg-config
|
||||||
|
gdb
|
||||||
|
valgrind
|
||||||
|
clang-tools
|
||||||
|
gnumake
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
apps.default = {
|
||||||
|
type = "app";
|
||||||
|
program = "${self.packages.${system}.default}/bin/notewrapper";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
CC := gcc
|
CC := gcc
|
||||||
|
|
||||||
CFLAGS := -Wall -Wextra -O2 \
|
VERSION := $(shell git describe --tags --always --dirty)
|
||||||
|
|
||||||
|
CFLAGS := -Wall -Wextra -Werror -O2 \
|
||||||
|
-DVERSION=\"$(VERSION)\" \
|
||||||
$(shell pkg-config --cflags libcjson ncurses)
|
$(shell pkg-config --cflags libcjson ncurses)
|
||||||
|
|
||||||
LDFLAGS := $(shell pkg-config --libs libcjson ncurses)
|
LDFLAGS := $(shell pkg-config --libs libcjson ncurses)
|
||||||
@@ -20,3 +23,17 @@ run: $(TARGET)
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGET)
|
rm -f $(TARGET)
|
||||||
|
|
||||||
|
test: $(TARGET)
|
||||||
|
@timeout 5s ./$(TARGET); \
|
||||||
|
status=$$?; \
|
||||||
|
if [ $$status -eq 124 ]; then \
|
||||||
|
echo "Program ran for 5 seconds without crashing ✅"; \
|
||||||
|
exit 0; \
|
||||||
|
elif [ $$status -ne 0 ]; then \
|
||||||
|
echo "Program crashed ❌"; \
|
||||||
|
exit 1; \
|
||||||
|
else \
|
||||||
|
echo "Program exited cleanly ✅"; \
|
||||||
|
exit 0; \
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
{ pkgs ? import <nixpkgs> {} }:
|
|
||||||
|
|
||||||
pkgs.mkShell {
|
|
||||||
buildInputs = [
|
|
||||||
pkgs.cjson
|
|
||||||
pkgs.ncurses
|
|
||||||
pkgs.pkg-config
|
|
||||||
pkgs.gdb
|
|
||||||
pkgs.valgrind
|
|
||||||
pkgs.kdePackages.kcachegrind
|
|
||||||
];
|
|
||||||
}
|
|
||||||
+311
-153
@@ -1,6 +1,6 @@
|
|||||||
|
#include "notes.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "notes.h"
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int shouldDebug = 0;
|
int shouldDebug = 0;
|
||||||
@@ -14,10 +14,8 @@ int main(int argc, char *argv[]) {
|
|||||||
shouldDebug = 1;
|
shouldDebug = 1;
|
||||||
|
|
||||||
} else if (strcmp(arg, "--config") == 0) {
|
} else if (strcmp(arg, "--config") == 0) {
|
||||||
error(i + 1 == argc, "user",
|
error(i + 1 == argc, "user", "Missing argument. Use --config <path>");
|
||||||
"Missing argument. Use --config <path>");
|
|
||||||
overwriteConfigPath = ++i; // consume argument
|
overwriteConfigPath = ++i; // consume argument
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short options (allow grouping like -V)
|
// Short options (allow grouping like -V)
|
||||||
@@ -32,10 +30,8 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
case 'c':
|
case 'c':
|
||||||
// must NOT be combined (needs argument)
|
// must NOT be combined (needs argument)
|
||||||
error(arg[j+1] != '\0', "user",
|
error(arg[j + 1] != '\0', "user", "-c cannot be combined (use -c <path>)");
|
||||||
"-c cannot be combined (use -c <path>)");
|
error(i + 1 == argc, "user", "Missing argument for -c");
|
||||||
error(i + 1 == argc, "user",
|
|
||||||
"Missing argument for -c");
|
|
||||||
|
|
||||||
overwriteConfigPath = ++i; // consume argument
|
overwriteConfigPath = ++i; // consume argument
|
||||||
goto arg_next;
|
goto arg_next;
|
||||||
@@ -47,9 +43,8 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
arg_next:
|
arg_next:;
|
||||||
;
|
}
|
||||||
}
|
|
||||||
// gets the home directory
|
// gets the home directory
|
||||||
struct passwd *pw = getpwuid(getuid());
|
struct passwd *pw = getpwuid(getuid());
|
||||||
const char *homedir = pw->pw_dir;
|
const char *homedir = pw->pw_dir;
|
||||||
@@ -69,27 +64,31 @@ arg_next:
|
|||||||
}
|
}
|
||||||
// check if the config file exists
|
// check if the config file exists
|
||||||
struct stat st = {0};
|
struct stat st = {0};
|
||||||
error(stat(configPath, &st) == -1, "user", "The config file %s does not exist.\nMaybe try the default path to the config ~/.config/notewrapper/config.json\nOr if you used the flag -c or --config, verifiy that you point to the correct file.", configPath); // if the config directory does not exist
|
error(stat(configPath, &st) == -1, "user",
|
||||||
|
"The config file %s does not exist.\nMaybe try the default path to the config "
|
||||||
|
"~/.config/notewrapper/config.json\nOr if you used the flag -c or --config, verifiy that "
|
||||||
|
"you point to the correct file.",
|
||||||
|
configPath); // if the config directory does not exist
|
||||||
|
|
||||||
// opens config.json
|
// opens config.json
|
||||||
FILE *f = fopen(configPath, "r");
|
FILE *f = fopen(configPath, "r");
|
||||||
error(!f, "program", "The config file does exist, but can not be open.");
|
error(!f, "program", "The config file does exist, but can not be open.");
|
||||||
|
|
||||||
// loads and read the config file
|
// loads and read the config file
|
||||||
//gets the size
|
// gets the size
|
||||||
fseek(f, 0, SEEK_END);
|
fseek(f, 0, SEEK_END);
|
||||||
size_t size = ftell(f);
|
size_t size = ftell(f);
|
||||||
rewind(f);
|
rewind(f);
|
||||||
//gets the data
|
// gets the data
|
||||||
char *data = malloc(size+1);
|
char *data = malloc(size + 1);
|
||||||
error(!data, "program", "malloc failed allocating memory for the variable data.");
|
error(!data, "program", "malloc failed allocating memory for the variable data.");
|
||||||
size_t readBytes = fread(data, 1, size, f); // 1 --> size of each item
|
size_t readBytes = fread(data, 1, size, f); // 1 --> size of each item
|
||||||
|
|
||||||
if (readBytes!=size) {
|
if (readBytes != size) {
|
||||||
free(data);
|
free(data);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
error(readBytes!=size, "program", "Failed to read config file (%s) (%zu bytes read, expected %ld)", configPath, readBytes, size);
|
error(readBytes != size, "program", "Failed to read config file (%s) (%zu bytes read, expected %ld)", configPath, readBytes, size);
|
||||||
data[size] = '\0';
|
data[size] = '\0';
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
@@ -97,23 +96,39 @@ arg_next:
|
|||||||
debug("Parsing the JSON config");
|
debug("Parsing the JSON config");
|
||||||
|
|
||||||
cJSON *json = cJSON_Parse(data);
|
cJSON *json = cJSON_Parse(data);
|
||||||
if (!json) {free(data);}
|
if (!json) {
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
error(!json, "program", "JSON parse error");
|
error(!json, "program", "JSON parse error");
|
||||||
|
|
||||||
|
// Parse all of the directories which will( or do) contain the vaults
|
||||||
cJSON *dirJson = cJSON_GetObjectItem(json, "directory");
|
cJSON *dirJson = cJSON_GetObjectItem(json, "directory");
|
||||||
char *notesDirectoryString = malloc(PATH_MAX);
|
error(dirJson && !cJSON_IsArray(dirJson), "user", "In %s, \"directory\" is missing or isn't an array", configPath);
|
||||||
if (dirJson && cJSON_IsString(dirJson) && cJSON_GetStringValue(dirJson) != NULL) { // we replaced all the ->valuestr and ->valueint to cJSON_GetStringValue() and cJSON_GetNumberValue()
|
int numDirectories = cJSON_GetArraySize(dirJson);
|
||||||
char *rawPath = cJSON_GetStringValue(dirJson);
|
debug("In %s, detected %d paths in \"directory\"", configPath, numDirectories);
|
||||||
if (rawPath[0] == '~') { // expands ~ in the path
|
error(numDirectories == 0, "user", "In %s, \"directory\" is an empty array.", configPath);
|
||||||
snprintf(notesDirectoryString, PATH_MAX, "%s/%s", homedir, rawPath+1);
|
|
||||||
|
char **directoriesArray = malloc(numDirectories * sizeof(char *));
|
||||||
|
debug("Directories:");
|
||||||
|
cJSON *tempEntry = NULL;
|
||||||
|
int i = 0;
|
||||||
|
cJSON_ArrayForEach(tempEntry,
|
||||||
|
dirJson) { // iterate over all the elements of the array _i. e._ over all the dirs
|
||||||
|
if (tempEntry && cJSON_IsString(tempEntry)) {
|
||||||
|
if (cJSON_GetStringValue(tempEntry)[0] == '~') { // we must expand ~
|
||||||
|
char *tempUnFixedName = cJSON_GetStringValue(tempEntry);
|
||||||
|
tempUnFixedName++; // shifts and removes the ~
|
||||||
|
directoriesArray[i] = malloc(PATH_MAX);
|
||||||
|
debug("~ in %s was expanded to %s", tempUnFixedName, homedir);
|
||||||
|
snprintf(directoriesArray[i], PATH_MAX, "%s%s", homedir, tempUnFixedName);
|
||||||
} else {
|
} else {
|
||||||
notesDirectoryString = rawPath;
|
directoriesArray[i] = strdup(cJSON_GetStringValue(tempEntry));
|
||||||
|
altDebug("%s\n", directoriesArray[i]);
|
||||||
}
|
}
|
||||||
debug("In %s, \"directory\" was set to %s.", configPath, notesDirectoryString);
|
|
||||||
} else {
|
} else {
|
||||||
// default vault path if it is not set in the config
|
error(1, "user", "In %s, in \"directory\", invalid type of one of the entries.", configPath);
|
||||||
snprintf(notesDirectoryString, PATH_MAX, "%s/Documents/Notes/", homedir);
|
}
|
||||||
debug("In %s, \"directory\" wasn't set or we encountered a abnormal type. Defaulting to %s.", configPath, notesDirectoryString);
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch the render and jumpToEnfOfFileOnLaunch bools
|
// fetch the render and jumpToEnfOfFileOnLaunch bools
|
||||||
@@ -133,17 +148,26 @@ arg_next:
|
|||||||
debug("In %s, \"jumpToEndOfFileOnLaunch\" was set to %d.", configPath, shouldJumpToEnd);
|
debug("In %s, \"jumpToEndOfFileOnLaunch\" was set to %d.", configPath, shouldJumpToEnd);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
debug("In %s, \"jumpToEndOfFileOnLaunch\" wasn't set or we encountered a abnormal type. Defaulting to true.", configPath);
|
debug("In %s, \"jumpToEndOfFileOnLaunch\" wasn't set or we encountered a abnormal type. "
|
||||||
|
"Defaulting to true.",
|
||||||
|
configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *editorToOpen = "neovim"; // default
|
char *editorToOpen = getenv("EDITOR"); // default to $EDITOR
|
||||||
|
int defaultEditor = 1; // this will be used in the warning if the editor does not exists or is unsupported.
|
||||||
cJSON *editorToOpenJSON = cJSON_GetObjectItem(json, "editor");
|
cJSON *editorToOpenJSON = cJSON_GetObjectItem(json, "editor");
|
||||||
if (editorToOpenJSON && cJSON_IsString(editorToOpenJSON)) {
|
if (editorToOpenJSON && cJSON_IsString(editorToOpenJSON)) {
|
||||||
editorToOpen = strdup(cJSON_GetStringValue(editorToOpenJSON)); // we must strdup and not just = as we will free all the json after (before parsing args)
|
editorToOpen = strdup(cJSON_GetStringValue(editorToOpenJSON)); // we must strdup and not just = as we will free all the json after
|
||||||
|
// (before parsing args)
|
||||||
|
defaultEditor = 0;
|
||||||
debug("In %s, \"editor\" was set to %s.", configPath, editorToOpen);
|
debug("In %s, \"editor\" was set to %s.", configPath, editorToOpen);
|
||||||
error(!isStringInArray(editorToOpen, supportedEditor, numEditors), "user", "%s (fetched from config.json) is not a supported editor.", editorToOpen);
|
// error(!isStringInArray(editorToOpen, supportedEditor, numEditors), "user", "%s (fetched
|
||||||
|
// from config.json) is not a supported editor.", editorToOpen); // we check if editor is
|
||||||
|
// supported at the end
|
||||||
} else {
|
} else {
|
||||||
debug("In %s, \"editor\" wasn't set or we encountered a abnormal type. Defaulting to %s.", configPath, editorToOpen);
|
debug("In %s, \"editor\" wasn't set or we encountered a abnormal type. Defaulting to "
|
||||||
|
"$EDITOR (%s).\n P.S. this might still be overwritten by -e or --editor.",
|
||||||
|
configPath, editorToOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON *journalRegexJSON = cJSON_GetObjectItem(json, "journalRegex");
|
cJSON *journalRegexJSON = cJSON_GetObjectItem(json, "journalRegex");
|
||||||
@@ -152,10 +176,12 @@ arg_next:
|
|||||||
journalRegex = strdup(cJSON_GetStringValue(journalRegexJSON));
|
journalRegex = strdup(cJSON_GetStringValue(journalRegexJSON));
|
||||||
debug("In %s, \"journalRegex\" was set to %s.", configPath, journalRegex);
|
debug("In %s, \"journalRegex\" was set to %s.", configPath, journalRegex);
|
||||||
} else {
|
} else {
|
||||||
debug("In %s, \"journalRegex\" wasn't set or we encountered a abnormal type. Defaulting to %s.", configPath, journalRegex);
|
debug("In %s, \"journalRegex\" wasn't set or we encountered a abnormal type. Defaulting to "
|
||||||
|
"%s.",
|
||||||
|
configPath, journalRegex);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *timeFormat = "# \%Y \%m \%d \%a";// default
|
char *timeFormat = "# \%Y \%m \%d \%a"; // default
|
||||||
cJSON *timeFormatJSON = cJSON_GetObjectItem(json, "dateEntry");
|
cJSON *timeFormatJSON = cJSON_GetObjectItem(json, "dateEntry");
|
||||||
if (timeFormatJSON && cJSON_IsString(timeFormatJSON)) {
|
if (timeFormatJSON && cJSON_IsString(timeFormatJSON)) {
|
||||||
timeFormat = strdup(cJSON_GetStringValue(timeFormatJSON));
|
timeFormat = strdup(cJSON_GetStringValue(timeFormatJSON));
|
||||||
@@ -171,12 +197,17 @@ arg_next:
|
|||||||
newLineOnOpening = cJSON_IsTrue(newLineOnOpeningJSON) ? 1 : 0;
|
newLineOnOpening = cJSON_IsTrue(newLineOnOpeningJSON) ? 1 : 0;
|
||||||
debug("In %s, \"newLineOnOpening\" was set to %d.", configPath, newLineOnOpening);
|
debug("In %s, \"newLineOnOpening\" was set to %d.", configPath, newLineOnOpening);
|
||||||
} else {
|
} else {
|
||||||
debug("In %s, \"newLineOnOpening\" wasn't set or we encountered a abnormal type. Defaulting to true.", configPath);
|
debug("In %s, \"newLineOnOpening\" wasn't set or we encountered a abnormal type. "
|
||||||
|
"Defaulting to true.",
|
||||||
|
configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
int doesBackup = 0;
|
int doesBackup = 0;
|
||||||
int interval = 0; // this is an int. But some times it will be inputed a string. We must translate it.
|
int interval = 0; // this is an int. But some times it will be inputed a string. We must translate it.
|
||||||
char *pathToBackup = malloc(PATH_MAX);
|
// to handle linking each directory to its backup path. We create an array of char*. If we want
|
||||||
|
// to backup the ith directory, the ith pointer will point to the path. Else, we set the ith
|
||||||
|
// pointer to NULL.
|
||||||
|
char **backupDirectoriesArray = malloc(numDirectories * sizeof(char *));
|
||||||
char **rsyncArgs = NULL;
|
char **rsyncArgs = NULL;
|
||||||
int rsyncArgsNumber = 0;
|
int rsyncArgsNumber = 0;
|
||||||
cJSON *backupJSON = cJSON_GetObjectItem(json, "backup");
|
cJSON *backupJSON = cJSON_GetObjectItem(json, "backup");
|
||||||
@@ -185,19 +216,47 @@ arg_next:
|
|||||||
if (doesBackupJSON && cJSON_IsBool(doesBackupJSON)) {
|
if (doesBackupJSON && cJSON_IsBool(doesBackupJSON)) {
|
||||||
doesBackup = cJSON_IsTrue(doesBackupJSON) ? 1 : 0;
|
doesBackup = cJSON_IsTrue(doesBackupJSON) ? 1 : 0;
|
||||||
debug("doesBackup is set to %d", doesBackup);
|
debug("doesBackup is set to %d", doesBackup);
|
||||||
// handles the path to the backup
|
if (!doesBackup) {
|
||||||
cJSON *pathToBackupJSON = cJSON_GetObjectItem(backupJSON, "directory");
|
debug("a backup is not needed. Skipping parsing the rest of the json");
|
||||||
if (pathToBackupJSON && cJSON_IsString(pathToBackupJSON)) {
|
goto backup_config_end; // easier to just go to rather than do a big if statement
|
||||||
char *temp = cJSON_GetStringValue(pathToBackupJSON); // we name it temp. As we can't directly set pathToBackup because snprintf doesn't like when a pointer is both an arg and the destination // temp should be freed when free(json), because cJSON_GetStringValue returns a pointer.
|
|
||||||
if (temp[0] == '~') { // expands ~
|
|
||||||
temp++; // we shift by one to remove ~
|
|
||||||
snprintf(pathToBackup, PATH_MAX, "%s/%s", homedir, temp);
|
|
||||||
debug("~ was expanded to %s\nThe backup path is %s", homedir, pathToBackup);
|
|
||||||
} else {
|
|
||||||
pathToBackup = strndup(temp, PATH_MAX); // temp will be freed later so strndup
|
|
||||||
debug("Directory for backup is set to %s in %s", pathToBackup, configPath);
|
|
||||||
}
|
}
|
||||||
} else {error(1, "user", "%s did not contained a directory inside the backup section or the value is from an unexpected type", configPath);}
|
|
||||||
|
// handles the path to the backup for each directory
|
||||||
|
cJSON *pathToBackupJSON = cJSON_GetObjectItem(backupJSON, "directory");
|
||||||
|
debug("%s", cJSON_Print(pathToBackupJSON));
|
||||||
|
error(pathToBackupJSON == NULL &&
|
||||||
|
!(cJSON_IsObject(pathToBackupJSON) || cJSON_IsArray(pathToBackupJSON) || cJSON_IsString(pathToBackupJSON) || cJSON_IsNumber(pathToBackupJSON) || cJSON_IsBool(pathToBackupJSON)),
|
||||||
|
"user", "In %s, incorrect type for \"directory\". It must be a JSON object.",
|
||||||
|
configPath); // for some reason cJSON_IsObject does not work. So we must do it
|
||||||
|
// like this.
|
||||||
|
|
||||||
|
// we can't just iterate over directoriesArray as we replaced ~ to $HOME
|
||||||
|
// we must iterate over the json entries
|
||||||
|
cJSON *directoryEntry = NULL;
|
||||||
|
int i = 0;
|
||||||
|
cJSON_ArrayForEach(directoryEntry,
|
||||||
|
dirJson) { // iterate over all the elements of the array _i. e._ over all the dirs
|
||||||
|
// we don't need to type error handle as we did it before
|
||||||
|
char *directoryEntryPath = cJSON_GetStringValue(directoryEntry);
|
||||||
|
cJSON *pathToBackupForIthDirectoryJSON = cJSON_GetObjectItem(pathToBackupJSON, directoryEntryPath);
|
||||||
|
if (pathToBackupForIthDirectoryJSON) { // if the entry exist
|
||||||
|
char *textPath = cJSON_GetStringValue(pathToBackupForIthDirectoryJSON);
|
||||||
|
if (textPath[0] == '~') {
|
||||||
|
textPath++; // cuts the ~
|
||||||
|
backupDirectoriesArray[i] = malloc(PATH_MAX);
|
||||||
|
error(backupDirectoriesArray[i] == NULL, "program", "malloc failed");
|
||||||
|
snprintf(backupDirectoriesArray[i], PATH_MAX, "%s%s", homedir, textPath);
|
||||||
|
} else {
|
||||||
|
backupDirectoriesArray[i] = strdup(textPath);
|
||||||
|
}
|
||||||
|
debug("%s will be backed up to %s", directoriesArray[i], backupDirectoriesArray[i]);
|
||||||
|
} else { // we set it to NULL to be sure we won't backup it
|
||||||
|
backupDirectoriesArray[i] = NULL;
|
||||||
|
debug("%s won't be backed up", directoriesArray[i]);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
// handles the interval of backup
|
// handles the interval of backup
|
||||||
cJSON *intervalJSON = cJSON_GetObjectItem(backupJSON, "interval");
|
cJSON *intervalJSON = cJSON_GetObjectItem(backupJSON, "interval");
|
||||||
if (intervalJSON && cJSON_IsString(intervalJSON)) {
|
if (intervalJSON && cJSON_IsString(intervalJSON)) {
|
||||||
@@ -211,31 +270,57 @@ arg_next:
|
|||||||
} else if (strcmp(temp, "monthly") == 0) {
|
} else if (strcmp(temp, "monthly") == 0) {
|
||||||
interval = MONTHLY;
|
interval = MONTHLY;
|
||||||
debug("interval in %s is set to \"monthly\" which is %d", configPath, MONTHLY);
|
debug("interval in %s is set to \"monthly\" which is %d", configPath, MONTHLY);
|
||||||
} else {error(1, "user", "Unexpected string %s for entry \"interval\" in %s. You must put an int (number of seconds) or \"daily\" or \"weekly\" or \"monthly\".", intervalJSON->valuestring, configPath);}
|
} else {
|
||||||
|
error(1, "user",
|
||||||
|
"Unexpected string %s for entry \"interval\" in %s. You must put an int "
|
||||||
|
"(number of seconds) or \"daily\" or \"weekly\" or \"monthly\".",
|
||||||
|
intervalJSON->valuestring, configPath);
|
||||||
|
}
|
||||||
} else if (intervalJSON && cJSON_IsNumber(intervalJSON)) {
|
} else if (intervalJSON && cJSON_IsNumber(intervalJSON)) {
|
||||||
interval = (int)cJSON_GetNumberValue(intervalJSON);
|
interval = (int)cJSON_GetNumberValue(intervalJSON);
|
||||||
debug("interval in %s is set to %d", configPath, interval);
|
debug("interval in %s is set to %d", configPath, interval);
|
||||||
} else {error(1, "user", "%s did not contained an interval value inside the backup section or the value is from an unexpected type", configPath);}
|
} else {
|
||||||
} else{error(1, "user", "%s did not contained a enable value inside the backup section or the value is from an unexpected type", configPath);}
|
error(1, "user",
|
||||||
|
"%s did not contained an interval value inside the backup section or the "
|
||||||
|
"value is from an unexpected type",
|
||||||
|
configPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error(1, "user",
|
||||||
|
"%s did not contained a enable value inside the backup section or the value is "
|
||||||
|
"from an unexpected type",
|
||||||
|
configPath);
|
||||||
|
}
|
||||||
// handle rsyncs array of arguments.
|
// handle rsyncs array of arguments.
|
||||||
cJSON *rsyncArgsJSON = cJSON_GetObjectItem(backupJSON, "rsyncArgs");
|
cJSON *rsyncArgsJSON = cJSON_GetObjectItem(backupJSON, "rsyncArgs");
|
||||||
if (rsyncArgsJSON && cJSON_IsArray(rsyncArgsJSON)) { // if it is what we expected
|
if (rsyncArgsJSON && cJSON_IsArray(rsyncArgsJSON)) { // if it is what we expected
|
||||||
rsyncArgsNumber = cJSON_GetArraySize(rsyncArgsJSON);
|
rsyncArgsNumber = cJSON_GetArraySize(rsyncArgsJSON);
|
||||||
|
debug("In %s, rsyncArgsNumber is calculated to %d.", configPath, rsyncArgsNumber);
|
||||||
rsyncArgs = realloc(rsyncArgs, (size_t)rsyncArgsNumber);
|
rsyncArgs = realloc(rsyncArgs, (size_t)rsyncArgsNumber);
|
||||||
|
debug("In %c, rsyncArgs are:", configPath);
|
||||||
for (int i = 0; i < rsyncArgsNumber; i++) {
|
for (int i = 0; i < rsyncArgsNumber; i++) {
|
||||||
cJSON *argJSON = cJSON_GetArrayItem(rsyncArgsJSON, i);
|
cJSON *argJSON = cJSON_GetArrayItem(rsyncArgsJSON, i);
|
||||||
if (argJSON && cJSON_IsString(argJSON)) {
|
if (argJSON && cJSON_IsString(argJSON)) {
|
||||||
rsyncArgs[i] = cJSON_GetStringValue(argJSON);
|
rsyncArgs[i] = strdup(cJSON_GetStringValue(argJSON));
|
||||||
} else {error(1, "user", "One element in rsyncArgs array in %s is not a string", configPath);}
|
altDebug("%s\n", rsyncArgs[i]);
|
||||||
}
|
|
||||||
} else {error(1, "user", "%s did not contained a rsyncArgs array inside the backup section or the value is from an unexpected type", configPath);}
|
|
||||||
} else {
|
} else {
|
||||||
debug("In %s, \"backup\" wasn't set or we encountered a abnormal type. Defaulting to {\"enable\": false}.", configPath);
|
error(1, "user", "One element in rsyncArgs array in %s is not a string", configPath);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error(1, "user",
|
||||||
|
"%s did not contained a rsyncArgs array inside the backup section or the value "
|
||||||
|
"is from an unexpected type",
|
||||||
|
configPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug("In %s, \"backup\" wasn't set or we encountered a abnormal type. Defaulting to "
|
||||||
|
"{\"enable\": false}.",
|
||||||
|
configPath);
|
||||||
|
}
|
||||||
|
backup_config_end:
|
||||||
|
|
||||||
|
// cleans up
|
||||||
|
|
||||||
//cleans up
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
free(data);
|
free(data);
|
||||||
debug("Finished parsing the JSON config");
|
debug("Finished parsing the JSON config");
|
||||||
@@ -243,13 +328,15 @@ arg_next:
|
|||||||
//---------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// flags and arguments overwrite the config
|
// flags and arguments overwrite the config
|
||||||
debug("Parsing the attribute flags.\n This flags might overwrite the options in the config file.");
|
debug("Parsing the attribute flags.\n This flags might overwrite the options in the config "
|
||||||
// thoses bypasses are used if some specific flags are passed as args. This allows to bypass the TUI selectors
|
"file.");
|
||||||
|
// thoses bypasses are used if some specific flags are passed as args. This allows to bypass the
|
||||||
|
// TUI selectors
|
||||||
int bypassSelectionVault = 0;
|
int bypassSelectionVault = 0;
|
||||||
char *bypassSelectionVaultValue = NULL;
|
char *bypassSelectionVaultValue = NULL;
|
||||||
int bypassSelectionNote = 0;
|
int bypassSelectionNote = 0;
|
||||||
char *bypassSelectionNoteValue = NULL;
|
char *bypassSelectionNoteValue = NULL;
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
char *arg = argv[i];
|
char *arg = argv[i];
|
||||||
|
|
||||||
if (strncmp(arg, "--", 2) == 0) {
|
if (strncmp(arg, "--", 2) == 0) {
|
||||||
@@ -263,14 +350,10 @@ for (int i = 1; i < argc; i++) {
|
|||||||
overwriteConfigPath = ++i;
|
overwriteConfigPath = ++i;
|
||||||
debug("--config set to %s", argv[i]);
|
debug("--config set to %s", argv[i]);
|
||||||
|
|
||||||
} else if (strcmp(arg, "--directory") == 0) {
|
|
||||||
error(i + 1 == argc, "user", "Missing argument for --directory");
|
|
||||||
notesDirectoryString = argv[++i];
|
|
||||||
debug("--directory set to %s", notesDirectoryString);
|
|
||||||
|
|
||||||
} else if (strcmp(arg, "--editor") == 0) {
|
} else if (strcmp(arg, "--editor") == 0) {
|
||||||
error(i + 1 == argc, "user", "Missing argument for --editor");
|
error(i + 1 == argc, "user", "Missing argument for --editor");
|
||||||
editorToOpen = argv[++i];
|
editorToOpen = argv[++i];
|
||||||
|
defaultEditor = 0;
|
||||||
debug("--editor set to %s", editorToOpen);
|
debug("--editor set to %s", editorToOpen);
|
||||||
|
|
||||||
} else if (strcmp(arg, "--note") == 0) {
|
} else if (strcmp(arg, "--note") == 0) {
|
||||||
@@ -305,21 +388,25 @@ for (int i = 1; i < argc; i++) {
|
|||||||
printf("Usage: notewrapper [options]\n");
|
printf("Usage: notewrapper [options]\n");
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -c, --config <path/to/config> Specify the config file.\n");
|
printf(" -c, --config <path/to/config> Specify the config file.\n");
|
||||||
printf(" -d, --directory <path/to/directory> Specify the vaults' directory.\n");
|
|
||||||
printf(" -h, --help Display this message.\n");
|
printf(" -h, --help Display this message.\n");
|
||||||
printf(" -e, --editor Specify the editor to open.\n");
|
printf(" -e, --editor Specify the editor to open.\n");
|
||||||
printf(" -j, --jump Jumps to the end of the file on opening.\n");
|
printf(" -j, --jump Jumps to the end of the file "
|
||||||
printf(" -J, --no-jump Do not jump to the end of the file\n");
|
"on opening.\n");
|
||||||
printf(" -n, --note <note's name> Specify the note (or journal).\n");
|
printf(" -J, --no-jump Do not jump to the end of "
|
||||||
printf(" -r, --render Renders the note with Vivify.\n");
|
"the file\n");
|
||||||
|
printf(" -n, --note <note's name> Specify the note (or "
|
||||||
|
"journal).\n");
|
||||||
|
printf(" -r, --render Renders the note with "
|
||||||
|
"Vivify.\n");
|
||||||
printf(" -R, --no-render Do not render.\n");
|
printf(" -R, --no-render Do not render.\n");
|
||||||
printf(" -v, --vault <vault's name> Specify the vault.\n");
|
printf(" -v, --vault <vault's name> Specify the vault.\n");
|
||||||
printf(" --version Display the program version and the GPL3 notice.\n");
|
printf(" --version Display the program version "
|
||||||
|
"and the GPL3 notice.\n");
|
||||||
printf(" -V, --verbose Show debug information.\n");
|
printf(" -V, --verbose Show debug information.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
} else if (strcmp(arg, "--version") == 0) {
|
} else if (strcmp(arg, "--version") == 0) {
|
||||||
printf("NoteWrapper version 1.0\n");
|
printf("NoteWrapper %s\n", VERSION);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -363,44 +450,40 @@ for (int i = 1; i < argc; i++) {
|
|||||||
printf("Usage: notewrapper [options]\n");
|
printf("Usage: notewrapper [options]\n");
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -c, --config <path/to/config> Specify the config file.\n");
|
printf(" -c, --config <path/to/config> Specify the config file.\n");
|
||||||
printf(" -d, --directory <path/to/directory> Specify the vaults' directory.\n");
|
|
||||||
printf(" -h, --help Display this message.\n");
|
printf(" -h, --help Display this message.\n");
|
||||||
printf(" -e, --editor Specify the editor to open.\n");
|
printf(" -e, --editor Specify the editor to "
|
||||||
printf(" -j, --jump Jumps to the end of the file on opening.\n");
|
"open.\n");
|
||||||
printf(" -J, --no-jump Do not jump to the end of the file\n");
|
printf(" -j, --jump Jumps to the end of the "
|
||||||
printf(" -n, --note <note's name> Specify the note (or journal).\n");
|
"file on opening.\n");
|
||||||
printf(" -r, --render Renders the note with Vivify.\n");
|
printf(" -J, --no-jump Do not jump to the end "
|
||||||
|
"of the file\n");
|
||||||
|
printf(" -n, --note <note's name> Specify the note (or "
|
||||||
|
"journal).\n");
|
||||||
|
printf(" -r, --render Renders the note with "
|
||||||
|
"Vivify.\n");
|
||||||
printf(" -R, --no-render Do not render.\n");
|
printf(" -R, --no-render Do not render.\n");
|
||||||
printf(" -v, --vault <vault's name> Specify the vault.\n");
|
printf(" -v, --vault <vault's name> Specify the vault.\n");
|
||||||
printf(" --version Display the program version and the GPL3 notice.\n");
|
printf(" --version Display the program "
|
||||||
|
"version and the GPL3 notice.\n");
|
||||||
printf(" -V, --verbose Show debug information.\n");
|
printf(" -V, --verbose Show debug information.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// -------- flags with arguments (MUST be last in group) --------
|
// -------- flags with arguments (MUST be last in group) --------
|
||||||
case 'd':
|
|
||||||
case 'e':
|
case 'e':
|
||||||
case 'n':
|
case 'n':
|
||||||
case 'v':
|
case 'v':
|
||||||
case 'c': {
|
case 'c': {
|
||||||
|
|
||||||
error(arg[j + 1] != '\0',
|
error(arg[j + 1] != '\0', "user", "-%c must not be combined with other flags", opt);
|
||||||
"user",
|
|
||||||
"-%c must not be combined with other flags", opt);
|
|
||||||
|
|
||||||
error(i + 1 == argc,
|
error(i + 1 == argc, "user", "Missing argument for -%c", opt);
|
||||||
"user",
|
|
||||||
"Missing argument for -%c", opt);
|
|
||||||
|
|
||||||
char *value = argv[++i];
|
char *value = argv[++i];
|
||||||
|
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'd':
|
|
||||||
notesDirectoryString = value;
|
|
||||||
debug("-d set directory to %s", value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'e':
|
case 'e':
|
||||||
editorToOpen = value;
|
editorToOpen = value;
|
||||||
|
defaultEditor = 0;
|
||||||
debug("-e set editor to %s", value);
|
debug("-e set editor to %s", value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -434,47 +517,59 @@ for (int i = 1; i < argc; i++) {
|
|||||||
error(1, "user", "Unexpected argument \"%s\"", arg);
|
error(1, "user", "Unexpected argument \"%s\"", arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
next_arg:
|
next_arg:;
|
||||||
;
|
}
|
||||||
}
|
|
||||||
// if -n or --note is set but not -v or --vaults it gives an error
|
// if -n or --note is set but not -v or --vaults it gives an error
|
||||||
error(!bypassSelectionVault && bypassSelectionNote, "user", "If you want to specify the note, you must also specify the vault with -v <vault's name> or --vault <vault's name>.");
|
error(!bypassSelectionVault && bypassSelectionNote, "user",
|
||||||
|
"If you want to specify the note, you must also specify the vault with -v <vault's name> "
|
||||||
|
"or --vault <vault's name>.");
|
||||||
error(!doesEditorExist(editorToOpen, shouldDebug), "user", "%s is either not in your path or not installed.", editorToOpen);
|
|
||||||
debug("Finished parsing the attribute flags");
|
debug("Finished parsing the attribute flags");
|
||||||
|
|
||||||
|
isEditorValid(editorToOpen, defaultEditor,
|
||||||
|
shouldDebug); // check if editor is supported and if it is installed. If not, it
|
||||||
|
// will throw an error.
|
||||||
|
|
||||||
|
int *backupMessage = malloc(sizeof(int)); // Used later to set the backuping message.
|
||||||
|
*backupMessage = 0; // default don't show the message
|
||||||
|
int forceBackup = 0;
|
||||||
if (doesBackup) {
|
if (doesBackup) {
|
||||||
handleBackups(notesDirectoryString, pathToBackup, homedir, interval, (const char**)rsyncArgs, rsyncArgsNumber, shouldDebug);
|
debug("Handling normal backup");
|
||||||
|
forceBackup = 0;
|
||||||
|
handleBackups(directoriesArray, numDirectories, backupDirectoriesArray, homedir, interval, (const char **)rsyncArgs, rsyncArgsNumber, forceBackup, backupMessage, shouldDebug);
|
||||||
|
debug("backupMessage was set to %d by a normal backup", *backupMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
initscr(); //initialize ncurses
|
initscr(); // initialize ncurses
|
||||||
|
|
||||||
int shouldExit = 0;
|
int shouldExit = 0;
|
||||||
while(!shouldExit) {
|
while (!shouldExit) {
|
||||||
// this loop is the vault selector
|
// this loop is the vault selector
|
||||||
// select a vault
|
// select a vault
|
||||||
char *vaultSelected = NULL;
|
char *vaultSelected = NULL;
|
||||||
|
|
||||||
int vaultsCount = 0;
|
int vaultsCount = 0;
|
||||||
char **vaultsArray = getVaultsFromDirectory(notesDirectoryString, &vaultsCount, shouldDebug);
|
int *vaultsCountForEachDirectory = malloc(numDirectories * sizeof(int));
|
||||||
|
char **vaultsArray = getVaultsFromDirectories(directoriesArray, numDirectories, vaultsCountForEachDirectory, &vaultsCount,
|
||||||
|
shouldDebug); // when selecting each vault won't show the directory from which they
|
||||||
|
// come. We will find this later.
|
||||||
|
|
||||||
// bypass if -v or --vault is set
|
// bypass if -v or --vault is set
|
||||||
if (bypassSelectionVault) {
|
if (bypassSelectionVault) {
|
||||||
// bypasses the vault selection if the flag is -v. If the vault doesn't exist, just create a new one
|
// bypasses the vault selection if the flag is -v. If the vault doesn't exist, just
|
||||||
|
// create a new one
|
||||||
vaultSelected = bypassSelectionVaultValue;
|
vaultSelected = bypassSelectionVaultValue;
|
||||||
debug("bypassing vault selection");
|
debug("bypassing vault selection");
|
||||||
if (!isStringInArray(bypassSelectionVaultValue, (const char **)vaultsArray, vaultsCount)) { // we pass just vaultsCount and not vaultsCount + extraOptions to avoid matching with the extraOptions.
|
if (!isStringInArray(bypassSelectionVaultValue, (const char **)vaultsArray,
|
||||||
|
vaultsCount)) { // we pass just vaultsCount and not vaultsCount + extraOptions
|
||||||
|
// to avoid matching with the extraOptions.
|
||||||
debug("[BYPASS] %s did not exist. Creating a new vault");
|
debug("[BYPASS] %s did not exist. Creating a new vault");
|
||||||
goto vault_creation;
|
goto vault_creation;
|
||||||
} else {
|
} else {
|
||||||
goto note_selection;
|
goto note_selection;
|
||||||
debug("[BYPASS] %s does exist. Going to note selection");
|
debug("[BYPASS] %s does exist. Going to note selection");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qsort(vaultsArray, vaultsCount, sizeof(const char *), compareString); // sorts the vaults alphabetically
|
|
||||||
debug("┌--------------------------------\nAvailable vaults:");
|
debug("┌--------------------------------\nAvailable vaults:");
|
||||||
if (shouldDebug) {
|
if (shouldDebug) {
|
||||||
for (int i = 0; i < vaultsCount; i++) {
|
for (int i = 0; i < vaultsCount; i++) {
|
||||||
@@ -482,30 +577,49 @@ next_arg:
|
|||||||
}
|
}
|
||||||
altDebug("└ ------------------------------\n");
|
altDebug("└ ------------------------------\n");
|
||||||
}
|
}
|
||||||
|
int extraOptions = 3;
|
||||||
|
if (doesBackup) {
|
||||||
|
extraOptions++; // space for "Backup now" button
|
||||||
|
}
|
||||||
|
if (*backupMessage) {
|
||||||
|
extraOptions++;
|
||||||
|
}
|
||||||
|
vaultsArray = realloc(vaultsArray, (vaultsCount + extraOptions) * sizeof(char *)); // resize vaultsArray to fit the extra options
|
||||||
|
int index = 0;
|
||||||
|
vaultsArray[vaultsCount + index++] = "Create a new vault"; // some more options that are not vaults
|
||||||
|
vaultsArray[vaultsCount + index++] = "Settings";
|
||||||
|
if (doesBackup) {
|
||||||
|
vaultsArray[vaultsCount + index++] = "Backup now";
|
||||||
|
}
|
||||||
|
vaultsArray[vaultsCount + index++] = "Quit (Ctrl+C)";
|
||||||
|
if (*backupMessage) {
|
||||||
|
vaultsArray[vaultsCount + index++] = "A backup was launched. Do not immediatly close the program. After a few seconds, you can safely ignore this message.";
|
||||||
|
};
|
||||||
|
|
||||||
// adds "create a new vault" into the vaultsArray
|
*backupMessage = 0; // we reset it to show the message only once
|
||||||
const int extraOptions = 3;
|
|
||||||
vaultsArray = realloc(vaultsArray, (vaultsCount + extraOptions)*sizeof(char*)); // resize vaultsArray to fit the extra options
|
|
||||||
vaultsArray[vaultsCount] = "Create a new vault"; // some more options that are not vaults
|
|
||||||
vaultsArray[vaultsCount+1] = "Settings";
|
|
||||||
vaultsArray[vaultsCount+2] = "Quit (Ctrl+C)";
|
|
||||||
|
|
||||||
|
|
||||||
vaultSelected = ncursesSelect(vaultsArray, "Select vault to open (Use arrows or WASD, Enter to select):", vaultsCount, extraOptions, " ", "Or select an option below", "", shouldDebug);
|
vaultSelected = ncursesSelect(vaultsArray, "Select vault to open (Use arrows or WASD, Enter to select):", vaultsCount, extraOptions, " ", "Or select an option below", "", shouldDebug);
|
||||||
|
|
||||||
// now that we won't use vaultsArray in this iteration of the loop, we should free it and all its elements. (As this is memory in the heap and not the stack and thus is our responsability to manage)
|
// now that we won't use vaultsArray in this iteration of the loop, we should free it and
|
||||||
|
// all its elements. (As this is memory in the heap and not the stack and thus is our
|
||||||
|
// responsability to manage)
|
||||||
for (int i = 0; i < vaultsCount; i++) {
|
for (int i = 0; i < vaultsCount; i++) {
|
||||||
if (vaultSelected != vaultsArray[i]) { // i forgot this condition before. and freed the pointer equal to vaultSelected... So don't remove this condition
|
if (vaultSelected != vaultsArray[i]) { // i forgot this condition before. and freed the pointer equal to
|
||||||
free(vaultsArray[i]); // we must only free the vaults options and not the extraOptions to avoid segfault
|
// vaultSelected... So don't remove this condition
|
||||||
|
free(vaultsArray[i]); // we must only free the vaults options and not the
|
||||||
|
// extraOptions to avoid segfault
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(vaultsArray);
|
|
||||||
|
|
||||||
debug("Selected vault: %s", vaultSelected);
|
debug("Selected vault: %s", vaultSelected);
|
||||||
if (strcmp(vaultSelected,"Create a new vault") != 0 && strcmp(vaultSelected,"Settings") != 0 && strcmp(vaultSelected,"Quit (Ctrl+C)") != 0) {
|
if (strcmp(vaultSelected, "Create a new vault") != 0 && strcmp(vaultSelected, "Settings") != 0 && strcmp(vaultSelected, "Quit (Ctrl+C)") != 0 && strcmp(vaultSelected, "Backup now") != 0 &&
|
||||||
note_selection:
|
strcmp(vaultSelected, "A backup was launched. Do not immediatly close the program. After a few seconds, you can safely ignore this message.") != 0) {
|
||||||
bypassSelectionVault = 0; // we must reset bypassSelectionVault to not get stuck in a infinite loop of bypassing
|
note_selection:
|
||||||
|
bypassSelectionVault = 0; // we must reset bypassSelectionVault to not get stuck in a
|
||||||
|
// infinite loop of bypassing
|
||||||
int shouldChangeVault = 0;
|
int shouldChangeVault = 0;
|
||||||
|
// we must find the directory from which the vault comes again.
|
||||||
|
char *notesDirectoryString = getDirectoryFromVault(vaultSelected, vaultsArray, vaultsCount, vaultsCountForEachDirectory, directoriesArray, numDirectories, shouldDebug);
|
||||||
while (!shouldExit && !shouldChangeVault) {
|
while (!shouldExit && !shouldChangeVault) {
|
||||||
// this loop is the note selector
|
// this loop is the note selector
|
||||||
int filesCount = 0;
|
int filesCount = 0;
|
||||||
@@ -515,7 +629,7 @@ note_selection:
|
|||||||
char **journalArray = getJournalsFromVault(notesDirectoryString, vaultSelected, journalRegex, &journalCount, shouldDebug);
|
char **journalArray = getJournalsFromVault(notesDirectoryString, vaultSelected, journalRegex, &journalCount, shouldDebug);
|
||||||
|
|
||||||
// appends the journal at the end of filesArray
|
// appends the journal at the end of filesArray
|
||||||
filesArray = realloc(filesArray, (filesCount + journalCount)*sizeof(char*));
|
filesArray = realloc(filesArray, (filesCount + journalCount) * sizeof(char *));
|
||||||
for (int i = 0; i < journalCount; i++) {
|
for (int i = 0; i < journalCount; i++) {
|
||||||
filesArray[i + filesCount] = journalArray[i];
|
filesArray[i + filesCount] = journalArray[i];
|
||||||
}
|
}
|
||||||
@@ -530,18 +644,21 @@ note_selection:
|
|||||||
altDebug("└------------------------------\n");
|
altDebug("└------------------------------\n");
|
||||||
}
|
}
|
||||||
// adds options
|
// adds options
|
||||||
int extraNotesOptions = 4;
|
int extraNotesOptions = 5;
|
||||||
filesArray = realloc(filesArray, (filesCount + extraNotesOptions)*sizeof(char*)); // resize filesArray to fit the extra options
|
filesArray = realloc(filesArray, (filesCount + extraNotesOptions) * sizeof(char *)); // resize filesArray to fit the extra options
|
||||||
filesArray[filesCount] = "Create new note";
|
filesArray[filesCount] = "Create new note";
|
||||||
filesArray[filesCount+1] = "Back to vault selection";
|
filesArray[filesCount + 1] = "Search inside the vault";
|
||||||
filesArray[filesCount+2] = "Delete vault";
|
filesArray[filesCount + 2] = "Back to vault selection";
|
||||||
filesArray[filesCount+3] = "Quit (Ctrl+C)";
|
filesArray[filesCount + 3] = "Delete vault";
|
||||||
|
filesArray[filesCount + 4] = "Quit (Ctrl+C)";
|
||||||
char *noteSelected;
|
char *noteSelected;
|
||||||
// if we set to bypass the note selector
|
// if we set to bypass the note selector
|
||||||
if (bypassSelectionNote) {
|
if (bypassSelectionNote) {
|
||||||
debug("We are bypassing note selection.");
|
debug("We are bypassing note selection.");
|
||||||
noteSelected = bypassSelectionNoteValue;
|
noteSelected = bypassSelectionNoteValue;
|
||||||
if (isStringInArray(noteSelected, (const char **)filesArray, filesCount)) {// we just give filesCount and not filesCount + extraOptions to avoid matching with an extra options.
|
if (isStringInArray(noteSelected, (const char **)filesArray,
|
||||||
|
filesCount)) { // we just give filesCount and not filesCount +
|
||||||
|
// extraOptions to avoid matching with an extra options.
|
||||||
debug("The note specified with -n or --note does exist. Opening it.");
|
debug("The note specified with -n or --note does exist. Opening it.");
|
||||||
goto open_note;
|
goto open_note;
|
||||||
} else { // if the specified note doesn't exist. We creat it
|
} else { // if the specified note doesn't exist. We creat it
|
||||||
@@ -549,18 +666,25 @@ note_selection:
|
|||||||
goto note_creation;
|
goto note_creation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
noteSelected = ncursesSelect(filesArray, "Select note or journal to open (Use arrows or WASD, Enter to select):", filesCount, extraNotesOptions, " ", "Or select an option below", "", shouldDebug);
|
noteSelected =
|
||||||
// now that we won't use filesArray in this iteration of the loop, we should free it and all its elements. (As this is memory in the heap and not the stack and thus is our responsability to manage)
|
ncursesSelect(filesArray, "Select note or journal to open (Use arrows or WASD, Enter to select):", filesCount, extraNotesOptions, " ", "Or select an option below", "", shouldDebug);
|
||||||
|
// now that we won't use filesArray in this iteration of the loop, we should free it
|
||||||
|
// and all its elements. (As this is memory in the heap and not the stack and thus
|
||||||
|
// is our responsability to manage)
|
||||||
for (int i = 0; i < filesCount; i++) {
|
for (int i = 0; i < filesCount; i++) {
|
||||||
if (noteSelected != filesArray[i]) { // we must prevent noteSelected to be freed. It will cause a lot of problems
|
if (noteSelected != filesArray[i]) { // we must prevent noteSelected to be
|
||||||
free(filesArray[i]); // we must only free the files options and not the extraOptions to avoid segfault
|
// freed. It will cause a lot of problems
|
||||||
|
free(filesArray[i]); // we must only free the files options and not the
|
||||||
|
// extraOptions to avoid segfault
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(filesArray);
|
free(filesArray);
|
||||||
debug("Selected note: %s", noteSelected);
|
debug("Selected note: %s", noteSelected);
|
||||||
if (strcmp(noteSelected, "Create new note") != 0 && strcmp(noteSelected,"Back to vault selection") != 0 && strcmp(noteSelected, "Delete vault") != 0 && strcmp(noteSelected,"Quit (Ctrl+C)") != 0) {
|
if (strcmp(noteSelected, "Create new note") != 0 && strcmp(noteSelected, "Back to vault selection") != 0 && strcmp(noteSelected, "Delete vault") != 0 &&
|
||||||
open_note:
|
strcmp(noteSelected, "Quit (Ctrl+C)") != 0 && strcmp(noteSelected, "Search inside the vault") != 0) {
|
||||||
bypassSelectionNote = 0; // we must reset bypassSelectionNote to avoid getting into an infinite loop of bypassing the note selection
|
open_note:
|
||||||
|
bypassSelectionNote = 0; // we must reset bypassSelectionNote to avoid getting into an infinite
|
||||||
|
// loop of bypassing the note selection
|
||||||
char *fullPath = malloc(PATH_MAX);
|
char *fullPath = malloc(PATH_MAX);
|
||||||
snprintf(fullPath, PATH_MAX, "%s/%s/%s", notesDirectoryString, vaultSelected, noteSelected);
|
snprintf(fullPath, PATH_MAX, "%s/%s/%s", notesDirectoryString, vaultSelected, noteSelected);
|
||||||
// if it is a journal we must update it before
|
// if it is a journal we must update it before
|
||||||
@@ -569,31 +693,57 @@ open_note:
|
|||||||
error(regexReturn, "program", "Regex compilation failed.");
|
error(regexReturn, "program", "Regex compilation failed.");
|
||||||
regexReturn = regexec(®ex, noteSelected, 0, NULL, 0);
|
regexReturn = regexec(®ex, noteSelected, 0, NULL, 0);
|
||||||
|
|
||||||
|
|
||||||
int *journalWasUpdated = malloc(sizeof(int));
|
int *journalWasUpdated = malloc(sizeof(int));
|
||||||
*journalWasUpdated = 0;
|
*journalWasUpdated = 0;
|
||||||
|
|
||||||
|
// used to go back to note selection
|
||||||
|
int *returnNoteSelection = malloc(sizeof(int));
|
||||||
|
*returnNoteSelection = 0;
|
||||||
|
|
||||||
if (!regexReturn) { // if the regex matches -> it's a journal
|
if (!regexReturn) { // if the regex matches -> it's a journal
|
||||||
debug("%s is a journal. Updating it...", noteSelected);
|
debug("%s is a journal. Updating it...", noteSelected);
|
||||||
fullPath = updateJournal(fullPath, noteSelected, timeFormat, journalWasUpdated, shouldDebug); // we return the path. As if it is a divided journal we must point to the correct entry
|
fullPath = updateJournal(fullPath, noteSelected, timeFormat, journalWasUpdated, returnNoteSelection,
|
||||||
|
shouldDebug); // we return the path. As if it is a divided journal we
|
||||||
|
// must point to the correct entry
|
||||||
}
|
}
|
||||||
|
if (!*returnNoteSelection) { // if we don't need to return to the note selection
|
||||||
if (newLineOnOpening) {
|
if (newLineOnOpening) {
|
||||||
if (*journalWasUpdated) {
|
if (*journalWasUpdated) {
|
||||||
appendToFile(fullPath, " \n", shouldDebug); // when updating the journal it adds a \n char at the end. So appendToFile(\n) does not work. We append a (special and rare) whitespace character + \n to bypass this issue.
|
appendToFile(fullPath, " \n",
|
||||||
|
shouldDebug); // when updating the journal it adds a \n char at the
|
||||||
|
// end. So appendToFile(\n) does not work. We append a
|
||||||
|
// (special and rare) whitespace character + \n to
|
||||||
|
// bypass this issue.
|
||||||
}
|
}
|
||||||
appendToFile(fullPath, "\n", shouldDebug);
|
appendToFile(fullPath, "\n", shouldDebug);
|
||||||
}
|
}
|
||||||
openEditor(fullPath, editorToOpen, shouldRender, shouldJumpToEnd, shouldDebug);
|
openEditor(fullPath, editorToOpen, shouldRender, shouldJumpToEnd, shouldDebug);
|
||||||
free(fullPath);
|
free(fullPath);
|
||||||
} else if (strcmp(noteSelected,"Create new note") == 0) {
|
}
|
||||||
note_creation:
|
free(returnNoteSelection);
|
||||||
|
} else if (strcmp(noteSelected, "Create new note") == 0) {
|
||||||
|
note_creation:
|
||||||
noteSelected = createNewNote(notesDirectoryString, vaultSelected, bypassSelectionNote, bypassSelectionNoteValue, journalRegex, shouldDebug);
|
noteSelected = createNewNote(notesDirectoryString, vaultSelected, bypassSelectionNote, bypassSelectionNoteValue, journalRegex, shouldDebug);
|
||||||
// we can just go back to open_note
|
// we can just go back to open_note
|
||||||
goto open_note;
|
goto open_note;
|
||||||
} else if (strcmp(noteSelected,"Back to vault selection") == 0) {
|
} else if (strcmp(noteSelected, "Search inside the vault") == 0) {
|
||||||
|
char *vaultPath = malloc(PATH_MAX);
|
||||||
|
snprintf(vaultPath, PATH_MAX, "%s/%s/", notesDirectoryString, vaultSelected);
|
||||||
|
char *result = fzfSelect(vaultPath, "Input text to be searched", shouldDebug);
|
||||||
|
bypassSelectionNote = 1;
|
||||||
|
bypassSelectionNoteValue = result;
|
||||||
|
free(vaultPath);
|
||||||
|
} else if (strcmp(noteSelected, "Back to vault selection") == 0) {
|
||||||
shouldChangeVault = 1;
|
shouldChangeVault = 1;
|
||||||
} else if (strcmp(noteSelected, "Delete vault") == 0) {
|
} else if (strcmp(noteSelected, "Delete vault") == 0) {
|
||||||
|
// we must find where does the vault comes from.
|
||||||
|
char *notesDirectoryString = getDirectoryFromVault(vaultSelected, vaultsArray, vaultsCount, vaultsCountForEachDirectory, directoriesArray, numDirectories, shouldDebug);
|
||||||
const char *yesNo[] = {"No, go back to note selection.", "Yes."};
|
const char *yesNo[] = {"No, go back to note selection.", "Yes."};
|
||||||
char *answer = ncursesSelect((char **)yesNo, "Are you sure you want to delete the entire vault? This can not be undone (Use arrows or WASD, Enter to select):", 1, 1, " ", "", "", shouldDebug); debug("You answered: %s for deleting the vault %s", answer, vaultSelected);
|
char *answer = ncursesSelect((char **)yesNo,
|
||||||
|
"Are you sure you want to delete the entire vault? This can "
|
||||||
|
"not be undone (Use arrows or WASD, Enter to select):",
|
||||||
|
1, 1, " ", "", "", shouldDebug);
|
||||||
|
debug("You answered: %s for deleting the vault %s", answer, vaultSelected);
|
||||||
if (strcmp(answer, "Yes.") == 0) {
|
if (strcmp(answer, "Yes.") == 0) {
|
||||||
// delete the vault after confirmation by the user
|
// delete the vault after confirmation by the user
|
||||||
char pathToRMRF[PATH_MAX];
|
char pathToRMRF[PATH_MAX];
|
||||||
@@ -602,23 +752,31 @@ note_creation:
|
|||||||
rmrf(pathToRMRF, shouldDebug);
|
rmrf(pathToRMRF, shouldDebug);
|
||||||
shouldChangeVault = 1;
|
shouldChangeVault = 1;
|
||||||
}
|
}
|
||||||
} else if (strcmp(noteSelected,"Quit (Ctrl+C)") == 0) {
|
} else if (strcmp(noteSelected, "Quit (Ctrl+C)") == 0) {
|
||||||
debug("The program was exited.");
|
debug("The program was exited.");
|
||||||
shouldExit = 1;
|
shouldExit = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free(vaultsArray);
|
||||||
} else if (strcmp(vaultSelected,"Create a new vault") == 0) {
|
} else if (strcmp(vaultSelected, "Create a new vault") == 0) {
|
||||||
vault_creation:
|
vault_creation:
|
||||||
createNewVault(notesDirectoryString, bypassSelectionVault, bypassSelectionVaultValue, shouldDebug);
|
createNewVault(directoriesArray, numDirectories, vaultsArray, vaultsCount, bypassSelectionVault, bypassSelectionVaultValue, shouldDebug);
|
||||||
bypassSelectionVault = 0; // we need to reset bypassSelectionVault to avoid getting into an infinite loop of bypassing
|
bypassSelectionVault = 0; // we need to reset bypassSelectionVault to avoid getting into
|
||||||
} else if (strcmp(vaultSelected,"Settings") == 0) {
|
// an infinite loop of bypassing
|
||||||
openEditor(configPath, editorToOpen, 0, 0, shouldDebug); // as this is not a md file we set render and jumptoEnfOfFile to 0
|
} else if (strcmp(vaultSelected, "Settings") == 0) {
|
||||||
} else if (strcmp(vaultSelected,"Quit (Ctrl+C)") == 0) {
|
openEditor(configPath, editorToOpen, 0, 0,
|
||||||
|
shouldDebug); // as this is not a md file we set render and jumptoEnfOfFile to 0
|
||||||
|
} else if (strcmp(vaultSelected, "Backup now") == 0) {
|
||||||
|
debug("Running a forced backup");
|
||||||
|
forceBackup = 1;
|
||||||
|
handleBackups(directoriesArray, numDirectories, backupDirectoriesArray, homedir, interval, (const char **)rsyncArgs, rsyncArgsNumber, forceBackup, backupMessage, shouldDebug);
|
||||||
|
debug("backupMessage was set to %d by a normal backup", *backupMessage);
|
||||||
|
} else if (strcmp(vaultSelected, "Quit (Ctrl+C)") == 0) {
|
||||||
debug("The program was exited");
|
debug("The program was exited");
|
||||||
shouldExit = 1;
|
shouldExit = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(configPath);
|
free(configPath);
|
||||||
|
free(directoriesArray);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+142
-59
@@ -1,14 +1,42 @@
|
|||||||
#include "notes.h"
|
#include "notes.h"
|
||||||
#include "ui.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber, int *vaultNumberPerDirectory, char **directoryArray, int directoryNumber, int shouldDebug) {
|
||||||
|
debug("Searching the vault %s inside all the directories...", targetVault);
|
||||||
|
debug("Here are how many vaults there is per directory:");
|
||||||
|
for (int i = 0; i < directoryNumber; i++) {
|
||||||
|
altDebug("%d for %s\n", vaultNumberPerDirectory[i], directoryArray[i]);
|
||||||
|
}
|
||||||
|
debug("Here are all the vaults in the order they will be searched:");
|
||||||
|
for (int i = 0; i < vaultTotalNumber; i++) {
|
||||||
|
altDebug("%s\n", vaultsArray[i]);
|
||||||
|
}
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < directoryNumber; i++) {
|
||||||
|
for (int j = 0; j < vaultNumberPerDirectory[i]; j++) {
|
||||||
|
if (strcmp(vaultsArray[index], targetVault) == 0) {
|
||||||
|
debug("%s found in %s. (index %d)", targetVault, directoryArray[i], index);
|
||||||
|
return directoryArray[i];
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error(1, "program", "the vault %s was not found", targetVault);
|
||||||
|
return "this makes the compiler and clangd happy. Who doesn't want GCC and clangd to be happy? "
|
||||||
|
"Such person would be a terrible monster... One must imagine GCC and clangd happy.";
|
||||||
|
}
|
||||||
|
|
||||||
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
||||||
debug("Searching %s for journals", vault);
|
debug("Searching %s for journals", vault);
|
||||||
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
// originally from
|
||||||
|
// https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||||
struct dirent *vaultEntry;
|
struct dirent *vaultEntry;
|
||||||
char tempPath[PATH_MAX];
|
char tempPath[PATH_MAX];
|
||||||
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault, vault); // sets the full absolute path to fullPathEntry
|
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault,
|
||||||
|
vault); // sets the full absolute path to fullPathEntry
|
||||||
DIR *vaultDirectory = opendir(tempPath);
|
DIR *vaultDirectory = opendir(tempPath);
|
||||||
error(vaultDirectory==NULL, "program", "Could not open directory %s", tempPath);
|
error(vaultDirectory == NULL, "program", "Could not open directory %s", tempPath);
|
||||||
char **journalsArray = NULL; // will contain all the notes
|
char **journalsArray = NULL; // will contain all the notes
|
||||||
int journalsCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
int journalsCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
||||||
|
|
||||||
@@ -28,7 +56,8 @@ char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex,
|
|||||||
regexReturn = regexec(®ex, vaultEntry->d_name, 0, NULL, 0);
|
regexReturn = regexec(®ex, vaultEntry->d_name, 0, NULL, 0);
|
||||||
if (!regexReturn) { // if the regex matches
|
if (!regexReturn) { // if the regex matches
|
||||||
altDebug("matched with the regex. It is a journal.\n");
|
altDebug("matched with the regex. It is a journal.\n");
|
||||||
journalsArray = realloc(journalsArray, (journalsCount + 1)*sizeof(char*)); // resize notesArray so that
|
journalsArray = realloc(journalsArray,
|
||||||
|
(journalsCount + 1) * sizeof(char *)); // resize notesArray so that
|
||||||
journalsArray[journalsCount] = strdup(vaultEntry->d_name); // copy the dir name into notesArray
|
journalsArray[journalsCount] = strdup(vaultEntry->d_name); // copy the dir name into notesArray
|
||||||
journalsCount++;
|
journalsCount++;
|
||||||
} else {
|
} else {
|
||||||
@@ -45,15 +74,17 @@ char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex,
|
|||||||
return journalsArray;
|
return journalsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
char** getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
||||||
// this function is inputed a path to a vault (which was selected before) and outpus all the suitable notes (so not the hidden ones)
|
// this function is inputed a path to a vault (which was selected before) and outpus all the
|
||||||
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
// suitable notes (so not the hidden ones) originally from
|
||||||
|
// https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||||
debug("Searching %s for notes", vault);
|
debug("Searching %s for notes", vault);
|
||||||
struct dirent *vaultEntry;
|
struct dirent *vaultEntry;
|
||||||
char tempPath[PATH_MAX];
|
char tempPath[PATH_MAX];
|
||||||
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault, vault); // sets the full absolute path to fullPathEntry
|
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault,
|
||||||
|
vault); // sets the full absolute path to fullPathEntry
|
||||||
DIR *vaultDirectory = opendir(tempPath);
|
DIR *vaultDirectory = opendir(tempPath);
|
||||||
error(vaultDirectory==NULL, "program", "Could not open directory %s", tempPath);
|
error(vaultDirectory == NULL, "program", "Could not open directory %s", tempPath);
|
||||||
char **notesArray = NULL; // will contain all the notes
|
char **notesArray = NULL; // will contain all the notes
|
||||||
int notesCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
int notesCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
||||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||||
@@ -70,17 +101,24 @@ char** getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int
|
|||||||
regexReturn = regcomp(®ex, journalRegex, 0);
|
regexReturn = regcomp(®ex, journalRegex, 0);
|
||||||
if (entryName[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
if (entryName[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
||||||
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s/%s", pathToVault, vault, entryName); // sets the full absolute path to fullPathEntry
|
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s/%s", pathToVault, vault,
|
||||||
|
entryName); // sets the full absolute path to fullPathEntry
|
||||||
// check if it matches the regex. If it does not match regexReturn != 0.
|
// check if it matches the regex. If it does not match regexReturn != 0.
|
||||||
regexReturn = regexec(®ex, entryName, 0, NULL, 0);
|
regexReturn = regexec(®ex, entryName, 0, NULL, 0);
|
||||||
struct stat metadataPathEntry;
|
struct stat metadataPathEntry;
|
||||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 && entryName[entryLenght - 3] == '.' && entryName[entryLenght - 2] == 'm' && entryName[entryLenght - 1] == 'd' && regexReturn && S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a file ending in .md and that does no match the regex
|
if (stat(fullPathEntry, &metadataPathEntry) == 0 && entryName[entryLenght - 3] == '.' && entryName[entryLenght - 2] == 'm' && entryName[entryLenght - 1] == 'd' && regexReturn &&
|
||||||
notesArray = realloc(notesArray, (notesCount + 1)*sizeof(char*)); // resize notesArray so that
|
S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a file ending in .md and
|
||||||
|
// that does no match the regex
|
||||||
|
notesArray = realloc(notesArray, (notesCount + 1) * sizeof(char *)); // resize notesArray so that
|
||||||
notesArray[notesCount] = strdup(entryName); // copy the dir name into notesArray
|
notesArray[notesCount] = strdup(entryName); // copy the dir name into notesArray
|
||||||
notesCount++;
|
notesCount++;
|
||||||
altDebug("did not match with the regex. It is a note.\n");
|
altDebug("did not match with the regex. It is a note.\n");
|
||||||
} else if (!regexReturn) {altDebug("matched with the regex. It is a journal.\n");}
|
} else if (!regexReturn) {
|
||||||
} else {altDebug("was ignored\n");}
|
altDebug("matched with the regex. It is a journal.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
altDebug("was ignored\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
altDebug("└ ------------------------------\n");
|
altDebug("└ ------------------------------\n");
|
||||||
|
|
||||||
@@ -90,42 +128,69 @@ char** getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int
|
|||||||
return notesArray;
|
return notesArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
char **getVaultsFromDirectory(char *dirString, int *count, int shouldDebug) {
|
char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber, int *vaultsPerDirectoryNumber, int *count, int shouldDebug) {
|
||||||
// this function is inputed a path to a directory (which comes usually from the config file) and outpus all the suitable directories (so not the hidden ones) which will serve as separate vaults for notes
|
// to avoid having to work with a tree-dimensional array. We will use a 2d and
|
||||||
debug("Opening %s ", dirString);
|
// vaultsPerDirectoryNumber will indicate the width of the directories.
|
||||||
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
char **vaultsArray = NULL;
|
||||||
|
int nthVault = 0; // this is only used internally to set the string into the right place in
|
||||||
|
// directoryStringArray.
|
||||||
|
int previousStartIndex = 0;
|
||||||
|
for (int i = 0; i < directoryNumber; i++) {
|
||||||
|
debug("Opening %s", directoryStringArray[i]);
|
||||||
|
// originally from
|
||||||
|
// https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||||
struct dirent *vaultsDirectoryEntry;
|
struct dirent *vaultsDirectoryEntry;
|
||||||
DIR *vaultsDirectory = opendir(dirString);
|
DIR *vaultsDirectory = opendir(directoryStringArray[i]);
|
||||||
error(vaultsDirectory==NULL, "program", "Could not open directory %s", dirString);
|
error(!vaultsDirectory, "program", "Could not open directory %s", directoryStringArray[i]);
|
||||||
char **dirsArray = NULL; // will contain all the dirs/vaults
|
vaultsPerDirectoryNumber[i] = 0;
|
||||||
int dirsCount = 0; // we need to count how many dirs there is to always readjust how many memory we alloc
|
debug("┌------------------------------\n Detected files and dirs %s:", directoryStringArray[i]);
|
||||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||||
// for readdir()
|
// for readdir()
|
||||||
debug("┌------------------------------\n Detected files and dirs from the directory:");
|
|
||||||
while ((vaultsDirectoryEntry = readdir(vaultsDirectory)) != NULL) {
|
while ((vaultsDirectoryEntry = readdir(vaultsDirectory)) != NULL) {
|
||||||
altDebug("%s\n", vaultsDirectoryEntry->d_name);
|
char *entryName = vaultsDirectoryEntry->d_name; // gets the entry as a string value
|
||||||
if (vaultsDirectoryEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
altDebug("%s", entryName);
|
||||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
if (entryName[0] != '.') { // if not hidden file/dir
|
||||||
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s", dirString, vaultsDirectoryEntry->d_name); // sets the full absolute path to fullPathEntry
|
char tempFullEntryPath[PATH_MAX]; // we recreate the full path to check it's
|
||||||
|
// proprieties
|
||||||
struct stat metadataPathEntry;
|
snprintf(tempFullEntryPath, PATH_MAX, "%s%s", directoryStringArray[i], entryName);
|
||||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 && S_ISDIR(metadataPathEntry.st_mode)) { // if this entry is a directory
|
altDebug(" (%s)", tempFullEntryPath);
|
||||||
dirsArray = realloc(dirsArray, (dirsCount + 1)*sizeof(char*)); // resize dirsArray so that
|
// checking the metadata to see if it is a dir
|
||||||
dirsArray[dirsCount] = strdup(vaultsDirectoryEntry->d_name); // copy the dir name into dirsArray
|
struct stat metadataEntry;
|
||||||
dirsCount++;
|
if (stat(tempFullEntryPath, &metadataEntry) == 0 && S_ISDIR(metadataEntry.st_mode)) { // get's the metadata (stat should return 0 if
|
||||||
|
// fails) and sees if it is a dir.
|
||||||
|
altDebug(" is a vault");
|
||||||
|
vaultsArray = realloc(vaultsArray, sizeof(char *) * (nthVault + 1)); // resize vaultsArray
|
||||||
|
error(vaultsArray == NULL, "program", "realloc failed");
|
||||||
|
vaultsPerDirectoryNumber[i]++; // it will be used later to know which vaults
|
||||||
|
// goes into which directory
|
||||||
|
vaultsArray[nthVault] = strdup(entryName); // we use strdup and not strcpy, because memory used with
|
||||||
|
// opendir and readdir will be closed.
|
||||||
|
error(vaultsArray[nthVault] == NULL, "program", "strdup failed");
|
||||||
|
nthVault++; // it is used immediatly to set the vault into directoryStringArray
|
||||||
|
} else {
|
||||||
|
altDebug(" is not a vault (not a dir.)");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
altDebug(" is not a vault (hidden file/dir)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// we sort entries for the same vault. We can't sort them all. This would break the order.
|
||||||
altDebug("└------------------------------\n");
|
qsort(vaultsArray + previousStartIndex, nthVault - previousStartIndex, sizeof(const char *),
|
||||||
|
compareString); // sorts the vaults alphabetically
|
||||||
// free's some used memory
|
|
||||||
closedir(vaultsDirectory);
|
closedir(vaultsDirectory);
|
||||||
*count = dirsCount;
|
previousStartIndex = nthVault;
|
||||||
return dirsArray;
|
}
|
||||||
|
// we calculate once the total number of vaults to avoid recalculation every time we use it
|
||||||
|
*count = 0;
|
||||||
|
for (int i = 0; i < directoryNumber; i++) {
|
||||||
|
*count += vaultsPerDirectoryNumber[i];
|
||||||
|
}
|
||||||
|
return vaultsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int shouldDebug) {
|
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int *returnNoteSelection, int shouldDebug) {
|
||||||
path[PATH_MAX] = '\0'; // it assures it is a string (Most cases this does nothing). But rewriting at least one bytes make the compile happy. He doesn't want to return an unchanged input.
|
path[PATH_MAX] = '\0'; // it assures it is a string (Most cases this does nothing). But rewriting at least
|
||||||
|
// one bytes make the compile happy. He doesn't want to return an unchanged input.
|
||||||
debug("Handling the journal %s", path);
|
debug("Handling the journal %s", path);
|
||||||
|
|
||||||
char *date = getFormatedTime(timeFormat, shouldDebug);
|
char *date = getFormatedTime(timeFormat, shouldDebug);
|
||||||
@@ -135,7 +200,7 @@ char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWas
|
|||||||
sanitize(dateWithExtension);
|
sanitize(dateWithExtension);
|
||||||
debug("Sanitized date: %s\n(it might be used later for a file name if the journal is divided)", dateWithExtension);
|
debug("Sanitized date: %s\n(it might be used later for a file name if the journal is divided)", dateWithExtension);
|
||||||
struct stat metadata;
|
struct stat metadata;
|
||||||
error(stat(path, &metadata), "program", "stat() failed to get information about %s", path);
|
stat(path, &metadata);
|
||||||
if (S_ISREG(metadata.st_mode)) {
|
if (S_ISREG(metadata.st_mode)) {
|
||||||
debug("%s is a unified journal.", path);
|
debug("%s is a unified journal.", path);
|
||||||
if (!isStringInFile(path, date, shouldDebug)) { // if there is no entry for current date
|
if (!isStringInFile(path, date, shouldDebug)) { // if there is no entry for current date
|
||||||
@@ -150,19 +215,22 @@ char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWas
|
|||||||
snprintf(temp, PATH_MAX, "%s/", path); // appends a /. For safety
|
snprintf(temp, PATH_MAX, "%s/", path); // appends a /. For safety
|
||||||
strncpy(path, temp, PATH_MAX);
|
strncpy(path, temp, PATH_MAX);
|
||||||
DIR *dividedJournalDirectory = opendir(path);
|
DIR *dividedJournalDirectory = opendir(path);
|
||||||
error(dividedJournalDirectory==NULL, "program", "Could not open directory %s", path);
|
error(dividedJournalDirectory == NULL, "program", "Could not open directory %s", path);
|
||||||
/*char **entryArray = NULL; // will contain all the entries of the divided journal
|
/*char **entryArray = NULL; // will contain all the entries of the divided journal
|
||||||
// This time it will be different. We must add "invert" the extraOptions to the options so that the Create new entry in journal is on top
|
// This time it will be different. We must add "invert" the extraOptions to the options so
|
||||||
entryArray = realloc(entryArray, sizeof(char*));*/
|
that the Create new entry in journal is on top entryArray = realloc(entryArray,
|
||||||
|
sizeof(char*));*/
|
||||||
// simpler to just malloc 2 and later realloc
|
// simpler to just malloc 2 and later realloc
|
||||||
const int extraOptions = 3;
|
const int extraOptions = 4;
|
||||||
char **entryArray = malloc(extraOptions*sizeof(char*));
|
char **entryArray = malloc(extraOptions * sizeof(char *));
|
||||||
char *createEntryMessage = malloc(PATH_MAX);
|
char *createEntryMessage = malloc(PATH_MAX);
|
||||||
snprintf(createEntryMessage, PATH_MAX, "Create new entry for the journal %s", journal);
|
snprintf(createEntryMessage, PATH_MAX, "Create new entry for the journal %s", journal);
|
||||||
entryArray[0] = createEntryMessage;
|
entryArray[0] = createEntryMessage;
|
||||||
entryArray[1] = "Open random entry";
|
entryArray[1] = "Open random entry";
|
||||||
entryArray[2] = "Search inside entries";
|
entryArray[2] = "Search inside entries";
|
||||||
int entryCount = extraOptions; // we need to count how many dirs there is to always readjust how many memory we alloc
|
entryArray[3] = "Return to the note selection";
|
||||||
|
int entryCount = extraOptions; // we need to count how many dirs there is to always readjust
|
||||||
|
// how many memory we alloc
|
||||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||||
// for readdir()
|
// for readdir()
|
||||||
debug("┌------------------------------\n Detected files and dirs from %s:", path);
|
debug("┌------------------------------\n Detected files and dirs from %s:", path);
|
||||||
@@ -171,11 +239,12 @@ char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWas
|
|||||||
altDebug("%s\n", dividedJournalEntry->d_name);
|
altDebug("%s\n", dividedJournalEntry->d_name);
|
||||||
if (dividedJournalEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
if (dividedJournalEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
||||||
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s", path, dividedJournalEntry->d_name); // sets the full absolute path to fullPathEntry
|
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s", path,
|
||||||
|
dividedJournalEntry->d_name); // sets the full absolute path to fullPathEntry
|
||||||
|
|
||||||
struct stat metadataPathEntry;
|
struct stat metadataPathEntry;
|
||||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 && S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a directory
|
if (stat(fullPathEntry, &metadataPathEntry) == 0 && S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a directory
|
||||||
entryArray = realloc(entryArray, (entryCount + 1)*sizeof(char*));
|
entryArray = realloc(entryArray, (entryCount + 1) * sizeof(char *));
|
||||||
entryArray[entryCount] = strdup(dividedJournalEntry->d_name); // copy the dir name into entryArray
|
entryArray[entryCount] = strdup(dividedJournalEntry->d_name); // copy the dir name into entryArray
|
||||||
entryCount++;
|
entryCount++;
|
||||||
}
|
}
|
||||||
@@ -184,17 +253,25 @@ char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWas
|
|||||||
altDebug("└------------------------------\n");
|
altDebug("└------------------------------\n");
|
||||||
// free's some used memory
|
// free's some used memory
|
||||||
closedir(dividedJournalDirectory);
|
closedir(dividedJournalDirectory);
|
||||||
qsort(entryArray + extraOptions, entryCount - extraOptions, sizeof(const char *), reverseCompareString); // sorts the journals entries alphabetically. // the + extraOptions - extraOptions is to not sort "Create new entry for the journal" or Open random entry which is the first element
|
qsort(entryArray + extraOptions, entryCount - extraOptions, sizeof(const char *),
|
||||||
|
reverseCompareString); // sorts the journals entries alphabetically. // the +
|
||||||
|
// extraOptions - extraOptions is to not sort "Create new entry
|
||||||
|
// for the journal" or Open random entry which is the first
|
||||||
|
// element
|
||||||
|
|
||||||
// we must now select to create new entry or to enter in old one
|
// we must now select to create new entry or to enter in old one
|
||||||
char *selectedOption = ncursesSelect(entryArray, "Create new entry or acces old entry (Use arrows or WASD, Enter to select):", extraOptions, entryCount - extraOptions, " ", " ", "", shouldDebug); // the "Create new entry for the journal %s" will be the only options. All other will be extraOptions. This is made so that "Create [...] %s" will always be on top
|
char *selectedOption = ncursesSelect(entryArray, "Create new entry or acces old entry (Use arrows or WASD, Enter to select):", extraOptions, entryCount - extraOptions, " ", " ", "",
|
||||||
|
shouldDebug); // the "Create new entry for the journal %s" will be the only options. All
|
||||||
|
// other will be extraOptions. This is made so that "Create [...] %s" will
|
||||||
|
// always be on top
|
||||||
debug("Selected option from journal entry selection: %s", selectedOption);
|
debug("Selected option from journal entry selection: %s", selectedOption);
|
||||||
if (strcmp(selectedOption, createEntryMessage) == 0) { // create new entry
|
if (strcmp(selectedOption, createEntryMessage) == 0) { // create new entry
|
||||||
char temp[PATH_MAX];
|
char temp[PATH_MAX];
|
||||||
error(strlen(path)+1+strlen(dateWithExtension)+1>PATH_MAX, "Error file path too long. %s/%s must not exceed PATH_MAX", path, dateWithExtension);
|
error(strlen(path) + 1 + strlen(dateWithExtension) + 1 > PATH_MAX, "Error file path too long. %s/%s must not exceed PATH_MAX", path, dateWithExtension);
|
||||||
snprintf(temp, PATH_MAX, "%s/%s", path, dateWithExtension);
|
snprintf(temp, PATH_MAX, "%s/%s", path, dateWithExtension);
|
||||||
strncpy(path, temp, PATH_MAX);
|
strncpy(path, temp, PATH_MAX);
|
||||||
if (!isStringInArray(dateWithExtension, (const char **)entryArray, entryCount)) { // it only creates it if it doesn't already exist
|
if (!isStringInArray(dateWithExtension, (const char **)entryArray,
|
||||||
|
entryCount)) { // it only creates it if it doesn't already exist
|
||||||
// if it does exist it will just pass the full path
|
// if it does exist it will just pass the full path
|
||||||
debug("Creating entry %s inside %s", date, path);
|
debug("Creating entry %s inside %s", date, path);
|
||||||
FILE *file;
|
FILE *file;
|
||||||
@@ -211,19 +288,25 @@ char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWas
|
|||||||
} else if (strcmp(selectedOption, entryArray[1]) == 0) { // if we choosed to open a random entry
|
} else if (strcmp(selectedOption, entryArray[1]) == 0) { // if we choosed to open a random entry
|
||||||
// setting up the seed for rand()
|
// setting up the seed for rand()
|
||||||
srand(time(NULL)); // not totaly random, just pseudorandom
|
srand(time(NULL)); // not totaly random, just pseudorandom
|
||||||
int randomEntry = extraOptions + (rand() % (entryCount - extraOptions)); // we get a random number between extraOptions and entryCount (so all journal entries and not extraOptions)
|
int randomEntry = extraOptions + (rand() % (entryCount - extraOptions)); // we get a random number between extraOptions and entryCount (so
|
||||||
|
// all journal entries and not extraOptions)
|
||||||
char temp[PATH_MAX];
|
char temp[PATH_MAX];
|
||||||
snprintf(temp, PATH_MAX, "%s/%s", path, entryArray[randomEntry]); // reconstruct the path
|
snprintf(temp, PATH_MAX, "%s/%s", path,
|
||||||
|
entryArray[randomEntry]); // reconstruct the path
|
||||||
strncpy(path, temp, PATH_MAX);
|
strncpy(path, temp, PATH_MAX);
|
||||||
debug("Selected random entry %s. It's path is %s.", entryArray[randomEntry], path);
|
debug("Selected random entry %s. It's path is %s.", entryArray[randomEntry], path);
|
||||||
} else if (strcmp(selectedOption, entryArray[2]) == 0) { // if we choosed to search
|
} else if (strcmp(selectedOption, entryArray[2]) == 0) { // if we choosed to search
|
||||||
char *temp = fzfSelect(path, "Input text to be searched", shouldDebug); // uses ripgrep and fzf to search inside the files
|
char *temp = fzfSelect(path, "Input text to be searched",
|
||||||
|
shouldDebug); // uses ripgrep and fzf to search inside the files
|
||||||
char *selectedOption = malloc(PATH_MAX); // we can't just use path as both input and output of snprintf
|
char *selectedOption = malloc(PATH_MAX); // we can't just use path as both input and output of snprintf
|
||||||
snprintf(selectedOption, PATH_MAX, "%s/%s", path, temp);
|
snprintf(selectedOption, PATH_MAX, "%s/%s", path, temp);
|
||||||
path = strdup(selectedOption);
|
path = strdup(selectedOption);
|
||||||
free(selectedOption);
|
free(selectedOption);
|
||||||
|
} else if (strcmp(selectedOption, "Return to the note selection") == 0) {
|
||||||
|
*returnNoteSelection = 1;
|
||||||
} else { // we just recreate the path to the selected entry
|
} else { // we just recreate the path to the selected entry
|
||||||
// snprintf does not like to have the same variable as input and output so we use a buffer
|
// snprintf does not like to have the same variable as input and output so we use a
|
||||||
|
// buffer
|
||||||
char temp[PATH_MAX];
|
char temp[PATH_MAX];
|
||||||
snprintf(temp, PATH_MAX, "%s/%s", path, selectedOption);
|
snprintf(temp, PATH_MAX, "%s/%s", path, selectedOption);
|
||||||
strncpy(path, temp, PATH_MAX);
|
strncpy(path, temp, PATH_MAX);
|
||||||
|
|||||||
+17
-8
@@ -1,22 +1,31 @@
|
|||||||
#ifndef NOTES_H
|
#ifndef NOTES_H
|
||||||
#define NOTES_H
|
#define NOTES_H
|
||||||
#include "utils.h"
|
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "utils.h"
|
||||||
|
// find which directory contains targetVault
|
||||||
|
char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber, int *vaultNumberPerDirectory, char **directoryArray, int directoryNumber, int shouldDebug);
|
||||||
// gets the journals from the vault.
|
// gets the journals from the vault.
|
||||||
//to distinguish journals from note we use regex.
|
// to distinguish journals from note we use regex.
|
||||||
//Note: we must handle them separatly as journals can either be a single file (which will treat specially) or a directory.
|
// Note: we must handle them separatly as journals can either be a single file (which will treat
|
||||||
|
// specially) or a directory.
|
||||||
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
||||||
// this function is inputed a path to a vault (which was selected before) and outpus all the suitable notes (so not the hidden ones).
|
// this function is inputed a path to a vault (which was selected before) and outpus all the
|
||||||
// journalRegex is the regex code for the journals. If a note matches this code, it is treated as a journal and it is not outputed from this function.
|
// suitable notes (so not the hidden ones). journalRegex is the regex code for the journals. If a
|
||||||
|
// note matches this code, it is treated as a journal and it is not outputed from this function.
|
||||||
char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
||||||
// this function is inputed a path to a directory (which comes usually from the config file) and outpus all the suitable directories (so not the hidden ones) which will serve as separate vaults for notes.
|
// function gets as input an array of directoryNumber strings. Which are the directories the
|
||||||
char **getVaultsFromDirectory(char *dirString, int *count, int shouldDebug);
|
// function will search for vaults. it returns an array of vaults. the vaults are organized in order
|
||||||
|
// per directory. vaultsPerDirectoryNumber and count should be initiallized before calling the
|
||||||
|
// function and the address be inputed. count is the total number of vaults.
|
||||||
|
// vaultsPerDirectoryNumber gives how many vaults each directory has.
|
||||||
|
char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber, int *vaultsPerDirectoryNumber, int *count, int shouldDebug);
|
||||||
// path is the path to the file.
|
// path is the path to the file.
|
||||||
// journal is the name of the file.
|
// journal is the name of the file.
|
||||||
// journalWasUpdated will be set to 1 if a new entry was created
|
// journalWasUpdated will be set to 1 if a new entry was created
|
||||||
// handles both type of journal (divided and unified).
|
// handles both type of journal (divided and unified).
|
||||||
// creates new entry with date.
|
// creates new entry with date.
|
||||||
// for divided select if we want to acces to a new entry or a old one.
|
// for divided select if we want to acces to a new entry or a old one.
|
||||||
|
// returnNoteSelection tells us if the user wants to go back to the note selection.
|
||||||
// returns the path to the file that needs to be opened.
|
// returns the path to the file that needs to be opened.
|
||||||
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int shouldDebug);
|
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int *returnNoteSelection, int shouldDebug);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
void createNewVault(char *dirToVault, int bypass, char *bypassvalue, int shouldDebug) {
|
void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount, int bypass, char *bypassvalue, int shouldDebug) {
|
||||||
int duplicateWarning = 0; // set to 1 later if the vault you tried to create already existed
|
int duplicateWarning = 0; // set to 1 later if the vault you tried to create already existed
|
||||||
int emptyWarning = 0; // set to 1 later if inpted empty name
|
int emptyWarning = 0; // set to 1 later if inpted empty name
|
||||||
input_screen:
|
input_screen:
|
||||||
|
// choose in which directory the vault will be created
|
||||||
|
char *dirToVault = ncursesSelect(directoriesArray, "Select a directory, in which the vault will remain (Use arrows or WASD, Enter to select)", directoryCount, 0, "", "", " ", shouldDebug);
|
||||||
char *vaultName = malloc(PATH_MAX);
|
char *vaultName = malloc(PATH_MAX);
|
||||||
if (!bypass) { // if won't bypass (if -v or --vault weren't set)
|
if (!bypass) { // if won't bypass (if -v or --vault weren't set)
|
||||||
echo();
|
echo();
|
||||||
@@ -29,15 +32,15 @@ input_screen:
|
|||||||
attroff(COLOR_PAIR(2));
|
attroff(COLOR_PAIR(2));
|
||||||
}
|
}
|
||||||
move(1, 1); // replace cursor
|
move(1, 1); // replace cursor
|
||||||
wgetnstr(stdscr, vaultName, PATH_MAX-1);
|
wgetnstr(stdscr, vaultName, PATH_MAX - 1);
|
||||||
refresh();
|
refresh();
|
||||||
endwin();
|
endwin();
|
||||||
reset_shell_mode();
|
reset_shell_mode();
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
} else {
|
} else {
|
||||||
strncpy(vaultName, bypassvalue, PATH_MAX -2); // -2 (and later -1) because indexing
|
strncpy(vaultName, bypassvalue, PATH_MAX - 2); // -2 (and later -1) because indexing
|
||||||
vaultName[PATH_MAX-1] = '\0';
|
vaultName[PATH_MAX - 1] = '\0';
|
||||||
}
|
}
|
||||||
// check if no empty string
|
// check if no empty string
|
||||||
if (strcmp(vaultName, "") == 0) {
|
if (strcmp(vaultName, "") == 0) {
|
||||||
@@ -48,13 +51,14 @@ input_screen:
|
|||||||
sanitize(vaultName);
|
sanitize(vaultName);
|
||||||
debug("Sanitized vaultName=%s", vaultName);
|
debug("Sanitized vaultName=%s", vaultName);
|
||||||
|
|
||||||
struct stat st = {0}; // https://stackoverflow.com/a/7430262
|
if (!isStringInArray(vaultName, (const char **)vaultsArray,
|
||||||
char vaultFullPath[PATH_MAX];
|
vaultCount)) { // if vault doesnt already exists. We avoid vaults with the
|
||||||
|
// same name even if they are from different directories.
|
||||||
|
char vaultFullPath[PATH_MAX]; // recreating the full path
|
||||||
sprintf(vaultFullPath, "%s/%s/", dirToVault, vaultName);
|
sprintf(vaultFullPath, "%s/%s/", dirToVault, vaultName);
|
||||||
if (stat(vaultFullPath, &st) == -1) {
|
|
||||||
mkdir(vaultFullPath, 0744);
|
mkdir(vaultFullPath, 0744);
|
||||||
} else {
|
} else {
|
||||||
// if stat(...) != -1 it means the vault already exist. We will go back to the input screen with a new message.
|
|
||||||
duplicateWarning = 1;
|
duplicateWarning = 1;
|
||||||
goto input_screen;
|
goto input_screen;
|
||||||
}
|
}
|
||||||
@@ -70,9 +74,14 @@ char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, c
|
|||||||
clear();
|
clear();
|
||||||
printw("Enter the name of the new note: ");
|
printw("Enter the name of the new note: ");
|
||||||
mvprintw(3, 0, "Unsafe characters such as \\, and / will be replaced by _");
|
mvprintw(3, 0, "Unsafe characters such as \\, and / will be replaced by _");
|
||||||
mvprintw(4, 0, "If the name matches with the regex for a journal (%s), it will create a journal instead of a note.", journalRegex);
|
mvprintw(4, 0,
|
||||||
move(1,1);
|
"If the name matches with the regex for a journal (%s), it will create a journal "
|
||||||
wgetnstr(stdscr, fileName, BUFFER_SIZE-4); //limits the buffer to prevent overflow (-4 to account indexing and from ".md" in case we need to add it later)
|
"instead of a note.",
|
||||||
|
journalRegex);
|
||||||
|
move(1, 1);
|
||||||
|
wgetnstr(stdscr, fileName,
|
||||||
|
BUFFER_SIZE - 4); // limits the buffer to prevent overflow (-4 to account indexing
|
||||||
|
// and from ".md" in case we need to add it later)
|
||||||
refresh();
|
refresh();
|
||||||
endwin();
|
endwin();
|
||||||
reset_shell_mode();
|
reset_shell_mode();
|
||||||
@@ -80,9 +89,11 @@ char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, c
|
|||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
} else { // bypasses user input if we bypass is set to 1
|
} else { // bypasses user input if we bypass is set to 1
|
||||||
strncpy(fileName, bypassvalue, BUFFER_SIZE);
|
strncpy(fileName, bypassvalue, BUFFER_SIZE);
|
||||||
fileName[BUFFER_SIZE-1] = '\0';
|
fileName[BUFFER_SIZE - 1] = '\0';
|
||||||
}
|
}
|
||||||
error(strcmp(fileName, "") == 0, "user", "fileName is empty"); // replace this with a warning and add a warning if duplicate file and handle case where multiple warnings (if such case is possible)
|
error(strcmp(fileName, "") == 0, "user",
|
||||||
|
"fileName is empty"); // replace this with a warning and add a warning if duplicate file and
|
||||||
|
// handle case where multiple warnings (if such case is possible)
|
||||||
// check/sanitize the input
|
// check/sanitize the input
|
||||||
debug("Inputed fileName=%s", fileName);
|
debug("Inputed fileName=%s", fileName);
|
||||||
sanitize(fileName);
|
sanitize(fileName);
|
||||||
@@ -102,30 +113,33 @@ char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, c
|
|||||||
char **options = malloc(32); // the number of bytes is exactly what in the two strings
|
char **options = malloc(32); // the number of bytes is exactly what in the two strings
|
||||||
options[0] = "Divided journal";
|
options[0] = "Divided journal";
|
||||||
options[1] = "Unified journal";
|
options[1] = "Unified journal";
|
||||||
char *optionSelected = ncursesSelect(options, "Select which type of journal you want to create (Use arrows or WASD, Enter to select):", 2, 0, "", "", " ", shouldDebug);
|
char *optionSelected = ncursesSelect(options,
|
||||||
|
"Select which type of journal you want to create "
|
||||||
|
"(Use arrows or WASD, Enter to select):",
|
||||||
|
2, 0, "", "", " ", shouldDebug);
|
||||||
debug("%s was selected to be a %s", fileName, optionSelected);
|
debug("%s was selected to be a %s", fileName, optionSelected);
|
||||||
if (strcmp(optionSelected, options[0]) == 0) { // if it is a divided journal
|
if (strcmp(optionSelected, options[0]) == 0) { // if it is a divided journal
|
||||||
char *fileFullPath = malloc(PATH_MAX);
|
char *fileFullPath = malloc(PATH_MAX);
|
||||||
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s/", dirToVault, vaultFromDir, fileName);
|
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s/", dirToVault, vaultFromDir, fileName);
|
||||||
struct stat st = {0};
|
struct stat st = {0};
|
||||||
if (stat(fileFullPath, &st) == -1) {
|
if (stat(fileFullPath, &st) == -1) {
|
||||||
error(!mkdir(fileFullPath, 0744), "program", "mkdir failed");
|
error(mkdir(fileFullPath, 0744), "program", "mkdir failed");
|
||||||
} else {
|
} else {
|
||||||
error(1, "program", "%s could not be created", fileFullPath);
|
error(1, "program", "%s could not be created", fileFullPath);
|
||||||
}
|
}
|
||||||
free(fileFullPath);
|
free(fileFullPath);
|
||||||
} else { // if it is a unified journal
|
} else { // if it is a unified journal
|
||||||
char *fileFullPath = malloc(PATH_MAX);
|
char *fileFullPath = malloc(PATH_MAX);
|
||||||
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
// we append .md directly to fileName and not only to fileFullPath to avoid passing the wrong fileName later.
|
||||||
int len = strlen(fileFullPath);
|
int lenName = strlen(fileName);
|
||||||
if (fileFullPath[len-3] != '.' || fileFullPath[len-2] != 'm' || fileFullPath[len-1] != 'd') { // there might be a cleaner way to do this
|
if (fileName[lenName - 3] != '.' && fileName[lenName - 2] != 'm' && fileName[lenName - 2] != '1') {
|
||||||
error(len > PATH_MAX - 3, "user", "%s is too big (greater than PATH_MAX-3) and we can't append .md", fileFullPath);
|
strcat(fileName, ".md");
|
||||||
strncat(fileFullPath, ".md", PATH_MAX);
|
debug("We just appended .md to %s", fileName);
|
||||||
// we checked before if fileName didn't already exist.
|
|
||||||
// we must redo it as we add a .mode
|
|
||||||
struct stat st;
|
|
||||||
error(stat(fileFullPath, &st) == 0, "user", "%s already exists", fileFullPath);
|
|
||||||
}
|
}
|
||||||
|
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||||
|
// we verify if the file doesn't already exist as we might just have changed the fileName and (by extension) the file path.
|
||||||
|
struct stat metadata;
|
||||||
|
error(stat(fileFullPath, &metadata) == 0, "user", "%s already exists", fileFullPath);
|
||||||
FILE *filePointer;
|
FILE *filePointer;
|
||||||
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
||||||
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
||||||
@@ -138,10 +152,12 @@ char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, c
|
|||||||
debug("%s does not match with %s treating it as a note", fileName, journalRegex);
|
debug("%s does not match with %s treating it as a note", fileName, journalRegex);
|
||||||
// if there is no .md add an .md
|
// if there is no .md add an .md
|
||||||
int len = strlen(fileName);
|
int len = strlen(fileName);
|
||||||
if (fileName[len-3] != '.' || fileName[len-2] != 'm' || fileName[len-1] != 'd') { // there might be a cleaner way to do this
|
if (fileName[len - 3] != '.' || fileName[len - 2] != 'm' || fileName[len - 1] != 'd') { // there might be a cleaner way to do this
|
||||||
strcat(fileName, ".md"); // this should not cause an overflow issue as we get at most 252 chars (+'.'+'m'+'d'+'\0' makes it to 256) with wgetnstr
|
strcat(fileName, ".md"); // this should not cause an overflow issue as we get at most
|
||||||
|
// 252 chars (+'.'+'m'+'d'+'\0' makes it to 256) with wgetnstr
|
||||||
}
|
}
|
||||||
char *fileFullPath = malloc(PATH_MAX); // this dinamically allocated because we use it in the main function to call openEditor
|
char *fileFullPath = malloc(PATH_MAX); // this dinamically allocated because we use it in
|
||||||
|
// the main function to call openEditor
|
||||||
sprintf(fileFullPath, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
sprintf(fileFullPath, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||||
FILE *filePointer;
|
FILE *filePointer;
|
||||||
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
||||||
@@ -153,8 +169,9 @@ char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, c
|
|||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
char *fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
||||||
// we are gonna write each line of each files (with the filename and the line number to an index)
|
// we are gonna write each line of each files (with the filename and the line number to an
|
||||||
|
// index)
|
||||||
char command[CMD_BUFFER];
|
char command[CMD_BUFFER];
|
||||||
char indexFile[] = "/tmp/notewrapper_index_XXXXXX";
|
char indexFile[] = "/tmp/notewrapper_index_XXXXXX";
|
||||||
|
|
||||||
@@ -162,17 +179,7 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
|||||||
error(fd == -1, "program", "mkstemp failed");
|
error(fd == -1, "program", "mkstemp failed");
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
/*
|
snprintf(command, sizeof(command), "rg --line-number --no-heading --color=never . \"%s\" > %s", pathToFiles, indexFile);
|
||||||
* STEP 1:
|
|
||||||
* Build index once into temp file
|
|
||||||
* format: file:line:content
|
|
||||||
*/
|
|
||||||
snprintf(command, sizeof(command),
|
|
||||||
"rg --line-number --no-heading --color=never . \"%s\" > %s",
|
|
||||||
pathToFiles,
|
|
||||||
indexFile
|
|
||||||
);
|
|
||||||
|
|
||||||
debug("INDEX CMD: %s", command);
|
debug("INDEX CMD: %s", command);
|
||||||
|
|
||||||
if (system(command) != 0) {
|
if (system(command) != 0) {
|
||||||
@@ -180,53 +187,13 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
|||||||
error(1, "program", "rg indexing failed");
|
error(1, "program", "rg indexing failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* STEP 2:
|
|
||||||
* Use fzf on the index file (NO rg anymore)
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
EXPLANATION:
|
|
||||||
|
|
||||||
cat %s
|
|
||||||
→ feeds prebuilt index file (format: file:line:content) into fzf
|
|
||||||
|
|
||||||
fzf --delimiter ':'
|
|
||||||
→ splits each line into fields:
|
|
||||||
{1} = file path
|
|
||||||
{2} = line number
|
|
||||||
{3} = content
|
|
||||||
|
|
||||||
--prompt
|
|
||||||
→ UI prompt text shown in fzf
|
|
||||||
|
|
||||||
--preview
|
|
||||||
→ runs a shell command for selected item:
|
|
||||||
file={1} → current file
|
|
||||||
line={2} → line number of match
|
|
||||||
|
|
||||||
nl -ba "$file"
|
|
||||||
→ prints file with line numbers
|
|
||||||
|
|
||||||
sed -n "$((line-5)),$((line+5))p"
|
|
||||||
→ extracts a window of 5 lines above and below match
|
|
||||||
|
|
||||||
sed "... -> ..."
|
|
||||||
→ highlights the matched line (middle of window) with red arrow
|
|
||||||
|
|
||||||
--preview-window=right:60%:hidden
|
|
||||||
→ preview appears on right, 60% width, initially hidden
|
|
||||||
|
|
||||||
--bind 'change:show-preview'
|
|
||||||
→ preview only appears after first selection change (not at startup)
|
|
||||||
*/
|
|
||||||
snprintf(command, sizeof(command),
|
snprintf(command, sizeof(command),
|
||||||
"cat %s | fzf --delimiter ':' --prompt='%s' "
|
"cat %s | fzf --delimiter ':' --prompt='%s' "
|
||||||
"--preview 'file={1}; line={2}; nl -ba \"$file\" | sed -n \"$((line-5)),$((line+5))p\" | sed \"$((line-$(($((line-5))))+1))s/^/\\x1b[31m-> \\x1b[0m/\"' "
|
"--preview 'file={1}; line={2}; nl -ba \"$file\" | sed -n \"$((line-5)),$((line+5))p\" | "
|
||||||
|
"sed \"$((line-$(($((line-5))))+1))s/^/\\x1b[31m-> \\x1b[0m/\"' "
|
||||||
"--preview-window=right:60%%:hidden "
|
"--preview-window=right:60%%:hidden "
|
||||||
"--bind 'change:show-preview'",
|
"--bind 'change:show-preview'",
|
||||||
indexFile,
|
indexFile, selectText ? selectText : "> ");
|
||||||
selectText ? selectText : "> "
|
|
||||||
);
|
|
||||||
debug("FZF CMD: %s", command);
|
debug("FZF CMD: %s", command);
|
||||||
|
|
||||||
FILE *fzfPipe = popen(command, "r");
|
FILE *fzfPipe = popen(command, "r");
|
||||||
@@ -242,16 +209,25 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
|||||||
buffer[strcspn(buffer, "\n")] = '\0';
|
buffer[strcspn(buffer, "\n")] = '\0';
|
||||||
result = strdup(buffer);
|
result = strdup(buffer);
|
||||||
}
|
}
|
||||||
|
debug("Buffer: %s", buffer);
|
||||||
|
debug("result: %s", result);
|
||||||
|
|
||||||
// cut at first ':'
|
// cut at first ':'
|
||||||
result = strtok(result, ":");
|
result = strtok(result, ":");
|
||||||
// cut at / (because we need the full paths before and we only need to return the journal entry)
|
// we need to cut out the whole pathToFiles
|
||||||
char *token = strtok(result, "/");
|
int pathToFilesLen = strlen(pathToFiles);
|
||||||
result = NULL;
|
int cutOut = 0;
|
||||||
while (token != NULL) {
|
for (int i = 0; i < pathToFilesLen; i++) {
|
||||||
result = token;
|
if (pathToFiles[i] == result[i]) {
|
||||||
token = strtok(NULL, "/"); //Passing NULL means “don’t start a new string, resume
|
cutOut = i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
result += cutOut + 1; // the + 1 cuts the first /
|
||||||
|
|
||||||
|
// in case we select a journal, we won't open the entry just yet. We only need to return the journal name (everything before [now the first] /
|
||||||
|
result = strtok(result, "/");
|
||||||
|
|
||||||
|
debug("Cleaned note: %s", result);
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
pclose(fzfPipe);
|
pclose(fzfPipe);
|
||||||
@@ -260,22 +236,25 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText, int shouldDebug) {
|
char *ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText,
|
||||||
int highlight = 0; //curently highlighted option
|
int shouldDebug) { // TODO we should see how it handles larges strings (like large directories)
|
||||||
|
int highlight = 0; // curently highlighted option
|
||||||
int key;
|
int key;
|
||||||
|
|
||||||
cbreak(); // disable line buffering
|
cbreak(); // disable line buffering
|
||||||
noecho(); // don't echo key presses
|
noecho(); // don't echo key presses
|
||||||
keypad(stdscr, TRUE); // enable arrow keys
|
keypad(stdscr, TRUE); // enable arrow keys
|
||||||
start_color();
|
start_color();
|
||||||
curs_set(0); // sets the cursor to invisible. (We will emulate the cursor with a hightlight that go up and down).
|
curs_set(0); // sets the cursor to invisible. (We will emulate the cursor with a hightlight that
|
||||||
|
// go up and down).
|
||||||
use_default_colors();
|
use_default_colors();
|
||||||
init_pair(1, COLOR_WHITE, -1); // color for optionsNumber (so notes, vaults, etc.)
|
init_pair(1, COLOR_WHITE, -1); // color for optionsNumber (so notes, vaults, etc.)
|
||||||
init_pair(2, COLOR_BLUE, -1); // color for extraOptionsNumber (so settings, delete notes, create vault, etc.)
|
init_pair(2, COLOR_BLUE,
|
||||||
|
-1); // color for extraOptionsNumber (so settings, delete notes, create vault, etc.)
|
||||||
while (1) {
|
while (1) {
|
||||||
clear();
|
clear();
|
||||||
attron(COLOR_PAIR(1));
|
attron(COLOR_PAIR(1));
|
||||||
mvprintw(0,0, "%s", optionsText);
|
mvprintw(0, 0, "%s", optionsText);
|
||||||
int offset = 1;
|
int offset = 1;
|
||||||
if (strcmp(bottomText, "") != 0) {
|
if (strcmp(bottomText, "") != 0) {
|
||||||
mvprintw(offset, 1, "%s", bottomText);
|
mvprintw(offset, 1, "%s", bottomText);
|
||||||
@@ -286,14 +265,14 @@ char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int ex
|
|||||||
if (i == highlight) {
|
if (i == highlight) {
|
||||||
attron(A_REVERSE);
|
attron(A_REVERSE);
|
||||||
} // highlight selected
|
} // highlight selected
|
||||||
mvprintw(i+offset, 2, "%s", options[i]);
|
mvprintw(i + offset, 2, "%s", options[i]);
|
||||||
if (i == highlight) {
|
if (i == highlight) {
|
||||||
attroff(A_REVERSE);
|
attroff(A_REVERSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attroff(COLOR_PAIR(1));
|
attroff(COLOR_PAIR(1));
|
||||||
if (strcmp(middleText, "") != 0) {
|
if (strcmp(middleText, "") != 0) {
|
||||||
mvprintw(offset+optionsNumber, 1, "%s", middleText);
|
mvprintw(offset + optionsNumber, 1, "%s", middleText);
|
||||||
offset++;
|
offset++;
|
||||||
}
|
}
|
||||||
// Printf extraOptions with highlighting
|
// Printf extraOptions with highlighting
|
||||||
@@ -302,24 +281,24 @@ char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int ex
|
|||||||
if (k == highlight) {
|
if (k == highlight) {
|
||||||
attron(A_REVERSE);
|
attron(A_REVERSE);
|
||||||
}
|
}
|
||||||
mvprintw(k+offset, 2, "%s", options[k]);
|
mvprintw(k + offset, 2, "%s", options[k]);
|
||||||
if (k == highlight) {
|
if (k == highlight) {
|
||||||
attroff(A_REVERSE);
|
attroff(A_REVERSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attroff(COLOR_PAIR(2));
|
attroff(COLOR_PAIR(2));
|
||||||
attron(COLOR_PAIR(1));
|
attron(COLOR_PAIR(1));
|
||||||
mvprintw(offset+optionsNumber+extraOptionsNumber, 1, "%s", topText);
|
mvprintw(offset + optionsNumber + extraOptionsNumber, 1, "%s", topText);
|
||||||
attroff(COLOR_PAIR(1));
|
attroff(COLOR_PAIR(1));
|
||||||
key = getch(); //get some int which value correspond to some key being pressed
|
key = getch(); // get some int which value correspond to some key being pressed
|
||||||
|
|
||||||
switch(key) {
|
switch (key) {
|
||||||
case KEY_UP:
|
case KEY_UP:
|
||||||
case 'w':
|
case 'w':
|
||||||
case 'W':
|
case 'W':
|
||||||
highlight--;
|
highlight--;
|
||||||
if (highlight < 0) {
|
if (highlight < 0) {
|
||||||
highlight = optionsNumber + extraOptionsNumber - 1;// can't select the -1nth option
|
highlight = optionsNumber + extraOptionsNumber - 1; // can't select the -1nth option
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
@@ -334,7 +313,7 @@ char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int ex
|
|||||||
goto end_loop;
|
goto end_loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end_loop:
|
end_loop:
|
||||||
endwin(); // end ncurses mode
|
endwin(); // end ncurses mode
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
debug("Selected option: %s", options[highlight]);
|
debug("Selected option: %s", options[highlight]);
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
#ifndef UI_H
|
#ifndef UI_H
|
||||||
#define UI_H
|
#define UI_H
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <regex.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ncurses.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <pwd.h>
|
#include <unistd.h>
|
||||||
#include <ctype.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <regex.h>
|
|
||||||
#include "utils.h"
|
|
||||||
#define CMD_BUFFER 4096 // for fzfSelect
|
#define CMD_BUFFER 4096 // for fzfSelect
|
||||||
#define RESULT_BUFFER 1024 // for fzfSelect
|
#define RESULT_BUFFER 1024 // for fzfSelect
|
||||||
|
|
||||||
@@ -22,24 +22,23 @@ returns the path to the note.
|
|||||||
If you want to bypass the input TUI set bypass to 1 and bypassvalue to the note name
|
If you want to bypass the input TUI set bypass to 1 and bypassvalue to the note name
|
||||||
*/
|
*/
|
||||||
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue, char *journalRegex, int debug);
|
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue, char *journalRegex, int debug);
|
||||||
/*Uses ncurses to get an input from the user.
|
/*
|
||||||
|
Chooses in which directory it want to create the vault.
|
||||||
|
Uses ncurses to get an input from the user.
|
||||||
Creates a new vault with this input.
|
Creates a new vault with this input.
|
||||||
If vault already exists, prints a warning.
|
If vault already exists, prints a warning.
|
||||||
returns nothing. */
|
returns nothing. */
|
||||||
void createNewVault(char *dirToVault, int bypass, char *bypassvalue, int debug);
|
void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount, int bypass, char *bypassvalue, int debug);
|
||||||
// uses fzf and ripgrep to search inside the files to choose between all the options.
|
// uses fzf and ripgrep to search inside the files to choose between all the options.
|
||||||
char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug);
|
char *fzfSelect(char *pathToFiles, char *selectText, int shouldDebug);
|
||||||
/*this function is used multiple times let the user select one options from many with ncurses in a TUI.
|
/*this function is used multiple times let the user select one options from many with ncurses in a
|
||||||
options is the array of strings with all the options.
|
TUI. options is the array of strings with all the options. optionsText is the text that will be
|
||||||
optionsText is the text that will be printed at the top (For example: "Please select ...").
|
printed at the top (For example: "Please select ..."). we distinguish options from extraOptions.
|
||||||
we distinguish options from extraOptions.
|
|
||||||
options could be the list of all notes or all vaults.
|
options could be the list of all notes or all vaults.
|
||||||
extraOptions are options that are special and have a special color (for ex: "Delete vault", "Settings", etc.).
|
extraOptions are options that are special and have a special color (for ex: "Delete vault",
|
||||||
note: extraOptions should be at the end of options.
|
"Settings", etc.). note: extraOptions should be at the end of options. topText is printed between
|
||||||
topText is printed between options text and the start of options.
|
options text and the start of options. middleText (usally \n) is printed between options and
|
||||||
middleText (usally \n) is printed between options and extraOptions.
|
extraOptions. bottomText is printed bellow. topText and middle must be exactly one line. If you want
|
||||||
bottomText is printed bellow.
|
empty lines use " " and not "". returns the selected option. */
|
||||||
topText and middle must be exactly one line. If you want empty lines use " " and not "".
|
char *ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText, int debug);
|
||||||
returns the selected option. */
|
|
||||||
char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText, int debug);
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+292
-58
@@ -1,8 +1,7 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
const char *supportedEditor[] = {"neovim", "vim", "nano"};
|
const char *supportedEditor[] = {"helix", "jed", "kakoune", "micro", "nano", "neovim", "vi", "vim"};
|
||||||
const int numEditors = 3;
|
const int numEditors = 8;
|
||||||
|
|
||||||
int compareString(const void *a, const void *b) {
|
int compareString(const void *a, const void *b) {
|
||||||
const char *str1 = *(const char **)a;
|
const char *str1 = *(const char **)a;
|
||||||
@@ -12,7 +11,7 @@ int compareString(const void *a, const void *b) {
|
|||||||
int reverseCompareString(const void *a, const void *b) {
|
int reverseCompareString(const void *a, const void *b) {
|
||||||
const char *str1 = *(const char **)a;
|
const char *str1 = *(const char **)a;
|
||||||
const char *str2 = *(const char **)b;
|
const char *str2 = *(const char **)b;
|
||||||
return -1*strcmp(str1, str2);
|
return -1 * strcmp(str1, str2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getCurrentTime(int *hour, int *minute, int *second) {
|
void getCurrentTime(int *hour, int *minute, int *second) {
|
||||||
@@ -28,7 +27,7 @@ void _debug(const int d, const char *file, const int line, const char *function,
|
|||||||
if (d) {
|
if (d) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
va_list args; //variadic function stuff
|
va_list args; // variadic function stuff
|
||||||
va_start(args, message);
|
va_start(args, message);
|
||||||
int h, m, s;
|
int h, m, s;
|
||||||
getCurrentTime(&h, &m, &s);
|
getCurrentTime(&h, &m, &s);
|
||||||
@@ -38,7 +37,8 @@ void _debug(const int d, const char *file, const int line, const char *function,
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void _altDebug(const int d, const char *message, ...) { // use for less formal debuggin. (usefull if enumerating or making a list
|
void _altDebug(const int d, const char *message,
|
||||||
|
...) { // use for less formal debuggin. (usefull if enumerating or making a list
|
||||||
if (d) {
|
if (d) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
@@ -49,7 +49,8 @@ void _altDebug(const int d, const char *message, ...) { // use for less formal d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _error(const int shouldDebug, const int condition, const char *type, const char *file, const int line, const char *function, const char *message, ...) { // used for formatted errors
|
void _error(const int shouldDebug, const int condition, const char *type, const char *file, const int line, const char *function, const char *message,
|
||||||
|
...) { // used for formatted errors
|
||||||
if (condition) {
|
if (condition) {
|
||||||
int h, m, s;
|
int h, m, s;
|
||||||
getCurrentTime(&h, &m, &s);
|
getCurrentTime(&h, &m, &s);
|
||||||
@@ -73,20 +74,26 @@ void _error(const int shouldDebug, const int condition, const char *type, const
|
|||||||
|
|
||||||
static void copyDir(const char *source, const char *destination, const char **rsyncArgs, const int rsyncArgsNumber, const int shouldDebug) {
|
static void copyDir(const char *source, const char *destination, const char **rsyncArgs, const int rsyncArgsNumber, const int shouldDebug) {
|
||||||
debug("Backuping... source: %s and destination: %s", source, destination);
|
debug("Backuping... source: %s and destination: %s", source, destination);
|
||||||
|
debug("Rsync has %d extra arguments", rsyncArgsNumber);
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
error(pid < 0, "program", "fork() faild. pid = %d", pid);
|
error(pid < 0, "program", "fork() faild. pid = %d", pid);
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
// Child process: execute rsync
|
// Child process: execute rsync
|
||||||
char **args = malloc((4 + rsyncArgsNumber)*sizeof(char*));
|
char **args = malloc((4 + rsyncArgsNumber) * sizeof(char *));
|
||||||
args[0] = "rsync";
|
args[0] = "rsync";
|
||||||
for (int i = 0; i < rsyncArgsNumber; i++) {
|
for (int i = 0; i < rsyncArgsNumber; i++) {
|
||||||
args[i+1] = (char *)rsyncArgs[i];
|
args[i + 1] = (char *)rsyncArgs[i];
|
||||||
if (i == rsyncArgsNumber-1) { // last loop
|
if (i == rsyncArgsNumber - 1) { // last loop
|
||||||
args[i+2] = (char*)source;
|
args[i + 2] = (char *)source;
|
||||||
args[i+3] = (char*)destination;
|
args[i + 3] = (char *)destination;
|
||||||
args[i+4] = NULL; // execvp expect last arg to be NULL
|
args[i + 4] = NULL; // execvp expect last arg to be NULL
|
||||||
|
debug("rsync command:");
|
||||||
|
for (int k = 0; k <= i + 4; k++) {
|
||||||
|
altDebug("%s ", args[k]);
|
||||||
|
}
|
||||||
|
altDebug("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
execvp("rsync", args);
|
execvp("rsync", args);
|
||||||
@@ -107,19 +114,24 @@ static void ensureDir(const char *path, const int shouldDebug) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void initAppFilesAndDirs(const char *home, const int shouldDebug) {
|
void initAppFilesAndDirs(const char *home, const int shouldDebug) {
|
||||||
|
// sometimes .cache and .config doesn't exist. Such as with github actions machines
|
||||||
char config_dir[PATH_MAX];
|
char config_dir[PATH_MAX];
|
||||||
char cache_dir[PATH_MAX];
|
char cache_dir[PATH_MAX];
|
||||||
|
|
||||||
snprintf(config_dir, sizeof(config_dir),
|
snprintf(config_dir, sizeof(config_dir), "%s/.config/", home);
|
||||||
"%s/.config/notewrapper", home);
|
|
||||||
|
|
||||||
snprintf(cache_dir, sizeof(cache_dir),
|
snprintf(cache_dir, sizeof(cache_dir), "%s/.cache/", home);
|
||||||
"%s/.cache/notewrapper", home);
|
|
||||||
|
|
||||||
ensureDir(config_dir, shouldDebug);
|
ensureDir(config_dir, shouldDebug);
|
||||||
ensureDir(cache_dir, shouldDebug);
|
ensureDir(cache_dir, shouldDebug);
|
||||||
|
|
||||||
char config_file[PATH_MAX+12];
|
strncat(config_dir, "notewrapper/", PATH_MAX);
|
||||||
|
strncat(cache_dir, "notewrapper/", PATH_MAX);
|
||||||
|
|
||||||
|
ensureDir(config_dir, shouldDebug);
|
||||||
|
ensureDir(cache_dir, shouldDebug);
|
||||||
|
|
||||||
|
char config_file[PATH_MAX + 12];
|
||||||
snprintf(config_file, sizeof(config_file), "%s/config.json", config_dir);
|
snprintf(config_file, sizeof(config_file), "%s/config.json", config_dir);
|
||||||
|
|
||||||
FILE *f = fopen(config_file, "r");
|
FILE *f = fopen(config_file, "r");
|
||||||
@@ -130,28 +142,34 @@ void initAppFilesAndDirs(const char *home, const int shouldDebug) {
|
|||||||
debug("Creating default config file.");
|
debug("Creating default config file.");
|
||||||
FILE *w = fopen(config_file, "w");
|
FILE *w = fopen(config_file, "w");
|
||||||
error(!w, "program", "fopen failed opening %s");
|
error(!w, "program", "fopen failed opening %s");
|
||||||
fprintf(w,
|
fprintf(w, // TODO CHANGE default json
|
||||||
"{\n"
|
"{\n"
|
||||||
" \"directory\": \"~/Documents/Notes/\",\n"
|
" \"directory\": [\"~/Documents/\"],\n" // I personally use ~/Documents/Notes. But the
|
||||||
" \"render\": true,\n"
|
// dir doesn't exist most times. So for the
|
||||||
" \"jumpToEndOfFileOnLaunch\": true,\n"
|
// default user it is better to put ~/Documents
|
||||||
" \"editor\": \"neovim\",\n"
|
" \"render\": true,\n"
|
||||||
" \"journalRegex\": \".*journal.*\",\n"
|
" \"jumpToEndOfFileOnLaunch\": true,\n"
|
||||||
" \"dateEntry\": \"# %%Y %%m %%d %%a\",\n"
|
" \"editor\": \"neovim\",\n"
|
||||||
" \"newLineOnOpening\": true,\n"
|
" \"journalRegex\": \".*journal.*\",\n"
|
||||||
" \"backup\": {\n"
|
" \"dateEntry\": \"# %%Y %%m %%d %%a\",\n"
|
||||||
" \"enable\": false,\n"
|
" \"newLineOnOpening\": true,\n"
|
||||||
" \"directory\": \"/path/to/backup\",\n"
|
" \"backup\": {\n"
|
||||||
" \"interval\": \"weekly\",\n"
|
" \"enable\": false,\n"
|
||||||
" \"rsyncArgs\": [\"-Lqah\", \"--update\"]\n"
|
" \"directory\": {\n"
|
||||||
" }\n"
|
" \"~/Documents/\": \"path/to/backup/\"\n"
|
||||||
"}\n");
|
" },\n"
|
||||||
|
" \"interval\": \"weekly\",\n"
|
||||||
|
" \"rsyncArgs\": [\"-Lqah\", \"--update\"]\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n");
|
||||||
|
|
||||||
fclose(w);
|
fclose(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const char *homeDir, const int interval, const char **rsyncArguments, const int rsyncArgumentsNumber, const int shouldDebug) {
|
void handleBackups(char **sourceDirectoryArray, const int sourceNumber, char **destinationDirectoryArray, const char *homeDir, const int interval, const char **rsyncArguments, const int rsyncArgumentsNumber,
|
||||||
|
const int forceBackup, int *backupMessage, const int shouldDebug) {
|
||||||
int shouldBackup = 0;
|
int shouldBackup = 0;
|
||||||
|
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
debug("Time since epoch is %ld", (long)now);
|
debug("Time since epoch is %ld", (long)now);
|
||||||
char cacheFilePATH[PATH_MAX];
|
char cacheFilePATH[PATH_MAX];
|
||||||
@@ -160,6 +178,7 @@ void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const cha
|
|||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(cacheFilePATH, &st) != 0) { // file does not exist
|
if (stat(cacheFilePATH, &st) != 0) { // file does not exist
|
||||||
shouldBackup = 1;
|
shouldBackup = 1;
|
||||||
|
debug("Creating (for the first time) %s", cacheFilePATH);
|
||||||
FILE *cacheFile = fopen(cacheFilePATH, "w");
|
FILE *cacheFile = fopen(cacheFilePATH, "w");
|
||||||
if (!cacheFile) {
|
if (!cacheFile) {
|
||||||
error(1, "program", "Failed to open cache file for writing: %s", cacheFilePATH);
|
error(1, "program", "Failed to open cache file for writing: %s", cacheFilePATH);
|
||||||
@@ -168,6 +187,7 @@ void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const cha
|
|||||||
fclose(cacheFile);
|
fclose(cacheFile);
|
||||||
} else {
|
} else {
|
||||||
FILE *cacheFile = fopen(cacheFilePATH, "r");
|
FILE *cacheFile = fopen(cacheFilePATH, "r");
|
||||||
|
debug("reading %s", cacheFilePATH);
|
||||||
if (!cacheFile) {
|
if (!cacheFile) {
|
||||||
error(1, "program", "Failed to open cache file for reading: %s", cacheFilePATH);
|
error(1, "program", "Failed to open cache file for reading: %s", cacheFilePATH);
|
||||||
}
|
}
|
||||||
@@ -183,8 +203,13 @@ void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const cha
|
|||||||
if (endptr == line || (*endptr != '\n' && *endptr != '\0')) {
|
if (endptr == line || (*endptr != '\n' && *endptr != '\0')) {
|
||||||
error(1, "program", "Invalid timestamp in cache file: %s", line);
|
error(1, "program", "Invalid timestamp in cache file: %s", line);
|
||||||
}
|
}
|
||||||
|
double deltaTime = difftime(now, lastBackupTime);
|
||||||
if (difftime(now, lastBackupTime) > interval) {
|
if (deltaTime > interval || forceBackup) {
|
||||||
|
if (!forceBackup) {
|
||||||
|
debug("(difftime) %f is greater than (interval) %d -> backuping...", deltaTime, interval);
|
||||||
|
} else {
|
||||||
|
debug("Backup was forced. backuping...");
|
||||||
|
}
|
||||||
shouldBackup = 1;
|
shouldBackup = 1;
|
||||||
cacheFile = fopen(cacheFilePATH, "w");
|
cacheFile = fopen(cacheFilePATH, "w");
|
||||||
if (!cacheFile) {
|
if (!cacheFile) {
|
||||||
@@ -192,25 +217,57 @@ void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const cha
|
|||||||
}
|
}
|
||||||
fprintf(cacheFile, "%ld\n", (long)now);
|
fprintf(cacheFile, "%ld\n", (long)now);
|
||||||
fclose(cacheFile);
|
fclose(cacheFile);
|
||||||
|
} else {
|
||||||
|
debug("(difftime) %f is smaller than (interval) %d -> no need to backup.", deltaTime, interval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldBackup) {
|
if (shouldBackup) {
|
||||||
copyDir(pathOfVaults, pathOfBackup, rsyncArguments, rsyncArgumentsNumber, shouldDebug);
|
*backupMessage = 1;
|
||||||
|
debug("A backup is needed");
|
||||||
|
for (int i = 0; i < sourceNumber; i++) {
|
||||||
|
debug("%s", destinationDirectoryArray[i]);
|
||||||
|
if (destinationDirectoryArray[i]) { // if we don't want to backup it, it was set to NULL
|
||||||
|
copyDir(sourceDirectoryArray[i], destinationDirectoryArray[i], rsyncArguments, rsyncArgumentsNumber, shouldDebug);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int doesEditorExist (char *editorToCheck, int shouldDebug) { // Some exectuables have not exaclty the same name as the editor.
|
int isEditorValid(char *editorToCheck, int useDefaultEditor,
|
||||||
char *editor;
|
int shouldDebug) { // check if editor is supported and if it is installed
|
||||||
if (strcmp(editorToCheck, "neovim") == 0) {
|
// check if supported
|
||||||
editor = strdup("nvim"); // we must use strdup and not just copy as we would have modified editorToOpen in main
|
if (useDefaultEditor) { // we use a different error message if it defaulted to $EDITOR
|
||||||
|
error(!isStringInArray(editorToCheck, supportedEditor, numEditors), "user",
|
||||||
|
"%s is not a supported editor.\n(defaulted to $EDITOR as neither \"editor\" was set "
|
||||||
|
"in the configuration file nor -e/--editor <editor> was set.)\n See "
|
||||||
|
"https://github.com/tomasriveral/NoteWrapper#editor-support for a list of supported "
|
||||||
|
"editors.",
|
||||||
|
editorToCheck);
|
||||||
|
} else {
|
||||||
|
error(!isStringInArray(editorToCheck, supportedEditor, numEditors), "user",
|
||||||
|
"%s is not a supported editor.\n See "
|
||||||
|
"https://github.com/tomasriveral/NoteWrapper#editor-support for a list of supported "
|
||||||
|
"editors.",
|
||||||
|
editorToCheck);
|
||||||
}
|
}
|
||||||
else {
|
// check if installed
|
||||||
|
char *editor;
|
||||||
|
if (strcmp(editorToCheck, "neovim") == 0) { // some executables are not name the same as the project
|
||||||
|
editor = strdup("nvim"); // we must use strdup and not just copy as we would have modified
|
||||||
|
// editorToOpen in main
|
||||||
|
} else if (strcmp(editorToCheck, "helix") == 0) {
|
||||||
|
editor = strdup("hx");
|
||||||
|
} else if (strcmp(editorToCheck, "kakoune") == 0) {
|
||||||
|
editor = strdup("kak");
|
||||||
|
} else {
|
||||||
editor = strdup(editorToCheck);
|
editor = strdup(editorToCheck);
|
||||||
}
|
}
|
||||||
char *path_env = getenv("PATH");
|
char *path_env = getenv("PATH");
|
||||||
error(!path_env, "program", "getenv(\"PATH\") failed to get your path. NoteWrapper is unable to check if your desired editor is installed\n");
|
error(!path_env, "program",
|
||||||
debug("Your PATH is %s\n", path_env);
|
"getenv(\"PATH\") failed to get your path. NoteWrapper is unable to check if your "
|
||||||
|
"desired editor is installed\n");
|
||||||
|
debug("Your PATH is %s", path_env);
|
||||||
char *paths = strdup(path_env); // duplicate because strtok modifies the string
|
char *paths = strdup(path_env); // duplicate because strtok modifies the string
|
||||||
char *dir = strtok(paths, ":");
|
char *dir = strtok(paths, ":");
|
||||||
while (dir) {
|
while (dir) {
|
||||||
@@ -266,7 +323,10 @@ void appendToFile(const char *path, const char *string, const int shouldDebug) {
|
|||||||
FILE *file = fopen(path, "r");
|
FILE *file = fopen(path, "r");
|
||||||
char lastLine[1024] = {0};
|
char lastLine[1024] = {0};
|
||||||
error(file == NULL, "program", "could not open %s", path);
|
error(file == NULL, "program", "could not open %s", path);
|
||||||
char buffer[BUFFER_SIZE]; // it does not matter if we have a small buffer. string is relatively small (most time \n or the name of the file). So it is under BUFFER_SIZE. If the last line is more than BUFFER_SIZE. It can't be equal to string
|
char buffer[BUFFER_SIZE]; // it does not matter if we have a small buffer. string is relatively
|
||||||
|
// small (most time \n or the name of the file). So it is under
|
||||||
|
// BUFFER_SIZE. If the last line is more than BUFFER_SIZE. It can't be
|
||||||
|
// equal to string
|
||||||
|
|
||||||
// Read file line by line to get the last one
|
// Read file line by line to get the last one
|
||||||
while (fgets(buffer, sizeof(buffer), file) != NULL) {
|
while (fgets(buffer, sizeof(buffer), file) != NULL) {
|
||||||
@@ -315,7 +375,8 @@ int unlink_cb(const char *filePath, const struct stat *sb, int typeflag, struct
|
|||||||
(void)typeflag;
|
(void)typeflag;
|
||||||
(void)ftwbuf;
|
(void)ftwbuf;
|
||||||
|
|
||||||
int shouldDebug = 1; //normally shouldDebug should be passed to the function. However, it's to difficult here and the best it to set it to 1.
|
int shouldDebug = 1; // normally shouldDebug should be passed to the function. However, it's to
|
||||||
|
// difficult here and the best it to set it to 1.
|
||||||
|
|
||||||
// some sanity checks to be sure that we don't delete something we shouldn't
|
// some sanity checks to be sure that we don't delete something we shouldn't
|
||||||
error(strcmp(filePath, "") == 0, "program", "filePath is empty. Refusing to delete directory");
|
error(strcmp(filePath, "") == 0, "program", "filePath is empty. Refusing to delete directory");
|
||||||
@@ -331,14 +392,16 @@ int rmrf(char *path, int shouldDebug) {
|
|||||||
return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
|
return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int openEditor(char *path, char *editor, int render, int shouldJumpToEndOfFile, int shouldDebug) {
|
int openEditor(char *path, char *editor, int render, int shouldJumpToEndOfFile, int shouldDebug) {
|
||||||
|
|
||||||
// this ensures that ncurses won't affect the editor behaviour
|
// this ensures that ncurses won't affect the editor behaviour
|
||||||
pid_t editor_pid = fork();
|
pid_t editor_pid = fork();
|
||||||
error(editor_pid < 0, "program", "fork() failed.");
|
error(editor_pid < 0, "program", "fork() failed.");
|
||||||
|
|
||||||
if (editor_pid == 0) {
|
// instead of reusing part of the codes, for any new editor copy an example and adapt it. This
|
||||||
|
// is in case we need a custom fix for an editor.
|
||||||
|
|
||||||
|
if (editor_pid == 0) {
|
||||||
// =========================
|
// =========================
|
||||||
// CHILD: launch editor
|
// CHILD: launch editor
|
||||||
// =========================
|
// =========================
|
||||||
@@ -365,6 +428,30 @@ if (editor_pid == 0) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error(1, "program", "execlp() failed.");
|
||||||
|
// ---- MICRO ----
|
||||||
|
} else if (strcmp(editor, "micro") == 0) {
|
||||||
|
|
||||||
|
if (render) {
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
debug("Running MICRO_VIVIFY=1 micro %s +99999", path);
|
||||||
|
setenv("MICRO_VIVIFY", "1", 1);
|
||||||
|
execlp("micro", "micro", path, "+999999", NULL);
|
||||||
|
} else {
|
||||||
|
debug("Running MICRO_VIVIFY=1 micro %s", path);
|
||||||
|
setenv("MICRO_VIVIFY", "1", 1);
|
||||||
|
execlp("micro", "micro", path, NULL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
debug("Running micro %s +999999", path);
|
||||||
|
execlp("micro", "micro", path, "+999999", NULL);
|
||||||
|
} else {
|
||||||
|
debug("Running micro %s", path);
|
||||||
|
execlp("micro", "micro", path, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
error(1, "program", "execlp() failed.");
|
error(1, "program", "execlp() failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,21 +460,19 @@ if (editor_pid == 0) {
|
|||||||
|
|
||||||
// If render enabled → spawn viv in parallel
|
// If render enabled → spawn viv in parallel
|
||||||
if (render) {
|
if (render) {
|
||||||
debug("Running the editor...");
|
debug("Running Vivify...");
|
||||||
pid_t viv_pid = fork();
|
pid_t viv_pid = fork();
|
||||||
error(viv_pid < 0, "program", "fork() failed.");
|
error(viv_pid < 0, "program", "fork() failed.");
|
||||||
|
|
||||||
if (viv_pid == 0) {
|
if (viv_pid == 0) {
|
||||||
// GRANDCHILD → viv
|
// GRANDCHILD → viv
|
||||||
|
|
||||||
|
|
||||||
char viv_path[PATH_MAX];
|
char viv_path[PATH_MAX];
|
||||||
strncpy(viv_path, path, PATH_MAX - 1);
|
strncpy(viv_path, path, PATH_MAX - 1);
|
||||||
viv_path[PATH_MAX - 1] = '\0';
|
viv_path[PATH_MAX - 1] = '\0';
|
||||||
|
|
||||||
if (shouldJumpToEndOfFile) {
|
if (shouldJumpToEndOfFile) {
|
||||||
strncat(viv_path, ":99999",
|
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||||
PATH_MAX - strlen(viv_path) - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("Running viv %s", viv_path);
|
debug("Running viv %s", viv_path);
|
||||||
@@ -407,10 +492,158 @@ if (editor_pid == 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
error(1, "program", "execlp() failed.");
|
error(1, "program", "execlp() failed.");
|
||||||
|
// ---- HELIX ----
|
||||||
|
} else if (strcmp(editor, "helix") == 0) {
|
||||||
|
|
||||||
|
// If render enabled → spawn viv in parallel
|
||||||
|
if (render) {
|
||||||
|
debug("Running Vivify...");
|
||||||
|
pid_t viv_pid = fork();
|
||||||
|
error(viv_pid < 0, "program", "fork() failed.");
|
||||||
|
|
||||||
|
if (viv_pid == 0) {
|
||||||
|
// GRANDCHILD → viv
|
||||||
|
|
||||||
|
char viv_path[PATH_MAX];
|
||||||
|
strncpy(viv_path, path, PATH_MAX - 1);
|
||||||
|
viv_path[PATH_MAX - 1] = '\0';
|
||||||
|
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug("Running viv %s", viv_path);
|
||||||
|
execlp("viv", "viv", viv_path, NULL);
|
||||||
|
error(1, "program", "execlp() failed.");
|
||||||
|
}
|
||||||
|
// IMPORTANT: do NOT wait for viv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now run helix (this replaces the child process)
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
strncat(path, ":99999", PATH_MAX);
|
||||||
|
debug("Running hx %s", path);
|
||||||
|
execlp("hx", "hx", path, NULL);
|
||||||
|
} else {
|
||||||
|
debug("Running hx %s", path);
|
||||||
|
execlp("hx", "hx", path, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(1, "program", "execlp() failed.");
|
||||||
|
}
|
||||||
|
// ---- kakoune ----
|
||||||
|
else if (strcmp(editor, "kakoune") == 0) {
|
||||||
|
|
||||||
|
// If render enabled → spawn viv in parallel
|
||||||
|
if (render) {
|
||||||
|
debug("Running Vivify...");
|
||||||
|
pid_t viv_pid = fork();
|
||||||
|
error(viv_pid < 0, "program", "fork() failed.");
|
||||||
|
|
||||||
|
if (viv_pid == 0) {
|
||||||
|
// GRANDCHILD → viv
|
||||||
|
|
||||||
|
char viv_path[PATH_MAX];
|
||||||
|
strncpy(viv_path, path, PATH_MAX - 1);
|
||||||
|
viv_path[PATH_MAX - 1] = '\0';
|
||||||
|
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("Running viv %s", viv_path);
|
||||||
|
execlp("viv", "viv", viv_path, NULL);
|
||||||
|
error(1, "program", "execlp() failed.");
|
||||||
|
}
|
||||||
|
// IMPORTANT: do NOT wait for viv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now run nano (this replaces the child process)
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
debug("Running kak + %s", path);
|
||||||
|
execlp("kak", "kak", "+", path, NULL);
|
||||||
|
} else {
|
||||||
|
debug("Running kak %s", path);
|
||||||
|
execlp("kak", "kak", path, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(1, "program", "execlp() failed.");
|
||||||
|
// ---- VI ----
|
||||||
|
} else if (strcmp(editor, "vi") == 0) {
|
||||||
|
|
||||||
|
// If render enabled → spawn viv in parallel
|
||||||
|
if (render) {
|
||||||
|
debug("Running Vivify...");
|
||||||
|
pid_t viv_pid = fork();
|
||||||
|
error(viv_pid < 0, "program", "fork() failed.");
|
||||||
|
|
||||||
|
if (viv_pid == 0) {
|
||||||
|
// GRANDCHILD → viv
|
||||||
|
|
||||||
|
char viv_path[PATH_MAX];
|
||||||
|
strncpy(viv_path, path, PATH_MAX - 1);
|
||||||
|
viv_path[PATH_MAX - 1] = '\0';
|
||||||
|
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("Running viv %s", viv_path);
|
||||||
|
execlp("viv", "viv", viv_path, NULL);
|
||||||
|
error(1, "program", "execlp() failed.");
|
||||||
|
}
|
||||||
|
// IMPORTANT: do NOT wait for viv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now run vi (this replaces the child process)
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
debug("Running vi +:$ %s", path);
|
||||||
|
execlp("vi", "vi", "+:$", path, NULL);
|
||||||
|
} else {
|
||||||
|
debug("Running vi %s", path);
|
||||||
|
execlp("vi", "vi", path, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(1, "program", "execlp() failed.");
|
||||||
|
// ---- Jed ----
|
||||||
|
} else if (strcmp(editor, "jed") == 0) {
|
||||||
|
|
||||||
|
// If render enabled → spawn viv in parallel
|
||||||
|
if (render) {
|
||||||
|
debug("Running Vivify...");
|
||||||
|
pid_t viv_pid = fork();
|
||||||
|
error(viv_pid < 0, "program", "fork() failed.");
|
||||||
|
|
||||||
|
if (viv_pid == 0) {
|
||||||
|
// GRANDCHILD → viv
|
||||||
|
|
||||||
|
char viv_path[PATH_MAX];
|
||||||
|
strncpy(viv_path, path, PATH_MAX - 1);
|
||||||
|
viv_path[PATH_MAX - 1] = '\0';
|
||||||
|
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("Running viv %s", viv_path);
|
||||||
|
execlp("viv", "viv", viv_path, NULL);
|
||||||
|
error(1, "program", "execlp() failed.");
|
||||||
|
}
|
||||||
|
// IMPORTANT: do NOT wait for viv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now run jed (this replaces the child process)
|
||||||
|
if (shouldJumpToEndOfFile) {
|
||||||
|
debug("Running jed %s -g 99999999", path);
|
||||||
|
execlp("jed", "jed", path, "-g", "99999999", NULL);
|
||||||
|
} else {
|
||||||
|
debug("Running jed %s", path);
|
||||||
|
execlp("jed", "jed", path, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(1, "program", "execlp() failed.");
|
||||||
// ---- UNKNOWN EDITOR ----
|
// ---- UNKNOWN EDITOR ----
|
||||||
else {
|
} else {
|
||||||
error(1, "program", "Unknown editor.");
|
error(1, "program", "Unknown editor.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,7 +652,8 @@ if (editor_pid == 0) {
|
|||||||
// PARENT: wait ONLY editor
|
// PARENT: wait ONLY editor
|
||||||
// =========================
|
// =========================
|
||||||
int status;
|
int status;
|
||||||
while (waitpid(editor_pid, &status, 0) == -1) { // we can't just use waitpid(). Because when resizing the terminal, waitpid() is returned so we loop to see if there is not a problem
|
while (waitpid(editor_pid, &status, 0) == -1) { // we can't just use waitpid(). Because when resizing the terminal, waitpid() is
|
||||||
|
// returned so we loop to see if there is not a problem
|
||||||
if (errno != EINTR) {
|
if (errno != EINTR) {
|
||||||
perror("waitpid");
|
perror("waitpid");
|
||||||
break;
|
break;
|
||||||
|
|||||||
+36
-30
@@ -1,52 +1,53 @@
|
|||||||
#ifndef UTILS_H
|
#ifndef UTILS_H
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <cjson/cJSON.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ftw.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <regex.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <dirent.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <ftw.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <ncurses.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <limits.h>
|
|
||||||
#include <cjson/cJSON.h>
|
|
||||||
#include <regex.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <ftw.h>
|
#include <unistd.h>
|
||||||
#define BUFFER_SIZE 256 //standard buffer size.
|
#ifndef VERSION
|
||||||
// the three supported values are "daily" "weekly" and "monthly" (for months we use the avarage lenght of a month in a non leap year). All values were calculated on my loyal TI-30 ECO RS
|
#define VERSION "dev"
|
||||||
|
#endif
|
||||||
|
#define BUFFER_SIZE 256 // standard buffer size.
|
||||||
|
// the three supported values are "daily" "weekly" and "monthly" (for months we use the avarage
|
||||||
|
// lenght of a month in a non leap year). All values were calculated on my loyal TI-30 ECO RS
|
||||||
#define DAILY 86400
|
#define DAILY 86400
|
||||||
#define WEEKLY 604800
|
#define WEEKLY 604800
|
||||||
#define MONTHLY 2628000 // calculated from the average lenght of a non leap year
|
#define MONTHLY 2628000 // calculated from the average lenght of a non leap year
|
||||||
#define debug(message, ...) \
|
#define debug(message, ...) _debug(shouldDebug, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||||
_debug(shouldDebug, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
#define altDebug(message, ...) _altDebug(shouldDebug, message, ##__VA_ARGS__)
|
||||||
#define altDebug(message, ...) \
|
#define error(condition, type, message, ...) _error(shouldDebug, condition, type, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||||
_altDebug(shouldDebug, message, ##__VA_ARGS__)
|
|
||||||
#define error(condition, type, message, ...) \
|
|
||||||
_error(shouldDebug, condition, type, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
|
||||||
// you must edit this two values if you want to add suport for an editor
|
// you must edit this two values if you want to add suport for an editor
|
||||||
extern const char *supportedEditor[]; // array of supported editors
|
extern const char *supportedEditor[]; // array of supported editors
|
||||||
extern const int numEditors; // number of supported editors
|
extern const int numEditors; // number of supported editors
|
||||||
|
|
||||||
|
// compares two strings alphabetically.
|
||||||
//compares two strings alphabetically.
|
// this function is used for qsort
|
||||||
//this function is used for qsort
|
|
||||||
int compareString(const void *a, const void *b);
|
int compareString(const void *a, const void *b);
|
||||||
// compares two strings in reversed alphabetical order
|
// compares two strings in reversed alphabetical order
|
||||||
// this function is used for qsort
|
// this function is used for qsort
|
||||||
int reverseCompareString(const void *a, const void *b);
|
int reverseCompareString(const void *a, const void *b);
|
||||||
|
// checks if editor is supported and if it installed.
|
||||||
// this basically checks all the dirs from your path for the editor. This is a safety check.
|
// this basically checks all the dirs from your path for the editor. This is a safety check.
|
||||||
// If the executable from an editor is not the editor name (for example neovim and nvim), you must handle at the start of the function.
|
// If the executable from an editor is not the editor name (for example neovim and nvim), you must
|
||||||
int doesEditorExist(char *editorToCheck, int debug);
|
// handle at the start of the function. This can return an error and stop the program.
|
||||||
|
int isEditorValid(char *editorToCheck, int useDefaultEditor, int debug);
|
||||||
// please use the macro debug instead of _debug.
|
// please use the macro debug instead of _debug.
|
||||||
//formated debugging.
|
// formated debugging.
|
||||||
void _debug(const int d, const char *file, const int line, const char *function, const char *message, ...);
|
void _debug(const int d, const char *file, const int line, const char *function, const char *message, ...);
|
||||||
//use for less formal debuggin. (usefull if enumerating or making a list).
|
// use for less formal debuggin. (usefull if enumerating or making a list).
|
||||||
void _altDebug(const int d, const char *message, ...);
|
void _altDebug(const int d, const char *message, ...);
|
||||||
// formated error.
|
// formated error.
|
||||||
void _error(const int shouldDebug, const int condition, const char *type, const char *file, const int line, const char *function, const char *message, ...);
|
void _error(const int shouldDebug, const int condition, const char *type, const char *file, const int line, const char *function, const char *message, ...);
|
||||||
@@ -63,8 +64,8 @@ void appendToFile(const char *path, const char *string, const int shouldDebug);
|
|||||||
// replace unwanted chars by '_'.
|
// replace unwanted chars by '_'.
|
||||||
// '.' is replaced if it is only the first two chars
|
// '.' is replaced if it is only the first two chars
|
||||||
void sanitize(char *string);
|
void sanitize(char *string);
|
||||||
//from https://stackoverflow.com/a/5467788.
|
// from https://stackoverflow.com/a/5467788.
|
||||||
//deletes an entire directory. Use with parsimony and carefullness.
|
// deletes an entire directory. Use with parsimony and carefullness.
|
||||||
int rmrf(char *path, int shouldDebug);
|
int rmrf(char *path, int shouldDebug);
|
||||||
// Inputs are the path to the file, the editor to open and some rendering option.
|
// Inputs are the path to the file, the editor to open and some rendering option.
|
||||||
// render: if we render the .md file with Vivify.
|
// render: if we render the .md file with Vivify.
|
||||||
@@ -77,6 +78,11 @@ int openEditor(char *path, char *editor, int render, int shouldJumpToEndOfFile,
|
|||||||
char *getFormatedTime(char *format, int shouldDebug);
|
char *getFormatedTime(char *format, int shouldDebug);
|
||||||
/* Caclulates if we need to do another backup (by reading ~/.cache/NoteWrapper/backupTime.txt.
|
/* Caclulates if we need to do another backup (by reading ~/.cache/NoteWrapper/backupTime.txt.
|
||||||
If need launches in the background rsync to do the backuping.
|
If need launches in the background rsync to do the backuping.
|
||||||
rsyncArgs is the array of arguments to be passed to rsync. Do not inclue destination or source. It will be added in the function.*/
|
Each pair of source/destination will launch one rsync process.
|
||||||
void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const char *homeDir, const int interval, const char **rsyncArgs, const int rsyncArgsNumber, const int shouldDebug);
|
If a pair don't need to be backed up, the destination should be set to NULL.
|
||||||
|
rsyncArgs is the array of arguments to be passed to rsync. Do not inclue destination or source. It
|
||||||
|
will be added in the function.
|
||||||
|
backupMessage will be set to 1 if a backup was launched.*/
|
||||||
|
void handleBackups(char **sourceDirectoryArray, const int sourceDirectoryNumber, char **destinationDirectoryArray, const char *homeDir, const int interval, const char **rsyncArgs, const int rsyncArgsNumber,
|
||||||
|
const int forceBackup, int *backupMessage, const int shouldDebug);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user