├── plugins ├── inisync │ ├── qpmx.json │ ├── inisync.json │ ├── .tsdummy.pro.dummy │ ├── inisynchelperplugin.cpp │ ├── inisynchelperplugin.h │ ├── inisync.pro │ ├── conflip_inisync_template.ts │ ├── conflip_inisync_de.ts │ └── inisynchelper.h ├── jsonsync │ ├── qpmx.json │ ├── jsonsync.json │ ├── .tsdummy.pro.dummy │ ├── jsonsynchelperplugin.h │ ├── jsonsynchelperplugin.cpp │ ├── jsonsync.pro │ ├── conflip_jsonsync_template.ts │ ├── conflip_jsonsync_de.ts │ └── jsonsynchelper.h ├── pathsync │ ├── qpmx.json │ ├── pathsync.json │ ├── .tsdummy.pro.dummy │ ├── pathsynchelperplugin.h │ ├── pathsynchelperplugin.cpp │ ├── pathsync.pro │ ├── conflip_pathsync_template.ts │ ├── conflip_pathsync_de.ts │ └── pathsynchelper.h ├── xmlsync │ ├── qpmx.json │ ├── xmlsync.json │ ├── .tsdummy.pro.dummy │ ├── xmlsynchelperplugin.cpp │ ├── xmlsynchelperplugin.h │ ├── xmlsync.pro │ ├── conflip_xmlsync_template.ts │ ├── xmlsynchelper.h │ └── conflip_xmlsync_de.ts ├── dconfsync │ ├── qpmx.json │ ├── dconfsync.json │ ├── .tsdummy.pro.dummy │ ├── dconfsynchelperplugin.h │ ├── conflip_dconfsync_template.ts │ ├── dconfsynchelperplugin.cpp │ ├── conflip_dconfsync_de.ts │ ├── dconfaccess.h │ ├── dconfsync.pro │ ├── dconfsynchelper.h │ └── dconfaccess.cpp └── plugins.pro ├── test ├── backup │ ├── dirs │ │ ├── hidden_copy │ │ │ ├── .file0 │ │ │ ├── file1 │ │ │ ├── file2 │ │ │ └── file3 │ │ ├── match_dirs │ │ │ ├── dirs_file.txt │ │ │ ├── nodirs_file.txt │ │ │ ├── dirs_dir │ │ │ │ └── dirs_file.txt │ │ │ └── nodirs_dir │ │ │ │ └── nodirs_file.txt │ │ ├── basic_symlink │ │ │ ├── .file0 │ │ │ ├── file1 │ │ │ ├── file2 │ │ │ └── file3 │ │ ├── hidden_symlink │ │ │ ├── .file0 │ │ │ ├── file1 │ │ │ ├── file2 │ │ │ └── file3 │ │ ├── recurse_copy │ │ │ ├── dir1 │ │ │ │ ├── subdir │ │ │ │ │ ├── exclude_absolute.txt │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── exclude_pattern_42.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ ├── file5.x.txt │ │ │ │ │ └── dir1 │ │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ └── file5.x.txt │ │ │ │ ├── exclude_absolute │ │ │ │ │ └── nostuff.txt │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ └── nostuff.txt │ │ │ │ └── dir1 │ │ │ │ │ ├── exclude_absolute │ │ │ │ │ └── nostuff.txt │ │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ └── nostuff.txt │ │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ ├── dir2 │ │ │ │ ├── subdir │ │ │ │ │ ├── exclude_absolute.txt │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── exclude_pattern_42.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ ├── file5.x.txt │ │ │ │ │ └── dir1 │ │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ └── file5.x.txt │ │ │ │ ├── exclude_absolute │ │ │ │ │ └── nostuff.txt │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ └── nostuff.txt │ │ │ │ └── dir1 │ │ │ │ │ ├── exclude_absolute │ │ │ │ │ └── nostuff.txt │ │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ └── nostuff.txt │ │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ ├── dir3 │ │ │ │ ├── subdir │ │ │ │ │ ├── exclude_absolute.txt │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── exclude_pattern_42.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ ├── file5.x.txt │ │ │ │ │ └── dir1 │ │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ └── file5.x.txt │ │ │ │ ├── exclude_absolute │ │ │ │ │ └── nostuff.txt │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ └── nostuff.txt │ │ │ │ └── dir1 │ │ │ │ │ ├── exclude_absolute │ │ │ │ │ └── nostuff.txt │ │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ └── nostuff.txt │ │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ ├── exclude_absolute │ │ │ │ └── nostuff.txt │ │ │ └── exclude_pattern_42 │ │ │ │ └── nostuff.txt │ │ ├── recurse_symlink │ │ │ ├── dir2 │ │ │ │ ├── subdir │ │ │ │ │ ├── exclude_absolute │ │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ ├── file5.x.txt │ │ │ │ │ └── dir1 │ │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ └── file5.x.txt │ │ │ │ ├── exclude_absolute │ │ │ │ │ └── nostuff.txt │ │ │ │ ├── dir1 │ │ │ │ │ ├── exclude_absolute │ │ │ │ │ │ └── nostuff.txt │ │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ │ └── nostuff.txt │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ └── file5.x.txt │ │ │ │ └── exclude_pattern_42 │ │ │ │ │ └── nostuff.txt │ │ │ ├── dir3 │ │ │ │ ├── subdir │ │ │ │ │ ├── exclude_absolute │ │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ ├── file5.x.txt │ │ │ │ │ └── dir1 │ │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ └── file5.x.txt │ │ │ │ ├── exclude_absolute │ │ │ │ │ └── nostuff.txt │ │ │ │ ├── dir1 │ │ │ │ │ ├── exclude_absolute │ │ │ │ │ │ └── nostuff.txt │ │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ │ └── nostuff.txt │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ └── file5.x.txt │ │ │ │ └── exclude_pattern_42 │ │ │ │ │ └── nostuff.txt │ │ │ ├── exclude_absolute │ │ │ │ └── nostuff.txt │ │ │ ├── dir1 │ │ │ │ ├── exclude_absolute │ │ │ │ │ └── nostuff.txt │ │ │ │ ├── subdir │ │ │ │ │ ├── exclude_absolute.txt │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── exclude_pattern_42.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ ├── file5.x.txt │ │ │ │ │ └── dir1 │ │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ └── file5.x.txt │ │ │ │ ├── dir1 │ │ │ │ │ ├── exclude_absolute │ │ │ │ │ │ └── nostuff.txt │ │ │ │ │ ├── exclude_pattern_42 │ │ │ │ │ │ └── nostuff.txt │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ └── file5.x.txt │ │ │ │ └── exclude_pattern_42 │ │ │ │ │ └── nostuff.txt │ │ │ └── exclude_pattern_42 │ │ │ │ └── nostuff.txt │ │ ├── pattern_copy │ │ │ ├── dir1 │ │ │ │ ├── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ ├── file5.x.txt │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ └── file5.x.txt │ │ │ │ └── dir1 │ │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ ├── dir2 │ │ │ │ ├── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ ├── file5.x.txt │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ └── file5.x.txt │ │ │ │ └── dir1 │ │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ ├── dir3 │ │ │ │ ├── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ ├── file5.x.txt │ │ │ │ │ └── subdir │ │ │ │ │ │ ├── file1.txt │ │ │ │ │ │ ├── file2.txt │ │ │ │ │ │ ├── file3.txt │ │ │ │ │ │ ├── file4.log │ │ │ │ │ │ ├── .file0.txt │ │ │ │ │ │ └── file5.x.txt │ │ │ │ └── dir1 │ │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ └── special │ │ │ │ ├── subdir │ │ │ │ ├── .file0.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file2.txt │ │ │ │ ├── file3.txt │ │ │ │ ├── file4.log │ │ │ │ ├── file5.x.txt │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ │ └── dir1 │ │ │ │ └── subdir │ │ │ │ ├── .file0.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file2.txt │ │ │ │ ├── file3.txt │ │ │ │ ├── file4.log │ │ │ │ └── file5.x.txt │ │ └── pattern_symlink │ │ │ ├── dir1 │ │ │ ├── subdir │ │ │ │ ├── .file0.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file2.txt │ │ │ │ ├── file3.txt │ │ │ │ ├── file4.log │ │ │ │ ├── file5.x.txt │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ └── dir1 │ │ │ │ └── subdir │ │ │ │ ├── .file0.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file2.txt │ │ │ │ ├── file3.txt │ │ │ │ ├── file4.log │ │ │ │ └── file5.x.txt │ │ │ ├── dir2 │ │ │ ├── subdir │ │ │ │ ├── .file0.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file2.txt │ │ │ │ ├── file3.txt │ │ │ │ ├── file4.log │ │ │ │ ├── file5.x.txt │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ └── dir1 │ │ │ │ └── subdir │ │ │ │ ├── .file0.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file2.txt │ │ │ │ ├── file3.txt │ │ │ │ ├── file4.log │ │ │ │ └── file5.x.txt │ │ │ ├── dir3 │ │ │ ├── subdir │ │ │ │ ├── .file0.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file2.txt │ │ │ │ ├── file3.txt │ │ │ │ ├── file4.log │ │ │ │ ├── file5.x.txt │ │ │ │ └── subdir │ │ │ │ │ ├── .file0.txt │ │ │ │ │ ├── file1.txt │ │ │ │ │ ├── file2.txt │ │ │ │ │ ├── file3.txt │ │ │ │ │ ├── file4.log │ │ │ │ │ └── file5.x.txt │ │ │ └── dir1 │ │ │ │ └── subdir │ │ │ │ ├── .file0.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file2.txt │ │ │ │ ├── file3.txt │ │ │ │ ├── file4.log │ │ │ │ └── file5.x.txt │ │ │ └── special │ │ │ ├── subdir │ │ │ ├── .file0.txt │ │ │ ├── file1.txt │ │ │ ├── file2.txt │ │ │ ├── file3.txt │ │ │ ├── file4.log │ │ │ ├── file5.x.txt │ │ │ └── subdir │ │ │ │ ├── .file0.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file2.txt │ │ │ │ ├── file3.txt │ │ │ │ ├── file4.log │ │ │ │ └── file5.x.txt │ │ │ └── dir1 │ │ │ └── subdir │ │ │ ├── .file0.txt │ │ │ ├── file1.txt │ │ │ ├── file2.txt │ │ │ ├── file3.txt │ │ │ ├── file4.log │ │ │ └── file5.x.txt │ ├── files │ │ ├── copy_file.txt │ │ ├── symlink_file.txt │ │ ├── copy_pattern_1.txt │ │ ├── copy_pattern_11.txt │ │ ├── copy_pattern_111.txt │ │ ├── symlink_pattern_a.txt │ │ ├── symlink_pattern_b.txt │ │ ├── symlink_pattern_c.txt │ │ ├── Copy_File_Sensitive.txt │ │ └── Symlink_File_Sensitive.txt │ ├── json │ │ ├── object.qbjs │ │ ├── object.json │ │ └── array.json │ ├── ini │ │ └── test.ini │ ├── xml │ │ └── test.xml │ └── repo │ │ └── config.json ├── .gitignore ├── json │ ├── object.qbjs │ ├── object.json │ └── array.json └── cleanup.sh ├── lib ├── conflipdatabase.cpp ├── lib_conflip_global.h ├── conflip.pc.in ├── qpmx.json ├── conflip.h ├── conflipdatabase.h ├── synchelperplugin.h ├── settings.xml ├── syncentry.h ├── syncentry.cpp ├── lib.pro ├── conflip.cpp ├── synchelper.h └── synchelper.cpp ├── daemon ├── qpmx.json ├── scinstall_conflip.bat.in ├── main.cpp ├── conflip.plist.in ├── conflip.service.in ├── conflipservice.h ├── pathresolver.h ├── syncengine.h ├── daemon.pro ├── conflipservice.cpp ├── pathresolver.cpp └── syncengine.cpp ├── lib.pri ├── Conflip.pro ├── gui ├── conflip.desktop ├── qpmx.json ├── createentrydialog.h ├── mainwindow.h ├── gui.pro ├── main.cpp ├── createentrydialog.cpp ├── conflip.svg ├── mainwindow.ui └── createentrydialog.ui ├── .gitignore ├── README.md ├── LICENSE └── .qmake.conf /plugins/inisync/qpmx.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /plugins/jsonsync/qpmx.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /plugins/pathsync/qpmx.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /plugins/xmlsync/qpmx.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /plugins/dconfsync/qpmx.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/backup/dirs/hidden_copy/.file0: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/hidden_copy/file1: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/hidden_copy/file2: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/hidden_copy/file3: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/match_dirs/dirs_file.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/backup/files/copy_file.txt: -------------------------------------------------------------------------------- 1 | copy_file.txt -------------------------------------------------------------------------------- /test/backup/dirs/basic_symlink/.file0: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/basic_symlink/file1: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/basic_symlink/file2: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/basic_symlink/file3: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/hidden_symlink/.file0: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/hidden_symlink/file1: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/hidden_symlink/file2: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/hidden_symlink/file3: -------------------------------------------------------------------------------- 1 | basic_symlink -------------------------------------------------------------------------------- /test/backup/dirs/match_dirs/nodirs_file.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/backup/files/symlink_file.txt: -------------------------------------------------------------------------------- 1 | symlink_file.txt -------------------------------------------------------------------------------- /lib/conflipdatabase.cpp: -------------------------------------------------------------------------------- 1 | #include "conflipdatabase.h" 2 | -------------------------------------------------------------------------------- /test/backup/dirs/match_dirs/dirs_dir/dirs_file.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/backup/files/copy_pattern_1.txt: -------------------------------------------------------------------------------- 1 | copy_pattern_*.txt -------------------------------------------------------------------------------- /test/backup/files/copy_pattern_11.txt: -------------------------------------------------------------------------------- 1 | copy_pattern_*.txt -------------------------------------------------------------------------------- /test/backup/files/copy_pattern_111.txt: -------------------------------------------------------------------------------- 1 | copy_pattern_*.txt -------------------------------------------------------------------------------- /test/backup/dirs/match_dirs/nodirs_dir/nodirs_file.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/backup/files/symlink_pattern_a.txt: -------------------------------------------------------------------------------- 1 | symlink_pattern_*.txt -------------------------------------------------------------------------------- /test/backup/files/symlink_pattern_b.txt: -------------------------------------------------------------------------------- 1 | symlink_pattern_*.txt -------------------------------------------------------------------------------- /test/backup/files/symlink_pattern_c.txt: -------------------------------------------------------------------------------- 1 | symlink_pattern_*.txt -------------------------------------------------------------------------------- /plugins/inisync/inisync.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys" : [ "ini" ] 3 | } 4 | -------------------------------------------------------------------------------- /plugins/xmlsync/xmlsync.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys" : [ "xml" ] 3 | } 4 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | /dirs 2 | /files 3 | /repo 4 | /ini 5 | /xml 6 | -------------------------------------------------------------------------------- /test/backup/files/Copy_File_Sensitive.txt: -------------------------------------------------------------------------------- 1 | copy_file_sensitive.txt -------------------------------------------------------------------------------- /plugins/dconfsync/dconfsync.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys" : [ "dconf" ] 3 | } 4 | -------------------------------------------------------------------------------- /test/backup/files/Symlink_File_Sensitive.txt: -------------------------------------------------------------------------------- 1 | symlink_file_sensitive.txt -------------------------------------------------------------------------------- /plugins/jsonsync/jsonsync.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys" : [ "json", "qbjs" ] 3 | } 4 | -------------------------------------------------------------------------------- /plugins/pathsync/pathsync.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys" : [ "symlink", "copy" ] 3 | } 4 | -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/exclude_absolute.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/exclude_absolute.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/exclude_absolute.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/exclude_absolute: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/exclude_absolute: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/exclude_pattern_42.txt: -------------------------------------------------------------------------------- 1 | exclude_pattern_42 -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/exclude_pattern_42.txt: -------------------------------------------------------------------------------- 1 | exclude_pattern_42 -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/exclude_pattern_42.txt: -------------------------------------------------------------------------------- 1 | exclude_pattern_42 -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/exclude_absolute.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/exclude_pattern_42: -------------------------------------------------------------------------------- 1 | exclude_pattern_42 -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/exclude_pattern_42: -------------------------------------------------------------------------------- 1 | exclude_pattern_42 -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/dir1/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/dir1/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/dir1/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/dir1/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/dir1/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/dir1/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/dir1/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/exclude_pattern_42.txt: -------------------------------------------------------------------------------- 1 | exclude_pattern_42 -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/dir1/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/dir1/exclude_absolute/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir1/subdir/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir2/subdir/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/dir3/subdir/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_copy/special/subdir/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir1/subdir/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir2/subdir/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/dir3/subdir/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/dir1/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/dir1/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/dir1/exclude_pattern_42/nostuff.txt: -------------------------------------------------------------------------------- 1 | exclude_absolute -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/pattern_symlink/special/subdir/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir1/subdir/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir2/subdir/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_copy/dir3/subdir/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir1/subdir/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir2/subdir/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/dir1/subdir/.file0.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/dir1/subdir/file1.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/dir1/subdir/file2.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/dir1/subdir/file3.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/dir1/subdir/file4.log: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/backup/dirs/recurse_symlink/dir3/subdir/dir1/subdir/file5.x.txt: -------------------------------------------------------------------------------- 1 | pattern_symlink/*/subdir/*.txt -------------------------------------------------------------------------------- /test/json/object.qbjs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/Conflip/master/test/json/object.qbjs -------------------------------------------------------------------------------- /test/backup/json/object.qbjs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/Conflip/master/test/backup/json/object.qbjs -------------------------------------------------------------------------------- /test/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | cd $(dirname $(realpath $0)) 5 | rm -rf dirs 6 | rm -rf files 7 | rm -rf ini 8 | rm -rf xml 9 | rm -rf repo 10 | cp -r backup/* ./ 11 | -------------------------------------------------------------------------------- /plugins/inisync/.tsdummy.pro.dummy: -------------------------------------------------------------------------------- 1 | include(/home/sky/Programming/QtProjects/build-Conflip-Desktop_Qt_5_12_1_GCC_64bit-Debug/plugins/inisync/.ts-sources.pri) 2 | include(/home/sky/Programming/QtProjects/Conflip/plugins/inisync/inisync.pro) 3 | -------------------------------------------------------------------------------- /plugins/xmlsync/.tsdummy.pro.dummy: -------------------------------------------------------------------------------- 1 | include(/home/sky/Programming/QtProjects/build-Conflip-Desktop_Qt_5_12_1_GCC_64bit-Debug/plugins/xmlsync/.ts-sources.pri) 2 | include(/home/sky/Programming/QtProjects/Conflip/plugins/xmlsync/xmlsync.pro) 3 | -------------------------------------------------------------------------------- /plugins/jsonsync/.tsdummy.pro.dummy: -------------------------------------------------------------------------------- 1 | include(/home/sky/Programming/QtProjects/build-Conflip-Desktop_Qt_5_12_1_GCC_64bit-Debug/plugins/jsonsync/.ts-sources.pri) 2 | include(/home/sky/Programming/QtProjects/Conflip/plugins/jsonsync/jsonsync.pro) 3 | -------------------------------------------------------------------------------- /plugins/pathsync/.tsdummy.pro.dummy: -------------------------------------------------------------------------------- 1 | include(/home/sky/Programming/QtProjects/build-Conflip-Desktop_Qt_5_12_1_GCC_64bit-Debug/plugins/pathsync/.ts-sources.pri) 2 | include(/home/sky/Programming/QtProjects/Conflip/plugins/pathsync/pathsync.pro) 3 | -------------------------------------------------------------------------------- /plugins/dconfsync/.tsdummy.pro.dummy: -------------------------------------------------------------------------------- 1 | include(/home/sky/Programming/QtProjects/build-Conflip-Desktop_Qt_5_12_1_GCC_64bit-Debug/plugins/dconfsync/.ts-sources.pri) 2 | include(/home/sky/Programming/QtProjects/Conflip/plugins/dconfsync/dconfsync.pro) 3 | -------------------------------------------------------------------------------- /plugins/plugins.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS += \ 4 | pathsync \ 5 | inisync \ 6 | xmlsync \ 7 | jsonsync 8 | 9 | unix:!android:!ios:system(pkg-config --exists dconf): SUBDIRS += dconfsync 10 | 11 | prepareRecursiveTarget(lrelease) 12 | QMAKE_EXTRA_TARGETS += lrelease 13 | -------------------------------------------------------------------------------- /daemon/qpmx.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [], 3 | "license": { 4 | "file": "", 5 | "name": "" 6 | }, 7 | "prcFile": "", 8 | "priFile": "", 9 | "priIncludes": [ 10 | ], 11 | "publishers": { 12 | }, 13 | "qbsFile": "", 14 | "source": false 15 | } 16 | -------------------------------------------------------------------------------- /test/json/object.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": true, 3 | "noKey": false, 4 | "group": { 5 | "subKey": "Hello World", 6 | "more": {}, 7 | "list": [1, 2, 3, 4, 5] 8 | }, 9 | "anotherGroup": { 10 | "null": null, 11 | "notNull": 42 12 | }, 13 | "anotherGroup2": { 14 | "some": "stuff" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/lib_conflip_global.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_GLOBAL_H 2 | #define LIB_GLOBAL_H 3 | 4 | #include 5 | 6 | #if defined(CONFLIP_LIBRARY) 7 | # define LIBCONFLIPSHARED_EXPORT Q_DECL_EXPORT 8 | #else 9 | # define LIBCONFLIPSHARED_EXPORT Q_DECL_IMPORT 10 | #endif 11 | 12 | #endif // LIB_GLOBAL_H 13 | -------------------------------------------------------------------------------- /test/backup/json/object.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": true, 3 | "noKey": false, 4 | "group": { 5 | "subKey": "Hello World", 6 | "more": {}, 7 | "list": [1, 2, 3, 4, 5] 8 | }, 9 | "anotherGroup": { 10 | "null": null, 11 | "notNull": 42 12 | }, 13 | "anotherGroup2": { 14 | "some": "stuff" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/conflip.pc.in: -------------------------------------------------------------------------------- 1 | prefix=%{PREFIX} 2 | exec_prefix=${prefix} 3 | libdir=${prefix}/lib 4 | includedir=${prefix}/include 5 | 6 | Name: Conflip 7 | Description: Conflip application library 8 | Version: %{VERSION} 9 | Libs: -lconflip 10 | Cflags: -I${includedir}/conflip -I${includedir} 11 | Requires: Qt5Core Qt3JsonSerializer Qt1MvvmCore 12 | -------------------------------------------------------------------------------- /lib.pri: -------------------------------------------------------------------------------- 1 | QT += mvvmcore jsonserializer 2 | 3 | CFLIB_DIR = $$shadowed($$PWD/lib) 4 | 5 | win32:CONFIG(release, debug|release): LIBS += -L$$CFLIB_DIR/release/ -lconflip 6 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$CFLIB_DIR/debug/ -lconflip 7 | else:unix: LIBS += -L$$CFLIB_DIR/ -lconflip 8 | 9 | INCLUDEPATH += $$PWD/lib $$CFLIB_DIR 10 | DEPENDPATH += $$PWD/lib 11 | -------------------------------------------------------------------------------- /daemon/scinstall_conflip.bat.in: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | sc create conflip binPath= "%{INSTALL_BINS}\\conflipd.exe --backend windows" start= auto displayname= "%{ProjectName} Service" || exit /B 1 5 | sc description conflip "The Conflip Backend Service" || exit /B 1 6 | 7 | if "%2" == "" goto system_service 8 | sc config conflip obj= %1 password= %2 9 | :system_service 10 | -------------------------------------------------------------------------------- /lib/qpmx.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "package": "de.skycoder42.qpluginfactory", 5 | "provider": "qpm", 6 | "version": "1.3.1" 7 | } 8 | ], 9 | "license": { 10 | "file": "", 11 | "name": "" 12 | }, 13 | "prcFile": "", 14 | "priFile": "", 15 | "priIncludes": [ 16 | ], 17 | "publishers": { 18 | }, 19 | "qbsFile": "", 20 | "source": true 21 | } 22 | -------------------------------------------------------------------------------- /daemon/main.cpp: -------------------------------------------------------------------------------- 1 | #include "conflipservice.h" 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | ConflipService service{argc, argv}; 6 | 7 | for(auto i = 1; i < argc; i++) { 8 | if(qstrcmp(argv[i], "--slice") == 0) { 9 | if((i + 1) >= argc) 10 | return EXIT_FAILURE; 11 | service.setSlice(QString::fromUtf8(argv[i + 1])); 12 | } 13 | } 14 | 15 | return service.exec(); 16 | } 17 | -------------------------------------------------------------------------------- /Conflip.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS += \ 4 | lib \ 5 | plugins \ 6 | daemon \ 7 | gui 8 | 9 | plugins.depends += lib 10 | daemon.depends += lib plugins 11 | gui.depends += lib 12 | 13 | lrelease.target = lrelease 14 | lrelease.CONFIG = recursive 15 | lrelease.recurse_target = lrelease 16 | lrelease.recurse += gui lib plugins 17 | QMAKE_EXTRA_TARGETS += lrelease 18 | 19 | DISTFILES += .qmake.conf 20 | -------------------------------------------------------------------------------- /test/json/array.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": true, 4 | "noKey": false, 5 | "group": { 6 | "subKey": "Hello World", 7 | "more": {}, 8 | "list": [1, 2, 3, 4, 5] 9 | }, 10 | "anotherGroup": { 11 | "null": null, 12 | "notNull": 42 13 | }, 14 | "anotherGroup2": { 15 | "some": "stuff" 16 | } 17 | }, 18 | { 19 | "key": 42, 20 | "group": {}, 21 | "anotherGroup": false, 22 | "master": 1000 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /test/backup/json/array.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": true, 4 | "noKey": false, 5 | "group": { 6 | "subKey": "Hello World", 7 | "more": {}, 8 | "list": [1, 2, 3, 4, 5] 9 | }, 10 | "anotherGroup": { 11 | "null": null, 12 | "notNull": 42 13 | }, 14 | "anotherGroup2": { 15 | "some": "stuff" 16 | } 17 | }, 18 | { 19 | "key": 42, 20 | "group": {}, 21 | "anotherGroup": false, 22 | "master": 1000 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /test/backup/ini/test.ini: -------------------------------------------------------------------------------- 1 | nogroup=true 2 | 3 | [general] 4 | key=value 5 | key[de]=wert 6 | number=42 7 | question=true 8 | 9 | users\size=3 10 | users\1\name=Ralf 11 | users\1\age=42 12 | users\2\name=Lilly 13 | users\2\age=77 14 | users\3\name=Nia 15 | users\3\age=16 16 | 17 | [advanced] 18 | is\a\key=yes 19 | is\a\number=no but=you! 20 | is\a\number\child=0.04 21 | and\the\extra=[test] 22 | text=english 23 | text[de]=german 24 | text[jp]=japanese 25 | -------------------------------------------------------------------------------- /gui/conflip.desktop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env xdg-open 2 | [Desktop Entry] 3 | Type=Application 4 | Version=1.1 5 | Name=Conflip 6 | GenericName=Settings Synchronization 7 | Comment=A tool to synchronize settings/configurations across multiple machines 8 | Categories=Utility;Qt; 9 | Exec=conflip 10 | Icon=conflip 11 | Terminal=false 12 | Actions=Synchronize; 13 | 14 | [Desktop Action Synchronize] 15 | Exec=systemctl --user reload conflip.service 16 | Name=Trigger Synchronization 17 | Icon=view-refresh 18 | -------------------------------------------------------------------------------- /daemon/conflip.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | de.skycoder42.conflip 7 | ProgramArguments 8 | 9 | %{INSTALL_BINS}/conflipd 10 | --backend 11 | launchd 12 | 13 | RunAtLoad 14 | true 15 | 16 | 17 | -------------------------------------------------------------------------------- /plugins/inisync/inisynchelperplugin.cpp: -------------------------------------------------------------------------------- 1 | #include "inisynchelperplugin.h" 2 | #include "inisynchelper.h" 3 | 4 | IniSyncHelperPlugin::IniSyncHelperPlugin(QObject *parent) : 5 | QObject{parent} 6 | {} 7 | 8 | QStringList IniSyncHelperPlugin::translations() const 9 | { 10 | return {QStringLiteral("conflip_inisync")}; 11 | } 12 | 13 | SyncHelper *IniSyncHelperPlugin::createInstance(const QString &provider, QObject *parent) 14 | { 15 | if(provider == IniSyncHelper::ModeIni) 16 | return new IniSyncHelper(parent); 17 | else 18 | return nullptr; 19 | } 20 | -------------------------------------------------------------------------------- /plugins/xmlsync/xmlsynchelperplugin.cpp: -------------------------------------------------------------------------------- 1 | #include "xmlsynchelperplugin.h" 2 | #include "xmlsynchelper.h" 3 | 4 | XmlSyncHelperPlugin::XmlSyncHelperPlugin(QObject *parent) : 5 | QObject(parent) 6 | {} 7 | 8 | QStringList XmlSyncHelperPlugin::translations() const 9 | { 10 | return {QStringLiteral("conflip_xmlsync")}; 11 | } 12 | 13 | SyncHelper *XmlSyncHelperPlugin::createInstance(const QString &provider, QObject *parent) 14 | { 15 | if(provider == XmlSyncHelper::ModeXml) 16 | return new XmlSyncHelper(parent); 17 | else 18 | return nullptr; 19 | } 20 | -------------------------------------------------------------------------------- /lib/conflip.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFLIP_H 2 | #define CONFLIP_H 3 | 4 | #include 5 | #include 6 | 7 | #include "lib_conflip_global.h" 8 | 9 | namespace Conflip 10 | { 11 | 12 | LIBCONFLIPSHARED_EXPORT QString ConfigFileName(); 13 | LIBCONFLIPSHARED_EXPORT bool initConfDir(); 14 | 15 | LIBCONFLIPSHARED_EXPORT QStringList listPlugins(); 16 | LIBCONFLIPSHARED_EXPORT void loadTranslations(const QString &type); 17 | LIBCONFLIPSHARED_EXPORT SyncHelper *loadHelper(const QString &type, QObject *parent = nullptr); 18 | 19 | }; 20 | 21 | #endif // CONFLIP_H 22 | -------------------------------------------------------------------------------- /daemon/conflip.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Conflip Service 3 | Documentation=https://github.com/Skycoder42/Conflip 4 | 5 | [Service] 6 | Type=notify 7 | NotifyAccess=exec 8 | ExecStart=%{INSTALL_BINS}/conflipd -platform minimal --backend systemd %{SLICE} 9 | ExecReload=%{INSTALL_BINS}/conflipd -platform minimal --backend systemd reload $MAINPID %{SLICE} 10 | ExecStop=%{INSTALL_BINS}/conflipd -platform minimal --backend systemd stop $MAINPID %{SLICE} 11 | #WatchdogSec=10 12 | Restart=on-abnormal 13 | RuntimeDirectory=%{conflip} 14 | 15 | [Install] 16 | WantedBy=default.target 17 | -------------------------------------------------------------------------------- /plugins/inisync/inisynchelperplugin.h: -------------------------------------------------------------------------------- 1 | #ifndef INISYNCHELPERPLUGIN_H 2 | #define INISYNCHELPERPLUGIN_H 3 | 4 | #include 5 | 6 | class IniSyncHelperPlugin : public QObject, public SyncHelperPlugin 7 | { 8 | Q_OBJECT 9 | Q_INTERFACES(SyncHelperPlugin) 10 | Q_PLUGIN_METADATA(IID SyncHelperPluginIid FILE "inisync.json") 11 | 12 | public: 13 | IniSyncHelperPlugin(QObject *parent = nullptr); 14 | 15 | QStringList translations() const override; 16 | SyncHelper *createInstance(const QString &provider, QObject *parent) override; 17 | }; 18 | 19 | #endif // INISYNCHELPERPLUGIN_H 20 | -------------------------------------------------------------------------------- /plugins/xmlsync/xmlsynchelperplugin.h: -------------------------------------------------------------------------------- 1 | #ifndef XMLSYNCHELPERPLUGIN_H 2 | #define XMLSYNCHELPERPLUGIN_H 3 | 4 | #include 5 | 6 | class XmlSyncHelperPlugin : public QObject, public SyncHelperPlugin 7 | { 8 | Q_OBJECT 9 | Q_INTERFACES(SyncHelperPlugin) 10 | Q_PLUGIN_METADATA(IID SyncHelperPluginIid FILE "xmlsync.json") 11 | 12 | public: 13 | XmlSyncHelperPlugin(QObject *parent = nullptr); 14 | 15 | QStringList translations() const override; 16 | SyncHelper *createInstance(const QString &provider, QObject *parent) override; 17 | }; 18 | 19 | #endif // XMLSYNCHELPERPLUGIN_H 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.qbs.user 20 | *.qbs.user.* 21 | *.moc 22 | moc_*.cpp 23 | moc_*.h 24 | qrc_*.cpp 25 | ui_*.h 26 | Makefile* 27 | *build-* 28 | 29 | # QtCreator 30 | 31 | *.autosave 32 | 33 | # QtCtreator Qml 34 | *.qmlproject.user 35 | *.qmlproject.user.* 36 | 37 | # QtCtreator CMake 38 | CMakeLists.txt.user* 39 | 40 | # qpm 41 | vendor 42 | /gui/.tsdummy.pro.dummy 43 | *.tsdummy.pro.dummy 44 | -------------------------------------------------------------------------------- /plugins/jsonsync/jsonsynchelperplugin.h: -------------------------------------------------------------------------------- 1 | #ifndef JSONSYNCHELPERPLUGIN_H 2 | #define JSONSYNCHELPERPLUGIN_H 3 | 4 | #include 5 | 6 | class JsonSyncHelperPlugin : public QObject, public SyncHelperPlugin 7 | { 8 | Q_OBJECT 9 | Q_INTERFACES(SyncHelperPlugin) 10 | Q_PLUGIN_METADATA(IID SyncHelperPluginIid FILE "jsonsync.json") 11 | 12 | public: 13 | JsonSyncHelperPlugin(QObject *parent = nullptr); 14 | 15 | QStringList translations() const override; 16 | SyncHelper *createInstance(const QString &provider, QObject *parent) override; 17 | }; 18 | 19 | #endif // JSONSYNCHELPERPLUGIN_H 20 | -------------------------------------------------------------------------------- /plugins/pathsync/pathsynchelperplugin.h: -------------------------------------------------------------------------------- 1 | #ifndef PATHSYNCHELPERPLUGIN_H 2 | #define PATHSYNCHELPERPLUGIN_H 3 | 4 | #include 5 | 6 | class PathSyncHelperPlugin : public QObject, public SyncHelperPlugin 7 | { 8 | Q_OBJECT 9 | Q_INTERFACES(SyncHelperPlugin) 10 | Q_PLUGIN_METADATA(IID SyncHelperPluginIid FILE "pathsync.json") 11 | 12 | public: 13 | PathSyncHelperPlugin(QObject *parent = nullptr); 14 | 15 | QStringList translations() const override; 16 | SyncHelper *createInstance(const QString &provider, QObject *parent) override; 17 | }; 18 | 19 | #endif // PATHSYNCHELPERPLUGIN_H 20 | -------------------------------------------------------------------------------- /plugins/pathsync/pathsynchelperplugin.cpp: -------------------------------------------------------------------------------- 1 | #include "pathsynchelperplugin.h" 2 | #include "pathsynchelper.h" 3 | 4 | PathSyncHelperPlugin::PathSyncHelperPlugin(QObject *parent) : 5 | QObject{parent} 6 | {} 7 | 8 | QStringList PathSyncHelperPlugin::translations() const 9 | { 10 | return {QStringLiteral("conflip_pathsync")}; 11 | } 12 | 13 | SyncHelper *PathSyncHelperPlugin::createInstance(const QString &provider, QObject *parent) 14 | { 15 | if(provider == PathSyncHelper::ModeSymlink || provider == PathSyncHelper::ModeCopy) 16 | return new PathSyncHelper{parent}; 17 | else 18 | return nullptr; 19 | } 20 | -------------------------------------------------------------------------------- /plugins/dconfsync/dconfsynchelperplugin.h: -------------------------------------------------------------------------------- 1 | #ifndef DCONFSYNCHELPERPLUGIN_H 2 | #define DCONFSYNCHELPERPLUGIN_H 3 | 4 | #include 5 | 6 | class DConfSyncHelperPlugin : public QObject, public SyncHelperPlugin 7 | { 8 | Q_OBJECT 9 | Q_INTERFACES(SyncHelperPlugin) 10 | Q_PLUGIN_METADATA(IID SyncHelperPluginIid FILE "dconfsync.json") 11 | 12 | public: 13 | DConfSyncHelperPlugin(QObject *parent = nullptr); 14 | 15 | QStringList translations() const override; 16 | SyncHelper *createInstance(const QString &provider, QObject *parent) override; 17 | }; 18 | 19 | #endif // DCONFSYNCHELPERPLUGIN_H 20 | -------------------------------------------------------------------------------- /plugins/jsonsync/jsonsynchelperplugin.cpp: -------------------------------------------------------------------------------- 1 | #include "jsonsynchelperplugin.h" 2 | #include "jsonsynchelper.h" 3 | 4 | JsonSyncHelperPlugin::JsonSyncHelperPlugin(QObject *parent) : 5 | QObject{parent} 6 | {} 7 | 8 | QStringList JsonSyncHelperPlugin::translations() const 9 | { 10 | return {QStringLiteral("conflip_jsonsync")}; 11 | } 12 | 13 | SyncHelper *JsonSyncHelperPlugin::createInstance(const QString &provider, QObject *parent) 14 | { 15 | if(provider == JsonSyncHelper::ModeJson || 16 | provider == JsonSyncHelper::ModeQbjs) 17 | return new JsonSyncHelper{parent}; 18 | else 19 | return nullptr; 20 | } 21 | -------------------------------------------------------------------------------- /lib/conflipdatabase.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFLIPDATABASE_H 2 | #define CONFLIPDATABASE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "syncentry.h" 8 | #include "lib_conflip_global.h" 9 | 10 | class LIBCONFLIPSHARED_EXPORT ConflipDatabase 11 | { 12 | Q_GADGET 13 | 14 | Q_PROPERTY(QList entries MEMBER entries) 15 | Q_PROPERTY(QList unsynced MEMBER unsynced) 16 | Q_PROPERTY(QMap hasErrors MEMBER hasErrors) 17 | 18 | public: 19 | QList entries; 20 | QList unsynced; 21 | QMap hasErrors; 22 | }; 23 | 24 | #endif // CONFLIPDATABASE_H 25 | -------------------------------------------------------------------------------- /plugins/dconfsync/conflip_dconfsync_template.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DConfSyncHelper 6 | 7 | 8 | &Keys 9 | 10 | 11 | 12 | 13 | Enter the keys you want to synchronize. All entries that start with the given keys will be synchronized. 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /plugins/dconfsync/dconfsynchelperplugin.cpp: -------------------------------------------------------------------------------- 1 | #include "dconfsynchelperplugin.h" 2 | #include "dconfsynchelper.h" 3 | #include 4 | 5 | DConfSyncHelperPlugin::DConfSyncHelperPlugin(QObject *parent) : 6 | QObject(parent) 7 | { 8 | QJsonSerializer::registerAllConverters(); 9 | } 10 | 11 | QStringList DConfSyncHelperPlugin::translations() const 12 | { 13 | return {QStringLiteral("conflip_dconfsync")}; 14 | } 15 | 16 | SyncHelper *DConfSyncHelperPlugin::createInstance(const QString &provider, QObject *parent) 17 | { 18 | if(provider == DConfSyncHelper::ModeDConf) 19 | return new DConfSyncHelper(parent); 20 | else 21 | return nullptr; 22 | } 23 | -------------------------------------------------------------------------------- /daemon/conflipservice.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFLIPSERVICE_H 2 | #define CONFLIPSERVICE_H 3 | 4 | #include 5 | 6 | #include "syncengine.h" 7 | 8 | class ConflipService : public QtService::Service 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit ConflipService(int &argc, char **argv); 13 | 14 | void setSlice(const QString &slice); 15 | 16 | protected: 17 | CommandResult onStart() override; 18 | CommandResult onStop(int &exitCode) override; 19 | CommandResult onReload() override; 20 | CommandResult onPause() override; 21 | CommandResult onResume() override; 22 | 23 | private: 24 | SyncEngine *_engine = nullptr; 25 | }; 26 | 27 | #endif // CONFLIPSERVICE_H 28 | -------------------------------------------------------------------------------- /lib/synchelperplugin.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCHELPERPLUGIN_H 2 | #define SYNCHELPERPLUGIN_H 3 | 4 | #include 5 | #include "synchelper.h" 6 | 7 | class SyncHelperPlugin 8 | { 9 | Q_DISABLE_COPY(SyncHelperPlugin) 10 | public: 11 | inline SyncHelperPlugin() = default; 12 | virtual inline ~SyncHelperPlugin() = default; 13 | 14 | virtual inline QStringList translations() const { return {}; } 15 | virtual SyncHelper *createInstance(const QString &provider, QObject *parent = nullptr) = 0; 16 | }; 17 | 18 | #define SyncHelperPluginIid "de.skycoder42.conflip.SyncHelperPlugin" 19 | Q_DECLARE_INTERFACE(SyncHelperPlugin, SyncHelperPluginIid) 20 | 21 | #endif // SYNCHELPERPLUGIN_H 22 | -------------------------------------------------------------------------------- /test/backup/xml/test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Test 4 | 5 | 6 | no 7 | 8 | no 9 | 10 | Test 11 | 12 | no 13 | 14 | 15 | Text 16 | 17 | 18 | one 19 | two 20 | three 21 | 22 | 23 | one 24 | two 25 | three 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /lib/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | lib_conflip_global.h 4 | QUuid 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /gui/qpmx.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "package": "de.skycoder42.dialog-master", 5 | "provider": "qpm", 6 | "version": "1.3.2" 7 | }, 8 | { 9 | "package": "de.skycoder42.qpathedit", 10 | "provider": "qpm", 11 | "version": "2.2.0" 12 | }, 13 | { 14 | "package": "de.skycoder42.qobjectlistmodel", 15 | "provider": "qpm", 16 | "version": "1.2.1" 17 | } 18 | ], 19 | "license": { 20 | "file": "", 21 | "name": "" 22 | }, 23 | "prcFile": "", 24 | "priFile": "", 25 | "priIncludes": [ 26 | ], 27 | "publishers": { 28 | }, 29 | "source": false 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Conflip 2 | A tool to synchronize settings/configurations across multiple machines 3 | 4 |
Icons made by Roundicons from www.flaticon.com is licensed by CC 3.0 BY
5 |
Icons made by Creaticca Creative Agency from www.flaticon.com is licensed by CC 3.0 BY
6 | -------------------------------------------------------------------------------- /plugins/dconfsync/conflip_dconfsync_de.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DConfSyncHelper 6 | 7 | 8 | &Keys 9 | &Schlüssel 10 | 11 | 12 | 13 | Enter the keys you want to synchronize. All entries that start with the given keys will be synchronized. 14 | Geben Sie die Schlüssel der Einträge ein, die synchronisiert werden sollen. Alle Einträge, die mit diesen anfangen, werden synchronisiert. 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /daemon/pathresolver.h: -------------------------------------------------------------------------------- 1 | #ifndef PATHRESOLVER_H 2 | #define PATHRESOLVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class PathResolver : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit PathResolver(QObject *parent = nullptr); 16 | 17 | void setSyncDir(const QDir &dir); 18 | QStringList resolvePath(const SyncEntry &entry, SyncHelper *helper) const; 19 | 20 | private: 21 | QStringList findFiles(const QDir &cd, QStringList pathList, const SyncEntry &entry) const; 22 | QDir findRootDir(QStringList &pathList, const SyncEntry &entry) const; 23 | QDir createDir(const QString &path, const SyncEntry &entry) const; 24 | 25 | QDir _syncDir; 26 | }; 27 | 28 | #endif // PATHRESOLVER_H 29 | -------------------------------------------------------------------------------- /plugins/dconfsync/dconfaccess.h: -------------------------------------------------------------------------------- 1 | #ifndef DCONFREADER_H 2 | #define DCONFREADER_H 3 | 4 | #include 5 | #include 6 | 7 | using DConfClient = struct _DConfClient; 8 | 9 | class DConfAccess 10 | { 11 | public: 12 | DConfAccess(); 13 | ~DConfAccess(); 14 | 15 | void open(const QByteArray &path); 16 | 17 | QByteArrayList readAllKeys(const QByteArrayList &filters) const; 18 | std::tuple readData(const QByteArray &key) const; //(type, value) 19 | bool writeData(const QByteArray &key, const QByteArray &type, const QByteArray &data, QByteArray *errorMsg = nullptr); 20 | 21 | void sync(); 22 | 23 | private: 24 | DConfClient *_client; 25 | QByteArray _path; 26 | bool _needsSync; 27 | 28 | QByteArrayList readAllKeysImpl(const QByteArray &base) const; 29 | }; 30 | 31 | #endif // DCONFREADER_H 32 | -------------------------------------------------------------------------------- /plugins/inisync/inisync.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | 3 | CONFIG += plugin 4 | 5 | QT = core 6 | 7 | TARGET = inisync 8 | DESTDIR = $$PLUGIN_DESTDIR/conflip 9 | 10 | HEADERS += \ 11 | inisynchelperplugin.h \ 12 | inisynchelper.h 13 | 14 | SOURCES += \ 15 | inisynchelperplugin.cpp \ 16 | inisynchelper.cpp 17 | 18 | TRANSLATIONS += conflip_inisync_de.ts \ 19 | conflip_inisync_template.ts 20 | 21 | DISTFILES += inisync.json 22 | 23 | target.path = $$INSTALL_PLUGINS/conflip 24 | qpmx_ts_target.path = $$INSTALL_TRANSLATIONS 25 | INSTALLS += target qpmx_ts_target 26 | 27 | include(../../lib.pri) 28 | 29 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.) 30 | else: include($$OUT_PWD/qpmx_generated.pri) 31 | -------------------------------------------------------------------------------- /plugins/xmlsync/xmlsync.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | 3 | CONFIG += plugin 4 | 5 | QT = core xml 6 | 7 | TARGET = xmlsync 8 | DESTDIR = $$PLUGIN_DESTDIR/conflip 9 | 10 | HEADERS += \ 11 | xmlsynchelperplugin.h \ 12 | xmlsynchelper.h 13 | 14 | SOURCES += \ 15 | xmlsynchelperplugin.cpp \ 16 | xmlsynchelper.cpp 17 | 18 | TRANSLATIONS += conflip_xmlsync_de.ts \ 19 | conflip_xmlsync_template.ts 20 | 21 | DISTFILES += xmlsync.json 22 | 23 | target.path = $$INSTALL_PLUGINS/conflip 24 | qpmx_ts_target.path = $$INSTALL_TRANSLATIONS 25 | INSTALLS += target qpmx_ts_target 26 | 27 | include(../../lib.pri) 28 | 29 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.) 30 | else: include($$OUT_PWD/qpmx_generated.pri) 31 | -------------------------------------------------------------------------------- /plugins/jsonsync/jsonsync.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | 3 | CONFIG += plugin 4 | 5 | QT = core 6 | 7 | TARGET = jsonsync 8 | DESTDIR = $$PLUGIN_DESTDIR/conflip 9 | 10 | HEADERS += \ 11 | jsonsynchelperplugin.h \ 12 | jsonsynchelper.h 13 | 14 | SOURCES += \ 15 | jsonsynchelperplugin.cpp \ 16 | jsonsynchelper.cpp 17 | 18 | TRANSLATIONS += conflip_jsonsync_de.ts \ 19 | conflip_jsonsync_template.ts 20 | 21 | DISTFILES += jsonsync.json 22 | 23 | target.path = $$INSTALL_PLUGINS/conflip 24 | qpmx_ts_target.path = $$INSTALL_TRANSLATIONS 25 | INSTALLS += target qpmx_ts_target 26 | 27 | include(../../lib.pri) 28 | 29 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.) 30 | else: include($$OUT_PWD/qpmx_generated.pri) 31 | -------------------------------------------------------------------------------- /plugins/pathsync/pathsync.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | 3 | CONFIG += plugin 4 | 5 | QT = core 6 | 7 | TARGET = pathsync 8 | DESTDIR = $$PLUGIN_DESTDIR/conflip 9 | 10 | HEADERS += \ 11 | pathsynchelperplugin.h \ 12 | pathsynchelper.h 13 | 14 | SOURCES += \ 15 | pathsynchelperplugin.cpp \ 16 | pathsynchelper.cpp 17 | 18 | DISTFILES += pathsync.json 19 | 20 | TRANSLATIONS += conflip_pathsync_de.ts \ 21 | conflip_pathsync_template.ts 22 | 23 | target.path = $$INSTALL_PLUGINS/conflip 24 | qpmx_ts_target.path = $$INSTALL_TRANSLATIONS 25 | INSTALLS += target qpmx_ts_target 26 | 27 | include(../../lib.pri) 28 | 29 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.) 30 | else: include($$OUT_PWD/qpmx_generated.pri) 31 | -------------------------------------------------------------------------------- /gui/createentrydialog.h: -------------------------------------------------------------------------------- 1 | #ifndef CREATEENTRYDIALOG_H 2 | #define CREATEENTRYDIALOG_H 3 | 4 | #include 5 | #include 6 | 7 | namespace Ui { 8 | class CreateEntryDialog; 9 | } 10 | 11 | class CreateEntryDialog : public QDialog 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit CreateEntryDialog(QWidget *parent = nullptr); 17 | ~CreateEntryDialog() override; 18 | 19 | static SyncEntry createEntry(QWidget *parent = nullptr); 20 | static SyncEntry editEntry(const SyncEntry &entry, QWidget *parent = nullptr); 21 | 22 | private slots: 23 | void showHint(); 24 | 25 | void on_actionSelect_File_triggered(); 26 | void on_actionSelect_Directory_triggered(); 27 | void on_action_Add_Extra_triggered(); 28 | void on_action_Remove_Extra_triggered(); 29 | void on_modeComboBox_currentIndexChanged(const QString &text); 30 | 31 | private: 32 | Ui::CreateEntryDialog *ui; 33 | }; 34 | 35 | #endif // CREATEENTRYDIALOG_H 36 | -------------------------------------------------------------------------------- /plugins/jsonsync/conflip_jsonsync_template.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JsonSyncHelper 6 | 7 | 8 | &Keys 9 | 10 | 11 | 12 | 13 | <p>Enter the key-path you want to synchronize. The path is a chain of keys of properties that are traversed into the object and the last in the chain is synchronized. The keys are seperated by a '/'.</p><p>For example, the key "group/key" would enter the property named "group" in the root element and there get the value of the "key" property and synchronize it.</p> 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /plugins/dconfsync/dconfsync.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | 3 | CONFIG += plugin 4 | 5 | QT = core jsonserializer 6 | 7 | TARGET = dconfsync 8 | DESTDIR = $$PLUGIN_DESTDIR/conflip 9 | 10 | HEADERS += \ 11 | dconfsynchelperplugin.h \ 12 | dconfaccess.h \ 13 | dconfsynchelper.h 14 | 15 | SOURCES += \ 16 | dconfsynchelperplugin.cpp \ 17 | dconfaccess.cpp \ 18 | dconfsynchelper.cpp 19 | 20 | TRANSLATIONS += conflip_dconfsync_de.ts \ 21 | conflip_dconfsync_template.ts 22 | 23 | DISTFILES += dconfsync.json 24 | 25 | target.path = $$INSTALL_PLUGINS/conflip 26 | qpmx_ts_target.path = $$INSTALL_TRANSLATIONS 27 | INSTALLS += target qpmx_ts_target 28 | 29 | include(../../lib.pri) 30 | 31 | CONFIG += link_pkgconfig 32 | PKGCONFIG += dconf 33 | 34 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.) 35 | else: include($$OUT_PWD/qpmx_generated.pri) 36 | -------------------------------------------------------------------------------- /plugins/inisync/conflip_inisync_template.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IniSyncHelper 6 | 7 | 8 | &Keys 9 | 10 | 11 | 12 | 13 | <p>Enter the keys you want to synchronize. All entries that start with the given keys will be synchronized.You can also enter "inverted" keys to exclude entries.</p><p>For example, adding the keys<ul> <li>test\group</li> <li>!test\group\child</li> <li>test\group\child\special</li></ul>Will synchronize every entry that begins with "test\group", but exclude all entries from "test\group\child", except everything that starts with "test\group\child\special".</p> 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /plugins/pathsync/conflip_pathsync_template.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PathSyncHelper 6 | 7 | 8 | &Excluded Paths 9 | 10 | 11 | 12 | 13 | <p>You can enter regular expressions that are matched against the found paths to exclude them from synchronization. The regex must be a standard PCRE-expression and can contain unicode characters</p><p>For example, assuming your path pattern is "dir/file_*.txt" and your directory contains the files<ul> <li>file_tree.txt</li> <li>file_house.txt</li> <li>file_heart.txt</li></ul>And you add the exclude pattern "_h\w+e.*\.", it will only match "file_tree.txt" and exclude the other two</p> 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /lib/syncentry.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCENTRY_H 2 | #define SYNCENTRY_H 3 | 4 | #include 5 | #include 6 | #include 7 | class QDebug; 8 | 9 | #include "lib_conflip_global.h" 10 | 11 | class LIBCONFLIPSHARED_EXPORT SyncEntry 12 | { 13 | Q_GADGET 14 | 15 | Q_PROPERTY(QString pathPattern MEMBER pathPattern) 16 | Q_PROPERTY(QStringList extras MEMBER extras) 17 | Q_PROPERTY(QString mode MEMBER mode) 18 | Q_PROPERTY(bool includeHidden MEMBER includeHidden) 19 | Q_PROPERTY(bool caseSensitive MEMBER caseSensitive) 20 | Q_PROPERTY(bool matchDirs MEMBER matchDirs) 21 | Q_PROPERTY(QList syncedMachines MEMBER syncedMachines) 22 | 23 | public: 24 | QString pathPattern; 25 | QStringList extras; 26 | QString mode; 27 | bool includeHidden = true; 28 | bool caseSensitive = true; 29 | bool matchDirs = true; 30 | QList syncedMachines; 31 | 32 | void setCleanPathPattern(const QString &path); 33 | 34 | bool operator==(const SyncEntry &other) const; 35 | bool operator!=(const SyncEntry &other) const; 36 | 37 | operator bool() const; 38 | bool operator!() const; 39 | }; 40 | 41 | LIBCONFLIPSHARED_EXPORT QDebug operator<<(QDebug debug, const SyncEntry &entry); 42 | 43 | #endif // SYNCENTRY_H 44 | -------------------------------------------------------------------------------- /gui/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace Ui { 14 | class MainWindow; 15 | } 16 | 17 | class MainWindow : public QMainWindow 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | explicit MainWindow(QWidget *parent = nullptr); 23 | ~MainWindow() override; 24 | 25 | private slots: 26 | void on_action_Reload_Daemon_triggered(); 27 | void on_action_Add_Entry_triggered(); 28 | void on_action_Edit_Entry_triggered(); 29 | void on_action_Remove_Entry_triggered(); 30 | void on_action_About_triggered(); 31 | void on_actionPaste_Path_triggered(); 32 | 33 | void reload(); 34 | void update(); 35 | void updatePath(const QString &path); 36 | 37 | private: 38 | Ui::MainWindow *ui; 39 | QtService::ServiceControl *_svcControl; 40 | 41 | QGadgetListModel *_model; 42 | QObjectProxyModel *_proxyModel; 43 | QSortFilterProxyModel *_sortModel; 44 | 45 | QJsonSerializer *_serializer; 46 | QFileSystemWatcher *_watcher; 47 | 48 | QList _rmList; 49 | }; 50 | 51 | #endif // MAINWINDOW_H 52 | -------------------------------------------------------------------------------- /daemon/syncengine.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCENGINE_H 2 | #define SYNCENGINE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "pathresolver.h" 14 | 15 | class SyncEngine : public QObject 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit SyncEngine(QObject *parent = nullptr); 21 | 22 | bool start(); 23 | void pause(); 24 | void resume(); 25 | void reload(); 26 | 27 | private slots: 28 | void triggerSync(); 29 | 30 | void syncDone(SyncTask *task, SyncTask::Result result); 31 | 32 | private: 33 | QTimer *_timer; 34 | QFileSystemWatcher *_watcher; 35 | QJsonSerializer *_serializer; 36 | PathResolver *_resolver; 37 | QThreadPool *_threadPool; 38 | 39 | QHash _helpers; 40 | 41 | QDir _workingDir; 42 | bool _skipNextUpdate = false; 43 | 44 | ConflipDatabase _currentDb; 45 | bool _dbChanged = false; 46 | QHash _activeTasks; 47 | bool _hasErrors = false; 48 | 49 | SyncHelper *getHelper(const QString &type); 50 | void syncEntries(QList &entries); 51 | void removeUnsynced(QList &entries); 52 | 53 | void completeSync(); 54 | }; 55 | 56 | #endif // SYNCENGINE_H 57 | -------------------------------------------------------------------------------- /gui/gui.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += core gui widgets jsonserializer service 4 | 5 | TARGET = $$TARGET_BASE 6 | DESTDIR = $$BIN_DESTDIR 7 | 8 | HEADERS += \ 9 | mainwindow.h \ 10 | createentrydialog.h 11 | 12 | SOURCES += \ 13 | main.cpp \ 14 | mainwindow.cpp \ 15 | createentrydialog.cpp 16 | 17 | FORMS += \ 18 | mainwindow.ui \ 19 | createentrydialog.ui 20 | 21 | TRANSLATIONS += conflip_de.ts \ 22 | conflip_template.ts 23 | 24 | DISTFILES += qpmx.json \ 25 | conflip.svg \ 26 | conflip.desktop 27 | 28 | never_true_lupdate_only: SOURCES += ../lib/synchelper.cpp 29 | 30 | linux { 31 | DEFINES += SERVICE_BACKEND=\\\"systemd\\\" 32 | } 33 | 34 | target.path = $$INSTALL_BINS 35 | qpmx_ts_target.path = $$INSTALL_TRANSLATIONS 36 | install_icons.files += conflip.svg 37 | install_icons.path = $$INSTALL_SHARE/icons/hicolor/scalable/apps 38 | install_desktop.files = conflip.desktop 39 | install_desktop.path = $$INSTALL_SHARE/applications/ 40 | INSTALLS += target qpmx_ts_target install_icons install_desktop 41 | 42 | include(../lib.pri) 43 | 44 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.) 45 | else: include($$OUT_PWD/qpmx_generated.pri) 46 | -------------------------------------------------------------------------------- /plugins/jsonsync/conflip_jsonsync_de.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JsonSyncHelper 6 | 7 | 8 | &Keys 9 | &Schlüssel 10 | 11 | 12 | 13 | <p>Enter the key-path you want to synchronize. The path is a chain of keys of properties that are traversed into the object and the last in the chain is synchronized. The keys are seperated by a '/'.</p><p>For example, the key "group/key" would enter the property named "group" in the root element and there get the value of the "key" property and synchronize it.</p> 14 | <p>Geben Sie die Schlüssel der Einträge ein, die synchronisiert werden sollen. Der Pfad ist eine Kette von Eingeschaften die in dem Objekte durchlaufen werden bis zum letzten Elemen, welches dann synchronisiert wird. Getrennt werden die Schlüssel durch ein '/'.</p> 15 | <p>Der Schlüssel "group/key" würde Beispielsweise im Wurzel-Element nach der Eingeschaft "group" suchen und innerhalt diesem Element dann die Eigenschaft "key" und diese synchronisieren.</p> 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /gui/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QApplication a(argc, argv); 11 | QApplication::setApplicationName(QStringLiteral(TARGET)); 12 | QApplication::setApplicationVersion(QStringLiteral(VERSION)); 13 | QApplication::setOrganizationName(QStringLiteral(COMPANY)); 14 | QApplication::setOrganizationDomain(QStringLiteral(BUNDLE)); 15 | QApplication::setApplicationDisplayName(QStringLiteral("Conflip")); 16 | QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("conflip"))); 17 | 18 | auto translator = new QTranslator(qApp); 19 | if(translator->load(QLocale(), 20 | QStringLiteral("conflip"), 21 | QStringLiteral("_"), 22 | QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { 23 | qApp->installTranslator(translator); 24 | } 25 | 26 | QCommandLineParser parser; 27 | parser.addHelpOption(); 28 | parser.addVersionOption(); 29 | parser.addOption({ 30 | {QStringLiteral("s"), QStringLiteral("slice")}, 31 | QStringLiteral("Run in slice mode. The instance will use the settings based on the passed."), 32 | QStringLiteral("slice") 33 | }); 34 | parser.process(a); 35 | if(parser.isSet(QStringLiteral("slice"))) 36 | QApplication::setApplicationName(QStringLiteral(TARGET "@%1").arg(parser.value(QStringLiteral("slice")))); 37 | 38 | MainWindow w; 39 | w.show(); 40 | 41 | return a.exec(); 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Felix Barz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /.qmake.conf: -------------------------------------------------------------------------------- 1 | TARGET_BASE = conflip 2 | VERSION = 2.1.2 3 | 4 | CONFIG += warning_clean exceptions c++17 5 | DEFINES += QT_DEPRECATED_WARNINGS QT_ASCII_CAST_WARNINGS QT_USE_QSTRINGBUILDER 6 | 7 | QMAKE_TARGET_COMPANY = "Skycoder42" 8 | QMAKE_TARGET_DESCRIPTION = "File-based settings synchroization" 9 | QMAKE_TARGET_COPYRIGHT = "Felix Barz" 10 | QMAKE_TARGET_BUNDLE_PREFIX = de.skycoder42 11 | 12 | DEFINES += "TARGET=\\\"$$TARGET_BASE\\\"" 13 | DEFINES += "VERSION=\\\"$$VERSION\\\"" 14 | DEFINES += "COMPANY=\"\\\"$$QMAKE_TARGET_COMPANY\\\"\"" 15 | DEFINES += "BUNDLE=\"\\\"$$QMAKE_TARGET_BUNDLE_PREFIX\\\"\"" 16 | 17 | BIN_DESTDIR = $$shadowed($$PWD)/bin 18 | PLUGIN_DESTDIR= $$shadowed($$PWD)/plugins 19 | 20 | # installing 21 | exists($$PWD/install.pri):include($$PWD/install.pri) 22 | isEmpty(INSTALL_PREFIX) { 23 | INSTALL_PREFIX = $$[QT_INSTALL_PREFIX] 24 | isEmpty(INSTALL_BINS): INSTALL_BINS = $$[QT_INSTALL_BINS] 25 | isEmpty(INSTALL_LIBS): INSTALL_LIBS = $$[QT_INSTALL_LIBS] 26 | isEmpty(INSTALL_HEADERS): INSTALL_HEADERS = $$[QT_INSTALL_HEADERS] 27 | isEmpty(INSTALL_PLUGINS): INSTALL_PLUGINS = $$[QT_INSTALL_PLUGINS] 28 | isEmpty(INSTALL_TRANSLATIONS): INSTALL_TRANSLATIONS = $$[QT_INSTALL_TRANSLATIONS] 29 | } else { 30 | isEmpty(INSTALL_BINS): INSTALL_BINS = $${INSTALL_PREFIX}/bin 31 | isEmpty(INSTALL_LIBS): INSTALL_LIBS = $${INSTALL_PREFIX}/lib 32 | isEmpty(INSTALL_HEADERS): INSTALL_HEADERS = $${INSTALL_PREFIX}/include 33 | isEmpty(INSTALL_PLUGINS): INSTALL_PLUGINS = $${INSTALL_PREFIX}/plugins 34 | isEmpty(INSTALL_TRANSLATIONS): INSTALL_TRANSLATIONS = $${INSTALL_PREFIX}/translations 35 | } 36 | isEmpty(INSTALL_SHARE): INSTALL_SHARE = $${INSTALL_PREFIX}/share 37 | -------------------------------------------------------------------------------- /lib/syncentry.cpp: -------------------------------------------------------------------------------- 1 | #include "syncentry.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void SyncEntry::setCleanPathPattern(const QString &path) 7 | { 8 | pathPattern = QDir::cleanPath(path); 9 | if(pathPattern.startsWith(QStringLiteral("~/"))) 10 | pathPattern = QDir::home().absoluteFilePath(pathPattern.mid(2)); 11 | } 12 | 13 | bool SyncEntry::operator==(const SyncEntry &other) const 14 | { 15 | return pathPattern == other.pathPattern && 16 | extras == other.extras && 17 | mode == other.mode && 18 | includeHidden == other.includeHidden && 19 | caseSensitive == other.caseSensitive && 20 | matchDirs == other.matchDirs && 21 | syncedMachines == other.syncedMachines; 22 | } 23 | 24 | bool SyncEntry::operator!=(const SyncEntry &other) const 25 | { 26 | return pathPattern != other.pathPattern || 27 | extras != other.extras || 28 | mode != other.mode || 29 | includeHidden != other.includeHidden || 30 | caseSensitive != other.caseSensitive || 31 | matchDirs != other.matchDirs || 32 | syncedMachines != other.syncedMachines; 33 | } 34 | 35 | SyncEntry::operator bool() const 36 | { 37 | return !pathPattern.isNull(); 38 | } 39 | 40 | bool SyncEntry::operator!() const 41 | { 42 | return pathPattern.isNull(); 43 | } 44 | 45 | QDebug operator<<(QDebug debug, const SyncEntry &entry) 46 | { 47 | QDebugStateSaver state(debug); 48 | if(entry.extras.isEmpty()) { 49 | debug.noquote().nospace() << entry.pathPattern 50 | << ":" << entry.mode; 51 | } else { 52 | debug.noquote().nospace() << entry.pathPattern 53 | << ":" << entry.mode 54 | << "->[" << entry.extras.join(QStringLiteral(", ")) << "]"; 55 | } 56 | return debug; 57 | } 58 | -------------------------------------------------------------------------------- /plugins/xmlsync/conflip_xmlsync_template.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | XmlSyncHelper 6 | 7 | 8 | &Keys 9 | 10 | 11 | 12 | 13 | <p>Enter the paths of the XML-Elements you want to synchronize. Elements are seperated by a '/' and there are some special characters that match special things. The possible variants are:<ul> <li>"some/node": A path of elements. This will synchronize the element "node" within the element "some" inside the root element. All attributes, child-elements and text are synchronized</li> <li>"node/~": This will synchronize the text within the "node" element, and nothing else</li> <li>"node/=": This will synchronize all attributes of "node" but not the contents</li> <li>"node/=key": This will synchronize the attribute "key" of "node" but not the contents</li> <li>"node/#": This will synchronize all attributes and content text of "node"</li> <li>"node/": This will synchronize all child elements in "node", but no attributes</li></ul></p><p>You never have to specify the root element, as a XML-Document can only have a single root.</p> 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /plugins/inisync/conflip_inisync_de.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IniSyncHelper 6 | 7 | 8 | &Keys 9 | &Schlüssel 10 | 11 | 12 | 13 | <p>Enter the keys you want to synchronize. All entries that start with the given keys will be synchronized.You can also enter "inverted" keys to exclude entries.</p><p>For example, adding the keys<ul> <li>test\group</li> <li>!test\group\child</li> <li>test\group\child\special</li></ul>Will synchronize every entry that begins with "test\group", but exclude all entries from "test\group\child", except everything that starts with "test\group\child\special".</p> 14 | <p>Geben Sie die Schlüssel der Einträge ein, die synchronisiert werden sollen. Alle Einträge, die mit diesen anfangen, werden synchronisiert. Sie können außerdem "invertierte" Schlüssel eingeben, um Einträge auszuschließen</p> 15 | <p>Verwendet man beispielsweise die Schlüssel: 16 | <ul> 17 | <li>test\group</li> 18 | <li>!test\group\child</li> 19 | <li>test\group\child\special</li> 20 | </ul> 21 | Werden alle Einträge die mit "test\group" beginnen synchronisiert, mit Ausnahme derer, die mit "test\group\child" beginnen, und von denen aber trotzdem die, die mit "test\group\child\special" beginnen.</p> 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /plugins/pathsync/conflip_pathsync_de.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PathSyncHelper 6 | 7 | 8 | &Excluded Paths 9 | &Ausgeschlossene Pfade 10 | 11 | 12 | 13 | <p>You can enter regular expressions that are matched against the found paths to exclude them from synchronization. The regex must be a standard PCRE-expression and can contain unicode characters</p><p>For example, assuming your path pattern is "dir/file_*.txt" and your directory contains the files<ul> <li>file_tree.txt</li> <li>file_house.txt</li> <li>file_heart.txt</li></ul>And you add the exclude pattern "_h\w+e.*\.", it will only match "file_tree.txt" and exclude the other two</p> 14 | <p>Sie können hier reguläre Ausdrücke eingeben, welche gegen die gefundenen Pfade eingesetzt werden, um die passenden von der Synchronisation auszuschließen. Der Ausdruck muss ein PCRE-Ausdruck sein und darf Unicode enthalten.</p> 15 | <p>Wenn z.B. der Pfad als "dir/file_*.txt" gegeben ist und die Dateien in dem Ordner sind 16 | <ul> 17 | <li>file_tree.txt</li> 18 | <li>file_house.txt</li> 19 | <li>file_heart.txt</li> 20 | </ul> 21 | Wenn dann als Ausdruck "_h\w+e.*\." verwendet wird, dann wird nur file_tree.txt synchronisiert, weil die anderen Beiden vom Ausdruck gematched werden.</p> 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /lib/lib.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | 3 | QT += jsonserializer mvvmcore 4 | 5 | TARGET = $$qtLibraryTarget($$TARGET_BASE) 6 | 7 | DEFINES += CONFLIP_LIBRARY 8 | 9 | HEADERS += \ 10 | conflip.h \ 11 | lib_conflip_global.h \ 12 | syncentry.h \ 13 | conflipdatabase.h \ 14 | synchelper.h \ 15 | synchelperplugin.h 16 | 17 | SOURCES += \ 18 | conflip.cpp \ 19 | syncentry.cpp \ 20 | conflipdatabase.cpp \ 21 | synchelper.cpp 22 | 23 | SETTINGS_DEFINITIONS += \ 24 | settings.xml 25 | 26 | DISTFILES += qpmx.json \ 27 | conflip.pc.in 28 | 29 | QMAKE_EXTRA_TARGETS += lrelease 30 | 31 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.) 32 | else: include($$OUT_PWD/qpmx_generated.pri) 33 | 34 | # install copy 35 | for(header, SETTINGSGENERATOR_BUILD_HEADERS) { 36 | theader = $$shadowed($$SETTINGSGENERATOR_DIR/$$basename(header)) 37 | !exists($$theader):system($$QMAKE_COPY_FILE $$shell_quote($$shell_path($$header)) $$shell_quote($$shell_path($$theader))) 38 | } 39 | 40 | # install 41 | linux { 42 | create_pc.target = conflip.pc 43 | create_pc.depends += $$PWD/conflip.pc.in 44 | create_pc.commands += sed "s:%{PREFIX}:$$INSTALL_PREFIX:g" $$PWD/conflip.pc.in | sed "s:%{VERSION}:$$VERSION:g" > conflip.pc 45 | 46 | QMAKE_EXTRA_TARGETS += create_pc 47 | PRE_TARGETDEPS += conflip.pc 48 | 49 | install_pc.files += $$OUT_PWD/conflip.pc 50 | install_pc.CONFIG += no_check_exist 51 | install_pc.path = $$INSTALL_LIBS/pkgconfig/ 52 | INSTALLS += install_pc 53 | } 54 | 55 | target.path = $$INSTALL_LIBS 56 | header_install.files = $$HEADERS $$SETTINGSGENERATOR_BUILD_HEADERS $$SETTINGSGENERATOR_DIR/settings.h 57 | header_install.path = $$INSTALL_HEADERS/conflip 58 | INSTALLS += target header_install 59 | -------------------------------------------------------------------------------- /plugins/pathsync/pathsynchelper.h: -------------------------------------------------------------------------------- 1 | #ifndef PATHSYNCHELPER_H 2 | #define PATHSYNCHELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class PathSyncHelper; 10 | class PathSyncTask : public SyncTask 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | // sync 16 | PathSyncTask(const PathSyncHelper *helper, 17 | QString &&mode, 18 | const QDir &syncDir, 19 | QString &&path, 20 | QStringList &&extras, 21 | bool isFirstUse, 22 | QObject *parent); 23 | // remove 24 | PathSyncTask(const PathSyncHelper *helper, 25 | QString &&mode, 26 | const QDir &syncDir, 27 | QString &&path, 28 | QObject *parent); 29 | 30 | protected: 31 | void performSync() override; 32 | void undoSync() override; 33 | 34 | private: 35 | mutable QCache _regexCache; 36 | 37 | void syncAsSymlink(const QFileInfo &src, const QFileInfo &sync); 38 | void syncAsCopy(const QFileInfo &src, const QFileInfo &sync); 39 | void unlink(const QFileInfo &src, const QFileInfo &sync); 40 | 41 | void movePath(const QFileInfo &from, const QFileInfo &to, bool fromIsSrc); 42 | bool removePath(const QFileInfo &path); 43 | 44 | QByteArray hashFile(const QFileInfo &file, const QByteArray &target) const; 45 | }; 46 | 47 | class PathSyncHelper : public SyncHelper 48 | { 49 | Q_OBJECT 50 | 51 | public: 52 | static const QString ModeSymlink; 53 | static const QString ModeCopy; 54 | 55 | explicit PathSyncHelper(QObject *parent = nullptr); 56 | 57 | QString syncPrefix(const QString &mode) const override; 58 | bool pathIsPattern(const QString &mode) const override; 59 | bool canSyncDirs(const QString &mode) const override; 60 | ExtrasHint extrasHint() const override; 61 | 62 | SyncTask *createSyncTask(QString mode, const QDir &syncDir, QString path, QStringList extras, bool isFirstUse, QObject *parent) override; 63 | SyncTask *createUndoSyncTask(QString mode, const QDir &syncDir, QString path, QObject *parent) override; 64 | }; 65 | 66 | #endif // PATHSYNCHELPER_H 67 | -------------------------------------------------------------------------------- /lib/conflip.cpp: -------------------------------------------------------------------------------- 1 | #include "conflip.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "syncentry.h" 9 | #include "synchelperplugin.h" 10 | #include "settings.h" 11 | 12 | namespace { 13 | 14 | QSet loadedTranslations; 15 | 16 | void conflip_lib_startup() 17 | { 18 | QJsonSerializer::registerAllConverters(); 19 | } 20 | 21 | } 22 | Q_COREAPP_STARTUP_FUNCTION(conflip_lib_startup) 23 | 24 | Q_GLOBAL_PLUGIN_OBJECT_FACTORY(SyncHelperPlugin, SyncHelper, "conflip", helperFactory) 25 | 26 | QString Conflip::ConfigFileName() 27 | { 28 | return QStringLiteral("config.json"); 29 | } 30 | 31 | QStringList Conflip::listPlugins() 32 | { 33 | return helperFactory->allKeys(); 34 | } 35 | 36 | void Conflip::loadTranslations(const QString &type) 37 | { 38 | if(!loadedTranslations.contains(type)) { 39 | auto plg = helperFactory->plugin(type); 40 | if(!plg) 41 | return; 42 | for(const auto &qmFile : plg->translations()) { 43 | auto translator = new QTranslator{qApp}; 44 | if(translator->load(QLocale(), 45 | qmFile, 46 | QStringLiteral("_"), 47 | QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { 48 | qApp->installTranslator(translator); 49 | } 50 | } 51 | loadedTranslations.insert(type); 52 | } 53 | } 54 | 55 | SyncHelper *Conflip::loadHelper(const QString &type, QObject *parent) 56 | { 57 | return helperFactory->createInstance(type, parent); 58 | } 59 | 60 | 61 | bool Conflip::initConfDir() 62 | { 63 | QDir pathDir{Settings::instance()->engine.dir}; 64 | pathDir.makeAbsolute(); 65 | if(!pathDir.exists()) { 66 | if(!pathDir.mkpath(QStringLiteral("."))) { 67 | qCritical().noquote() << "Failed to create config directory:" 68 | << pathDir.absolutePath(); 69 | return false; 70 | } 71 | } 72 | 73 | QFile confFile{pathDir.absoluteFilePath(Conflip::ConfigFileName())}; 74 | if(!confFile.exists()) { 75 | if(!confFile.open(QIODevice::WriteOnly | QIODevice::Text)) { 76 | qCritical().noquote() << "Failed to create config file:" 77 | << confFile.fileName(); 78 | return false; 79 | } 80 | confFile.write("{}\n"); //empty json object 81 | confFile.close(); 82 | } 83 | 84 | return true; 85 | } 86 | -------------------------------------------------------------------------------- /daemon/daemon.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT = core service jsonserializer 4 | 5 | CONFIG += console 6 | CONFIG -= app_bundle 7 | 8 | TARGET = $${TARGET_BASE}d 9 | DESTDIR = $$BIN_DESTDIR 10 | 11 | HEADERS += \ 12 | syncengine.h \ 13 | pathresolver.h \ 14 | conflipservice.h 15 | 16 | SOURCES += \ 17 | main.cpp \ 18 | syncengine.cpp \ 19 | pathresolver.cpp \ 20 | conflipservice.cpp 21 | 22 | DISTFILES += \ 23 | conflip.service.in \ 24 | qpmx.json 25 | 26 | # install 27 | linux { 28 | create_service.target = conflip.service 29 | create_service.depends += $$PWD/conflip.service.in 30 | create_service.commands += sed "s:%{INSTALL_BINS}:$$INSTALL_BINS:g" $$PWD/conflip.service.in | sed "s:%{SLICE}::g" > conflip.service 31 | 32 | create_service_slice.target = conflip@.service 33 | create_service_slice.depends += $$PWD/conflip.service.in 34 | create_service_slice.commands += sed "s:%{INSTALL_BINS}:$$INSTALL_BINS:g" $$PWD/conflip.service.in | sed $$shell_quote(s:%{SLICE}:--slice %I:g) > conflip@.service 35 | 36 | TEST_TARGET=$${TARGET_BASE}@test 37 | slice_rundummy.target = $$BIN_DESTDIR/$$TEST_TARGET 38 | slice_rundummy.commands += echo \'$${LITERAL_HASH}!/bin/sh\' > $$shell_quote($$BIN_DESTDIR/$$TEST_TARGET) \ 39 | $$escape_expand(\n\t)echo exec $$shell_quote($$BIN_DESTDIR/$$TARGET) $$shell_quote(\"$${LITERAL_DOLLAR}$${LITERAL_DOLLAR}@\") --slice test >> $$shell_quote($$BIN_DESTDIR/$$TEST_TARGET) \ 40 | $$escape_expand(\n\t)chmod a+x $$shell_quote($$BIN_DESTDIR/$$TEST_TARGET) 41 | 42 | QMAKE_EXTRA_TARGETS += create_service create_service_slice slice_rundummy 43 | PRE_TARGETDEPS += conflip.service conflip@.service "$$BIN_DESTDIR/$$TEST_TARGET" 44 | 45 | install_service_user.files += $$OUT_PWD/conflip.service $$OUT_PWD/conflip@.service 46 | install_service_user.CONFIG += no_check_exist 47 | install_service_user.path = $$INSTALL_LIBS/systemd/user/ 48 | install_service_system.files += $$OUT_PWD/conflip.service $$OUT_PWD/conflip@.service 49 | install_service_system.CONFIG += no_check_exist 50 | install_service_system.path = $$INSTALL_LIBS/systemd/system/ 51 | INSTALLS += install_service_user install_service_system 52 | } 53 | 54 | target.path = $$INSTALL_BINS 55 | INSTALLS += target 56 | 57 | # libs 58 | include(../lib.pri) 59 | 60 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.) 61 | else: include($$OUT_PWD/qpmx_generated.pri) 62 | -------------------------------------------------------------------------------- /plugins/xmlsync/xmlsynchelper.h: -------------------------------------------------------------------------------- 1 | #ifndef XMLSYNCHELPER_H 2 | #define XMLSYNCHELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class XmlSyncHelper; 9 | class XmlSyncTask : public SyncTask 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | // sync 15 | XmlSyncTask(const XmlSyncHelper *helper, 16 | QString &&mode, 17 | const QDir &syncDir, 18 | QString &&path, 19 | QStringList &&extras, 20 | bool isFirstUse, 21 | QObject *parent); 22 | // remove 23 | XmlSyncTask(const XmlSyncHelper *helper, 24 | QString &&mode, 25 | const QDir &syncDir, 26 | QString &&path, 27 | QObject *parent); 28 | 29 | protected: 30 | void performSync() override; 31 | 32 | private: 33 | static QMutex _XmlMutex; 34 | 35 | void updateText(const QDomElement& srcElement, const QDomElement& syncElement, 36 | bool srcExists, bool syncExists, 37 | bool srcIsNewer, 38 | bool &srcNeedsUpdate, bool &syncNeedsUpdate, 39 | const QString &key); 40 | void updateAttributes(const QDomElement& srcElement, const QDomElement& syncElement, 41 | bool srcIsNewer, 42 | bool &srcNeedsUpdate, bool &syncNeedsUpdate, 43 | const QString &key); 44 | void updateAttribute(QDomElement srcElement, QDomElement syncElement, 45 | QDomAttr srcAttr, QDomAttr syncAttr, 46 | bool srcIsNewer, 47 | bool &srcNeedsUpdate, bool &syncNeedsUpdate, 48 | const QString &key); 49 | 50 | QDomDocument loadDocument(const QFileInfo &file) const; 51 | void saveDocument(const QFileInfo &file, const QDomDocument& document); 52 | 53 | QDomDocument createDoc(const QDomDocument& other) const; 54 | QDomElement cd(QDomElement current, const QString &tag, bool &exists) const; 55 | QString getText(const QDomElement& element) const; 56 | void writeText(QDomElement element, const QString &text) const; 57 | void removeAttribs(QDomElement node) const; 58 | }; 59 | 60 | class XmlSyncHelper : public SyncHelper 61 | { 62 | Q_OBJECT 63 | 64 | public: 65 | static const QString ModeXml; 66 | 67 | explicit XmlSyncHelper(QObject *parent = nullptr); 68 | 69 | bool pathIsPattern(const QString &mode) const override; 70 | bool canSyncDirs(const QString &mode) const override; 71 | ExtrasHint extrasHint() const override; 72 | 73 | SyncTask *createSyncTask(QString mode, const QDir &syncDir, QString path, QStringList extras, bool isFirstUse, QObject *parent) override; 74 | SyncTask *createUndoSyncTask(QString mode, const QDir &syncDir, QString path, QObject *parent) override; 75 | }; 76 | 77 | #endif // XMLSYNCHELPER_H 78 | -------------------------------------------------------------------------------- /daemon/conflipservice.cpp: -------------------------------------------------------------------------------- 1 | #include "conflipservice.h" 2 | #include 3 | #include 4 | 5 | ConflipService::ConflipService(int &argc, char **argv) : 6 | Service(argc, argv) 7 | { 8 | QCoreApplication::setApplicationName(QStringLiteral(TARGET)); 9 | QCoreApplication::setApplicationVersion(QStringLiteral(VERSION)); 10 | QCoreApplication::setOrganizationName(QStringLiteral(COMPANY)); 11 | QCoreApplication::setOrganizationDomain(QStringLiteral(BUNDLE)); 12 | } 13 | 14 | void ConflipService::setSlice(const QString &slice) 15 | { 16 | QCoreApplication::setApplicationName(QStringLiteral(TARGET "@%1").arg(slice)); 17 | } 18 | 19 | QtService::Service::CommandResult ConflipService::onStart() 20 | { 21 | QCommandLineParser parser; 22 | parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); 23 | parser.addOption({QStringLiteral("platform"), {}}); 24 | parser.addOption({QStringLiteral("backend"), {}}); 25 | parser.addOption({QStringLiteral("slice"), {}}); 26 | parser.addOption({QStringLiteral("loglevel"), {}, QStringLiteral("level"), 27 | #ifdef QT_NO_DEBUG 28 | QString::number(3)}); 29 | #else 30 | QString::number(4)}); 31 | #endif 32 | if(!parser.parse(QCoreApplication::arguments())) { 33 | qCritical().noquote() << parser.errorText(); 34 | return CommandResult::Failed; 35 | } 36 | 37 | QString logStr; 38 | switch (parser.value(QStringLiteral("loglevel")).toInt()) { 39 | case 0: 40 | logStr.prepend(QStringLiteral("\n*.critical=false")); 41 | Q_FALLTHROUGH(); 42 | case 1: 43 | logStr.prepend(QStringLiteral("\n*.warning=false")); 44 | Q_FALLTHROUGH(); 45 | case 2: 46 | logStr.prepend(QStringLiteral("\n*.info=false")); 47 | Q_FALLTHROUGH(); 48 | case 3: 49 | logStr.prepend(QStringLiteral("*.debug=false")); 50 | Q_FALLTHROUGH(); 51 | case 4: 52 | default: 53 | break; 54 | } 55 | QLoggingCategory::setFilterRules(logStr); 56 | 57 | _engine = new SyncEngine{this}; 58 | if(_engine->start()) 59 | return CommandResult::Completed; 60 | else 61 | return CommandResult::Failed; 62 | } 63 | 64 | QtService::Service::CommandResult ConflipService::onStop(int &exitCode) 65 | { 66 | _engine->pause(); 67 | exitCode = EXIT_SUCCESS; 68 | return CommandResult::Completed; 69 | } 70 | 71 | QtService::Service::CommandResult ConflipService::onReload() 72 | { 73 | _engine->reload(); 74 | return CommandResult::Completed; 75 | } 76 | 77 | QtService::Service::CommandResult ConflipService::onPause() 78 | { 79 | _engine->pause(); 80 | return CommandResult::Completed; 81 | } 82 | 83 | QtService::Service::CommandResult ConflipService::onResume() 84 | { 85 | _engine->resume(); 86 | return CommandResult::Completed; 87 | } 88 | -------------------------------------------------------------------------------- /plugins/dconfsync/dconfsynchelper.h: -------------------------------------------------------------------------------- 1 | #ifndef DCONFSYNCHELPER_H 2 | #define DCONFSYNCHELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class DConfEntry 12 | { 13 | Q_GADGET 14 | 15 | Q_PROPERTY(QString type READ getType WRITE setType) 16 | Q_PROPERTY(QByteArray data MEMBER data) 17 | Q_PROPERTY(QDateTime lastModified MEMBER lastModified) 18 | 19 | public: 20 | QByteArray type; 21 | QByteArray data; 22 | QDateTime lastModified; 23 | 24 | bool operator==(const DConfEntry &other) const; 25 | 26 | private: 27 | QString getType() const; 28 | void setType(const QString &value); 29 | }; 30 | 31 | uint qHash(const DConfEntry &entry, uint seed); 32 | 33 | class DConfSyncHelper; 34 | class DConfSyncTask : public SyncTask 35 | { 36 | Q_OBJECT 37 | 38 | public: 39 | // sync 40 | DConfSyncTask(const DConfSyncHelper *helper, 41 | QString &&mode, 42 | const QDir &syncDir, 43 | QString &&path, 44 | QStringList &&extras, 45 | bool isFirstUse, 46 | QObject *parent); 47 | // remove 48 | DConfSyncTask(const DConfSyncHelper *helper, 49 | QString &&mode, 50 | const QDir &syncDir, 51 | QString &&path, 52 | QObject *parent); 53 | 54 | // SyncTask interface 55 | protected: 56 | void performSync() override; 57 | void undoSync() override; 58 | 59 | private: 60 | using DConfMap = QMap; 61 | QJsonSerializer *_serializer; 62 | 63 | QFileInfo syncFile(); 64 | QSharedPointer loadSettings(const QString &path); 65 | DConfMap readSyncConf(const QFileInfo &file) const; 66 | void writeSyncConf(const QFileInfo &file, const DConfMap &map); 67 | }; 68 | 69 | class DConfSyncHelper : public SyncHelper 70 | { 71 | Q_OBJECT 72 | 73 | public: 74 | static const QString ModeDConf; 75 | 76 | explicit DConfSyncHelper(QObject *parent = nullptr); 77 | 78 | bool pathIsPattern(const QString &mode) const override; 79 | bool canSyncDirs(const QString &mode) const override; 80 | ExtrasHint extrasHint() const override; 81 | 82 | SyncTask *createSyncTask(QString mode, const QDir &syncDir, QString path, QStringList extras, bool isFirstUse, QObject *parent) override; 83 | SyncTask *createUndoSyncTask(QString mode, const QDir &syncDir, QString path, QObject *parent) override; 84 | 85 | private: 86 | using DConfMap = QMap; 87 | QJsonSerializer *_serializer; 88 | 89 | QSharedPointer loadSettings(const QString &path); 90 | DConfMap readSyncConf(const QFileInfo &file) const; 91 | void writeSyncConf(const QFileInfo &file, const DConfMap &map); 92 | 93 | void log(const QString &path, const char *msg, bool dbg = false) const; 94 | void log(const QString &path, const char *msg, const QByteArray &key, bool dbg = false) const; 95 | }; 96 | 97 | #endif // DCONFSYNCHELPER_H 98 | -------------------------------------------------------------------------------- /plugins/inisync/inisynchelper.h: -------------------------------------------------------------------------------- 1 | #ifndef INISYNCHELPER_H 2 | #define INISYNCHELPER_H 3 | 4 | #include 5 | #include 6 | 7 | class IniSyncHelper; 8 | class IniSyncTask : public SyncTask 9 | { 10 | Q_OBJECT 11 | public: 12 | // sync 13 | IniSyncTask(const IniSyncHelper *helper, 14 | QString &&mode, 15 | const QDir &syncDir, 16 | QString &&path, 17 | QStringList &&extras, 18 | bool isFirstUse, 19 | QObject *parent); 20 | // remove 21 | IniSyncTask(const IniSyncHelper *helper, 22 | QString &&mode, 23 | const QDir &syncDir, 24 | QString &&path, 25 | QObject *parent); 26 | 27 | protected: 28 | void performSync() override; 29 | 30 | private: 31 | using KeyInfo = std::pair, QList>; 32 | using IniGroupMapping = QMap; 33 | using IniEntryMapping = QMap; 34 | 35 | IniEntryMapping readSyncMapping(const QFileInfo &sync); 36 | void writeMapping(QIODevice *device, const IniEntryMapping &mapping, bool firstLine, bool &needSave, bool log = true); 37 | void writeMapping(const QFileInfo &file, const IniEntryMapping &mapping); 38 | bool shouldSync(const QByteArray &group, const QByteArray &key, const KeyInfo &extras); 39 | bool startsWith(const QByteArrayList &key, const QByteArrayList &subList); 40 | 41 | QString logKey(const QByteArray &cGroup, const QByteArray &key) const; 42 | }; 43 | 44 | class IniSyncHelper : public SyncHelper 45 | { 46 | Q_OBJECT 47 | 48 | public: 49 | static const QString ModeIni; 50 | 51 | explicit IniSyncHelper(QObject *parent = nullptr); 52 | 53 | bool pathIsPattern(const QString &mode) const override; 54 | bool canSyncDirs(const QString &mode) const override; 55 | ExtrasHint extrasHint() const override; 56 | 57 | SyncTask *createSyncTask(QString mode, const QDir &syncDir, QString path, QStringList extras, bool isFirstUse, QObject *parent) override; 58 | SyncTask *createUndoSyncTask(QString mode, const QDir &syncDir, QString path, QObject *parent) override; 59 | 60 | private: 61 | using KeyInfo = std::pair, QList>; 62 | using IniGroupMapping = QMap; 63 | using IniEntryMapping = QMap; 64 | 65 | IniEntryMapping createMapping(const QFileInfo &file) const; 66 | void writeMapping(QIODevice *device, const IniEntryMapping &mapping, bool firstLine, bool &needSave, const QString &logStr = {}) const; 67 | void writeMapping(const QFileInfo &file, const IniEntryMapping &mapping); 68 | bool shouldSync(const QByteArray &group, const QByteArray &key, const KeyInfo &extras) const; 69 | bool startsWith(const QByteArrayList &key, const QByteArrayList &subList) const; 70 | 71 | void log(const QFileInfo &file, const char *msg, bool dbg = false) const; 72 | void log(const QFileInfo &file, const char *msg, const QByteArray &cGroup, const QByteArray &key, bool dbg = false) const; 73 | }; 74 | 75 | #endif // INISYNCHELPER_H 76 | -------------------------------------------------------------------------------- /plugins/xmlsync/conflip_xmlsync_de.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | XmlSyncHelper 6 | 7 | 8 | &Keys 9 | &Schlüssel 10 | 11 | 12 | 13 | <p>Enter the paths of the XML-Elements you want to synchronize. Elements are seperated by a '/' and there are some special characters that match special things. The possible variants are:<ul> <li>"some/node": A path of elements. This will synchronize the element "node" within the element "some" inside the root element. All attributes, child-elements and text are synchronized</li> <li>"node/~": This will synchronize the text within the "node" element, and nothing else</li> <li>"node/=": This will synchronize all attributes of "node" but not the contents</li> <li>"node/=key": This will synchronize the attribute "key" of "node" but not the contents</li> <li>"node/#": This will synchronize all attributes and content text of "node"</li> <li>"node/": This will synchronize all child elements in "node", but no attributes</li></ul></p><p>You never have to specify the root element, as a XML-Document can only have a single root.</p> 14 | <p>Geben Sie den Pfad zu den XML-Elementen ein, die synchronisiert werden sollen. Elemente werden durch ein '/' getrennt und es gibt einige Spezial-Symbole für Dinge innerhalb von Elementen. Die verschiedenen Varianten sind: 15 | <ul> 16 | <li>"some/node": Ein Pfad von Elementen. Hier würde das Element "node" innerhalb des Elements "some" innerhalb des Wurzel-Elements synchronisiert. Alle Attribute, Unterelemente und Text werden mit synchronisiert</li> 17 | <li>"node/~": Synchronisiert den Text des "node" Elements, aber sonst nichts</li> 18 | <li>"node/=": Synchronisiert alle Attribute des "node" Elements, aber keine "Inhalte" des Elements</li> 19 | <li>"node/=key": Synchronisiert nur das Attribut "key" des "node" Elements, und sonst nichts</li> 20 | <li>"node/#": Synchronisiert alle Attribute und den Text des "node" Elements, aber keine Unterelemente</li> 21 | <li>"node/": Synchronisiert alle Unterelemente des "Node" Elements, aber keiner seiner Attribute</li> 22 | </ul></p> 23 | <p>Das Wurzel-Element muss dabei nie explizit Angegeben werden, da XML-Dokumente nur ein Wurzel-Element haben können.</p> 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/synchelper.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCHELPER_H 2 | #define SYNCHELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "syncentry.h" 10 | #include "lib_conflip_global.h" 11 | 12 | class SyncHelper; 13 | class LIBCONFLIPSHARED_EXPORT SyncTask : public QObject, public QRunnable 14 | { 15 | Q_OBJECT 16 | Q_DISABLE_COPY(SyncTask) 17 | 18 | public: 19 | enum Result { 20 | Invalid, 21 | Synced, 22 | Removed, 23 | Error, 24 | NotASymlink 25 | }; 26 | Q_ENUM(Result) 27 | 28 | // sync 29 | SyncTask(const SyncHelper *helper, 30 | QString mode, 31 | const QDir &syncDir, 32 | QString path, 33 | QStringList extras, 34 | bool isFirstUse, 35 | QObject *parent); 36 | // remove 37 | SyncTask(const SyncHelper *helper, 38 | QString mode, 39 | const QDir &syncDir, 40 | QString path, 41 | QObject *parent); 42 | 43 | void run() final; 44 | 45 | protected: 46 | virtual void performSync() = 0; 47 | virtual void undoSync(); 48 | 49 | QFileInfo srcPath() const; 50 | QFileInfo syncPath() const; 51 | 52 | QDebug debug(const QString &extra = {}) const; 53 | QDebug info(const QString &extra = {}) const; 54 | QDebug warning(const QString &extra = {}) const; 55 | QDebug critical(const QString &extra = {}) const; 56 | Q_NORETURN void fatal(const QString &message, const QString &extra = {}) const; 57 | Q_NORETURN void fatal(const QByteArray &message, const QString &extra = {}) const; 58 | Q_NORETURN void fatal(const char *message, const QString &extra = {}) const; 59 | 60 | const SyncHelper * const helper; 61 | const QString mode; 62 | const QDir syncDir; 63 | const QString path; 64 | const QStringList extras; 65 | const bool isFirstUse = false; 66 | 67 | signals: 68 | void syncDone(SyncTask *task, SyncTask::Result result); 69 | 70 | private slots: 71 | void reportResult(); 72 | 73 | private: 74 | const bool _isRemove; 75 | mutable QList> _log; 76 | Result _result = Invalid; 77 | 78 | void logBase(QDebug &dbg, const QString &extra) const; 79 | }; 80 | 81 | class LIBCONFLIPSHARED_EXPORT SyncHelper : public QObject 82 | { 83 | public: 84 | struct ExtrasHint { 85 | bool enabled; 86 | QString title; 87 | QString hint; 88 | }; 89 | 90 | SyncHelper(QObject *parent = nullptr); 91 | 92 | virtual QString syncPrefix(const QString &mode) const; 93 | 94 | virtual bool pathIsPattern(const QString &mode) const = 0; 95 | virtual bool canSyncDirs(const QString &mode) const = 0; 96 | virtual QString toSyncPath(const QString &mode, const QDir &syncDir, const QString &path) const; 97 | virtual QString toSrcPath(const QString &mode, const QDir &syncDir, const QString &path) const; 98 | 99 | virtual ExtrasHint extrasHint() const; 100 | 101 | virtual SyncTask *createSyncTask(QString mode, 102 | const QDir &syncDir, 103 | QString path, 104 | QStringList extras, 105 | bool isFirstUse, 106 | QObject *parent) = 0; 107 | virtual SyncTask *createUndoSyncTask(QString mode, 108 | const QDir &syncDir, 109 | QString path, 110 | QObject *parent) = 0; 111 | }; 112 | 113 | class NotASymlinkException {}; 114 | 115 | #endif // SYNCHELPER_H 116 | -------------------------------------------------------------------------------- /daemon/pathresolver.cpp: -------------------------------------------------------------------------------- 1 | #include "pathresolver.h" 2 | 3 | #include 4 | #include 5 | 6 | PathResolver::PathResolver(QObject *parent) : 7 | QObject(parent) 8 | {} 9 | 10 | void PathResolver::setSyncDir(const QDir &dir) 11 | { 12 | _syncDir = dir; 13 | } 14 | 15 | QStringList PathResolver::resolvePath(const SyncEntry &entry, SyncHelper *helper) const 16 | { 17 | QStringList allPaths; 18 | 19 | // match all "src" paths 20 | auto pathList = entry.pathPattern.split(QLatin1Char('/'), QString::SkipEmptyParts); 21 | auto cd = findRootDir(pathList, entry); 22 | allPaths.append(findFiles(cd, pathList, entry)); 23 | 24 | // match all "sync" paths 25 | pathList = helper->toSyncPath(entry.mode, _syncDir, entry.pathPattern).split(QLatin1Char('/'), QString::SkipEmptyParts); 26 | cd = findRootDir(pathList, entry); 27 | const auto sPaths = findFiles(cd, pathList, entry); 28 | allPaths.reserve(allPaths.size() + sPaths.size()); 29 | for(const auto &path : sPaths) 30 | allPaths.append(helper->toSrcPath(entry.mode, _syncDir, path)); 31 | 32 | allPaths.removeDuplicates(); 33 | return allPaths; 34 | } 35 | 36 | QStringList PathResolver::findFiles(const QDir &cd, QStringList pathList, const SyncEntry &entry) const 37 | { 38 | auto element = pathList.takeFirst(); 39 | if(element == QStringLiteral("**")) { 40 | QStringList expList {element}; 41 | expList.append(pathList); 42 | 43 | QStringList resList; 44 | auto subElements = cd.entryInfoList(); 45 | for(const auto& subElement : subElements) { 46 | if(!subElement.isDir()) 47 | continue; 48 | // search without ** 49 | resList.append(findFiles(createDir(subElement.absoluteFilePath(), entry), pathList, entry)); 50 | // search with ** 51 | resList.append(findFiles(createDir(subElement.absoluteFilePath(), entry), expList, entry)); 52 | } 53 | 54 | return resList; 55 | } else { 56 | QStringList resList; 57 | auto subElements = cd.entryInfoList({element}); 58 | resList.reserve(subElements.size()); 59 | if(pathList.isEmpty()) { 60 | for(const auto& subElement : subElements) { 61 | if(!entry.matchDirs && subElement.isDir()) 62 | continue; 63 | resList.append(subElement.absoluteFilePath()); 64 | } 65 | } else { 66 | for(const auto& subElement : subElements) { 67 | if(!subElement.isDir()) 68 | continue; 69 | resList.append(findFiles(createDir(subElement.absoluteFilePath(), entry), pathList, entry)); 70 | } 71 | } 72 | 73 | return resList; 74 | } 75 | } 76 | 77 | QDir PathResolver::findRootDir(QStringList &pathList, const SyncEntry &entry) const 78 | { 79 | if(pathList.isEmpty()) 80 | return createDir(QDir::rootPath(), entry); 81 | 82 | const QStorageInfo firstInfo{pathList.first()}; 83 | if(firstInfo.isValid() && firstInfo.rootPath() == pathList.first()) //NOTE untested on win 84 | return createDir(pathList.takeFirst(), entry); 85 | else 86 | return createDir(QDir::rootPath(), entry); 87 | } 88 | 89 | QDir PathResolver::createDir(const QString &path, const SyncEntry &entry) const 90 | { 91 | QDir dir{path}; 92 | QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Readable; 93 | if(entry.caseSensitive) 94 | filters |= QDir::CaseSensitive; 95 | if(entry.includeHidden) 96 | filters |= QDir::Hidden; 97 | dir.setFilter(filters); 98 | return dir; 99 | } 100 | -------------------------------------------------------------------------------- /plugins/dconfsync/dconfaccess.cpp: -------------------------------------------------------------------------------- 1 | #include "dconfaccess.h" 2 | 3 | extern "C" { 4 | #include 5 | } 6 | 7 | namespace { 8 | 9 | void variantCleanup(gpointer data) 10 | { 11 | delete static_cast(data); 12 | } 13 | 14 | } 15 | 16 | DConfAccess::DConfAccess() : 17 | _client(dconf_client_new()), 18 | _path(), 19 | _needsSync(false) 20 | {} 21 | 22 | DConfAccess::~DConfAccess() 23 | { 24 | sync(); 25 | g_object_unref(_client); 26 | } 27 | 28 | void DConfAccess::open(const QByteArray &path) 29 | { 30 | _path = path; 31 | } 32 | 33 | QByteArrayList DConfAccess::readAllKeys(const QByteArrayList &filters) const 34 | { 35 | auto bList = readAllKeysImpl({}); 36 | for(auto it = bList.begin(); it != bList.end();) { 37 | auto erase = true; 38 | for(const auto &filter : filters) { 39 | if(it->startsWith(filter)) { 40 | erase = false; 41 | break; 42 | } 43 | } 44 | if(erase) 45 | it = bList.erase(it); 46 | else 47 | it++; 48 | } 49 | return bList; 50 | } 51 | 52 | 53 | std::tuple DConfAccess::readData(const QByteArray &key) const 54 | { 55 | // read variant 56 | auto variant = dconf_client_read(_client, QByteArray(_path + key).constData()); 57 | if(!variant) 58 | return {}; 59 | 60 | // normalize 61 | auto nVariant = g_variant_get_normal_form(variant); 62 | g_variant_unref(variant); 63 | if(!nVariant) 64 | return {}; 65 | 66 | // read type and value 67 | QByteArray type {g_variant_get_type_string(nVariant)}; 68 | auto size = g_variant_get_size(nVariant); 69 | QByteArray value(static_cast(size), '\0'); 70 | g_variant_store(nVariant, value.data()); 71 | g_variant_unref(nVariant); 72 | 73 | return {type, value}; 74 | } 75 | 76 | bool DConfAccess::writeData(const QByteArray &key, const QByteArray &type, const QByteArray &data, QByteArray *errorMsg) 77 | { 78 | QByteArray realPath = _path + key; 79 | 80 | // create the type 81 | if(!g_variant_type_string_is_valid(type.constData())) 82 | return false; 83 | auto vType = g_variant_type_new(type.constData()); 84 | 85 | // create the variant 86 | auto variant = g_variant_new_from_data(vType, 87 | data.constData(), static_cast(data.size()), 88 | true, 89 | variantCleanup, new QByteArray(data)); 90 | 91 | //write the value (async) 92 | GError *error = nullptr; 93 | auto ok = dconf_client_write_fast(_client, realPath.constData(), variant, &error); 94 | if(!ok) { 95 | if(errorMsg) 96 | *errorMsg = error->message; 97 | g_error_free(error); 98 | } 99 | 100 | _needsSync = true; 101 | g_variant_type_free(vType); 102 | return ok; 103 | } 104 | 105 | void DConfAccess::sync() 106 | { 107 | if(_needsSync) { 108 | dconf_client_sync(_client); 109 | _needsSync = false; 110 | } 111 | } 112 | 113 | QByteArrayList DConfAccess::readAllKeysImpl(const QByteArray &base) const 114 | { 115 | QByteArray realPath = _path + base; 116 | gint len = 0; 117 | auto list = dconf_client_list(_client, realPath.constData(), &len); 118 | if(list) { 119 | QByteArrayList bList; 120 | bList.reserve(len); 121 | for(gint i = 0; i < len; i++) { 122 | QByteArray element = base + QByteArray(list[i]); 123 | if(element.endsWith('/')) 124 | bList.append(readAllKeysImpl(element)); 125 | else 126 | bList.append(element); 127 | } 128 | g_strfreev(list); 129 | return bList; 130 | } else 131 | return {}; 132 | } 133 | -------------------------------------------------------------------------------- /plugins/jsonsync/jsonsynchelper.h: -------------------------------------------------------------------------------- 1 | #ifndef JSONSYNCHELPER_H 2 | #define JSONSYNCHELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class JsonSyncHelper; 11 | class JsonSyncTask : public SyncTask 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | // sync 17 | JsonSyncTask(const JsonSyncHelper *helper, 18 | QString &&mode, 19 | const QDir &syncDir, 20 | QString &&path, 21 | QStringList &&extras, 22 | bool isFirstUse, 23 | QObject *parent); 24 | // remove 25 | JsonSyncTask(const JsonSyncHelper *helper, 26 | QString &&mode, 27 | const QDir &syncDir, 28 | QString &&path, 29 | QObject *parent); 30 | protected: 31 | void performSync() override; 32 | 33 | private: 34 | const bool _isBinary; 35 | QList _keyChains; 36 | bool _srcIsNewer = false; 37 | bool _srcNeedsUpdate = false; 38 | bool _syncNeedsUpdate = false; 39 | 40 | void performArraySync(QJsonArray &srcArray, 41 | const QJsonArray &syncArray, 42 | QJsonArray &resArray); 43 | void performObjSync(QJsonObject &srcObj, 44 | const QJsonObject &syncObj, 45 | QJsonObject &resObj); 46 | 47 | void traverse(QJsonObject &srcObj, 48 | const QJsonObject &syncObj, 49 | QJsonObject &resObj, 50 | const QStringList &keyChain, 51 | int keyIndex); 52 | QJsonObject getChild(const QJsonObject &srcObj, 53 | const QString &key, 54 | const QStringList &keyChain, 55 | int keyIndex, const QByteArray &target); 56 | 57 | void syncElement(QJsonObject &srcObj, 58 | const QJsonObject &syncObj, 59 | QJsonObject &resObj, 60 | const QString &key, 61 | const QString &logKey); 62 | 63 | QJsonDocument readFile(const QString &path, const QByteArray &target); 64 | void writeFile(const QString &path, const QJsonDocument &doc, const QByteArray &target); 65 | }; 66 | 67 | class JsonSyncHelper : public SyncHelper 68 | { 69 | Q_OBJECT 70 | 71 | public: 72 | static const QString ModeJson; 73 | static const QString ModeQbjs; 74 | 75 | explicit JsonSyncHelper(QObject *parent = nullptr); 76 | 77 | bool pathIsPattern(const QString &mode) const override; 78 | bool canSyncDirs(const QString &mode) const override; 79 | ExtrasHint extrasHint() const override; 80 | 81 | SyncTask *createSyncTask(QString mode, const QDir &syncDir, QString path, QStringList extras, bool isFirstUse, QObject *parent) override; 82 | SyncTask *createUndoSyncTask(QString mode, const QDir &syncDir, QString path, QObject *parent) override; 83 | 84 | private: 85 | bool _isBinary; 86 | 87 | void performArraySync(QJsonArray &srcArray, 88 | const QJsonArray &syncArray, 89 | QJsonArray &resArray, 90 | const QList &keyChains, 91 | bool srcIsNewer, 92 | bool &srcNeedsUpdate, 93 | bool &syncNeedsUpdate, 94 | const QString &logPath); 95 | void performObjSync(QJsonObject &srcObj, 96 | const QJsonObject &syncObj, 97 | QJsonObject &resObj, 98 | const QList &keyChains, 99 | bool srcIsNewer, 100 | bool &srcNeedsUpdate, 101 | bool &syncNeedsUpdate, 102 | const QString &logPath); 103 | 104 | void traverse(QJsonObject &srcObj, 105 | const QJsonObject &syncObj, 106 | QJsonObject &resObj, 107 | const QStringList &keyChain, 108 | int keyIndex, 109 | bool srcIsNewer, 110 | bool &srcNeedsUpdate, 111 | bool &syncNeedsUpdate, 112 | const QString &logPath); 113 | QJsonObject getChild(const QJsonObject &srcObj, 114 | const QString &key, 115 | const QStringList &keyChain, 116 | int keyIndex, const QByteArray &target); 117 | 118 | void syncElement(QJsonObject &srcObj, 119 | const QJsonObject &syncObj, 120 | QJsonObject &resObj, 121 | const QString &key, 122 | bool srcIsNewer, 123 | bool &srcNeedsUpdate, 124 | bool &syncNeedsUpdate, 125 | const QString &logPath, 126 | const QByteArray &logKey); 127 | 128 | QJsonDocument readFile(const QString &path, const QByteArray &target); 129 | void writeFile(const QString &path, const QJsonDocument &doc, const QByteArray &target); 130 | 131 | void log(const QString &path, const char *msg, bool dbg = false) const; 132 | void log(const QString &path, const char *msg, const QByteArray &key, bool dbg = false) const; 133 | }; 134 | 135 | #endif // JSONSYNCHELPER_H 136 | -------------------------------------------------------------------------------- /gui/createentrydialog.cpp: -------------------------------------------------------------------------------- 1 | #include "createentrydialog.h" 2 | #include "ui_createentrydialog.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | CreateEntryDialog::CreateEntryDialog(QWidget *parent) : 9 | QDialog(parent), 10 | ui(new Ui::CreateEntryDialog) 11 | { 12 | ui->setupUi(this); 13 | DialogMaster::masterDialog(this); 14 | 15 | ui->selectButton->addActions({ 16 | ui->actionSelect_File, 17 | ui->actionSelect_Directory 18 | }); 19 | ui->selectButton->setDefaultAction(ui->actionSelect_File); 20 | 21 | ui->modeComboBox->addItems(Conflip::listPlugins()); 22 | ui->modeComboBox->setCurrentText(QStringLiteral("symlink")); 23 | 24 | ui->addButton->setDefaultAction(ui->action_Add_Extra); 25 | ui->removeButton->setDefaultAction(ui->action_Remove_Extra); 26 | ui->extrasListWidget->addActions({ 27 | ui->action_Add_Extra, 28 | ui->action_Remove_Extra 29 | }); 30 | ui->helpButton->setDefaultAction(ui->actionWhats_this); 31 | connect(ui->actionWhats_this, &QAction::triggered, 32 | this, &CreateEntryDialog::showHint); 33 | 34 | if(Settings::instance()->gui.createentrydialog.size.isSet()) 35 | resize(Settings::instance()->gui.createentrydialog.size); 36 | } 37 | 38 | CreateEntryDialog::~CreateEntryDialog() 39 | { 40 | Settings::instance()->gui.createentrydialog.size = size(); 41 | 42 | delete ui; 43 | } 44 | 45 | SyncEntry CreateEntryDialog::createEntry(QWidget *parent) 46 | { 47 | return editEntry({}, parent); 48 | } 49 | 50 | SyncEntry CreateEntryDialog::editEntry(const SyncEntry &entry, QWidget *parent) 51 | { 52 | CreateEntryDialog dialog(parent); 53 | 54 | if(entry) { 55 | dialog.ui->pathLineEdit->setText(entry.pathPattern); 56 | dialog.ui->modeComboBox->setCurrentText(entry.mode); 57 | dialog.ui->hiddenFilesCheckBox->setChecked(entry.includeHidden); 58 | dialog.ui->caseSensitiveCheckBox->setChecked(entry.caseSensitive); 59 | dialog.ui->matchDirectoriesCheckBox->setChecked(entry.matchDirs); 60 | for(const auto& extra : entry.extras) { 61 | auto item = new QListWidgetItem(extra, dialog.ui->extrasListWidget); 62 | item->setFlags(item->flags() | Qt::ItemIsEditable); 63 | } 64 | } 65 | 66 | if(dialog.exec() == QDialog::Accepted) { 67 | SyncEntry resEntry; 68 | resEntry.setCleanPathPattern(dialog.ui->pathLineEdit->text()); 69 | resEntry.mode = dialog.ui->modeComboBox->currentText(); 70 | resEntry.includeHidden = dialog.ui->hiddenFilesCheckBox->isChecked(); 71 | resEntry.caseSensitive = 72 | dialog.ui->caseSensitiveCheckBox->isChecked(); 73 | resEntry.matchDirs = dialog.ui->matchDirectoriesCheckBox->isEnabled() && 74 | dialog.ui->matchDirectoriesCheckBox->isChecked(); 75 | for(auto i = 0; i < dialog.ui->extrasListWidget->count(); i++) 76 | resEntry.extras.append(dialog.ui->extrasListWidget->item(i)->text()); 77 | return resEntry; 78 | } else 79 | return {}; 80 | } 81 | 82 | void CreateEntryDialog::showHint() 83 | { 84 | QWhatsThis::showText(mapToGlobal({width()/2, ui->extrasListWidget->y()}), 85 | ui->extrasListWidget->whatsThis(), 86 | this); 87 | } 88 | 89 | void CreateEntryDialog::on_actionSelect_File_triggered() 90 | { 91 | auto path = DialogMaster::getOpenFileName(this, tr("Select a file")); 92 | if(!path.isEmpty()) 93 | ui->pathLineEdit->setText(path); 94 | } 95 | 96 | void CreateEntryDialog::on_actionSelect_Directory_triggered() 97 | { 98 | auto path = DialogMaster::getExistingDirectory(this, tr("Select a directory")); 99 | if(!path.isEmpty()) 100 | ui->pathLineEdit->setText(path); 101 | } 102 | 103 | void CreateEntryDialog::on_action_Add_Extra_triggered() 104 | { 105 | auto item = new QListWidgetItem(ui->extrasListWidget); 106 | item->setFlags(item->flags() | Qt::ItemIsEditable); 107 | ui->extrasListWidget->setCurrentItem(item); 108 | ui->extrasListWidget->editItem(item); 109 | } 110 | 111 | void CreateEntryDialog::on_action_Remove_Extra_triggered() 112 | { 113 | auto item = ui->extrasListWidget->currentItem(); 114 | if(item) 115 | delete item; 116 | } 117 | 118 | void CreateEntryDialog::on_modeComboBox_currentIndexChanged(const QString &text) 119 | { 120 | Conflip::loadTranslations(text); 121 | auto helper = Conflip::loadHelper(text, this); 122 | if(helper) { 123 | ui->matchDirectoriesCheckBox->setEnabled(helper->canSyncDirs(text)); 124 | auto info = helper->extrasHint(); 125 | ui->extrasLabel->setEnabled(info.enabled); 126 | ui->extrasListWidget->setEnabled(info.enabled); 127 | ui->action_Add_Extra->setEnabled(info.enabled); 128 | ui->action_Remove_Extra->setEnabled(info.enabled); 129 | ui->actionWhats_this->setEnabled(info.enabled); 130 | ui->extrasLabel->setText(info.title + tr(":")); 131 | ui->extrasLabel->setWhatsThis(info.hint); 132 | ui->extrasListWidget->setWhatsThis(info.hint); 133 | helper->deleteLater(); 134 | } else { 135 | ui->matchDirectoriesCheckBox->setEnabled(true); 136 | ui->extrasLabel->setEnabled(false); 137 | ui->extrasListWidget->setEnabled(false); 138 | ui->action_Add_Extra->setEnabled(false); 139 | ui->action_Remove_Extra->setEnabled(false); 140 | ui->actionWhats_this->setEnabled(false); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /gui/conflip.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /lib/synchelper.cpp: -------------------------------------------------------------------------------- 1 | #include "synchelper.h" 2 | #include 3 | #include 4 | 5 | namespace { 6 | 7 | class SyncErrorException {}; 8 | 9 | } 10 | 11 | SyncHelper::SyncHelper(QObject *parent) : 12 | QObject(parent) 13 | {} 14 | 15 | QString SyncHelper::syncPrefix(const QString &mode) const 16 | { 17 | return mode; 18 | } 19 | 20 | QString SyncHelper::toSyncPath(const QString &mode, const QDir &syncDir, const QString &path) const 21 | { 22 | auto basePath = QDir::cleanPath(QFileInfo{path}.absoluteFilePath()); 23 | if(basePath.startsWith(QDir::homePath())) 24 | basePath = syncPrefix(mode) + QStringLiteral("/user/") + QDir::home().relativeFilePath(basePath); 25 | else 26 | basePath = syncPrefix(mode) + QStringLiteral("/system/") + basePath; 27 | return QDir::cleanPath(syncDir.absoluteFilePath(basePath)); 28 | } 29 | 30 | QString SyncHelper::toSrcPath(const QString &mode, const QDir &syncDir, const QString &path) const 31 | { 32 | const auto userPrefix = syncDir.absoluteFilePath(syncPrefix(mode) + QStringLiteral("/user/")); 33 | const auto systemPrefix = syncDir.absoluteFilePath(syncPrefix(mode) + QStringLiteral("/system/")); 34 | auto basePath = QDir::cleanPath(QFileInfo{path}.absoluteFilePath()); 35 | if(path.startsWith(userPrefix)) 36 | basePath = QDir{userPrefix}.relativeFilePath(basePath); 37 | else if(path.startsWith(systemPrefix)) 38 | basePath = QDir{systemPrefix}.relativeFilePath(basePath); 39 | else { 40 | qWarning() << "Tried to convert path" << path << "from sync to src path, but it is not a synced path"; 41 | return {}; 42 | } 43 | return QDir::cleanPath(QDir::home().absoluteFilePath(basePath)); 44 | } 45 | 46 | SyncHelper::ExtrasHint SyncHelper::extrasHint() const 47 | { 48 | return {false, tr("&Extras"), {}}; 49 | } 50 | 51 | 52 | 53 | SyncTask::SyncTask(const SyncHelper *helper, QString mode, const QDir &syncDir, QString path, QStringList extras, bool isFirstUse, QObject *parent) : 54 | QObject{parent}, 55 | helper{helper}, 56 | mode{std::move(mode)}, 57 | syncDir{syncDir}, 58 | path{std::move(path)}, 59 | extras{std::move(extras)}, 60 | isFirstUse{isFirstUse}, 61 | _isRemove{false} 62 | { 63 | setAutoDelete(false); 64 | } 65 | 66 | SyncTask::SyncTask(const SyncHelper *helper, QString mode, const QDir &syncDir, QString path, QObject *parent) : 67 | QObject{parent}, 68 | helper{helper}, 69 | mode{std::move(mode)}, 70 | syncDir{syncDir}, 71 | path{std::move(path)}, 72 | _isRemove{true} 73 | { 74 | setAutoDelete(false); 75 | } 76 | 77 | void SyncTask::run() 78 | { 79 | try { 80 | if(_isRemove) { 81 | undoSync(); 82 | _result = Removed; 83 | } else { 84 | performSync(); 85 | _result = Synced; 86 | } 87 | } catch(NotASymlinkException &) { 88 | _result = NotASymlink; 89 | } catch(SyncErrorException &) { 90 | _result = Error; 91 | } 92 | 93 | Q_ASSERT(_result != Invalid); 94 | QMetaObject::invokeMethod(this, "reportResult", Qt::QueuedConnection); 95 | } 96 | 97 | void SyncTask::undoSync() 98 | { 99 | QFileInfo targetInfo{helper->toSyncPath(mode, syncDir, path)}; 100 | QFile tFile(targetInfo.absoluteFilePath()); 101 | if(tFile.exists()) { 102 | if(!tFile.remove()) 103 | fatal("Failed to remove synced file"); 104 | info() << "Removed file from synchronisation"; 105 | } 106 | } 107 | 108 | QFileInfo SyncTask::srcPath() const 109 | { 110 | QFileInfo srcInfo{path}; 111 | srcInfo.setCaching(false); 112 | if(!srcInfo.dir().exists()) { 113 | if(!srcInfo.dir().mkpath(QStringLiteral("."))) 114 | fatal("Failed to create source directory"); 115 | } 116 | return srcInfo; 117 | } 118 | 119 | QFileInfo SyncTask::syncPath() const 120 | { 121 | QFileInfo syncInfo{helper->toSyncPath(mode, syncDir, path)}; 122 | syncInfo.setCaching(false); 123 | if(!syncInfo.dir().exists()) { 124 | if(!syncInfo.dir().mkpath(QStringLiteral("."))) 125 | fatal("Failed to create sync directory"); 126 | } 127 | return syncInfo; 128 | } 129 | 130 | QDebug SyncTask::debug(const QString &extra) const 131 | { 132 | _log.append({QtDebugMsg, {}}); 133 | QDebug dbg{&(_log.last().second)}; 134 | logBase(dbg, extra); 135 | return dbg; 136 | } 137 | 138 | QDebug SyncTask::info(const QString &extra) const 139 | { 140 | _log.append({QtInfoMsg, {}}); 141 | QDebug dbg{&(_log.last().second)}; 142 | logBase(dbg, extra); 143 | return dbg; 144 | } 145 | 146 | QDebug SyncTask::warning(const QString &extra) const 147 | { 148 | _log.append({QtWarningMsg, {}}); 149 | QDebug dbg{&(_log.last().second)}; 150 | logBase(dbg, extra); 151 | return dbg; 152 | } 153 | 154 | QDebug SyncTask::critical(const QString &extra) const 155 | { 156 | _log.append({QtCriticalMsg, {}}); 157 | QDebug dbg{&(_log.last().second)}; 158 | logBase(dbg, extra); 159 | return dbg; 160 | } 161 | 162 | void SyncTask::fatal(const QString &message, const QString &extra) const 163 | { 164 | fatal(message.toUtf8(), extra); 165 | } 166 | 167 | void SyncTask::fatal(const QByteArray &message, const QString &extra) const 168 | { 169 | fatal(message.constData(), extra); 170 | } 171 | 172 | void SyncTask::fatal(const char *message, const QString &extra) const 173 | { 174 | critical(extra) << message; 175 | throw SyncErrorException{}; 176 | } 177 | 178 | void SyncTask::reportResult() 179 | { 180 | if(!_log.isEmpty()) { 181 | auto cat = QStringLiteral("%1-SYNC").arg(mode.toUpper()).toUtf8(); 182 | QMessageLogger logger{nullptr, 0, nullptr, cat.constData()}; 183 | for(const auto &msg : qAsConst(_log)) { 184 | switch(msg.first) { 185 | case QtDebugMsg: 186 | logger.debug("%s", msg.second.toUtf8().constData()); 187 | break; 188 | case QtInfoMsg: 189 | logger.info("%s", msg.second.toUtf8().constData()); 190 | break; 191 | case QtWarningMsg: 192 | logger.warning("%s", msg.second.toUtf8().constData()); 193 | break; 194 | case QtCriticalMsg: 195 | logger.critical("%s", msg.second.toUtf8().constData()); 196 | break; 197 | default: 198 | Q_UNREACHABLE(); 199 | break; 200 | } 201 | } 202 | } 203 | 204 | emit syncDone(this, _result); 205 | } 206 | 207 | void SyncTask::logBase(QDebug &dbg, const QString &extra) const 208 | { 209 | QDebugStateSaver state{dbg}; 210 | dbg.nospace().noquote() << path; 211 | if(!extra.isEmpty()) 212 | dbg << " [" << extra << "]"; 213 | dbg << " =>"; 214 | } 215 | -------------------------------------------------------------------------------- /test/backup/repo/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "entries": [ 3 | { 4 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/files/symlink_file.txt", 5 | "mode": "symlink", 6 | "extras": [], 7 | "includeHidden": true, 8 | "caseSensitive": true, 9 | "matchDirs": true 10 | }, 11 | { 12 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/files/copy_file.txt", 13 | "mode": "copy", 14 | "extras": [], 15 | "includeHidden": true, 16 | "caseSensitive": true, 17 | "matchDirs": true 18 | }, 19 | 20 | { 21 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/files/symlink_file_sensitive.txt", 22 | "mode": "symlink", 23 | "extras": [], 24 | "includeHidden": true, 25 | "caseSensitive": false, 26 | "matchDirs": true 27 | }, 28 | { 29 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/files/copy_file_sensitive.txt", 30 | "mode": "copy", 31 | "extras": [], 32 | "includeHidden": true, 33 | "caseSensitive": false, 34 | "matchDirs": true 35 | }, 36 | 37 | { 38 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/files/symlink_pattern_*.txt", 39 | "mode": "symlink", 40 | "extras": [], 41 | "includeHidden": true, 42 | "caseSensitive": true, 43 | "matchDirs": true 44 | }, 45 | { 46 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/files/copy_pattern_*.txt", 47 | "mode": "copy", 48 | "extras": [], 49 | "includeHidden": true, 50 | "caseSensitive": true, 51 | "matchDirs": true 52 | }, 53 | 54 | { 55 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/dirs/basic_symlink", 56 | "mode": "symlink", 57 | "extras": [], 58 | "includeHidden": true, 59 | "caseSensitive": true, 60 | "matchDirs": true 61 | }, 62 | 63 | { 64 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/dirs/hidden_symlink/*", 65 | "mode": "symlink", 66 | "extras": [], 67 | "includeHidden": false, 68 | "caseSensitive": true, 69 | "matchDirs": true 70 | }, 71 | { 72 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/dirs/hidden_copy/*", 73 | "mode": "copy", 74 | "extras": [], 75 | "includeHidden": false, 76 | "caseSensitive": true, 77 | "matchDirs": true 78 | }, 79 | 80 | { 81 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/dirs/pattern_symlink/*/subdir/*.txt", 82 | "mode": "symlink", 83 | "extras": [ 84 | "special/subdir", 85 | ".*\\.x\\.txt$" 86 | ], 87 | "includeHidden": true, 88 | "caseSensitive": true, 89 | "matchDirs": true 90 | }, 91 | { 92 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/dirs/pattern_copy/*/subdir/*.txt", 93 | "mode": "copy", 94 | "extras": [ 95 | "subdir/file2.txt", 96 | ".*\\.x\\.txt$" 97 | ], 98 | "includeHidden": true, 99 | "caseSensitive": true, 100 | "matchDirs": true 101 | }, 102 | 103 | { 104 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/dirs/recurse_symlink/**/*.txt", 105 | "mode": "symlink", 106 | "extras": [ 107 | "exclude_absolute", 108 | "exclude_pattern_\\d+" 109 | ], 110 | "includeHidden": true, 111 | "caseSensitive": true, 112 | "matchDirs": true 113 | }, 114 | { 115 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/dirs/recurse_copy/**/*.txt", 116 | "mode": "copy", 117 | "extras": [ 118 | "exclude_absolute", 119 | "exclude_pattern_\\d+" 120 | ], 121 | "includeHidden": true, 122 | "caseSensitive": true, 123 | "matchDirs": true 124 | }, 125 | 126 | { 127 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/dirs/match_dirs/dirs_*", 128 | "mode": "symlink", 129 | "extras": [], 130 | "includeHidden": true, 131 | "caseSensitive": true, 132 | "matchDirs": true 133 | }, 134 | { 135 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/dirs/match_dirs/nodirs_*", 136 | "mode": "symlink", 137 | "extras": [], 138 | "includeHidden": true, 139 | "caseSensitive": true, 140 | "matchDirs": false 141 | }, 142 | 143 | 144 | 145 | { 146 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/ini/test.ini", 147 | "mode": "ini", 148 | "extras": [ 149 | "general\\key", 150 | "general\\users", 151 | "advanced", 152 | "!advanced\\is\\a", 153 | "advanced\\is\\a\\number", 154 | "!advanced\\is\\a\\number\\child", 155 | "nogroup" 156 | ], 157 | "includeHidden": true, 158 | "caseSensitive": true, 159 | "matchDirs": false 160 | }, 161 | 162 | 163 | 164 | { 165 | "pathPattern": "/org/gnome/feedreader/", 166 | "mode": "dconf", 167 | "extras": [ 168 | "article", 169 | "content-grabber", 170 | "drop-articles-after", 171 | "feedlist", 172 | "max-articles", 173 | "mediaplayer", 174 | "only-feeds", 175 | "sync", 176 | "tweaks" 177 | ], 178 | "includeHidden": true, 179 | "caseSensitive": true, 180 | "matchDirs": false 181 | }, 182 | 183 | 184 | 185 | { 186 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/xml/test.xml", 187 | "mode": "xml", 188 | "extras": [ 189 | "simple/~", 190 | "attribs/=", 191 | "attrib/=baum", 192 | "mixed/#", 193 | "unmixed/#", 194 | "all", 195 | "sub/element", 196 | "sub/group/" 197 | ], 198 | "includeHidden": true, 199 | "caseSensitive": true, 200 | "matchDirs": false 201 | }, 202 | 203 | 204 | 205 | { 206 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/json/object.json", 207 | "mode": "json", 208 | "extras": [ 209 | "key", 210 | "group/subKey", 211 | "group/list", 212 | "anotherGroup" 213 | ], 214 | "includeHidden": true, 215 | "caseSensitive": true, 216 | "matchDirs": false 217 | }, 218 | { 219 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/json/array.json", 220 | "mode": "json", 221 | "extras": [ 222 | "key", 223 | "group/subKey", 224 | "group/list", 225 | "anotherGroup" 226 | ], 227 | "includeHidden": true, 228 | "caseSensitive": true, 229 | "matchDirs": false 230 | }, 231 | { 232 | "pathPattern": "/home/sky/Programming/QtProjects/Conflip/test/json/object.qbjs", 233 | "mode": "qbjs", 234 | "extras": [ 235 | "key", 236 | "group/subKey", 237 | "group/list", 238 | "anotherGroup" 239 | ], 240 | "includeHidden": true, 241 | "caseSensitive": true, 242 | "matchDirs": false 243 | } 244 | ] 245 | } 246 | -------------------------------------------------------------------------------- /gui/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 600 10 | 450 11 | 12 | 13 | 14 | Manage Paths 15 | 16 | 17 | Qt::ToolButtonFollowStyle 18 | 19 | 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | QPathEdit::JoinedButton 28 | 29 | 30 | QPathEdit::ExistingFolder 31 | 32 | 33 | QFileDialog::ShowDirsOnly 34 | 35 | 36 | true 37 | 38 | 39 | Select a directory to synchronize to 40 | 41 | 42 | 43 | 44 | 45 | 46 | Qt::ActionsContextMenu 47 | 48 | 49 | true 50 | 51 | 52 | Qt::ElideMiddle 53 | 54 | 55 | false 56 | 57 | 58 | false 59 | 60 | 61 | true 62 | 63 | 64 | true 65 | 66 | 67 | false 68 | 69 | 70 | false 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 0 80 | 0 81 | 600 82 | 23 83 | 84 | 85 | 86 | 87 | &File 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | &Edit 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | &Help 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | Toolbar 118 | 119 | 120 | TopToolBarArea 121 | 122 | 123 | false 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | false 134 | 135 | 136 | 137 | .. 138 | 139 | 140 | &Add Entry 141 | 142 | 143 | Add Entry 144 | 145 | 146 | Ctrl+Ins 147 | 148 | 149 | 150 | 151 | false 152 | 153 | 154 | 155 | .. 156 | 157 | 158 | Re&load Daemon 159 | 160 | 161 | Ctrl+R 162 | 163 | 164 | 165 | 166 | 167 | .. 168 | 169 | 170 | E&xit 171 | 172 | 173 | Alt+F4 174 | 175 | 176 | 177 | 178 | 179 | .. 180 | 181 | 182 | &About 183 | 184 | 185 | 186 | 187 | 188 | .. 189 | 190 | 191 | About &Qt 192 | 193 | 194 | 195 | 196 | false 197 | 198 | 199 | 200 | .. 201 | 202 | 203 | &Remove Entry 204 | 205 | 206 | Remove Entry 207 | 208 | 209 | Del 210 | 211 | 212 | 213 | 214 | false 215 | 216 | 217 | 218 | .. 219 | 220 | 221 | &Edit Entry 222 | 223 | 224 | 225 | 226 | 227 | .. 228 | 229 | 230 | Paste Path 231 | 232 | 233 | Ctrl+V 234 | 235 | 236 | 237 | 238 | 239 | 240 | QPathEdit 241 | QWidget 242 |
qpathedit.h
243 |
244 |
245 | 246 | 247 |
248 | -------------------------------------------------------------------------------- /daemon/syncengine.cpp: -------------------------------------------------------------------------------- 1 | #include "syncengine.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std::chrono; 10 | 11 | SyncEngine::SyncEngine(QObject *parent) : 12 | QObject{parent}, 13 | _timer{new QTimer{this}}, 14 | _watcher{new QFileSystemWatcher{this}}, 15 | _serializer{new QJsonSerializer{this}}, 16 | _resolver{new PathResolver{this}}, 17 | _threadPool{new QThreadPool{this}} 18 | { 19 | if(!Settings::instance()->engine.machineid.isSet()) 20 | Settings::instance()->engine.machineid = QUuid::createUuid(); 21 | 22 | connect(_timer, &QTimer::timeout, 23 | this, &SyncEngine::triggerSync); 24 | connect(_watcher, &QFileSystemWatcher::fileChanged, 25 | this, &SyncEngine::triggerSync); 26 | } 27 | 28 | bool SyncEngine::start() 29 | { 30 | _workingDir = QDir::cleanPath(Settings::instance()->engine.dir); 31 | _workingDir.makeAbsolute(); 32 | if(!Conflip::initConfDir()) 33 | return false; 34 | 35 | _resolver->setSyncDir(_workingDir); 36 | _timer->setInterval(minutes(Settings::instance()->engine.interval)); 37 | resume(); 38 | return true; 39 | } 40 | 41 | void SyncEngine::pause() 42 | { 43 | _timer->stop(); 44 | _threadPool->clear(); 45 | _threadPool->waitForDone(); 46 | } 47 | 48 | void SyncEngine::resume() 49 | { 50 | _timer->start(); 51 | QMetaObject::invokeMethod(this, "triggerSync", Qt::QueuedConnection); 52 | } 53 | 54 | void SyncEngine::reload() 55 | { 56 | Settings::instance()->accessor()->sync(); 57 | _workingDir = QDir::cleanPath(Settings::instance()->engine.dir); 58 | _workingDir.makeAbsolute(); 59 | if(Conflip::initConfDir()) { 60 | _resolver->setSyncDir(_workingDir); 61 | _timer->setInterval(minutes(Settings::instance()->engine.interval)); 62 | pause(); 63 | resume(); 64 | } 65 | } 66 | 67 | void SyncEngine::triggerSync() 68 | { 69 | if(_skipNextUpdate) { 70 | _skipNextUpdate = false; 71 | return; 72 | } 73 | 74 | QFile readFile(_workingDir.absoluteFilePath(Conflip::ConfigFileName())); 75 | if(!readFile.exists()) 76 | return; 77 | 78 | // prepare or update the watcher 79 | auto files = _watcher->files(); 80 | if(!files.contains(readFile.fileName())) { 81 | if(!files.isEmpty()) 82 | _watcher->removePaths(files); 83 | _watcher->addPath(readFile.fileName()); 84 | } 85 | 86 | if(!readFile.open(QIODevice::ReadOnly | QIODevice::Text)) { 87 | qCritical() << "Failed to open file" << readFile.fileName() 88 | << "with error:" << qUtf8Printable(readFile.errorString()); 89 | return; 90 | } 91 | 92 | try { 93 | _currentDb = _serializer->deserializeFrom(&readFile); 94 | readFile.close(); 95 | _dbChanged = false; 96 | _hasErrors = false; 97 | syncEntries(_currentDb.entries); 98 | removeUnsynced(_currentDb.unsynced); 99 | } catch (QJsonSerializerException &e) { 100 | qCritical() << "Failed to parse" << readFile.fileName() 101 | << "with error:\n" << e.what(); 102 | return; 103 | } 104 | } 105 | 106 | void SyncEngine::syncDone(SyncTask *task, SyncTask::Result result) 107 | { 108 | auto entry = _activeTasks.take(task); 109 | if(!entry) 110 | return; 111 | 112 | switch(result) { 113 | case SyncTask::NotASymlink: 114 | entry->mode = QStringLiteral("copy"); 115 | _dbChanged = true; 116 | Q_FALLTHROUGH(); //TODO check if ok so 117 | case SyncTask::Synced: 118 | // if sync successful and not first used yet -> mark first used 119 | if(!entry->syncedMachines.contains(Settings::instance()->engine.machineid)) { 120 | entry->syncedMachines.append(Settings::instance()->engine.machineid); 121 | _dbChanged = true; 122 | } 123 | break; 124 | case SyncTask::Removed: 125 | entry->syncedMachines.removeAll(Settings::instance()->engine.machineid); 126 | if(entry->syncedMachines.isEmpty()) { 127 | if(!_currentDb.unsynced.removeOne(*entry)) { 128 | qWarning().noquote() << "Failed to remove unsynced sync entry from database. Sync entry path: " 129 | << entry->pathPattern; 130 | } 131 | entry = nullptr; 132 | } 133 | _dbChanged = true; 134 | break; 135 | case SyncTask::Error: 136 | _hasErrors = true; 137 | break; 138 | default: 139 | Q_UNREACHABLE(); 140 | break; 141 | } 142 | 143 | if(_activeTasks.isEmpty()) 144 | completeSync(); 145 | task->deleteLater(); 146 | } 147 | 148 | SyncHelper *SyncEngine::getHelper(const QString &type) 149 | { 150 | auto helper = _helpers.value(type); 151 | if(!helper) { 152 | try { 153 | helper = Conflip::loadHelper(type, this); 154 | if(!helper) { 155 | qCritical() << "No plugin found to load helper of type" << type; 156 | return nullptr; 157 | } 158 | _helpers.insert(type, helper); 159 | } catch(QException &e) { 160 | qCritical() << "Failed to load plugin for type" << type 161 | << "with error:" << e.what(); 162 | return nullptr; 163 | } 164 | } 165 | return helper; 166 | } 167 | 168 | void SyncEngine::syncEntries(QList &entries) 169 | { 170 | for(auto &entry : entries) { 171 | auto helper = getHelper(entry.mode); 172 | Q_ASSERT_X(helper, Q_FUNC_INFO, "No helper defined for entry mode"); 173 | 174 | //fixup matchDirs if set but not supported by helper 175 | if(entry.matchDirs && !helper->canSyncDirs(entry.mode)) { 176 | entry.matchDirs = false; 177 | _dbChanged = true; 178 | } 179 | 180 | QStringList paths; 181 | if(helper->pathIsPattern(entry.mode)) 182 | paths = _resolver->resolvePath(entry, helper); 183 | else 184 | paths = QStringList { entry.pathPattern }; 185 | for(const auto& path : qAsConst(paths)) { 186 | auto task = helper->createSyncTask(entry.mode, 187 | _workingDir, 188 | path, 189 | entry.extras, 190 | !entry.syncedMachines.contains(Settings::instance()->engine.machineid), 191 | this); 192 | connect(task, &SyncTask::syncDone, 193 | this, &SyncEngine::syncDone); 194 | _activeTasks.insert(task, &entry); 195 | _threadPool->start(task); 196 | //TODO check for path conflicts 197 | } 198 | } 199 | } 200 | 201 | void SyncEngine::removeUnsynced(QList &entries) 202 | { 203 | for(auto &entry : entries) { 204 | if(!entry.syncedMachines.contains(Settings::instance()->engine.machineid)) 205 | continue; 206 | 207 | auto helper = getHelper(entry.mode); 208 | Q_ASSERT_X(helper, Q_FUNC_INFO, "No helper defined for entry mode"); 209 | 210 | QStringList paths; 211 | if(helper->pathIsPattern(entry.mode)) 212 | paths = _resolver->resolvePath(entry, helper); 213 | else 214 | paths = QStringList { entry.pathPattern }; 215 | for(const auto& path : qAsConst(paths)) { 216 | auto task = helper->createUndoSyncTask(entry.mode, _workingDir, path, this); 217 | connect(task, &SyncTask::syncDone, 218 | this, &SyncEngine::syncDone); 219 | _activeTasks.insert(task, &entry); 220 | _threadPool->start(task); 221 | //TODO check for path conflicts 222 | } 223 | } 224 | } 225 | 226 | void SyncEngine::completeSync() 227 | { 228 | auto &machineError = _currentDb.hasErrors[Settings::instance()->engine.machineid.get().toString(QUuid::WithoutBraces)]; 229 | if(_hasErrors != machineError) { 230 | machineError = _hasErrors; 231 | _dbChanged = true; 232 | } 233 | 234 | if(_dbChanged) { 235 | QSaveFile writeFile(_workingDir.absoluteFilePath(Conflip::ConfigFileName())); 236 | if(!writeFile.open(QIODevice::WriteOnly | QIODevice::Text)) { 237 | qCritical() << "Failed to update file" << writeFile.fileName() 238 | << "with error:" << qUtf8Printable(writeFile.errorString()); 239 | } 240 | 241 | try { 242 | _serializer->serializeTo(&writeFile, _currentDb); 243 | _skipNextUpdate = true; 244 | if(!writeFile.commit()) { 245 | _skipNextUpdate = false; 246 | qCritical() << "Failed to update file" << writeFile.fileName() 247 | << "with error:" << qUtf8Printable(writeFile.errorString()); 248 | } 249 | } catch (QJsonSerializerException &e) { 250 | writeFile.cancelWriting(); 251 | qCritical() << "Failed to serialize data to" << writeFile.fileName() 252 | << "with error:\n" << e.what(); 253 | return; 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /gui/createentrydialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CreateEntryDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 500 10 | 400 11 | 12 | 13 | 14 | Edit Sync Entry 15 | 16 | 17 | 18 | 19 | 20 | &Path: 21 | 22 | 23 | pathLineEdit 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Enter a path or path pattern 33 | 34 | 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | QToolButton::MenuButtonPopup 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | &Mode: 52 | 53 | 54 | modeComboBox 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | &Hidden Files: 65 | 66 | 67 | hiddenFilesCheckBox 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | 78 | 79 | 80 | 81 | &Case Sensitive: 82 | 83 | 84 | caseSensitiveCheckBox 85 | 86 | 87 | 88 | 89 | 90 | 91 | true 92 | 93 | 94 | 95 | 96 | 97 | 98 | Qt::ActionsContextMenu 99 | 100 | 101 | true 102 | 103 | 104 | true 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Qt::ToolButtonFollowStyle 114 | 115 | 116 | 117 | 118 | 119 | 120 | Qt::ToolButtonFollowStyle 121 | 122 | 123 | 124 | 125 | 126 | 127 | Qt::Horizontal 128 | 129 | 130 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | Match &Direcoties: 140 | 141 | 142 | matchDirectoriesCheckBox 143 | 144 | 145 | 146 | 147 | 148 | 149 | true 150 | 151 | 152 | 153 | 154 | 155 | 156 | &Extras: 157 | 158 | 159 | extrasListWidget 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | Qt::Horizontal 169 | 170 | 171 | 172 | 40 173 | 20 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | .. 188 | 189 | 190 | &Add Extra 191 | 192 | 193 | Ctrl+Ins 194 | 195 | 196 | true 197 | 198 | 199 | 200 | 201 | 202 | .. 203 | 204 | 205 | &Remove Extra 206 | 207 | 208 | Del 209 | 210 | 211 | true 212 | 213 | 214 | 215 | 216 | 217 | .. 218 | 219 | 220 | Select &File 221 | 222 | 223 | 224 | 225 | 226 | .. 227 | 228 | 229 | Select &Directory 230 | 231 | 232 | 233 | 234 | 235 | .. 236 | 237 | 238 | Whats this? 239 | 240 | 241 | F1 242 | 243 | 244 | true 245 | 246 | 247 | 248 | 249 | 250 | 251 | buttonBox 252 | accepted() 253 | CreateEntryDialog 254 | accept() 255 | 256 | 257 | 248 258 | 254 259 | 260 | 261 | 157 262 | 274 263 | 264 | 265 | 266 | 267 | buttonBox 268 | rejected() 269 | CreateEntryDialog 270 | reject() 271 | 272 | 273 | 316 274 | 260 275 | 276 | 277 | 286 278 | 274 279 | 280 | 281 | 282 | 283 | 284 | --------------------------------------------------------------------------------