pax_global_header00006660000000000000000000000064152347724210014521gustar00rootroot0000000000000052 comment=47839a844053dcab0b5997f4e01d843ae465f9b0 linuxdeepin-util-dfm-a4307d3/000077500000000000000000000000001523477242100161135ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/.github/000077500000000000000000000000001523477242100174535ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/.github/workflows/000077500000000000000000000000001523477242100215105ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/.github/workflows/backup-to-gitlab.yml000066400000000000000000000005371523477242100253650ustar00rootroot00000000000000name: backup to gitlab on: [push] concurrency: group: ${{ github.workflow }} cancel-in-progress: true jobs: backup-to-gitlabwh: uses: linuxdeepin/.github/.github/workflows/backup-to-gitlabwh.yml@master secrets: inherit backup-to-gitee: uses: linuxdeepin/.github/.github/workflows/backup-to-gitee.yml@master secrets: inherit linuxdeepin-util-dfm-a4307d3/.github/workflows/call-api-check.yml000066400000000000000000000004351523477242100247720ustar00rootroot00000000000000name: apiCheck on: pull_request_target: types: [opened, synchronize, reopened] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: api-check: uses: linuxdeepin/.github/.github/workflows/api-check.yml@master secrets: inheritlinuxdeepin-util-dfm-a4307d3/.github/workflows/call-auto-tag.yml000066400000000000000000000005171523477242100246700ustar00rootroot00000000000000name: auto tag on: pull_request_target: types: [opened, synchronize, closed] paths: - "debian/changelog" concurrency: group: ${{ github.workflow }}-pull/${{ github.event.number }} cancel-in-progress: true jobs: auto_tag: uses: linuxdeepin/.github/.github/workflows/auto-tag.yml@master secrets: inherit linuxdeepin-util-dfm-a4307d3/.github/workflows/call-build-distribution.yml000066400000000000000000000004321523477242100267570ustar00rootroot00000000000000name: Call build-distribution on: push: paths-ignore: - ".github/workflows/**" pull_request_target: paths-ignore: - ".github/workflows/**" jobs: check_job: uses: linuxdeepin/.github/.github/workflows/build-distribution.yml@master secrets: inherit linuxdeepin-util-dfm-a4307d3/.github/workflows/call-chatOps.yml000066400000000000000000000002421523477242100245430ustar00rootroot00000000000000name: chatOps on: issue_comment: types: [created] jobs: chatopt: uses: linuxdeepin/.github/.github/workflows/chatOps.yml@master secrets: inherit linuxdeepin-util-dfm-a4307d3/.github/workflows/call-clacheck.yml000066400000000000000000000005251523477242100247030ustar00rootroot00000000000000name: Call CLA check on: issue_comment: types: [created] pull_request_target: types: [opened, closed, synchronize] concurrency: group: ${{ github.workflow }}-pull/${{ github.event.number }} cancel-in-progress: true jobs: clacheck: uses: linuxdeepin/.github/.github/workflows/cla-check.yml@master secrets: inherit linuxdeepin-util-dfm-a4307d3/.github/workflows/call-commitlint.yml000066400000000000000000000003641523477242100253260ustar00rootroot00000000000000name: Call commitlint on: pull_request_target: concurrency: group: ${{ github.workflow }}-pull/${{ github.event.number }} cancel-in-progress: true jobs: check_job: uses: linuxdeepin/.github/.github/workflows/commitlint.yml@master linuxdeepin-util-dfm-a4307d3/.github/workflows/call-debian-check.yml000066400000000000000000000004461523477242100254450ustar00rootroot00000000000000name: debianCheck on: pull_request_target: types: [opened, synchronize, reopened] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: debian-check: uses: linuxdeepin/.github/.github/workflows/debian-check.yml@master secrets: inheritlinuxdeepin-util-dfm-a4307d3/.github/workflows/call-license-check.yml000066400000000000000000000005521523477242100256430ustar00rootroot00000000000000name: Call License and README Check on: pull_request_target: types: [opened, synchronize, reopened] permissions: pull-requests: write contents: read concurrency: group: ${{ github.workflow }}-pull/${{ github.event.number }} cancel-in-progress: true jobs: license-check: uses: linuxdeepin/.github/.github/workflows/license-check.yml@master linuxdeepin-util-dfm-a4307d3/.github/workflows/call-static-check.yml000066400000000000000000000004461523477242100255120ustar00rootroot00000000000000name: staticCheck on: pull_request_target: types: [opened, synchronize, reopened] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: static-check: uses: linuxdeepin/.github/.github/workflows/static-check.yml@master secrets: inheritlinuxdeepin-util-dfm-a4307d3/.github/workflows/cppcheck.yml000066400000000000000000000014641523477242100240200ustar00rootroot00000000000000name: cppcheck on: pull_request_target: paths-ignore: - ".github/workflows/**" concurrency: group: ${{ github.workflow }}-pull/${{ github.event.number }} cancel-in-progress: true jobs: cppchceck: name: cppcheck runs-on: ubuntu-latest steps: - run: export - uses: actions/checkout@v7 with: ref: refs/heads/${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} allow-unsafe-pr-checkout: true persist-credentials: false - uses: linuxdeepin/action-cppcheck@main with: github_token: ${{ secrets.GITHUB_TOKEN }} repository: ${{ github.repository }} pull_request_id: ${{ github.event.pull_request.number }} allow_approve: false linuxdeepin-util-dfm-a4307d3/.gitignore000066400000000000000000000012661523477242100201100ustar00rootroot00000000000000# Compiled Object files *.slo *.lo *.o *.obj *.moc *.moc.cpp *.orig *.user .*.swp .swp.* CMakeLists.txt.user* .cmake/ # Compiled Dynamic libraries *.so *.dylib # Compiled Static libraries *.lai *.la *.a # Executables *.exe *.out *.app Makefile Makefile.* *.Debug *.Release *.rc debug release build* *.pro.user *.pro.user* *.qm *.autosave .debhelper .vscode obj-x86_64-linux-gnu debian/tmp/* debian/libdfm-io/* debian/libdfm-io-dev/* debian/libdfm-mount/* debian/libdfm-mount-dev/* debian/libdfm-burn/* debian/libdfm-burn-dev/* debian/libdfm-search-dev/* debian/libdfm-search/* debian/*.log debian/*.substvars debian/files # ai CLAUDE.md AGENTS.md .cursor .trellis .claude .agents .codex linuxdeepin-util-dfm-a4307d3/.obs/000077500000000000000000000000001523477242100167545ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/.obs/workflows.yml000066400000000000000000000017321523477242100215370ustar00rootroot00000000000000test_build: steps: - link_package: source_project: deepin:Develop:dde source_package: %{SCM_REPOSITORY_NAME} target_project: deepin:CI - configure_repositories: project: deepin:CI repositories: - name: deepin_develop paths: - target_project: deepin:CI target_repository: deepin_develop architectures: - x86_64 - aarch64 - name: debian paths: - target_project: deepin:CI target_repository: debian_sid architectures: - x86_64 filters: event: pull_request tag_build: steps: - trigger_services: project: deepin:Unstable:dde package: %{SCM_REPOSITORY_NAME} filters: event: tag_push commit_build: steps: - trigger_services: project: deepin:Develop:dde package: %{SCM_REPOSITORY_NAME} filters: event: push linuxdeepin-util-dfm-a4307d3/.reuse/000077500000000000000000000000001523477242100173145ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/.reuse/dep5000066400000000000000000000025761523477242100201060ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name:util-dfm Upstream-Contact: UnionTech Software Technology Co., Ltd. <> Source: https://github.com/linuxdeepin/util-dfm # ignore files Files: .gitignore Copyright: None License: CC0-1.0 # github files Files: .github/workflows/*.yml Copyright: None License: CC0-1.0 # ci Files: .obs/workflows.yml Copyright: None License: CC0-1.0 # debian Files: debian/* Copyright: None License: CC0-1.0 # Project files Files: *CMakeLists.txt *.pc.in *.pri *.cmake Copyright: None License: CC0-1.0 # README & DOCS Files: *README.md *README.zh_CN.md CHANGELOG.md docs/* Copyright: UnionTech Software Technology Co., Ltd. License: CC-BY-4.0 # pc/cmake.in Files: misc/dfm-*/dfm-*Config.cmake.in Copyright: UnionTech Software Technology Co., Ltd. License: GPL-3.0-or-later # udfclient Files: src/dfm-burn/3rdparty/udfclient/* Copyright: Reinoud Zandijk License: ClArtistic # cpp-stub (MIT) Files: 3rdparty/testutils/cpp-stub/stub.h 3rdparty/testutils/cpp-stub/addr_any.h 3rdparty/testutils/cpp-stub/addr_pri.h Copyright: jobczz License: MIT # ELFIO (MIT) Files: 3rdparty/testutils/cpp-stub/elfio.hpp Copyright: Sergei Tikhomirov License: MIT # semantic rules Files: src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/*.json Copyright: 2026 UnionTech Software Technology Co., Ltd. License: GPL-3.0-or-later linuxdeepin-util-dfm-a4307d3/3rdparty/000077500000000000000000000000001523477242100176635ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/3rdparty/.gitkeep000066400000000000000000000000001523477242100213020ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/000077500000000000000000000000001523477242100217235ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/cpp-stub/000077500000000000000000000000001523477242100234605ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/cpp-stub/addr_any.h000066400000000000000000000211341523477242100254130ustar00rootroot00000000000000#ifndef __ADDR_ANY_H__ #define __ADDR_ANY_H__ //linux #include #include //c #include #include #include //c++ #include #include //project #include "elfio.hpp" class AddrAny { public: AddrAny() { m_init = get_exe_pathname(m_fullname); m_baseaddr = 0; } AddrAny(std::string libname) { m_init = get_lib_pathname_and_baseaddr(libname, m_fullname, m_baseaddr); } int get_local_func_addr_symtab(std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_SYMTAB, STB_LOCAL, func_name_regex_str, result); } int get_global_func_addr_symtab(std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_SYMTAB, STB_GLOBAL, func_name_regex_str, result); } int get_weak_func_addr_symtab(std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_SYMTAB, STB_WEAK, func_name_regex_str, result); } int get_global_func_addr_dynsym( std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_DYNSYM, STB_GLOBAL, func_name_regex_str, result); } int get_weak_func_addr_dynsym(std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_DYNSYM, STB_WEAK, func_name_regex_str, result); } private: bool demangle(std::string& s, std::string& name) { int status; char* pname = abi::__cxa_demangle(s.c_str(), 0, 0, &status); if (status != 0) { switch(status) { case -1: name = "memory allocation error"; break; case -2: name = "invalid name given"; break; case -3: name = "internal error: __cxa_demangle: invalid argument"; break; default: name = "unknown error occured"; break; } return false; } name = pname; free(pname); return true; } bool get_exe_pathname( std::string& name) { char line[512]; FILE *fp; uintptr_t base_addr; char perm[5]; unsigned long offset; int pathname_pos; char *pathname; size_t pathname_len; int match = 0; if(NULL == (fp = fopen("/proc/self/maps", "r"))) { return false; } while(fgets(line, sizeof(line), fp)) { if(sscanf(line, "%" PRIxPTR "-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; if(0 != offset) continue; //get pathname while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) pathname_pos += 1; if(pathname_pos >= (int)(sizeof(line) - 1)) continue; pathname = line + pathname_pos; pathname_len = strlen(pathname); if(0 == pathname_len) continue; if(pathname[pathname_len - 1] == '\n') { pathname[pathname_len - 1] = '\0'; pathname_len -= 1; } if(0 == pathname_len) continue; if('[' == pathname[0]) continue; name = pathname; match = 1; break; } fclose(fp); if(0 == match) { return false; } else { return true; } } bool get_lib_pathname_and_baseaddr(std::string pathname_regex_str, std::string& name, unsigned long& addr) { char line[512]; FILE *fp; uintptr_t base_addr; char perm[5]; unsigned long offset; int pathname_pos; char *pathname; size_t pathname_len; int match; regex_t pathname_regex; regcomp(&pathname_regex, pathname_regex_str.c_str(), 0); if(NULL == (fp = fopen("/proc/self/maps", "r"))) { return false; } while(fgets(line, sizeof(line), fp)) { if(sscanf(line, "%" PRIxPTR "-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; //check permission if(perm[0] != 'r') continue; if(perm[3] != 'p') continue; //do not touch the shared memory //check offset // //We are trying to find ELF header in memory. //It can only be found at the beginning of a mapped memory regions //whose offset is 0. if(0 != offset) continue; //get pathname while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) pathname_pos += 1; if(pathname_pos >= (int)(sizeof(line) - 1)) continue; pathname = line + pathname_pos; pathname_len = strlen(pathname); if(0 == pathname_len) continue; if(pathname[pathname_len - 1] == '\n') { pathname[pathname_len - 1] = '\0'; pathname_len -= 1; } if(0 == pathname_len) continue; if('[' == pathname[0]) continue; //check pathname //if we need to hook this elf? match = 0; if(0 == regexec(&pathname_regex, pathname, 0, NULL, 0)) { match = 1; name = pathname; addr = (unsigned long)base_addr; break; } if(0 == match) continue; } fclose(fp); if(0 == match) { return false; } else { return true; } } int get_func_addr(unsigned int ttype, unsigned int stype, std::string& func_name_regex_str, std::map& result) { // Create an elfio reader ELFIO::elfio reader; int count = 0; regex_t pathname_regex; if(!m_init) { return -1; } regcomp(&pathname_regex, func_name_regex_str.c_str(), 0); // Load ELF data if(!reader.load(m_fullname.c_str())) { return -1; } ELFIO::Elf_Half sec_num = reader.sections.size(); for(int i = 0; i < sec_num; ++i) { ELFIO::section* psec = reader.sections[i]; // Check section type if(psec->get_type() == ttype) { const ELFIO::symbol_section_accessor symbols( reader, psec ); for ( unsigned int j = 0; j < symbols.get_symbols_num(); ++j ) { std::string name; std::string name_mangle; ELFIO::Elf64_Addr value; ELFIO::Elf_Xword size; unsigned char bind; unsigned char type; ELFIO::Elf_Half section_index; unsigned char other; // Read symbol properties symbols.get_symbol( j, name, value, size, bind, type, section_index, other ); if(type == STT_FUNC && bind == stype) { bool ret = demangle(name,name_mangle); if(ret == true) { if (0 == regexec(&pathname_regex, name_mangle.c_str(), 0, NULL, 0)) { result.insert ( std::pair(name_mangle,(void*)(value + m_baseaddr))); count++; } } else { if (0 == regexec(&pathname_regex, name.c_str(), 0, NULL, 0)) { result.insert ( std::pair(name,(void*)(value + m_baseaddr))); count++; } } } } break; } } return count; } private: bool m_init; std::string m_name; std::string m_fullname; unsigned long m_baseaddr; }; #endif linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/cpp-stub/addr_pri.h000066400000000000000000000243641523477242100254260ustar00rootroot00000000000000#ifndef __ADDR_PRI_H__ #define __ADDR_PRI_H__ #include #include //base on C++11 /********************************************************** access private function **********************************************************/ namespace std { template using enable_if_t = typename enable_if::type; template using remove_reference_t = typename remove_reference::type; } // std // Unnamed namespace is used to avoid duplicate symbols if the macros are used namespace { namespace private_access_detail { // @tparam TagType, used to declare different "get" funciton overloads for // different members/statics template struct private_access { // Normal lookup cannot find in-class defined (inline) friend functions. friend PtrType get(TagType) { return PtrValue; } }; } // namespace private_access_detail } // namespace // Used macro naming conventions: // The "namespace" of this macro library is PRIVATE_ACCESS, i.e. all // macro here has this prefix. // All implementation macro, which are not meant to be used directly have the // PRIVATE_ACCESS_DETAIL prefix. // Some macros have the ABCD_IMPL form, which means they contain the // implementation details for the specific ABCD macro. #define PRIVATE_ACCESS_DETAIL_CONCATENATE_IMPL(x, y) x##y #define PRIVATE_ACCESS_DETAIL_CONCATENATE(x, y) \ PRIVATE_ACCESS_DETAIL_CONCATENATE_IMPL(x, y) // @param PtrTypeKind E.g if we have "class A", then it can be "A::*" in case of // members, or it can be "*" in case of statics. #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, \ PtrTypeKind) \ namespace { \ namespace private_access_detail { \ /* Tag type, used to declare different get funcitons for different \ * members \ */ \ struct Tag {}; \ /* Explicit instantiation */ \ template struct private_access; \ /* We can build the PtrType only with two aliases */ \ /* E.g. using PtrType = int(int) *; would be illformed */ \ using PRIVATE_ACCESS_DETAIL_CONCATENATE(Alias_, Tag) = Type; \ using PRIVATE_ACCESS_DETAIL_CONCATENATE(PtrType_, Tag) = \ PRIVATE_ACCESS_DETAIL_CONCATENATE(Alias_, Tag) PtrTypeKind; \ /* Declare the friend function, now it is visible in namespace scope. \ * Note, \ * we could declare it inside the Tag type too, in that case ADL would \ * find \ * the declaration. By choosing to declare it here, the Tag type remains \ * a \ * simple tag type, it has no other responsibilities. */ \ PRIVATE_ACCESS_DETAIL_CONCATENATE(PtrType_, Tag) get(Tag); \ } \ } #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FIELD(Tag, Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, Class::*) \ namespace { \ namespace access_private_field { \ Type &Class##Name(Class &&t) { return t.*get(private_access_detail::Tag{}); } \ Type &Class##Name(Class &t) { return t.*get(private_access_detail::Tag{}); } \ /* The following usings are here to avoid duplicate const qualifier \ * warnings \ */ \ using PRIVATE_ACCESS_DETAIL_CONCATENATE(X, Tag) = Type; \ using PRIVATE_ACCESS_DETAIL_CONCATENATE(Y, Tag) = \ const PRIVATE_ACCESS_DETAIL_CONCATENATE(X, Tag); \ PRIVATE_ACCESS_DETAIL_CONCATENATE(Y, Tag) & Class##Name(const Class &t) {\ return t.*get(private_access_detail::Tag{}); \ } \ } \ } #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FUN(Tag, Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, Class::*) \ namespace { \ namespace call_private_fun { \ /* We do perfect forwarding, but we want to restrict the overload set \ * only for objects which have the type Class. */ \ template , \ Class>::value> * = nullptr, \ typename... Args> \ auto Class##Name(Obj &&o, Args &&... args) -> decltype( \ (std::forward(o).* \ get(private_access_detail::Tag{}))(std::forward(args)...)) { \ return (std::forward(o).*get(private_access_detail::Tag{}))( \ std::forward(args)...); \ } \ } \ namespace get_private_fun { \ auto Class##Name() -> decltype( \ get(private_access_detail::Tag{})) { \ return (get(private_access_detail::Tag{})); \ } \ } \ } #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FIELD(Tag, Class, Type, \ Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, *) \ namespace { \ namespace access_private_static_field { \ namespace Class { \ Type &Class##Name() { return *get(private_access_detail::Tag{}); } \ } \ } \ } #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FUN(Tag, Class, Type, \ Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, *) \ namespace { \ namespace call_private_static_fun { \ namespace Class { \ template \ auto Class##Name(Args &&... args) -> decltype( \ get(private_access_detail::Tag{})(std::forward(args)...)) { \ return get(private_access_detail::Tag{})( \ std::forward(args)...); \ } \ } \ } \ namespace get_private_static_fun { \ namespace Class { \ auto Class##Name() -> decltype(get(private_access_detail::Tag{})) { \ return get(private_access_detail::Tag{}); \ } \ } \ } \ } #define PRIVATE_ACCESS_DETAIL_UNIQUE_TAG \ PRIVATE_ACCESS_DETAIL_CONCATENATE(PrivateAccessTag, __COUNTER__) #define ACCESS_PRIVATE_FIELD(Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FIELD(PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, \ Class, Type, Name) #define ACCESS_PRIVATE_FUN(Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FUN(PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, \ Class, Type, Name) #define ACCESS_PRIVATE_STATIC_FIELD(Class, Type, Name) \ Type Class::Name; \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FIELD( \ PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, Class, Type, Name) #define ACCESS_PRIVATE_STATIC_FUN(Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FUN( \ PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, Class, Type, Name) #endif linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/cpp-stub/elfio.hpp000066400000000000000000004531041523477242100252760ustar00rootroot00000000000000 /*** Start of inlined file: elfio_dump.hpp ***/ #ifndef ELFIO_DUMP_HPP #define ELFIO_DUMP_HPP #include #include #include #include #include /*** Start of inlined file: elfio.hpp ***/ #ifndef ELFIO_HPP #define ELFIO_HPP #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable : 4996 ) #pragma warning( disable : 4355 ) #pragma warning( disable : 4244 ) #endif #include #include #include #include #include #include #include #include /*** Start of inlined file: elf_types.hpp ***/ #ifndef ELFTYPES_H #define ELFTYPES_H #ifndef ELFIO_NO_OWN_TYPES #if !defined( ELFIO_NO_CSTDINT ) && !defined( ELFIO_NO_INTTYPES ) #include #else typedef unsigned char uint8_t; typedef signed char int8_t; typedef unsigned short uint16_t; typedef signed short int16_t; #ifdef _MSC_VER typedef unsigned __int32 uint32_t; typedef signed __int32 int32_t; typedef unsigned __int64 uint64_t; typedef signed __int64 int64_t; #else typedef unsigned int uint32_t; typedef signed int int32_t; typedef unsigned long long uint64_t; typedef signed long long int64_t; #endif // _MSC_VER #endif // ELFIO_NO_CSTDINT #endif // ELFIO_NO_OWN_TYPES namespace ELFIO { // Attention! Platform depended definitions. typedef uint16_t Elf_Half; typedef uint32_t Elf_Word; typedef int32_t Elf_Sword; typedef uint64_t Elf_Xword; typedef int64_t Elf_Sxword; typedef uint32_t Elf32_Addr; typedef uint32_t Elf32_Off; typedef uint64_t Elf64_Addr; typedef uint64_t Elf64_Off; #define Elf32_Half Elf_Half #define Elf64_Half Elf_Half #define Elf32_Word Elf_Word #define Elf64_Word Elf_Word #define Elf32_Sword Elf_Sword #define Elf64_Sword Elf_Sword /////////////////////// // ELF Header Constants // File type #define ET_NONE 0 #define ET_REL 1 #define ET_EXEC 2 #define ET_DYN 3 #define ET_CORE 4 #define ET_LOOS 0xFE00 #define ET_HIOS 0xFEFF #define ET_LOPROC 0xFF00 #define ET_HIPROC 0xFFFF #define EM_NONE 0 // No machine #define EM_M32 1 // AT&T WE 32100 #define EM_SPARC 2 // SUN SPARC #define EM_386 3 // Intel 80386 #define EM_68K 4 // Motorola m68k family #define EM_88K 5 // Motorola m88k family #define EM_486 6 // Intel 80486// Reserved for future use #define EM_860 7 // Intel 80860 #define EM_MIPS 8 // MIPS R3000 (officially, big-endian only) #define EM_S370 9 // IBM System/370 #define EM_MIPS_RS3_LE \ 10 // MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated #define EM_res011 11 // Reserved #define EM_res012 12 // Reserved #define EM_res013 13 // Reserved #define EM_res014 14 // Reserved #define EM_PARISC 15 // HPPA #define EM_res016 16 // Reserved #define EM_VPP550 17 // Fujitsu VPP500 #define EM_SPARC32PLUS 18 // Sun's "v8plus" #define EM_960 19 // Intel 80960 #define EM_PPC 20 // PowerPC #define EM_PPC64 21 // 64-bit PowerPC #define EM_S390 22 // IBM S/390 #define EM_SPU 23 // Sony/Toshiba/IBM SPU #define EM_res024 24 // Reserved #define EM_res025 25 // Reserved #define EM_res026 26 // Reserved #define EM_res027 27 // Reserved #define EM_res028 28 // Reserved #define EM_res029 29 // Reserved #define EM_res030 30 // Reserved #define EM_res031 31 // Reserved #define EM_res032 32 // Reserved #define EM_res033 33 // Reserved #define EM_res034 34 // Reserved #define EM_res035 35 // Reserved #define EM_V800 36 // NEC V800 series #define EM_FR20 37 // Fujitsu FR20 #define EM_RH32 38 // TRW RH32 #define EM_MCORE 39 // Motorola M*Core // May also be taken by Fujitsu MMA #define EM_RCE 39 // Old name for MCore #define EM_ARM 40 // ARM #define EM_OLD_ALPHA 41 // Digital Alpha #define EM_SH 42 // Renesas (formerly Hitachi) / SuperH SH #define EM_SPARCV9 43 // SPARC v9 64-bit #define EM_TRICORE 44 // Siemens Tricore embedded processor #define EM_ARC 45 // ARC Cores #define EM_H8_300 46 // Renesas (formerly Hitachi) H8/300 #define EM_H8_300H 47 // Renesas (formerly Hitachi) H8/300H #define EM_H8S 48 // Renesas (formerly Hitachi) H8S #define EM_H8_500 49 // Renesas (formerly Hitachi) H8/500 #define EM_IA_64 50 // Intel IA-64 Processor #define EM_MIPS_X 51 // Stanford MIPS-X #define EM_COLDFIRE 52 // Motorola Coldfire #define EM_68HC12 53 // Motorola M68HC12 #define EM_MMA 54 // Fujitsu Multimedia Accelerator #define EM_PCP 55 // Siemens PCP #define EM_NCPU 56 // Sony nCPU embedded RISC processor #define EM_NDR1 57 // Denso NDR1 microprocesspr #define EM_STARCORE 58 // Motorola Star*Core processor #define EM_ME16 59 // Toyota ME16 processor #define EM_ST100 60 // STMicroelectronics ST100 processor #define EM_TINYJ 61 // Advanced Logic Corp. TinyJ embedded processor #define EM_X86_64 62 // Advanced Micro Devices X86-64 processor #define EM_PDSP 63 // Sony DSP Processor #define EM_PDP10 64 // Digital Equipment Corp. PDP-10 #define EM_PDP11 65 // Digital Equipment Corp. PDP-11 #define EM_FX66 66 // Siemens FX66 microcontroller #define EM_ST9PLUS 67 // STMicroelectronics ST9+ 8/16 bit microcontroller #define EM_ST7 68 // STMicroelectronics ST7 8-bit microcontroller #define EM_68HC16 69 // Motorola MC68HC16 Microcontroller #define EM_68HC11 70 // Motorola MC68HC11 Microcontroller #define EM_68HC08 71 // Motorola MC68HC08 Microcontroller #define EM_68HC05 72 // Motorola MC68HC05 Microcontroller #define EM_SVX 73 // Silicon Graphics SVx #define EM_ST19 74 // STMicroelectronics ST19 8-bit cpu #define EM_VAX 75 // Digital VAX #define EM_CRIS 76 // Axis Communications 32-bit embedded processor #define EM_JAVELIN 77 // Infineon Technologies 32-bit embedded cpu #define EM_FIREPATH 78 // Element 14 64-bit DSP processor #define EM_ZSP 79 // LSI Logic's 16-bit DSP processor #define EM_MMIX 80 // Donald Knuth's educational 64-bit processor #define EM_HUANY 81 // Harvard's machine-independent format #define EM_PRISM 82 // SiTera Prism #define EM_AVR 83 // Atmel AVR 8-bit microcontroller #define EM_FR30 84 // Fujitsu FR30 #define EM_D10V 85 // Mitsubishi D10V #define EM_D30V 86 // Mitsubishi D30V #define EM_V850 87 // NEC v850 #define EM_M32R 88 // Renesas M32R (formerly Mitsubishi M32R) #define EM_MN10300 89 // Matsushita MN10300 #define EM_MN10200 90 // Matsushita MN10200 #define EM_PJ 91 // picoJava #define EM_OPENRISC 92 // OpenRISC 32-bit embedded processor #define EM_ARC_A5 93 // ARC Cores Tangent-A5 #define EM_XTENSA 94 // Tensilica Xtensa Architecture #define EM_VIDEOCORE 95 // Alphamosaic VideoCore processor #define EM_TMM_GPP 96 // Thompson Multimedia General Purpose Processor #define EM_NS32K 97 // National Semiconductor 32000 series #define EM_TPC 98 // Tenor Network TPC processor #define EM_SNP1K 99 // Trebia SNP 1000 processor #define EM_ST200 100 // STMicroelectronics ST200 microcontroller #define EM_IP2K 101 // Ubicom IP2022 micro controller #define EM_MAX 102 // MAX Processor #define EM_CR 103 // National Semiconductor CompactRISC #define EM_F2MC16 104 // Fujitsu F2MC16 #define EM_MSP430 105 // TI msp430 micro controller #define EM_BLACKFIN 106 // ADI Blackfin #define EM_SE_C33 107 // S1C33 Family of Seiko Epson processors #define EM_SEP 108 // Sharp embedded microprocessor #define EM_ARCA 109 // Arca RISC Microprocessor #define EM_UNICORE \ 110 // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University #define EM_EXCESS 111 // eXcess: 16/32/64-bit configurable embedded CPU #define EM_DXP 112 // Icera Semiconductor Inc. Deep Execution Processor #define EM_ALTERA_NIOS2 113 // Altera Nios II soft-core processor #define EM_CRX 114 // National Semiconductor CRX #define EM_XGATE 115 // Motorola XGATE embedded processor #define EM_C166 116 // Infineon C16x/XC16x processor #define EM_M16C 117 // Renesas M16C series microprocessors #define EM_DSPIC30F \ 118 // Microchip Technology dsPIC30F Digital Signal Controller #define EM_CE 119 // Freescale Communication Engine RISC core #define EM_M32C 120 // Renesas M32C series microprocessors #define EM_res121 121 // Reserved #define EM_res122 122 // Reserved #define EM_res123 123 // Reserved #define EM_res124 124 // Reserved #define EM_res125 125 // Reserved #define EM_res126 126 // Reserved #define EM_res127 127 // Reserved #define EM_res128 128 // Reserved #define EM_res129 129 // Reserved #define EM_res130 130 // Reserved #define EM_TSK3000 131 // Altium TSK3000 core #define EM_RS08 132 // Freescale RS08 embedded processor #define EM_res133 133 // Reserved #define EM_ECOG2 134 // Cyan Technology eCOG2 microprocessor #define EM_SCORE 135 // Sunplus Score #define EM_SCORE7 135 // Sunplus S+core7 RISC processor #define EM_DSP24 136 // New Japan Radio (NJR) 24-bit DSP Processor #define EM_VIDEOCORE3 137 // Broadcom VideoCore III processor #define EM_LATTICEMICO32 138 // RISC processor for Lattice FPGA architecture #define EM_SE_C17 139 // Seiko Epson C17 family #define EM_TI_C6000 140 // Texas Instruments TMS320C6000 DSP family #define EM_TI_C2000 141 // Texas Instruments TMS320C2000 DSP family #define EM_TI_C5500 142 // Texas Instruments TMS320C55x DSP family #define EM_res143 143 // Reserved #define EM_res144 144 // Reserved #define EM_res145 145 // Reserved #define EM_res146 146 // Reserved #define EM_res147 147 // Reserved #define EM_res148 148 // Reserved #define EM_res149 149 // Reserved #define EM_res150 150 // Reserved #define EM_res151 151 // Reserved #define EM_res152 152 // Reserved #define EM_res153 153 // Reserved #define EM_res154 154 // Reserved #define EM_res155 155 // Reserved #define EM_res156 156 // Reserved #define EM_res157 157 // Reserved #define EM_res158 158 // Reserved #define EM_res159 159 // Reserved #define EM_MMDSP_PLUS 160 // STMicroelectronics 64bit VLIW Data Signal Processor #define EM_CYPRESS_M8C 161 // Cypress M8C microprocessor #define EM_R32C 162 // Renesas R32C series microprocessors #define EM_TRIMEDIA 163 // NXP Semiconductors TriMedia architecture family #define EM_QDSP6 164 // QUALCOMM DSP6 Processor #define EM_8051 165 // Intel 8051 and variants #define EM_STXP7X 166 // STMicroelectronics STxP7x family #define EM_NDS32 \ 167 // Andes Technology compact code size embedded RISC processor family #define EM_ECOG1 168 // Cyan Technology eCOG1X family #define EM_ECOG1X 168 // Cyan Technology eCOG1X family #define EM_MAXQ30 169 // Dallas Semiconductor MAXQ30 Core Micro-controllers #define EM_XIMO16 170 // New Japan Radio (NJR) 16-bit DSP Processor #define EM_MANIK 171 // M2000 Reconfigurable RISC Microprocessor #define EM_CRAYNV2 172 // Cray Inc. NV2 vector architecture #define EM_RX 173 // Renesas RX family #define EM_METAG 174 // Imagination Technologies META processor architecture #define EM_MCST_ELBRUS 175 // MCST Elbrus general purpose hardware architecture #define EM_ECOG16 176 // Cyan Technology eCOG16 family #define EM_CR16 177 // National Semiconductor CompactRISC 16-bit processor #define EM_ETPU 178 // Freescale Extended Time Processing Unit #define EM_SLE9X 179 // Infineon Technologies SLE9X core #define EM_L1OM 180 // Intel L1OM #define EM_INTEL181 181 // Reserved by Intel #define EM_INTEL182 182 // Reserved by Intel #define EM_res183 183 // Reserved by ARM #define EM_res184 184 // Reserved by ARM #define EM_AVR32 185 // Atmel Corporation 32-bit microprocessor family #define EM_STM8 186 // STMicroeletronics STM8 8-bit microcontroller #define EM_TILE64 187 // Tilera TILE64 multicore architecture family #define EM_TILEPRO 188 // Tilera TILEPro multicore architecture family #define EM_MICROBLAZE 189 // Xilinx MicroBlaze 32-bit RISC soft processor core #define EM_CUDA 190 // NVIDIA CUDA architecture #define EM_TILEGX 191 // Tilera TILE-Gx multicore architecture family #define EM_CLOUDSHIELD 192 // CloudShield architecture family #define EM_COREA_1ST 193 // KIPO-KAIST Core-A 1st generation processor family #define EM_COREA_2ND 194 // KIPO-KAIST Core-A 2nd generation processor family #define EM_ARC_COMPACT2 195 // Synopsys ARCompact V2 #define EM_OPEN8 196 // Open8 8-bit RISC soft processor core #define EM_RL78 197 // Renesas RL78 family #define EM_VIDEOCORE5 198 // Broadcom VideoCore V processor #define EM_78KOR 199 // Renesas 78KOR family #define EM_56800EX 200 // Freescale 56800EX Digital Signal Controller (DSC) #define EM_BA1 201 // Beyond BA1 CPU architecture #define EM_BA2 202 // Beyond BA2 CPU architecture #define EM_XCORE 203 // XMOS xCORE processor family #define EM_MCHP_PIC 204 // Microchip 8-bit PIC(r) family #define EM_INTEL205 205 // Reserved by Intel #define EM_INTEL206 206 // Reserved by Intel #define EM_INTEL207 207 // Reserved by Intel #define EM_INTEL208 208 // Reserved by Intel #define EM_INTEL209 209 // Reserved by Intel #define EM_KM32 210 // KM211 KM32 32-bit processor #define EM_KMX32 211 // KM211 KMX32 32-bit processor #define EM_KMX16 212 // KM211 KMX16 16-bit processor #define EM_KMX8 213 // KM211 KMX8 8-bit processor #define EM_KVARC 214 // KM211 KVARC processor #define EM_CDP 215 // Paneve CDP architecture family #define EM_COGE 216 // Cognitive Smart Memory Processor #define EM_COOL 217 // iCelero CoolEngine #define EM_NORC 218 // Nanoradio Optimized RISC #define EM_CSR_KALIMBA 219 // CSR Kalimba architecture family #define EM_Z80 220 // Zilog Z80 #define EM_VISIUM 221 // Controls and Data Services VISIUMcore processor #define EM_FT32 222 // FTDI Chip FT32 high performance 32-bit RISC architecture #define EM_MOXIE 223 // Moxie processor family #define EM_AMDGPU 224 // AMD GPU architecture #define EM_RISCV 243 // RISC-V #define EM_LANAI 244 // Lanai processor #define EM_CEVA 245 // CEVA Processor Architecture Family #define EM_CEVA_X2 246 // CEVA X2 Processor Family #define EM_BPF 247 // Linux BPF – in-kernel virtual machine #define EM_GRAPHCORE_IPU 248 // Graphcore Intelligent Processing Unit #define EM_IMG1 249 // Imagination Technologies #define EM_NFP 250 // Netronome Flow Processor (P) #define EM_CSKY 252 // C-SKY processor family // File version #define EV_NONE 0 #define EV_CURRENT 1 // Identification index #define EI_MAG0 0 #define EI_MAG1 1 #define EI_MAG2 2 #define EI_MAG3 3 #define EI_CLASS 4 #define EI_DATA 5 #define EI_VERSION 6 #define EI_OSABI 7 #define EI_ABIVERSION 8 #define EI_PAD 9 #define EI_NIDENT 16 // Magic number #define ELFMAG0 0x7F #define ELFMAG1 'E' #define ELFMAG2 'L' #define ELFMAG3 'F' // File class #define ELFCLASSNONE 0 #define ELFCLASS32 1 #define ELFCLASS64 2 // Encoding #define ELFDATANONE 0 #define ELFDATA2LSB 1 #define ELFDATA2MSB 2 // OS extensions #define ELFOSABI_NONE 0 // No extensions or unspecified #define ELFOSABI_HPUX 1 // Hewlett-Packard HP-UX #define ELFOSABI_NETBSD 2 // NetBSD #define ELFOSABI_LINUX 3 // Linux #define ELFOSABI_SOLARIS 6 // Sun Solaris #define ELFOSABI_AIX 7 // AIX #define ELFOSABI_IRIX 8 // IRIX #define ELFOSABI_FREEBSD 9 // FreeBSD #define ELFOSABI_TRU64 10 // Compaq TRU64 UNIX #define ELFOSABI_MODESTO 11 // Novell Modesto #define ELFOSABI_OPENBSD 12 // Open BSD #define ELFOSABI_OPENVMS 13 // Open VMS #define ELFOSABI_NSK 14 // Hewlett-Packard Non-Stop Kernel #define ELFOSABI_AROS 15 // Amiga Research OS #define ELFOSABI_FENIXOS 16 // The FenixOS highly scalable multi-core OS // 64-255 Architecture-specific value range #define ELFOSABI_AMDGPU_HSA \ 64 // AMDGPU OS for HSA compatible compute // kernels. #define ELFOSABI_AMDGPU_PAL \ 65 // AMDGPU OS for AMD PAL compatible graphics // shaders and compute kernels. #define ELFOSABI_AMDGPU_MESA3D \ 66 // AMDGPU OS for Mesa3D compatible graphics // shaders and compute kernels. // AMDGPU specific e_flags #define EF_AMDGPU_MACH 0x0ff // AMDGPU processor selection mask. #define EF_AMDGPU_XNACK \ 0x100 // Indicates if the XNACK target feature is // enabled for all code contained in the ELF. // AMDGPU processors #define EF_AMDGPU_MACH_NONE 0x000 // Unspecified processor. #define EF_AMDGPU_MACH_R600_R600 0x001 #define EF_AMDGPU_MACH_R600_R630 0x002 #define EF_AMDGPU_MACH_R600_RS880 0x003 #define EF_AMDGPU_MACH_R600_RV670 0x004 #define EF_AMDGPU_MACH_R600_RV710 0x005 #define EF_AMDGPU_MACH_R600_RV730 0x006 #define EF_AMDGPU_MACH_R600_RV770 0x007 #define EF_AMDGPU_MACH_R600_CEDAR 0x008 #define EF_AMDGPU_MACH_R600_CYPRESS 0x009 #define EF_AMDGPU_MACH_R600_JUNIPER 0x00a #define EF_AMDGPU_MACH_R600_REDWOOD 0x00b #define EF_AMDGPU_MACH_R600_SUMO 0x00c #define EF_AMDGPU_MACH_R600_BARTS 0x00d #define EF_AMDGPU_MACH_R600_CAICOS 0x00e #define EF_AMDGPU_MACH_R600_CAYMAN 0x00f #define EF_AMDGPU_MACH_R600_TURKS 0x010 #define EF_AMDGPU_MACH_R600_RESERVED_FIRST 0x011 #define EF_AMDGPU_MACH_R600_RESERVED_LAST 0x01f #define EF_AMDGPU_MACH_R600_FIRST EF_AMDGPU_MACH_R600_R600 #define EF_AMDGPU_MACH_R600_LAST EF_AMDGPU_MACH_R600_TURKS #define EF_AMDGPU_MACH_AMDGCN_GFX600 0x020 #define EF_AMDGPU_MACH_AMDGCN_GFX601 0x021 #define EF_AMDGPU_MACH_AMDGCN_GFX700 0x022 #define EF_AMDGPU_MACH_AMDGCN_GFX701 0x023 #define EF_AMDGPU_MACH_AMDGCN_GFX702 0x024 #define EF_AMDGPU_MACH_AMDGCN_GFX703 0x025 #define EF_AMDGPU_MACH_AMDGCN_GFX704 0x026 #define EF_AMDGPU_MACH_AMDGCN_GFX801 0x028 #define EF_AMDGPU_MACH_AMDGCN_GFX802 0x029 #define EF_AMDGPU_MACH_AMDGCN_GFX803 0x02a #define EF_AMDGPU_MACH_AMDGCN_GFX810 0x02b #define EF_AMDGPU_MACH_AMDGCN_GFX900 0x02c #define EF_AMDGPU_MACH_AMDGCN_GFX902 0x02d #define EF_AMDGPU_MACH_AMDGCN_GFX904 0x02e #define EF_AMDGPU_MACH_AMDGCN_GFX906 0x02f #define EF_AMDGPU_MACH_AMDGCN_RESERVED0 0x027 #define EF_AMDGPU_MACH_AMDGCN_RESERVED1 0x030 #define EF_AMDGPU_MACH_AMDGCN_FIRST EF_AMDGPU_MACH_AMDGCN_GFX600 #define EF_AMDGPU_MACH_AMDGCN_LAST EF_AMDGPU_MACH_AMDGCN_GFX906 ///////////////////// // Sections constants // Section indexes #define SHN_UNDEF 0 #define SHN_LORESERVE 0xFF00 #define SHN_LOPROC 0xFF00 #define SHN_HIPROC 0xFF1F #define SHN_LOOS 0xFF20 #define SHN_HIOS 0xFF3F #define SHN_ABS 0xFFF1 #define SHN_COMMON 0xFFF2 #define SHN_XINDEX 0xFFFF #define SHN_HIRESERVE 0xFFFF // Section types #define SHT_NULL 0 #define SHT_PROGBITS 1 #define SHT_SYMTAB 2 #define SHT_STRTAB 3 #define SHT_RELA 4 #define SHT_HASH 5 #define SHT_DYNAMIC 6 #define SHT_NOTE 7 #define SHT_NOBITS 8 #define SHT_REL 9 #define SHT_SHLIB 10 #define SHT_DYNSYM 11 #define SHT_INIT_ARRAY 14 #define SHT_FINI_ARRAY 15 #define SHT_PREINIT_ARRAY 16 #define SHT_GROUP 17 #define SHT_SYMTAB_SHNDX 18 #define SHT_LOOS 0x60000000 #define SHT_HIOS 0x6fffffff #define SHT_LOPROC 0x70000000 #define SHT_HIPROC 0x7FFFFFFF #define SHT_LOUSER 0x80000000 #define SHT_HIUSER 0xFFFFFFFF // Section attribute flags #define SHF_WRITE 0x1 #define SHF_ALLOC 0x2 #define SHF_EXECINSTR 0x4 #define SHF_MERGE 0x10 #define SHF_STRINGS 0x20 #define SHF_INFO_LINK 0x40 #define SHF_LINK_ORDER 0x80 #define SHF_OS_NONCONFORMING 0x100 #define SHF_GROUP 0x200 #define SHF_TLS 0x400 #define SHF_MASKOS 0x0ff00000 #define SHF_MASKPROC 0xF0000000 // Section group flags #define GRP_COMDAT 0x1 #define GRP_MASKOS 0x0ff00000 #define GRP_MASKPROC 0xf0000000 // Symbol binding #define STB_LOCAL 0 #define STB_GLOBAL 1 #define STB_WEAK 2 #define STB_LOOS 10 #define STB_HIOS 12 #define STB_MULTIDEF 13 #define STB_LOPROC 13 #define STB_HIPROC 15 // Note types #define NT_AMDGPU_METADATA 1 #define NT_AMD_AMDGPU_HSA_METADATA 10 #define NT_AMD_AMDGPU_ISA 11 #define NT_AMD_AMDGPU_PAL_METADATA 12 // Symbol types #define STT_NOTYPE 0 #define STT_OBJECT 1 #define STT_FUNC 2 #define STT_SECTION 3 #define STT_FILE 4 #define STT_COMMON 5 #define STT_TLS 6 #define STT_LOOS 10 #define STT_AMDGPU_HSA_KERNEL 10 #define STT_HIOS 12 #define STT_LOPROC 13 #define STT_HIPROC 15 // Symbol visibility #define STV_DEFAULT 0 #define STV_INTERNAL 1 #define STV_HIDDEN 2 #define STV_PROTECTED 3 // Undefined name #define STN_UNDEF 0 // Relocation types #define R_386_NONE 0 #define R_X86_64_NONE 0 #define R_AMDGPU_NONE 0 #define R_386_32 1 #define R_X86_64_64 1 #define R_AMDGPU_ABS32_LO 1 #define R_386_PC32 2 #define R_X86_64_PC32 2 #define R_AMDGPU_ABS32_HI 2 #define R_386_GOT32 3 #define R_X86_64_GOT32 3 #define R_AMDGPU_ABS64 3 #define R_386_PLT32 4 #define R_X86_64_PLT32 4 #define R_AMDGPU_REL32 4 #define R_386_COPY 5 #define R_X86_64_COPY 5 #define R_AMDGPU_REL64 5 #define R_386_GLOB_DAT 6 #define R_X86_64_GLOB_DAT 6 #define R_AMDGPU_ABS32 6 #define R_386_JMP_SLOT 7 #define R_X86_64_JUMP_SLOT 7 #define R_AMDGPU_GOTPCREL 7 #define R_386_RELATIVE 8 #define R_X86_64_RELATIVE 8 #define R_AMDGPU_GOTPCREL32_LO 8 #define R_386_GOTOFF 9 #define R_X86_64_GOTPCREL 9 #define R_AMDGPU_GOTPCREL32_HI 9 #define R_386_GOTPC 10 #define R_X86_64_32 10 #define R_AMDGPU_REL32_LO 10 #define R_386_32PLT 11 #define R_X86_64_32S 11 #define R_AMDGPU_REL32_HI 11 #define R_X86_64_16 12 #define R_X86_64_PC16 13 #define R_AMDGPU_RELATIVE64 13 #define R_386_TLS_TPOFF 14 #define R_X86_64_8 14 #define R_386_TLS_IE 15 #define R_X86_64_PC8 15 #define R_386_TLS_GOTIE 16 #define R_X86_64_DTPMOD64 16 #define R_386_TLS_LE 17 #define R_X86_64_DTPOFF64 17 #define R_386_TLS_GD 18 #define R_X86_64_TPOFF64 18 #define R_386_TLS_LDM 19 #define R_X86_64_TLSGD 19 #define R_386_16 20 #define R_X86_64_TLSLD 20 #define R_386_PC16 21 #define R_X86_64_DTPOFF32 21 #define R_386_8 22 #define R_X86_64_GOTTPOFF 22 #define R_386_PC8 23 #define R_X86_64_TPOFF32 23 #define R_386_TLS_GD_32 24 #define R_X86_64_PC64 24 #define R_386_TLS_GD_PUSH 25 #define R_X86_64_GOTOFF64 25 #define R_386_TLS_GD_CALL 26 #define R_X86_64_GOTPC32 26 #define R_386_TLS_GD_POP 27 #define R_X86_64_GOT64 27 #define R_386_TLS_LDM_32 28 #define R_X86_64_GOTPCREL64 28 #define R_386_TLS_LDM_PUSH 29 #define R_X86_64_GOTPC64 29 #define R_386_TLS_LDM_CALL 30 #define R_X86_64_GOTPLT64 30 #define R_386_TLS_LDM_POP 31 #define R_X86_64_PLTOFF64 31 #define R_386_TLS_LDO_32 32 #define R_386_TLS_IE_32 33 #define R_386_TLS_LE_32 34 #define R_X86_64_GOTPC32_TLSDESC 34 #define R_386_TLS_DTPMOD32 35 #define R_X86_64_TLSDESC_CALL 35 #define R_386_TLS_DTPOFF32 36 #define R_X86_64_TLSDESC 36 #define R_386_TLS_TPOFF32 37 #define R_X86_64_IRELATIVE 37 #define R_386_SIZE32 38 #define R_386_TLS_GOTDESC 39 #define R_386_TLS_DESC_CALL 40 #define R_386_TLS_DESC 41 #define R_386_IRELATIVE 42 #define R_386_GOT32X 43 #define R_X86_64_GNU_VTINHERIT 250 #define R_X86_64_GNU_VTENTRY 251 // Segment types #define PT_NULL 0 #define PT_LOAD 1 #define PT_DYNAMIC 2 #define PT_INTERP 3 #define PT_NOTE 4 #define PT_SHLIB 5 #define PT_PHDR 6 #define PT_TLS 7 #define PT_LOOS 0x60000000 #define PT_HIOS 0x6fffffff #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7FFFFFFF // Segment flags #define PF_X 1 // Execute #define PF_W 2 // Write #define PF_R 4 // Read #define PF_MASKOS 0x0ff00000 // Unspecified #define PF_MASKPROC 0xf0000000 // Unspecified // Dynamic Array Tags #define DT_NULL 0 #define DT_NEEDED 1 #define DT_PLTRELSZ 2 #define DT_PLTGOT 3 #define DT_HASH 4 #define DT_STRTAB 5 #define DT_SYMTAB 6 #define DT_RELA 7 #define DT_RELASZ 8 #define DT_RELAENT 9 #define DT_STRSZ 10 #define DT_SYMENT 11 #define DT_INIT 12 #define DT_FINI 13 #define DT_SONAME 14 #define DT_RPATH 15 #define DT_SYMBOLIC 16 #define DT_REL 17 #define DT_RELSZ 18 #define DT_RELENT 19 #define DT_PLTREL 20 #define DT_DEBUG 21 #define DT_TEXTREL 22 #define DT_JMPREL 23 #define DT_BIND_NOW 24 #define DT_INIT_ARRAY 25 #define DT_FINI_ARRAY 26 #define DT_INIT_ARRAYSZ 27 #define DT_FINI_ARRAYSZ 28 #define DT_RUNPATH 29 #define DT_FLAGS 30 #define DT_ENCODING 32 #define DT_PREINIT_ARRAY 32 #define DT_PREINIT_ARRAYSZ 33 #define DT_MAXPOSTAGS 34 #define DT_LOOS 0x6000000D #define DT_HIOS 0x6ffff000 #define DT_LOPROC 0x70000000 #define DT_HIPROC 0x7FFFFFFF // DT_FLAGS values #define DF_ORIGIN 0x1 #define DF_SYMBOLIC 0x2 #define DF_TEXTREL 0x4 #define DF_BIND_NOW 0x8 #define DF_STATIC_TLS 0x10 // ELF file header struct Elf32_Ehdr { unsigned char e_ident[EI_NIDENT]; Elf_Half e_type; Elf_Half e_machine; Elf_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf_Word e_flags; Elf_Half e_ehsize; Elf_Half e_phentsize; Elf_Half e_phnum; Elf_Half e_shentsize; Elf_Half e_shnum; Elf_Half e_shstrndx; }; struct Elf64_Ehdr { unsigned char e_ident[EI_NIDENT]; Elf_Half e_type; Elf_Half e_machine; Elf_Word e_version; Elf64_Addr e_entry; Elf64_Off e_phoff; Elf64_Off e_shoff; Elf_Word e_flags; Elf_Half e_ehsize; Elf_Half e_phentsize; Elf_Half e_phnum; Elf_Half e_shentsize; Elf_Half e_shnum; Elf_Half e_shstrndx; }; // Section header struct Elf32_Shdr { Elf_Word sh_name; Elf_Word sh_type; Elf_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf_Word sh_size; Elf_Word sh_link; Elf_Word sh_info; Elf_Word sh_addralign; Elf_Word sh_entsize; }; struct Elf64_Shdr { Elf_Word sh_name; Elf_Word sh_type; Elf_Xword sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; Elf_Xword sh_size; Elf_Word sh_link; Elf_Word sh_info; Elf_Xword sh_addralign; Elf_Xword sh_entsize; }; // Segment header struct Elf32_Phdr { Elf_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf_Word p_filesz; Elf_Word p_memsz; Elf_Word p_flags; Elf_Word p_align; }; struct Elf64_Phdr { Elf_Word p_type; Elf_Word p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; Elf_Xword p_filesz; Elf_Xword p_memsz; Elf_Xword p_align; }; // Symbol table entry struct Elf32_Sym { Elf_Word st_name; Elf32_Addr st_value; Elf_Word st_size; unsigned char st_info; unsigned char st_other; Elf_Half st_shndx; }; struct Elf64_Sym { Elf_Word st_name; unsigned char st_info; unsigned char st_other; Elf_Half st_shndx; Elf64_Addr st_value; Elf_Xword st_size; }; #define ELF_ST_BIND( i ) ( ( i ) >> 4 ) #define ELF_ST_TYPE( i ) ( (i)&0xf ) #define ELF_ST_INFO( b, t ) ( ( ( b ) << 4 ) + ( (t)&0xf ) ) #define ELF_ST_VISIBILITY( o ) ( (o)&0x3 ) // Relocation entries struct Elf32_Rel { Elf32_Addr r_offset; Elf_Word r_info; }; struct Elf32_Rela { Elf32_Addr r_offset; Elf_Word r_info; Elf_Sword r_addend; }; struct Elf64_Rel { Elf64_Addr r_offset; Elf_Xword r_info; }; struct Elf64_Rela { Elf64_Addr r_offset; Elf_Xword r_info; Elf_Sxword r_addend; }; #define ELF32_R_SYM( i ) ( ( i ) >> 8 ) #define ELF32_R_TYPE( i ) ( (unsigned char)( i ) ) #define ELF32_R_INFO( s, t ) ( ( ( s ) << 8 ) + (unsigned char)( t ) ) #define ELF64_R_SYM( i ) ( ( i ) >> 32 ) #define ELF64_R_TYPE( i ) ( (i)&0xffffffffL ) #define ELF64_R_INFO( s, t ) \ ( ( ( ( int64_t )( s ) ) << 32 ) + ( (t)&0xffffffffL ) ) // Dynamic structure struct Elf32_Dyn { Elf_Sword d_tag; union { Elf_Word d_val; Elf32_Addr d_ptr; } d_un; }; struct Elf64_Dyn { Elf_Sxword d_tag; union { Elf_Xword d_val; Elf64_Addr d_ptr; } d_un; }; } // namespace ELFIO #endif // ELFTYPES_H /*** End of inlined file: elf_types.hpp ***/ /*** Start of inlined file: elfio_version.hpp ***/ #define ELFIO_VERSION "3.8" /*** End of inlined file: elfio_version.hpp ***/ /*** Start of inlined file: elfio_utils.hpp ***/ #ifndef ELFIO_UTILS_HPP #define ELFIO_UTILS_HPP #define ELFIO_GET_ACCESS( TYPE, NAME, FIELD ) \ TYPE get_##NAME() const { return ( *convertor )( FIELD ); } #define ELFIO_SET_ACCESS( TYPE, NAME, FIELD ) \ void set_##NAME( TYPE value ) \ { \ FIELD = value; \ FIELD = ( *convertor )( FIELD ); \ } #define ELFIO_GET_SET_ACCESS( TYPE, NAME, FIELD ) \ TYPE get_##NAME() const { return ( *convertor )( FIELD ); } \ void set_##NAME( TYPE value ) \ { \ FIELD = value; \ FIELD = ( *convertor )( FIELD ); \ } #define ELFIO_GET_ACCESS_DECL( TYPE, NAME ) virtual TYPE get_##NAME() const = 0 #define ELFIO_SET_ACCESS_DECL( TYPE, NAME ) \ virtual void set_##NAME( TYPE value ) = 0 #define ELFIO_GET_SET_ACCESS_DECL( TYPE, NAME ) \ virtual TYPE get_##NAME() const = 0; \ virtual void set_##NAME( TYPE value ) = 0 namespace ELFIO { //------------------------------------------------------------------------------ class endianess_convertor { public: //------------------------------------------------------------------------------ endianess_convertor() { need_conversion = false; } //------------------------------------------------------------------------------ void setup( unsigned char elf_file_encoding ) { need_conversion = ( elf_file_encoding != get_host_encoding() ); } //------------------------------------------------------------------------------ uint64_t operator()( uint64_t value ) const { if ( !need_conversion ) { return value; } value = ( ( value & 0x00000000000000FFull ) << 56 ) | ( ( value & 0x000000000000FF00ull ) << 40 ) | ( ( value & 0x0000000000FF0000ull ) << 24 ) | ( ( value & 0x00000000FF000000ull ) << 8 ) | ( ( value & 0x000000FF00000000ull ) >> 8 ) | ( ( value & 0x0000FF0000000000ull ) >> 24 ) | ( ( value & 0x00FF000000000000ull ) >> 40 ) | ( ( value & 0xFF00000000000000ull ) >> 56 ); return value; } //------------------------------------------------------------------------------ int64_t operator()( int64_t value ) const { if ( !need_conversion ) { return value; } return ( int64_t )( *this )( (uint64_t)value ); } //------------------------------------------------------------------------------ uint32_t operator()( uint32_t value ) const { if ( !need_conversion ) { return value; } value = ( ( value & 0x000000FF ) << 24 ) | ( ( value & 0x0000FF00 ) << 8 ) | ( ( value & 0x00FF0000 ) >> 8 ) | ( ( value & 0xFF000000 ) >> 24 ); return value; } //------------------------------------------------------------------------------ int32_t operator()( int32_t value ) const { if ( !need_conversion ) { return value; } return ( int32_t )( *this )( (uint32_t)value ); } //------------------------------------------------------------------------------ uint16_t operator()( uint16_t value ) const { if ( !need_conversion ) { return value; } value = ( ( value & 0x00FF ) << 8 ) | ( ( value & 0xFF00 ) >> 8 ); return value; } //------------------------------------------------------------------------------ int16_t operator()( int16_t value ) const { if ( !need_conversion ) { return value; } return ( int16_t )( *this )( (uint16_t)value ); } //------------------------------------------------------------------------------ int8_t operator()( int8_t value ) const { return value; } //------------------------------------------------------------------------------ uint8_t operator()( uint8_t value ) const { return value; } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ unsigned char get_host_encoding() const { static const int tmp = 1; if ( 1 == *(const char*)&tmp ) { return ELFDATA2LSB; } else { return ELFDATA2MSB; } } //------------------------------------------------------------------------------ private: bool need_conversion; }; //------------------------------------------------------------------------------ inline uint32_t elf_hash( const unsigned char* name ) { uint32_t h = 0, g; while ( *name ) { h = ( h << 4 ) + *name++; g = h & 0xf0000000; if ( g != 0 ) h ^= g >> 24; h &= ~g; } return h; } } // namespace ELFIO #endif // ELFIO_UTILS_HPP /*** End of inlined file: elfio_utils.hpp ***/ /*** Start of inlined file: elfio_header.hpp ***/ #ifndef ELF_HEADER_HPP #define ELF_HEADER_HPP #include namespace ELFIO { class elf_header { public: virtual ~elf_header(){}; virtual bool load( std::istream& stream ) = 0; virtual bool save( std::ostream& stream ) const = 0; // ELF header functions ELFIO_GET_ACCESS_DECL( unsigned char, class ); ELFIO_GET_ACCESS_DECL( unsigned char, elf_version ); ELFIO_GET_ACCESS_DECL( unsigned char, encoding ); ELFIO_GET_ACCESS_DECL( Elf_Half, header_size ); ELFIO_GET_ACCESS_DECL( Elf_Half, section_entry_size ); ELFIO_GET_ACCESS_DECL( Elf_Half, segment_entry_size ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, version ); ELFIO_GET_SET_ACCESS_DECL( unsigned char, os_abi ); ELFIO_GET_SET_ACCESS_DECL( unsigned char, abi_version ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, type ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, machine ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, entry ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, sections_num ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, sections_offset ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, segments_num ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, segments_offset ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, section_name_str_index ); }; template struct elf_header_impl_types; template <> struct elf_header_impl_types { typedef Elf32_Phdr Phdr_type; typedef Elf32_Shdr Shdr_type; static const unsigned char file_class = ELFCLASS32; }; template <> struct elf_header_impl_types { typedef Elf64_Phdr Phdr_type; typedef Elf64_Shdr Shdr_type; static const unsigned char file_class = ELFCLASS64; }; template class elf_header_impl : public elf_header { public: //------------------------------------------------------------------------------ elf_header_impl( endianess_convertor* convertor_, unsigned char encoding ) { convertor = convertor_; std::fill_n( reinterpret_cast( &header ), sizeof( header ), '\0' ); header.e_ident[EI_MAG0] = ELFMAG0; header.e_ident[EI_MAG1] = ELFMAG1; header.e_ident[EI_MAG2] = ELFMAG2; header.e_ident[EI_MAG3] = ELFMAG3; header.e_ident[EI_CLASS] = elf_header_impl_types::file_class; header.e_ident[EI_DATA] = encoding; header.e_ident[EI_VERSION] = EV_CURRENT; header.e_version = ( *convertor )( (Elf_Word)EV_CURRENT ); header.e_ehsize = ( sizeof( header ) ); header.e_ehsize = ( *convertor )( header.e_ehsize ); header.e_shstrndx = ( *convertor )( (Elf_Half)1 ); header.e_phentsize = sizeof( typename elf_header_impl_types::Phdr_type ); header.e_shentsize = sizeof( typename elf_header_impl_types::Shdr_type ); header.e_phentsize = ( *convertor )( header.e_phentsize ); header.e_shentsize = ( *convertor )( header.e_shentsize ); } //------------------------------------------------------------------------------ bool load( std::istream& stream ) { stream.seekg( 0 ); stream.read( reinterpret_cast( &header ), sizeof( header ) ); return ( stream.gcount() == sizeof( header ) ); } //------------------------------------------------------------------------------ bool save( std::ostream& stream ) const { stream.seekp( 0 ); stream.write( reinterpret_cast( &header ), sizeof( header ) ); return stream.good(); } //------------------------------------------------------------------------------ // ELF header functions ELFIO_GET_ACCESS( unsigned char, class, header.e_ident[EI_CLASS] ); ELFIO_GET_ACCESS( unsigned char, elf_version, header.e_ident[EI_VERSION] ); ELFIO_GET_ACCESS( unsigned char, encoding, header.e_ident[EI_DATA] ); ELFIO_GET_ACCESS( Elf_Half, header_size, header.e_ehsize ); ELFIO_GET_ACCESS( Elf_Half, section_entry_size, header.e_shentsize ); ELFIO_GET_ACCESS( Elf_Half, segment_entry_size, header.e_phentsize ); ELFIO_GET_SET_ACCESS( Elf_Word, version, header.e_version ); ELFIO_GET_SET_ACCESS( unsigned char, os_abi, header.e_ident[EI_OSABI] ); ELFIO_GET_SET_ACCESS( unsigned char, abi_version, header.e_ident[EI_ABIVERSION] ); ELFIO_GET_SET_ACCESS( Elf_Half, type, header.e_type ); ELFIO_GET_SET_ACCESS( Elf_Half, machine, header.e_machine ); ELFIO_GET_SET_ACCESS( Elf_Word, flags, header.e_flags ); ELFIO_GET_SET_ACCESS( Elf_Half, section_name_str_index, header.e_shstrndx ); ELFIO_GET_SET_ACCESS( Elf64_Addr, entry, header.e_entry ); ELFIO_GET_SET_ACCESS( Elf_Half, sections_num, header.e_shnum ); ELFIO_GET_SET_ACCESS( Elf64_Off, sections_offset, header.e_shoff ); ELFIO_GET_SET_ACCESS( Elf_Half, segments_num, header.e_phnum ); ELFIO_GET_SET_ACCESS( Elf64_Off, segments_offset, header.e_phoff ); private: T header; endianess_convertor* convertor; }; } // namespace ELFIO #endif // ELF_HEADER_HPP /*** End of inlined file: elfio_header.hpp ***/ /*** Start of inlined file: elfio_section.hpp ***/ #ifndef ELFIO_SECTION_HPP #define ELFIO_SECTION_HPP #include #include #include namespace ELFIO { class section { friend class elfio; public: virtual ~section(){}; ELFIO_GET_ACCESS_DECL( Elf_Half, index ); ELFIO_GET_SET_ACCESS_DECL( std::string, name ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, flags ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, info ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, link ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, addr_align ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, entry_size ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, address ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, size ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, name_string_offset ); ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); virtual const char* get_data() const = 0; virtual void set_data( const char* pData, Elf_Word size ) = 0; virtual void set_data( const std::string& data ) = 0; virtual void append_data( const char* pData, Elf_Word size ) = 0; virtual void append_data( const std::string& data ) = 0; virtual size_t get_stream_size() const = 0; virtual void set_stream_size( size_t value ) = 0; protected: ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); ELFIO_SET_ACCESS_DECL( Elf_Half, index ); virtual void load( std::istream& stream, std::streampos header_offset ) = 0; virtual void save( std::ostream& stream, std::streampos header_offset, std::streampos data_offset ) = 0; virtual bool is_address_initialized() const = 0; }; template class section_impl : public section { public: //------------------------------------------------------------------------------ section_impl( const endianess_convertor* convertor_ ) : convertor( convertor_ ) { std::fill_n( reinterpret_cast( &header ), sizeof( header ), '\0' ); is_address_set = false; data = 0; data_size = 0; index = 0; stream_size = 0; } //------------------------------------------------------------------------------ ~section_impl() { delete[] data; } //------------------------------------------------------------------------------ // Section info functions ELFIO_GET_SET_ACCESS( Elf_Word, type, header.sh_type ); ELFIO_GET_SET_ACCESS( Elf_Xword, flags, header.sh_flags ); ELFIO_GET_SET_ACCESS( Elf_Xword, size, header.sh_size ); ELFIO_GET_SET_ACCESS( Elf_Word, link, header.sh_link ); ELFIO_GET_SET_ACCESS( Elf_Word, info, header.sh_info ); ELFIO_GET_SET_ACCESS( Elf_Xword, addr_align, header.sh_addralign ); ELFIO_GET_SET_ACCESS( Elf_Xword, entry_size, header.sh_entsize ); ELFIO_GET_SET_ACCESS( Elf_Word, name_string_offset, header.sh_name ); ELFIO_GET_ACCESS( Elf64_Addr, address, header.sh_addr ); //------------------------------------------------------------------------------ Elf_Half get_index() const { return index; } //------------------------------------------------------------------------------ std::string get_name() const { return name; } //------------------------------------------------------------------------------ void set_name( std::string name_ ) { name = name_; } //------------------------------------------------------------------------------ void set_address( Elf64_Addr value ) { header.sh_addr = value; header.sh_addr = ( *convertor )( header.sh_addr ); is_address_set = true; } //------------------------------------------------------------------------------ bool is_address_initialized() const { return is_address_set; } //------------------------------------------------------------------------------ const char* get_data() const { return data; } //------------------------------------------------------------------------------ void set_data( const char* raw_data, Elf_Word size ) { if ( get_type() != SHT_NOBITS ) { delete[] data; data = new ( std::nothrow ) char[size]; if ( 0 != data && 0 != raw_data ) { data_size = size; std::copy( raw_data, raw_data + size, data ); } else { data_size = 0; } } set_size( data_size ); } //------------------------------------------------------------------------------ void set_data( const std::string& str_data ) { return set_data( str_data.c_str(), (Elf_Word)str_data.size() ); } //------------------------------------------------------------------------------ void append_data( const char* raw_data, Elf_Word size ) { if ( get_type() != SHT_NOBITS ) { if ( get_size() + size < data_size ) { std::copy( raw_data, raw_data + size, data + get_size() ); } else { data_size = 2 * ( data_size + size ); char* new_data = new ( std::nothrow ) char[data_size]; if ( 0 != new_data ) { std::copy( data, data + get_size(), new_data ); std::copy( raw_data, raw_data + size, new_data + get_size() ); delete[] data; data = new_data; } else { size = 0; } } set_size( get_size() + size ); } } //------------------------------------------------------------------------------ void append_data( const std::string& str_data ) { return append_data( str_data.c_str(), (Elf_Word)str_data.size() ); } //------------------------------------------------------------------------------ protected: //------------------------------------------------------------------------------ ELFIO_GET_SET_ACCESS( Elf64_Off, offset, header.sh_offset ); //------------------------------------------------------------------------------ void set_index( Elf_Half value ) { index = value; } //------------------------------------------------------------------------------ void load( std::istream& stream, std::streampos header_offset ) { std::fill_n( reinterpret_cast( &header ), sizeof( header ), '\0' ); stream.seekg( 0, stream.end ); set_stream_size( stream.tellg() ); stream.seekg( header_offset ); stream.read( reinterpret_cast( &header ), sizeof( header ) ); Elf_Xword size = get_size(); if ( 0 == data && SHT_NULL != get_type() && SHT_NOBITS != get_type() && size < get_stream_size() ) { data = new ( std::nothrow ) char[size + 1]; if ( ( 0 != size ) && ( 0 != data ) ) { stream.seekg( ( *convertor )( header.sh_offset ) ); stream.read( data, size ); data[size] = 0; // Ensure data is ended with 0 to avoid oob read data_size = size; } else { data_size = 0; } } } //------------------------------------------------------------------------------ void save( std::ostream& stream, std::streampos header_offset, std::streampos data_offset ) { if ( 0 != get_index() ) { header.sh_offset = data_offset; header.sh_offset = ( *convertor )( header.sh_offset ); } save_header( stream, header_offset ); if ( get_type() != SHT_NOBITS && get_type() != SHT_NULL && get_size() != 0 && data != 0 ) { save_data( stream, data_offset ); } } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ void save_header( std::ostream& stream, std::streampos header_offset ) const { stream.seekp( header_offset ); stream.write( reinterpret_cast( &header ), sizeof( header ) ); } //------------------------------------------------------------------------------ void save_data( std::ostream& stream, std::streampos data_offset ) const { stream.seekp( data_offset ); stream.write( get_data(), get_size() ); } //------------------------------------------------------------------------------ size_t get_stream_size() const { return stream_size; } //------------------------------------------------------------------------------ void set_stream_size( size_t value ) { stream_size = value; } //------------------------------------------------------------------------------ private: T header; Elf_Half index; std::string name; char* data; Elf_Word data_size; const endianess_convertor* convertor; bool is_address_set; size_t stream_size; }; } // namespace ELFIO #endif // ELFIO_SECTION_HPP /*** End of inlined file: elfio_section.hpp ***/ /*** Start of inlined file: elfio_segment.hpp ***/ #ifndef ELFIO_SEGMENT_HPP #define ELFIO_SEGMENT_HPP #include #include #include namespace ELFIO { class segment { friend class elfio; public: virtual ~segment(){}; ELFIO_GET_ACCESS_DECL( Elf_Half, index ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, align ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, virtual_address ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, physical_address ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, file_size ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, memory_size ); ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); virtual const char* get_data() const = 0; virtual Elf_Half add_section_index( Elf_Half index, Elf_Xword addr_align ) = 0; virtual Elf_Half get_sections_num() const = 0; virtual Elf_Half get_section_index_at( Elf_Half num ) const = 0; virtual bool is_offset_initialized() const = 0; protected: ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); ELFIO_SET_ACCESS_DECL( Elf_Half, index ); virtual const std::vector& get_sections() const = 0; virtual void load( std::istream& stream, std::streampos header_offset ) = 0; virtual void save( std::ostream& stream, std::streampos header_offset, std::streampos data_offset ) = 0; }; //------------------------------------------------------------------------------ template class segment_impl : public segment { public: //------------------------------------------------------------------------------ segment_impl( endianess_convertor* convertor_ ) : stream_size( 0 ), index( 0 ), data( 0 ), convertor( convertor_ ) { is_offset_set = false; std::fill_n( reinterpret_cast( &ph ), sizeof( ph ), '\0' ); } //------------------------------------------------------------------------------ virtual ~segment_impl() { delete[] data; } //------------------------------------------------------------------------------ // Section info functions ELFIO_GET_SET_ACCESS( Elf_Word, type, ph.p_type ); ELFIO_GET_SET_ACCESS( Elf_Word, flags, ph.p_flags ); ELFIO_GET_SET_ACCESS( Elf_Xword, align, ph.p_align ); ELFIO_GET_SET_ACCESS( Elf64_Addr, virtual_address, ph.p_vaddr ); ELFIO_GET_SET_ACCESS( Elf64_Addr, physical_address, ph.p_paddr ); ELFIO_GET_SET_ACCESS( Elf_Xword, file_size, ph.p_filesz ); ELFIO_GET_SET_ACCESS( Elf_Xword, memory_size, ph.p_memsz ); ELFIO_GET_ACCESS( Elf64_Off, offset, ph.p_offset ); size_t stream_size; //------------------------------------------------------------------------------ size_t get_stream_size() const { return stream_size; } //------------------------------------------------------------------------------ void set_stream_size( size_t value ) { stream_size = value; } //------------------------------------------------------------------------------ Elf_Half get_index() const { return index; } //------------------------------------------------------------------------------ const char* get_data() const { return data; } //------------------------------------------------------------------------------ Elf_Half add_section_index( Elf_Half sec_index, Elf_Xword addr_align ) { sections.push_back( sec_index ); if ( addr_align > get_align() ) { set_align( addr_align ); } return (Elf_Half)sections.size(); } //------------------------------------------------------------------------------ Elf_Half get_sections_num() const { return (Elf_Half)sections.size(); } //------------------------------------------------------------------------------ Elf_Half get_section_index_at( Elf_Half num ) const { if ( num < sections.size() ) { return sections[num]; } return Elf_Half( -1 ); } //------------------------------------------------------------------------------ protected: //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void set_offset( Elf64_Off value ) { ph.p_offset = value; ph.p_offset = ( *convertor )( ph.p_offset ); is_offset_set = true; } //------------------------------------------------------------------------------ bool is_offset_initialized() const { return is_offset_set; } //------------------------------------------------------------------------------ const std::vector& get_sections() const { return sections; } //------------------------------------------------------------------------------ void set_index( Elf_Half value ) { index = value; } //------------------------------------------------------------------------------ void load( std::istream& stream, std::streampos header_offset ) { stream.seekg( 0, stream.end ); set_stream_size( stream.tellg() ); stream.seekg( header_offset ); stream.read( reinterpret_cast( &ph ), sizeof( ph ) ); is_offset_set = true; if ( PT_NULL != get_type() && 0 != get_file_size() ) { stream.seekg( ( *convertor )( ph.p_offset ) ); Elf_Xword size = get_file_size(); if ( size > get_stream_size() ) { data = 0; } else { data = new (std::nothrow) char[size + 1]; if ( 0 != data ) { stream.read( data, size ); data[size] = 0; } } } } //------------------------------------------------------------------------------ void save( std::ostream& stream, std::streampos header_offset, std::streampos data_offset ) { ph.p_offset = data_offset; ph.p_offset = ( *convertor )( ph.p_offset ); stream.seekp( header_offset ); stream.write( reinterpret_cast( &ph ), sizeof( ph ) ); } //------------------------------------------------------------------------------ private: T ph; Elf_Half index; char* data; std::vector sections; endianess_convertor* convertor; bool is_offset_set; }; } // namespace ELFIO #endif // ELFIO_SEGMENT_HPP /*** End of inlined file: elfio_segment.hpp ***/ /*** Start of inlined file: elfio_strings.hpp ***/ #ifndef ELFIO_STRINGS_HPP #define ELFIO_STRINGS_HPP #include #include #include namespace ELFIO { //------------------------------------------------------------------------------ template class string_section_accessor_template { public: //------------------------------------------------------------------------------ string_section_accessor_template( S* section_ ) : string_section( section_ ) { } //------------------------------------------------------------------------------ const char* get_string( Elf_Word index ) const { if ( string_section ) { if ( index < string_section->get_size() ) { const char* data = string_section->get_data(); if ( 0 != data ) { return data + index; } } } return 0; } //------------------------------------------------------------------------------ Elf_Word add_string( const char* str ) { Elf_Word current_position = 0; if ( string_section ) { // Strings are addeded to the end of the current section data current_position = (Elf_Word)string_section->get_size(); if ( current_position == 0 ) { char empty_string = '\0'; string_section->append_data( &empty_string, 1 ); current_position++; } string_section->append_data( str, (Elf_Word)std::strlen( str ) + 1 ); } return current_position; } //------------------------------------------------------------------------------ Elf_Word add_string( const std::string& str ) { return add_string( str.c_str() ); } //------------------------------------------------------------------------------ private: S* string_section; }; using string_section_accessor = string_section_accessor_template
; using const_string_section_accessor = string_section_accessor_template; } // namespace ELFIO #endif // ELFIO_STRINGS_HPP /*** End of inlined file: elfio_strings.hpp ***/ #define ELFIO_HEADER_ACCESS_GET( TYPE, FNAME ) \ TYPE get_##FNAME() const { return header ? ( header->get_##FNAME() ) : 0; } #define ELFIO_HEADER_ACCESS_GET_SET( TYPE, FNAME ) \ TYPE get_##FNAME() const \ { \ return header ? ( header->get_##FNAME() ) : 0; \ } \ void set_##FNAME( TYPE val ) \ { \ if ( header ) { \ header->set_##FNAME( val ); \ } \ } namespace ELFIO { //------------------------------------------------------------------------------ class elfio { public: //------------------------------------------------------------------------------ elfio() : sections( this ), segments( this ) { header = 0; current_file_pos = 0; create( ELFCLASS32, ELFDATA2LSB ); } //------------------------------------------------------------------------------ ~elfio() { clean(); } //------------------------------------------------------------------------------ void create( unsigned char file_class, unsigned char encoding ) { clean(); convertor.setup( encoding ); header = create_header( file_class, encoding ); create_mandatory_sections(); } //------------------------------------------------------------------------------ bool load( const std::string& file_name ) { std::ifstream stream; stream.open( file_name.c_str(), std::ios::in | std::ios::binary ); if ( !stream ) { return false; } return load( stream ); } //------------------------------------------------------------------------------ bool load( std::istream& stream ) { clean(); unsigned char e_ident[EI_NIDENT]; // Read ELF file signature stream.read( reinterpret_cast( &e_ident ), sizeof( e_ident ) ); // Is it ELF file? if ( stream.gcount() != sizeof( e_ident ) || e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3 ) { return false; } if ( ( e_ident[EI_CLASS] != ELFCLASS64 ) && ( e_ident[EI_CLASS] != ELFCLASS32 ) ) { return false; } convertor.setup( e_ident[EI_DATA] ); header = create_header( e_ident[EI_CLASS], e_ident[EI_DATA] ); if ( 0 == header ) { return false; } if ( !header->load( stream ) ) { return false; } load_sections( stream ); bool is_still_good = load_segments( stream ); return is_still_good; } //------------------------------------------------------------------------------ bool save( const std::string& file_name ) { std::ofstream stream; stream.open( file_name.c_str(), std::ios::out | std::ios::binary ); if ( !stream ) { return false; } return save( stream ); } //------------------------------------------------------------------------------ bool save( std::ostream& stream ) { if ( !stream || !header ) { return false; } bool is_still_good = true; // Define layout specific header fields // The position of the segment table is fixed after the header. // The position of the section table is variable and needs to be fixed // before saving. header->set_segments_num( segments.size() ); header->set_segments_offset( segments.size() ? header->get_header_size() : 0 ); header->set_sections_num( sections.size() ); header->set_sections_offset( 0 ); // Layout the first section right after the segment table current_file_pos = header->get_header_size() + header->get_segment_entry_size() * (Elf_Xword)header->get_segments_num(); calc_segment_alignment(); is_still_good = layout_segments_and_their_sections(); is_still_good = is_still_good && layout_sections_without_segments(); is_still_good = is_still_good && layout_section_table(); is_still_good = is_still_good && save_header( stream ); is_still_good = is_still_good && save_sections( stream ); is_still_good = is_still_good && save_segments( stream ); return is_still_good; } //------------------------------------------------------------------------------ // ELF header access functions ELFIO_HEADER_ACCESS_GET( unsigned char, class ); ELFIO_HEADER_ACCESS_GET( unsigned char, elf_version ); ELFIO_HEADER_ACCESS_GET( unsigned char, encoding ); ELFIO_HEADER_ACCESS_GET( Elf_Word, version ); ELFIO_HEADER_ACCESS_GET( Elf_Half, header_size ); ELFIO_HEADER_ACCESS_GET( Elf_Half, section_entry_size ); ELFIO_HEADER_ACCESS_GET( Elf_Half, segment_entry_size ); ELFIO_HEADER_ACCESS_GET_SET( unsigned char, os_abi ); ELFIO_HEADER_ACCESS_GET_SET( unsigned char, abi_version ); ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, type ); ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, machine ); ELFIO_HEADER_ACCESS_GET_SET( Elf_Word, flags ); ELFIO_HEADER_ACCESS_GET_SET( Elf64_Addr, entry ); ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, sections_offset ); ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, segments_offset ); ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, section_name_str_index ); //------------------------------------------------------------------------------ const endianess_convertor& get_convertor() const { return convertor; } //------------------------------------------------------------------------------ Elf_Xword get_default_entry_size( Elf_Word section_type ) const { switch ( section_type ) { case SHT_RELA: if ( header->get_class() == ELFCLASS64 ) { return sizeof( Elf64_Rela ); } else { return sizeof( Elf32_Rela ); } case SHT_REL: if ( header->get_class() == ELFCLASS64 ) { return sizeof( Elf64_Rel ); } else { return sizeof( Elf32_Rel ); } case SHT_SYMTAB: if ( header->get_class() == ELFCLASS64 ) { return sizeof( Elf64_Sym ); } else { return sizeof( Elf32_Sym ); } case SHT_DYNAMIC: if ( header->get_class() == ELFCLASS64 ) { return sizeof( Elf64_Dyn ); } else { return sizeof( Elf32_Dyn ); } default: return 0; } } //------------------------------------------------------------------------------ private: bool is_offset_in_section( Elf64_Off offset, const section* sec ) const { return ( offset >= sec->get_offset() ) && ( offset < ( sec->get_offset() + sec->get_size() ) ); } //------------------------------------------------------------------------------ public: //! returns an empty string if no problems are detected, //! or a string containing an error message if problems are found std::string validate() const { // check for overlapping sections in the file for ( int i = 0; i < sections.size(); ++i ) { for ( int j = i + 1; j < sections.size(); ++j ) { const section* a = sections[i]; const section* b = sections[j]; if ( !( a->get_type() & SHT_NOBITS ) && !( b->get_type() & SHT_NOBITS ) && ( a->get_size() > 0 ) && ( b->get_size() > 0 ) && ( a->get_offset() > 0 ) && ( b->get_offset() > 0 ) ) { if ( is_offset_in_section( a->get_offset(), b ) || is_offset_in_section( a->get_offset() + a->get_size() - 1, b ) || is_offset_in_section( b->get_offset(), a ) || is_offset_in_section( b->get_offset() + b->get_size() - 1, a ) ) { return "Sections " + a->get_name() + " and " + b->get_name() + " overlap in file"; } } } } // more checks to be added here... return ""; } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ void clean() { delete header; header = 0; std::vector::const_iterator it; for ( it = sections_.begin(); it != sections_.end(); ++it ) { delete *it; } sections_.clear(); std::vector::const_iterator it1; for ( it1 = segments_.begin(); it1 != segments_.end(); ++it1 ) { delete *it1; } segments_.clear(); } //------------------------------------------------------------------------------ elf_header* create_header( unsigned char file_class, unsigned char encoding ) { elf_header* new_header = 0; if ( file_class == ELFCLASS64 ) { new_header = new elf_header_impl( &convertor, encoding ); } else if ( file_class == ELFCLASS32 ) { new_header = new elf_header_impl( &convertor, encoding ); } else { return 0; } return new_header; } //------------------------------------------------------------------------------ section* create_section() { section* new_section; unsigned char file_class = get_class(); if ( file_class == ELFCLASS64 ) { new_section = new section_impl( &convertor ); } else if ( file_class == ELFCLASS32 ) { new_section = new section_impl( &convertor ); } else { return 0; } new_section->set_index( (Elf_Half)sections_.size() ); sections_.push_back( new_section ); return new_section; } //------------------------------------------------------------------------------ segment* create_segment() { segment* new_segment; unsigned char file_class = header->get_class(); if ( file_class == ELFCLASS64 ) { new_segment = new segment_impl( &convertor ); } else if ( file_class == ELFCLASS32 ) { new_segment = new segment_impl( &convertor ); } else { return 0; } new_segment->set_index( (Elf_Half)segments_.size() ); segments_.push_back( new_segment ); return new_segment; } //------------------------------------------------------------------------------ void create_mandatory_sections() { // Create null section without calling to 'add_section' as no string // section containing section names exists yet section* sec0 = create_section(); sec0->set_index( 0 ); sec0->set_name( "" ); sec0->set_name_string_offset( 0 ); set_section_name_str_index( 1 ); section* shstrtab = sections.add( ".shstrtab" ); shstrtab->set_type( SHT_STRTAB ); shstrtab->set_addr_align( 1 ); } //------------------------------------------------------------------------------ Elf_Half load_sections( std::istream& stream ) { Elf_Half entry_size = header->get_section_entry_size(); Elf_Half num = header->get_sections_num(); Elf64_Off offset = header->get_sections_offset(); for ( Elf_Half i = 0; i < num; ++i ) { section* sec = create_section(); sec->load( stream, (std::streamoff)offset + (std::streampos)i * entry_size ); sec->set_index( i ); // To mark that the section is not permitted to reassign address // during layout calculation sec->set_address( sec->get_address() ); } Elf_Half shstrndx = get_section_name_str_index(); if ( SHN_UNDEF != shstrndx ) { string_section_accessor str_reader( sections[shstrndx] ); for ( Elf_Half i = 0; i < num; ++i ) { Elf_Word section_offset = sections[i]->get_name_string_offset(); const char* p = str_reader.get_string( section_offset ); if ( p != 0 ) { sections[i]->set_name( p ); } } } return num; } //------------------------------------------------------------------------------ //! Checks whether the addresses of the section entirely fall within the given segment. //! It doesn't matter if the addresses are memory addresses, or file offsets, //! they just need to be in the same address space bool is_sect_in_seg( Elf64_Off sect_begin, Elf_Xword sect_size, Elf64_Off seg_begin, Elf64_Off seg_end ) { return ( seg_begin <= sect_begin ) && ( sect_begin + sect_size <= seg_end ) && ( sect_begin < seg_end ); // this is important criteria when sect_size == 0 // Example: seg_begin=10, seg_end=12 (-> covering the bytes 10 and 11) // sect_begin=12, sect_size=0 -> shall return false! } //------------------------------------------------------------------------------ bool load_segments( std::istream& stream ) { Elf_Half entry_size = header->get_segment_entry_size(); Elf_Half num = header->get_segments_num(); Elf64_Off offset = header->get_segments_offset(); for ( Elf_Half i = 0; i < num; ++i ) { segment* seg; unsigned char file_class = header->get_class(); if ( file_class == ELFCLASS64 ) { seg = new segment_impl( &convertor ); } else if ( file_class == ELFCLASS32 ) { seg = new segment_impl( &convertor ); } else { return false; } seg->load( stream, (std::streamoff)offset + (std::streampos)i * entry_size ); seg->set_index( i ); // Add sections to the segments (similar to readelfs algorithm) Elf64_Off segBaseOffset = seg->get_offset(); Elf64_Off segEndOffset = segBaseOffset + seg->get_file_size(); Elf64_Off segVBaseAddr = seg->get_virtual_address(); Elf64_Off segVEndAddr = segVBaseAddr + seg->get_memory_size(); for ( Elf_Half j = 0; j < sections.size(); ++j ) { const section* psec = sections[j]; // SHF_ALLOC sections are matched based on the virtual address // otherwise the file offset is matched if ( ( psec->get_flags() & SHF_ALLOC ) ? is_sect_in_seg( psec->get_address(), psec->get_size(), segVBaseAddr, segVEndAddr ) : is_sect_in_seg( psec->get_offset(), psec->get_size(), segBaseOffset, segEndOffset ) ) { // Alignment of segment shall not be updated, to preserve original value // It will be re-calculated on saving. seg->add_section_index( psec->get_index(), 0 ); } } // Add section into the segments' container segments_.push_back( seg ); } return true; } //------------------------------------------------------------------------------ bool save_header( std::ostream& stream ) { return header->save( stream ); } //------------------------------------------------------------------------------ bool save_sections( std::ostream& stream ) { for ( unsigned int i = 0; i < sections_.size(); ++i ) { section* sec = sections_.at( i ); std::streampos headerPosition = (std::streamoff)header->get_sections_offset() + (std::streampos)header->get_section_entry_size() * sec->get_index(); sec->save( stream, headerPosition, sec->get_offset() ); } return true; } //------------------------------------------------------------------------------ bool save_segments( std::ostream& stream ) { for ( unsigned int i = 0; i < segments_.size(); ++i ) { segment* seg = segments_.at( i ); std::streampos headerPosition = header->get_segments_offset() + (std::streampos)header->get_segment_entry_size() * seg->get_index(); seg->save( stream, headerPosition, seg->get_offset() ); } return true; } //------------------------------------------------------------------------------ bool is_section_without_segment( unsigned int section_index ) { bool found = false; for ( unsigned int j = 0; !found && ( j < segments.size() ); ++j ) { for ( unsigned int k = 0; !found && ( k < segments[j]->get_sections_num() ); ++k ) { found = segments[j]->get_section_index_at( k ) == section_index; } } return !found; } //------------------------------------------------------------------------------ bool is_subsequence_of( segment* seg1, segment* seg2 ) { // Return 'true' if sections of seg1 are a subset of sections in seg2 const std::vector& sections1 = seg1->get_sections(); const std::vector& sections2 = seg2->get_sections(); bool found = false; if ( sections1.size() < sections2.size() ) { found = std::includes( sections2.begin(), sections2.end(), sections1.begin(), sections1.end() ); } return found; } //------------------------------------------------------------------------------ std::vector get_ordered_segments() { std::vector res; std::deque worklist; res.reserve( segments.size() ); std::copy( segments_.begin(), segments_.end(), std::back_inserter( worklist ) ); // Bring the segments which start at address 0 to the front size_t nextSlot = 0; for ( size_t i = 0; i < worklist.size(); ++i ) { if ( i != nextSlot && worklist[i]->is_offset_initialized() && worklist[i]->get_offset() == 0 ) { if ( worklist[nextSlot]->get_offset() == 0 ) { ++nextSlot; } std::swap( worklist[i], worklist[nextSlot] ); ++nextSlot; } } while ( !worklist.empty() ) { segment* seg = worklist.front(); worklist.pop_front(); size_t i = 0; for ( ; i < worklist.size(); ++i ) { if ( is_subsequence_of( seg, worklist[i] ) ) { break; } } if ( i < worklist.size() ) worklist.push_back( seg ); else res.push_back( seg ); } return res; } //------------------------------------------------------------------------------ bool layout_sections_without_segments() { for ( unsigned int i = 0; i < sections_.size(); ++i ) { if ( is_section_without_segment( i ) ) { section* sec = sections_[i]; Elf_Xword section_align = sec->get_addr_align(); if ( section_align > 1 && current_file_pos % section_align != 0 ) { current_file_pos += section_align - current_file_pos % section_align; } if ( 0 != sec->get_index() ) sec->set_offset( current_file_pos ); if ( SHT_NOBITS != sec->get_type() && SHT_NULL != sec->get_type() ) { current_file_pos += sec->get_size(); } } } return true; } //------------------------------------------------------------------------------ void calc_segment_alignment() { for ( std::vector::iterator s = segments_.begin(); s != segments_.end(); ++s ) { segment* seg = *s; for ( int i = 0; i < seg->get_sections_num(); ++i ) { section* sect = sections_[seg->get_section_index_at( i )]; if ( sect->get_addr_align() > seg->get_align() ) { seg->set_align( sect->get_addr_align() ); } } } } //------------------------------------------------------------------------------ bool layout_segments_and_their_sections() { std::vector worklist; std::vector section_generated( sections.size(), false ); // Get segments in a order in where segments which contain a // sub sequence of other segments are located at the end worklist = get_ordered_segments(); for ( unsigned int i = 0; i < worklist.size(); ++i ) { Elf_Xword segment_memory = 0; Elf_Xword segment_filesize = 0; Elf_Xword seg_start_pos = current_file_pos; segment* seg = worklist[i]; // Special case: PHDR segment // This segment contains the program headers but no sections if ( seg->get_type() == PT_PHDR && seg->get_sections_num() == 0 ) { seg_start_pos = header->get_segments_offset(); segment_memory = segment_filesize = header->get_segment_entry_size() * (Elf_Xword)header->get_segments_num(); } // Special case: else if ( seg->is_offset_initialized() && seg->get_offset() == 0 ) { seg_start_pos = 0; if ( seg->get_sections_num() ) { segment_memory = segment_filesize = current_file_pos; } } // New segments with not generated sections // have to be aligned else if ( seg->get_sections_num() && !section_generated[seg->get_section_index_at( 0 )] ) { Elf_Xword align = seg->get_align() > 0 ? seg->get_align() : 1; Elf64_Off cur_page_alignment = current_file_pos % align; Elf64_Off req_page_alignment = seg->get_virtual_address() % align; Elf64_Off error = req_page_alignment - cur_page_alignment; current_file_pos += ( seg->get_align() + error ) % align; seg_start_pos = current_file_pos; } else if ( seg->get_sections_num() ) { seg_start_pos = sections[seg->get_section_index_at( 0 )]->get_offset(); } // Write segment's data for ( unsigned int j = 0; j < seg->get_sections_num(); ++j ) { Elf_Half index = seg->get_section_index_at( j ); section* sec = sections[index]; // The NULL section is always generated if ( SHT_NULL == sec->get_type() ) { section_generated[index] = true; continue; } Elf_Xword secAlign = 0; // Fix up the alignment if ( !section_generated[index] && sec->is_address_initialized() && SHT_NOBITS != sec->get_type() && SHT_NULL != sec->get_type() && 0 != sec->get_size() ) { // Align the sections based on the virtual addresses // when possible (this is what matters for execution) Elf64_Off req_offset = sec->get_address() - seg->get_virtual_address(); Elf64_Off cur_offset = current_file_pos - seg_start_pos; if ( req_offset < cur_offset ) { // something has gone awfully wrong, abort! // secAlign would turn out negative, seeking backwards and overwriting previous data return false; } secAlign = req_offset - cur_offset; } else if ( !section_generated[index] && !sec->is_address_initialized() ) { // If no address has been specified then only the section // alignment constraint has to be matched Elf_Xword align = sec->get_addr_align(); if ( align == 0 ) { align = 1; } Elf64_Off error = current_file_pos % align; secAlign = ( align - error ) % align; } else if ( section_generated[index] ) { // Alignment for already generated sections secAlign = sec->get_offset() - seg_start_pos - segment_filesize; } // Determine the segment file and memory sizes // Special case .tbss section (NOBITS) in non TLS segment if ( ( sec->get_flags() & SHF_ALLOC ) && !( ( sec->get_flags() & SHF_TLS ) && ( seg->get_type() != PT_TLS ) && ( SHT_NOBITS == sec->get_type() ) ) ) segment_memory += sec->get_size() + secAlign; if ( SHT_NOBITS != sec->get_type() ) segment_filesize += sec->get_size() + secAlign; // Nothing to be done when generating nested segments if ( section_generated[index] ) { continue; } current_file_pos += secAlign; // Set the section addresses when missing if ( !sec->is_address_initialized() ) sec->set_address( seg->get_virtual_address() + current_file_pos - seg_start_pos ); if ( 0 != sec->get_index() ) sec->set_offset( current_file_pos ); if ( SHT_NOBITS != sec->get_type() ) current_file_pos += sec->get_size(); section_generated[index] = true; } seg->set_file_size( segment_filesize ); // If we already have a memory size from loading an elf file (value > 0), // it must not shrink! // Memory size may be bigger than file size and it is the loader's job to do something // with the surplus bytes in memory, like initializing them with a defined value. if ( seg->get_memory_size() < segment_memory ) { seg->set_memory_size( segment_memory ); } seg->set_offset( seg_start_pos ); } return true; } //------------------------------------------------------------------------------ bool layout_section_table() { // Simply place the section table at the end for now Elf64_Off alignmentError = current_file_pos % 4; current_file_pos += ( 4 - alignmentError ) % 4; header->set_sections_offset( current_file_pos ); return true; } //------------------------------------------------------------------------------ public: friend class Sections; class Sections { public: //------------------------------------------------------------------------------ Sections( elfio* parent_ ) : parent( parent_ ) {} //------------------------------------------------------------------------------ Elf_Half size() const { return (Elf_Half)parent->sections_.size(); } //------------------------------------------------------------------------------ section* operator[]( unsigned int index ) const { section* sec = 0; if ( index < parent->sections_.size() ) { sec = parent->sections_[index]; } return sec; } //------------------------------------------------------------------------------ section* operator[]( const std::string& name ) const { section* sec = 0; std::vector::const_iterator it; for ( it = parent->sections_.begin(); it != parent->sections_.end(); ++it ) { if ( ( *it )->get_name() == name ) { sec = *it; break; } } return sec; } //------------------------------------------------------------------------------ section* add( const std::string& name ) { section* new_section = parent->create_section(); new_section->set_name( name ); Elf_Half str_index = parent->get_section_name_str_index(); section* string_table( parent->sections_[str_index] ); string_section_accessor str_writer( string_table ); Elf_Word pos = str_writer.add_string( name ); new_section->set_name_string_offset( pos ); return new_section; } //------------------------------------------------------------------------------ std::vector::iterator begin() { return parent->sections_.begin(); } //------------------------------------------------------------------------------ std::vector::iterator end() { return parent->sections_.end(); } //------------------------------------------------------------------------------ std::vector::const_iterator begin() const { return parent->sections_.cbegin(); } //------------------------------------------------------------------------------ std::vector::const_iterator end() const { return parent->sections_.cend(); } //------------------------------------------------------------------------------ private: elfio* parent; } sections; //------------------------------------------------------------------------------ public: friend class Segments; class Segments { public: //------------------------------------------------------------------------------ Segments( elfio* parent_ ) : parent( parent_ ) {} //------------------------------------------------------------------------------ Elf_Half size() const { return (Elf_Half)parent->segments_.size(); } //------------------------------------------------------------------------------ segment* operator[]( unsigned int index ) const { return parent->segments_[index]; } //------------------------------------------------------------------------------ segment* add() { return parent->create_segment(); } //------------------------------------------------------------------------------ std::vector::iterator begin() { return parent->segments_.begin(); } //------------------------------------------------------------------------------ std::vector::iterator end() { return parent->segments_.end(); } //------------------------------------------------------------------------------ std::vector::const_iterator begin() const { return parent->segments_.cbegin(); } //------------------------------------------------------------------------------ std::vector::const_iterator end() const { return parent->segments_.cend(); } //------------------------------------------------------------------------------ private: elfio* parent; } segments; //------------------------------------------------------------------------------ private: elf_header* header; std::vector sections_; std::vector segments_; endianess_convertor convertor; Elf_Xword current_file_pos; }; } // namespace ELFIO /*** Start of inlined file: elfio_symbols.hpp ***/ #ifndef ELFIO_SYMBOLS_HPP #define ELFIO_SYMBOLS_HPP namespace ELFIO { //------------------------------------------------------------------------------ template class symbol_section_accessor_template { public: //------------------------------------------------------------------------------ symbol_section_accessor_template( const elfio& elf_file_, S* symbol_section_ ) : elf_file( elf_file_ ), symbol_section( symbol_section_ ) { find_hash_section(); } //------------------------------------------------------------------------------ Elf_Xword get_symbols_num() const { Elf_Xword nRet = 0; if ( 0 != symbol_section->get_entry_size() ) { nRet = symbol_section->get_size() / symbol_section->get_entry_size(); } return nRet; } //------------------------------------------------------------------------------ bool get_symbol( Elf_Xword index, std::string& name, Elf64_Addr& value, Elf_Xword& size, unsigned char& bind, unsigned char& type, Elf_Half& section_index, unsigned char& other ) const { bool ret = false; if ( elf_file.get_class() == ELFCLASS32 ) { ret = generic_get_symbol( index, name, value, size, bind, type, section_index, other ); } else { ret = generic_get_symbol( index, name, value, size, bind, type, section_index, other ); } return ret; } //------------------------------------------------------------------------------ bool get_symbol( const std::string& name, Elf64_Addr& value, Elf_Xword& size, unsigned char& bind, unsigned char& type, Elf_Half& section_index, unsigned char& other ) const { bool ret = false; if ( 0 != get_hash_table_index() ) { Elf_Word nbucket = *(const Elf_Word*)hash_section->get_data(); Elf_Word nchain = *(const Elf_Word*)( hash_section->get_data() + sizeof( Elf_Word ) ); Elf_Word val = elf_hash( (const unsigned char*)name.c_str() ); Elf_Word y = *(const Elf_Word*)( hash_section->get_data() + ( 2 + val % nbucket ) * sizeof( Elf_Word ) ); std::string str; get_symbol( y, str, value, size, bind, type, section_index, other ); while ( str != name && STN_UNDEF != y && y < nchain ) { y = *(const Elf_Word*)( hash_section->get_data() + ( 2 + nbucket + y ) * sizeof( Elf_Word ) ); get_symbol( y, str, value, size, bind, type, section_index, other ); } if ( str == name ) { ret = true; } } else { for ( Elf_Xword i = 0; i < get_symbols_num() && !ret; i++ ) { std::string symbol_name; if ( get_symbol( i, symbol_name, value, size, bind, type, section_index, other ) ) { if ( symbol_name == name ) { ret = true; } } } } return ret; } //------------------------------------------------------------------------------ bool get_symbol( const Elf64_Addr& value, std::string& name, Elf_Xword& size, unsigned char& bind, unsigned char& type, Elf_Half& section_index, unsigned char& other ) const { const endianess_convertor& convertor = elf_file.get_convertor(); Elf_Xword idx = 0; bool match = false; Elf64_Addr v = 0; if ( elf_file.get_class() == ELFCLASS32 ) { match = generic_search_symbols( [&]( const Elf32_Sym* sym ) { return convertor( sym->st_value ) == value; }, idx ); } else { match = generic_search_symbols( [&]( const Elf64_Sym* sym ) { return convertor( sym->st_value ) == value; }, idx ); } if ( match ) { return get_symbol( idx, name, v, size, bind, type, section_index, other ); } return false; } //------------------------------------------------------------------------------ Elf_Word add_symbol( Elf_Word name, Elf64_Addr value, Elf_Xword size, unsigned char info, unsigned char other, Elf_Half shndx ) { Elf_Word nRet; if ( symbol_section->get_size() == 0 ) { if ( elf_file.get_class() == ELFCLASS32 ) { nRet = generic_add_symbol( 0, 0, 0, 0, 0, 0 ); } else { nRet = generic_add_symbol( 0, 0, 0, 0, 0, 0 ); } } if ( elf_file.get_class() == ELFCLASS32 ) { nRet = generic_add_symbol( name, value, size, info, other, shndx ); } else { nRet = generic_add_symbol( name, value, size, info, other, shndx ); } return nRet; } //------------------------------------------------------------------------------ Elf_Word add_symbol( Elf_Word name, Elf64_Addr value, Elf_Xword size, unsigned char bind, unsigned char type, unsigned char other, Elf_Half shndx ) { return add_symbol( name, value, size, ELF_ST_INFO( bind, type ), other, shndx ); } //------------------------------------------------------------------------------ Elf_Word add_symbol( string_section_accessor& pStrWriter, const char* str, Elf64_Addr value, Elf_Xword size, unsigned char info, unsigned char other, Elf_Half shndx ) { Elf_Word index = pStrWriter.add_string( str ); return add_symbol( index, value, size, info, other, shndx ); } //------------------------------------------------------------------------------ Elf_Word add_symbol( string_section_accessor& pStrWriter, const char* str, Elf64_Addr value, Elf_Xword size, unsigned char bind, unsigned char type, unsigned char other, Elf_Half shndx ) { return add_symbol( pStrWriter, str, value, size, ELF_ST_INFO( bind, type ), other, shndx ); } //------------------------------------------------------------------------------ Elf_Xword arrange_local_symbols( std::function func = nullptr ) { int nRet = 0; if ( elf_file.get_class() == ELFCLASS32 ) { nRet = generic_arrange_local_symbols( func ); } else { nRet = generic_arrange_local_symbols( func ); } return nRet; } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ void find_hash_section() { hash_section = 0; hash_section_index = 0; Elf_Half nSecNo = elf_file.sections.size(); for ( Elf_Half i = 0; i < nSecNo && 0 == hash_section_index; ++i ) { const section* sec = elf_file.sections[i]; if ( sec->get_link() == symbol_section->get_index() ) { hash_section = sec; hash_section_index = i; } } } //------------------------------------------------------------------------------ Elf_Half get_string_table_index() const { return (Elf_Half)symbol_section->get_link(); } //------------------------------------------------------------------------------ Elf_Half get_hash_table_index() const { return hash_section_index; } //------------------------------------------------------------------------------ template const T* generic_get_symbol_ptr( Elf_Xword index ) const { if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) { const T* pSym = reinterpret_cast( symbol_section->get_data() + index * symbol_section->get_entry_size() ); return pSym; } return nullptr; } //------------------------------------------------------------------------------ template bool generic_search_symbols( std::function match, Elf_Xword& idx ) const { for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) { const T* symPtr = generic_get_symbol_ptr( i ); if ( symPtr == nullptr ) return false; if ( match( symPtr ) ) { idx = i; return true; } } return false; } //------------------------------------------------------------------------------ template bool generic_get_symbol( Elf_Xword index, std::string& name, Elf64_Addr& value, Elf_Xword& size, unsigned char& bind, unsigned char& type, Elf_Half& section_index, unsigned char& other ) const { bool ret = false; if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) { const T* pSym = reinterpret_cast( symbol_section->get_data() + index * symbol_section->get_entry_size() ); const endianess_convertor& convertor = elf_file.get_convertor(); section* string_section = elf_file.sections[get_string_table_index()]; string_section_accessor str_reader( string_section ); const char* pStr = str_reader.get_string( convertor( pSym->st_name ) ); if ( 0 != pStr ) { name = pStr; } value = convertor( pSym->st_value ); size = convertor( pSym->st_size ); bind = ELF_ST_BIND( pSym->st_info ); type = ELF_ST_TYPE( pSym->st_info ); section_index = convertor( pSym->st_shndx ); other = pSym->st_other; ret = true; } return ret; } //------------------------------------------------------------------------------ template Elf_Word generic_add_symbol( Elf_Word name, Elf64_Addr value, Elf_Xword size, unsigned char info, unsigned char other, Elf_Half shndx ) { const endianess_convertor& convertor = elf_file.get_convertor(); T entry; entry.st_name = convertor( name ); entry.st_value = value; entry.st_value = convertor( entry.st_value ); entry.st_size = size; entry.st_size = convertor( entry.st_size ); entry.st_info = convertor( info ); entry.st_other = convertor( other ); entry.st_shndx = convertor( shndx ); symbol_section->append_data( reinterpret_cast( &entry ), sizeof( entry ) ); Elf_Word nRet = symbol_section->get_size() / sizeof( entry ) - 1; return nRet; } //------------------------------------------------------------------------------ template Elf_Xword generic_arrange_local_symbols( std::function func ) { const endianess_convertor& convertor = elf_file.get_convertor(); const Elf_Xword size = symbol_section->get_entry_size(); Elf_Xword first_not_local = 1; // Skip the first entry. It is always NOTYPE Elf_Xword current = 0; Elf_Xword count = get_symbols_num(); while ( true ) { T* p1 = nullptr; T* p2 = nullptr; while ( first_not_local < count ) { p1 = const_cast( generic_get_symbol_ptr( first_not_local ) ); if ( ELF_ST_BIND( convertor( p1->st_info ) ) != STB_LOCAL ) break; ++first_not_local; } current = first_not_local + 1; while ( current < count ) { p2 = const_cast( generic_get_symbol_ptr( current ) ); if ( ELF_ST_BIND( convertor( p2->st_info ) ) == STB_LOCAL ) break; ++current; } if ( first_not_local < count && current < count ) { if ( func ) func( first_not_local, current ); // Swap the symbols T tmp; std::copy( p1, p1 + 1, &tmp ); std::copy( p2, p2 + 1, p1 ); std::copy( &tmp, &tmp + 1, p2 ); } else { // Update 'info' field of the section symbol_section->set_info( first_not_local ); break; } } // Elf_Word nRet = symbol_section->get_size() / sizeof(entry) - 1; return first_not_local; } //------------------------------------------------------------------------------ private: const elfio& elf_file; S* symbol_section; Elf_Half hash_section_index; const section* hash_section; }; using symbol_section_accessor = symbol_section_accessor_template
; using const_symbol_section_accessor = symbol_section_accessor_template; } // namespace ELFIO #endif // ELFIO_SYMBOLS_HPP /*** End of inlined file: elfio_symbols.hpp ***/ /*** Start of inlined file: elfio_note.hpp ***/ #ifndef ELFIO_NOTE_HPP #define ELFIO_NOTE_HPP namespace ELFIO { //------------------------------------------------------------------------------ // There are discrepancies in documentations. SCO documentation // (http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section) // requires 8 byte entries alignment for 64-bit ELF file, // but Oracle's definition uses the same structure // for 32-bit and 64-bit formats. // (https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-18048.html) // // It looks like EM_X86_64 Linux implementation is similar to Oracle's // definition. Therefore, the same alignment works for both formats //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ template class note_section_accessor_template { public: //------------------------------------------------------------------------------ note_section_accessor_template( const elfio& elf_file_, S* section_ ) : elf_file( elf_file_ ), note_section( section_ ) { process_section(); } //------------------------------------------------------------------------------ Elf_Word get_notes_num() const { return (Elf_Word)note_start_positions.size(); } //------------------------------------------------------------------------------ bool get_note( Elf_Word index, Elf_Word& type, std::string& name, void*& desc, Elf_Word& descSize ) const { if ( index >= note_section->get_size() ) { return false; } const char* pData = note_section->get_data() + note_start_positions[index]; int align = sizeof( Elf_Word ); const endianess_convertor& convertor = elf_file.get_convertor(); type = convertor( *(const Elf_Word*)( pData + 2 * align ) ); Elf_Word namesz = convertor( *(const Elf_Word*)( pData ) ); descSize = convertor( *(const Elf_Word*)( pData + sizeof( namesz ) ) ); Elf_Xword max_name_size = note_section->get_size() - note_start_positions[index]; if ( namesz < 1 || namesz > max_name_size || (Elf_Xword)namesz + descSize > max_name_size ) { return false; } name.assign( pData + 3 * align, namesz - 1 ); if ( 0 == descSize ) { desc = 0; } else { desc = const_cast( pData + 3 * align + ( ( namesz + align - 1 ) / align ) * align ); } return true; } //------------------------------------------------------------------------------ void add_note( Elf_Word type, const std::string& name, const void* desc, Elf_Word descSize ) { const endianess_convertor& convertor = elf_file.get_convertor(); int align = sizeof( Elf_Word ); Elf_Word nameLen = (Elf_Word)name.size() + 1; Elf_Word nameLenConv = convertor( nameLen ); std::string buffer( reinterpret_cast( &nameLenConv ), align ); Elf_Word descSizeConv = convertor( descSize ); buffer.append( reinterpret_cast( &descSizeConv ), align ); type = convertor( type ); buffer.append( reinterpret_cast( &type ), align ); buffer.append( name ); buffer.append( 1, '\x00' ); const char pad[] = { '\0', '\0', '\0', '\0' }; if ( nameLen % align != 0 ) { buffer.append( pad, align - nameLen % align ); } if ( desc != 0 && descSize != 0 ) { buffer.append( reinterpret_cast( desc ), descSize ); if ( descSize % align != 0 ) { buffer.append( pad, align - descSize % align ); } } note_start_positions.push_back( note_section->get_size() ); note_section->append_data( buffer ); } private: //------------------------------------------------------------------------------ void process_section() { const endianess_convertor& convertor = elf_file.get_convertor(); const char* data = note_section->get_data(); Elf_Xword size = note_section->get_size(); Elf_Xword current = 0; note_start_positions.clear(); // Is it empty? if ( 0 == data || 0 == size ) { return; } Elf_Word align = sizeof( Elf_Word ); while ( current + (Elf_Xword)3 * align <= size ) { note_start_positions.push_back( current ); Elf_Word namesz = convertor( *(const Elf_Word*)( data + current ) ); Elf_Word descsz = convertor( *(const Elf_Word*)( data + current + sizeof( namesz ) ) ); current += (Elf_Xword)3 * sizeof( Elf_Word ) + ( ( namesz + align - 1 ) / align ) * (Elf_Xword)align + ( ( descsz + align - 1 ) / align ) * (Elf_Xword)align; } } //------------------------------------------------------------------------------ private: const elfio& elf_file; S* note_section; std::vector note_start_positions; }; using note_section_accessor = note_section_accessor_template
; using const_note_section_accessor = note_section_accessor_template; } // namespace ELFIO #endif // ELFIO_NOTE_HPP /*** End of inlined file: elfio_note.hpp ***/ /*** Start of inlined file: elfio_relocation.hpp ***/ #ifndef ELFIO_RELOCATION_HPP #define ELFIO_RELOCATION_HPP namespace ELFIO { template struct get_sym_and_type; template <> struct get_sym_and_type { static int get_r_sym( Elf_Xword info ) { return ELF32_R_SYM( (Elf_Word)info ); } static int get_r_type( Elf_Xword info ) { return ELF32_R_TYPE( (Elf_Word)info ); } }; template <> struct get_sym_and_type { static int get_r_sym( Elf_Xword info ) { return ELF32_R_SYM( (Elf_Word)info ); } static int get_r_type( Elf_Xword info ) { return ELF32_R_TYPE( (Elf_Word)info ); } }; template <> struct get_sym_and_type { static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } }; template <> struct get_sym_and_type { static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } }; //------------------------------------------------------------------------------ template class relocation_section_accessor_template { public: //------------------------------------------------------------------------------ relocation_section_accessor_template( const elfio& elf_file_, S* section_ ) : elf_file( elf_file_ ), relocation_section( section_ ) { } //------------------------------------------------------------------------------ Elf_Xword get_entries_num() const { Elf_Xword nRet = 0; if ( 0 != relocation_section->get_entry_size() ) { nRet = relocation_section->get_size() / relocation_section->get_entry_size(); } return nRet; } //------------------------------------------------------------------------------ bool get_entry( Elf_Xword index, Elf64_Addr& offset, Elf_Word& symbol, Elf_Word& type, Elf_Sxword& addend ) const { if ( index >= get_entries_num() ) { // Is index valid return false; } if ( elf_file.get_class() == ELFCLASS32 ) { if ( SHT_REL == relocation_section->get_type() ) { generic_get_entry_rel( index, offset, symbol, type, addend ); } else if ( SHT_RELA == relocation_section->get_type() ) { generic_get_entry_rela( index, offset, symbol, type, addend ); } } else { if ( SHT_REL == relocation_section->get_type() ) { generic_get_entry_rel( index, offset, symbol, type, addend ); } else if ( SHT_RELA == relocation_section->get_type() ) { generic_get_entry_rela( index, offset, symbol, type, addend ); } } return true; } //------------------------------------------------------------------------------ bool get_entry( Elf_Xword index, Elf64_Addr& offset, Elf64_Addr& symbolValue, std::string& symbolName, Elf_Word& type, Elf_Sxword& addend, Elf_Sxword& calcValue ) const { // Do regular job Elf_Word symbol; bool ret = get_entry( index, offset, symbol, type, addend ); // Find the symbol Elf_Xword size; unsigned char bind; unsigned char symbolType; Elf_Half section; unsigned char other; symbol_section_accessor symbols( elf_file, elf_file.sections[get_symbol_table_index()] ); ret = ret && symbols.get_symbol( symbol, symbolName, symbolValue, size, bind, symbolType, section, other ); if ( ret ) { // Was it successful? switch ( type ) { case R_386_NONE: // none calcValue = 0; break; case R_386_32: // S + A calcValue = symbolValue + addend; break; case R_386_PC32: // S + A - P calcValue = symbolValue + addend - offset; break; case R_386_GOT32: // G + A - P calcValue = 0; break; case R_386_PLT32: // L + A - P calcValue = 0; break; case R_386_COPY: // none calcValue = 0; break; case R_386_GLOB_DAT: // S case R_386_JMP_SLOT: // S calcValue = symbolValue; break; case R_386_RELATIVE: // B + A calcValue = addend; break; case R_386_GOTOFF: // S + A - GOT calcValue = 0; break; case R_386_GOTPC: // GOT + A - P calcValue = 0; break; default: // Not recognized symbol! calcValue = 0; break; } } return ret; } //------------------------------------------------------------------------------ bool set_entry( Elf_Xword index, Elf64_Addr offset, Elf_Word symbol, Elf_Word type, Elf_Sxword addend ) { if ( index >= get_entries_num() ) { // Is index valid return false; } if ( elf_file.get_class() == ELFCLASS32 ) { if ( SHT_REL == relocation_section->get_type() ) { generic_set_entry_rel( index, offset, symbol, type, addend ); } else if ( SHT_RELA == relocation_section->get_type() ) { generic_set_entry_rela( index, offset, symbol, type, addend ); } } else { if ( SHT_REL == relocation_section->get_type() ) { generic_set_entry_rel( index, offset, symbol, type, addend ); } else if ( SHT_RELA == relocation_section->get_type() ) { generic_set_entry_rela( index, offset, symbol, type, addend ); } } return true; } //------------------------------------------------------------------------------ void add_entry( Elf64_Addr offset, Elf_Xword info ) { if ( elf_file.get_class() == ELFCLASS32 ) { generic_add_entry( offset, info ); } else { generic_add_entry( offset, info ); } } //------------------------------------------------------------------------------ void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned char type ) { Elf_Xword info; if ( elf_file.get_class() == ELFCLASS32 ) { info = ELF32_R_INFO( (Elf_Xword)symbol, type ); } else { info = ELF64_R_INFO( (Elf_Xword)symbol, type ); } add_entry( offset, info ); } //------------------------------------------------------------------------------ void add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) { if ( elf_file.get_class() == ELFCLASS32 ) { generic_add_entry( offset, info, addend ); } else { generic_add_entry( offset, info, addend ); } } //------------------------------------------------------------------------------ void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned char type, Elf_Sxword addend ) { Elf_Xword info; if ( elf_file.get_class() == ELFCLASS32 ) { info = ELF32_R_INFO( (Elf_Xword)symbol, type ); } else { info = ELF64_R_INFO( (Elf_Xword)symbol, type ); } add_entry( offset, info, addend ); } //------------------------------------------------------------------------------ void add_entry( string_section_accessor str_writer, const char* str, symbol_section_accessor sym_writer, Elf64_Addr value, Elf_Word size, unsigned char sym_info, unsigned char other, Elf_Half shndx, Elf64_Addr offset, unsigned char type ) { Elf_Word str_index = str_writer.add_string( str ); Elf_Word sym_index = sym_writer.add_symbol( str_index, value, size, sym_info, other, shndx ); add_entry( offset, sym_index, type ); } //------------------------------------------------------------------------------ void swap_symbols( Elf_Xword first, Elf_Xword second ) { Elf64_Addr offset; Elf_Word symbol; Elf_Word rtype; Elf_Sxword addend; for ( Elf_Word i = 0; i < get_entries_num(); i++ ) { get_entry( i, offset, symbol, rtype, addend ); if ( symbol == first ) { set_entry( i, offset, (Elf_Word)second, rtype, addend ); } if ( symbol == second ) { set_entry( i, offset, (Elf_Word)first, rtype, addend ); } } } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ Elf_Half get_symbol_table_index() const { return (Elf_Half)relocation_section->get_link(); } //------------------------------------------------------------------------------ template void generic_get_entry_rel( Elf_Xword index, Elf64_Addr& offset, Elf_Word& symbol, Elf_Word& type, Elf_Sxword& addend ) const { const endianess_convertor& convertor = elf_file.get_convertor(); const T* pEntry = reinterpret_cast( relocation_section->get_data() + index * relocation_section->get_entry_size() ); offset = convertor( pEntry->r_offset ); Elf_Xword tmp = convertor( pEntry->r_info ); symbol = get_sym_and_type::get_r_sym( tmp ); type = get_sym_and_type::get_r_type( tmp ); addend = 0; } //------------------------------------------------------------------------------ template void generic_get_entry_rela( Elf_Xword index, Elf64_Addr& offset, Elf_Word& symbol, Elf_Word& type, Elf_Sxword& addend ) const { const endianess_convertor& convertor = elf_file.get_convertor(); const T* pEntry = reinterpret_cast( relocation_section->get_data() + index * relocation_section->get_entry_size() ); offset = convertor( pEntry->r_offset ); Elf_Xword tmp = convertor( pEntry->r_info ); symbol = get_sym_and_type::get_r_sym( tmp ); type = get_sym_and_type::get_r_type( tmp ); addend = convertor( pEntry->r_addend ); } //------------------------------------------------------------------------------ template void generic_set_entry_rel( Elf_Xword index, Elf64_Addr offset, Elf_Word symbol, Elf_Word type, Elf_Sxword ) { const endianess_convertor& convertor = elf_file.get_convertor(); T* pEntry = const_cast( reinterpret_cast( relocation_section->get_data() + index * relocation_section->get_entry_size() ) ); if ( elf_file.get_class() == ELFCLASS32 ) { pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); } else { pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); } pEntry->r_offset = offset; pEntry->r_offset = convertor( pEntry->r_offset ); pEntry->r_info = convertor( pEntry->r_info ); } //------------------------------------------------------------------------------ template void generic_set_entry_rela( Elf_Xword index, Elf64_Addr offset, Elf_Word symbol, Elf_Word type, Elf_Sxword addend ) { const endianess_convertor& convertor = elf_file.get_convertor(); T* pEntry = const_cast( reinterpret_cast( relocation_section->get_data() + index * relocation_section->get_entry_size() ) ); if ( elf_file.get_class() == ELFCLASS32 ) { pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); } else { pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); } pEntry->r_offset = offset; pEntry->r_addend = addend; pEntry->r_offset = convertor( pEntry->r_offset ); pEntry->r_info = convertor( pEntry->r_info ); pEntry->r_addend = convertor( pEntry->r_addend ); } //------------------------------------------------------------------------------ template void generic_add_entry( Elf64_Addr offset, Elf_Xword info ) { const endianess_convertor& convertor = elf_file.get_convertor(); T entry; entry.r_offset = offset; entry.r_info = info; entry.r_offset = convertor( entry.r_offset ); entry.r_info = convertor( entry.r_info ); relocation_section->append_data( reinterpret_cast( &entry ), sizeof( entry ) ); } //------------------------------------------------------------------------------ template void generic_add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) { const endianess_convertor& convertor = elf_file.get_convertor(); T entry; entry.r_offset = offset; entry.r_info = info; entry.r_addend = addend; entry.r_offset = convertor( entry.r_offset ); entry.r_info = convertor( entry.r_info ); entry.r_addend = convertor( entry.r_addend ); relocation_section->append_data( reinterpret_cast( &entry ), sizeof( entry ) ); } //------------------------------------------------------------------------------ private: const elfio& elf_file; S* relocation_section; }; using relocation_section_accessor = relocation_section_accessor_template
; using const_relocation_section_accessor = relocation_section_accessor_template; } // namespace ELFIO #endif // ELFIO_RELOCATION_HPP /*** End of inlined file: elfio_relocation.hpp ***/ /*** Start of inlined file: elfio_dynamic.hpp ***/ #ifndef ELFIO_DYNAMIC_HPP #define ELFIO_DYNAMIC_HPP namespace ELFIO { //------------------------------------------------------------------------------ template class dynamic_section_accessor_template { public: //------------------------------------------------------------------------------ dynamic_section_accessor_template( const elfio& elf_file_, S* section_ ) : elf_file( elf_file_ ), dynamic_section( section_ ) { } //------------------------------------------------------------------------------ Elf_Xword get_entries_num() const { Elf_Xword nRet = 0; if ( 0 != dynamic_section->get_entry_size() ) { nRet = dynamic_section->get_size() / dynamic_section->get_entry_size(); } return nRet; } //------------------------------------------------------------------------------ bool get_entry( Elf_Xword index, Elf_Xword& tag, Elf_Xword& value, std::string& str ) const { if ( index >= get_entries_num() ) { // Is index valid return false; } if ( elf_file.get_class() == ELFCLASS32 ) { generic_get_entry_dyn( index, tag, value ); } else { generic_get_entry_dyn( index, tag, value ); } // If the tag may have a string table reference, prepare the string if ( tag == DT_NEEDED || tag == DT_SONAME || tag == DT_RPATH || tag == DT_RUNPATH ) { string_section_accessor strsec = elf_file.sections[get_string_table_index()]; const char* result = strsec.get_string( value ); if ( 0 == result ) { str.clear(); return false; } str = result; } else { str.clear(); } return true; } //------------------------------------------------------------------------------ void add_entry( Elf_Xword tag, Elf_Xword value ) { if ( elf_file.get_class() == ELFCLASS32 ) { generic_add_entry( tag, value ); } else { generic_add_entry( tag, value ); } } //------------------------------------------------------------------------------ void add_entry( Elf_Xword tag, const std::string& str ) { string_section_accessor strsec = elf_file.sections[get_string_table_index()]; Elf_Xword value = strsec.add_string( str ); add_entry( tag, value ); } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ Elf_Half get_string_table_index() const { return (Elf_Half)dynamic_section->get_link(); } //------------------------------------------------------------------------------ template void generic_get_entry_dyn( Elf_Xword index, Elf_Xword& tag, Elf_Xword& value ) const { const endianess_convertor& convertor = elf_file.get_convertor(); // Check unusual case when dynamic section has no data if ( dynamic_section->get_data() == 0 || ( index + 1 ) * dynamic_section->get_entry_size() > dynamic_section->get_size() ) { tag = DT_NULL; value = 0; return; } const T* pEntry = reinterpret_cast( dynamic_section->get_data() + index * dynamic_section->get_entry_size() ); tag = convertor( pEntry->d_tag ); switch ( tag ) { case DT_NULL: case DT_SYMBOLIC: case DT_TEXTREL: case DT_BIND_NOW: value = 0; break; case DT_NEEDED: case DT_PLTRELSZ: case DT_RELASZ: case DT_RELAENT: case DT_STRSZ: case DT_SYMENT: case DT_SONAME: case DT_RPATH: case DT_RELSZ: case DT_RELENT: case DT_PLTREL: case DT_INIT_ARRAYSZ: case DT_FINI_ARRAYSZ: case DT_RUNPATH: case DT_FLAGS: case DT_PREINIT_ARRAYSZ: value = convertor( pEntry->d_un.d_val ); break; case DT_PLTGOT: case DT_HASH: case DT_STRTAB: case DT_SYMTAB: case DT_RELA: case DT_INIT: case DT_FINI: case DT_REL: case DT_DEBUG: case DT_JMPREL: case DT_INIT_ARRAY: case DT_FINI_ARRAY: case DT_PREINIT_ARRAY: default: value = convertor( pEntry->d_un.d_ptr ); break; } } //------------------------------------------------------------------------------ template void generic_add_entry( Elf_Xword tag, Elf_Xword value ) { const endianess_convertor& convertor = elf_file.get_convertor(); T entry; switch ( tag ) { case DT_NULL: case DT_SYMBOLIC: case DT_TEXTREL: case DT_BIND_NOW: value = 0; case DT_NEEDED: case DT_PLTRELSZ: case DT_RELASZ: case DT_RELAENT: case DT_STRSZ: case DT_SYMENT: case DT_SONAME: case DT_RPATH: case DT_RELSZ: case DT_RELENT: case DT_PLTREL: case DT_INIT_ARRAYSZ: case DT_FINI_ARRAYSZ: case DT_RUNPATH: case DT_FLAGS: case DT_PREINIT_ARRAYSZ: entry.d_un.d_val = convertor( value ); break; case DT_PLTGOT: case DT_HASH: case DT_STRTAB: case DT_SYMTAB: case DT_RELA: case DT_INIT: case DT_FINI: case DT_REL: case DT_DEBUG: case DT_JMPREL: case DT_INIT_ARRAY: case DT_FINI_ARRAY: case DT_PREINIT_ARRAY: default: entry.d_un.d_ptr = convertor( value ); break; } entry.d_tag = convertor( tag ); dynamic_section->append_data( reinterpret_cast( &entry ), sizeof( entry ) ); } //------------------------------------------------------------------------------ private: const elfio& elf_file; S* dynamic_section; }; using dynamic_section_accessor = dynamic_section_accessor_template
; using const_dynamic_section_accessor = dynamic_section_accessor_template; } // namespace ELFIO #endif // ELFIO_DYNAMIC_HPP /*** End of inlined file: elfio_dynamic.hpp ***/ /*** Start of inlined file: elfio_modinfo.hpp ***/ #ifndef ELFIO_MODINFO_HPP #define ELFIO_MODINFO_HPP #include #include namespace ELFIO { //------------------------------------------------------------------------------ template class modinfo_section_accessor_template { public: //------------------------------------------------------------------------------ modinfo_section_accessor_template( S* section_ ) : modinfo_section( section_ ) { process_section(); } //------------------------------------------------------------------------------ Elf_Word get_attribute_num() const { return (Elf_Word)content.size(); } //------------------------------------------------------------------------------ bool get_attribute( Elf_Word no, std::string& field, std::string& value ) const { if ( no < content.size() ) { field = content[no].first; value = content[no].second; return true; } return false; } //------------------------------------------------------------------------------ bool get_attribute( std::string field_name, std::string& value ) const { for ( auto i = content.begin(); i != content.end(); i++ ) { if ( field_name == i->first ) { value = i->second; return true; } } return false; } //------------------------------------------------------------------------------ Elf_Word add_attribute( std::string field, std::string value ) { Elf_Word current_position = 0; if ( modinfo_section ) { // Strings are addeded to the end of the current section data current_position = (Elf_Word)modinfo_section->get_size(); std::string attribute = field + "=" + value; modinfo_section->append_data( attribute + '\0' ); content.push_back( std::pair( field, value ) ); } return current_position; } //------------------------------------------------------------------------------ private: void process_section() { const char* pdata = modinfo_section->get_data(); if ( pdata ) { ELFIO::Elf_Xword i = 0; while ( i < modinfo_section->get_size() ) { while ( i < modinfo_section->get_size() && !pdata[i] ) i++; if ( i < modinfo_section->get_size() ) { std::string info = pdata + i; size_t loc = info.find( '=' ); std::pair attribute( info.substr( 0, loc ), info.substr( loc + 1 ) ); content.push_back( attribute ); i += info.length(); } } } } //------------------------------------------------------------------------------ private: S* modinfo_section; std::vector> content; }; using modinfo_section_accessor = modinfo_section_accessor_template
; using const_modinfo_section_accessor = modinfo_section_accessor_template; } // namespace ELFIO #endif // ELFIO_MODINFO_HPP /*** End of inlined file: elfio_modinfo.hpp ***/ #ifdef _MSC_VER #pragma warning( pop ) #endif #endif // ELFIO_HPP /*** End of inlined file: elfio.hpp ***/ namespace ELFIO { static struct class_table_t { const char key; const char* str; } class_table[] = { { ELFCLASS32, "ELF32" }, { ELFCLASS64, "ELF64" }, }; static struct endian_table_t { const char key; const char* str; } endian_table[] = { { ELFDATANONE, "None" }, { ELFDATA2LSB, "Little endian" }, { ELFDATA2MSB, "Big endian" }, }; static struct version_table_t { const Elf64_Word key; const char* str; } version_table[] = { { EV_NONE, "None" }, { EV_CURRENT, "Current" }, }; static struct type_table_t { const Elf32_Half key; const char* str; } type_table[] = { { ET_NONE, "No file type" }, { ET_REL, "Relocatable file" }, { ET_EXEC, "Executable file" }, { ET_DYN, "Shared object file" }, { ET_CORE, "Core file" }, }; static struct machine_table_t { const Elf64_Half key; const char* str; } machine_table[] = { { EM_NONE, "No machine" }, { EM_M32, "AT&T WE 32100" }, { EM_SPARC, "SUN SPARC" }, { EM_386, "Intel 80386" }, { EM_68K, "Motorola m68k family" }, { EM_88K, "Motorola m88k family" }, { EM_486, "Intel 80486// Reserved for future use" }, { EM_860, "Intel 80860" }, { EM_MIPS, "MIPS R3000 (officially, big-endian only)" }, { EM_S370, "IBM System/370" }, { EM_MIPS_RS3_LE, "MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated" }, { EM_res011, "Reserved" }, { EM_res012, "Reserved" }, { EM_res013, "Reserved" }, { EM_res014, "Reserved" }, { EM_PARISC, "HPPA" }, { EM_res016, "Reserved" }, { EM_VPP550, "Fujitsu VPP500" }, { EM_SPARC32PLUS, "Sun's v8plus" }, { EM_960, "Intel 80960" }, { EM_PPC, "PowerPC" }, { EM_PPC64, "64-bit PowerPC" }, { EM_S390, "IBM S/390" }, { EM_SPU, "Sony/Toshiba/IBM SPU" }, { EM_res024, "Reserved" }, { EM_res025, "Reserved" }, { EM_res026, "Reserved" }, { EM_res027, "Reserved" }, { EM_res028, "Reserved" }, { EM_res029, "Reserved" }, { EM_res030, "Reserved" }, { EM_res031, "Reserved" }, { EM_res032, "Reserved" }, { EM_res033, "Reserved" }, { EM_res034, "Reserved" }, { EM_res035, "Reserved" }, { EM_V800, "NEC V800 series" }, { EM_FR20, "Fujitsu FR20" }, { EM_RH32, "TRW RH32" }, { EM_MCORE, "Motorola M*Core // May also be taken by Fujitsu MMA" }, { EM_RCE, "Old name for MCore" }, { EM_ARM, "ARM" }, { EM_OLD_ALPHA, "Digital Alpha" }, { EM_SH, "Renesas (formerly Hitachi) / SuperH SH" }, { EM_SPARCV9, "SPARC v9 64-bit" }, { EM_TRICORE, "Siemens Tricore embedded processor" }, { EM_ARC, "ARC Cores" }, { EM_H8_300, "Renesas (formerly Hitachi) H8/300" }, { EM_H8_300H, "Renesas (formerly Hitachi) H8/300H" }, { EM_H8S, "Renesas (formerly Hitachi) H8S" }, { EM_H8_500, "Renesas (formerly Hitachi) H8/500" }, { EM_IA_64, "Intel IA-64 Processor" }, { EM_MIPS_X, "Stanford MIPS-X" }, { EM_COLDFIRE, "Motorola Coldfire" }, { EM_68HC12, "Motorola M68HC12" }, { EM_MMA, "Fujitsu Multimedia Accelerator" }, { EM_PCP, "Siemens PCP" }, { EM_NCPU, "Sony nCPU embedded RISC processor" }, { EM_NDR1, "Denso NDR1 microprocesspr" }, { EM_STARCORE, "Motorola Star*Core processor" }, { EM_ME16, "Toyota ME16 processor" }, { EM_ST100, "STMicroelectronics ST100 processor" }, { EM_TINYJ, "Advanced Logic Corp. TinyJ embedded processor" }, { EM_X86_64, "Advanced Micro Devices X86-64 processor" }, { EM_PDSP, "Sony DSP Processor" }, { EM_PDP10, "Digital Equipment Corp. PDP-10" }, { EM_PDP11, "Digital Equipment Corp. PDP-11" }, { EM_FX66, "Siemens FX66 microcontroller" }, { EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 bit microcontroller" }, { EM_ST7, "STMicroelectronics ST7 8-bit microcontroller" }, { EM_68HC16, "Motorola MC68HC16 Microcontroller" }, { EM_68HC11, "Motorola MC68HC11 Microcontroller" }, { EM_68HC08, "Motorola MC68HC08 Microcontroller" }, { EM_68HC05, "Motorola MC68HC05 Microcontroller" }, { EM_SVX, "Silicon Graphics SVx" }, { EM_ST19, "STMicroelectronics ST19 8-bit cpu" }, { EM_VAX, "Digital VAX" }, { EM_CRIS, "Axis Communications 32-bit embedded processor" }, { EM_JAVELIN, "Infineon Technologies 32-bit embedded cpu" }, { EM_FIREPATH, "Element 14 64-bit DSP processor" }, { EM_ZSP, "LSI Logic's 16-bit DSP processor" }, { EM_MMIX, "Donald Knuth's educational 64-bit processor" }, { EM_HUANY, "Harvard's machine-independent format" }, { EM_PRISM, "SiTera Prism" }, { EM_AVR, "Atmel AVR 8-bit microcontroller" }, { EM_FR30, "Fujitsu FR30" }, { EM_D10V, "Mitsubishi D10V" }, { EM_D30V, "Mitsubishi D30V" }, { EM_V850, "NEC v850" }, { EM_M32R, "Renesas M32R (formerly Mitsubishi M32R)" }, { EM_MN10300, "Matsushita MN10300" }, { EM_MN10200, "Matsushita MN10200" }, { EM_PJ, "picoJava" }, { EM_OPENRISC, "OpenRISC 32-bit embedded processor" }, { EM_ARC_A5, "ARC Cores Tangent-A5" }, { EM_XTENSA, "Tensilica Xtensa Architecture" }, { EM_VIDEOCORE, "Alphamosaic VideoCore processor" }, { EM_TMM_GPP, "Thompson Multimedia General Purpose Processor" }, { EM_NS32K, "National Semiconductor 32000 series" }, { EM_TPC, "Tenor Network TPC processor" }, { EM_SNP1K, "Trebia SNP 1000 processor" }, { EM_ST200, "STMicroelectronics ST200 microcontroller" }, { EM_IP2K, "Ubicom IP2022 micro controller" }, { EM_MAX, "MAX Processor" }, { EM_CR, "National Semiconductor CompactRISC" }, { EM_F2MC16, "Fujitsu F2MC16" }, { EM_MSP430, "TI msp430 micro controller" }, { EM_BLACKFIN, "ADI Blackfin" }, { EM_SE_C33, "S1C33 Family of Seiko Epson processors" }, { EM_SEP, "Sharp embedded microprocessor" }, { EM_ARCA, "Arca RISC Microprocessor" }, { EM_UNICORE, "Microprocessor series from PKU-Unity Ltd. and MPRC of " "Peking University" }, { EM_EXCESS, "eXcess: 16/32/64-bit configurable embedded CPU" }, { EM_DXP, "Icera Semiconductor Inc. Deep Execution Processor" }, { EM_ALTERA_NIOS2, "Altera Nios II soft-core processor" }, { EM_CRX, "National Semiconductor CRX" }, { EM_XGATE, "Motorola XGATE embedded processor" }, { EM_C166, "Infineon C16x/XC16x processor" }, { EM_M16C, "Renesas M16C series microprocessors" }, { EM_DSPIC30F, "Microchip Technology dsPIC30F Digital Signal Controller" }, { EM_CE, "Freescale Communication Engine RISC core" }, { EM_M32C, "Renesas M32C series microprocessors" }, { EM_res121, "Reserved" }, { EM_res122, "Reserved" }, { EM_res123, "Reserved" }, { EM_res124, "Reserved" }, { EM_res125, "Reserved" }, { EM_res126, "Reserved" }, { EM_res127, "Reserved" }, { EM_res128, "Reserved" }, { EM_res129, "Reserved" }, { EM_res130, "Reserved" }, { EM_TSK3000, "Altium TSK3000 core" }, { EM_RS08, "Freescale RS08 embedded processor" }, { EM_res133, "Reserved" }, { EM_ECOG2, "Cyan Technology eCOG2 microprocessor" }, { EM_SCORE, "Sunplus Score" }, { EM_SCORE7, "Sunplus S+core7 RISC processor" }, { EM_DSP24, "New Japan Radio (NJR) 24-bit DSP Processor" }, { EM_VIDEOCORE3, "Broadcom VideoCore III processor" }, { EM_LATTICEMICO32, "RISC processor for Lattice FPGA architecture" }, { EM_SE_C17, "Seiko Epson C17 family" }, { EM_TI_C6000, "Texas Instruments TMS320C6000 DSP family" }, { EM_TI_C2000, "Texas Instruments TMS320C2000 DSP family" }, { EM_TI_C5500, "Texas Instruments TMS320C55x DSP family" }, { EM_res143, "Reserved" }, { EM_res144, "Reserved" }, { EM_res145, "Reserved" }, { EM_res146, "Reserved" }, { EM_res147, "Reserved" }, { EM_res148, "Reserved" }, { EM_res149, "Reserved" }, { EM_res150, "Reserved" }, { EM_res151, "Reserved" }, { EM_res152, "Reserved" }, { EM_res153, "Reserved" }, { EM_res154, "Reserved" }, { EM_res155, "Reserved" }, { EM_res156, "Reserved" }, { EM_res157, "Reserved" }, { EM_res158, "Reserved" }, { EM_res159, "Reserved" }, { EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor" }, { EM_CYPRESS_M8C, "Cypress M8C microprocessor" }, { EM_R32C, "Renesas R32C series microprocessors" }, { EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family" }, { EM_QDSP6, "QUALCOMM DSP6 Processor" }, { EM_8051, "Intel 8051 and variants" }, { EM_STXP7X, "STMicroelectronics STxP7x family" }, { EM_NDS32, "Andes Technology compact code size embedded RISC processor family" }, { EM_ECOG1, "Cyan Technology eCOG1X family" }, { EM_ECOG1X, "Cyan Technology eCOG1X family" }, { EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core Micro-controllers" }, { EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor" }, { EM_MANIK, "M2000 Reconfigurable RISC Microprocessor" }, { EM_CRAYNV2, "Cray Inc. NV2 vector architecture" }, { EM_RX, "Renesas RX family" }, { EM_METAG, "Imagination Technologies META processor architecture" }, { EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture" }, { EM_ECOG16, "Cyan Technology eCOG16 family" }, { EM_CR16, "National Semiconductor CompactRISC 16-bit processor" }, { EM_ETPU, "Freescale Extended Time Processing Unit" }, { EM_SLE9X, "Infineon Technologies SLE9X core" }, { EM_L1OM, "Intel L1OM" }, { EM_INTEL181, "Reserved by Intel" }, { EM_INTEL182, "Reserved by Intel" }, { EM_res183, "Reserved by ARM" }, { EM_res184, "Reserved by ARM" }, { EM_AVR32, "Atmel Corporation 32-bit microprocessor family" }, { EM_STM8, "STMicroeletronics STM8 8-bit microcontroller" }, { EM_TILE64, "Tilera TILE64 multicore architecture family" }, { EM_TILEPRO, "Tilera TILEPro multicore architecture family" }, { EM_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core" }, { EM_CUDA, "NVIDIA CUDA architecture " }, }; static struct section_type_table_t { const Elf64_Half key; const char* str; } section_type_table[] = { { SHT_NULL, "NULL" }, { SHT_PROGBITS, "PROGBITS" }, { SHT_SYMTAB, "SYMTAB" }, { SHT_STRTAB, "STRTAB" }, { SHT_RELA, "RELA" }, { SHT_HASH, "HASH" }, { SHT_DYNAMIC, "DYNAMIC" }, { SHT_NOTE, "NOTE" }, { SHT_NOBITS, "NOBITS" }, { SHT_REL, "REL" }, { SHT_SHLIB, "SHLIB" }, { SHT_DYNSYM, "DYNSYM" }, { SHT_INIT_ARRAY, "INIT_ARRAY" }, { SHT_FINI_ARRAY, "FINI_ARRAY" }, { SHT_PREINIT_ARRAY, "PREINIT_ARRAY" }, { SHT_GROUP, "GROUP" }, { SHT_SYMTAB_SHNDX, "SYMTAB_SHNDX " }, }; static struct segment_type_table_t { const Elf_Word key; const char* str; } segment_type_table[] = { { PT_NULL, "NULL" }, { PT_LOAD, "LOAD" }, { PT_DYNAMIC, "DYNAMIC" }, { PT_INTERP, "INTERP" }, { PT_NOTE, "NOTE" }, { PT_SHLIB, "SHLIB" }, { PT_PHDR, "PHDR" }, { PT_TLS, "TLS" }, }; static struct segment_flag_table_t { const Elf_Word key; const char* str; } segment_flag_table[] = { { 0, "" }, { 1, "X" }, { 2, "W" }, { 3, "WX" }, { 4, "R" }, { 5, "RX" }, { 6, "RW" }, { 7, "RWX" }, }; static struct symbol_bind_t { const Elf_Word key; const char* str; } symbol_bind_table[] = { { STB_LOCAL, "LOCAL" }, { STB_GLOBAL, "GLOBAL" }, { STB_WEAK, "WEAK" }, { STB_LOOS, "LOOS" }, { STB_HIOS, "HIOS" }, { STB_MULTIDEF, "MULTIDEF" }, { STB_LOPROC, "LOPROC" }, { STB_HIPROC, "HIPROC" }, }; static struct symbol_type_t { const Elf_Word key; const char* str; } symbol_type_table[] = { { STT_NOTYPE, "NOTYPE" }, { STT_OBJECT, "OBJECT" }, { STT_FUNC, "FUNC" }, { STT_SECTION, "SECTION" }, { STT_FILE, "FILE" }, { STT_COMMON, "COMMON" }, { STT_TLS, "TLS" }, { STT_LOOS, "LOOS" }, { STT_HIOS, "HIOS" }, { STT_LOPROC, "LOPROC" }, { STT_HIPROC, "HIPROC" }, }; static struct dynamic_tag_t { const Elf_Word key; const char* str; } dynamic_tag_table[] = { { DT_NULL, "NULL" }, { DT_NEEDED, "NEEDED" }, { DT_PLTRELSZ, "PLTRELSZ" }, { DT_PLTGOT, "PLTGOT" }, { DT_HASH, "HASH" }, { DT_STRTAB, "STRTAB" }, { DT_SYMTAB, "SYMTAB" }, { DT_RELA, "RELA" }, { DT_RELASZ, "RELASZ" }, { DT_RELAENT, "RELAENT" }, { DT_STRSZ, "STRSZ" }, { DT_SYMENT, "SYMENT" }, { DT_INIT, "INIT" }, { DT_FINI, "FINI" }, { DT_SONAME, "SONAME" }, { DT_RPATH, "RPATH" }, { DT_SYMBOLIC, "SYMBOLIC" }, { DT_REL, "REL" }, { DT_RELSZ, "RELSZ" }, { DT_RELENT, "RELENT" }, { DT_PLTREL, "PLTREL" }, { DT_DEBUG, "DEBUG" }, { DT_TEXTREL, "TEXTREL" }, { DT_JMPREL, "JMPREL" }, { DT_BIND_NOW, "BIND_NOW" }, { DT_INIT_ARRAY, "INIT_ARRAY" }, { DT_FINI_ARRAY, "FINI_ARRAY" }, { DT_INIT_ARRAYSZ, "INIT_ARRAYSZ" }, { DT_FINI_ARRAYSZ, "FINI_ARRAYSZ" }, { DT_RUNPATH, "RUNPATH" }, { DT_FLAGS, "FLAGS" }, { DT_ENCODING, "ENCODING" }, { DT_PREINIT_ARRAY, "PREINIT_ARRAY" }, { DT_PREINIT_ARRAYSZ, "PREINIT_ARRAYSZ" }, { DT_MAXPOSTAGS, "MAXPOSTAGS" }, }; static const ELFIO::Elf_Xword MAX_DATA_ENTRIES = 64; //------------------------------------------------------------------------------ class dump { #define DUMP_DEC_FORMAT( width ) \ std::setw( width ) << std::setfill( ' ' ) << std::dec << std::right #define DUMP_HEX_FORMAT( width ) \ std::setw( width ) << std::setfill( '0' ) << std::hex << std::right #define DUMP_STR_FORMAT( width ) \ std::setw( width ) << std::setfill( ' ' ) << std::hex << std::left public: //------------------------------------------------------------------------------ static void header( std::ostream& out, const elfio& reader ) { if ( !reader.get_header_size() ) { return; } out << "ELF Header" << std::endl << std::endl << " Class: " << str_class( reader.get_class() ) << std::endl << " Encoding: " << str_endian( reader.get_encoding() ) << std::endl << " ELFVersion: " << str_version( reader.get_elf_version() ) << std::endl << " Type: " << str_type( reader.get_type() ) << std::endl << " Machine: " << str_machine( reader.get_machine() ) << std::endl << " Version: " << str_version( reader.get_version() ) << std::endl << " Entry: " << "0x" << std::hex << reader.get_entry() << std::endl << " Flags: " << "0x" << std::hex << reader.get_flags() << std::endl << std::endl; } //------------------------------------------------------------------------------ static void section_headers( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.sections.size(); if ( n == 0 ) { return; } out << "Section Headers:" << std::endl; if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit out << "[ Nr ] Type Addr Size ES Flg Lk Inf " "Al Name" << std::endl; } else { // Output for 64-bit out << "[ Nr ] Type Addr Size " " ES Flg" << std::endl << " Lk Inf Al Name" << std::endl; } for ( Elf_Half i = 0; i < n; ++i ) { // For all sections section* sec = reader.sections[i]; section_header( out, i, sec, reader.get_class() ); } out << "Key to Flags: W (write), A (alloc), X (execute)\n\n" << std::endl; } //------------------------------------------------------------------------------ static void section_header( std::ostream& out, Elf_Half no, const section* sec, unsigned char elf_class ) { std::ios_base::fmtflags original_flags = out.flags(); if ( elf_class == ELFCLASS32 ) { // Output for 32-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) << " " << DUMP_HEX_FORMAT( 8 ) << sec->get_address() << " " << DUMP_HEX_FORMAT( 8 ) << sec->get_size() << " " << DUMP_HEX_FORMAT( 2 ) << sec->get_entry_size() << " " << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) << " " << DUMP_HEX_FORMAT( 2 ) << sec->get_link() << " " << DUMP_HEX_FORMAT( 3 ) << sec->get_info() << " " << DUMP_HEX_FORMAT( 2 ) << sec->get_addr_align() << " " << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; } else { // Output for 64-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) << " " << DUMP_HEX_FORMAT( 16 ) << sec->get_address() << " " << DUMP_HEX_FORMAT( 16 ) << sec->get_size() << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_entry_size() << " " << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) << " " << std::endl << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_link() << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_info() << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_addr_align() << " " << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; } out.flags( original_flags ); return; } //------------------------------------------------------------------------------ static void segment_headers( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.segments.size(); if ( n == 0 ) { return; } out << "Segment headers:" << std::endl; if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit out << "[ Nr ] Type VirtAddr PhysAddr FileSize Mem.Size " "Flags Align" << std::endl; } else { // Output for 64-bit out << "[ Nr ] Type VirtAddr PhysAddr " "Flags" << std::endl << " FileSize Mem.Size " "Align" << std::endl; } for ( Elf_Half i = 0; i < n; ++i ) { segment* seg = reader.segments[i]; segment_header( out, i, seg, reader.get_class() ); } out << std::endl; } //------------------------------------------------------------------------------ static void segment_header( std::ostream& out, Elf_Half no, const segment* seg, unsigned int elf_class ) { std::ios_base::fmtflags original_flags = out.flags(); if ( elf_class == ELFCLASS32 ) { // Output for 32-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_virtual_address() << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_physical_address() << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_file_size() << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_memory_size() << " " << DUMP_STR_FORMAT( 8 ) << str_segment_flag( seg->get_flags() ) << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_align() << " " << std::endl; } else { // Output for 64-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_virtual_address() << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_physical_address() << " " << DUMP_STR_FORMAT( 16 ) << str_segment_flag( seg->get_flags() ) << " " << std::endl << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_file_size() << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_memory_size() << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_align() << " " << std::endl; } out.flags( original_flags ); } //------------------------------------------------------------------------------ static void symbol_tables( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.sections.size(); for ( Elf_Half i = 0; i < n; ++i ) { // For all sections section* sec = reader.sections[i]; if ( SHT_SYMTAB == sec->get_type() || SHT_DYNSYM == sec->get_type() ) { symbol_section_accessor symbols( reader, sec ); Elf_Xword sym_no = symbols.get_symbols_num(); if ( sym_no > 0 ) { out << "Symbol table (" << sec->get_name() << ")" << std::endl; if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit out << "[ Nr ] Value Size Type Bind " "Sect Name" << std::endl; } else { // Output for 64-bit out << "[ Nr ] Value Size Type " " Bind Sect" << std::endl << " Name" << std::endl; } for ( Elf_Xword i = 0; i < sym_no; ++i ) { std::string name; Elf64_Addr value = 0; Elf_Xword size = 0; unsigned char bind = 0; unsigned char type = 0; Elf_Half section = 0; unsigned char other = 0; symbols.get_symbol( i, name, value, size, bind, type, section, other ); symbol_table( out, i, name, value, size, bind, type, section, reader.get_class() ); } out << std::endl; } } } } //------------------------------------------------------------------------------ static void symbol_table( std::ostream& out, Elf_Xword no, std::string& name, Elf64_Addr value, Elf_Xword size, unsigned char bind, unsigned char type, Elf_Half section, unsigned int elf_class ) { std::ios_base::fmtflags original_flags = out.flags(); if ( elf_class == ELFCLASS32 ) { // Output for 32-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_HEX_FORMAT( 8 ) << value << " " << DUMP_HEX_FORMAT( 8 ) << size << " " << DUMP_STR_FORMAT( 7 ) << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) << section << " " << DUMP_STR_FORMAT( 1 ) << name << " " << std::endl; } else { // Output for 64-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_HEX_FORMAT( 16 ) << value << " " << DUMP_HEX_FORMAT( 16 ) << size << " " << DUMP_STR_FORMAT( 7 ) << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) << section << " " << std::endl << " " << DUMP_STR_FORMAT( 1 ) << name << " " << std::endl; } out.flags( original_flags ); } //------------------------------------------------------------------------------ static void notes( std::ostream& out, const elfio& reader ) { Elf_Half no = reader.sections.size(); for ( Elf_Half i = 0; i < no; ++i ) { // For all sections section* sec = reader.sections[i]; if ( SHT_NOTE == sec->get_type() ) { // Look at notes note_section_accessor notes( reader, sec ); Elf_Word no_notes = notes.get_notes_num(); if ( no > 0 ) { out << "Note section (" << sec->get_name() << ")" << std::endl << " No Type Name" << std::endl; for ( Elf_Word j = 0; j < no_notes; ++j ) { // For all notes Elf_Word type; std::string name; void* desc; Elf_Word descsz; if ( notes.get_note( j, type, name, desc, descsz ) ) { // 'name' usually contains \0 at the end. Try to fix it name = name.c_str(); note( out, j, type, name ); } } out << std::endl; } } } } //------------------------------------------------------------------------------ static void modinfo( std::ostream& out, const elfio& reader ) { Elf_Half no = reader.sections.size(); for ( Elf_Half i = 0; i < no; ++i ) { // For all sections section* sec = reader.sections[i]; if ( ".modinfo" == sec->get_name() ) { // Look for the section out << "Section .modinfo" << std::endl; const_modinfo_section_accessor modinfo( sec ); for ( Elf_Word i = 0; i < modinfo.get_attribute_num(); i++ ) { std::string field; std::string value; if ( modinfo.get_attribute( i, field, value ) ) { out << " " << std::setw( 20 ) << field << std::setw( 0 ) << " = " << value << std::endl; } } out << std::endl; break; } } } //------------------------------------------------------------------------------ static void note( std::ostream& out, int no, Elf_Word type, const std::string& name ) { out << " [" << DUMP_DEC_FORMAT( 2 ) << no << "] " << DUMP_HEX_FORMAT( 8 ) << type << " " << DUMP_STR_FORMAT( 1 ) << name << std::endl; } //------------------------------------------------------------------------------ static void dynamic_tags( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.sections.size(); for ( Elf_Half i = 0; i < n; ++i ) { // For all sections section* sec = reader.sections[i]; if ( SHT_DYNAMIC == sec->get_type() ) { dynamic_section_accessor dynamic( reader, sec ); Elf_Xword dyn_no = dynamic.get_entries_num(); if ( dyn_no > 0 ) { out << "Dynamic section (" << sec->get_name() << ")" << std::endl; out << "[ Nr ] Tag Name/Value" << std::endl; for ( Elf_Xword i = 0; i < dyn_no; ++i ) { Elf_Xword tag = 0; Elf_Xword value = 0; std::string str; dynamic.get_entry( i, tag, value, str ); dynamic_tag( out, i, tag, value, str, reader.get_class() ); if ( DT_NULL == tag ) { break; } } out << std::endl; } } } } //------------------------------------------------------------------------------ static void dynamic_tag( std::ostream& out, Elf_Xword no, Elf_Xword tag, Elf_Xword value, std::string str, unsigned int /*elf_class*/ ) { out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 16 ) << str_dynamic_tag( tag ) << " "; if ( str.empty() ) { out << DUMP_HEX_FORMAT( 16 ) << value << " "; } else { out << DUMP_STR_FORMAT( 32 ) << str << " "; } out << std::endl; } //------------------------------------------------------------------------------ static void section_data( std::ostream& out, const section* sec ) { std::ios_base::fmtflags original_flags = out.flags(); out << sec->get_name() << std::endl; const char* pdata = sec->get_data(); if ( pdata ) { ELFIO::Elf_Xword i; for ( i = 0; i < std::min( sec->get_size(), MAX_DATA_ENTRIES ); ++i ) { if ( i % 16 == 0 ) { out << "[" << DUMP_HEX_FORMAT( 8 ) << i << "]"; } out << " " << DUMP_HEX_FORMAT( 2 ) << ( pdata[i] & 0x000000FF ); if ( i % 16 == 15 ) { out << std::endl; } } if ( i % 16 != 0 ) { out << std::endl; } out.flags( original_flags ); } return; } //------------------------------------------------------------------------------ static void section_datas( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.sections.size(); if ( n == 0 ) { return; } out << "Section Data:" << std::endl; for ( Elf_Half i = 1; i < n; ++i ) { // For all sections section* sec = reader.sections[i]; if ( sec->get_type() == SHT_NOBITS ) { continue; } section_data( out, sec ); } out << std::endl; } //------------------------------------------------------------------------------ static void segment_data( std::ostream& out, Elf_Half no, const segment* seg ) { std::ios_base::fmtflags original_flags = out.flags(); out << "Segment # " << no << std::endl; const char* pdata = seg->get_data(); if ( pdata ) { ELFIO::Elf_Xword i; for ( i = 0; i < std::min( seg->get_file_size(), MAX_DATA_ENTRIES ); ++i ) { if ( i % 16 == 0 ) { out << "[" << DUMP_HEX_FORMAT( 8 ) << i << "]"; } out << " " << DUMP_HEX_FORMAT( 2 ) << ( pdata[i] & 0x000000FF ); if ( i % 16 == 15 ) { out << std::endl; } } if ( i % 16 != 0 ) { out << std::endl; } out.flags( original_flags ); } return; } //------------------------------------------------------------------------------ static void segment_datas( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.segments.size(); if ( n == 0 ) { return; } out << "Segment Data:" << std::endl; for ( Elf_Half i = 0; i < n; ++i ) { // For all sections segment* seg = reader.segments[i]; segment_data( out, i, seg ); } out << std::endl; } private: //------------------------------------------------------------------------------ template std::string static find_value_in_table( const T& table, const K& key ) { std::string res = "?"; for ( unsigned int i = 0; i < sizeof( table ) / sizeof( table[0] ); ++i ) { if ( table[i].key == key ) { res = table[i].str; break; } } return res; } //------------------------------------------------------------------------------ template static std::string format_assoc( const T& table, const K& key ) { std::string str = find_value_in_table( table, key ); if ( str == "?" ) { std::ostringstream oss; oss << str << " (0x" << std::hex << key << ")"; str = oss.str(); } return str; } //------------------------------------------------------------------------------ template static std::string format_assoc( const T& table, const char key ) { return format_assoc( table, (const int)key ); } //------------------------------------------------------------------------------ static std::string section_flags( Elf_Xword flags ) { std::string ret = ""; if ( flags & SHF_WRITE ) { ret += "W"; } if ( flags & SHF_ALLOC ) { ret += "A"; } if ( flags & SHF_EXECINSTR ) { ret += "X"; } return ret; } //------------------------------------------------------------------------------ #define STR_FUNC_TABLE( name ) \ template static std::string str_##name( const T key ) \ { \ return format_assoc( name##_table, key ); \ } STR_FUNC_TABLE( class ) STR_FUNC_TABLE( endian ) STR_FUNC_TABLE( version ) STR_FUNC_TABLE( type ) STR_FUNC_TABLE( machine ) STR_FUNC_TABLE( section_type ) STR_FUNC_TABLE( segment_type ) STR_FUNC_TABLE( segment_flag ) STR_FUNC_TABLE( symbol_bind ) STR_FUNC_TABLE( symbol_type ) STR_FUNC_TABLE( dynamic_tag ) #undef STR_FUNC_TABLE #undef DUMP_DEC_FORMAT #undef DUMP_HEX_FORMAT #undef DUMP_STR_FORMAT }; // class dump }; // namespace ELFIO #endif // ELFIO_DUMP_HPP /*** End of inlined file: elfio_dump.hpp ***/ linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/cpp-stub/stub.h000066400000000000000000000234071523477242100246140ustar00rootroot00000000000000#ifndef __STUB_H__ #define __STUB_H__ #ifdef _WIN32 //windows #include #include #else //linux #include #include #include #endif //c #include #include //c++ #include #define ADDR(CLASS_NAME,MEMBER_NAME) (&CLASS_NAME::MEMBER_NAME) /********************************************************** replace function **********************************************************/ #ifdef _WIN32 #define CACHEFLUSH(addr, size) FlushInstructionCache(GetCurrentProcess(), addr, size) #else #define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size) #endif #if defined(__aarch64__) || defined(_M_ARM64) #define CODESIZE 16U #define CODESIZE_MIN 16U #define CODESIZE_MAX CODESIZE // ldr x9, +8 // br x9 // addr #define REPLACE_FAR(t, fn, fn_stub)\ ((uint32_t*)fn)[0] = 0x58000040 | 9;\ ((uint32_t*)fn)[1] = 0xd61f0120 | (9 << 5);\ *(long long *)(fn + 8) = (long long )fn_stub;\ CACHEFLUSH((char *)fn, CODESIZE); #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) #elif defined(__arm__) || defined(_M_ARM) #define CODESIZE 8U #define CODESIZE_MIN 8U #define CODESIZE_MAX CODESIZE // ldr pc, [pc, #-4] #define REPLACE_FAR(t, fn, fn_stub)\ ((uint32_t*)fn)[0] = 0xe51ff004;\ ((uint32_t*)fn)[1] = (uint32_t)fn_stub;\ CACHEFLUSH((char *)fn, CODESIZE); #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) #elif defined(__mips64) #define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size) #define CODESIZE 80U #define CODESIZE_MIN 80U #define CODESIZE_MAX CODESIZE //mips没有PC指针,所以需要手动入栈出栈 //120000ce0: 67bdffe0 daddiu sp, sp, -32 //入栈 //120000ce4: ffbf0018 sd ra, 24(sp) //120000ce8: ffbe0010 sd s8, 16(sp) //120000cec: ffbc0008 sd gp, 8(sp) //120000cf0: 03a0f025 move s8, sp //120000d2c: 03c0e825 move sp, s8 //出栈 //120000d30: dfbf0018 ld ra, 24(sp) //120000d34: dfbe0010 ld s8, 16(sp) //120000d38: dfbc0008 ld gp, 8(sp) //120000d3c: 67bd0020 daddiu sp, sp, 32 //120000d40: 03e00008 jr ra #define REPLACE_FAR(t, fn, fn_stub)\ ((uint32_t *)fn)[0] = 0x67bdffe0;\ ((uint32_t *)fn)[1] = 0xffbf0018;\ ((uint32_t *)fn)[2] = 0xffbe0010;\ ((uint32_t *)fn)[3] = 0xffbc0008;\ ((uint32_t *)fn)[4] = 0x03a0f025;\ *(uint16_t *)(fn + 20) = (long long)fn_stub >> 32;\ *(fn + 22) = 0x19;\ *(fn + 23) = 0x24;\ ((uint32_t *)fn)[6] = 0x0019cc38;\ *(uint16_t *)(fn + 28) = (long long)fn_stub >> 16;\ *(fn + 30) = 0x39;\ *(fn + 31) = 0x37;\ ((uint32_t *)fn)[8] = 0x0019cc38;\ *(uint16_t *)(fn + 36) = (long long)fn_stub;\ *(fn + 38) = 0x39;\ *(fn + 39) = 0x37;\ ((uint32_t *)fn)[10] = 0x0320f809;\ ((uint32_t *)fn)[11] = 0x00000000;\ ((uint32_t *)fn)[12] = 0x00000000;\ ((uint32_t *)fn)[13] = 0x03c0e825;\ ((uint32_t *)fn)[14] = 0xdfbf0018;\ ((uint32_t *)fn)[15] = 0xdfbe0010;\ ((uint32_t *)fn)[16] = 0xdfbc0008;\ ((uint32_t *)fn)[17] = 0x67bd0020;\ ((uint32_t *)fn)[18] = 0x03e00008;\ ((uint32_t *)fn)[19] = 0x00000000;\ CACHEFLUSH((char *)fn, CODESIZE); #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) #elif defined(__thumb__) || defined(_M_THUMB) #error "Thumb is not supported" #else //__i386__ _x86_64__ #define CODESIZE 13U #define CODESIZE_MIN 5U #define CODESIZE_MAX CODESIZE //13 byte(jmp m16:64) //movabs $0x102030405060708,%r11 //jmpq *%r11 static void REPLACE_FAR(void *t, char *fn, char *fn_stub) { *fn = 0x49; *(fn + 1) = 0xbb; *(long long *)(fn + 2) = (long long)fn_stub; *(fn + 10) = 0x41; *(fn + 11) = 0xff; *(fn + 12) = 0xe3; CACHEFLUSH((char *)fn, CODESIZE); } //5 byte(jmp rel32) #define REPLACE_NEAR(t, fn, fn_stub)\ *fn = 0xE9;\ *(int *)(fn + 1) = (int)(fn_stub - fn - CODESIZE_MIN);\ CACHEFLUSH((char *)fn, CODESIZE); #endif struct func_stub { char *fn; unsigned char code_buf[CODESIZE]; bool far_jmp; }; class Stub { public: Stub() { #ifdef _WIN32 SYSTEM_INFO sys_info; GetSystemInfo(&sys_info); m_pagesize = sys_info.dwPageSize; #else m_pagesize = sysconf(_SC_PAGE_SIZE); #endif if (m_pagesize < 0) { m_pagesize = 4096; } } ~Stub() { clear(); } virtual void clear() { std::map::iterator iter; struct func_stub *pstub; for(iter=m_result.begin(); iter != m_result.end(); iter++) { pstub = iter->second; #ifdef _WIN32 DWORD lpflOldProtect; if(0 != VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) #else if (0 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) #endif { if(pstub->far_jmp) { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); } else { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); } #ifdef _WIN32 VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect); #else CACHEFLUSH(pstub->fn,CODESIZE); mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC); #endif } iter->second = NULL; delete pstub; } m_result.clear(); return; } template bool set(T addr, S addr_stub) { char * fn; char * fn_stub; fn = addrof(addr); fn_stub = addrof(addr_stub); struct func_stub *pstub; std::map::iterator iter = m_result.find(fn); if (iter == m_result.end()) { pstub = new func_stub; //start pstub->fn = fn; if(distanceof(fn, fn_stub)) { pstub->far_jmp = true; std::memcpy(pstub->code_buf, fn, CODESIZE_MAX); } else { pstub->far_jmp = false; std::memcpy(pstub->code_buf, fn, CODESIZE_MIN); } } else { pstub = iter->second; pstub->far_jmp = distanceof(fn, fn_stub); } #ifdef _WIN32 DWORD lpflOldProtect; if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) #else if (-1 == mprotect(pageof(pstub->fn), static_cast(m_pagesize * 2), PROT_READ | PROT_WRITE | PROT_EXEC)) #endif { throw("stub set memory protect to w+r+x faild"); return false; } if(pstub->far_jmp) { REPLACE_FAR(this, fn, fn_stub); } else { REPLACE_NEAR(this, fn, fn_stub); } #ifdef _WIN32 if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) #else if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) #endif { throw("stub set memory protect to r+x failed"); return false; } m_result.insert(std::pair(fn,pstub)); return true; } template bool reset(T addr) { char * fn; fn = addrof(addr); std::map::iterator iter = m_result.find(fn); if (iter == m_result.end()) { return true; } struct func_stub *pstub; pstub = iter->second; #ifdef _WIN32 DWORD lpflOldProtect; if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) #else if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) #endif { throw("stub reset memory protect to w+r+x faild"); return false; } if(pstub->far_jmp) { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); } else { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); } #ifdef _WIN32 if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) #else CACHEFLUSH(pstub->fn,CODESIZE); if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) #endif { throw("stub reset memory protect to r+x failed"); return false; } m_result.erase(iter); delete pstub; return true; } protected: char *pageof(char* addr) { #ifdef _WIN32 return (char *)((unsigned long long)addr & ~(m_pagesize - 1)); #else return (char *)((unsigned long)addr & ~(m_pagesize - 1)); #endif } template char* addrof(T addr) { union { T _s; char* _d; }ut; ut._s = addr; return ut._d; } bool distanceof(char* addr, char* addr_stub) { std::ptrdiff_t diff = addr_stub >= addr ? addr_stub - addr : addr - addr_stub; if((sizeof(addr) > 4) && (((diff >> 31) - 1) > 0)) { return true; } return false; } protected: #ifdef _WIN32 //LLP64 long long m_pagesize; #else //LP64 long m_pagesize; #endif std::map m_result; }; #endif linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/stub-ext/000077500000000000000000000000001523477242100234765ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/stub-ext/stub-shadow.cpp000066400000000000000000000010211523477242100264340ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: MIT #include "stub-shadow.h" namespace stub_ext { WrapperMap stub_wrappers; Wrapper::Wrapper() { } Wrapper::~Wrapper() { } void freeWrapper(Wrapper *wrapper) { if (!wrapper) return; for (auto iter = stub_wrappers.begin(); iter != stub_wrappers.end();) { if (iter->second == wrapper) iter = stub_wrappers.erase(iter); else ++iter; } delete wrapper; } } linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/stub-ext/stub-shadow.h000066400000000000000000000103521523477242100261100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: MIT #ifndef STUBSHADOW_H #define STUBSHADOW_H #include #include namespace stub_ext { #define LAMDA_FUNCTION_TYPE decltype(&Lamda::operator()) class Wrapper { public: Wrapper(); virtual ~Wrapper(); }; typedef std::unordered_map WrapperMap; extern WrapperMap stub_wrappers; template class LamdaWrapper : public Wrapper { public: LamdaWrapper(Lamda func): Wrapper(),_func(func){} ~LamdaWrapper(){} Lamda _func; }; template struct VFLocator { }; template struct VFLocator { typedef Ret (*Func)(Obj*, Args...); }; template struct VFLocator { typedef Ret (*Func)(Obj*, Args...); }; template struct LamdaCaller { }; template struct LamdaCaller { template static Ret call(LamdaWrapper *wrapper, OrgArgs&&... args) { return wrapper->_func(std::forward(args)...); } }; template struct LamdaCaller { template static Ret call(LamdaWrapper *wrapper, OrgArgs&&... args) { return wrapper->_func(); } }; template struct FuncShadow { }; template struct FuncShadow { typedef Ret (*Shadow)(Args...); typedef Ret RetType; static Ret call(Args ...args) { Shadow shadow = &call; long id = (long)shadow; auto iter = stub_wrappers.find(id); assert(stub_wrappers.find(id) != stub_wrappers.end()); LamdaWrapper *wrapper = dynamic_cast *>(iter->second); return LamdaCaller::call(wrapper, args...); } }; template struct FuncShadow { typedef Ret (*Shadow)(Args...); typedef Ret RetType; static Ret call(Args ...args) { Shadow shadow = &call; long id = (long)shadow; auto iter = stub_wrappers.find(id); assert(stub_wrappers.find(id) != stub_wrappers.end()); LamdaWrapper *wrapper = dynamic_cast *>(iter->second); return LamdaCaller::call(wrapper, args...); } }; template struct FuncShadow { typedef Ret (*Shadow)(Obj *,Args...); typedef Ret RetType; static Ret call(Obj *obj, Args ...args) { Shadow shadow = &call; long id = (long)shadow; auto iter = stub_wrappers.find(id); assert(stub_wrappers.find(id) != stub_wrappers.end()); LamdaWrapper *wrapper = dynamic_cast *>(iter->second); return LamdaCaller::call(wrapper, obj, args...); } }; template struct FuncShadow { typedef Ret (*Shadow)(Obj *,Args...); typedef Ret RetType; static Ret call(Obj *obj, Args ...args) { Shadow shadow = &call; long id = (long)shadow; auto iter = stub_wrappers.find(id); assert(stub_wrappers.find(id) != stub_wrappers.end()); LamdaWrapper *wrapper = dynamic_cast *>(iter->second); return LamdaCaller::call(wrapper, obj, args...); } }; template typename FuncShadow::Shadow depictShadow(Wrapper **wrapper, Func func, Lamda lamda) { *wrapper = new LamdaWrapper(lamda); typename FuncShadow::Shadow shadow = &FuncShadow::call; long id = (long)shadow; assert(stub_wrappers.find(id) == stub_wrappers.end()); stub_wrappers.insert(std::make_pair(id,*wrapper)); return shadow; } void freeWrapper(Wrapper *wrapper); } #endif // STUBSHADOW_H linuxdeepin-util-dfm-a4307d3/3rdparty/testutils/stub-ext/stubext.h000066400000000000000000000052141523477242100253470ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: MIT #ifndef STUBEXT_H #define STUBEXT_H //需修改Stub的私用成员函数和成员变量为保护类型 #include "stub.h" #include "stub-shadow.h" #ifdef DEBUG_STUB_INVOKE // use to make sure the stub function is invoked. # define __DBG_STUB_INVOKE__ printf("stub at %s:%d is invoked.\n", __FILE__, __LINE__); #else # define __DBG_STUB_INVOKE__ #endif #define VADDR(CLASS_NAME, MEMBER_NAME) (typename stub_ext::VFLocator::Func)(&CLASS_NAME::MEMBER_NAME) namespace stub_ext { class StubExt : public Stub { public: StubExt() : Stub() { } template bool set_lamda(T addr, Lamda lamda) { char *fn = addrof(addr); if (m_result.find(fn) != m_result.end()) reset(addr); Wrapper *wrapper = nullptr; auto addr_stub = depictShadow(&wrapper, addr, lamda); if (set(addr, addr_stub)) { m_wrappers.insert(std::make_pair(fn, wrapper)); return true; } else { freeWrapper(wrapper); } return false; } template void reset(T addr) { Stub::reset(addr); char *fn = addrof(addr); auto iter = m_wrappers.find(fn); if (iter != m_wrappers.end()) { freeWrapper(iter->second); m_wrappers.erase(iter); } } ~StubExt() { clear(); } void clear() override { Stub::clear(); for (auto iter = m_wrappers.begin(); iter != m_wrappers.end(); ++iter) { freeWrapper(iter->second); } m_wrappers.clear(); } template static void *get_ctor_addr(bool start = true) { // the start vairable must be true, or the compiler will optimize out. if (start) goto Start; Call_Constructor: // This line of code will not be executed. // The purpose of the code is to allow the compiler to generate the assembly code that calls the constructor. T(); Start: // The address of the line of code T() obtained by assembly char *p = (char *)&&Call_Constructor; // https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html // CALL rel32 void *ret = 0; char pos; char call = 0xe8; do { pos = *p; if (pos == call) { ret = p + 5 + (*(int *)(p + 1)); } } while (!ret && (++p)); return ret; } protected: std::map m_wrappers; }; } #endif // STUBEXT_H linuxdeepin-util-dfm-a4307d3/CMakeLists.txt000066400000000000000000000026361523477242100206620ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) project(util-dfm) # Setup the environment SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_AUTOMOC ON) SET(CMAKE_INCLUDE_CURRENT_DIR ON) #SET(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall") option(OPT_ENABLE_QT6 "Use Qt6" ON) set(DFM_BUILD_WITH_QT6 FALSE) if(OPT_ENABLE_QT6) find_package(Qt6 QUIET) if (Qt6_FOUND) message(STATUS "Found Qt6: ${Qt6_VERSION}") set(DFM_BUILD_WITH_QT6 TRUE) set(QT_VERSION_MAJOR 6) set(DFM_VERSION_MAJOR 6) set(PROJECT_VERSION_MAJOR 1) endif() endif() if(NOT DFM_BUILD_WITH_QT6) message(WARNING "Qt6 not found, Use Qt5.") set(QT_VERSION_MAJOR 5) set(DFM_VERSION_MAJOR "") set(PROJECT_VERSION_MAJOR 0) endif() # default find_package(PkgConfig REQUIRED) # Install settings if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set(CMAKE_INSTALL_PREFIX /usr) endif () include(GNUInstallDirs) add_subdirectory(${PROJECT_SOURCE_DIR}/src) # Unit tests (enabled by default, except in Release/MinSizeRel builds) if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") option(BUILD_UNIT_TESTS "Build unit tests" OFF) else() option(BUILD_UNIT_TESTS "Build unit tests" ON) endif() if(BUILD_UNIT_TESTS) add_subdirectory(${PROJECT_SOURCE_DIR}/autotests) endif() # Legacy tests (temporarily disabled) # add_subdirectory(${PROJECT_SOURCE_DIR}/tests) linuxdeepin-util-dfm-a4307d3/LICENSE000066400000000000000000001045131523477242100171240ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . linuxdeepin-util-dfm-a4307d3/LICENSES/000077500000000000000000000000001523477242100173205ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/LICENSES/CC-BY-4.0.txt000066400000000000000000000411771523477242100211670ustar00rootroot00000000000000Creative Commons Attribution 4.0 International Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 – Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 – Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: A. reproduce and Share the Licensed Material, in whole or in part; and B. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 5. Downstream recipients. A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 – License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: A. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 – Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 – Disclaimer of Warranties and Limitation of Liability. a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 – Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 – Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 – Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. linuxdeepin-util-dfm-a4307d3/LICENSES/CC0-1.0.txt000066400000000000000000000156101523477242100207250ustar00rootroot00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. linuxdeepin-util-dfm-a4307d3/LICENSES/ClArtistic.txt000066400000000000000000000144041523477242100221250ustar00rootroot00000000000000 The Clarified Artistic License Preamble: --------- The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: ------------ "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Distribution fee" is a fee you charge for providing a copy of this Package to another party. "Freely Available" means that no fee is charged for the right to use the item, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. Licence: -------- 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain, or those made Freely Available, or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: 1. place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major network archive site allowing unrestricted access to them, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. 2. use the modified Package only within your corporation or organization. 3. rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. 4. make other distribution arrangements with the Copyright Holder. 5. permit and encourge anyone who receives a copy of the modified Package permission to make your modifications Freely Available in some specific way. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: 1. distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. 2. accompany the distribution with the machine-readable source of the Package with your modifications. 3. give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. 4. make other distribution arrangements with the Copyright Holder. 5. offer the machine-readable source of the Package, with your modifications, by mail order. 5. You may charge a distribution fee for any distribution of this Package. If you offer support for this Package, you may charge any fee you choose for that support. You may not charge a license fee for the right to use this Package itself. You may distribute this Package in aggregate with other (possibly commercial and possibly nonfree) programs as part of a larger (possibly commercial and possibly nonfree) software distribution, and charge license fees for other parts of that software distribution, provided that you do not advertise this Package as a product of your own. If the Package includes an interpreter, You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of the Standard Version of the Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. linuxdeepin-util-dfm-a4307d3/LICENSES/GPL-3.0-or-later.txt000066400000000000000000001035561523477242100225360ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . linuxdeepin-util-dfm-a4307d3/LICENSES/MIT.txt000066400000000000000000000020661523477242100205160ustar00rootroot00000000000000MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. linuxdeepin-util-dfm-a4307d3/README.md000066400000000000000000000034371523477242100174010ustar00rootroot00000000000000# util-dfm Deepin File Manager utilities (libdfm-io, libdfm-mount and libdfm-burn) developed by UnionTech Software Technology Co., Ltd ### Dependencies ### Build dependencies debhelper (>=9), cmake, pkg-config, qtbase5-dev, qtbase5-private-dev, qt5-qmake, libglibmm-2.4-dev, qttools5-dev-tools, libglib2.0-dev, libudisks2-dev, libisoburn-dev, libmediainfo-dev, libsecret-1-dev ## Installation ``` shell $ git clone https://github.com/linuxdeepin/util-dfm $ cd util-dfm ``` ### Install dependeincies Make sure you have installed all dependencies. The package name may vary between different distros. If util-dfm is available in your distro, it is a better idea to check the packaging script provided by your distro. otherwise use this install from project. ``` shell $ sudo apt build-dep ./ ``` ### Build from source code 1. Build: ```bash $ cd util-dfm $ mkdir build $ cd build $ cmake .. $ make ``` 3. Install: ```bash $ sudo make install ``` ## Documentations ## Getting help - [Official Forum](https://bbs.deepin.org/) - [Developer Center](https://github.com/linuxdeepin/developer-center) - [Gitter](https://gitter.im/orgs/linuxdeepin/rooms) - [IRC Channel](https://webchat.freenode.net/?channels=deepin) - [Wiki](https://wiki.deepin.org/) ## Getting involved We encourage you to report issues and contribute changes - [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en) (English) - [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) (中文) - [Translate for your language on Transifex](https://www.transifex.com/linuxdeepin/deepin-file-manager/) ## License util-dfm is licensed under [GPL-3.0-or-later](LICENSE.txt) linuxdeepin-util-dfm-a4307d3/README.zh_CN.md000066400000000000000000000025071523477242100203760ustar00rootroot00000000000000# util-dfm 版权归统信软件技术有限公司所有 由libdfm-io 、libdfm-mount 和 libdfm-burn三个包组成的工具包 ### 依赖 ### 编译依赖 debhelper (>=9), cmake, pkg-config, qtbase5-dev, qtbase5-private-dev, qt5-qmake, libglibmm-2.4-dev, qttools5-dev-tools, libglib2.0-dev, libudisks2-dev, libisoburn-dev, libmediainfo-dev, libsecret-1-dev ## 安装 ### 源码安装 1. Build: ```bash $ cd util-dfm $ mkdir build $ cd build $ cmake .. $ make ``` 3. Install: ```bash $ sudo make install ``` ## 文档 ## 帮助 - [Official Forum](https://bbs.deepin.org/) - [Developer Center](https://github.com/linuxdeepin/developer-center) - [Gitter](https://gitter.im/orgs/linuxdeepin/rooms) - [IRC Channel](https://webchat.freenode.net/?channels=deepin) - [Wiki](https://wiki.deepin.org/) ## 贡献指南 我们鼓励您报告问题并做出更改 - [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en) (English) - [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) (中文) - [Translate for your language on Transifex](https://www.transifex.com/linuxdeepin/deepin-file-manager/) ## 开源许可证 util-dfm 在 [GPL-3.0-or-later](LICENSE.txt)下开发 linuxdeepin-util-dfm-a4307d3/autotests/000077500000000000000000000000001523477242100201465ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/autotests/CMakeLists.txt000066400000000000000000000021021523477242100227010ustar00rootroot00000000000000# Unit tests for util-dfm using Qt Test cmake_minimum_required(VERSION 3.10) project(autotests) # BUILD_UNIT_TESTS is defined in top-level CMakeLists.txt; # this guard acts as a safety net if autotests is somehow included directly if(NOT BUILD_UNIT_TESTS) return() endif() message(STATUS "Building unit tests") # Find Qt Test component based on Qt version if(QT_VERSION_MAJOR EQUAL 6) find_package(Qt6 REQUIRED COMPONENTS Test Core) else() find_package(Qt5 REQUIRED COMPONENTS Test Core) endif() # Common test configuration set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DQT_DEBUG") # Enable testing enable_testing() # Add test subdirs for each module add_subdirectory(dfm-io-tests) add_subdirectory(dfm-mount-tests) add_subdirectory(dfm-burn-tests) add_subdirectory(dfm-search-tests) # Add top-level test target (run all tests) add_custom_target(test-all COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure DEPENDS dfm-io-test dfm-mount-test dfm-burn-test dfm-search-test WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMENT "Running all unit tests..." ) linuxdeepin-util-dfm-a4307d3/autotests/README.md000066400000000000000000000055161523477242100214340ustar00rootroot00000000000000# Unit Tests for util-dfm This directory contains unit tests for the util-dfm libraries using the Qt Test framework. ## Directory Structure ``` autotests/ ├── CMakeLists.txt # Main test configuration ├── README.md # This file ├── dfm-io-tests/ # Tests for dfm-io library │ ├── CMakeLists.txt │ └── tst_dfm_io.cpp ├── dfm-mount-tests/ # Tests for dfm-mount library │ ├── CMakeLists.txt │ └── tst_dfm_mount.cpp ├── dfm-burn-tests/ # Tests for dfm-burn library │ ├── CMakeLists.txt │ └── tst_dfm_burn.cpp └── dfm-search-tests/ # Tests for dfm-search library ├── CMakeLists.txt └── tst_dfm_search.cpp ``` ## Building with Tests By default, unit tests are enabled. To build with tests: ```bash mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX=/usr -j$(nproc) .. cmake --build . ``` To disable building of tests: ```bash cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_UNIT_TESTS=OFF -j$(nproc) .. ``` ## Running Tests ### Run all tests ```bash # From build directory ctest --output-on-failure # Or use the custom target make test-all ``` ### Run individual module tests ```bash # From build directory ./bin/dfm-io-test ./bin/dfm-mount-test ./bin/dfm-burn-test ./bin/dfm-search-test ``` ### Run with verbose output ```bash ctest --verbose # Or for individual tests ./bin/dfm-io-test -vs2 ``` ## Writing New Tests 1. Choose the appropriate test subdirectory for the module you're testing 2. Create a new test file following the naming convention `tst_*.cpp` 3. Include `` header 4. Create a test class inheriting from `QObject` with `Q_OBJECT` macro 5. Use `QTEST_MAIN()` macro at the end of your test file 6. Update the corresponding CMakeLists.txt to include your new file Example test: ```cpp #include class tst_MyTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void myTestFunction(); }; void tst_MyTest::initTestCase() { // Setup that runs once before all tests } void tst_MyTest::cleanupTestCase() { // Cleanup that runs once after all tests } void tst_MyTest::myTestFunction() { QVERIFY(true); // Verifies condition is true QCOMPARE(1 + 1, 2); // Verifies equality QVERIFY_EXCEPTION_THROWN( throw std::runtime_error("Test"), std::runtime_error ); } QTEST_MAIN(tst_MyTest) #include "tst_mytest.moc" ``` ## Qt Version Compatibility The test system automatically detects Qt versions (Qt5 or Qt6) and links against the appropriate test library: - Qt6: `Qt6::Test` - Qt5: `Qt5::Test` Library names are also adjusted based on Qt version: - Qt6: `dfm6-io`, `dfm6-mount`, `dfm6-burn`, `dfm6-search` - Qt5: `dfm-io`, `dfm-mount`, `dfm-burn`, `dfm-search` linuxdeepin-util-dfm-a4307d3/autotests/dfm-burn-tests/000077500000000000000000000000001523477242100230205ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/autotests/dfm-burn-tests/CMakeLists.txt000066400000000000000000000014251523477242100255620ustar00rootroot00000000000000# Unit tests for dfm-burn if(DFM_BUILD_WITH_QT6) set(BURN_TEST_LIB dfm6-burn) set(QT_TEST_LIB Qt6::Test) else() set(BURN_TEST_LIB dfm-burn) set(QT_TEST_LIB Qt5::Test) endif() message(STATUS "Adding unit tests for ${BURN_TEST_LIB}") # Collect test source files file(GLOB_RECURSE TEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h ) # Create the test executable add_executable(dfm-burn-test ${TEST_SRCS} ) # Link against the dfm-burn library and Qt Test target_link_libraries(dfm-burn-test ${BURN_TEST_LIB} ${QT_TEST_LIB} ) # Add include directories target_include_directories(dfm-burn-test PRIVATE ${CMAKE_SOURCE_DIR}/src/dfm-burn ) # Register the test with CTest add_test(NAME dfm-burn-test COMMAND dfm-burn-test) linuxdeepin-util-dfm-a4307d3/autotests/dfm-burn-tests/tst_dfm_burn.cpp000066400000000000000000000012021523477242100262050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include class tst_DfmBurn : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void initialization_test(); }; void tst_DfmBurn::initTestCase() { // Setup that runs once before all tests } void tst_DfmBurn::cleanupTestCase() { // Cleanup that runs once after all tests } void tst_DfmBurn::initialization_test() { // A basic test to verify the test framework is working QVERIFY(true); } QTEST_MAIN(tst_DfmBurn) #include "tst_dfm_burn.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-io-tests/000077500000000000000000000000001523477242100224615ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/autotests/dfm-io-tests/CMakeLists.txt000066400000000000000000000016451523477242100252270ustar00rootroot00000000000000# Unit tests for dfm-io if(DFM_BUILD_WITH_QT6) set(IO_TEST_LIB dfm6-io) set(QT_TEST_LIB Qt6::Test) else() set(IO_TEST_LIB dfm-io) set(QT_TEST_LIB Qt5::Test) endif() message(STATUS "Adding unit tests for ${IO_TEST_LIB}") # Collect test source files file(GLOB_RECURSE TEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h ) # Create the test executable add_executable(dfm-io-test ${TEST_SRCS} ) # Link against the dfm-io library and Qt Test target_link_libraries(dfm-io-test ${IO_TEST_LIB} ${QT_TEST_LIB} ) # Add include directories if(DFM_BUILD_WITH_QT6) target_include_directories(dfm-io-test PRIVATE ${CMAKE_SOURCE_DIR}/src/dfm-io/dfm-io ) else() target_include_directories(dfm-io-test PRIVATE ${CMAKE_SOURCE_DIR}/src/dfm-io/dfm-io ) endif() # Register the test with CTest add_test(NAME dfm-io-test COMMAND dfm-io-test) linuxdeepin-util-dfm-a4307d3/autotests/dfm-io-tests/tst_dfm_io.cpp000066400000000000000000000011661523477242100253200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include class tst_DfmIO : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void initialization_test(); }; void tst_DfmIO::initTestCase() { // Setup that runs once before all tests } void tst_DfmIO::cleanupTestCase() { // Cleanup that runs once after all tests } void tst_DfmIO::initialization_test() { // A basic test to verify the test framework is working QVERIFY(true); } QTEST_MAIN(tst_DfmIO) #include "tst_dfm_io.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-mount-tests/000077500000000000000000000000001523477242100232145ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/autotests/dfm-mount-tests/CMakeLists.txt000066400000000000000000000014431523477242100257560ustar00rootroot00000000000000# Unit tests for dfm-mount if(DFM_BUILD_WITH_QT6) set(MOUNT_TEST_LIB dfm6-mount) set(QT_TEST_LIB Qt6::Test) else() set(MOUNT_TEST_LIB dfm-mount) set(QT_TEST_LIB Qt5::Test) endif() message(STATUS "Adding unit tests for ${MOUNT_TEST_LIB}") # Collect test source files file(GLOB_RECURSE TEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h ) # Create the test executable add_executable(dfm-mount-test ${TEST_SRCS} ) # Link against the dfm-mount library and Qt Test target_link_libraries(dfm-mount-test ${MOUNT_TEST_LIB} ${QT_TEST_LIB} ) # Add include directories target_include_directories(dfm-mount-test PRIVATE ${CMAKE_SOURCE_DIR}/src/dfm-mount ) # Register the test with CTest add_test(NAME dfm-mount-test COMMAND dfm-mount-test) linuxdeepin-util-dfm-a4307d3/autotests/dfm-mount-tests/tst_dfm_mount.cpp000066400000000000000000000012101523477242100265740ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include class tst_DfmMount : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void initialization_test(); }; void tst_DfmMount::initTestCase() { // Setup that runs once before all tests } void tst_DfmMount::cleanupTestCase() { // Cleanup that runs once after all tests } void tst_DfmMount::initialization_test() { // A basic test to verify the test framework is working QVERIFY(true); } QTEST_MAIN(tst_DfmMount) #include "tst_dfm_mount.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/000077500000000000000000000000001523477242100233175ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/CMakeLists.txt000066400000000000000000000036041523477242100260620ustar00rootroot00000000000000# Unit tests for dfm-search if(DFM_BUILD_WITH_QT6) set(SEARCH_TEST_LIB dfm6-search) set(QT_TEST_LIB Qt6::Test) else() set(SEARCH_TEST_LIB dfm-search) set(QT_TEST_LIB Qt5::Test) endif() message(STATUS "Adding unit tests for ${SEARCH_TEST_LIB}") # Collect all test source files (including main.cpp) file(GLOB TEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) # Create a single test executable add_executable(dfm-search-test ${TEST_SRCS} ) target_link_libraries(dfm-search-test ${SEARCH_TEST_LIB} ${QT_TEST_LIB} ) # Add size_parser source for testing (it's part of dfm-searcher client but we test it here) target_sources(dfm-search-test PRIVATE ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/size_parser.cpp ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/time_parser.cpp ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/cli_options.cpp ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/preview_command.cpp ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/preview_output_utils.cpp ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/output/output_formatter.h ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/output/text_output.cpp ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/output/json_output.cpp ${CMAKE_SOURCE_DIR}/3rdparty/testutils/stub-ext/stub-shadow.cpp ) target_include_directories(dfm-search-test PRIVATE ${CMAKE_SOURCE_DIR}/src/dfm-search ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-lib ${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client ${CMAKE_SOURCE_DIR}/3rdparty/testutils/stub-ext ${CMAKE_SOURCE_DIR}/3rdparty/testutils/cpp-stub ) # Pass source directory for locating rule files at runtime target_compile_definitions(dfm-search-test PRIVATE TEST_SOURCE_DIR="${CMAKE_SOURCE_DIR}" ) # Register the test with CTest add_test(NAME dfm-search-test COMMAND dfm-search-test) linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/main.cpp000066400000000000000000000100501523477242100247430ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include // Test object creation functions are defined in their respective .cpp files extern QObject *create_tst_DfmSearch(); extern QObject *create_tst_SearchUtils(); extern QObject *create_tst_TimeRangeFilter(); extern QObject *create_tst_TextSearchAPI(); extern QObject *create_tst_RuleEngine(); extern QObject *create_tst_TimeExtraction(); extern QObject *create_tst_FileTypeExtraction(); extern QObject *create_tst_KeywordExtraction(); extern QObject *create_tst_ParsedIntent(); extern QObject *create_tst_ChineseNLP(); extern QObject *create_tst_SizeRangeFilter(); extern QObject *create_tst_IsSemanticQuery(); extern QObject *create_tst_SearchTarget(); extern QObject *create_tst_SemanticQueryBuilderTarget(); extern QObject *create_tst_LocationExtraction(); extern QObject *create_tst_ContentRetriever(); extern QObject *create_tst_ContentSearchEngine(); extern QObject *create_tst_FileNameSearchEngine(); extern QObject *create_tst_RecentSearchEngine(); extern QObject *create_tst_CliOptions(); extern QObject *create_tst_DConfigParsing(); extern QObject *create_tst_SearchOutput(); int main(int argc, char *argv[]) { int result = 0; // Run all test objects QObject *testObj1 = create_tst_DfmSearch(); result |= QTest::qExec(testObj1, argc, argv); delete testObj1; QObject *testObj2 = create_tst_SearchUtils(); result |= QTest::qExec(testObj2, argc, argv); delete testObj2; QObject *testObj3 = create_tst_TimeRangeFilter(); result |= QTest::qExec(testObj3, argc, argv); delete testObj3; QObject *testObj4 = create_tst_TextSearchAPI(); result |= QTest::qExec(testObj4, argc, argv); delete testObj4; QObject *testObj5 = create_tst_RuleEngine(); result |= QTest::qExec(testObj5, argc, argv); delete testObj5; QObject *testObj6 = create_tst_TimeExtraction(); result |= QTest::qExec(testObj6, argc, argv); delete testObj6; QObject *testObj7 = create_tst_FileTypeExtraction(); result |= QTest::qExec(testObj7, argc, argv); delete testObj7; QObject *testObj8 = create_tst_KeywordExtraction(); result |= QTest::qExec(testObj8, argc, argv); delete testObj8; QObject *testObj9 = create_tst_ParsedIntent(); result |= QTest::qExec(testObj9, argc, argv); delete testObj9; QObject *testObj10 = create_tst_ChineseNLP(); result |= QTest::qExec(testObj10, argc, argv); delete testObj10; QObject *testObj11 = create_tst_SizeRangeFilter(); result |= QTest::qExec(testObj11, argc, argv); delete testObj11; QObject *testObj12 = create_tst_IsSemanticQuery(); result |= QTest::qExec(testObj12, argc, argv); delete testObj12; QObject *testObj13 = create_tst_SearchTarget(); result |= QTest::qExec(testObj13, argc, argv); delete testObj13; QObject *testObj14 = create_tst_SemanticQueryBuilderTarget(); result |= QTest::qExec(testObj14, argc, argv); delete testObj14; QObject *testObj14b = create_tst_LocationExtraction(); result |= QTest::qExec(testObj14b, argc, argv); delete testObj14b; QObject *testObj15 = create_tst_ContentRetriever(); result |= QTest::qExec(testObj15, argc, argv); delete testObj15; QObject *testObj16 = create_tst_ContentSearchEngine(); result |= QTest::qExec(testObj16, argc, argv); delete testObj16; QObject *testObj17 = create_tst_FileNameSearchEngine(); result |= QTest::qExec(testObj17, argc, argv); delete testObj17; QObject *testObj18 = create_tst_RecentSearchEngine(); result |= QTest::qExec(testObj18, argc, argv); delete testObj18; QObject *testObj19 = create_tst_CliOptions(); result |= QTest::qExec(testObj19, argc, argv); delete testObj19; QObject *testObj20 = create_tst_DConfigParsing(); result |= QTest::qExec(testObj20, argc, argv); delete testObj20; QObject *testObj21 = create_tst_SearchOutput(); result |= QTest::qExec(testObj21, argc, argv); delete testObj21; return result; } linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_chinese_nlp.cpp000066400000000000000000003221101523477242100272030ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include "semantic/intentparser.h" #include "semantic/semanticruleengine.h" using namespace DFMSEARCH; static QString rulesDir() { return QStringLiteral(TEST_SOURCE_DIR) + QStringLiteral("/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN"); } // Helper: compare two QStringList as sets (order-independent) static bool setEquals(const QStringList &a, const QStringList &b) { return QSet(a.begin(), a.end()) == QSet(b.begin(), b.end()); } namespace { QHash loadFiletypeExtensions() { const QString rulesPath = QDir(rulesDir()).filePath(QStringLiteral("filetype_rules.json")); QFile file(rulesPath); if (!file.open(QIODevice::ReadOnly)) { qFatal("Failed to open filetype rules: %s", qPrintable(rulesPath)); } QJsonParseError parseError; const QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parseError); if (parseError.error != QJsonParseError::NoError || !doc.isObject()) { qFatal("Failed to parse filetype rules %s: %s", qPrintable(rulesPath), qPrintable(parseError.errorString())); } QHash extensionsByRuleId; const QJsonArray groups = doc.object().value(QStringLiteral("groups")).toArray(); for (const QJsonValue &groupValue : groups) { const QJsonObject groupObj = groupValue.toObject(); if (groupObj.value(QStringLiteral("name")).toString() != QLatin1String("filetype")) { continue; } const QJsonArray rules = groupObj.value(QStringLiteral("rules")).toArray(); for (const QJsonValue &ruleValue : rules) { const QJsonObject ruleObj = ruleValue.toObject(); const QString ruleId = ruleObj.value(QStringLiteral("id")).toString(); const QJsonArray extensions = ruleObj.value(QStringLiteral("metadata")) .toObject() .value(QStringLiteral("extensions")) .toArray(); QStringList values; values.reserve(extensions.size()); for (const QJsonValue &extValue : extensions) { values.append(extValue.toString()); } extensionsByRuleId.insert(ruleId, values); } } return extensionsByRuleId; } QStringList expectedExtsForRule(const char *ruleId) { static const QHash kExtensionsByRuleId = loadFiletypeExtensions(); const auto it = kExtensionsByRuleId.constFind(QLatin1String(ruleId)); if (it == kExtensionsByRuleId.constEnd()) { qFatal("Missing filetype rule in test helper: %s", ruleId); } return it.value(); } QStringList imageExpectedExts() { return expectedExtsForRule("filetype_image"); } QStringList videoExpectedExts() { return expectedExtsForRule("filetype_video"); } QStringList audioExpectedExts() { return expectedExtsForRule("filetype_audio"); } } // namespace class tst_ChineseNLP : public QObject { Q_OBJECT private: SemanticRuleEngine *m_engine = nullptr; IntentParser *m_parser = nullptr; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void init(); // Time preset tests void timePreset_today(); void timePreset_today_alt(); void timePreset_yesterday(); void timePreset_yesterday_variants(); void timePreset_dayBeforeYesterday(); void timePreset_thisWeek_variants(); void timePreset_lastWeek_variants(); void timePreset_thisMonth_variants(); void timePreset_lastMonth_variants(); void timePreset_thisYear_variants(); void timePreset_lastYear_variants(); // Time custom tests void timeCustom_year(); void timeCustom_year_twoDigit(); void timeCustom_month(); void timeCustom_yearMonth(); void timeCustom_yearMonth_separators(); void timeCustom_yearMonth_chineseYear(); void timeCustom_yearMonth_chineseYear_noResidualKeyword(); void timeCustom_date(); void timeCustom_dateSpoken(); void timeCustom_fullDate(); void timeCustom_fullDate_separators(); void timeCustom_yesterday_variants_all(); void timeCustom_lastYear_extra(); // File type tests void fileType_precise_pdf(); void fileType_precise_word(); void fileType_precise_excel(); void fileType_precise_ppt(); void fileType_category_image_variants(); void fileType_category_video_variants(); void fileType_category_audio_variants(); void fileType_category_archive(); void fileType_category_application(); void fileType_category_designSource(); void fileType_general_document(); void fileType_general_spreadsheet(); void fileType_general_presentation(); // Filetype all-synonyms tests (from requirements) void fileType_document_general_allSynonyms(); void fileType_spreadsheet_general_allSynonyms(); void filetype_presentation_general_allSynonyms(); void fileType_image_allSynonyms(); void fileType_video_allSynonyms(); void fileType_audio_allSynonyms(); void fileType_archive_allSynonyms(); void fileType_application_allSynonyms(); void fileType_design_source_allSynonyms(); void fileType_designSource_shadowsImageSubstring(); // Filetype specific+general combination tests (span leak regression) void fileType_specificAndGeneral_pptDocument(); void fileType_specificAndGeneral_xlsSpreadsheet(); void fileType_specificAndGeneral_wordDocument(); void fileType_specificAndGeneral_pdfDocument(); void fileType_specificAndGeneral_pptPresentation(); // Filetype full-extension match tests (regex alternation order regression) void fileType_fullExtensionMatch_pptx(); void fileType_fullExtensionMatch_docx(); void fileType_fullExtensionMatch_xlsx(); // Combined time+type tests void combined_fullDateAndType(); void combined_monthAndType(); void combined_yearAndType(); // Size tests void size_fuzzy_large(); void size_fuzzy_large_synonyms(); void size_fuzzy_small(); void size_dynamic_min(); void size_dynamic_max(); void size_dynamic_between(); void size_chineseUnits_min(); void size_chineseUnits_max(); void size_chineseUnits_range(); void size_noUnit_bytes(); void size_combined_withTime(); void size_combined_withType(); void size_combined_full(); void size_suffix_min(); void size_suffix_max(); void size_suffix_combined(); void size_suffix_chineseUnits(); void size_doesNotShadow_yearMonth(); // Relative time tests void timeRelative_justNow(); void timeRelative_justNow_synonyms(); void timeRelative_recentDays(); void timeRelative_recentDays_synonyms(); void timeRelative_pastFewDays(); void timeRelative_pastFewDays_synonyms(); void timeRelative_aWhileAgo(); void timeRelative_aWhileAgo_synonyms(); void timeRelative_priority_vs_preset(); // Dynamic relative time tests void timeDynamic_recent_days(); void timeDynamic_recent_hours(); void timeDynamic_recent_weeks(); void timeDynamic_recent_months(); void timeDynamic_combined_noKeyword(); void timeDynamic_combined_withType(); void timeDynamic_chineseNumerals(); // Action behavior tests void action_create_birthTime(); void action_create_synonyms(); void action_modify_modifyTime(); void action_modify_synonyms(); void action_default_unspecified(); void action_combined_withTime_create(); void action_combined_withTime_modify(); // Action: recently-used files (DBus RecentManager data source) void action_recent_basic(); void action_recent_synonyms(); void action_recent_timeFieldForced(); void action_recent_combined_withTime(); void action_recent_combined_withFiletype(); void action_recent_combined_withKeyword(); void action_recent_notTriggered_byBareVerb(); void action_recent_consumesTriggerWords(); // Keyword tests void keyword_contains_single(); void keyword_contains_multi(); void keyword_named(); void keyword_contentHas(); void keyword_contentHas_multi(); // Target tests void target_genericFileConsumed(); void target_filenameConsumed(); void target_folderConsumed(); void target_directoryConsumed(); // Noise + unconsumed text tests void noise_action_words(); void noise_polite_words(); void noise_suffix_words(); // Location tests void location_desktop(); void location_download(); void location_documentsDir(); void location_picturesDir(); void location_musicDir(); void location_videosDir(); void location_noLocation(); void location_desktopAndDownload(); void location_wechatPatternRecognition(); void location_wechatConsumesTriggerWords(); void location_imReceiveConsumesTriggerWords(); // Location vs filetype conflict resolution tests void location_filetypeConflict_videosDirMusic(); void location_filetypeConflict_musicDirVideo(); void location_filetypeConflict_picturesDirDocument(); void location_filetypeConflict_documentsDirPicture(); // End-to-end combined tests void combined_timeAndFiletype(); void combined_timeAndFiletype_multi(); void combined_timeAndFiletype_all(); void combined_timeAndKeyword(); void combined_filetypeAndKeyword(); void combined_timeAndFiletypeAndKeyword(); void combined_noiseStripping(); void combined_fullSentence(); void combined_noTime(); void combined_onlyKeyword(); void combined_generalSuppressed(); void combined_contentHasAndType(); }; void tst_ChineseNLP::initTestCase() { // Initialize QCoreApplication for Qt test framework if (!QCoreApplication::instance()) { int argc = 0; new QCoreApplication(argc, nullptr); } m_engine = new SemanticRuleEngine(this); // Load all semantic rule files used by the default parser const QString dir = rulesDir(); QVERIFY2(QDir(dir).exists(), qPrintable(QStringLiteral("Rules dir not found: ") + dir)); const QStringList files = { "noise_rules.json", "time_rules.json", "filetype_rules.json", "keyword_rules.json", "size_rules.json", "action_rules.json", "location_rules.json", "target_rules.json" }; for (const QString &f : files) { const QString path = dir + QLatin1Char('/') + f; bool ok = m_engine->loadRuleFile(path); QVERIFY2(ok, qPrintable(QStringLiteral("Failed to load: ") + path)); } // Verify all groups loaded QVERIFY(m_engine->hasGroup("time")); QVERIFY(m_engine->hasGroup("filetype")); QVERIFY(m_engine->hasGroup("keyword")); QVERIFY(m_engine->hasGroup("noise")); QVERIFY(m_engine->hasGroup("size")); QVERIFY(m_engine->hasGroup("action")); QVERIFY(m_engine->hasGroup("location")); QVERIFY(m_engine->hasGroup("target")); const QStringList groups = m_engine->groupNames(); QCOMPARE(groups.size(), 8); m_parser = new IntentParser(m_engine); // Verify default extractors are initialized QStringList names = m_parser->extractorNames(); QCOMPARE(names.size(), 7); QVERIFY(names.contains("time")); QVERIFY(names.contains("filetype")); QVERIFY(names.contains("size")); QVERIFY(names.contains("action")); QVERIFY(names.contains("location")); QVERIFY(names.contains("target")); QVERIFY(names.contains("keyword")); } void tst_ChineseNLP::cleanupTestCase() { delete m_parser; m_parser = nullptr; } void tst_ChineseNLP::init() { // Each test gets a fresh parse — no shared state between tests } // ===== Time Preset Tests ===== void tst_ChineseNLP::timePreset_today() { ParsedIntent intent; m_parser->parse(QStringLiteral("今天的文件"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); } void tst_ChineseNLP::timePreset_today_alt() { // 今日 and 今日份 ParsedIntent intent1; m_parser->parse(QStringLiteral("今日的文档"), intent1); QCOMPARE(intent1.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent1.timeConstraint().preset(), TimePreset::Today); ParsedIntent intent2; m_parser->parse(QStringLiteral("今日份图片"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent2.timeConstraint().preset(), TimePreset::Today); } void tst_ChineseNLP::timePreset_yesterday() { ParsedIntent intent; m_parser->parse(QStringLiteral("昨天的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Yesterday); } void tst_ChineseNLP::timePreset_yesterday_variants() { const QStringList inputs = { QStringLiteral("昨日"), QStringLiteral("昨晚"), QStringLiteral("昨天上午"), QStringLiteral("昨天下午"), QStringLiteral("昨天晚上") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Yesterday); } } void tst_ChineseNLP::timePreset_dayBeforeYesterday() { ParsedIntent intent; m_parser->parse(QStringLiteral("前天的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::DayBeforeYesterday); } void tst_ChineseNLP::timePreset_thisWeek_variants() { const QStringList inputs = { QStringLiteral("本周"), QStringLiteral("这周"), QStringLiteral("这个星期"), QStringLiteral("这一个星期") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::ThisWeek); } } void tst_ChineseNLP::timePreset_lastWeek_variants() { const QStringList inputs = { QStringLiteral("上周"), QStringLiteral("上个星期"), QStringLiteral("上星期"), QStringLiteral("上一个星期") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::LastWeek); } } void tst_ChineseNLP::timePreset_thisMonth_variants() { const QStringList inputs = { QStringLiteral("本月"), QStringLiteral("这个月"), QStringLiteral("当月") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::ThisMonth); } } void tst_ChineseNLP::timePreset_lastMonth_variants() { const QStringList inputs = { QStringLiteral("上个月"), QStringLiteral("上月") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::LastMonth); } } void tst_ChineseNLP::timePreset_thisYear_variants() { const QStringList inputs = { QStringLiteral("今年"), QStringLiteral("本年"), QStringLiteral("这年") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::ThisYear); } } void tst_ChineseNLP::timePreset_lastYear_variants() { const QStringList inputs = { QStringLiteral("去年"), QStringLiteral("上一年") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::LastYear); } } // ===== Time Custom Tests ===== void tst_ChineseNLP::timeCustom_year() { ParsedIntent intent; m_parser->parse(QStringLiteral("2025年的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent.timeConstraint().customStart().date().month(), 1); QCOMPARE(intent.timeConstraint().customStart().date().day(), 1); QCOMPARE(intent.timeConstraint().customEnd().date().year(), 2025); QCOMPARE(intent.timeConstraint().customEnd().date().month(), 12); QCOMPARE(intent.timeConstraint().customEnd().date().day(), 31); } void tst_ChineseNLP::timeCustom_year_twoDigit() { // Two-digit year: 25 -> 2025 ParsedIntent intent; m_parser->parse(QStringLiteral("25年的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent.timeConstraint().customEnd().date().year(), 2025); } // ===== File Type Tests ===== void tst_ChineseNLP::fileType_precise_pdf() { ParsedIntent intent; m_parser->parse(QStringLiteral("pdf"), intent); QVERIFY(intent.fileExtensions().contains("pdf")); QCOMPARE(intent.fileExtensions().size(), 1); } void tst_ChineseNLP::fileType_precise_word() { ParsedIntent intent; m_parser->parse(QStringLiteral("word"), intent); QVERIFY(setEquals(intent.fileExtensions(), QStringList { "doc", "docx" })); } void tst_ChineseNLP::fileType_precise_excel() { ParsedIntent intent; m_parser->parse(QStringLiteral("excel"), intent); QVERIFY(setEquals(intent.fileExtensions(), QStringList { "xls", "xlsx" })); } void tst_ChineseNLP::fileType_precise_ppt() { ParsedIntent intent; m_parser->parse(QStringLiteral("ppt"), intent); QVERIFY(setEquals(intent.fileExtensions(), QStringList { "ppt", "pptx" })); } void tst_ChineseNLP::fileType_category_image_variants() { const QStringList inputs = { QStringLiteral("图片"), QStringLiteral("照片"), QStringLiteral("截图"), QStringLiteral("壁纸"), QStringLiteral("海报"), QStringLiteral("相片"), QStringLiteral("表情包"), QStringLiteral("图") }; const QStringList expectedExts = imageExpectedExts(); for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExts), qPrintable(QStringLiteral("Failed for input: ") + input)); } } void tst_ChineseNLP::fileType_category_video_variants() { const QStringList inputs = { QStringLiteral("视频"), QStringLiteral("录像"), QStringLiteral("电影"), QStringLiteral("动画"), QStringLiteral("短片"), QStringLiteral("片子") }; const QStringList expectedExts = videoExpectedExts(); for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExts), qPrintable(QStringLiteral("Failed for input: ") + input)); } } void tst_ChineseNLP::fileType_category_audio_variants() { const QStringList inputs = { QStringLiteral("音频"), QStringLiteral("音乐"), QStringLiteral("录音"), QStringLiteral("歌"), QStringLiteral("语音") }; const QStringList expectedExts = audioExpectedExts(); for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExts), qPrintable(QStringLiteral("Failed for input: ") + input)); } } void tst_ChineseNLP::fileType_category_archive() { ParsedIntent intent; m_parser->parse(QStringLiteral("压缩包"), intent); QVERIFY(intent.fileExtensions().contains("zip")); QVERIFY(intent.fileExtensions().contains("gz")); QVERIFY(intent.fileExtensions().contains("rar")); QVERIFY(intent.fileExtensions().contains("7z")); } void tst_ChineseNLP::fileType_category_application() { ParsedIntent intent; m_parser->parse(QStringLiteral("安装包"), intent); QVERIFY(intent.fileExtensions().contains("deb")); QVERIFY(intent.fileExtensions().contains("sh")); } void tst_ChineseNLP::fileType_category_designSource() { ParsedIntent intent; m_parser->parse(QStringLiteral("源文件"), intent); QVERIFY(intent.fileExtensions().contains("psd")); QVERIFY(intent.fileExtensions().contains("ai")); } void tst_ChineseNLP::fileType_general_document() { ParsedIntent intent; m_parser->parse(QStringLiteral("文档"), intent); const QStringList expectedExts = expectedExtsForRule("filetype_document_general"); QVERIFY(setEquals(intent.fileExtensions(), expectedExts)); } void tst_ChineseNLP::fileType_general_spreadsheet() { ParsedIntent intent; m_parser->parse(QStringLiteral("表格"), intent); const QStringList expectedExts = expectedExtsForRule("filetype_spreadsheet_general"); QVERIFY(setEquals(intent.fileExtensions(), expectedExts)); } void tst_ChineseNLP::fileType_general_presentation() { ParsedIntent intent; m_parser->parse(QStringLiteral("幻灯片"), intent); const QStringList expectedExts = expectedExtsForRule("filetype_presentation_general"); QVERIFY(setEquals(intent.fileExtensions(), expectedExts)); } // ===== Keyword Tests ===== void tst_ChineseNLP::keyword_contains_single() { ParsedIntent intent; m_parser->parse(QStringLiteral("包含会议记录的文档"), intent); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("会议记录")); } void tst_ChineseNLP::keyword_contains_multi() { ParsedIntent intent; m_parser->parse(QStringLiteral("包含预算和收入的报告"), intent); QCOMPARE(intent.keywords().size(), 2); QVERIFY(intent.keywords().contains("预算")); QVERIFY(intent.keywords().contains("收入")); } void tst_ChineseNLP::keyword_named() { ParsedIntent intent; m_parser->parse(QStringLiteral("名为方案A的文档"), intent); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("方案A")); } void tst_ChineseNLP::keyword_contentHas() { ParsedIntent intent; m_parser->parse(QStringLiteral("内容包含数据分析的报告"), intent); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("数据分析")); } void tst_ChineseNLP::keyword_contentHas_multi() { ParsedIntent intent; m_parser->parse(QStringLiteral("内容含有产品规划和市场调研的报告"), intent); QCOMPARE(intent.keywords().size(), 2); QVERIFY(intent.keywords().contains("产品规划")); QVERIFY(intent.keywords().contains("市场调研")); } void tst_ChineseNLP::target_genericFileConsumed() { ParsedIntent intent; m_parser->parse(QStringLiteral("文件"), intent); QVERIFY(intent.keywords().isEmpty()); bool matched = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("target_file_generic")) { matched = true; QCOMPARE(span.start(), 0); QCOMPARE(span.end(), 2); break; } } QVERIFY2(matched, "target_file_generic should produce a consumed span"); } void tst_ChineseNLP::target_filenameConsumed() { ParsedIntent intent; m_parser->parse(QStringLiteral("文件名"), intent); QVERIFY(intent.keywords().isEmpty()); bool matched = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("target_filename")) { matched = true; QCOMPARE(span.start(), 0); QCOMPARE(span.end(), 3); break; } } QVERIFY2(matched, "target_filename should produce a consumed span"); } void tst_ChineseNLP::target_folderConsumed() { ParsedIntent intent; m_parser->parse(QStringLiteral("文件夹"), intent); QVERIFY(intent.keywords().isEmpty()); bool matched = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("target_folder")) { matched = true; QCOMPARE(span.start(), 0); QCOMPARE(span.end(), 3); break; } } QVERIFY2(matched, "target_folder should produce a consumed span for 文件夹"); } void tst_ChineseNLP::target_directoryConsumed() { ParsedIntent intent; m_parser->parse(QStringLiteral("目录"), intent); QVERIFY(intent.keywords().isEmpty()); bool matched = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("target_folder")) { matched = true; QCOMPARE(span.start(), 0); QCOMPARE(span.end(), 2); break; } } QVERIFY2(matched, "target_folder should produce a consumed span for 目录"); } // ===== Noise + Unconsumed Text Tests ===== void tst_ChineseNLP::noise_action_words() { // "搜索" is noise; "上周" is time; "图片" is filetype ParsedIntent intent; m_parser->parse(QStringLiteral("搜索上周的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::LastWeek); // Filetype should be matched QVERIFY(!intent.fileExtensions().isEmpty()); // No keywords since all text is consumed QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::noise_polite_words() { // "请帮我找" consumed as noise; "今天" time; "文档" filetype ParsedIntent intent; m_parser->parse(QStringLiteral("请帮我找今天的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); QVERIFY(!intent.fileExtensions().isEmpty()); // All text consumed by noise + time + filetype QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::noise_suffix_words() { // "昨天上午" time; "的照片" noise_suffix ParsedIntent intent; m_parser->parse(QStringLiteral("昨天上午的照片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Yesterday); QVERIFY(!intent.fileExtensions().isEmpty()); QVERIFY(intent.keywords().isEmpty()); } // ===== End-to-End Combined Tests ===== void tst_ChineseNLP::combined_timeAndFiletype() { ParsedIntent intent; m_parser->parse(QStringLiteral("今天的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); const QStringList imageExts = imageExpectedExts(); QVERIFY(setEquals(intent.fileExtensions(), imageExts)); // "的" is consumed by noise_suffix "的图片" QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::combined_timeAndFiletype_multi() { ParsedIntent intent; m_parser->parse(QStringLiteral("今天的图片和视频"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); // Should contain both image and video extensions QVERIFY(intent.fileExtensions().contains("jpg")); QVERIFY(intent.fileExtensions().contains("png")); QVERIFY(intent.fileExtensions().contains("mp4")); QVERIFY(intent.fileExtensions().contains("mkv")); QVERIFY(intent.fileExtensions().contains("avi")); QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::combined_timeAndFiletype_all() { ParsedIntent intent; m_parser->parse(QStringLiteral("今天的图片和视频和音频"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); QVERIFY(intent.fileExtensions().contains("jpg")); QVERIFY(intent.fileExtensions().contains("mp4")); QVERIFY(intent.fileExtensions().contains("mp3")); QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::combined_timeAndKeyword() { // "今天" time, "包含会议记录的" keyword pattern, "文档" filetype // But since keyword pattern matches, filetype_document_general is skipped // because keyword_extractor returns early. The filetype extractor runs // before keyword extractor and matches "文档". ParsedIntent intent; m_parser->parse(QStringLiteral("今天包含会议记录的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); QVERIFY(intent.keywords().contains("会议记录")); // "文档" matches filetype_document_general QVERIFY(!intent.fileExtensions().isEmpty()); } void tst_ChineseNLP::combined_filetypeAndKeyword() { // "名为方案A的" → keyword "方案A"; "pdf" → filetype ParsedIntent intent; m_parser->parse(QStringLiteral("名为方案A的pdf"), intent); QVERIFY(intent.fileExtensions().contains("pdf")); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("方案A")); } void tst_ChineseNLP::combined_timeAndFiletypeAndKeyword() { // "昨天" time, "视频" filetype (priority 150, non-general), // "包含报告的" keyword → "报告" // Note: "报告" also matches filetype_document_general but it's general // and gets skipped since video exts are already in seenExtensions ParsedIntent intent; m_parser->parse(QStringLiteral("昨天的视频和包含报告的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Yesterday); // Video extensions QVERIFY(intent.fileExtensions().contains("mp4")); QVERIFY(intent.fileExtensions().contains("avi")); // Keyword extracted from structured pattern QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("报告")); } void tst_ChineseNLP::combined_noiseStripping() { // "帮我找" noise_action, "今天" time, "会议" unconsumed → keyword, "文档" filetype ParsedIntent intent; m_parser->parse(QStringLiteral("帮我找今天的会议文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); QVERIFY(!intent.fileExtensions().isEmpty()); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("会议")); } void tst_ChineseNLP::combined_fullSentence() { // "请搜索上周的图片和视频" → noise(请,搜索) + time(上周) + filetype(图片,视频) ParsedIntent intent; m_parser->parse(QStringLiteral("请搜索上周的图片和视频"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::LastWeek); QVERIFY(intent.fileExtensions().contains("jpg")); QVERIFY(intent.fileExtensions().contains("mp4")); QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::combined_noTime() { // No time, keyword from "包含数据", filetype from "表格" ParsedIntent intent; m_parser->parse(QStringLiteral("包含数据的表格"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::None); QVERIFY(!intent.fileExtensions().isEmpty()); QVERIFY(intent.fileExtensions().contains("xls")); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("数据")); } void tst_ChineseNLP::combined_onlyKeyword() { // No time, no filetype, only unconsumed text as keyword ParsedIntent intent; m_parser->parse(QStringLiteral("会议记录"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::None); QVERIFY(intent.fileExtensions().isEmpty()); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("会议记录")); } void tst_ChineseNLP::combined_generalSuppressed() { // "pdf" precise (priority 200) wins; "文档" general (priority 100) suppressed ParsedIntent intent; m_parser->parse(QStringLiteral("pdf文档"), intent); QCOMPARE(intent.fileExtensions().size(), 1); QCOMPARE(intent.fileExtensions().first(), QString("pdf")); } void tst_ChineseNLP::combined_contentHasAndType() { // "内容包含测试的报告" → keyword "测试", filetype "报告" (document general) ParsedIntent intent; m_parser->parse(QStringLiteral("内容包含测试的报告"), intent); QVERIFY(intent.keywords().contains("测试")); QVERIFY(!intent.fileExtensions().isEmpty()); // "报告" is in filetype_document_general pattern QVERIFY(intent.fileExtensions().contains("doc")); QVERIFY(intent.fileExtensions().contains("pdf")); } // ===== New Time Custom Tests ===== void tst_ChineseNLP::timeCustom_month() { // "12月" → this month 12 ParsedIntent intent; m_parser->parse(QStringLiteral("12月的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent.timeConstraint().customStart().date().day(), 1); // End should be last day of December QCOMPARE(intent.timeConstraint().customEnd().date().month(), 12); // "5月份" — same month, different syntax ParsedIntent intent2; m_parser->parse(QStringLiteral("5月份的图片"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent2.timeConstraint().customStart().date().month(), 5); // "七月" — Chinese numeral month should work too ParsedIntent intent3; m_parser->parse(QStringLiteral("七月的文件"), intent3); QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent3.timeConstraint().customStart().date().month(), 7); QCOMPARE(intent3.timeConstraint().customEnd().date().month(), 7); // "十二月" — leading-ten Chinese numeral month should work too ParsedIntent intent4; m_parser->parse(QStringLiteral("十二月的文件"), intent4); QCOMPARE(intent4.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent4.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent4.timeConstraint().customEnd().date().month(), 12); } void tst_ChineseNLP::timeCustom_yearMonth() { // "2025年12月" → year=2025, month=12 ParsedIntent intent; m_parser->parse(QStringLiteral("2025年12月的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent.timeConstraint().customStart().date().day(), 1); QCOMPARE(intent.timeConstraint().customEnd().date().year(), 2025); QCOMPARE(intent.timeConstraint().customEnd().date().month(), 12); // "2025年12" — trailing 月 is optional ParsedIntent intent2; m_parser->parse(QStringLiteral("2025年12创建的文件"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent2.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent2.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent2.timeConstraint().customStart().date().day(), 1); // "2025年12月份" — support 月份 suffix too ParsedIntent intent3; m_parser->parse(QStringLiteral("2025年12月份创建的文件"), intent3); QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent3.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent3.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent3.timeConstraint().customStart().date().day(), 1); // "2025年七月" — Chinese numeral month should also be parsed ParsedIntent intent4; m_parser->parse(QStringLiteral("2025年七月的文档"), intent4); QCOMPARE(intent4.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent4.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent4.timeConstraint().customStart().date().month(), 7); QCOMPARE(intent4.timeConstraint().customStart().date().day(), 1); // "2025年十一月" — leading-ten Chinese numeral month in year-month form ParsedIntent intent5; m_parser->parse(QStringLiteral("2025年十一月的文档"), intent5); QCOMPARE(intent5.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent5.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent5.timeConstraint().customStart().date().month(), 11); QCOMPARE(intent5.timeConstraint().customStart().date().day(), 1); } void tst_ChineseNLP::timeCustom_yearMonth_separators() { // "2025-12" — dash separator ParsedIntent intent1; m_parser->parse(QStringLiteral("2025-12的图片"), intent1); QCOMPARE(intent1.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent1.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent1.timeConstraint().customStart().date().month(), 12); // "2025/12" — slash separator ParsedIntent intent2; m_parser->parse(QStringLiteral("2025/12的视频"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent2.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent2.timeConstraint().customStart().date().month(), 12); // "25.12" — dot separator, 2-digit year ParsedIntent intent3; m_parser->parse(QStringLiteral("25.12的文件"), intent3); QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent3.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent3.timeConstraint().customStart().date().month(), 12); } // BUG-372165: Chinese-numeral year ("二五年"/"二零二五年"/"二〇二五年") must be // recognised by the year rules, not leaked into keywords / bound to the current year. void tst_ChineseNLP::timeCustom_yearMonth_chineseYear() { // "二五年十二月" — 2-digit Chinese year + Chinese month → 2025-12 ParsedIntent intent1; m_parser->parse(QStringLiteral("二五年十二月的文档"), intent1); QCOMPARE(intent1.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent1.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent1.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent1.timeConstraint().customStart().date().day(), 1); QCOMPARE(intent1.timeConstraint().customEnd().date().year(), 2025); QCOMPARE(intent1.timeConstraint().customEnd().date().month(), 12); // "二五年12月" — 2-digit Chinese year + Arabic month → 2025-12 ParsedIntent intent2; m_parser->parse(QStringLiteral("二五年12月的文档"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent2.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent2.timeConstraint().customStart().date().month(), 12); // "二零二五年十二月" — 4-digit Chinese year (with 零) + Chinese month → 2025-12 ParsedIntent intent3; m_parser->parse(QStringLiteral("二零二五年十二月的文档"), intent3); QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent3.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent3.timeConstraint().customStart().date().month(), 12); // "二〇二五年十二月" — 4-digit Chinese year (with 〇) + Chinese month → 2025-12 ParsedIntent intent4; m_parser->parse(QStringLiteral("二〇二五年十二月的文档"), intent4); QCOMPARE(intent4.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent4.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent4.timeConstraint().customStart().date().month(), 12); // Regression: a bare Chinese-numeral year ("二零二五年") must be parsed as a year, // spanning the whole year. ParsedIntent intent5; m_parser->parse(QStringLiteral("二零二五年的文档"), intent5); QCOMPARE(intent5.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent5.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent5.timeConstraint().customStart().date().month(), 1); QCOMPARE(intent5.timeConstraint().customEnd().date().year(), 2025); QCOMPARE(intent5.timeConstraint().customEnd().date().month(), 12); // Regression: "十二月的文件" must NOT be mis-parsed as a year-month; it stays a // month of the current year (the year capture group requires >= 2 Chinese digits // and cannot start with 十). ParsedIntent intent6; m_parser->parse(QStringLiteral("十二月的文件"), intent6); QCOMPARE(intent6.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent6.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent6.timeConstraint().customStart().date().year(), QDate::currentDate().year()); } // BUG-372165 scenario: the full叠加 query "二五年十二月创建的表格" must produce a // correct time constraint, the spreadsheet filetype, and NO residual keyword. void tst_ChineseNLP::timeCustom_yearMonth_chineseYear_noResidualKeyword() { ParsedIntent intent; m_parser->parse(QStringLiteral("二五年十二月创建的表格"), intent); // Correct time constraint: the whole month of 2025-12, not the current year. QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent.timeConstraint().customEnd().date().year(), 2025); QCOMPARE(intent.timeConstraint().customEnd().date().month(), 12); // Spreadsheet filetype consumed. QVERIFY(intent.fileExtensions().contains("xls")); QVERIFY(intent.fileExtensions().contains("xlsx")); QVERIFY(intent.fileExtensions().contains("csv")); // The Chinese-numeral year must be fully consumed by a time rule, not leaked // into keywords (which was the root cause of empty search results). QVERIFY2(intent.keywords().isEmpty(), "Chinese-numeral year must not leak into keywords"); // The consumed span must be the year-month rule, covering the whole "二五年十二月". bool hasYearMonth = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("time_exact_year_month")) { hasYearMonth = true; break; } } QVERIFY(hasYearMonth); } void tst_ChineseNLP::timeCustom_date() { // "12月5日" → this year, Dec 5 ParsedIntent intent; m_parser->parse(QStringLiteral("12月5日的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent.timeConstraint().customStart().date().day(), 5); QCOMPARE(intent.timeConstraint().customEnd().date().month(), 12); QCOMPARE(intent.timeConstraint().customEnd().date().day(), 5); QCOMPARE(intent.timeConstraint().customStart().date().year(), QDate::currentDate().year()); } void tst_ChineseNLP::timeCustom_dateSpoken() { // "3月8号" — spoken form with 号 ParsedIntent intent; m_parser->parse(QStringLiteral("3月8号的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().month(), 3); QCOMPARE(intent.timeConstraint().customStart().date().day(), 8); // "七月八号" — Chinese numeral month/day should work too ParsedIntent intent2; m_parser->parse(QStringLiteral("七月八号的图片"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent2.timeConstraint().customStart().date().month(), 7); QCOMPARE(intent2.timeConstraint().customStart().date().day(), 8); // "十二月十五号" — leading-ten Chinese numeral month/day should work too ParsedIntent intent3; m_parser->parse(QStringLiteral("十二月十五号的图片"), intent3); QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent3.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent3.timeConstraint().customStart().date().day(), 15); } void tst_ChineseNLP::timeCustom_fullDate() { // "2025年12月30日" — the specific example from requirements ParsedIntent intent; m_parser->parse(QStringLiteral("2025年12月30日的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent.timeConstraint().customStart().date().day(), 30); QCOMPARE(intent.timeConstraint().customEnd().date().year(), 2025); QCOMPARE(intent.timeConstraint().customEnd().date().month(), 12); QCOMPARE(intent.timeConstraint().customEnd().date().day(), 30); // Verify time boundaries QCOMPARE(intent.timeConstraint().customStart().time().hour(), 0); QCOMPARE(intent.timeConstraint().customEnd().time().hour(), 23); // "2025年12月30" — trailing 日号 is optional ParsedIntent intent2; m_parser->parse(QStringLiteral("2025年12月30创建的文件"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent2.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent2.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent2.timeConstraint().customStart().date().day(), 30); // "2025年七月三十日" — Chinese numeral month/day should work too ParsedIntent intent3; m_parser->parse(QStringLiteral("2025年七月三十日的文档"), intent3); QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent3.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent3.timeConstraint().customStart().date().month(), 7); QCOMPARE(intent3.timeConstraint().customStart().date().day(), 30); } void tst_ChineseNLP::timeCustom_fullDate_separators() { // "2025-12-05" — dash format ParsedIntent intent1; m_parser->parse(QStringLiteral("2025-12-05的文档"), intent1); QCOMPARE(intent1.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent1.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent1.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent1.timeConstraint().customStart().date().day(), 5); // "2025/12/5" — slash format (no leading zero) ParsedIntent intent2; m_parser->parse(QStringLiteral("2025/12/5的文件"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent2.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent2.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent2.timeConstraint().customStart().date().day(), 5); // "2025.12.5" — dot format ParsedIntent intent3; m_parser->parse(QStringLiteral("2025.12.5的图片"), intent3); QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent3.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent3.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent3.timeConstraint().customStart().date().day(), 5); } void tst_ChineseNLP::timeCustom_yesterday_variants_all() { // "昨天下午" and "昨天晚上" — these are multi-char variants ParsedIntent intent1; m_parser->parse(QStringLiteral("昨天下午的图片"), intent1); QCOMPARE(intent1.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent1.timeConstraint().preset(), TimePreset::Yesterday); ParsedIntent intent2; m_parser->parse(QStringLiteral("昨天晚上的视频"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent2.timeConstraint().preset(), TimePreset::Yesterday); } void tst_ChineseNLP::timeCustom_lastYear_extra() { // "去年一整年" — not in current rules, but in requirements // Current rules only have "去年|上一年". Test that "去年" works. ParsedIntent intent; m_parser->parse(QStringLiteral("去年的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::LastYear); } // ===== Filetype All-Synonyms Tests (from requirements) ===== void tst_ChineseNLP::fileType_document_general_allSynonyms() { // Requirements 2.3.2.2.2: 文档, 报告, 文章, 方案, 文本, 资料, 笔记, 稿件 const QStringList inputs = { QStringLiteral("文档"), QStringLiteral("报告"), QStringLiteral("文章"), QStringLiteral("方案"), QStringLiteral("文本"), QStringLiteral("资料"), QStringLiteral("笔记"), QStringLiteral("稿件") }; const QStringList expectedExts = expectedExtsForRule("filetype_document_general"); for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExts), qPrintable(QStringLiteral("Failed for input: ") + input + QStringLiteral(" got: ") + intent.fileExtensions().join(","))); } } void tst_ChineseNLP::fileType_spreadsheet_general_allSynonyms() { // Requirements: 表格, 统计表, 报表, 名单, 数据表, 数据, 明细 // NOTE: "数据" is excluded from rules due to high false-positive risk const QStringList inputs = { QStringLiteral("表格"), QStringLiteral("统计表"), QStringLiteral("报表"), QStringLiteral("名单"), QStringLiteral("数据表"), QStringLiteral("明细") }; const QStringList expectedExts = expectedExtsForRule("filetype_spreadsheet_general"); for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExts), qPrintable(QStringLiteral("Failed for input: ") + input + QStringLiteral(" got: ") + intent.fileExtensions().join(","))); } } void tst_ChineseNLP::filetype_presentation_general_allSynonyms() { // Requirements: 幻灯片, 演示文稿, 汇报, 课件, 宣讲 const QStringList inputs = { QStringLiteral("幻灯片"), QStringLiteral("演示文稿"), QStringLiteral("汇报"), QStringLiteral("课件"), QStringLiteral("宣讲") }; const QStringList expectedExts = expectedExtsForRule("filetype_presentation_general"); for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExts), qPrintable(QStringLiteral("Failed for input: ") + input + QStringLiteral(" got: ") + intent.fileExtensions().join(","))); } } void tst_ChineseNLP::fileType_image_allSynonyms() { // Requirements: 图片, 照片, 截图, 图, 壁纸, 海报, 相片, 表情包 const QStringList inputs = { QStringLiteral("图片"), QStringLiteral("照片"), QStringLiteral("截图"), QStringLiteral("图"), QStringLiteral("壁纸"), QStringLiteral("海报"), QStringLiteral("相片"), QStringLiteral("表情包") }; const QStringList expectedExts = imageExpectedExts(); for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExts), qPrintable(QStringLiteral("Failed for input: ") + input)); } } void tst_ChineseNLP::fileType_video_allSynonyms() { // Requirements: 视频, 录像, 电影, 动画, 短片, 片子 const QStringList inputs = { QStringLiteral("视频"), QStringLiteral("录像"), QStringLiteral("电影"), QStringLiteral("动画"), QStringLiteral("短片"), QStringLiteral("片子") }; const QStringList expectedExts = videoExpectedExts(); for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExts), qPrintable(QStringLiteral("Failed for input: ") + input)); } } void tst_ChineseNLP::fileType_audio_allSynonyms() { // Requirements: 音频, 音乐, 录音, 歌, 语音 const QStringList inputs = { QStringLiteral("音频"), QStringLiteral("音乐"), QStringLiteral("录音"), QStringLiteral("歌"), QStringLiteral("语音") }; const QStringList expectedExts = audioExpectedExts(); for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExts), qPrintable(QStringLiteral("Failed for input: ") + input)); } } void tst_ChineseNLP::fileType_archive_allSynonyms() { // Requirements: 压缩包, 归档, 源码包, 打包文件 const QStringList inputs = { QStringLiteral("压缩包"), QStringLiteral("归档"), QStringLiteral("源码包"), QStringLiteral("打包文件") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(intent.fileExtensions().contains("zip"), qPrintable(QStringLiteral("Missing zip for: ") + input)); } } void tst_ChineseNLP::fileType_application_allSynonyms() { // Requirements: 安装包, 软件, 应用, 脚本, 程序, 包 const QStringList inputs = { QStringLiteral("安装包"), QStringLiteral("软件"), QStringLiteral("应用"), QStringLiteral("脚本"), QStringLiteral("程序"), QStringLiteral("包") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(intent.fileExtensions().contains("deb"), qPrintable(QStringLiteral("Missing deb for: ") + input)); QVERIFY2(intent.fileExtensions().contains("sh"), qPrintable(QStringLiteral("Missing sh for: ") + input)); } } void tst_ChineseNLP::fileType_design_source_allSynonyms() { // Requirements: 源文件, 设计稿, psd, 矢量图, 工程文件 const QStringList inputs = { QStringLiteral("源文件"), QStringLiteral("设计稿"), QStringLiteral("矢量图"), QStringLiteral("工程文件") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(intent.fileExtensions().contains("psd"), qPrintable(QStringLiteral("Missing psd for: ") + input)); QVERIFY2(intent.fileExtensions().contains("ai"), qPrintable(QStringLiteral("Missing ai for: ") + input)); } } void tst_ChineseNLP::fileType_designSource_shadowsImageSubstring() { ParsedIntent intent; m_parser->parse(QStringLiteral("昨天创建的矢量图"), intent); QVERIFY2(setEquals(intent.fileExtensions(), expectedExtsForRule("filetype_design_source")), qPrintable(QStringLiteral("Extensions: ") + intent.fileExtensions().join(','))); bool hasDesignSource = false; bool hasImage = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("filetype_design_source")) { hasDesignSource = true; } else if (span.ruleId() == QLatin1String("filetype_image")) { hasImage = true; } } QVERIFY(hasDesignSource); QVERIFY(!hasImage); } // ===== Filetype specific+general combination tests (span leak regression) ===== // 当 specific filetype 规则与 general filetype 规则同时命中时,general 规则匹配的 // 文本(文档/表格/幻灯片)必须被消费,不得泄漏成 keyword;且 fileExtensions 应只 // 包含 specific 规则的扩展名(general 扩展名被跳过避免过度具体化稀释)。 void tst_ChineseNLP::fileType_specificAndGeneral_pptDocument() { // "找下PPT文档" → filetype_ppt (ppt|pptx) + filetype_document_general (文档) ParsedIntent intent; m_parser->parse(QStringLiteral("找下PPT文档"), intent); QVERIFY2(setEquals(intent.fileExtensions(), QStringList { "ppt", "pptx" }), qPrintable(QStringLiteral("Extensions: ") + intent.fileExtensions().join(','))); QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Keywords should be empty, got: ") + intent.keywords().join(','))); } void tst_ChineseNLP::fileType_specificAndGeneral_xlsSpreadsheet() { // "找下xls表格" → filetype_excel (excel|xls|xlsx) + filetype_spreadsheet_general (表格) ParsedIntent intent; m_parser->parse(QStringLiteral("找下xls表格"), intent); QVERIFY2(setEquals(intent.fileExtensions(), QStringList { "xls", "xlsx" }), qPrintable(QStringLiteral("Extensions: ") + intent.fileExtensions().join(','))); QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Keywords should be empty, got: ") + intent.keywords().join(','))); } void tst_ChineseNLP::fileType_specificAndGeneral_wordDocument() { // "找下Word文档" → filetype_word (word|doc|docx) + filetype_document_general (文档) ParsedIntent intent; m_parser->parse(QStringLiteral("找下Word文档"), intent); QVERIFY2(setEquals(intent.fileExtensions(), QStringList { "doc", "docx" }), qPrintable(QStringLiteral("Extensions: ") + intent.fileExtensions().join(','))); QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Keywords should be empty, got: ") + intent.keywords().join(','))); } void tst_ChineseNLP::fileType_specificAndGeneral_pdfDocument() { // "找下PDF文档" → filetype_pdf (pdf) + filetype_document_general (文档) ParsedIntent intent; m_parser->parse(QStringLiteral("找下PDF文档"), intent); QVERIFY2(setEquals(intent.fileExtensions(), QStringList { "pdf" }), qPrintable(QStringLiteral("Extensions: ") + intent.fileExtensions().join(','))); QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Keywords should be empty, got: ") + intent.keywords().join(','))); } void tst_ChineseNLP::fileType_specificAndGeneral_pptPresentation() { // "找下PPT幻灯片" → filetype_ppt (ppt|pptx) + filetype_presentation_general (幻灯片) ParsedIntent intent; m_parser->parse(QStringLiteral("找下PPT幻灯片"), intent); QVERIFY2(setEquals(intent.fileExtensions(), QStringList { "ppt", "pptx" }), qPrintable(QStringLiteral("Extensions: ") + intent.fileExtensions().join(','))); QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Keywords should be empty, got: ") + intent.keywords().join(','))); } void tst_ChineseNLP::fileType_fullExtensionMatch_pptx() { // "找下pptx文档" → filetype_ppt (pptx|ppt) matches "pptx" fully + filetype_document_general (文档) // Regression: alternation order must be long-prefix-first so "x" doesn't leak into keywords. ParsedIntent intent; m_parser->parse(QStringLiteral("找下pptx文档"), intent); QVERIFY2(setEquals(intent.fileExtensions(), QStringList { "ppt", "pptx" }), qPrintable(QStringLiteral("Extensions: ") + intent.fileExtensions().join(','))); QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Keywords should be empty, got: ") + intent.keywords().join(','))); } void tst_ChineseNLP::fileType_fullExtensionMatch_docx() { // "找下docx文档" → filetype_word (word|docx|doc) matches "docx" fully + filetype_document_general (文档) // Regression: alternation order must be long-prefix-first so "x" doesn't leak into keywords. ParsedIntent intent; m_parser->parse(QStringLiteral("找下docx文档"), intent); QVERIFY2(setEquals(intent.fileExtensions(), QStringList { "doc", "docx" }), qPrintable(QStringLiteral("Extensions: ") + intent.fileExtensions().join(','))); QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Keywords should be empty, got: ") + intent.keywords().join(','))); } void tst_ChineseNLP::fileType_fullExtensionMatch_xlsx() { // "找下xlsx表格" → filetype_excel (excel|xlsx|xls) matches "xlsx" fully + filetype_spreadsheet_general (表格) // Regression: alternation order must be long-prefix-first so "x" doesn't leak into keywords. ParsedIntent intent; m_parser->parse(QStringLiteral("找下xlsx表格"), intent); QVERIFY2(setEquals(intent.fileExtensions(), QStringList { "xls", "xlsx" }), qPrintable(QStringLiteral("Extensions: ") + intent.fileExtensions().join(','))); QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Keywords should be empty, got: ") + intent.keywords().join(','))); } // ===== Combined Time+Type Tests ===== void tst_ChineseNLP::combined_fullDateAndType() { // Requirements example: "2025年12月30日的文档" ParsedIntent intent; m_parser->parse(QStringLiteral("2025年12月30日的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent.timeConstraint().customStart().date().day(), 30); // "文档" matches filetype_document_general QVERIFY(intent.fileExtensions().contains("doc")); QVERIFY(intent.fileExtensions().contains("pdf")); QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::combined_monthAndType() { // "12月的图片" — month + image ParsedIntent intent; m_parser->parse(QStringLiteral("12月的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().month(), 12); QVERIFY(intent.fileExtensions().contains("jpg")); QVERIFY(intent.fileExtensions().contains("png")); QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::combined_yearAndType() { // "2025年的视频" — year + video ParsedIntent intent; m_parser->parse(QStringLiteral("2025年的视频"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent.timeConstraint().customEnd().date().year(), 2025); QVERIFY(intent.fileExtensions().contains("mp4")); QVERIFY(intent.fileExtensions().contains("avi")); } // ===== Size Tests ===== void tst_ChineseNLP::size_fuzzy_large() { ParsedIntent intent; m_parser->parse(QStringLiteral("大文件"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 524288000LL); // 500MB QCOMPARE(intent.sizeConstraint().maxSize(), 0LL); // no upper bound } void tst_ChineseNLP::size_fuzzy_large_synonyms() { const QStringList inputs = { QStringLiteral("很大的"), QStringLiteral("占空间的"), QStringLiteral("几个G的") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input + QStringLiteral("的图片"), intent); QVERIFY2(intent.sizeConstraint().isValid(), qPrintable(QStringLiteral("Size not valid for: ") + input)); } } void tst_ChineseNLP::size_fuzzy_small() { ParsedIntent intent; m_parser->parse(QStringLiteral("小文件"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 0LL); QCOMPARE(intent.sizeConstraint().maxSize(), 1048576LL); // 1MB QCOMPARE(intent.sizeConstraint().includeUpper(), false); } void tst_ChineseNLP::size_dynamic_min() { ParsedIntent intent; m_parser->parse(QStringLiteral("大于500M的文档"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 524288000LL); // 500MB QVERIFY(intent.sizeConstraint().includeLower()); } void tst_ChineseNLP::size_dynamic_max() { ParsedIntent intent; m_parser->parse(QStringLiteral("小于100K的文件"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().maxSize(), 102400LL); // 100KB QCOMPARE(intent.sizeConstraint().minSize(), 0LL); } void tst_ChineseNLP::size_dynamic_between() { ParsedIntent intent; m_parser->parse(QStringLiteral("1M-10M的文件"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 1048576LL); // 1MB QCOMPARE(intent.sizeConstraint().maxSize(), 10485760LL); // 10MB } void tst_ChineseNLP::size_chineseUnits_min() { ParsedIntent intent; m_parser->parse(QStringLiteral("大于100兆的文件"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 104857600LL); // 100MB QVERIFY(intent.sizeConstraint().includeLower()); } void tst_ChineseNLP::size_chineseUnits_max() { ParsedIntent intent; m_parser->parse(QStringLiteral("小于50兆的图片"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().maxSize(), 52428800LL); // 50MB } void tst_ChineseNLP::size_chineseUnits_range() { ParsedIntent intent; m_parser->parse(QStringLiteral("找下大小在1兆到10兆的文件"), intent); QVERIFY(intent.sizeConstraint().isValid()); QVERIFY(intent.keywords().isEmpty()); QCOMPARE(intent.sizeConstraint().minSize(), 1048576LL); // 1MB QCOMPARE(intent.sizeConstraint().maxSize(), 10485760LL); // 10MB } void tst_ChineseNLP::size_noUnit_bytes() { ParsedIntent intent; m_parser->parse(QStringLiteral("小于1024的文件"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().maxSize(), 1024LL); // raw bytes } void tst_ChineseNLP::size_combined_withTime() { ParsedIntent intent; m_parser->parse(QStringLiteral("今天的大文件"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 524288000LL); } void tst_ChineseNLP::size_combined_withType() { ParsedIntent intent; m_parser->parse(QStringLiteral("大文件 pdf"), intent); QVERIFY(intent.sizeConstraint().isValid()); QVERIFY(intent.fileExtensions().contains("pdf")); } void tst_ChineseNLP::size_combined_full() { ParsedIntent intent; m_parser->parse(QStringLiteral("昨天大于100M的图片和视频"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Yesterday); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 104857600LL); // 100MB QVERIFY(intent.fileExtensions().contains("jpg")); QVERIFY(intent.fileExtensions().contains("mp4")); } void tst_ChineseNLP::size_suffix_min() { // Suffix-only min: "10M以上" without prefix keyword ParsedIntent intent; m_parser->parse(QStringLiteral("10M以上的文件"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 10485760LL); // 10MB QCOMPARE(intent.sizeConstraint().maxSize(), 0LL); // "1G以上" — GB unit ParsedIntent intent2; m_parser->parse(QStringLiteral("1G以上的图片"), intent2); QVERIFY(intent2.sizeConstraint().isValid()); QCOMPARE(intent2.sizeConstraint().minSize(), 1073741824LL); // 1GB // "500K以上" — KB unit ParsedIntent intent3; m_parser->parse(QStringLiteral("500K以上的文档"), intent3); QVERIFY(intent3.sizeConstraint().isValid()); QCOMPARE(intent3.sizeConstraint().minSize(), 512000LL); // 500KB } void tst_ChineseNLP::size_suffix_max() { // Suffix-only max: "10M以内" ParsedIntent intent; m_parser->parse(QStringLiteral("10M以内的文档"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 0LL); QCOMPARE(intent.sizeConstraint().maxSize(), 10485760LL); // 10MB // "1G以下" — "以下" variant ParsedIntent intent2; m_parser->parse(QStringLiteral("1G以下的视频"), intent2); QVERIFY(intent2.sizeConstraint().isValid()); QCOMPARE(intent2.sizeConstraint().maxSize(), 1073741824LL); // 1GB } void tst_ChineseNLP::size_suffix_combined() { // The originally reported bug: "10M以上的表格" // Should parse both size constraint and filetype ParsedIntent intent; m_parser->parse(QStringLiteral("10M以上的表格"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 10485760LL); // 10MB QVERIFY(intent.fileExtensions().contains("xls")); QVERIFY(intent.fileExtensions().contains("xlsx")); QVERIFY(intent.fileExtensions().contains("csv")); // "5G以内的压缩包" — size + filetype ParsedIntent intent2; m_parser->parse(QStringLiteral("5G以内的压缩包"), intent2); QVERIFY(intent2.sizeConstraint().isValid()); QCOMPARE(intent2.sizeConstraint().maxSize(), 5368709120LL); // 5GB QVERIFY(intent2.fileExtensions().contains("zip")); QVERIFY(intent2.fileExtensions().contains("rar")); } void tst_ChineseNLP::size_suffix_chineseUnits() { // Chinese unit names with suffix: "100兆以上" ParsedIntent intent; m_parser->parse(QStringLiteral("100兆以上的文件"), intent); QVERIFY(intent.sizeConstraint().isValid()); QCOMPARE(intent.sizeConstraint().minSize(), 104857600LL); // 100MB // "50千以内" ParsedIntent intent2; m_parser->parse(QStringLiteral("50千以内的图片"), intent2); QVERIFY(intent2.sizeConstraint().isValid()); QCOMPARE(intent2.sizeConstraint().maxSize(), 51200LL); // 50KB } void tst_ChineseNLP::size_doesNotShadow_yearMonth() { ParsedIntent intent; m_parser->parse(QStringLiteral("2025-5的音乐"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent.timeConstraint().customStart().date().month(), 5); QVERIFY(!intent.sizeConstraint().isValid()); QVERIFY(setEquals(intent.fileExtensions(), audioExpectedExts())); bool hasTimeYearMonth = false; bool hasAudio = false; bool hasSizeBetween = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("time_exact_year_month")) { hasTimeYearMonth = true; } else if (span.ruleId() == QLatin1String("filetype_audio")) { hasAudio = true; } else if (span.ruleId() == QLatin1String("size_dynamic_between")) { hasSizeBetween = true; } } QVERIFY(hasTimeYearMonth); QVERIFY(hasAudio); QVERIFY(!hasSizeBetween); } // ===== Relative Time Tests ===== void tst_ChineseNLP::timeRelative_justNow() { ParsedIntent intent; m_parser->parse(QStringLiteral("刚刚的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); // End should be very close to NOW (within 2 seconds) const qint64 endDelta = qAbs(intent.timeConstraint().customEnd().secsTo(QDateTime::currentDateTime())); QVERIFY2(endDelta < 2, "Relative end should be close to NOW"); // Start should be ~2 hours ago const qint64 startDelta = qAbs(intent.timeConstraint().customStart().secsTo(QDateTime::currentDateTime().addSecs(-7200))); QVERIFY2(startDelta < 2, "Relative start should be ~2h ago"); QVERIFY(intent.fileExtensions().contains("jpg")); } void tst_ChineseNLP::timeRelative_justNow_synonyms() { const QStringList inputs = { QStringLiteral("刚才"), QStringLiteral("刚"), QStringLiteral("这会儿") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input + QStringLiteral("的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); } } void tst_ChineseNLP::timeRelative_recentDays() { ParsedIntent intent; m_parser->parse(QStringLiteral("最近的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); // End = NOW, Start = NOW - 3 days const qint64 endDelta = qAbs(intent.timeConstraint().customEnd().secsTo(QDateTime::currentDateTime())); QVERIFY2(endDelta < 2, "Recent days end should be NOW"); const qint64 startDelta = qAbs(intent.timeConstraint().customStart().secsTo(QDateTime::currentDateTime().addSecs(-259200))); QVERIFY2(startDelta < 2, "Recent days start should be ~3 days ago"); } void tst_ChineseNLP::timeRelative_recentDays_synonyms() { const QStringList inputs = { QStringLiteral("这几天"), QStringLiteral("近期"), QStringLiteral("这阵子") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input + QStringLiteral("的文件"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); } } void tst_ChineseNLP::timeRelative_pastFewDays() { ParsedIntent intent; m_parser->parse(QStringLiteral("前几天的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); // End = NOW - 3 days, Start = NOW - 7 days const qint64 endDelta = qAbs(intent.timeConstraint().customEnd().secsTo(QDateTime::currentDateTime().addSecs(-259200))); QVERIFY2(endDelta < 2, "Past few days end should be ~3 days ago"); const qint64 startDelta = qAbs(intent.timeConstraint().customStart().secsTo(QDateTime::currentDateTime().addSecs(-604800))); QVERIFY2(startDelta < 2, "Past few days start should be ~7 days ago"); } void tst_ChineseNLP::timeRelative_pastFewDays_synonyms() { const QStringList inputs = { QStringLiteral("之前几天"), QStringLiteral("那些天") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input + QStringLiteral("的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); } } void tst_ChineseNLP::timeRelative_aWhileAgo() { ParsedIntent intent; m_parser->parse(QStringLiteral("之前的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); // End = NOW - 30 days const qint64 endDelta = qAbs(intent.timeConstraint().customEnd().secsTo(QDateTime::currentDateTime().addSecs(-2592000))); QVERIFY2(endDelta < 2, "A while ago end should be ~30 days ago"); // Start should be epoch QCOMPARE(intent.timeConstraint().customStart(), QDateTime::fromMSecsSinceEpoch(0)); } void tst_ChineseNLP::timeRelative_aWhileAgo_synonyms() { const QStringList inputs = { QStringLiteral("早些时候"), QStringLiteral("以前") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input + QStringLiteral("的文件"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); } } void tst_ChineseNLP::timeRelative_priority_vs_preset() { // When both preset and relative could match, preset should win (higher priority) // "今天之前" — "今天" matches time_today (priority 200), "之前" matches time_a_while_ago (priority 80) ParsedIntent intent; m_parser->parse(QStringLiteral("今天之前的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); } // ===== Dynamic Relative Time Tests ===== void tst_ChineseNLP::timeDynamic_recent_days() { // "最近3天" — dynamic relative, should consume all 4 chars ParsedIntent intent; m_parser->parse(QStringLiteral("最近3天"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent.timeConstraint().relativeValue(), 3); QCOMPARE(intent.timeConstraint().relativeUnit(), TimeUnit::Days); // Verify time range: ~3 days ago to now const qint64 startDelta = qAbs(intent.timeConstraint().customStart().secsTo(QDateTime::currentDateTime())); QVERIFY2(startDelta >= 259000 && startDelta <= 259300, "Start should be ~3 days ago"); // "近3天" — shorter variant ParsedIntent intent2; m_parser->parse(QStringLiteral("近3天的图片"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent2.timeConstraint().relativeValue(), 3); QCOMPARE(intent2.timeConstraint().relativeUnit(), TimeUnit::Days); QVERIFY(intent2.fileExtensions().contains("jpg")); // "过去7天" — variant ParsedIntent intent3; m_parser->parse(QStringLiteral("过去7天"), intent3); QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent3.timeConstraint().relativeValue(), 7); // "前3天" — variant ParsedIntent intent4; m_parser->parse(QStringLiteral("前3天的文档"), intent4); QCOMPARE(intent4.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent4.timeConstraint().relativeValue(), 3); } void tst_ChineseNLP::timeDynamic_recent_hours() { // "最近2小时" — dynamic relative hours ParsedIntent intent; m_parser->parse(QStringLiteral("最近2小时的文件"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent.timeConstraint().relativeValue(), 2); QCOMPARE(intent.timeConstraint().relativeUnit(), TimeUnit::Hours); // "近1小时" — shorter variant ParsedIntent intent2; m_parser->parse(QStringLiteral("近1小时"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent2.timeConstraint().relativeValue(), 1); } void tst_ChineseNLP::timeDynamic_recent_weeks() { // "最近2周" — dynamic relative weeks ParsedIntent intent; m_parser->parse(QStringLiteral("最近2周的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent.timeConstraint().relativeValue(), 2); QCOMPARE(intent.timeConstraint().relativeUnit(), TimeUnit::Weeks); // ~14 days const qint64 startDelta = qAbs(intent.timeConstraint().customStart().secsTo(QDateTime::currentDateTime())); QVERIFY2(startDelta >= 1209000 && startDelta <= 1210000, "Start should be ~2 weeks ago"); } void tst_ChineseNLP::timeDynamic_recent_months() { // "最近3个月" — dynamic relative months ParsedIntent intent; m_parser->parse(QStringLiteral("最近3个月的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent.timeConstraint().relativeValue(), 3); QCOMPARE(intent.timeConstraint().relativeUnit(), TimeUnit::Months); QVERIFY(intent.fileExtensions().contains("jpg")); // "近1月" — shorter variant without "个" ParsedIntent intent2; m_parser->parse(QStringLiteral("近1月的文档"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent2.timeConstraint().relativeValue(), 1); } void tst_ChineseNLP::timeDynamic_combined_noKeyword() { // The originally reported bug: "最近3天的表格" // Should parse as: time(recent 3 days) + filetype(spreadsheet) — NO keyword ParsedIntent intent; m_parser->parse(QStringLiteral("最近3天的表格"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent.timeConstraint().relativeValue(), 3); QCOMPARE(intent.timeConstraint().relativeUnit(), TimeUnit::Days); // Filetype should be matched QVERIFY(intent.fileExtensions().contains("xls")); QVERIFY(intent.fileExtensions().contains("xlsx")); QVERIFY(intent.fileExtensions().contains("csv")); // No keywords — "3天" is consumed as part of the time expression QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Expected no keywords, got: ") + intent.keywords().join(","))); // "过去7天的文档" — same pattern ParsedIntent intent2; m_parser->parse(QStringLiteral("过去7天的文档"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent2.timeConstraint().relativeValue(), 7); QVERIFY(!intent2.fileExtensions().isEmpty()); QVERIFY2(intent2.keywords().isEmpty(), qPrintable(QStringLiteral("Expected no keywords, got: ") + intent2.keywords().join(","))); } void tst_ChineseNLP::timeDynamic_combined_withType() { // "最近3天的图片和视频" — time + multiple filetypes, no keyword ParsedIntent intent; m_parser->parse(QStringLiteral("最近3天的图片和视频"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent.timeConstraint().relativeValue(), 3); QVERIFY(intent.fileExtensions().contains("jpg")); QVERIFY(intent.fileExtensions().contains("mp4")); QVERIFY2(intent.keywords().isEmpty(), qPrintable(QStringLiteral("Expected no keywords, got: ") + intent.keywords().join(","))); // "近2个月的压缩包" — time + filetype ParsedIntent intent2; m_parser->parse(QStringLiteral("近2个月的压缩包"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent2.timeConstraint().relativeValue(), 2); QCOMPARE(intent2.timeConstraint().relativeUnit(), TimeUnit::Months); QVERIFY(intent2.fileExtensions().contains("zip")); QVERIFY2(intent2.keywords().isEmpty(), qPrintable(QStringLiteral("Expected no keywords, got: ") + intent2.keywords().join(","))); } void tst_ChineseNLP::timeDynamic_chineseNumerals() { // "最近一周的图片" — 一 = 1 ParsedIntent intent; m_parser->parse(QStringLiteral("最近一周的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent.timeConstraint().relativeValue(), 1); QCOMPARE(intent.timeConstraint().relativeUnit(), TimeUnit::Weeks); QVERIFY(intent.fileExtensions().contains("jpg")); QVERIFY(intent.keywords().isEmpty()); // "最近两周的表格" — 两 = 2 ParsedIntent intent2; m_parser->parse(QStringLiteral("最近两周的表格"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent2.timeConstraint().relativeValue(), 2); QCOMPARE(intent2.timeConstraint().relativeUnit(), TimeUnit::Weeks); QVERIFY(intent2.fileExtensions().contains("xls")); QVERIFY(intent2.keywords().isEmpty()); // "最近三天的文档" — 三 = 3 ParsedIntent intent3; m_parser->parse(QStringLiteral("最近三天的文档"), intent3); QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent3.timeConstraint().relativeValue(), 3); QCOMPARE(intent3.timeConstraint().relativeUnit(), TimeUnit::Days); QVERIFY(!intent3.fileExtensions().isEmpty()); QVERIFY(intent3.keywords().isEmpty()); // "近五个月的视频" — 五 = 5 ParsedIntent intent4; m_parser->parse(QStringLiteral("近五个月的视频"), intent4); QCOMPARE(intent4.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent4.timeConstraint().relativeValue(), 5); QCOMPARE(intent4.timeConstraint().relativeUnit(), TimeUnit::Months); QVERIFY(intent4.fileExtensions().contains("mp4")); QVERIFY(intent4.keywords().isEmpty()); // "过去七天" — 七 = 7 ParsedIntent intent5; m_parser->parse(QStringLiteral("过去七天"), intent5); QCOMPARE(intent5.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent5.timeConstraint().relativeValue(), 7); // "最近十五天" — leading-ten Chinese numeral in dynamic relative time ParsedIntent intent6; m_parser->parse(QStringLiteral("最近十五天"), intent6); QCOMPARE(intent6.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent6.timeConstraint().relativeValue(), 15); QCOMPARE(intent6.timeConstraint().relativeUnit(), TimeUnit::Days); // Mixed: Arabic + Chinese should still work // "最近3天" already tested above } // ===== Action Behavior Tests ===== void tst_ChineseNLP::action_create_birthTime() { ParsedIntent intent; m_parser->parse(QStringLiteral("新建的图片"), intent); QCOMPARE(intent.timeConstraint().timeField(), TimeField::BirthTime); // Action word should be consumed bool actionConsumed = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == "action_create") { actionConsumed = true; break; } } QVERIFY2(actionConsumed, "action_create should produce a consumed span"); } void tst_ChineseNLP::action_create_synonyms() { const QStringList inputs = { QStringLiteral("创建的文档"), QStringLiteral("存下来的图片"), QStringLiteral("保存的文件"), QStringLiteral("新加的视频") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QCOMPARE(intent.timeConstraint().timeField(), TimeField::BirthTime); } } void tst_ChineseNLP::action_modify_modifyTime() { ParsedIntent intent; m_parser->parse(QStringLiteral("修改过的图片"), intent); QCOMPARE(intent.timeConstraint().timeField(), TimeField::ModifyTime); // Action word should be consumed bool actionConsumed = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == "action_modify") { actionConsumed = true; break; } } QVERIFY2(actionConsumed, "action_modify should produce a consumed span"); } void tst_ChineseNLP::action_modify_synonyms() { const QStringList inputs = { QStringLiteral("编辑过的文档"), QStringLiteral("改过的文件"), QStringLiteral("写过的图片"), QStringLiteral("更新的视频"), QStringLiteral("修改的文档") }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QCOMPARE(intent.timeConstraint().timeField(), TimeField::ModifyTime); } } void tst_ChineseNLP::action_default_unspecified() { // Without action words, timeField should remain Unspecified ParsedIntent intent; m_parser->parse(QStringLiteral("今天的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); QCOMPARE(intent.timeConstraint().timeField(), TimeField::Unspecified); } void tst_ChineseNLP::action_combined_withTime_create() { // "新建的今天的文档" — action_create + time today ParsedIntent intent; m_parser->parse(QStringLiteral("新建的今天的文档"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); QCOMPARE(intent.timeConstraint().timeField(), TimeField::BirthTime); // Both time and action spans consumed int consumedCount = intent.consumedSpans().size(); QVERIFY2(consumedCount >= 2, qPrintable(QStringLiteral("Expected >=2 consumed spans, got ") + QString::number(consumedCount))); } void tst_ChineseNLP::action_combined_withTime_modify() { // "昨天修改过的图片" — time yesterday + action_modify ParsedIntent intent; m_parser->parse(QStringLiteral("昨天修改过的图片"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Yesterday); QCOMPARE(intent.timeConstraint().timeField(), TimeField::ModifyTime); QVERIFY(intent.fileExtensions().contains("jpg")); } // ===== Action: Recently-Used Files Tests ===== // action_recent rule (pattern: 打开过的|看过的|浏览过的|读过的|点开过的) // routes to the DBus RecentManager data source via intent.recentOnly. void tst_ChineseNLP::action_recent_basic() { // "打开过的文件" → recentOnly=true, span consumed with ruleId action_recent ParsedIntent intent; m_parser->parse(QStringLiteral("打开过的文件"), intent); QVERIFY2(intent.recentOnly(), "打开过的 should set recentOnly=true"); QCOMPARE(intent.timeConstraint().timeField(), TimeField::ModifyTime); bool actionConsumed = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("action_recent")) { actionConsumed = true; break; } } QVERIFY2(actionConsumed, "action_recent should produce a consumed span"); } void tst_ChineseNLP::action_recent_synonyms() { // All five synonyms from the requirements must trigger recentOnly. const QStringList inputs = { QStringLiteral("打开过的文件"), QStringLiteral("看过的文档"), QStringLiteral("浏览过的图片"), QStringLiteral("读过的pdf"), QStringLiteral("点开过的视频"), }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(intent.recentOnly(), qPrintable(QStringLiteral("recentOnly not set for: ") + input)); QCOMPARE(intent.timeConstraint().timeField(), TimeField::ModifyTime); } } void tst_ChineseNLP::action_recent_timeFieldForced() { // Even when no explicit time constraint is given, action_recent must // force timeField=ModifyTime because recent records only carry modified. ParsedIntent intent; m_parser->parse(QStringLiteral("看过的"), intent); QVERIFY(intent.recentOnly()); QCOMPARE(intent.timeConstraint().timeField(), TimeField::ModifyTime); } void tst_ChineseNLP::action_recent_combined_withTime() { // "昨天看过的" → time yesterday + recentOnly ParsedIntent intent; m_parser->parse(QStringLiteral("昨天看过的文件"), intent); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Yesterday); QCOMPARE(intent.timeConstraint().timeField(), TimeField::ModifyTime); QVERIFY(intent.recentOnly()); } void tst_ChineseNLP::action_recent_combined_withFiletype() { // "看过的pdf" → recentOnly + filetype pdf ParsedIntent intent; m_parser->parse(QStringLiteral("看过的pdf"), intent); QVERIFY(intent.recentOnly()); QVERIFY(intent.fileExtensions().contains("pdf")); // "浏览过的图片" → recentOnly + image extensions ParsedIntent intent2; m_parser->parse(QStringLiteral("浏览过的图片"), intent2); QVERIFY(intent2.recentOnly()); QVERIFY(intent2.fileExtensions().contains("jpg")); } void tst_ChineseNLP::action_recent_combined_withKeyword() { // "看过的预算" → action consumes "看过的", remaining "预算" becomes keyword. // "预算" is not a filetype, so it falls through to keyword extraction. ParsedIntent intent; m_parser->parse(QStringLiteral("看过的预算"), intent); QVERIFY(intent.recentOnly()); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("预算")); } void tst_ChineseNLP::action_recent_notTriggered_byBareVerb() { // Bare verb without "过的" suffix must NOT trigger recent (avoids false positives). // "打开文件" is a plain command, not a recent-files query. ParsedIntent intent; m_parser->parse(QStringLiteral("打开文件"), intent); QVERIFY2(!intent.recentOnly(), "打开 (without 过的) must not trigger recentOnly"); // "看文件" — also bare ParsedIntent intent2; m_parser->parse(QStringLiteral("看文件"), intent2); QVERIFY(!intent2.recentOnly()); } void tst_ChineseNLP::action_recent_consumesTriggerWords() { // Trigger words must be fully consumed and not leak into keywords. const struct { const char *input; const char *trigger; // must NOT appear in any keyword } cases[] = { { "打开过的报告", "打开" }, { "看过的文档", "看过" }, { "浏览过的图片", "浏览" }, { "读过的pdf", "读过" }, { "点开过的视频", "点开" }, }; for (const auto &c : cases) { ParsedIntent intent; m_parser->parse(QString::fromUtf8(c.input), intent); QVERIFY2(intent.recentOnly(), qUtf8Printable("recentOnly not set for: " + QString::fromUtf8(c.input))); for (const QString &kw : intent.keywords()) { QVERIFY2(!kw.contains(QString::fromUtf8(c.trigger)), qUtf8Printable(QString("Keyword '%1' must not contain trigger '%2' (input: %3)") .arg(kw) .arg(QString::fromUtf8(c.trigger)) .arg(c.input))); } } } // ===== Location Tests ===== void tst_ChineseNLP::location_desktop() { // "桌面上的文档" → location(桌面) + filetype(文档) const QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("桌面上的文档"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), desktopPath); QVERIFY(!intent.fileExtensions().isEmpty()); // document type extensions QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::location_download() { // "下载里的图片" → location(下载) + filetype(图片) const QString downloadPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("下载里的图片"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), downloadPath); QVERIFY(intent.fileExtensions().contains("jpg")); QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::location_documentsDir() { // "文档目录里的报告" → location(文档目录) + keyword(报告) const QString docsPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("文档目录里的报告"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), docsPath); } void tst_ChineseNLP::location_picturesDir() { // "图片文件夹里的照片" → location(图片文件夹) + filetype(图片) const QString picsPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("图片文件夹里的照片"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), picsPath); QVERIFY(intent.fileExtensions().contains("jpg")); } void tst_ChineseNLP::location_musicDir() { // "音乐目录里的歌曲" → location(音乐目录) const QString musicPath = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("音乐目录里的歌曲"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), musicPath); } void tst_ChineseNLP::location_videosDir() { // "视频目录下的电影" → location(视频目录) const QString videosPath = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("视频目录下的电影"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), videosPath); } void tst_ChineseNLP::location_noLocation() { // "今天的文档" → no location, default behavior unchanged ParsedIntent intent; m_parser->parse(QStringLiteral("今天的文档"), intent); QVERIFY(intent.searchDirectories().isEmpty()); QVERIFY(!intent.includeHidden()); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Preset); QCOMPARE(intent.timeConstraint().preset(), TimePreset::Today); } void tst_ChineseNLP::location_desktopAndDownload() { // "桌面和下载的图片" → location(桌面,下载) + filetype(图片) const QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); const QString downloadPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("桌面和下载的图片"), intent); QCOMPARE(intent.searchDirectories().size(), 2); QVERIFY(intent.searchDirectories().contains(desktopPath)); QVERIFY(intent.searchDirectories().contains(downloadPath)); QVERIFY(intent.fileExtensions().contains("jpg")); } void tst_ChineseNLP::location_wechatPatternRecognition() { // WeChat custom_path uses glob which may match nothing locally, // so verify pattern recognition via consumedSpans instead of searchDirectories. const QStringList inputs = { "微信的文件", "vx收到的文档", "微信里发给我的", "微信群的文件", "微信接收的压缩包" }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); QVERIFY2(!intent.consumedSpans().isEmpty(), qUtf8Printable("Expected consumed span for: " + input)); } } void tst_ChineseNLP::location_wechatConsumesTriggerWords() { // Verify that location trigger words are fully consumed and do NOT leak // into keywords. Regex alternation must place longer patterns first so that // "微信发我的" wins over "微信", otherwise "发给我" incorrectly becomes a keyword. const struct { const char *input; const char *expectedKeywordNotContaining; // keyword must NOT contain this } cases[] = { { "微信发给我的文档", "发给我" }, { "微信接收的文件", "接收" }, { "微信群里发的", "微信群" }, { "vx的文件", "vx" }, }; for (const auto &c : cases) { ParsedIntent intent; m_parser->parse(QString::fromUtf8(c.input), intent); // Trigger word itself must not appear as a keyword for (const QString &kw : intent.keywords()) { QVERIFY2(!kw.contains(QString::fromUtf8(c.expectedKeywordNotContaining)), qUtf8Printable(QString("Keyword '%1' should not contain trigger fragment '%2' (input: %3)") .arg(kw) .arg(c.expectedKeywordNotContaining) .arg(c.input))); } } } void tst_ChineseNLP::location_imReceiveConsumesTriggerWords() { // action_im_receive trigger words must be fully consumed and NOT leak // into keywords. Create a fake WeChat received-files directory so that // resolveImReceivedPaths() finds a valid path and consumes the span. // We put it under ~/Documents/xwechat_files/tst_*/msg/file which matches // the loc_wechat_data custom_path glob pattern. const QString fakeWechatBase = QDir::homePath() + "/Documents/xwechat_files/tst_unittest_user"; const QString fakeWechatDir = fakeWechatBase + "/msg/file"; const bool dirCreated = QDir().mkpath(fakeWechatDir); if (dirCreated) { const struct { const char *input; const char *trigger; // must NOT appear in any keyword } cases[] = { { "别人传的文档", "别人传" }, { "接收的文件", "接收" }, { "发给我的报告", "发给我" }, { "传给我的资料", "传给我" }, }; for (const auto &c : cases) { ParsedIntent intent; m_parser->parse(QString::fromUtf8(c.input), intent); for (const QString &kw : intent.keywords()) { QVERIFY2(!kw.contains(QString::fromUtf8(c.trigger)), qUtf8Printable(QString("Keyword '%1' must not contain '%2' " "(input: %3, keywords: %4)") .arg(kw) .arg(c.trigger) .arg(c.input) .arg(intent.keywords().join(", ")))); } } // Clean up immediately — do not wait for destructor QDir(fakeWechatBase).removeRecursively(); } else { QSKIP("Could not create fake WeChat dir for IM receive test"); } } // ===== Location vs Filetype Conflict Resolution Tests ===== // // When a word like "视频" appears in "视频目录", the location rule // "视频目录" and the filetype rule "视频" both match. The filetype // match span is fully contained within the location span, so it must // be suppressed — only a distinct filetype word elsewhere in the // query should survive. void tst_ChineseNLP::location_filetypeConflict_videosDirMusic() { // "视频目录中的音乐" → location(Videos) + filetype(audio only) // "视频" is contained in "视频目录" → filetype_video suppressed // "音乐" is outside the location span → filetype_audio survives const QString videosPath = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("视频目录中的音乐"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), videosPath); // Only audio extensions — video extensions must NOT be present QVERIFY2(setEquals(intent.fileExtensions(), audioExpectedExts()), qPrintable("Expected only audio extensions, got: " + intent.fileExtensions().join(", "))); QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::location_filetypeConflict_musicDirVideo() { // "音乐目录中的视频" → location(Music) + filetype(video only) // "音乐" is contained in "音乐目录" → filetype_audio suppressed // "视频" is outside the location span → filetype_video survives const QString musicPath = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("音乐目录中的视频"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), musicPath); QVERIFY2(setEquals(intent.fileExtensions(), videoExpectedExts()), qPrintable("Expected only video extensions, got: " + intent.fileExtensions().join(", "))); QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::location_filetypeConflict_picturesDirDocument() { // "图片目录中的文档" → location(Pictures) + filetype(document_general only) // "图片" is contained in "图片目录" → filetype_image suppressed // "文档" is outside the location span → filetype_document_general survives const QString picsPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("图片目录中的文档"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), picsPath); QVERIFY2(setEquals(intent.fileExtensions(), expectedExtsForRule("filetype_document_general")), qPrintable("Expected only document extensions, got: " + intent.fileExtensions().join(", "))); QVERIFY(intent.keywords().isEmpty()); } void tst_ChineseNLP::location_filetypeConflict_documentsDirPicture() { // "文档目录中的图片" → location(Documents) + filetype(image only) // "文档" is contained in "文档目录" → filetype_document_general suppressed // "图片" is outside the location span → filetype_image survives const QString docsPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); ParsedIntent intent; m_parser->parse(QStringLiteral("文档目录中的图片"), intent); QCOMPARE(intent.searchDirectories().size(), 1); QCOMPARE(intent.searchDirectories().first(), docsPath); // Only image extensions — document extensions must NOT be present QVERIFY2(setEquals(intent.fileExtensions(), imageExpectedExts()), qPrintable("Expected only image extensions, got: " + intent.fileExtensions().join(", "))); QVERIFY(intent.keywords().isEmpty()); } QObject *create_tst_ChineseNLP() { return new tst_ChineseNLP(); } #include "tst_chinese_nlp.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_cli_options.cpp000066400000000000000000000065431523477242100272470ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include "cli_options.h" using namespace dfmsearch; class tst_CliOptions : public QObject { Q_OBJECT private Q_SLOTS: void testFeaturesCount(); void testFeaturesContent(); void testFeaturesOrder(); void testFeaturesJsonOutput(); void testFeaturesTextOutput(); }; // --- supportedFeatures() integrity tests --- void tst_CliOptions::testFeaturesCount() { QStringList features = CliOptions::supportedFeatures(); QCOMPARE(features.size(), 26); } void tst_CliOptions::testFeaturesContent() { QStringList features = CliOptions::supportedFeatures(); QStringList expected = { "filename", "content", "ocr", "recent", "semantic", "indexed", "realtime", "simple", "boolean", "wildcard", "preview", "pinyin", "pinyin-acronym", "case-sensitive", "include-hidden", "file-types", "file-extensions", "exclude", "time-filter", "size-filter", "max-results", "max-preview", "offset", "filename-in-content", "json-output", "verbose" }; QCOMPARE(features, expected); } void tst_CliOptions::testFeaturesOrder() { QStringList features = CliOptions::supportedFeatures(); // Verify first and last items to ensure order is preserved QCOMPARE(features.first(), QStringLiteral("filename")); QCOMPARE(features.last(), QStringLiteral("verbose")); // Verify category boundaries QCOMPARE(features.at(4), QStringLiteral("semantic")); // last search type QCOMPARE(features.at(6), QStringLiteral("realtime")); // last search method QCOMPARE(features.at(9), QStringLiteral("wildcard")); // last query type QCOMPARE(features.at(10), QStringLiteral("preview")); // first functional feature } void tst_CliOptions::testFeaturesJsonOutput() { // Replicate the JSON formatting logic from parse() to verify structure QStringList features = CliOptions::supportedFeatures(); QJsonObject root; root["type"] = QStringLiteral("features"); QJsonArray featuresArray; for (const QString &feature : features) { featuresArray.append(feature); } root["features"] = featuresArray; root["count"] = features.size(); QJsonDocument doc(root); QVERIFY(doc.isObject()); QJsonObject obj = doc.object(); QCOMPARE(obj.value("type").toString(), QStringLiteral("features")); QCOMPARE(obj.value("count").toInt(), 26); QJsonArray arr = obj.value("features").toArray(); QCOMPARE(arr.size(), 26); QCOMPARE(arr.at(0).toString(), QStringLiteral("filename")); QCOMPARE(arr.at(25).toString(), QStringLiteral("verbose")); } void tst_CliOptions::testFeaturesTextOutput() { // Replicate the text output formatting (space-separated) from parse() QStringList features = CliOptions::supportedFeatures(); QString textOutput = features.join(' '); // Should be a single line with 25 spaces separating 26 items QCOMPARE(textOutput.count(' '), 25); QVERIFY(!textOutput.contains('\n')); QVERIFY(textOutput.startsWith(QStringLiteral("filename"))); QVERIFY(textOutput.endsWith(QStringLiteral("verbose"))); } QObject *create_tst_CliOptions() { return new tst_CliOptions(); } #include "tst_cli_options.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_content_retriever.cpp000066400000000000000000000435431523477242100304670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include "utils/contenthighlighter.h" #include #include #include #include #include #include using namespace dfmsearch; using namespace Lucene; namespace { void addStoredDocument(const IndexWriterPtr &writer, SearchType type, const QString &path, const QString &filename, const QString &content) { DocumentPtr doc = newLucene(); if (type == SearchType::Ocr) { doc->add(newLucene(LuceneFieldNames::OcrText::kPath, path.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kFilename, filename.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kOcrContents, content.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); } else { doc->add(newLucene(LuceneFieldNames::Content::kPath, path.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kFilename, filename.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kContents, content.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); } writer->addDocument(doc); } void createIndex(const QString &indexDir, SearchType type) { QDir().mkpath(indexDir); IndexWriterPtr writer = newLucene( FSDirectory::open(indexDir.toStdWString()), newLucene(), true, IndexWriter::MaxFieldLengthLIMITED); if (type == SearchType::Content) { addStoredDocument(writer, type, "/tmp/doc-a.txt", "doc-a.txt", "hello world from content index"); addStoredDocument(writer, type, "/tmp/doc-b.txt", "doc-b.txt", "meeting notes and budget data"); } else { addStoredDocument(writer, type, "/tmp/img-a.png", "img-a.png", "screenshot text from OCR"); } writer->close(); } } // namespace class tst_ContentRetriever : public QObject { Q_OBJECT private Q_SLOTS: void fetchContent_single(); void fetchContent_batch(); void fetchHighlight_usesTemporaryIndex(); void fetchContent_semanticRoutingFailsWhenNoDConfig(); void fetchHighlight_semanticRoutingFailsWhenNoDConfig(); void concurrentFetch_sharedRetriever(); void fetchPreview_noKeyword(); void fetchPreview_withKeyword(); void fetchPreview_keywordNotFound(); void fetchPreview_notIndexed(); void fetchPreview_offsetBeyondContent(); void fetchPreview_unlimitedNoKeyword(); void previewSnippet_basic(); void plainSnippet_basic(); }; void tst_ContentRetriever::fetchContent_single() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; const QString ocrIndexDir = tempDir.path() + "/ocr-index"; createIndex(contentIndexDir, SearchType::Content); createIndex(ocrIndexDir, SearchType::Ocr); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); retriever.setIndexDirectory(SearchType::Ocr, ocrIndexDir); QCOMPARE(retriever.fetchContent("/tmp/doc-a.txt", SearchType::Content), QString("hello world from content index")); QCOMPARE(retriever.fetchContent("/tmp/img-a.png", SearchType::Ocr), QString("screenshot text from OCR")); QVERIFY(retriever.fetchContent("/tmp/missing.txt", SearchType::Content).isEmpty()); } void tst_ContentRetriever::fetchContent_batch() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); const QMap results = retriever.fetchContents( { "/tmp/doc-a.txt", "/tmp/doc-b.txt", "/tmp/missing.txt" }, SearchType::Content); QCOMPARE(results.value("/tmp/doc-a.txt"), QString("hello world from content index")); QCOMPARE(results.value("/tmp/doc-b.txt"), QString("meeting notes and budget data")); QVERIFY(results.contains("/tmp/missing.txt")); QVERIFY(results.value("/tmp/missing.txt").isEmpty()); } void tst_ContentRetriever::fetchHighlight_usesTemporaryIndex() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); HighlightOptions options; options.setMaxPreviewLength(80); const QString snippet = retriever.fetchHighlight("/tmp/doc-b.txt", "budget", SearchType::Content, options); QVERIFY(snippet.contains("budget", Qt::CaseInsensitive)); } void tst_ContentRetriever::fetchContent_semanticRoutingFailsWhenNoDConfig() { // In test environment dconfig is unavailable, so semanticDocExtensions()/semanticPicExtensions() // return empty sets → Semantic type should be rejected with a warning and return empty. ContentRetriever retriever; QVERIFY(retriever.fetchContent("/tmp/doc-a.txt", SearchType::Semantic).isEmpty()); QVERIFY(retriever.fetchContent("/tmp/img-a.png", SearchType::Semantic).isEmpty()); } void tst_ContentRetriever::fetchHighlight_semanticRoutingFailsWhenNoDConfig() { ContentRetriever retriever; HighlightOptions options; QVERIFY(retriever.fetchHighlight("/tmp/doc-b.txt", "budget", SearchType::Semantic, options).isEmpty()); } void tst_ContentRetriever::concurrentFetch_sharedRetriever() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; const QString ocrIndexDir = tempDir.path() + "/ocr-index"; createIndex(contentIndexDir, SearchType::Content); createIndex(ocrIndexDir, SearchType::Ocr); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); retriever.setIndexDirectory(SearchType::Ocr, ocrIndexDir); HighlightOptions options; options.setMaxPreviewLength(80); std::atomic_bool failed { false }; std::vector> tasks; tasks.reserve(8); for (int worker = 0; worker < 8; ++worker) { tasks.emplace_back(std::async(std::launch::async, [&retriever, &options, &failed]() { for (int i = 0; i < 50; ++i) { if (retriever.fetchContent("/tmp/doc-a.txt", SearchType::Content) != QString("hello world from content index")) { failed.store(true); return; } if (retriever.fetchContent("/tmp/img-a.png", SearchType::Ocr) != QString("screenshot text from OCR")) { failed.store(true); return; } const QString snippet = retriever.fetchHighlight("/tmp/doc-b.txt", "budget", SearchType::Content, options); if (!snippet.contains("budget", Qt::CaseInsensitive)) { failed.store(true); return; } } })); } for (auto &task : tasks) { task.get(); } QVERIFY(!failed.load()); } void tst_ContentRetriever::fetchPreview_noKeyword() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); PreviewOptions options; options.setKeyword(QString()); options.setOffset(6); // skip "hello " options.setMaxLength(5); // expect "world" const PreviewResult result = retriever.fetchPreview("/tmp/doc-a.txt", SearchType::Content, options); QVERIFY(result.status() == PreviewStatus::Success); QCOMPARE(result.content(), QString("world")); QCOMPARE(result.charCount(), QString("hello world from content index").size()); QCOMPARE(result.keywordOffset(), -1); } void tst_ContentRetriever::fetchPreview_withKeyword() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); // Content of doc-a.txt is "hello world from content index" // "world" starts at position 6 PreviewOptions options; options.setKeyword("world"); options.setOffset(0); options.setMaxLength(10); const PreviewResult result = retriever.fetchPreview("/tmp/doc-a.txt", SearchType::Content, options); QVERIFY(result.status() == PreviewStatus::Success); QCOMPARE(result.content(), QString("world from")); QCOMPARE(result.charCount(), QString("hello world from content index").size()); QCOMPARE(result.keywordOffset(), 6); } void tst_ContentRetriever::fetchPreview_keywordNotFound() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); // Content of doc-a.txt is "hello world from content index" (30 chars) // offset=300 is beyond the content, so keyword won't be found PreviewOptions options; options.setKeyword("world"); options.setOffset(300); options.setMaxLength(10); const PreviewResult result = retriever.fetchPreview("/tmp/doc-a.txt", SearchType::Content, options); QVERIFY(result.status() == PreviewStatus::Success); QVERIFY(result.content().isEmpty()); QCOMPARE(result.charCount(), QString("hello world from content index").size()); QCOMPARE(result.keywordOffset(), -1); } void tst_ContentRetriever::fetchPreview_notIndexed() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); const PreviewResult missingPathResult = retriever.fetchPreview("/tmp/missing.txt", SearchType::Content); QVERIFY(missingPathResult.status() == PreviewStatus::NotIndexed); QVERIFY(missingPathResult.content().isEmpty()); QCOMPARE(missingPathResult.charCount(), 0); QCOMPARE(missingPathResult.keywordOffset(), -1); const PreviewResult unsupportedSemanticResult = retriever.fetchPreview("/tmp/package.deb", SearchType::Semantic); QVERIFY(unsupportedSemanticResult.status() == PreviewStatus::NotIndexed); QVERIFY(unsupportedSemanticResult.content().isEmpty()); QCOMPARE(unsupportedSemanticResult.charCount(), 0); QCOMPARE(unsupportedSemanticResult.keywordOffset(), -1); } void tst_ContentRetriever::fetchPreview_offsetBeyondContent() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); // No keyword, offset beyond content length → empty content PreviewOptions options; options.setKeyword(QString()); options.setOffset(1000); options.setMaxLength(50); const PreviewResult result = retriever.fetchPreview("/tmp/doc-a.txt", SearchType::Content, options); QVERIFY(result.status() == PreviewStatus::Success); QVERIFY(result.content().isEmpty()); QCOMPARE(result.charCount(), QString("hello world from content index").size()); QCOMPARE(result.keywordOffset(), -1); } void tst_ContentRetriever::fetchPreview_unlimitedNoKeyword() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); // No keyword + maxLength=0 (unlimited) → return all content from offset // Content of doc-a.txt is "hello world from content index" { PreviewOptions options; options.setKeyword(QString()); options.setOffset(0); options.setMaxLength(0); const PreviewResult result = retriever.fetchPreview("/tmp/doc-a.txt", SearchType::Content, options); QVERIFY(result.status() == PreviewStatus::Success); QCOMPARE(result.content(), QString("hello world from content index")); QCOMPARE(result.charCount(), QString("hello world from content index").size()); QCOMPARE(result.keywordOffset(), -1); } // With offset, unlimited still returns all remaining content { PreviewOptions options; options.setKeyword(QString()); options.setOffset(6); // skip "hello " options.setMaxLength(0); const PreviewResult result = retriever.fetchPreview("/tmp/doc-a.txt", SearchType::Content, options); QVERIFY(result.status() == PreviewStatus::Success); QCOMPARE(result.content(), QString("world from content index")); QCOMPARE(result.charCount(), QString("hello world from content index").size()); QCOMPARE(result.keywordOffset(), -1); } } void tst_ContentRetriever::previewSnippet_basic() { using DFMSEARCH::ContentHighlighter::previewSnippet; const QString content = QStringLiteral("hello world from content index"); // No keyword: simple offset + maxLength slice int kwOff = 42; QCOMPARE(previewSnippet(content, 6, 5, QString(), &kwOff), QString("world")); QCOMPARE(kwOff, -1); // With keyword: search from offset, return from match position QCOMPARE(previewSnippet(content, 0, 10, "world", &kwOff), QString("world from")); QCOMPARE(kwOff, 6); // Keyword not found QVERIFY(previewSnippet(content, 0, 10, "nonexistent", &kwOff).isEmpty()); QCOMPARE(kwOff, -1); // Offset beyond content QVERIFY(previewSnippet(content, 1000, 10, QString(), &kwOff).isEmpty()); // Negative offset → empty QVERIFY(previewSnippet(content, -1, 10, QString(), &kwOff).isEmpty()); // maxLength <= 0 means unlimited — returns all content from offset QCOMPARE(previewSnippet(content, 6, 0, QString(), &kwOff), QString("world from content index")); QCOMPARE(kwOff, -1); QCOMPARE(previewSnippet(content, 0, 0, QString(), &kwOff), content); QCOMPARE(kwOff, -1); // With keyword + maxLength=0 (unlimited) — returns from keyword match to end QCOMPARE(previewSnippet(content, 0, 0, "world", &kwOff), QString("world from content index")); QCOMPARE(kwOff, 6); } void tst_ContentRetriever::plainSnippet_basic() { using DFMSEARCH::ContentHighlighter::PlainSnippetResult; using DFMSEARCH::ContentHighlighter::plainSnippet; const QString content = QStringLiteral("0123456789abcdefghijKEYWORDklmnopqrstuvwxyz"); { const PlainSnippetResult result = plainSnippet({ "keyword" }, content, 20, 20); QCOMPARE(result.content, QString("abcdefghijKEYWORDklm")); QCOMPARE(result.snippetOffset, 10); } { const PlainSnippetResult result = plainSnippet({ "keyword", "0123" }, content, 20, 20); QCOMPARE(result.content, QString("0123456789abcdefghij")); QCOMPARE(result.snippetOffset, 0); } { const PlainSnippetResult result = plainSnippet({ "missing" }, content, 20, 20); QVERIFY(result.content.isEmpty()); QCOMPARE(result.snippetOffset, -1); } } QObject *create_tst_ContentRetriever() { return new tst_ContentRetriever(); } #include "tst_content_retriever.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_content_search_engine.cpp000066400000000000000000000532231523477242100312460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace DFMSEARCH; using namespace Lucene; namespace { struct TestDocument { QString path; QString filename; QString content; QString ancestorPath; QString hidden = "N"; qint64 modifyTime = 1710000000; qint64 birthTime = 1700000000; qint64 fileSize = 1024; }; DocumentPtr buildDocument(const TestDocument &docData) { DocumentPtr doc = newLucene(); doc->add(newLucene(LuceneFieldNames::Content::kPath, docData.path.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kFilename, docData.filename.toStdWString(), Field::STORE_YES, Field::INDEX_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kContents, docData.content.toStdWString(), Field::STORE_YES, Field::INDEX_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kAncestorPaths, docData.ancestorPath.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kIsHidden, docData.hidden.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kModifyTime, QString::number(docData.modifyTime).toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kBirthTime, QString::number(docData.birthTime).toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kFileSize, QString::number(docData.fileSize).toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); return doc; } DocumentPtr buildOcrDocument(const TestDocument &docData) { DocumentPtr doc = newLucene(); doc->add(newLucene(LuceneFieldNames::OcrText::kPath, docData.path.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kFilename, docData.filename.toStdWString(), Field::STORE_YES, Field::INDEX_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kOcrContents, docData.content.toStdWString(), Field::STORE_YES, Field::INDEX_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kAncestorPaths, docData.ancestorPath.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kIsHidden, docData.hidden.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kModifyTime, QString::number(docData.modifyTime).toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kBirthTime, QString::number(docData.birthTime).toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kFileSize, QString::number(docData.fileSize).toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kCheckSum, QStringLiteral("checksum-%1").arg(docData.filename).toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); return doc; } void createContentIndex(const QString &indexDir, const QList &documents) { QDir().mkpath(indexDir); IndexWriterPtr writer = newLucene( FSDirectory::open(indexDir.toStdWString()), newLucene(1, 2), true, IndexWriter::MaxFieldLengthLIMITED); for (const TestDocument &doc : documents) { writer->addDocument(buildDocument(doc)); } writer->close(); } void createOcrIndex(const QString &indexDir, const QList &documents) { QDir().mkpath(indexDir); IndexWriterPtr writer = newLucene( FSDirectory::open(indexDir.toStdWString()), newLucene(1, 2), true, IndexWriter::MaxFieldLengthLIMITED); for (const TestDocument &doc : documents) { writer->addDocument(buildOcrDocument(doc)); } writer->close(); } SearchOptions createBaseOptions(const QString &searchPath, const QString &indexDir) { (void) indexDir; SearchOptions options; options.setSearchMethod(SearchMethod::Indexed); options.setSearchPath(searchPath); options.setSyncSearchTimeout(5); return options; } QStringList resultPaths(const SearchResultExpected &expected) { QStringList paths; const SearchResultList results = expected.value(); for (const SearchResult &result : results) { paths.append(result.path()); } return paths; } } // namespace class tst_ContentSearchEngine : public QObject { Q_OBJECT private Q_SLOTS: void search_simpleContent_usesTemporaryIndex(); void search_booleanAnd_matchesContentOnly(); void search_booleanOr_matchesAnyContent(); void search_booleanOr_matchesAnyOfThreeContents(); void search_filenameAndContentMixed_requiresBoth(); void search_mixedAnd_excludesPureFilenameOnlyMatches(); void search_simpleOcr_usesTemporaryIndex(); void search_ocrBooleanAnd_matchesOcrContentOnly(); void search_ocrBooleanOr_matchesAnyOcrContent(); void search_ocrFilenameAndContentMixed_requiresBoth(); void search_ocrMixedAnd_excludesPureFilenameOnlyMatches(); }; void tst_ContentSearchEngine::search_simpleContent_usesTemporaryIndex() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/content-index"; QVERIFY(QDir().mkpath(rootDir)); createContentIndex(indexDir, { { rootDir + "/alpha-report.txt", "alpha-report.txt", "alpha budget summary", rootDir }, { rootDir + "/meeting-notes.txt", "meeting-notes.txt", "meeting notes and timeline", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::contentIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::Content)); engine->setSearchOptions(createBaseOptions(rootDir, indexDir)); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("budget")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/alpha-report.txt" }); } void tst_ContentSearchEngine::search_booleanAnd_matchesContentOnly() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/content-index"; QVERIFY(QDir().mkpath(rootDir)); createContentIndex(indexDir, { { rootDir + "/roadmap.txt", "roadmap.txt", "alpha budget roadmap", rootDir }, { rootDir + "/budget-only.txt", "budget-only.txt", "budget only", rootDir }, { rootDir + "/alpha-only.txt", "alpha-only.txt", "alpha only", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::contentIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::Content)); engine->setSearchOptions(createBaseOptions(rootDir, indexDir)); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "alpha", "budget" }, SearchQuery::BooleanOperator::AND)); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/roadmap.txt" }); } void tst_ContentSearchEngine::search_booleanOr_matchesAnyContent() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/content-index"; QVERIFY(QDir().mkpath(rootDir)); createContentIndex(indexDir, { { rootDir + "/alpha.txt", "alpha.txt", "alpha planning", rootDir }, { rootDir + "/budget.txt", "budget.txt", "budget tracking", rootDir }, { rootDir + "/other.txt", "other.txt", "travel notes", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::contentIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::Content)); engine->setSearchOptions(createBaseOptions(rootDir, indexDir)); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "alpha", "budget" }, SearchQuery::BooleanOperator::OR)); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 2); QVERIFY(paths.contains(rootDir + "/alpha.txt")); QVERIFY(paths.contains(rootDir + "/budget.txt")); } void tst_ContentSearchEngine::search_booleanOr_matchesAnyOfThreeContents() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/content-index"; QVERIFY(QDir().mkpath(rootDir)); createContentIndex(indexDir, { { rootDir + "/alpha.txt", "alpha.txt", "alpha planning", rootDir }, { rootDir + "/budget.txt", "budget.txt", "budget tracking", rootDir }, { rootDir + "/travel.txt", "travel.txt", "travel itinerary", rootDir }, { rootDir + "/other.txt", "other.txt", "miscellaneous notes", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::contentIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::Content)); engine->setSearchOptions(createBaseOptions(rootDir, indexDir)); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "alpha", "budget", "travel" }, SearchQuery::BooleanOperator::OR)); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 3); QVERIFY(paths.contains(rootDir + "/alpha.txt")); QVERIFY(paths.contains(rootDir + "/budget.txt")); QVERIFY(paths.contains(rootDir + "/travel.txt")); QVERIFY(!paths.contains(rootDir + "/other.txt")); } void tst_ContentSearchEngine::search_filenameAndContentMixed_requiresBoth() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/content-index"; QVERIFY(QDir().mkpath(rootDir)); createContentIndex(indexDir, { { rootDir + "/budget-alpha.txt", "budget-alpha.txt", "alpha roadmap", rootDir }, { rootDir + "/budget-gamma.txt", "budget-gamma.txt", "gamma roadmap", rootDir }, { rootDir + "/alpha-only.txt", "alpha-only.txt", "alpha roadmap", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::contentIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir, indexDir); ContentOptionsAPI contentOptions(options); contentOptions.setFilenameKeyword("budget"); std::unique_ptr engine(SearchEngine::create(SearchType::Content)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("alpha")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/budget-alpha.txt" }); } void tst_ContentSearchEngine::search_mixedAnd_excludesPureFilenameOnlyMatches() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/content-index"; QVERIFY(QDir().mkpath(rootDir)); createContentIndex(indexDir, { { rootDir + "/alpha-budget.txt", "alpha-budget.txt", "general notes", rootDir }, { rootDir + "/alpha-plan.txt", "alpha-plan.txt", "budget implementation details", rootDir }, { rootDir + "/budget-plan.txt", "budget-plan.txt", "alpha implementation details", rootDir }, { rootDir + "/alpha-budget-content.txt", "alpha-budget-content.txt", "alpha budget implementation", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::contentIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir, indexDir); ContentOptionsAPI contentOptions(options); contentOptions.setFilenameContentMixedAndSearchEnabled(true); std::unique_ptr engine(SearchEngine::create(SearchType::Content)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "alpha", "budget" }, SearchQuery::BooleanOperator::AND)); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 3); QVERIFY(!paths.contains(rootDir + "/alpha-budget.txt")); QVERIFY(paths.contains(rootDir + "/alpha-plan.txt")); QVERIFY(paths.contains(rootDir + "/budget-plan.txt")); QVERIFY(paths.contains(rootDir + "/alpha-budget-content.txt")); } void tst_ContentSearchEngine::search_simpleOcr_usesTemporaryIndex() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/ocr-docs"; const QString indexDir = tempDir.path() + "/ocr-index"; QVERIFY(QDir().mkpath(rootDir)); createOcrIndex(indexDir, { { rootDir + "/scan-a.png", "scan-a.png", "invoice amount recognized", rootDir }, { rootDir + "/scan-b.png", "scan-b.png", "meeting room whiteboard", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::ocrTextIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::Ocr)); engine->setSearchOptions(createBaseOptions(rootDir, indexDir)); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("invoice")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/scan-a.png" }); } void tst_ContentSearchEngine::search_ocrBooleanAnd_matchesOcrContentOnly() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/ocr-docs"; const QString indexDir = tempDir.path() + "/ocr-index"; QVERIFY(QDir().mkpath(rootDir)); createOcrIndex(indexDir, { { rootDir + "/receipt.png", "receipt.png", "invoice amount total", rootDir }, { rootDir + "/amount-only.png", "amount-only.png", "amount only", rootDir }, { rootDir + "/invoice-only.png", "invoice-only.png", "invoice only", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::ocrTextIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::Ocr)); engine->setSearchOptions(createBaseOptions(rootDir, indexDir)); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "invoice", "amount" }, SearchQuery::BooleanOperator::AND)); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/receipt.png" }); } void tst_ContentSearchEngine::search_ocrBooleanOr_matchesAnyOcrContent() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/ocr-docs"; const QString indexDir = tempDir.path() + "/ocr-index"; QVERIFY(QDir().mkpath(rootDir)); createOcrIndex(indexDir, { { rootDir + "/invoice.png", "invoice.png", "invoice recognized", rootDir }, { rootDir + "/budget.png", "budget.png", "budget recognized", rootDir }, { rootDir + "/other.png", "other.png", "travel receipt", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::ocrTextIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::Ocr)); engine->setSearchOptions(createBaseOptions(rootDir, indexDir)); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "invoice", "budget" }, SearchQuery::BooleanOperator::OR)); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 2); QVERIFY(paths.contains(rootDir + "/invoice.png")); QVERIFY(paths.contains(rootDir + "/budget.png")); } void tst_ContentSearchEngine::search_ocrFilenameAndContentMixed_requiresBoth() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/ocr-docs"; const QString indexDir = tempDir.path() + "/ocr-index"; QVERIFY(QDir().mkpath(rootDir)); createOcrIndex(indexDir, { { rootDir + "/budget-invoice.png", "budget-invoice.png", "invoice details", rootDir }, { rootDir + "/budget-other.png", "budget-other.png", "other details", rootDir }, { rootDir + "/invoice-only.png", "invoice-only.png", "invoice details", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::ocrTextIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir, indexDir); OcrTextOptionsAPI ocrOptions(options); ocrOptions.setFilenameKeyword("budget"); std::unique_ptr engine(SearchEngine::create(SearchType::Ocr)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("invoice")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/budget-invoice.png" }); } void tst_ContentSearchEngine::search_ocrMixedAnd_excludesPureFilenameOnlyMatches() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/ocr-docs"; const QString indexDir = tempDir.path() + "/ocr-index"; QVERIFY(QDir().mkpath(rootDir)); createOcrIndex(indexDir, { { rootDir + "/invoice-budget.png", "invoice-budget.png", "generic text", rootDir }, { rootDir + "/invoice-note.png", "invoice-note.png", "budget recognized", rootDir }, { rootDir + "/budget-note.png", "budget-note.png", "invoice recognized", rootDir }, { rootDir + "/invoice-budget-content.png", "invoice-budget-content.png", "invoice budget recognized", rootDir }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::ocrTextIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir, indexDir); OcrTextOptionsAPI ocrOptions(options); ocrOptions.setFilenameOcrContentMixedAndSearchEnabled(true); std::unique_ptr engine(SearchEngine::create(SearchType::Ocr)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "invoice", "budget" }, SearchQuery::BooleanOperator::AND)); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 3); QVERIFY(!paths.contains(rootDir + "/invoice-budget.png")); QVERIFY(paths.contains(rootDir + "/invoice-note.png")); QVERIFY(paths.contains(rootDir + "/budget-note.png")); QVERIFY(paths.contains(rootDir + "/invoice-budget-content.png")); } QObject *create_tst_ContentSearchEngine() { return new tst_ContentSearchEngine(); } #include "tst_content_search_engine.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_dconfig_parsing.cpp000066400000000000000000000111461523477242100300540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include using namespace DFMSEARCH; class tst_DConfigParsing : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void testSemicolonSeparatedString(); void testSingleValueString(); void testStringListVariant(); void testVariantListVariant(); void testEmptyString(); void testEmptyStringList(); void testInvalidVariant(); void testNonConvertibleType(); void testSemicolonWithEmptyParts(); void testTrailingSemicolon(); }; void tst_DConfigParsing::initTestCase() { } void tst_DConfigParsing::cleanupTestCase() { } void tst_DConfigParsing::testSemicolonSeparatedString() { QVariant value(QString("txt;pdf;docx;xlsx")); auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(result.has_value(), "Should return a value for semicolon-separated string"); QCOMPARE(result->size(), 4); QCOMPARE(result->at(0), QString("txt")); QCOMPARE(result->at(1), QString("pdf")); QCOMPARE(result->at(2), QString("docx")); QCOMPARE(result->at(3), QString("xlsx")); } void tst_DConfigParsing::testSingleValueString() { QVariant value(QString("txt")); auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(result.has_value(), "Should return a value for single string"); QCOMPARE(result->size(), 1); QCOMPARE(result->at(0), QString("txt")); } void tst_DConfigParsing::testStringListVariant() { QStringList list = { "txt", "pdf", "docx" }; QVariant value(list); auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(result.has_value(), "Should return a value for QStringList variant"); QCOMPARE(result->size(), 3); QCOMPARE(result->at(0), QString("txt")); QCOMPARE(result->at(1), QString("pdf")); QCOMPARE(result->at(2), QString("docx")); } void tst_DConfigParsing::testVariantListVariant() { QVariantList list; list << QVariant("home") << QVariant("usr") << QVariant("opt"); QVariant value(list); auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(result.has_value(), "Should return a value for QVariantList variant"); QCOMPARE(result->size(), 3); QCOMPARE(result->at(0), QString("home")); QCOMPARE(result->at(1), QString("usr")); QCOMPARE(result->at(2), QString("opt")); } void tst_DConfigParsing::testEmptyString() { QVariant value(QString("")); auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(result.has_value(), "Should return a value for empty string"); QCOMPARE(result->size(), 1); QVERIFY2(result->at(0).isEmpty(), "Single element should be empty string"); } void tst_DConfigParsing::testEmptyStringList() { QStringList list; QVariant value(list); auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(result.has_value(), "Should return a value for empty QStringList"); QCOMPARE(result->size(), 0); } void tst_DConfigParsing::testInvalidVariant() { QVariant value; auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(!result.has_value(), "Should return nullopt for invalid QVariant"); } void tst_DConfigParsing::testNonConvertibleType() { QVariant value(42); auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(!result.has_value(), "Should return nullopt for non-convertible type (int)"); } void tst_DConfigParsing::testSemicolonWithEmptyParts() { QVariant value(QString("txt;;pdf;")); auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(result.has_value(), "Should return a value"); QCOMPARE(result->size(), 4); QCOMPARE(result->at(0), QString("txt")); QCOMPARE(result->at(1), QString("")); QCOMPARE(result->at(2), QString("pdf")); QCOMPARE(result->at(3), QString("")); } void tst_DConfigParsing::testTrailingSemicolon() { QVariant value(QString("/home;/usr;/opt;")); auto result = SearchUtility::parseVariantToStringList(value); QVERIFY2(result.has_value(), "Should return a value"); QCOMPARE(result->size(), 4); QCOMPARE(result->at(0), QString("/home")); QCOMPARE(result->at(1), QString("/usr")); QCOMPARE(result->at(2), QString("/opt")); QCOMPARE(result->at(3), QString("")); } QObject *create_tst_DConfigParsing() { return new tst_DConfigParsing(); } #include "tst_dconfig_parsing.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_dfm_search.cpp000066400000000000000000000012701523477242100270100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include class tst_DfmSearch : public QObject { Q_OBJECT public Q_SLOTS: void initTestCase(); void cleanupTestCase(); void initialization_test(); }; void tst_DfmSearch::initTestCase() { // Setup that runs once before all tests } void tst_DfmSearch::cleanupTestCase() { // Cleanup that runs once after all tests } void tst_DfmSearch::initialization_test() { // A basic test to verify the test framework is working QVERIFY(true); } QObject *create_tst_DfmSearch() { return new tst_DfmSearch(); } #include "tst_dfm_search.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_filename_search_engine.cpp000066400000000000000000001204401523477242100313500ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace DFMSEARCH; using namespace Lucene; namespace { struct TestDocument { QString path; QString filename; QString fileType; QString fileExt; QString pinyin; QString pinyinAcronym; QString hidden = "N"; qint64 modifyTime = 1710000000; qint64 birthTime = 1700000000; qint64 fileSize = 1024; QString fileSizeStr = "1 KB"; }; QStringList ancestorPathsForDocument(const QString &path) { QStringList ancestors; QFileInfo info(path); QDir dir = info.dir(); while (dir.exists()) { const QString current = QDir::cleanPath(dir.absolutePath()); ancestors.append(current); if (!dir.cdUp()) { break; } } ancestors.removeDuplicates(); return ancestors; } DocumentPtr buildDocument(const TestDocument &docData) { DocumentPtr doc = newLucene(); doc->add(newLucene(LuceneFieldNames::FileName::kFullPath, docData.path.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::FileName::kFileName, docData.filename.toStdWString(), Field::STORE_YES, Field::INDEX_ANALYZED)); doc->add(newLucene(LuceneFieldNames::FileName::kFileNameLower, docData.filename.toLower().toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::FileName::kFileType, docData.fileType.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::FileName::kFileExt, docData.fileExt.toLower().toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::FileName::kPinyin, docData.pinyin.toStdWString(), Field::STORE_YES, Field::INDEX_ANALYZED)); doc->add(newLucene(LuceneFieldNames::FileName::kPinyinAcronym, docData.pinyinAcronym.toStdWString(), Field::STORE_YES, Field::INDEX_ANALYZED)); doc->add(newLucene(LuceneFieldNames::FileName::kIsHidden, docData.hidden.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); NumericFieldPtr modifyTimeField = newLucene(LuceneFieldNames::FileName::kModifyTime, Field::STORE_YES, true); modifyTimeField->setLongValue(docData.modifyTime); doc->add(modifyTimeField); NumericFieldPtr birthTimeField = newLucene(LuceneFieldNames::FileName::kBirthTime, Field::STORE_YES, true); birthTimeField->setLongValue(docData.birthTime); doc->add(birthTimeField); NumericFieldPtr fileSizeField = newLucene(LuceneFieldNames::FileName::kFileSize, Field::STORE_YES, true); fileSizeField->setLongValue(docData.fileSize); doc->add(fileSizeField); doc->add(newLucene(LuceneFieldNames::FileName::kFileSizeStr, docData.fileSizeStr.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); for (const QString &ancestor : ancestorPathsForDocument(docData.path)) { doc->add(newLucene(LuceneFieldNames::FileName::kAncestorPaths, ancestor.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); } return doc; } void createFileNameIndex(const QString &indexDir, const QList &documents) { QDir().mkpath(indexDir); IndexWriterPtr writer = newLucene( FSDirectory::open(indexDir.toStdWString()), newLucene(1, 2), true, IndexWriter::MaxFieldLengthLIMITED); for (const TestDocument &doc : documents) { writer->addDocument(buildDocument(doc)); } writer->close(); } SearchOptions createBaseOptions(const QString &searchPath) { SearchOptions options; options.setSearchMethod(SearchMethod::Indexed); options.setSearchPath(searchPath); options.setSyncSearchTimeout(5); return options; } SearchOptions createRealtimeOptions(const QString &searchPath) { SearchOptions options = createBaseOptions(searchPath); options.setSearchMethod(SearchMethod::Realtime); return options; } bool createFileWithSize(const QString &path, qint64 size) { QFile file(path); if (!file.open(QIODevice::WriteOnly)) { return false; } if (size > 0) { file.write(QByteArray(static_cast(size), 'a')); } file.close(); return true; } QStringList resultPaths(const SearchResultExpected &expected) { QStringList paths; const SearchResultList results = expected.value(); for (const SearchResult &result : results) { paths.append(result.path()); } return paths; } SearchQuery createWildcardQuery(const QString &pattern) { SearchQuery query(pattern, SearchQuery::Type::Wildcard); return query; } } // namespace class tst_FileNameSearchEngine : public QObject { Q_OBJECT private Q_SLOTS: void search_simpleKeyword_matchesIndexedFilename(); void search_booleanAnd_requiresAllTerms(); void search_booleanOr_matchesAnyTerm(); void search_booleanOr_matchesAnyOfThreeTerms(); void search_wildcard_matchesByPattern(); void search_fileTypeFilterOnly_returnsAllMatchingTypes(); void search_extensionFilterOnly_returnsAllMatchingSuffixes(); void search_keywordAndTypeFilter_requiresBoth(); void search_keywordAndExtensionFilter_requiresBoth(); void search_hiddenFiles_excludedByDefault_andIncludedWhenEnabled(); void search_excludedPath_filtersSubtreeAtQueryLayer(); void search_sizeAndTimeFilters_applyOnIndexedFields(); void search_pinyinAndAcronym_queriesMatchIndexedFields(); void search_detailedResults_populatesExtendedAttributes(); void search_emptyKeywordWithoutFilters_returnsValidationError(); void search_invalidFileType_returnsValidationError(); void realtime_simpleKeyword_matchesFilesystemEntries(); void realtime_booleanAndOr_andWildcard_queriesWork(); void realtime_extensionFilters_areApplied(); void realtime_hiddenAndExcludedPath_filtersWork(); void realtime_sizeAndTimeFilters_applyWithoutIndex(); void realtime_detailedResults_populateAttributes(); void realtime_pinyinOption_doesNotProducePinyinMatches(); void realtime_symlinkDirName_matchesInResults(); void realtime_symlinkDir_notRecursedInto(); void realtime_circularSymlinkDir_deduplicated(); }; void tst_FileNameSearchEngine::search_simpleKeyword_matchesIndexedFilename() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/alpha-report.txt", "alpha-report.txt", "doc", "txt" }, { rootDir + "/meeting-notes.txt", "meeting-notes.txt", "doc", "txt" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(createBaseOptions(rootDir)); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("report")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/alpha-report.txt" }); } void tst_FileNameSearchEngine::search_booleanAnd_requiresAllTerms() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/alpha-budget.txt", "alpha-budget.txt", "doc", "txt" }, { rootDir + "/alpha-only.txt", "alpha-only.txt", "doc", "txt" }, { rootDir + "/budget-only.txt", "budget-only.txt", "doc", "txt" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(createBaseOptions(rootDir)); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "alpha", "budget" }, SearchQuery::BooleanOperator::AND)); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/alpha-budget.txt" }); } void tst_FileNameSearchEngine::search_booleanOr_matchesAnyTerm() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/alpha.txt", "alpha.txt", "doc", "txt" }, { rootDir + "/budget.xlsx", "budget.xlsx", "doc", "xlsx" }, { rootDir + "/travel.jpg", "travel.jpg", "pic", "jpg" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(createBaseOptions(rootDir)); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "alpha", "budget" }, SearchQuery::BooleanOperator::OR)); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 2); QVERIFY(paths.contains(rootDir + "/alpha.txt")); QVERIFY(paths.contains(rootDir + "/budget.xlsx")); } void tst_FileNameSearchEngine::search_booleanOr_matchesAnyOfThreeTerms() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/alpha.txt", "alpha.txt", "doc", "txt" }, { rootDir + "/budget.xlsx", "budget.xlsx", "doc", "xlsx" }, { rootDir + "/travel.jpg", "travel.jpg", "pic", "jpg" }, { rootDir + "/summary.pdf", "summary.pdf", "doc", "pdf" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(createBaseOptions(rootDir)); const SearchResultExpected expected = engine->searchSync( SearchQuery::createBooleanQuery({ "alpha", "budget", "travel" }, SearchQuery::BooleanOperator::OR)); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 3); QVERIFY(paths.contains(rootDir + "/alpha.txt")); QVERIFY(paths.contains(rootDir + "/budget.xlsx")); QVERIFY(paths.contains(rootDir + "/travel.jpg")); QVERIFY(!paths.contains(rootDir + "/summary.pdf")); } void tst_FileNameSearchEngine::search_wildcard_matchesByPattern() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/Budget-2026.txt", "Budget-2026.txt", "doc", "txt" }, { rootDir + "/Budget-2025.txt", "Budget-2025.txt", "doc", "txt" }, { rootDir + "/Notes-2026.txt", "Notes-2026.txt", "doc", "txt" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir); options.setCaseSensitive(false); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(createWildcardQuery("budget-202?.txt")); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 2); QVERIFY(paths.contains(rootDir + "/Budget-2026.txt")); QVERIFY(paths.contains(rootDir + "/Budget-2025.txt")); } void tst_FileNameSearchEngine::search_fileTypeFilterOnly_returnsAllMatchingTypes() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/report.txt", "report.txt", "doc", "txt" }, { rootDir + "/slides.pptx", "slides.pptx", "doc", "pptx" }, { rootDir + "/holiday.jpg", "holiday.jpg", "pic", "jpg" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir); FileNameOptionsAPI api(options); api.setFileTypes({ "doc" }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery(QString())); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 2); QVERIFY(paths.contains(rootDir + "/report.txt")); QVERIFY(paths.contains(rootDir + "/slides.pptx")); } void tst_FileNameSearchEngine::search_extensionFilterOnly_returnsAllMatchingSuffixes() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/one.txt", "one.txt", "doc", "txt" }, { rootDir + "/two.TXT", "two.TXT", "doc", "txt" }, { rootDir + "/three.md", "three.md", "doc", "md" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir); FileNameOptionsAPI api(options); api.setFileExtensions({ "txt" }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery(QString())); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 2); QVERIFY(paths.contains(rootDir + "/one.txt")); QVERIFY(paths.contains(rootDir + "/two.TXT")); } void tst_FileNameSearchEngine::search_keywordAndTypeFilter_requiresBoth() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/budget.txt", "budget.txt", "doc", "txt" }, { rootDir + "/budget.jpg", "budget.jpg", "pic", "jpg" }, { rootDir + "/notes.txt", "notes.txt", "doc", "txt" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir); FileNameOptionsAPI api(options); api.setFileTypes({ "doc" }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("budget")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/budget.txt" }); } void tst_FileNameSearchEngine::search_keywordAndExtensionFilter_requiresBoth() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/budget.txt", "budget.txt", "doc", "txt" }, { rootDir + "/budget.md", "budget.md", "doc", "md" }, { rootDir + "/summary.txt", "summary.txt", "doc", "txt" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir); FileNameOptionsAPI api(options); api.setFileExtensions({ "txt" }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("budget")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/budget.txt" }); } void tst_FileNameSearchEngine::search_hiddenFiles_excludedByDefault_andIncludedWhenEnabled() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/visible-plan.txt", "visible-plan.txt", "doc", "txt", "", "", "N" }, { rootDir + "/.hidden-plan.txt", ".hidden-plan.txt", "doc", "txt", "", "", "Y" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); SearchOptions defaultOptions = createBaseOptions(rootDir); engine->setSearchOptions(defaultOptions); const SearchResultExpected defaultExpected = engine->searchSync(SearchQuery::createSimpleQuery("plan")); QVERIFY(defaultExpected.hasValue()); QCOMPARE(resultPaths(defaultExpected), QStringList { rootDir + "/visible-plan.txt" }); SearchOptions includeHiddenOptions = createBaseOptions(rootDir); includeHiddenOptions.setIncludeHidden(true); engine->setSearchOptions(includeHiddenOptions); const SearchResultExpected includeHiddenExpected = engine->searchSync(SearchQuery::createSimpleQuery("plan")); QVERIFY(includeHiddenExpected.hasValue()); QCOMPARE(resultPaths(includeHiddenExpected).size(), 2); } void tst_FileNameSearchEngine::search_excludedPath_filtersSubtreeAtQueryLayer() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString includedDir = rootDir + "/included"; const QString excludedDir = rootDir + "/excluded"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(includedDir)); QVERIFY(QDir().mkpath(excludedDir)); createFileNameIndex(indexDir, { { includedDir + "/budget.txt", "budget.txt", "doc", "txt" }, { excludedDir + "/budget.txt", "budget.txt", "doc", "txt" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir); options.setSearchExcludedPaths({ excludedDir }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("budget")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { includedDir + "/budget.txt" }); } void tst_FileNameSearchEngine::search_sizeAndTimeFilters_applyOnIndexedFields() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/recent-large.txt", "recent-large.txt", "doc", "txt", "", "", "N", 1712000000, 1700000000, 4096, "4 KB" }, { rootDir + "/recent-small.txt", "recent-small.txt", "doc", "txt", "", "", "N", 1712000000, 1700000000, 128, "128 B" }, { rootDir + "/old-large.txt", "old-large.txt", "doc", "txt", "", "", "N", 1701000000, 1690000000, 4096, "4 KB" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir); SizeRangeFilter sizeFilter; sizeFilter.setMin(1024); options.setSizeRangeFilter(sizeFilter); TimeRangeFilter timeFilter; timeFilter.setTimeField(TimeField::ModifyTime) .setRange(QDateTime::fromSecsSinceEpoch(1711500000), QDateTime::fromSecsSinceEpoch(1712500000)); options.setTimeRangeFilter(timeFilter); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("recent")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/recent-large.txt" }); } void tst_FileNameSearchEngine::search_pinyinAndAcronym_queriesMatchIndexedFields() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); createFileNameIndex(indexDir, { { rootDir + "/项目计划.docx", "项目计划.docx", "doc", "docx", "xiangmujihua", "xmjh" }, { rootDir + "/项目总结.docx", "项目总结.docx", "doc", "docx", "xiangmuzongjie", "xmzj" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); SearchOptions pinyinOptions = createBaseOptions(rootDir); FileNameOptionsAPI pinyinApi(pinyinOptions); pinyinApi.setPinyinEnabled(true); engine->setSearchOptions(pinyinOptions); const SearchResultExpected pinyinExpected = engine->searchSync(SearchQuery::createSimpleQuery("xiangmujihua")); QVERIFY(pinyinExpected.hasValue()); QCOMPARE(resultPaths(pinyinExpected), QStringList { rootDir + "/项目计划.docx" }); SearchOptions acronymOptions = createBaseOptions(rootDir); FileNameOptionsAPI acronymApi(acronymOptions); acronymApi.setPinyinAcronymEnabled(true); engine->setSearchOptions(acronymOptions); const SearchResultExpected acronymExpected = engine->searchSync(SearchQuery::createSimpleQuery("xmjh")); QVERIFY(acronymExpected.hasValue()); QCOMPARE(resultPaths(acronymExpected), QStringList { rootDir + "/项目计划.docx" }); } void tst_FileNameSearchEngine::search_detailedResults_populatesExtendedAttributes() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString indexDir = tempDir.path() + "/filename-index"; QVERIFY(QDir().mkpath(rootDir)); const qint64 modifyTime = 1712345678; const qint64 birthTime = 1701234567; createFileNameIndex(indexDir, { { rootDir + "/archive.zip", "archive.zip", "archive", "zip", "", "", "N", modifyTime, birthTime, 2048, "2 KB" }, }); stub_ext::StubExt stub; stub.set_lamda(DFMSEARCH::Global::fileNameIndexDirectory, [&indexDir]() { return indexDir; }); SearchOptions options = createBaseOptions(rootDir); options.setDetailedResultsEnabled(true); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("archive")); QVERIFY(expected.hasValue()); QCOMPARE(expected.value().size(), 1); SearchResult result = expected.value().first(); FileNameResultAPI api(result); QCOMPARE(result.path(), rootDir + "/archive.zip"); QCOMPARE(api.filename(), QString("archive.zip")); QCOMPARE(api.fileExtension(), QString("zip")); QCOMPARE(api.fileType(), QString("archive")); QCOMPARE(api.fileSizeBytes(), qint64(2048)); QCOMPARE(api.size(), QString("2 KB")); QCOMPARE(api.modifyTimestamp(), modifyTime); QCOMPARE(api.birthTimestamp(), birthTime); QCOMPARE(api.isDirectory(), false); QCOMPARE(api.isHidden(), false); } void tst_FileNameSearchEngine::search_emptyKeywordWithoutFilters_returnsValidationError() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(createBaseOptions(rootDir)); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery(QString())); QVERIFY(!expected.hasValue()); QCOMPARE(expected.error().code().value(), static_cast(FileNameSearchErrorCode::KeywordIsEmpty)); } void tst_FileNameSearchEngine::search_invalidFileType_returnsValidationError() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); SearchOptions options = createBaseOptions(rootDir); FileNameOptionsAPI api(options); api.setFileTypes({ "invalid-type" }); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("report")); QVERIFY(!expected.hasValue()); QCOMPARE(expected.error().code().value(), static_cast(FileNameSearchErrorCode::InvalidFileTypes)); } void tst_FileNameSearchEngine::realtime_simpleKeyword_matchesFilesystemEntries() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); QVERIFY(createFileWithSize(rootDir + "/alpha-report.txt", 32)); QVERIFY(createFileWithSize(rootDir + "/meeting-notes.txt", 32)); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(createRealtimeOptions(rootDir)); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("report")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { rootDir + "/alpha-report.txt" }); } void tst_FileNameSearchEngine::realtime_booleanAndOr_andWildcard_queriesWork() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); QVERIFY(createFileWithSize(rootDir + "/alpha-budget.txt", 16)); QVERIFY(createFileWithSize(rootDir + "/alpha-only.txt", 16)); QVERIFY(createFileWithSize(rootDir + "/budget-only.txt", 16)); QVERIFY(createFileWithSize(rootDir + "/Budget-2026.txt", 16)); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); SearchOptions boolOptions = createRealtimeOptions(rootDir); engine->setSearchOptions(boolOptions); const SearchResultExpected andExpected = engine->searchSync( SearchQuery::createBooleanQuery({ "alpha", "budget" }, SearchQuery::BooleanOperator::AND)); QVERIFY(andExpected.hasValue()); QCOMPARE(resultPaths(andExpected), QStringList { rootDir + "/alpha-budget.txt" }); const SearchResultExpected orExpected = engine->searchSync( SearchQuery::createBooleanQuery({ "alpha", "budget" }, SearchQuery::BooleanOperator::OR)); QVERIFY(orExpected.hasValue()); QCOMPARE(orExpected.value().size(), 4); SearchOptions wildcardOptions = createRealtimeOptions(rootDir); wildcardOptions.setCaseSensitive(false); engine->setSearchOptions(wildcardOptions); const SearchResultExpected wildcardExpected = engine->searchSync(createWildcardQuery("budget-202?.txt")); QVERIFY(wildcardExpected.hasValue()); QCOMPARE(resultPaths(wildcardExpected), QStringList { rootDir + "/Budget-2026.txt" }); } void tst_FileNameSearchEngine::realtime_extensionFilters_areApplied() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); QVERIFY(createFileWithSize(rootDir + "/report.txt", 10)); QVERIFY(createFileWithSize(rootDir + "/slides.pptx", 10)); QVERIFY(createFileWithSize(rootDir + "/holiday.jpg", 10)); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); SearchOptions extOptions = createRealtimeOptions(rootDir); FileNameOptionsAPI extApi(extOptions); extApi.setFileExtensions({ "txt", "pptx" }); engine->setSearchOptions(extOptions); const SearchResultExpected extExpected = engine->searchSync(SearchQuery::createSimpleQuery(QString())); QVERIFY(extExpected.hasValue()); QCOMPARE(extExpected.value().size(), 2); } void tst_FileNameSearchEngine::realtime_hiddenAndExcludedPath_filtersWork() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; const QString includedDir = rootDir + "/included"; const QString excludedDir = rootDir + "/excluded"; QVERIFY(QDir().mkpath(includedDir)); QVERIFY(QDir().mkpath(excludedDir)); QVERIFY(createFileWithSize(includedDir + "/visible-plan.txt", 8)); QVERIFY(createFileWithSize(rootDir + "/.hidden-plan.txt", 8)); QVERIFY(createFileWithSize(excludedDir + "/visible-plan.txt", 8)); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); SearchOptions defaultOptions = createRealtimeOptions(rootDir); defaultOptions.setSearchExcludedPaths({ excludedDir }); engine->setSearchOptions(defaultOptions); const SearchResultExpected defaultExpected = engine->searchSync(SearchQuery::createSimpleQuery("plan")); QVERIFY(defaultExpected.hasValue()); QCOMPARE(resultPaths(defaultExpected), QStringList { includedDir + "/visible-plan.txt" }); SearchOptions includeHiddenOptions = createRealtimeOptions(rootDir); includeHiddenOptions.setIncludeHidden(true); includeHiddenOptions.setSearchExcludedPaths({ excludedDir }); engine->setSearchOptions(includeHiddenOptions); const SearchResultExpected includeHiddenExpected = engine->searchSync(SearchQuery::createSimpleQuery("plan")); QVERIFY(includeHiddenExpected.hasValue()); const QStringList paths = resultPaths(includeHiddenExpected); QCOMPARE(paths.size(), 2); QVERIFY(paths.contains(includedDir + "/visible-plan.txt")); QVERIFY(paths.contains(rootDir + "/.hidden-plan.txt")); } void tst_FileNameSearchEngine::realtime_sizeAndTimeFilters_applyWithoutIndex() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); const QString recentLarge = rootDir + "/recent-large.txt"; const QString recentSmall = rootDir + "/recent-small.txt"; const QString oldLarge = rootDir + "/old-large.txt"; QVERIFY(createFileWithSize(recentLarge, 4096)); QVERIFY(createFileWithSize(recentSmall, 128)); QVERIFY(createFileWithSize(oldLarge, 4096)); const QDateTime recentTime = QDateTime::fromSecsSinceEpoch(1712000000); const QDateTime oldTime = QDateTime::fromSecsSinceEpoch(1701000000); QFile recentLargeFile(recentLarge); QFile recentSmallFile(recentSmall); QFile oldLargeFile(oldLarge); QVERIFY(recentLargeFile.open(QIODevice::ReadWrite)); QVERIFY(recentSmallFile.open(QIODevice::ReadWrite)); QVERIFY(oldLargeFile.open(QIODevice::ReadWrite)); QVERIFY(recentLargeFile.setFileTime(recentTime, QFileDevice::FileModificationTime)); QVERIFY(recentSmallFile.setFileTime(recentTime, QFileDevice::FileModificationTime)); QVERIFY(oldLargeFile.setFileTime(oldTime, QFileDevice::FileModificationTime)); recentLargeFile.close(); recentSmallFile.close(); oldLargeFile.close(); SearchOptions options = createRealtimeOptions(rootDir); SizeRangeFilter sizeFilter; sizeFilter.setMin(1024); options.setSizeRangeFilter(sizeFilter); TimeRangeFilter timeFilter; timeFilter.setTimeField(TimeField::ModifyTime) .setRange(QDateTime::fromSecsSinceEpoch(1711500000), QDateTime::fromSecsSinceEpoch(1712500000)); options.setTimeRangeFilter(timeFilter); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("recent")); QVERIFY(expected.hasValue()); QCOMPARE(resultPaths(expected), QStringList { recentLarge }); } void tst_FileNameSearchEngine::realtime_detailedResults_populateAttributes() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); const QString archivePath = rootDir + "/archive.zip"; QVERIFY(createFileWithSize(archivePath, 2048)); SearchOptions options = createRealtimeOptions(rootDir); options.setDetailedResultsEnabled(true); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("archive")); QVERIFY(expected.hasValue()); QCOMPARE(expected.value().size(), 1); SearchResult result = expected.value().first(); FileNameResultAPI api(result); QCOMPARE(result.path(), archivePath); QCOMPARE(api.filename(), QString("archive.zip")); QCOMPARE(api.fileExtension(), QString("zip")); QCOMPARE(api.fileType(), QString("zip")); QCOMPARE(api.fileSizeBytes(), qint64(2048)); QCOMPARE(api.isDirectory(), false); QCOMPARE(api.isHidden(), false); QVERIFY(api.modifyTimestamp() > 0); } void tst_FileNameSearchEngine::realtime_pinyinOption_doesNotProducePinyinMatches() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); QVERIFY(createFileWithSize(rootDir + "/项目计划.docx", 32)); SearchOptions options = createRealtimeOptions(rootDir); FileNameOptionsAPI api(options); api.setPinyinEnabled(true); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(options); const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("xiangmujihua")); QVERIFY(expected.hasValue()); QCOMPARE(expected.value().size(), 0); } void tst_FileNameSearchEngine::realtime_symlinkDirName_matchesInResults() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); // 创建一个真实目录和一个指向它的符号链接目录 const QString realDir = rootDir + "/real-target"; QVERIFY(QDir().mkpath(realDir)); QVERIFY(createFileWithSize(realDir + "/inside.txt", 16)); // 创建指向真实目录的符号链接 const QString symlinkDir = rootDir + "/target 快捷方式"; QVERIFY(QFile::link(realDir, symlinkDir)); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(createRealtimeOptions(rootDir)); // 搜索关键词应匹配符号链接目录名称 const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("快捷方式")); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 1); QCOMPARE(paths.first(), symlinkDir); } void tst_FileNameSearchEngine::realtime_symlinkDir_notRecursedInto() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); // 真实目录放在搜索根目录之外,确保只有通过 symlink 才能到达 const QString realDir = tempDir.path() + "/outside-target"; QVERIFY(QDir().mkpath(realDir)); // 创建指向真实目录的符号链接(链接位于搜索根目录内) const QString symlinkDir = rootDir + "/symlink"; QVERIFY(QFile::link(realDir, symlinkDir)); // 在真实目录内创建一个文件,该文件名不会出现在搜索路径其他位置 QVERIFY(createFileWithSize(realDir + "/unique-inside-file.txt", 16)); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(createRealtimeOptions(rootDir)); // 搜索真实目录内部的文件,应该找不到(因为不会递归进入 symlink) const SearchResultExpected expected = engine->searchSync( SearchQuery::createSimpleQuery("unique-inside-file")); QVERIFY(expected.hasValue()); QCOMPARE(expected.value().size(), 0); } void tst_FileNameSearchEngine::realtime_circularSymlinkDir_deduplicated() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString rootDir = tempDir.path() + "/docs"; QVERIFY(QDir().mkpath(rootDir)); // 创建一个指向自身祖先目录的循环符号链接 const QString symlinkDir = rootDir + "/loop-link"; QVERIFY(QFile::link(rootDir, symlinkDir)); std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); engine->setSearchOptions(createRealtimeOptions(rootDir)); // 搜索应该正常完成,不会因循环链接而死循环 const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("loop-link")); QVERIFY(expected.hasValue()); const QStringList paths = resultPaths(expected); QCOMPARE(paths.size(), 1); QCOMPARE(paths.first(), symlinkDir); } QObject *create_tst_FileNameSearchEngine() { return new tst_FileNameSearchEngine(); } #include "tst_filename_search_engine.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_recent_search_engine.cpp000066400000000000000000000420161523477242100310520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include "recentsearch/recentstrategies/recentsearchstrategy.h" using namespace DFMSEARCH; namespace { // 辅助函数:提取所有结果路径 QStringList resultPaths(const SearchResultList &results) { QStringList paths; for (const SearchResult &result : results) { paths.append(result.path()); } return paths; } // 创建模拟测试数据 QList createTestItems() { const qint64 now = QDateTime::currentDateTime().toSecsSinceEpoch(); const qint64 yesterday = QDateTime::currentDateTime().addDays(-1).toSecsSinceEpoch(); const qint64 lastWeek = QDateTime::currentDateTime().addDays(-7).toSecsSinceEpoch(); return { {"/tmp/test/report.pdf", "file:///tmp/test/report.pdf", now}, {"/tmp/test/meeting-notes.txt", "file:///tmp/test/meeting-notes.txt", yesterday}, {"/tmp/test/holiday-photo.jpg", "file:///tmp/test/holiday-photo.jpg", now}, {"/tmp/test/budget.xlsx", "file:///tmp/test/budget.xlsx", lastWeek}, {"/tmp/test/project-plan.docx", "file:///tmp/test/project-plan.docx", yesterday}, {"/tmp/test/notes.txt", "file:///tmp/test/notes.txt", yesterday}, }; } // 包含黑名单目录下文件的测试数据,用于验证黑名单路径过滤 QList createBlacklistTestItems() { const qint64 now = QDateTime::currentDateTime().toSecsSinceEpoch(); const qint64 yesterday = QDateTime::currentDateTime().addDays(-1).toSecsSinceEpoch(); return { // 黑名单目录 /home/uos/Downloads 下的文件(应被过滤) {"/home/uos/Downloads/report.pdf", "file:///home/uos/Downloads/report.pdf", now}, {"/home/uos/Downloads/notes.txt", "file:///home/uos/Downloads/notes.txt", yesterday}, {"/home/uos/Downloads/sub/deep.xlsx", "file:///home/uos/Downloads/sub/deep.xlsx", now}, // 名称前缀相同但非黑名单子目录的文件(应保留——路径边界匹配) {"/home/uos/Downloads_backup/archive.zip", "file:///home/uos/Downloads_backup/archive.zip", now}, // 非黑名单目录下的文件(应保留) {"/home/uos/Documents/plan.docx", "file:///home/uos/Documents/plan.docx", yesterday}, {"/tmp/test/notes.txt", "file:///tmp/test/notes.txt", yesterday}, }; } } // namespace // 可测试的 RecentSearchStrategy 子类,提供访问 fetchAddr 的接口 class TestableRecentStrategy : public RecentSearchStrategy { public: using RecentSearchStrategy::RecentSearchStrategy; auto fetchAddr() const { return VADDR(TestableRecentStrategy, fetchRecentItems); } }; class tst_RecentSearchEngine : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void search_emptyFetch_returnsEmptyResult(); void search_noFilter_returnsAllItems(); void search_keywordFilter_matchesOnlyFilenameWithKeyword(); void search_extensionFilter_matchesOnlyGivenExtensions(); void search_timeRangeFilter_keepsOnlyItemsInRange(); void search_combinedFilter_matchesIntersection(); void search_dbusFailure_doesNotCrashReturnsEmpty(); void search_detailedResults_containsExtendedAttributes(); void search_maxResults_truncatesResultCount(); void search_resultFoundEnabled_emitsSignalPerItem(); void search_excludedPathsFilter_removesBlacklistedItems(); void search_excludedPathsFilter_emptyKeepsAllItems(); void search_excludedPathsCombinedWithKeyword_appliesBoth(); void search_excludedPathsFilter_respectsPathBoundary(); }; void tst_RecentSearchEngine::initTestCase() { } void tst_RecentSearchEngine::cleanupTestCase() { } void tst_RecentSearchEngine::search_emptyFetch_returnsEmptyResult() { SearchOptions opts; TestableRecentStrategy strategy(opts); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), []() -> QList { return {}; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery("report"); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QCOMPARE(results.size(), 0); } void tst_RecentSearchEngine::search_noFilter_returnsAllItems() { SearchOptions opts; TestableRecentStrategy strategy(opts); const auto testItems = createTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery(""); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QCOMPARE(results.size(), testItems.size()); } void tst_RecentSearchEngine::search_keywordFilter_matchesOnlyFilenameWithKeyword() { SearchOptions opts; TestableRecentStrategy strategy(opts); const auto testItems = createTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery("notes"); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QStringList paths = resultPaths(results); QCOMPARE(paths.size(), 2); QVERIFY(paths.contains("/tmp/test/meeting-notes.txt")); QVERIFY(paths.contains("/tmp/test/notes.txt")); } void tst_RecentSearchEngine::search_extensionFilter_matchesOnlyGivenExtensions() { SearchOptions opts; FileNameOptionsAPI api(opts); api.setFileExtensions({"pdf", "txt"}); TestableRecentStrategy strategy(opts); const auto testItems = createTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery(""); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QStringList paths = resultPaths(results); QCOMPARE(paths.size(), 3); QVERIFY(paths.contains("/tmp/test/report.pdf")); QVERIFY(paths.contains("/tmp/test/meeting-notes.txt")); QVERIFY(paths.contains("/tmp/test/notes.txt")); } void tst_RecentSearchEngine::search_timeRangeFilter_keepsOnlyItemsInRange() { SearchOptions opts; TimeRangeFilter filter; filter.setTimeField(TimeField::ModifyTime).setYesterday(); opts.setTimeRangeFilter(filter); TestableRecentStrategy strategy(opts); const auto testItems = createTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery(""); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); // 找出昨天的条目:meeting-notes.txt, project-plan.docx, notes.txt QCOMPARE(results.size(), 3); QStringList paths = resultPaths(results); QVERIFY(paths.contains("/tmp/test/meeting-notes.txt")); QVERIFY(paths.contains("/tmp/test/project-plan.docx")); QVERIFY(paths.contains("/tmp/test/notes.txt")); } void tst_RecentSearchEngine::search_combinedFilter_matchesIntersection() { SearchOptions opts; FileNameOptionsAPI api(opts); api.setFileExtensions({"txt"}); TimeRangeFilter filter; filter.setTimeField(TimeField::ModifyTime).setYesterday(); opts.setTimeRangeFilter(filter); TestableRecentStrategy strategy(opts); const auto testItems = createTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery("meeting"); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); // 关键词 "meeting" + 扩展名 "txt" + 昨天时间范围 交集:仅 meeting-notes.txt QCOMPARE(results.size(), 1); QCOMPARE(results.first().path(), "/tmp/test/meeting-notes.txt"); } void tst_RecentSearchEngine::search_dbusFailure_doesNotCrashReturnsEmpty() { SearchOptions opts; TestableRecentStrategy strategy(opts); stub_ext::StubExt stub; // DBus 失败模拟:fetchRecentItems 返回空列表 stub.set_lamda(strategy.fetchAddr(), []() -> QList { return {}; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); QSignalSpy errorSpy(&strategy, &BaseSearchStrategy::errorOccurred); SearchQuery query = SearchFactory::createQuery("any keyword"); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QCOMPARE(results.size(), 0); // 不应该发射 error 信号(DBus 失败本身不标记为搜索错误,只返回空结果) QCOMPARE(errorSpy.count(), 0); } void tst_RecentSearchEngine::search_detailedResults_containsExtendedAttributes() { SearchOptions opts; opts.setDetailedResultsEnabled(true); TestableRecentStrategy strategy(opts); auto testItems = createTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery("report"); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QCOMPARE(results.size(), 1); SearchResult result = results.first(); FileNameResultAPI api(result); QCOMPARE(result.path(), "/tmp/test/report.pdf"); QCOMPARE(api.filename(), QString("report.pdf")); QCOMPARE(api.fileExtension(), QString("pdf")); QCOMPARE(api.isDirectory(), false); QCOMPARE(api.isHidden(), false); QCOMPARE(api.modifyTimestamp(), testItems[0].modified); } void tst_RecentSearchEngine::search_maxResults_truncatesResultCount() { SearchOptions opts; opts.setMaxResults(2); TestableRecentStrategy strategy(opts); const auto testItems = createTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery(""); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QCOMPARE(results.size(), 2); // 总共有 6 条,只返回前 2 条 } void tst_RecentSearchEngine::search_resultFoundEnabled_emitsSignalPerItem() { SearchOptions opts; opts.setResultFoundEnabled(true); opts.setMaxResults(3); TestableRecentStrategy strategy(opts); const auto testItems = createTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy foundSpy(&strategy, &BaseSearchStrategy::resultFound); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery(""); strategy.search(query); QCOMPARE(foundSpy.count(), 3); // maxResults = 3,所以发射 3 次 QCOMPARE(finishedSpy.count(), 1); } void tst_RecentSearchEngine::search_excludedPathsFilter_removesBlacklistedItems() { SearchOptions opts; opts.setSearchExcludedPaths({"/home/uos/Downloads"}); TestableRecentStrategy strategy(opts); const auto testItems = createBlacklistTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery(""); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QStringList paths = resultPaths(results); // 黑名单目录 /home/uos/Downloads 下的 3 个文件被过滤; // /home/uos/Downloads_backup/archive.zip 因路径边界匹配而保留,共 3 个 QCOMPARE(paths.size(), 3); QVERIFY(paths.contains("/home/uos/Documents/plan.docx")); QVERIFY(paths.contains("/tmp/test/notes.txt")); QVERIFY(paths.contains("/home/uos/Downloads_backup/archive.zip")); QVERIFY(!paths.contains("/home/uos/Downloads/report.pdf")); QVERIFY(!paths.contains("/home/uos/Downloads/notes.txt")); QVERIFY(!paths.contains("/home/uos/Downloads/sub/deep.xlsx")); } void tst_RecentSearchEngine::search_excludedPathsFilter_emptyKeepsAllItems() { SearchOptions opts; // 不设置黑名单路径 TestableRecentStrategy strategy(opts); const auto testItems = createBlacklistTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery(""); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); // 无黑名单时全部保留 QCOMPARE(results.size(), testItems.size()); } void tst_RecentSearchEngine::search_excludedPathsCombinedWithKeyword_appliesBoth() { SearchOptions opts; opts.setSearchExcludedPaths({"/home/uos/Downloads"}); TestableRecentStrategy strategy(opts); const auto testItems = createBlacklistTestItems(); stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [testItems]() -> QList { return testItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); // 关键词 "notes" 命中 /home/uos/Downloads/notes.txt(被黑名单过滤)与 /tmp/test/notes.txt(保留) SearchQuery query = SearchFactory::createQuery("notes"); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QStringList paths = resultPaths(results); QCOMPARE(paths.size(), 1); QVERIFY(paths.contains("/tmp/test/notes.txt")); QVERIFY(!paths.contains("/home/uos/Downloads/notes.txt")); } void tst_RecentSearchEngine::search_excludedPathsFilter_respectsPathBoundary() { // 验证路径边界匹配:黑名单 /home/uos/Downloads 不应误匹配 /home/uos/Downloads_backup SearchOptions opts; opts.setSearchExcludedPaths({"/home/uos/Downloads"}); TestableRecentStrategy strategy(opts); const qint64 now = QDateTime::currentDateTime().toSecsSinceEpoch(); const QList boundaryItems = { // 黑名单目录下的文件(应过滤) {"/home/uos/Downloads/file.txt", "file:///home/uos/Downloads/file.txt", now}, // 名称前缀相同但非子目录(应保留) {"/home/uos/Downloads_backup/file.txt", "file:///home/uos/Downloads_backup/file.txt", now}, {"/home/uos/Downloads_archive/file.txt", "file:///home/uos/Downloads_archive/file.txt", now}, }; stub_ext::StubExt stub; stub.set_lamda(strategy.fetchAddr(), [boundaryItems]() -> QList { return boundaryItems; }); QSignalSpy finishedSpy(&strategy, &BaseSearchStrategy::searchFinished); SearchQuery query = SearchFactory::createQuery(""); strategy.search(query); QCOMPARE(finishedSpy.count(), 1); SearchResultList results = finishedSpy.takeFirst().at(0).value(); QStringList paths = resultPaths(results); // 仅 /home/uos/Downloads/file.txt 被过滤,_backup 和 _archive 因路径边界而保留 QCOMPARE(paths.size(), 2); QVERIFY(paths.contains("/home/uos/Downloads_backup/file.txt")); QVERIFY(paths.contains("/home/uos/Downloads_archive/file.txt")); QVERIFY(!paths.contains("/home/uos/Downloads/file.txt")); } QObject *create_tst_RecentSearchEngine() { return new tst_RecentSearchEngine(); } #include "tst_recent_search_engine.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_search_output.cpp000066400000000000000000000261321523477242100276060ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "output/json_output.h" #include "output/text_output.h" #include "preview_command.h" #include "preview_output_utils.h" using namespace DFMSEARCH; using namespace Lucene; namespace { void addStoredDocument(const IndexWriterPtr &writer, SearchType type, const QString &path, const QString &filename, const QString &content) { DocumentPtr doc = newLucene(); if (type == SearchType::Ocr) { doc->add(newLucene(LuceneFieldNames::OcrText::kPath, path.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kFilename, filename.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::OcrText::kOcrContents, content.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); } else { doc->add(newLucene(LuceneFieldNames::Content::kPath, path.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kFilename, filename.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); doc->add(newLucene(LuceneFieldNames::Content::kContents, content.toStdWString(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED)); } writer->addDocument(doc); } void createIndex(const QString &indexDir, SearchType type) { QDir().mkpath(indexDir); IndexWriterPtr writer = newLucene( FSDirectory::open(indexDir.toStdWString()), newLucene(), true, IndexWriter::MaxFieldLengthLIMITED); if (type == SearchType::Content) { addStoredDocument(writer, type, "/tmp/doc-a.txt", "doc-a.txt", "hello world from content index"); } else { addStoredDocument(writer, type, "/tmp/img-a.png", "img-a.png", "screenshot text from OCR"); } writer->close(); } QString captureStdout(const std::function &fn) { QTemporaryFile file; if (!file.open()) { return {}; } fflush(stdout); std::cout.flush(); const int savedStdout = dup(STDOUT_FILENO); if (savedStdout < 0) { return {}; } dup2(file.handle(), STDOUT_FILENO); fn(); fflush(stdout); std::cout.flush(); dup2(savedStdout, STDOUT_FILENO); close(savedStdout); file.seek(0); return QString::fromUtf8(file.readAll()); } } // namespace class tst_SearchOutput : public QObject { Q_OBJECT private Q_SLOTS: void previewOutputHelpers_includeCharCount(); void previewCommand_failsForUnindexedPath(); void jsonOutput_contentAndFilenameCharCountContract(); void jsonOutput_ocrAndSemanticCharCountContract(); void textOutput_contentAndSemanticCharCountContract(); }; void tst_SearchOutput::previewOutputHelpers_includeCharCount() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); PreviewOptions options; options.setKeyword("world"); options.setOffset(0); options.setMaxLength(10); const PreviewResult result = retriever.fetchPreview("/tmp/doc-a.txt", SearchType::Content, options); const QJsonObject json = dfmsearch::previewResultToJson("/tmp/doc-a.txt", result); QCOMPARE(json.value("content").toString(), QString("world from")); QCOMPARE(json.value("charCount").toInt(), QString("hello world from content index").size()); QCOMPARE(json.value("keywordOffset").toInt(), 6); QString text; QTextStream stream(&text); dfmsearch::writePreviewResultText(stream, "/tmp/doc-a.txt", result); QVERIFY(text.contains("Char count: 30")); QVERIFY(text.contains("world from")); } void tst_SearchOutput::previewCommand_failsForUnindexedPath() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); const QString contentIndexDir = tempDir.path() + "/content-index"; createIndex(contentIndexDir, SearchType::Content); ContentRetriever retriever; retriever.setIndexDirectory(SearchType::Content, contentIndexDir); SearchCliConfig config; config.subcommand = "preview"; config.searchType = SearchType::Content; config.searchPath = "/tmp/doc-a.txt,/tmp/missing.txt"; config.jsonOutput = true; const PreviewCommandResult result = dfmsearch::runPreviewCommand(config, retriever); QCOMPARE(result.exitCode, 1); QVERIFY(result.stdoutText.isEmpty()); QVERIFY(result.stderrText.contains("Error: preview requires indexed content for:")); QVERIFY(result.stderrText.contains("/tmp/missing.txt")); QVERIFY(!result.stderrText.contains("/tmp/doc-a.txt")); } void tst_SearchOutput::jsonOutput_contentAndFilenameCharCountContract() { SearchOptions options; options.setDetailedResultsEnabled(true); SearchResult contentResult("/tmp/doc-a.txt"); ContentResultAPI contentApi(contentResult); contentApi.setCharCount(30); contentApi.setFilename("doc-a.txt"); contentResult.setCustomAttribute("plainContentMatch", QString("world from")); contentResult.setCustomAttribute("snippetOffset", 6); dfmsearch::JsonOutput contentOutput(false); contentOutput.setSearchOptions(options); contentOutput.setSearchContext("world", "/tmp", SearchType::Content, SearchMethod::Indexed); const QString contentJsonText = captureStdout([&]() { contentOutput.outputSearchStarted(); contentOutput.outputSearchFinished({ contentResult }); }); const QJsonObject contentRoot = QJsonDocument::fromJson(contentJsonText.toUtf8()).object(); const QJsonObject contentItem = contentRoot.value("results").toArray().first().toObject(); QCOMPARE(contentItem.value("charCount").toInt(), 30); SearchResult filenameResult("/tmp/doc-a.txt"); FileNameResultAPI fileApi(filenameResult); fileApi.setFilename("doc-a.txt"); fileApi.setFileType("txt"); fileApi.setSize("123"); filenameResult.setCustomAttribute("charCount", 30); dfmsearch::JsonOutput filenameOutput(false); filenameOutput.setSearchOptions(options); filenameOutput.setSearchContext("doc", "/tmp", SearchType::FileName, SearchMethod::Indexed); const QString filenameJsonText = captureStdout([&]() { filenameOutput.outputSearchStarted(); filenameOutput.outputSearchFinished({ filenameResult }); }); const QJsonObject filenameRoot = QJsonDocument::fromJson(filenameJsonText.toUtf8()).object(); const QJsonObject filenameItem = filenameRoot.value("results").toArray().first().toObject(); QVERIFY(!filenameItem.contains("charCount")); } void tst_SearchOutput::jsonOutput_ocrAndSemanticCharCountContract() { SearchOptions options; options.setDetailedResultsEnabled(true); SearchResult ocrResult("/tmp/img-a.png"); OcrTextResultAPI ocrApi(ocrResult); ocrApi.setCharCount(24); ocrApi.setOcrContent("screenshot text from OCR"); ocrResult.setCustomAttribute("plainContentMatch", QString("text from OCR")); ocrResult.setCustomAttribute("snippetOffset", 11); dfmsearch::JsonOutput ocrOutput(false); ocrOutput.setSearchOptions(options); ocrOutput.setSearchContext("text", "/tmp", SearchType::Ocr, SearchMethod::Indexed); const QString ocrJsonText = captureStdout([&]() { ocrOutput.outputSearchStarted(); ocrOutput.outputSearchFinished({ ocrResult }); }); const QJsonObject ocrRoot = QJsonDocument::fromJson(ocrJsonText.toUtf8()).object(); const QJsonObject ocrItem = ocrRoot.value("results").toArray().first().toObject(); QCOMPARE(ocrItem.value("charCount").toInt(), 24); SearchResult semanticResult("/tmp/doc-a.txt"); semanticResult.setCustomAttribute("charCount", 30); semanticResult.setCustomAttribute("contentMatch", QString("world from")); dfmsearch::JsonOutput semanticOutput(false); semanticOutput.setSearchOptions(options); semanticOutput.setSearchContext("world", "/tmp", SearchType::Semantic, SearchMethod::Indexed); const QString semanticJsonText = captureStdout([&]() { semanticOutput.outputSearchStarted(); semanticOutput.outputSearchFinished({ semanticResult }); }); const QJsonObject semanticRoot = QJsonDocument::fromJson(semanticJsonText.toUtf8()).object(); const QJsonObject semanticItem = semanticRoot.value("results").toArray().first().toObject(); QCOMPARE(semanticItem.value("charCount").toInt(), 30); } void tst_SearchOutput::textOutput_contentAndSemanticCharCountContract() { SearchOptions options; options.setDetailedResultsEnabled(true); SearchResult contentResult("/tmp/doc-a.txt"); ContentResultAPI contentApi(contentResult); contentApi.setCharCount(30); contentResult.setCustomAttribute("plainContentMatch", QString("world from")); contentResult.setCustomAttribute("snippetOffset", 6); dfmsearch::TextOutput contentOutput; contentOutput.setSearchOptions(options); contentOutput.setVerbose(true); contentOutput.setSearchContext("world", "/tmp", SearchType::Content, SearchMethod::Indexed); const QString contentText = captureStdout([&]() { contentOutput.outputSearchFinished({ contentResult }); }); QVERIFY(contentText.contains("Char count: 30")); SearchResult semanticResult("/tmp/doc-a.txt"); semanticResult.setCustomAttribute("charCount", 30); semanticResult.setCustomAttribute("contentMatch", QString("world from")); dfmsearch::TextOutput semanticOutput; semanticOutput.setSearchOptions(options); semanticOutput.setVerbose(true); semanticOutput.setSearchContext("world", "/tmp", SearchType::Semantic, SearchMethod::Indexed); const QString semanticText = captureStdout([&]() { semanticOutput.outputSearchFinished({ semanticResult }); }); QVERIFY(semanticText.contains("charCount: 30")); } QObject *create_tst_SearchOutput() { return new tst_SearchOutput(); } #include "tst_search_output.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_search_utils.cpp000066400000000000000000000276571523477242100274230ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include using namespace DFMSEARCH; class tst_SearchUtils : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void testGlobal(); void testPinyin(); void testPinyinAcronym(); void testAnythingStatus(); void testFileNameBlacklistMatcher(); void testNGramSearchQuery(); private: void doTestPinyin(const QString &caseName, const QString &input, bool expected); void doTestPinyinAcronym(const QString &caseName, const QString &input, bool expected); void doFileNameBlacklistMatchTest(const QString &caseName, const QString &inputPath, const QStringList &blacklistEntries, bool expected); }; void tst_SearchUtils::initTestCase() { // Setup that runs once before all tests } void tst_SearchUtils::cleanupTestCase() { // Cleanup that runs once after all tests } void tst_SearchUtils::doTestPinyin(const QString &caseName, const QString &input, bool expected) { bool actual = Global::isPinyinSequence(input); QCOMPARE(actual, expected); } void tst_SearchUtils::testPinyin() { // 有效拼音测试集 QList> validCases = { // 单韵母 { "a", true }, { "o", true }, { "e", true }, { "O", true }, // 声母+韵母 { "ba", true }, { "po", true }, { "mi", true }, // 特殊音节 { "zhi", true }, { "chi", true }, { "shi", true }, // 复韵母 { "ai", true }, { "er", true }, { "ang", true }, // ü相关 { "lv", true }, { "lüe", true }, // 多音节 { "nihao", true }, { "pinyin", true }, { "zhongwen", true }, { "shuang", true }, { "xian", true }, { "quan", true }, { "jiang", true }, // 大小写混合 { "ZhongGuo", true }, { "XIONG", true }, { "PinYin", true }, { "make", true }, { "xinjian", true }, { "zhangsheng", true }, { "wendan", true }, { "wendang", true }, { "xiaa", true }, { "chaojichangdeyijuhua", true }, { "chengong", true }, { "shibai", true }, { "case", true }, { "sougou", true }, { "sousuo", true }, { "jieshi", true }, { "zongjie", true }, { "jiu", true }, { "chengdu", true }, { "beijing", true }, { "xian", true }, { "chongqing", true }, { "chongqin", true }, { "chenqin", true }, { "shanghai", true }, }; // 无效拼音测试集 QList> invalidCases = { // 基本无效情况 { "", false }, { "vvv", false }, { "kkkk", false }, { "i", false }, { "u", false }, // 非法拼音组合 { "xqiong", false }, // 英文单词 { "hello", false }, { "world", false }, { "cmake", false }, // 数字和特殊字符 { "zh@ng", false }, // 不完整或错误的拼音 { "zh", false }, { "zho", false }, { "jx", false }, // 特殊规则测试 { "yi", true }, { "wu", true }, { "yu", true }, { "yue", true }, { "yuan", true }, // 边界情况 { "v", false }, { "ü", false }, { "ng", false }, { "gn", false }, { "123", false }, { "z", false }, { "zh", false }, { "p", false }, { "m", false }, { "b", false }, { "jiv", false }, }; for (const auto &pair : validCases) { doTestPinyin("有效拼音验证", pair.first, pair.second); } for (const auto &pair : invalidCases) { doTestPinyin("无效拼音检测", pair.first, pair.second); } } void tst_SearchUtils::doTestPinyinAcronym(const QString &caseName, const QString &input, bool expected) { bool actual = Global::isPinyinAcronymSequence(input); QCOMPARE(actual, expected); } void tst_SearchUtils::testPinyinAcronym() { // 有效拼音首字母测试集 QList> validCases = { // 基本有效情况 { "n", true }, { "nh", true }, { "wd", true }, { "xj", true }, { "zhzw", true }, { "dfm", true }, { "ABC", true }, { "AbC", true }, { "hello", true }, { "a", true }, { "z", true }, // 包含数字和符号的有效情况 { "nh123", true }, { "wd_v1", true }, { "test-file", true }, { "config.bak", true }, { "a1b2c3", true }, { "file_2023", true }, }; // 无效拼音首字母测试集 QList> invalidCases = { // 基本无效情况 { "", false }, { "你好", false }, { "n好", false }, { "123", false }, { "._-", false }, }; for (const auto &pair : validCases) { doTestPinyinAcronym("有效首字母验证", pair.first, pair.second); } for (const auto &pair : invalidCases) { doTestPinyinAcronym("无效首字母检测", pair.first, pair.second); } } void tst_SearchUtils::testAnythingStatus() { qDebug() << "=== Starting Anything Status Test ==="; // 测试状态获取 auto status = Global::fileNameIndexStatus(); // 结果验证 QVERIFY2(status.has_value(), "Could not retrieve status (file missing/permission error?)"); // 定义有效状态列表(小写) static const QSet validStatuses { "loading", "scanning", "monitoring", "closed" }; // 状态有效性检查 const QString currentStatus = status.value(); QVERIFY2(validStatuses.contains(currentStatus), QString("Invalid anything status value: %1\nExpected one of: %2") .arg(currentStatus, validStatuses.values().join(", ")) .toUtf8() .constData()); // 成功输出 qInfo() << "Test Passed. Current anything status:" << currentStatus; } void tst_SearchUtils::doFileNameBlacklistMatchTest(const QString &caseName, const QString &inputPath, const QStringList &blacklistEntries, bool expected) { const bool actual = Global::BlacklistMatcher::isPathBlacklisted(inputPath, blacklistEntries); QCOMPARE(actual, expected); } void tst_SearchUtils::testFileNameBlacklistMatcher() { doFileNameBlacklistMatchTest("绝对路径-自身命中", "/home/test/workspace", { "/home/test/workspace" }, true); doFileNameBlacklistMatchTest("绝对路径-子路径命中", "/home/test/workspace/a.txt", { "/home/test/workspace" }, true); doFileNameBlacklistMatchTest("绝对路径-边界不误匹配", "/home/test/workspace2", { "/home/test/workspace" }, false); doFileNameBlacklistMatchTest("目录名-直接命中", "/home/test/workspace", { "workspace" }, true); doFileNameBlacklistMatchTest("目录名-深层命中", "/home/test/aa/bb/workspace", { "workspace" }, true); doFileNameBlacklistMatchTest("目录名-不命中相似名称", "/home/test/aa/bb/myworkspace", { "workspace" }, false); } void tst_SearchUtils::testGlobal() { // Test supported content search extensions QStringList testExtensions = { "txt", "pdf", "docx", "unknown" }; for (const auto &ext : testExtensions) { bool result = Global::isSupportedContentSearchExtension(ext); QString message = QString("Check if '%1' is supported").arg(ext); QVERIFY2(result == (ext != "unknown"), message.toUtf8().constData()); } // Test default content search extensions QStringList defaultExtensions = Global::defaultContentSearchExtensions(); QVERIFY2(!defaultExtensions.isEmpty(), "Default supported content search extensions should not be empty"); // Test content index directory QVERIFY2(!Global::contentIndexDirectory().isEmpty(), "Content index directory should not be empty"); // Test path in content index directory QString testPath = QDir::homePath() + "/test.txt"; Global::isPathInContentIndexDirectory(testPath); // Test filename index directory QVERIFY2(!Global::fileNameIndexDirectory().isEmpty(), "Filename index directory should not be empty"); // Test default indexed dirs const auto &dirs = Global::defaultIndexedDirectory(); QVERIFY2(!dirs.isEmpty(), "Default indexed directories should not be empty"); // Test default blacklist paths const auto &blacklistPaths = Global::defaultBlacklistPaths(); Q_UNUSED(blacklistPaths); } void tst_SearchUtils::testNGramSearchQuery() { Lucene::QueryPtr oneCharQuery = LuceneQueryUtils::buildNGramSearchQuery("contents", "A"); Lucene::TermQueryPtr oneCharTermQuery = boost::dynamic_pointer_cast(oneCharQuery); QVERIFY(oneCharTermQuery); QCOMPARE(oneCharTermQuery->getTerm()->field(), Lucene::String(L"contents")); QCOMPARE(oneCharTermQuery->getTerm()->text(), Lucene::String(L"a")); Lucene::QueryPtr twoCharQuery = LuceneQueryUtils::buildNGramSearchQuery("contents", "Ab"); Lucene::TermQueryPtr twoCharTermQuery = boost::dynamic_pointer_cast(twoCharQuery); QVERIFY(twoCharTermQuery); QCOMPARE(twoCharTermQuery->getTerm()->text(), Lucene::String(L"ab")); Lucene::QueryPtr evenQuery = LuceneQueryUtils::buildNGramSearchQuery("contents", "abcdef"); Lucene::PhraseQueryPtr evenPhraseQuery = boost::dynamic_pointer_cast(evenQuery); QVERIFY(evenPhraseQuery); QCOMPARE(evenPhraseQuery->getTerms().size(), 3); QCOMPARE(evenPhraseQuery->getTerms()[0]->text(), Lucene::String(L"ab")); QCOMPARE(evenPhraseQuery->getTerms()[1]->text(), Lucene::String(L"cd")); QCOMPARE(evenPhraseQuery->getTerms()[2]->text(), Lucene::String(L"ef")); QCOMPARE(evenPhraseQuery->getPositions()[0], 1); QCOMPARE(evenPhraseQuery->getPositions()[1], 5); QCOMPARE(evenPhraseQuery->getPositions()[2], 9); Lucene::QueryPtr oddQuery = LuceneQueryUtils::buildNGramSearchQuery("contents", "abcde"); Lucene::PhraseQueryPtr oddPhraseQuery = boost::dynamic_pointer_cast(oddQuery); QVERIFY(oddPhraseQuery); QCOMPARE(oddPhraseQuery->getTerms().size(), 3); QCOMPARE(oddPhraseQuery->getTerms()[0]->text(), Lucene::String(L"ab")); QCOMPARE(oddPhraseQuery->getTerms()[1]->text(), Lucene::String(L"cd")); QCOMPARE(oddPhraseQuery->getTerms()[2]->text(), Lucene::String(L"de")); QCOMPARE(oddPhraseQuery->getPositions()[0], 1); QCOMPARE(oddPhraseQuery->getPositions()[1], 5); QCOMPARE(oddPhraseQuery->getPositions()[2], 7); } QObject *create_tst_SearchUtils() { return new tst_SearchUtils(); } #include "tst_search_utils.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_semantic_search.cpp000066400000000000000000001766301523477242100300620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include "semantic/semanticruleengine.h" #include "semantic/intentparser.h" #include "semantic/ruleconfigloader.h" #include "semantic/extractors/keywordextractor.h" #include "semantic/semanticquerybuilder.h" #include "semantic/extractors/locationextractor.h" using namespace DFMSEARCH; static bool buildGroupFromJson(const QByteArray &json, RuleGroup &outGroup) { QJsonDocument doc = QJsonDocument::fromJson(json); if (!doc.isObject()) { return false; } QJsonObject root = doc.object(); QJsonArray groups = root.value("groups").toArray(); if (groups.isEmpty()) { return false; } return SemanticRuleEngine::parseRuleGroupStatic(groups.at(0).toObject(), outGroup); } // Helper: build a simple rule group JSON string static QByteArray makeRuleJson(const QString &groupName, const QString &ruleId, const QString &pattern, int priority, const QVariantMap &metadata = {}) { QJsonObject ruleObj; ruleObj["id"] = ruleId; ruleObj["pattern"] = pattern; ruleObj["enabled"] = true; ruleObj["priority"] = priority; if (!metadata.isEmpty()) { ruleObj["metadata"] = QJsonObject::fromVariantMap(metadata); } QJsonObject ruleGroupObj; ruleGroupObj["name"] = groupName; ruleGroupObj["version"] = "1.0.0"; ruleGroupObj["rules"] = QJsonArray({ ruleObj }); QJsonObject root; root["groups"] = QJsonArray({ ruleGroupObj }); return QJsonDocument(root).toJson(QJsonDocument::Compact); } // ===== tst_RuleEngine ===== class tst_RuleEngine : public QObject { Q_OBJECT private Q_SLOTS: void parseValidGroup(); void parseEmptyGroup(); void parsePriorityOrdering(); void matchReturnsHighestPriority(); void matchAllReturnsAll(); void ruleMetadataAccess(); void hasGroupCheck(); }; void tst_RuleEngine::parseValidGroup() { QByteArray json = makeRuleJson("test", "r1", "hello", 100); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.name, QString("test")); QCOMPARE(group.rules.size(), 1); QCOMPARE(group.rules[0].id, QString("r1")); QVERIFY(group.rules[0].regex.isValid()); } void tst_RuleEngine::parseEmptyGroup() { QByteArray json = R"=====({"groups": [{"name": "empty", "rules": []}]})====="; RuleGroup group; QVERIFY(!buildGroupFromJson(json, group)); } void tst_RuleEngine::parsePriorityOrdering() { QJsonObject r1, r2, r3; r1["id"] = "low"; r1["pattern"] = "test"; r1["priority"] = 10; r2["id"] = "high"; r2["pattern"] = "test"; r2["priority"] = 200; r3["id"] = "mid"; r3["pattern"] = "test"; r3["priority"] = 100; QJsonObject ruleGroup; ruleGroup["name"] = "prio"; ruleGroup["version"] = "1.0.0"; ruleGroup["rules"] = QJsonArray({ r1, r2, r3 }); QJsonObject root; root["groups"] = QJsonArray({ ruleGroup }); RuleGroup group; QVERIFY(SemanticRuleEngine::parseRuleGroupStatic(ruleGroup, group)); QCOMPARE(group.rules.size(), 3); QStringList ids; for (const Rule &r : group.rules) { ids.append(r.id); } QVERIFY(ids.contains("low")); QVERIFY(ids.contains("mid")); QVERIFY(ids.contains("high")); } void tst_RuleEngine::matchReturnsHighestPriority() { QByteArray json = makeRuleJson("test_match", "r1", "abc", 200, { { "level", "high" } }); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(group.rules[0].regex.match("abc").hasMatch()); } void tst_RuleEngine::matchAllReturnsAll() { QJsonObject r1, r2, r3; r1["id"] = "r1"; r1["pattern"] = "cat"; r1["priority"] = 100; r2["id"] = "r2"; r2["pattern"] = "dog"; r2["priority"] = 100; r3["id"] = "r3"; r3["pattern"] = "bird"; r3["priority"] = 50; QJsonObject ruleGroup; ruleGroup["name"] = "test_all"; ruleGroup["version"] = "1.0.0"; ruleGroup["rules"] = QJsonArray({ r1, r2, r3 }); QJsonObject root; root["groups"] = QJsonArray({ ruleGroup }); RuleGroup group; QVERIFY(SemanticRuleEngine::parseRuleGroupStatic(ruleGroup, group)); QCOMPARE(group.rules.size(), 3); } void tst_RuleEngine::ruleMetadataAccess() { QVariantMap meta; meta["type"] = "preset"; meta["preset"] = "today"; QByteArray json = makeRuleJson("test_meta", "m1", "test", 100, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("type").toString(), QString("preset")); QCOMPARE(group.rules[0].metadata.value("preset").toString(), QString("today")); } void tst_RuleEngine::hasGroupCheck() { SemanticRuleEngine engine; QVERIFY(!engine.hasGroup("time")); QVERIFY(!engine.hasGroup("filetype")); QCOMPARE(engine.groupNames().size(), 0); } // ===== tst_TimeExtraction ===== class tst_TimeExtraction : public QObject { Q_OBJECT private Q_SLOTS: void presetToday(); void presetYesterday(); void presetThisWeek(); void presetThisMonth(); void presetThisYear(); void presetLastYear(); void customYear(); void customYearMonth(); void customFullDate(); void noMatch(); // Compound preset time rules (preset year+month, preset week+day, preset month+day) void presetLastYearMonth(); void presetThisYearMonth(); void presetThisWeekDay(); void presetLastWeekDay(); void presetThisMonthDay(); void presetLastMonthDay(); }; void tst_TimeExtraction::presetToday() { QVariantMap meta; meta["type"] = "preset"; meta["preset"] = "today"; QByteArray json = makeRuleJson("time", "time_today", "today", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("preset").toString(), QString("today")); QVERIFY(group.rules[0].regex.isValid()); QVERIFY(group.rules[0].regex.match("today").hasMatch()); QVERIFY(!group.rules[0].regex.match("yesterday").hasMatch()); } void tst_TimeExtraction::presetYesterday() { QVariantMap meta; meta["type"] = "preset"; meta["preset"] = "yesterday"; QByteArray json = makeRuleJson("time", "time_yesterday", "yesterday", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("preset").toString(), QString("yesterday")); QVERIFY(group.rules[0].regex.match("yesterday").hasMatch()); } void tst_TimeExtraction::presetThisWeek() { QVariantMap meta; meta["type"] = "preset"; meta["preset"] = "this_week"; QByteArray json = makeRuleJson("time", "time_this_week", "this_week", 190, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("preset").toString(), QString("this_week")); } void tst_TimeExtraction::presetThisMonth() { QVariantMap meta; meta["type"] = "preset"; meta["preset"] = "this_month"; QByteArray json = makeRuleJson("time", "time_this_month", "this_month", 180, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("preset").toString(), QString("this_month")); } void tst_TimeExtraction::presetThisYear() { QVariantMap meta; meta["type"] = "preset"; meta["preset"] = "this_year"; QByteArray json = makeRuleJson("time", "time_this_year", "this_year", 170, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("preset").toString(), QString("this_year")); } void tst_TimeExtraction::presetLastYear() { QVariantMap meta; meta["type"] = "preset"; meta["preset"] = "last_year"; QByteArray json = makeRuleJson("time", "time_last_year", "last_year", 170, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("preset").toString(), QString("last_year")); } void tst_TimeExtraction::customYear() { // Use programmatic JSON to avoid raw string delimiter conflict with regex patterns QVariantMap meta; meta["type"] = "custom"; meta["format"] = "year"; QByteArray json = makeRuleJson("time", "time_exact_year", "(?\\d{2,4})year", 160, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); auto match = group.rules[0].regex.match("2025year"); QVERIFY(match.hasMatch()); QCOMPARE(match.captured("year"), QString("2025")); } void tst_TimeExtraction::customYearMonth() { QVariantMap meta; meta["type"] = "custom"; meta["format"] = "year_month"; QByteArray json = makeRuleJson("time", "time_exact_year_month", "(?\\d{2,4})[年\\./\\-](?\\d{1,2})(?:月份?)?", 150, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); auto match1 = group.rules[0].regex.match("2025年12月"); QVERIFY(match1.hasMatch()); QCOMPARE(match1.captured("year"), QString("2025")); QCOMPARE(match1.captured("month"), QString("12")); auto match2 = group.rules[0].regex.match("2025年12"); QVERIFY(match2.hasMatch()); QCOMPARE(match2.captured("year"), QString("2025")); QCOMPARE(match2.captured("month"), QString("12")); auto match3 = group.rules[0].regex.match("2025年12月份"); QVERIFY(match3.hasMatch()); QCOMPARE(match3.captured("year"), QString("2025")); QCOMPARE(match3.captured("month"), QString("12")); auto match4 = group.rules[0].regex.match("2025-12"); QVERIFY(match4.hasMatch()); QCOMPARE(match4.captured("year"), QString("2025")); QCOMPARE(match4.captured("month"), QString("12")); auto match5 = group.rules[0].regex.match("25.12"); QVERIFY(match5.hasMatch()); QCOMPARE(match5.captured("year"), QString("25")); QCOMPARE(match5.captured("month"), QString("12")); } void tst_TimeExtraction::customFullDate() { QVariantMap meta; meta["type"] = "custom"; meta["format"] = "full_date"; QByteArray json = makeRuleJson("time", "time_exact_full_date", "(?\\d{2,4})[年\\./\\-](?\\d{1,2})[月\\./\\-](?\\d{1,2})[日号]?", 140, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); auto match1 = group.rules[0].regex.match("2025年12月5日"); QVERIFY(match1.hasMatch()); QCOMPARE(match1.captured("year"), QString("2025")); QCOMPARE(match1.captured("month"), QString("12")); QCOMPARE(match1.captured("day"), QString("5")); auto match2 = group.rules[0].regex.match("2025年12月5"); QVERIFY(match2.hasMatch()); QCOMPARE(match2.captured("year"), QString("2025")); QCOMPARE(match2.captured("month"), QString("12")); QCOMPARE(match2.captured("day"), QString("5")); auto match3 = group.rules[0].regex.match("2025-03-15"); QVERIFY(match3.hasMatch()); QCOMPARE(match3.captured("year"), QString("2025")); QCOMPARE(match3.captured("month"), QString("03")); QCOMPARE(match3.captured("day"), QString("15")); auto match4 = group.rules[0].regex.match("2025/12/5"); QVERIFY(match4.hasMatch()); QCOMPARE(match4.captured("year"), QString("2025")); QCOMPARE(match4.captured("month"), QString("12")); QCOMPARE(match4.captured("day"), QString("5")); } void tst_TimeExtraction::noMatch() { QVariantMap meta; meta["type"] = "preset"; meta["preset"] = "today"; QByteArray json = makeRuleJson("time", "time_today", "today", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(!group.rules[0].regex.match("random text without match").hasMatch()); } void tst_TimeExtraction::presetLastYearMonth() { // "去年12月" matches time_last_year_month, priority 175 > time_last_year(170) QVariantMap meta; meta["type"] = "preset_year_month"; meta["preset"] = "last_year"; QMap dig; dig["零"] = 0; dig["一"] = 1; dig["二"] = 2; dig["三"] = 3; dig["四"] = 4; dig["五"] = 5; dig["六"] = 6; dig["七"] = 7; dig["八"] = 8; dig["九"] = 9; dig["十"] = 10; meta["digit_map"] = dig; meta["tens_unit"] = QString("十"); QByteArray json = makeRuleJson("time", "time_last_year_month", "(?:去年|上一年)(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))月份?", 175, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); // Arabic month { auto m = group.rules[0].regex.match("去年12月"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("month"), QString("12")); } // Arabic month + 份 { auto m = group.rules[0].regex.match("去年3月份"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("month"), QString("3")); } // Chinese numeral month { auto m = group.rules[0].regex.match("去年十二月"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("month"), QString("十二")); } // Should NOT match: no month suffix QVERIFY(!group.rules[0].regex.match("去年一整年").hasMatch()); QVERIFY(!group.rules[0].regex.match("去年的").hasMatch()); } void tst_TimeExtraction::presetThisYearMonth() { QVariantMap meta; meta["type"] = "preset_year_month"; meta["preset"] = "this_year"; QMap dig; dig["零"] = 0; dig["一"] = 1; dig["二"] = 2; dig["三"] = 3; dig["四"] = 4; dig["五"] = 5; dig["六"] = 6; dig["七"] = 7; dig["八"] = 8; dig["九"] = 9; dig["十"] = 10; meta["digit_map"] = dig; meta["tens_unit"] = QString("十"); QByteArray json = makeRuleJson("time", "time_this_year_month", "(?:今年|本年|这年)(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))月份?", 175, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); { auto m = group.rules[0].regex.match("今年3月"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("month"), QString("3")); } { auto m = group.rules[0].regex.match("本年十二月"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("month"), QString("十二")); } // should not match plain year-only preset QVERIFY(!group.rules[0].regex.match("今年").hasMatch()); } void tst_TimeExtraction::presetThisWeekDay() { QVariantMap meta; meta["type"] = "preset_week_day"; meta["preset"] = "this_week"; QMap wd; wd["一"] = 1; wd["二"] = 2; wd["三"] = 3; wd["四"] = 4; wd["五"] = 5; wd["六"] = 6; wd["日"] = 7; wd["天"] = 7; meta["weekday_map"] = wd; QByteArray json = makeRuleJson("time", "time_this_week_day", "(?:本周|这周|这个星期|这一个星期)(?[一二三四五六日天])", 175, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); auto m1 = group.rules[0].regex.match("本周三"); QVERIFY(m1.hasMatch()); QCOMPARE(m1.captured("weekday"), QString("三")); auto m2 = group.rules[0].regex.match("这周五"); QVERIFY(m2.hasMatch()); QCOMPARE(m2.captured("weekday"), QString("五")); auto m3 = group.rules[0].regex.match("这个星期天"); QVERIFY(m3.hasMatch()); QCOMPARE(m3.captured("weekday"), QString("天")); // Should NOT match plain week preset without weekday QVERIFY(!group.rules[0].regex.match("本周").hasMatch()); } void tst_TimeExtraction::presetLastWeekDay() { QVariantMap meta; meta["type"] = "preset_week_day"; meta["preset"] = "last_week"; QMap wd; wd["一"] = 1; wd["二"] = 2; wd["三"] = 3; wd["四"] = 4; wd["五"] = 5; wd["六"] = 6; wd["日"] = 7; wd["天"] = 7; meta["weekday_map"] = wd; QByteArray json = makeRuleJson("time", "time_last_week_day", "(?:上周|上个星期|上星期|上一个星期)(?[一二三四五六日天])", 175, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); auto m1 = group.rules[0].regex.match("上周一"); QVERIFY(m1.hasMatch()); QCOMPARE(m1.captured("weekday"), QString("一")); auto m2 = group.rules[0].regex.match("上个星期五"); QVERIFY(m2.hasMatch()); QCOMPARE(m2.captured("weekday"), QString("五")); } void tst_TimeExtraction::presetThisMonthDay() { QVariantMap meta; meta["type"] = "preset_month_day"; meta["preset"] = "this_month"; QMap dig; dig["零"] = 0; dig["一"] = 1; dig["二"] = 2; dig["三"] = 3; dig["四"] = 4; dig["五"] = 5; dig["六"] = 6; dig["七"] = 7; dig["八"] = 8; dig["九"] = 9; dig["十"] = 10; meta["digit_map"] = dig; meta["tens_unit"] = QString("十"); QByteArray json = makeRuleJson("time", "time_this_month_day", "(?:本月|这个月|当月)(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[日号]", 175, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); { auto m = group.rules[0].regex.match("本月5号"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("day"), QString("5")); } { auto m = group.rules[0].regex.match("当月十五日"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("day"), QString("十五")); } { auto m = group.rules[0].regex.match("这个月31号"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("day"), QString("31")); } // should NOT match without day suffix QVERIFY(!group.rules[0].regex.match("本月").hasMatch()); } void tst_TimeExtraction::presetLastMonthDay() { QVariantMap meta; meta["type"] = "preset_month_day"; meta["preset"] = "last_month"; QMap dig; dig["零"] = 0; dig["一"] = 1; dig["二"] = 2; dig["三"] = 3; dig["四"] = 4; dig["五"] = 5; dig["六"] = 6; dig["七"] = 7; dig["八"] = 8; dig["九"] = 9; dig["十"] = 10; meta["digit_map"] = dig; meta["tens_unit"] = QString("十"); QByteArray json = makeRuleJson("time", "time_last_month_day", "(?:上个月|上月)(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[日号]", 175, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); { auto m = group.rules[0].regex.match("上个月15号"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("day"), QString("15")); } { auto m = group.rules[0].regex.match("上月三号"); QVERIFY(m.hasMatch()); QCOMPARE(m.captured("day"), QString("三")); } } // ===== tst_FileTypeExtraction ===== class tst_FileTypeExtraction : public QObject { Q_OBJECT private Q_SLOTS: void precisePdf(); void preciseWord(); void preciseExcel(); void precisePpt(); void imageType(); void videoType(); void audioType(); void genericDocument(); void genericSpreadsheet(); void genericPresentation(); void archiveType(); void extensionsList(); void generalFlag(); }; void tst_FileTypeExtraction::precisePdf() { QVariantMap meta; meta["extensions"] = QStringList({ "pdf" }); QByteArray json = makeRuleJson("filetype", "filetype_pdf", "pdf", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(group.rules[0].regex.match("pdf").hasMatch()); QCOMPARE(group.rules[0].metadata.value("extensions").toStringList(), QStringList({ "pdf" })); } void tst_FileTypeExtraction::preciseWord() { QVariantMap meta; meta["extensions"] = QStringList({ "doc", "docx" }); meta["fileTypes"] = QStringList({ "doc" }); QByteArray json = makeRuleJson("filetype", "filetype_word", "word|doc|docx", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(group.rules[0].regex.match("word").hasMatch()); QVERIFY(group.rules[0].regex.match("docx").hasMatch()); QVERIFY(!group.rules[0].regex.match("pdf").hasMatch()); } void tst_FileTypeExtraction::preciseExcel() { QVariantMap meta; meta["extensions"] = QStringList({ "xls", "xlsx" }); QByteArray json = makeRuleJson("filetype", "filetype_excel", "excel|xls|xlsx", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(group.rules[0].regex.match("excel").hasMatch()); QVERIFY(group.rules[0].regex.match("xlsx").hasMatch()); } void tst_FileTypeExtraction::precisePpt() { QVariantMap meta; meta["extensions"] = QStringList({ "ppt", "pptx" }); QByteArray json = makeRuleJson("filetype", "filetype_ppt", "ppt|pptx", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(group.rules[0].regex.match("ppt").hasMatch()); QVERIFY(group.rules[0].regex.match("pptx").hasMatch()); } void tst_FileTypeExtraction::imageType() { QVariantMap meta; meta["extensions"] = QStringList({ "jpg", "png", "gif" }); meta["fileTypes"] = QStringList({ "pic" }); QByteArray json = makeRuleJson("filetype", "filetype_image", "image", 150, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("fileTypes").toStringList(), QStringList({ "pic" })); } void tst_FileTypeExtraction::videoType() { QVariantMap meta; meta["extensions"] = QStringList({ "mp4", "avi", "mkv" }); meta["fileTypes"] = QStringList({ "video" }); QByteArray json = makeRuleJson("filetype", "filetype_video", "video", 150, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("fileTypes").toStringList(), QStringList({ "video" })); } void tst_FileTypeExtraction::audioType() { QVariantMap meta; meta["extensions"] = QStringList({ "mp3", "wav", "flac" }); meta["fileTypes"] = QStringList({ "audio" }); QByteArray json = makeRuleJson("filetype", "filetype_audio", "audio", 150, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("fileTypes").toStringList(), QStringList({ "audio" })); } void tst_FileTypeExtraction::genericDocument() { QVariantMap meta; meta["extensions"] = QStringList({ "doc", "docx", "pdf", "txt" }); meta["fileTypes"] = QStringList({ "doc" }); meta["general"] = true; QByteArray json = makeRuleJson("filetype", "filetype_document_general", "document", 100, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(group.rules[0].metadata.value("general").toBool()); QCOMPARE(group.rules[0].metadata.value("extensions").toStringList().size(), 4); } void tst_FileTypeExtraction::genericSpreadsheet() { QVariantMap meta; meta["extensions"] = QStringList({ "xls", "xlsx", "csv" }); meta["general"] = true; QByteArray json = makeRuleJson("filetype", "filetype_spreadsheet_general", "spreadsheet", 100, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(group.rules[0].metadata.value("general").toBool()); } void tst_FileTypeExtraction::genericPresentation() { QVariantMap meta; meta["extensions"] = QStringList({ "ppt", "pptx", "dps" }); meta["general"] = true; QByteArray json = makeRuleJson("filetype", "filetype_presentation_general", "presentation", 100, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(group.rules[0].metadata.value("general").toBool()); } void tst_FileTypeExtraction::archiveType() { QVariantMap meta; meta["extensions"] = QStringList({ "zip", "tar", "rar", "7z" }); QByteArray json = makeRuleJson("filetype", "filetype_archive", "archive", 150, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("extensions").toStringList().size(), 4); } void tst_FileTypeExtraction::extensionsList() { QVariantMap meta; meta["extensions"] = QStringList({ "a", "b", "c", "d", "e" }); QByteArray json = makeRuleJson("filetype", "ft", "test", 100, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("extensions").toStringList().size(), 5); } void tst_FileTypeExtraction::generalFlag() { QVariantMap meta; meta["extensions"] = QStringList({ "pdf" }); QByteArray json = makeRuleJson("filetype", "ft_precise", "pdf", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(!group.rules[0].metadata.value("general").toBool()); } // ===== tst_KeywordExtraction ===== class tst_KeywordExtraction : public QObject { Q_OBJECT private Q_SLOTS: void containsPattern(); void namedPattern(); void contentHasPattern(); void noMatch(); void captureGroup(); void multiKeywordFlag(); }; void tst_KeywordExtraction::containsPattern() { QVariantMap meta; meta["capture_group"] = 1; meta["multi_keyword"] = true; QByteArray json = makeRuleJson("keyword", "keyword_contains", "contains(.+?)(?:of|$)", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); auto match = group.rules[0].regex.match("contains meeting notes"); QVERIFY(match.hasMatch()); QCOMPARE(match.captured(1), QString(" meeting notes")); } void tst_KeywordExtraction::namedPattern() { QVariantMap meta; meta["capture_group"] = 1; meta["multi_keyword"] = false; QByteArray json = makeRuleJson("keyword", "keyword_named", "named (.+?)(?: of|$)", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); auto match = group.rules[0].regex.match("named report of"); QVERIFY(match.hasMatch()); QCOMPARE(match.captured(1), QString("report")); } void tst_KeywordExtraction::contentHasPattern() { QVariantMap meta; meta["capture_group"] = 1; meta["multi_keyword"] = true; QByteArray json = makeRuleJson("keyword", "keyword_content_has", "content(?: has| contains| includes)(.+?)(?: of|$)", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); auto match = group.rules[0].regex.match("content includes budget data"); QVERIFY(match.hasMatch()); QCOMPARE(match.captured(1), QString(" budget data")); } void tst_KeywordExtraction::noMatch() { QVariantMap meta; meta["capture_group"] = 1; QByteArray json = makeRuleJson("keyword", "keyword_contains", "contains(.+?)(?:of|$)", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(!group.rules[0].regex.match("no keyword pattern here").hasMatch()); } void tst_KeywordExtraction::captureGroup() { QVariantMap meta; meta["capture_group"] = 1; QByteArray json = makeRuleJson("keyword", "k1", "contains(.+?)(?:of|$)", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QCOMPARE(group.rules[0].metadata.value("capture_group").toInt(), 1); } void tst_KeywordExtraction::multiKeywordFlag() { QVariantMap meta; meta["capture_group"] = 1; meta["multi_keyword"] = true; QByteArray json = makeRuleJson("keyword", "k1", "contains(.+?)(?:of|$)", 200, meta); RuleGroup group; QVERIFY(buildGroupFromJson(json, group)); QVERIFY(group.rules[0].metadata.value("multi_keyword").toBool()); } // ===== tst_ParsedIntent ===== class tst_ParsedIntent : public QObject { Q_OBJECT private Q_SLOTS: void defaultState(); void timeConstraintDefault(); void timeConstraintPreset(); void matchSpanValidity(); }; void tst_ParsedIntent::defaultState() { ParsedIntent intent; QVERIFY(intent.timeConstraint().kind() == TimeConstraintKind::None); QVERIFY(intent.fileExtensions().isEmpty()); QVERIFY(intent.keywords().isEmpty()); QVERIFY(intent.consumedSpans().isEmpty()); } void tst_ParsedIntent::timeConstraintDefault() { TimeConstraint tc; QVERIFY(!tc.isValid()); QCOMPARE(tc.kind(), TimeConstraintKind::None); } void tst_ParsedIntent::timeConstraintPreset() { TimeConstraint tc; tc.setKind(TimeConstraintKind::Preset); tc.setPreset(TimePreset::Today); QVERIFY(tc.isValid()); } void tst_ParsedIntent::matchSpanValidity() { MatchSpan span; QVERIFY(!span.isValid()); span.setStart(0); span.setEnd(5); span.setRuleId("test_rule"); QVERIFY(span.isValid()); } // ===== tst_IsSemanticQuery ===== namespace { // Resolve the source tree rule directory relative to TEST_SOURCE_DIR. // Falls back to a heuristic path if TEST_SOURCE_DIR is not defined. QString sourceRulesDir() { QString base = QString::fromUtf8(TEST_SOURCE_DIR); if (base.isEmpty()) { base = QCoreApplication::applicationDirPath() + "/../../.."; } return base + "/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN"; } // Check whether the source tree rule files exist and are loadable. bool sourceRulesAvailable() { const QString dir = sourceRulesDir(); return QDir(dir).exists() && !QDir(dir).entryList({ "*.json" }, QDir::Files).isEmpty(); } // Replicate isSemanticQuery() logic using internal components with source-tree rules. bool checkIsSemanticQuery(SemanticRuleEngine *engine, IntentParser *parser, const QString &input) { if (input.trimmed().isEmpty()) { return false; } ParsedIntent intent; parser->parse(input, intent); // Replicate isSemanticIntent() — keep in sync with semanticsearcher.cpp. // 维度计数法:任何单维度都不算语义查询,至少 2 个维度组合才算。 // keyword_* 规则 span 单独成立 for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId().startsWith("keyword_")) { return true; } } int dimensionCount = 0; if (!intent.searchDirectories().isEmpty()) ++dimensionCount; if (intent.hiddenOnly()) ++dimensionCount; if (intent.recentOnly()) ++dimensionCount; if (intent.timeConstraint().isValid()) ++dimensionCount; if (intent.sizeConstraint().isValid()) ++dimensionCount; if (!intent.fileExtensions().isEmpty()) ++dimensionCount; if (!intent.keywords().isEmpty()) ++dimensionCount; bool hasTargetRule = false; for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId().startsWith("target_")) { hasTargetRule = true; break; } } if (hasTargetRule) ++dimensionCount; bool hasSpecificFt = false; bool hasGeneralFt = false; for (const MatchSpan &span : intent.consumedSpans()) { const QString rid = span.ruleId(); if (rid.startsWith("filetype_")) { if (rid.endsWith("_general")) hasGeneralFt = true; else hasSpecificFt = true; } } if (hasSpecificFt && hasGeneralFt) ++dimensionCount; return dimensionCount >= 2; } } // namespace class tst_IsSemanticQuery : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void emptyInput(); void whitespaceOnly(); void plainKeyword(); void plainChineseKeyword(); void todayKeyword(); void yesterdayKeyword(); void thisWeekKeyword(); void lastMonthKeyword(); void fileTypePdf(); void fileTypeImage(); void fileTypeDocument(); void locationDesktop(); void locationDownloads(); void sizeLarge(); void sizeSmall(); void sizeDynamic(); void timeAndFileType(); void singleTimeOnly(); void singleSizeOnly(); void singleFileTypeOnly(); void singleRecentOnly(); void timePlusTarget(); void timePlusFileType(); void fileTypePlusTarget(); void specificAndGeneralFiletype(); void sizePlusFileType(); void recentPlusTarget(); void locationAndTime(); void keywordOnlyNoMatch(); void consecutiveCalls(); void noiseWordsOnly(); void hiddenFile(); void structuredKeywordRule(); void locationOnlyRejected(); void locationPlusDimensionAccepted(); // Compound preset time rules (preset year+month, week+weekday, month+day) void compoundTimeLastYearMonth(); void compoundTimeThisYearMonth(); void compoundTimeThisWeekDay(); void compoundTimeLastWeekDay(); void compoundTimeThisMonthDay(); void compoundTimeLastMonthDay(); void compoundTimeNoKeywordLeak(); private: SemanticRuleEngine *m_engine = nullptr; IntentParser *m_parser = nullptr; }; void tst_IsSemanticQuery::initTestCase() { if (!sourceRulesAvailable()) { QSKIP("Rule files not found in source tree, skipping isSemanticQuery tests"); } m_engine = new SemanticRuleEngine(this); const QString dir = sourceRulesDir(); const QStringList ruleFiles = QDir(dir).entryList( { "*.json" }, QDir::Files, QDir::Name); for (const QString &filename : ruleFiles) { QString path = dir + "/" + filename; if (!m_engine->loadRuleFile(path)) { qWarning() << "Failed to load rule file:" << path; } } m_parser = new IntentParser(m_engine); } void tst_IsSemanticQuery::emptyInput() { QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, QString())); } void tst_IsSemanticQuery::whitespaceOnly() { QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, " ")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "\t\n")); } void tst_IsSemanticQuery::plainKeyword() { QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "hello")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "meeting notes")); } void tst_IsSemanticQuery::plainChineseKeyword() { // Pure Chinese text without any semantic triggers. // Avoid words that match filetype/location/time/size rules // (e.g. "报告" matches filetype_document_general, "音乐" matches filetype_audio). QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "蓝天白云")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "春夏秋冬")); } void tst_IsSemanticQuery::todayKeyword() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "今天的文件")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "今日份报告")); } void tst_IsSemanticQuery::yesterdayKeyword() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "昨天的报告")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "昨晚的截图")); } void tst_IsSemanticQuery::thisWeekKeyword() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "本周的文档")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "这周修改的")); } void tst_IsSemanticQuery::lastMonthKeyword() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "上个月的文件")); } void tst_IsSemanticQuery::fileTypePdf() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "pdf文档")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "找一下pdf")); } void tst_IsSemanticQuery::fileTypeImage() { QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "图片")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "截图")); } void tst_IsSemanticQuery::fileTypeDocument() { QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "文档")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "报告")); } void tst_IsSemanticQuery::locationDesktop() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "桌面的文件")); } void tst_IsSemanticQuery::locationDownloads() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "下载的文件")); } void tst_IsSemanticQuery::sizeLarge() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "大文件")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "几个G的文件")); } void tst_IsSemanticQuery::sizeSmall() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "小文件")); } void tst_IsSemanticQuery::sizeDynamic() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "大于500M的文件")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "小于100K")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "小于100K文件")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "10M以上的表格")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "1G以内的文档")); } void tst_IsSemanticQuery::timeAndFileType() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "今天的pdf")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "本周的图片")); } void tst_IsSemanticQuery::singleTimeOnly() { QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "去年")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "去年的")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "今天的")); } void tst_IsSemanticQuery::singleSizeOnly() { QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "大于10M")); } void tst_IsSemanticQuery::singleFileTypeOnly() { QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "PPT")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "文档")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "报告")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "图片")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "截图")); } void tst_IsSemanticQuery::singleRecentOnly() { QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "打开过的")); } void tst_IsSemanticQuery::timePlusTarget() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "去年的文件")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "去年的预算")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "今天的文件")); } void tst_IsSemanticQuery::timePlusFileType() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "去年的报告")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "本周的图片")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "今天的pdf")); } void tst_IsSemanticQuery::fileTypePlusTarget() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "PPT文件")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "pdf文件")); } void tst_IsSemanticQuery::specificAndGeneralFiletype() { // specific filetype + general filetype word (文档/表格/幻灯片) should be // recognized as semantic (≥2 dimensions via the specific+general combo rule). QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "PPT文档")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "pdf文档")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "xls表格")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "PPT幻灯片")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "Word文档")); } void tst_IsSemanticQuery::sizePlusFileType() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "大于10M的文档")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "10M以上的表格")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "1G以内的文档")); } void tst_IsSemanticQuery::recentPlusTarget() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "打开过的预算")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "打开过的文件")); } void tst_IsSemanticQuery::locationAndTime() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "桌面今天的文件")); } void tst_IsSemanticQuery::keywordOnlyNoMatch() { // Text that does not match any semantic rule pattern QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "xyzabc123")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "随便什么文字")); } void tst_IsSemanticQuery::consecutiveCalls() { // Multiple calls with the same input should return consistent results QString input = "今天的pdf"; bool first = checkIsSemanticQuery(m_engine, m_parser, input); bool second = checkIsSemanticQuery(m_engine, m_parser, input); bool third = checkIsSemanticQuery(m_engine, m_parser, input); QCOMPARE(first, second); QCOMPARE(second, third); QVERIFY(first); QString plain = "hello world"; bool p1 = checkIsSemanticQuery(m_engine, m_parser, plain); bool p2 = checkIsSemanticQuery(m_engine, m_parser, plain); QCOMPARE(p1, p2); QVERIFY(!p1); } void tst_IsSemanticQuery::noiseWordsOnly() { // Noise words alone (search action words) without any semantic dimension QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "搜索")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "查找")); } void tst_IsSemanticQuery::hiddenFile() { // hiddenOnly + target "文件" = 2 维度 → 通过 QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "隐藏文件")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "隐藏的文件")); // 纯属性槽单维度 → 不通过(与"打开过的"、"隐藏的"一致) QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "藏起来的")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "看不到的")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "隐藏的")); // time + hiddenOnly = 2 维度 → 通过 QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "今天的隐藏文件")); } void tst_IsSemanticQuery::structuredKeywordRule() { QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "包含测试的文件")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "文件名包含测试的文档")); QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "文件内容包含配置的文档")); } void tst_IsSemanticQuery::locationOnlyRejected() { // 仅命中位置槽、无其他维度 —— 不应通过语义查询检查。 // 这类查询本质是"列出某目录所有文件",indexedstrategy 没有列全部文件的能力, // 由 guardNonSemantic 拦下报 InvalidQuery,避免进入 doSearch 后空关键字报错。 QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "下载")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "微信")); QVERIFY(!checkIsSemanticQuery(m_engine, m_parser, "桌面")); } void tst_IsSemanticQuery::locationPlusDimensionAccepted() { // 位置槽 + 其他维度 —— 应通过语义查询检查。 QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "下载的隐藏文件")); // location + hiddenOnly QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "下载的文档")); // location + fileExtensions QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "下载的pdf")); // location + fileExtensions QVERIFY(checkIsSemanticQuery(m_engine, m_parser, "桌面的今天的文件")); // location + time + target } void tst_IsSemanticQuery::compoundTimeLastYearMonth() { if (!sourceRulesAvailable()) QSKIP("Rule files not found"); ParsedIntent intent; m_parser->parse(QString::fromUtf8("去年12月创建的表格"), intent); // The compound rule should have consumed “去年12月” as a single time span. bool hasCompound = false; for (const auto &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("time_last_year_month")) { hasCompound = true; break; } } QVERIFY(hasCompound); // Time constraint should be Custom (not Preset lastYear), // and the month component should NOT leak into keywords. QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QVERIFY(!intent.keywords().contains(QString::fromUtf8("12月"))); // The Custom range should cover exactly December of last year. const int thisYear = QDate::currentDate().year(); QCOMPARE(intent.timeConstraint().customStart().date(), QDate(thisYear - 1, 12, 1)); QCOMPARE(intent.timeConstraint().customEnd().date(), QDate(thisYear - 1, 12, 31)); } void tst_IsSemanticQuery::compoundTimeThisYearMonth() { if (!sourceRulesAvailable()) QSKIP("Rule files not found"); ParsedIntent intent; m_parser->parse(QString::fromUtf8("今年3月修改的文档"), intent); bool hasCompound = false; for (const auto &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("time_this_year_month")) { hasCompound = true; break; } } QVERIFY(hasCompound); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QVERIFY(!intent.keywords().contains(QString::fromUtf8("3月"))); const int thisYear = QDate::currentDate().year(); QCOMPARE(intent.timeConstraint().customStart().date(), QDate(thisYear, 3, 1)); QCOMPARE(intent.timeConstraint().customEnd().date(), QDate(thisYear, 3, 31)); } void tst_IsSemanticQuery::compoundTimeThisWeekDay() { if (!sourceRulesAvailable()) QSKIP("Rule files not found"); ParsedIntent intent; m_parser->parse(QString::fromUtf8("本周三的文件"), intent); bool hasCompound = false; for (const auto &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("time_this_week_day")) { hasCompound = true; break; } } QVERIFY(hasCompound); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QVERIFY(!intent.keywords().contains(QString::fromUtf8("三"))); // Verify the target date is this week's Wednesday. const QDate today = QDate::currentDate(); const QDate mondayOfThisWeek = today.addDays(1 - today.dayOfWeek()); const QDate expectedDate = mondayOfThisWeek.addDays(3 - 1); // Wed = 3 QCOMPARE(intent.timeConstraint().customStart().date(), expectedDate); QCOMPARE(intent.timeConstraint().customEnd().date(), expectedDate); } void tst_IsSemanticQuery::compoundTimeLastWeekDay() { if (!sourceRulesAvailable()) QSKIP("Rule files not found"); ParsedIntent intent; m_parser->parse(QString::fromUtf8("上周五的文档"), intent); bool hasCompound = false; for (const auto &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("time_last_week_day")) { hasCompound = true; break; } } QVERIFY(hasCompound); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QVERIFY(!intent.keywords().contains(QString::fromUtf8("五"))); const QDate today = QDate::currentDate(); const QDate mondayOfThisWeek = today.addDays(1 - today.dayOfWeek()); const QDate expectedDate = mondayOfThisWeek.addDays(-7 + (5 - 1)); // last week Fri QCOMPARE(intent.timeConstraint().customStart().date(), expectedDate); QCOMPARE(intent.timeConstraint().customEnd().date(), expectedDate); } void tst_IsSemanticQuery::compoundTimeThisMonthDay() { if (!sourceRulesAvailable()) QSKIP("Rule files not found"); ParsedIntent intent; m_parser->parse(QString::fromUtf8("本月15号的文件"), intent); bool hasCompound = false; for (const auto &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("time_this_month_day")) { hasCompound = true; break; } } QVERIFY(hasCompound); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QVERIFY(!intent.keywords().contains(QString::fromUtf8("15号"))); const QDate today = QDate::currentDate(); const QDate expectedDate = QDate(today.year(), today.month(), 15); QCOMPARE(intent.timeConstraint().customStart().date(), expectedDate); QCOMPARE(intent.timeConstraint().customEnd().date(), expectedDate); } void tst_IsSemanticQuery::compoundTimeLastMonthDay() { if (!sourceRulesAvailable()) QSKIP("Rule files not found"); ParsedIntent intent; m_parser->parse(QString::fromUtf8("上个月8号的文档"), intent); bool hasCompound = false; for (const auto &span : intent.consumedSpans()) { if (span.ruleId() == QLatin1String("time_last_month_day")) { hasCompound = true; break; } } QVERIFY(hasCompound); QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QVERIFY(!intent.keywords().contains(QString::fromUtf8("8号"))); const QDate today = QDate::currentDate(); const QDate lastMonth = today.addMonths(-1); const QDate expectedDate = QDate(lastMonth.year(), lastMonth.month(), 8); QCOMPARE(intent.timeConstraint().customStart().date(), expectedDate); QCOMPARE(intent.timeConstraint().customEnd().date(), expectedDate); } void tst_IsSemanticQuery::compoundTimeNoKeywordLeak() { // The core regression test: compound time input should NOT leak // the secondary time component (e.g. “12月”) into keywords. if (!sourceRulesAvailable()) QSKIP("Rule files not found"); const QStringList inputs = { QString::fromUtf8("去年12月创建的表格"), QString::fromUtf8("今年3月修改的文档"), QString::fromUtf8("本周三的文件"), QString::fromUtf8("上周五的文档"), QString::fromUtf8("本月15号的文件"), QString::fromUtf8("上个月8号的文档"), }; for (const QString &input : inputs) { ParsedIntent intent; m_parser->parse(input, intent); // Extract only the numeric month/day substring that was previously leaking. // For “去年12月创建的表格” the old code left “12月” in keywords. QVERIFY2(!intent.keywords().contains(QString::fromUtf8("12月")), qUtf8Printable("12月 leaked into keywords for: " + input)); QVERIFY2(!intent.keywords().contains(QString::fromUtf8("3月")), qUtf8Printable("3月 leaked into keywords for: " + input)); QVERIFY2(!intent.keywords().contains(QString::fromUtf8("三")), qUtf8Printable("三 leaked into keywords for: " + input)); QVERIFY2(!intent.keywords().contains(QString::fromUtf8("五")), qUtf8Printable("五 leaked into keywords for: " + input)); QVERIFY2(!intent.keywords().contains(QString::fromUtf8("15号")), qUtf8Printable("15号 leaked into keywords for: " + input)); QVERIFY2(!intent.keywords().contains(QString::fromUtf8("8号")), qUtf8Printable("8号 leaked into keywords for: " + input)); } } // ===== tst_SearchTarget ===== class tst_SearchTarget : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void defaultIsAll(); void filenameContains(); void filenameNamed(); void contentContains(); void genericContainsStaysAll(); void unconsumedTextStaysAll(); private: SemanticRuleEngine *m_engine = nullptr; KeywordExtractor *m_extractor = nullptr; }; void tst_SearchTarget::initTestCase() { if (!sourceRulesAvailable()) { QSKIP("Rule files not found in source tree, skipping search target tests"); } m_engine = new SemanticRuleEngine(this); const QString dir = sourceRulesDir(); const QStringList ruleFiles = QDir(dir).entryList( { "*.json" }, QDir::Files, QDir::Name); for (const QString &filename : ruleFiles) { QString path = dir + "/" + filename; if (!m_engine->loadRuleFile(path)) { qWarning() << "Failed to load rule file:" << path; } } m_extractor = new KeywordExtractor(m_engine); } void tst_SearchTarget::defaultIsAll() { ParsedIntent intent; m_extractor->extract("蓝天白云", intent); QCOMPARE(intent.searchTarget(), SearchTarget::All); } void tst_SearchTarget::filenameContains() { ParsedIntent intent; m_extractor->extract("文件名包含测试的文档", intent); QCOMPARE(intent.searchTarget(), SearchTarget::FileNameOnly); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("测试")); } void tst_SearchTarget::filenameNamed() { ParsedIntent intent; m_extractor->extract("名为报告的文件", intent); QCOMPARE(intent.searchTarget(), SearchTarget::FileNameOnly); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("报告")); } void tst_SearchTarget::contentContains() { ParsedIntent intent; m_extractor->extract("文件内容包含配置的文档", intent); QCOMPARE(intent.searchTarget(), SearchTarget::ContentOnly); QCOMPARE(intent.keywords().size(), 1); QCOMPARE(intent.keywords().first(), QString("配置")); } void tst_SearchTarget::genericContainsStaysAll() { ParsedIntent intent; m_extractor->extract("包含测试的文件", intent); QCOMPARE(intent.searchTarget(), SearchTarget::All); QVERIFY(!intent.keywords().isEmpty()); } void tst_SearchTarget::unconsumedTextStaysAll() { // No structured keyword rule matches → unconsumed text extraction ParsedIntent intent; m_extractor->extract("项目计划书", intent); QCOMPARE(intent.searchTarget(), SearchTarget::All); QVERIFY(!intent.keywords().isEmpty()); } // ===== tst_LocationExtraction ===== class tst_LocationExtraction : public QObject { Q_OBJECT private Q_SLOTS: // Static method tests — no source-tree rule dependency void expandGlobNoMatch(); void expandGlobSingleMatch(); void expandGlobMultipleMatches(); void expandGlobNestedDirs(); void expandNonGlob(); void expandGlobQuestionMark(); // Integration tests — inline rules only void extractWechatTriggerWords(); void extractCustomPathResolved(); }; void tst_LocationExtraction::expandGlobNoMatch() { // Glob matching nothing → empty result (no crash) QStringList result = LocationExtractor::expandCustomPath("/tmp", "nonexistent/*/data"); QVERIFY(result.isEmpty()); } void tst_LocationExtraction::expandGlobSingleMatch() { QString tmpBase = QDir::temp().filePath("tst_loc_" + QString::number(QDateTime::currentSecsSinceEpoch())); QDir().mkpath(tmpBase + "/user_a/sub/folder"); QStringList result = LocationExtractor::expandCustomPath(tmpBase, "user_*/sub/folder"); QCOMPARE(result.size(), 1); QVERIFY(result.first().endsWith("/user_a/sub/folder")); QDir(tmpBase).removeRecursively(); } void tst_LocationExtraction::expandGlobMultipleMatches() { QString tmpBase = QDir::temp().filePath("tst_loc_" + QString::number(QDateTime::currentSecsSinceEpoch())); QDir().mkpath(tmpBase + "/wxid_abc/msg/file"); QDir().mkpath(tmpBase + "/wxid_def/msg/file"); QDir().mkpath(tmpBase + "/wxid_ghi/msg/file"); QStringList result = LocationExtractor::expandCustomPath(tmpBase, "wxid_*/msg/file"); // At least our 3 test dirs should be present QVERIFY(result.size() >= 3); bool hasA = false, hasD = false, hasG = false; for (const QString &p : result) { if (p.contains("/wxid_abc/msg/file")) hasA = true; if (p.contains("/wxid_def/msg/file")) hasD = true; if (p.contains("/wxid_ghi/msg/file")) hasG = true; } QVERIFY(hasA && hasD && hasG); QDir(tmpBase).removeRecursively(); } void tst_LocationExtraction::expandGlobNestedDirs() { // Pattern: a/b/*/c/d → a//c/d QString tmpBase = QDir::temp().filePath("tst_loc_" + QString::number(QDateTime::currentMSecsSinceEpoch())); QDir().mkpath(tmpBase + "/docs/users/u1/c/d"); QDir().mkpath(tmpBase + "/docs/users/u2/c/d"); QStringList result = LocationExtractor::expandCustomPath(tmpBase, "docs/users/*/c/d"); QVERIFY(result.size() >= 2); QDir(tmpBase).removeRecursively(); } void tst_LocationExtraction::expandNonGlob() { // Non-glob path is returned as-is, appended to base QStringList result = LocationExtractor::expandCustomPath("/home/test", "Downloads/myapp/data"); QCOMPARE(result, QStringList { "/home/test/Downloads/myapp/data" }); } void tst_LocationExtraction::expandGlobQuestionMark() { // ? matches single character QString tmpBase = QDir::temp().filePath("tst_loc_" + QString::number(QDateTime::currentMSecsSinceEpoch())); QDir().mkpath(tmpBase + "/dir_a/data"); QDir().mkpath(tmpBase + "/dir_b/data"); QDir().mkpath(tmpBase + "/dir_abc/data"); // won't match "?_" pattern QStringList result = LocationExtractor::expandCustomPath(tmpBase, "dir_?/data"); QVERIFY(result.size() >= 2); bool hasA = false, hasB = false; for (const QString &p : result) { if (p.endsWith("/dir_a/data")) hasA = true; if (p.endsWith("/dir_b/data")) hasB = true; } QVERIFY(hasA && hasB); QDir(tmpBase).removeRecursively(); } void tst_LocationExtraction::extractWechatTriggerWords() { // Build inline rule engine to avoid source-tree / local filesystem dependency. // Directory must be under QDir::homePath() so expandCustomPath can find it. const QString suffix = QString::number(QDateTime::currentSecsSinceEpoch()); const QString baseName = "tst_wechat_" + suffix; const QString tmpDir = QDir::home().filePath(baseName); QDir().mkpath(tmpDir); QByteArray json = makeRuleJson("location", "loc_test_wechat", "微信|vx|wechat", 180, { { "custom_path", baseName }, { "include_hidden", false } }); QFile f(QDir::temp().filePath("tst_wechat_inline.json")); QVERIFY(f.open(QIODevice::WriteOnly)); f.write(json); f.close(); SemanticRuleEngine engine; QVERIFY(engine.loadRuleFile(f.fileName())); LocationExtractor extractor(&engine); const QStringList inputs = { "微信的文件", "vx收到的", "wechat文件" }; for (const QString &input : inputs) { ParsedIntent intent; extractor.extract(input, intent); QVERIFY2(!intent.consumedSpans().isEmpty(), qUtf8Printable("Expected consumed span for: " + input)); QVERIFY2(!intent.searchDirectories().isEmpty(), qUtf8Printable("Expected searchDirectory for: " + input)); } f.remove(); QDir(tmpDir).rmdir(tmpDir); } void tst_LocationExtraction::extractCustomPathResolved() { // Verify extract() resolves custom_path and appends to searchDirectories. // Directory must live under QDir::homePath() so expandCustomPath can find it. const QString suffix = QString::number(QDateTime::currentSecsSinceEpoch()); const QString baseName = "tst_custom_" + suffix; // Create under home, not QDir::temp() (which may be /tmp, outside home path) const QString tmpDir = QDir::home().filePath(baseName); QDir().mkpath(tmpDir + "/data"); QByteArray json = makeRuleJson("location", "loc_test_cp", "我的目录", 180, { { "custom_path", baseName + "/data" }, { "include_hidden", false } }); QFile f(QDir::temp().filePath("tst_cp_inline.json")); QVERIFY(f.open(QIODevice::WriteOnly)); f.write(json); f.close(); SemanticRuleEngine engine; QVERIFY(engine.loadRuleFile(f.fileName())); LocationExtractor extractor(&engine); ParsedIntent intent; extractor.extract("我的目录的文件", intent); QVERIFY2(!intent.searchDirectories().isEmpty(), "Expected at least one resolved search directory"); // Path must end with /data bool hasData = false; for (const QString &path : intent.searchDirectories()) { if (path.endsWith("/data")) hasData = true; } QVERIFY(hasData); f.remove(); QDir(tmpDir).removeRecursively(); } // ===== tst_SemanticQueryBuilderTarget ===== class tst_SemanticQueryBuilderTarget : public QObject { Q_OBJECT private Q_SLOTS: void defaultTarget(); void fileNameOnlyTarget(); void contentOnlyTarget(); void hiddenOnlyNoKeyword(); void noKeywordWithFileExtensions(); private: ParsedIntent makeIntent(const QStringList &keywords, SearchTarget target) const; }; ParsedIntent tst_SemanticQueryBuilderTarget::makeIntent( const QStringList &keywords, SearchTarget target) const { ParsedIntent intent; intent.setKeywords(keywords); intent.setSearchTarget(target); return intent; } void tst_SemanticQueryBuilderTarget::defaultTarget() { SemanticQueryBuilder builder; ParsedIntent intent = makeIntent({ "测试" }, SearchTarget::All); SemanticSearchPlan plan = builder.build(intent); // All three paths should produce queries QVERIFY(!plan.fileNameQuery.keyword().isEmpty()); QVERIFY(plan.contentQuery.has_value()); QVERIFY(plan.ocrQuery.has_value()); } void tst_SemanticQueryBuilderTarget::fileNameOnlyTarget() { SemanticQueryBuilder builder; ParsedIntent intent = makeIntent({ "测试" }, SearchTarget::FileNameOnly); SemanticSearchPlan plan = builder.build(intent); // Only filename query should be built QVERIFY(!plan.fileNameQuery.keyword().isEmpty()); QVERIFY(!plan.contentQuery.has_value()); QVERIFY(!plan.ocrQuery.has_value()); } void tst_SemanticQueryBuilderTarget::contentOnlyTarget() { SemanticQueryBuilder builder; ParsedIntent intent = makeIntent({ "测试" }, SearchTarget::ContentOnly); SemanticSearchPlan plan = builder.build(intent); // Filename should NOT be built; content and ocr should QVERIFY(plan.fileNameQuery.keyword().isEmpty()); QVERIFY(plan.contentQuery.has_value()); QVERIFY(plan.ocrQuery.has_value()); } void tst_SemanticQueryBuilderTarget::hiddenOnlyNoKeyword() { // "下载的隐藏文件" 这类仅含位置槽 + 属性槽、无关键字槽的语义搜索: // 解析后 hiddenOnly=true、includeHidden=true、searchTarget=FileNameOnly、keywords=[]。 // 预期 plan 仍应构建 fileName 路径(空关键字),由 indexedstrategy 的 // hiddenOnlyQuery (kIsHidden:Y) 作为主查询条件,pathPrefixQuery 限制到下载目录。 SemanticQueryBuilder builder; ParsedIntent intent; intent.setKeywords({}); // 无关键字 intent.setHiddenOnly(true); intent.setIncludeHidden(true); intent.setSearchTarget(SearchTarget::FileNameOnly); intent.setSearchDirectories({ QStringLiteral("/home/test/Downloads") }); SemanticSearchPlan plan = builder.build(intent); // plan 顶层标志必须正确(SemanticSearcher.prepareOptions 会据此透传到 SearchOptions) QVERIFY(plan.hiddenOnly); QVERIFY(plan.includeHidden); QVERIFY(plan.searchDirectories == QStringList { QStringLiteral("/home/test/Downloads") }); // 无关键字时改用 wildcard "*" 匹配所有文件名 —— // indexedstrategy Wildcard 分支 buildWildcardQuery("*") 生成匹配所有文件名的 WildcardQuery, // 让 pathPrefixQuery / hiddenOnlyQuery 作为真正的过滤器叠加。 QCOMPARE(plan.fileNameQuery.keyword(), QStringLiteral("*")); QCOMPARE(plan.fileNameQuery.type(), SearchQuery::Type::Wildcard); // content/ocr 不应启用(hiddenOnly 强制 FileNameOnly,且无关键字) QVERIFY(!plan.contentQuery.has_value()); QVERIFY(!plan.ocrQuery.has_value()); } void tst_SemanticQueryBuilderTarget::noKeywordWithFileExtensions() { // "今天的图片"、"下载的pdf" 这类有 fileExtensions 但无 keyword 的查询: // fileNameQuery 必须保持空 Simple(不是 wildcard "*"),否则 determineSearchType // 会把 wildcard "*" 误导进 Combined 分支用 buildSimpleQuery("*") 走 NGram —— 错误。 // 空 Simple 让 determineSearchType 走 Combined(ext only),由 extQuery 作主查询条件。 SemanticQueryBuilder builder; ParsedIntent intent; intent.setKeywords({}); intent.setFileExtensions({ "png", "jpg" }); intent.setSearchTarget(SearchTarget::All); SemanticSearchPlan plan = builder.build(intent); QVERIFY(plan.fileNameQuery.keyword().isEmpty()); QCOMPARE(plan.fileNameQuery.type(), SearchQuery::Type::Simple); } // ===== Factory functions ===== QObject *create_tst_RuleEngine() { return new tst_RuleEngine(); } QObject *create_tst_TimeExtraction() { return new tst_TimeExtraction(); } QObject *create_tst_FileTypeExtraction() { return new tst_FileTypeExtraction(); } QObject *create_tst_KeywordExtraction() { return new tst_KeywordExtraction(); } QObject *create_tst_ParsedIntent() { return new tst_ParsedIntent(); } QObject *create_tst_IsSemanticQuery() { return new tst_IsSemanticQuery(); } QObject *create_tst_SearchTarget() { return new tst_SearchTarget(); } QObject *create_tst_SemanticQueryBuilderTarget() { return new tst_SemanticQueryBuilderTarget(); } QObject *create_tst_LocationExtraction() { return new tst_LocationExtraction(); } #include "tst_semantic_search.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_size_range_filter.cpp000066400000000000000000000227151523477242100304170ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include "size_parser.h" using namespace DFMSEARCH; class tst_SizeRangeFilter : public QObject { Q_OBJECT private Q_SLOTS: // SizeRangeFilter tests void testDefaultState(); void testSetMin(); void testSetMax(); void testSetRange(); void testFluentChaining(); void testBoundaryControl(); void testCopyConstructor(); void testMoveConstructor(); void testClear(); void testIsValid(); // SizeParser tests void testParseSizeBytes(); void testParseSizeKilobytes(); void testParseSizeMegabytes(); void testParseSizeGigabytes(); void testParseSizeTerabytes(); void testParseSizeCaseInsensitive(); void testParseSizeInvalid(); void testParseSizeEmpty(); void testParseSizeWithSpaces(); // SearchOptions integration void testSearchOptionsSizeFilter(); void testSearchOptionsClearSizeFilter(); // FileNameResultAPI integration void testFileNameResultAPIFileSizeBytes(); }; // ==================== SizeRangeFilter Tests ==================== void tst_SizeRangeFilter::testDefaultState() { SizeRangeFilter filter; QCOMPARE(filter.minSize(), 0); QCOMPARE(filter.maxSize(), 0); QCOMPARE(filter.includeLower(), true); QCOMPARE(filter.includeUpper(), true); QCOMPARE(filter.isValid(), false); } void tst_SizeRangeFilter::testSetMin() { SizeRangeFilter filter; filter.setMin(1024); QCOMPARE(filter.minSize(), 1024); QCOMPARE(filter.isValid(), true); } void tst_SizeRangeFilter::testSetMax() { SizeRangeFilter filter; filter.setMax(10 * 1024 * 1024); QCOMPARE(filter.maxSize(), 10 * 1024 * 1024); QCOMPARE(filter.isValid(), true); } void tst_SizeRangeFilter::testSetRange() { SizeRangeFilter filter; filter.setRange(1024, 10 * 1024 * 1024); QCOMPARE(filter.minSize(), 1024); QCOMPARE(filter.maxSize(), 10 * 1024 * 1024); QCOMPARE(filter.isValid(), true); } void tst_SizeRangeFilter::testFluentChaining() { SizeRangeFilter filter; auto &ref = filter.setMin(1024).setMax(10 * 1024 * 1024); QCOMPARE(filter.minSize(), 1024); QCOMPARE(filter.maxSize(), 10 * 1024 * 1024); QCOMPARE(&ref, &filter); // 返回自身的引用 } void tst_SizeRangeFilter::testBoundaryControl() { SizeRangeFilter filter; filter.setRange(1024, 10 * 1024 * 1024); filter.setIncludeLower(false); filter.setIncludeUpper(false); QCOMPARE(filter.includeLower(), false); QCOMPARE(filter.includeUpper(), false); } void tst_SizeRangeFilter::testCopyConstructor() { SizeRangeFilter original; original.setRange(1024, 10 * 1024 * 1024); original.setIncludeLower(false); SizeRangeFilter copy(original); QCOMPARE(copy.minSize(), 1024); QCOMPARE(copy.maxSize(), 10 * 1024 * 1024); QCOMPARE(copy.includeLower(), false); QCOMPARE(copy.includeUpper(), true); QCOMPARE(copy.isValid(), true); } void tst_SizeRangeFilter::testMoveConstructor() { SizeRangeFilter original; original.setRange(1024, 10 * 1024 * 1024); SizeRangeFilter moved(std::move(original)); QCOMPARE(moved.minSize(), 1024); QCOMPARE(moved.maxSize(), 10 * 1024 * 1024); QCOMPARE(moved.isValid(), true); } void tst_SizeRangeFilter::testClear() { SizeRangeFilter filter; filter.setRange(1024, 10 * 1024 * 1024); filter.setIncludeLower(false); filter.setIncludeUpper(false); filter.clear(); QCOMPARE(filter.minSize(), 0); QCOMPARE(filter.maxSize(), 0); QCOMPARE(filter.includeLower(), true); // 重置为默认值 QCOMPARE(filter.includeUpper(), true); QCOMPARE(filter.isValid(), false); } void tst_SizeRangeFilter::testIsValid() { SizeRangeFilter filter; // 默认状态无效 QCOMPARE(filter.isValid(), false); // 设置 min 后有效 filter.setMin(1); QCOMPARE(filter.isValid(), true); filter.clear(); // 设置 max 后有效 filter.setMax(1); QCOMPARE(filter.isValid(), true); filter.clear(); // 设置 0 值仍无效 filter.setMin(0); QCOMPARE(filter.isValid(), false); filter.setMax(0); QCOMPARE(filter.isValid(), false); } // ==================== SizeParser Tests ==================== void tst_SizeRangeFilter::testParseSizeBytes() { qint64 bytes = 0; QVERIFY(dfmsearch::SizeParser::parseSize("512", bytes)); QCOMPARE(bytes, 512); QVERIFY(dfmsearch::SizeParser::parseSize("0", bytes)); QCOMPARE(bytes, 0); QVERIFY(dfmsearch::SizeParser::parseSize("1024", bytes)); QCOMPARE(bytes, 1024); } void tst_SizeRangeFilter::testParseSizeKilobytes() { qint64 bytes = 0; QVERIFY(dfmsearch::SizeParser::parseSize("1K", bytes)); QCOMPARE(bytes, 1024); QVERIFY(dfmsearch::SizeParser::parseSize("1KB", bytes)); QCOMPARE(bytes, 1024); QVERIFY(dfmsearch::SizeParser::parseSize("10K", bytes)); QCOMPARE(bytes, 10240); QVERIFY(dfmsearch::SizeParser::parseSize("1.5K", bytes)); QCOMPARE(bytes, 1536); } void tst_SizeRangeFilter::testParseSizeMegabytes() { qint64 bytes = 0; QVERIFY(dfmsearch::SizeParser::parseSize("1M", bytes)); QCOMPARE(bytes, 1024 * 1024); QVERIFY(dfmsearch::SizeParser::parseSize("1MB", bytes)); QCOMPARE(bytes, 1024 * 1024); QVERIFY(dfmsearch::SizeParser::parseSize("10M", bytes)); QCOMPARE(bytes, 10LL * 1024 * 1024); QVERIFY(dfmsearch::SizeParser::parseSize("1.5M", bytes)); QCOMPARE(bytes, static_cast(1.5 * 1024 * 1024)); } void tst_SizeRangeFilter::testParseSizeGigabytes() { qint64 bytes = 0; QVERIFY(dfmsearch::SizeParser::parseSize("1G", bytes)); QCOMPARE(bytes, 1024LL * 1024 * 1024); QVERIFY(dfmsearch::SizeParser::parseSize("1GB", bytes)); QCOMPARE(bytes, 1024LL * 1024 * 1024); QVERIFY(dfmsearch::SizeParser::parseSize("10G", bytes)); QCOMPARE(bytes, 10LL * 1024 * 1024 * 1024); } void tst_SizeRangeFilter::testParseSizeTerabytes() { qint64 bytes = 0; QVERIFY(dfmsearch::SizeParser::parseSize("1T", bytes)); QCOMPARE(bytes, 1024LL * 1024 * 1024 * 1024); QVERIFY(dfmsearch::SizeParser::parseSize("1TB", bytes)); QCOMPARE(bytes, 1024LL * 1024 * 1024 * 1024); } void tst_SizeRangeFilter::testParseSizeCaseInsensitive() { qint64 bytes = 0; QVERIFY(dfmsearch::SizeParser::parseSize("1k", bytes)); QCOMPARE(bytes, 1024); QVERIFY(dfmsearch::SizeParser::parseSize("1m", bytes)); QCOMPARE(bytes, 1024 * 1024); QVERIFY(dfmsearch::SizeParser::parseSize("1g", bytes)); QCOMPARE(bytes, 1024LL * 1024 * 1024); QVERIFY(dfmsearch::SizeParser::parseSize("1kb", bytes)); QCOMPARE(bytes, 1024); QVERIFY(dfmsearch::SizeParser::parseSize("1mb", bytes)); QCOMPARE(bytes, 1024 * 1024); } void tst_SizeRangeFilter::testParseSizeInvalid() { qint64 bytes = 0; // 未知后缀 QVERIFY(!dfmsearch::SizeParser::parseSize("1X", bytes)); // 纯字母 QVERIFY(!dfmsearch::SizeParser::parseSize("abc", bytes)); // 负数 QVERIFY(!dfmsearch::SizeParser::parseSize("-1K", bytes)); // 空数字 QVERIFY(!dfmsearch::SizeParser::parseSize("K", bytes)); } void tst_SizeRangeFilter::testParseSizeEmpty() { qint64 bytes = -1; QVERIFY(!dfmsearch::SizeParser::parseSize("", bytes)); QCOMPARE(bytes, -1); // 不应被修改 } void tst_SizeRangeFilter::testParseSizeWithSpaces() { qint64 bytes = 0; QVERIFY(dfmsearch::SizeParser::parseSize(" 1M ", bytes)); QCOMPARE(bytes, 1024 * 1024); QVERIFY(dfmsearch::SizeParser::parseSize(" 512 ", bytes)); QCOMPARE(bytes, 512); } // ==================== SearchOptions Integration Tests ==================== void tst_SizeRangeFilter::testSearchOptionsSizeFilter() { SearchOptions options; // 默认无大小过滤 QCOMPARE(options.hasSizeRangeFilter(), false); // 设置大小过滤 SizeRangeFilter filter; filter.setRange(1024, 10 * 1024 * 1024); options.setSizeRangeFilter(filter); QCOMPARE(options.hasSizeRangeFilter(), true); SizeRangeFilter retrieved = options.sizeRangeFilter(); QCOMPARE(retrieved.minSize(), 1024); QCOMPARE(retrieved.maxSize(), 10 * 1024 * 1024); } void tst_SizeRangeFilter::testSearchOptionsClearSizeFilter() { SearchOptions options; SizeRangeFilter filter; filter.setRange(1024, 10 * 1024 * 1024); options.setSizeRangeFilter(filter); QCOMPARE(options.hasSizeRangeFilter(), true); options.clearSizeRangeFilter(); QCOMPARE(options.hasSizeRangeFilter(), false); SizeRangeFilter retrieved = options.sizeRangeFilter(); QCOMPARE(retrieved.minSize(), 0); QCOMPARE(retrieved.maxSize(), 0); } // ==================== FileNameResultAPI Integration Tests ==================== void tst_SizeRangeFilter::testFileNameResultAPIFileSizeBytes() { SearchResult result("/home/user/test.txt"); FileNameResultAPI api(result); // 默认值 QCOMPARE(api.fileSizeBytes(), 0); // 设置和获取 api.setFileSizeBytes(1024); QCOMPARE(api.fileSizeBytes(), 1024); // 设置大文件 api.setFileSizeBytes(10LL * 1024 * 1024 * 1024); QCOMPARE(api.fileSizeBytes(), 10LL * 1024 * 1024 * 1024); // 设置 0 api.setFileSizeBytes(0); QCOMPARE(api.fileSizeBytes(), 0); } QObject *create_tst_SizeRangeFilter() { return new tst_SizeRangeFilter(); } #include "tst_size_range_filter.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_textsearch_api.cpp000066400000000000000000000236441523477242100277310ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include using namespace DFMSEARCH; class tst_TextSearchAPI : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); // TextSearchOptionsAPI tests void textSearchOptions_defaultValues(); void textSearchOptions_setMaxPreviewLength(); void textSearchOptions_setSearchResultHighlightEnabled(); void textSearchOptions_setFullTextRetrievalEnabled(); // TextSearchResultAPI tests void textSearchResult_highlightedContent(); void textSearchResult_filename(); void textSearchResult_isHidden(); void textSearchResult_modifyTimestamp(); void textSearchResult_birthTimestamp(); void textSearchResult_charCount(); void textSearchResult_plainSnippetAttributes(); // ContentOptionsAPI tests void contentOptions_inheritance(); void contentOptions_setFilenameContentMixedAndSearchEnabled(); // ContentResultAPI tests void contentResult_inheritance(); // OcrTextOptionsAPI tests void ocrTextOptions_inheritance(); void ocrTextOptions_defaultValues(); void ocrTextOptions_setFilenameOcrContentMixedAndSearchEnabled(); // OcrTextResultAPI tests void ocrTextResult_inheritance(); void ocrTextResult_ocrContent(); }; void tst_TextSearchAPI::initTestCase() { } void tst_TextSearchAPI::cleanupTestCase() { } // ==================== TextSearchOptionsAPI Tests ==================== void tst_TextSearchAPI::textSearchOptions_defaultValues() { SearchOptions options; TextSearchOptionsAPI api(options); // 默认值在构造函数中设置,但基类构造函数不设置默认值 // 默认值由子类设置 QCOMPARE(api.maxPreviewLength(), 0); QCOMPARE(api.isSearchResultHighlightEnabled(), false); QCOMPARE(api.isFullTextRetrievalEnabled(), false); } void tst_TextSearchAPI::textSearchOptions_setMaxPreviewLength() { SearchOptions options; TextSearchOptionsAPI api(options); api.setMaxPreviewLength(100); QCOMPARE(api.maxPreviewLength(), 100); api.setMaxPreviewLength(500); QCOMPARE(api.maxPreviewLength(), 500); api.setMaxPreviewLength(0); QCOMPARE(api.maxPreviewLength(), 0); } void tst_TextSearchAPI::textSearchOptions_setSearchResultHighlightEnabled() { SearchOptions options; TextSearchOptionsAPI api(options); api.setSearchResultHighlightEnabled(true); QCOMPARE(api.isSearchResultHighlightEnabled(), true); api.setSearchResultHighlightEnabled(false); QCOMPARE(api.isSearchResultHighlightEnabled(), false); } void tst_TextSearchAPI::textSearchOptions_setFullTextRetrievalEnabled() { SearchOptions options; TextSearchOptionsAPI api(options); api.setFullTextRetrievalEnabled(true); QCOMPARE(api.isFullTextRetrievalEnabled(), true); api.setFullTextRetrievalEnabled(false); QCOMPARE(api.isFullTextRetrievalEnabled(), false); } // ==================== TextSearchResultAPI Tests ==================== void tst_TextSearchAPI::textSearchResult_highlightedContent() { SearchResult result("/test/path"); TextSearchResultAPI api(result); QVERIFY(api.highlightedContent().isEmpty()); api.setHighlightedContent("test highlighted content"); QCOMPARE(api.highlightedContent(), QString("test highlighted content")); api.setHighlightedContent(""); QVERIFY(api.highlightedContent().isEmpty()); } void tst_TextSearchAPI::textSearchResult_filename() { SearchResult result("/test/path"); TextSearchResultAPI api(result); QVERIFY(api.filename().isEmpty()); api.setFilename("test.txt"); QCOMPARE(api.filename(), QString("test.txt")); api.setFilename(""); QVERIFY(api.filename().isEmpty()); } void tst_TextSearchAPI::textSearchResult_isHidden() { SearchResult result("/test/path"); TextSearchResultAPI api(result); QCOMPARE(api.isHidden(), false); api.setIsHidden(true); QCOMPARE(api.isHidden(), true); api.setIsHidden(false); QCOMPARE(api.isHidden(), false); } void tst_TextSearchAPI::textSearchResult_modifyTimestamp() { SearchResult result("/test/path"); TextSearchResultAPI api(result); QCOMPARE(api.modifyTimestamp(), 0); api.setModifyTimestamp(1700000000); QCOMPARE(api.modifyTimestamp(), 1700000000); QVERIFY(!api.modifyTimeString().isEmpty()); api.setModifyTimestamp(0); QCOMPARE(api.modifyTimestamp(), 0); } void tst_TextSearchAPI::textSearchResult_birthTimestamp() { SearchResult result("/test/path"); TextSearchResultAPI api(result); QCOMPARE(api.birthTimestamp(), 0); api.setBirthTimestamp(1600000000); QCOMPARE(api.birthTimestamp(), 1600000000); QVERIFY(!api.birthTimeString().isEmpty()); api.setBirthTimestamp(0); QCOMPARE(api.birthTimestamp(), 0); } void tst_TextSearchAPI::textSearchResult_charCount() { SearchResult result("/test/path"); TextSearchResultAPI api(result); QCOMPARE(api.charCount(), 0); api.setCharCount(128); QCOMPARE(api.charCount(), 128); api.setCharCount(0); QCOMPARE(api.charCount(), 0); } void tst_TextSearchAPI::textSearchResult_plainSnippetAttributes() { SearchResult result("/test/path"); QVERIFY(!result.hasCustomAttribute("plainContentMatch")); QVERIFY(!result.hasCustomAttribute("snippetOffset")); result.setCustomAttribute("plainContentMatch", QString("plain snippet")); result.setCustomAttribute("snippetOffset", 42); QCOMPARE(result.customAttribute("plainContentMatch").toString(), QString("plain snippet")); QCOMPARE(result.customAttribute("snippetOffset").toInt(), 42); } // ==================== ContentOptionsAPI Tests ==================== void tst_TextSearchAPI::contentOptions_inheritance() { SearchOptions options; ContentOptionsAPI api(options); // 验证继承自 TextSearchOptionsAPI api.setMaxPreviewLength(300); QCOMPARE(api.maxPreviewLength(), 300); api.setSearchResultHighlightEnabled(true); QCOMPARE(api.isSearchResultHighlightEnabled(), true); api.setFullTextRetrievalEnabled(false); QCOMPARE(api.isFullTextRetrievalEnabled(), false); } void tst_TextSearchAPI::contentOptions_setFilenameContentMixedAndSearchEnabled() { SearchOptions options; ContentOptionsAPI api(options); QCOMPARE(api.isFilenameContentMixedAndSearchEnabled(), false); api.setFilenameContentMixedAndSearchEnabled(true); QCOMPARE(api.isFilenameContentMixedAndSearchEnabled(), true); api.setFilenameContentMixedAndSearchEnabled(false); QCOMPARE(api.isFilenameContentMixedAndSearchEnabled(), false); } // ==================== ContentResultAPI Tests ==================== void tst_TextSearchAPI::contentResult_inheritance() { SearchResult result("/test/path"); ContentResultAPI api(result); // 验证继承自 TextSearchResultAPI api.setHighlightedContent("content match"); QCOMPARE(api.highlightedContent(), QString("content match")); api.setFilename("document.pdf"); QCOMPARE(api.filename(), QString("document.pdf")); api.setIsHidden(true); QCOMPARE(api.isHidden(), true); api.setModifyTimestamp(1700000000); QCOMPARE(api.modifyTimestamp(), 1700000000); api.setBirthTimestamp(1600000000); QCOMPARE(api.birthTimestamp(), 1600000000); } // ==================== OcrTextOptionsAPI Tests ==================== void tst_TextSearchAPI::ocrTextOptions_inheritance() { SearchOptions options; OcrTextOptionsAPI api(options); // 验证继承自 TextSearchOptionsAPI api.setMaxPreviewLength(250); QCOMPARE(api.maxPreviewLength(), 250); api.setSearchResultHighlightEnabled(true); QCOMPARE(api.isSearchResultHighlightEnabled(), true); api.setFullTextRetrievalEnabled(false); QCOMPARE(api.isFullTextRetrievalEnabled(), false); } void tst_TextSearchAPI::ocrTextOptions_defaultValues() { SearchOptions options; OcrTextOptionsAPI api(options); // 验证默认值 QCOMPARE(api.maxPreviewLength(), 200); QCOMPARE(api.isSearchResultHighlightEnabled(), false); QCOMPARE(api.isFullTextRetrievalEnabled(), true); QCOMPARE(api.isFilenameOcrContentMixedAndSearchEnabled(), false); } void tst_TextSearchAPI::ocrTextOptions_setFilenameOcrContentMixedAndSearchEnabled() { SearchOptions options; OcrTextOptionsAPI api(options); QCOMPARE(api.isFilenameOcrContentMixedAndSearchEnabled(), false); api.setFilenameOcrContentMixedAndSearchEnabled(true); QCOMPARE(api.isFilenameOcrContentMixedAndSearchEnabled(), true); api.setFilenameOcrContentMixedAndSearchEnabled(false); QCOMPARE(api.isFilenameOcrContentMixedAndSearchEnabled(), false); } // ==================== OcrTextResultAPI Tests ==================== void tst_TextSearchAPI::ocrTextResult_inheritance() { SearchResult result("/test/path"); OcrTextResultAPI api(result); // 验证继承自 TextSearchResultAPI api.setHighlightedContent("OCR match"); QCOMPARE(api.highlightedContent(), QString("OCR match")); api.setFilename("image.png"); QCOMPARE(api.filename(), QString("image.png")); api.setIsHidden(true); QCOMPARE(api.isHidden(), true); api.setModifyTimestamp(1700000000); QCOMPARE(api.modifyTimestamp(), 1700000000); api.setBirthTimestamp(1600000000); QCOMPARE(api.birthTimestamp(), 1600000000); } void tst_TextSearchAPI::ocrTextResult_ocrContent() { SearchResult result("/test/path"); OcrTextResultAPI api(result); QVERIFY(api.ocrContent().isEmpty()); api.setOcrContent("This is extracted OCR text from image"); QCOMPARE(api.ocrContent(), QString("This is extracted OCR text from image")); api.setOcrContent(""); QVERIFY(api.ocrContent().isEmpty()); } QObject *create_tst_TextSearchAPI() { return new tst_TextSearchAPI(); } #include "tst_textsearch_api.moc" linuxdeepin-util-dfm-a4307d3/autotests/dfm-search-tests/tst_timerangefilter.cpp000066400000000000000000000210011523477242100300700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include using namespace DFMSEARCH; class tst_TimeRangeFilter : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void default_constructor_test(); void set_time_field_test(); void set_last_test(); void set_today_test(); void set_yesterday_test(); void set_this_week_test(); void set_last_week_test(); void set_this_month_test(); void set_last_month_test(); void set_this_year_test(); void set_last_year_test(); void set_range_test(); void boundary_control_test(); void clear_test(); void is_valid_test(); void resolve_time_range_test(); void fluent_interface_test(); }; void tst_TimeRangeFilter::initTestCase() { } void tst_TimeRangeFilter::cleanupTestCase() { } void tst_TimeRangeFilter::default_constructor_test() { TimeRangeFilter filter; QVERIFY(!filter.isValid()); QCOMPARE(filter.timeField(), TimeField::ModifyTime); QVERIFY(filter.includeLower()); QVERIFY(!filter.includeUpper()); } void tst_TimeRangeFilter::set_time_field_test() { TimeRangeFilter filter; filter.setTimeField(TimeField::BirthTime); QCOMPARE(filter.timeField(), TimeField::BirthTime); filter.setTimeField(TimeField::ModifyTime); QCOMPARE(filter.timeField(), TimeField::ModifyTime); } void tst_TimeRangeFilter::set_last_test() { TimeRangeFilter filter; filter.setLast(3, TimeUnit::Days); QVERIFY(filter.isValid()); auto [start, end] = filter.resolveTimeRange(); QVERIFY(start.isValid()); QVERIFY(end.isValid()); // "Last 3 days" for setLast: rolling range from 3 days ago (00:00:00) to now QDateTime now = QDateTime::currentDateTime(); QDateTime expectedStart = QDateTime(now.date().addDays(-3), QTime(0, 0, 0)); QCOMPARE(start, expectedStart); // End should be close to now (within 2 seconds) QVERIFY(qAbs(end.secsTo(now)) < 2); // Test minutes - this is precise filter.setLast(30, TimeUnit::Minutes); auto [start2, end2] = filter.resolveTimeRange(); QVERIFY(start2.isValid()); QVERIFY(end2.isValid()); // Minutes should be precise (within 2 seconds tolerance) QVERIFY(qAbs(start2.secsTo(now.addSecs(-30 * 60))) < 2); // Test hours - this is precise filter.setLast(2, TimeUnit::Hours); auto [start3, end3] = filter.resolveTimeRange(); QVERIFY(start3.isValid()); QVERIFY(end3.isValid()); // Hours should be precise (within 2 seconds tolerance) QVERIFY(qAbs(start3.secsTo(now.addSecs(-2 * 3600))) < 2); } void tst_TimeRangeFilter::set_today_test() { TimeRangeFilter filter; filter.setToday(); QVERIFY(filter.isValid()); auto [start, end] = filter.resolveTimeRange(); QVERIFY(start.isValid()); QVERIFY(end.isValid()); // Today should start at 00:00:00 QCOMPARE(start.time(), QTime(0, 0, 0)); QCOMPARE(start.date(), QDate::currentDate()); // End should be tomorrow 00:00:00 QCOMPARE(end.date(), QDate::currentDate().addDays(1)); QCOMPARE(end.time(), QTime(0, 0, 0)); } void tst_TimeRangeFilter::set_yesterday_test() { TimeRangeFilter filter; filter.setYesterday(); QVERIFY(filter.isValid()); auto [start, end] = filter.resolveTimeRange(); QVERIFY(start.isValid()); QVERIFY(end.isValid()); // Yesterday should start at 00:00:00 QCOMPARE(start.time(), QTime(0, 0, 0)); QCOMPARE(start.date(), QDate::currentDate().addDays(-1)); // End should be today 00:00:00 QCOMPARE(end.date(), QDate::currentDate()); QCOMPARE(end.time(), QTime(0, 0, 0)); } void tst_TimeRangeFilter::set_this_week_test() { TimeRangeFilter filter; filter.setThisWeek(); QVERIFY(filter.isValid()); auto [start, end] = filter.resolveTimeRange(); QVERIFY(start.isValid()); QVERIFY(end.isValid()); // Should start on Monday 00:00:00 QCOMPARE(start.time(), QTime(0, 0, 0)); QCOMPARE(start.date().dayOfWeek(), 1); // Monday is day 1 } void tst_TimeRangeFilter::set_last_week_test() { TimeRangeFilter filter; filter.setLastWeek(); QVERIFY(filter.isValid()); auto [start, end] = filter.resolveTimeRange(); QVERIFY(start.isValid()); QVERIFY(end.isValid()); // Should be a 7-day range qint64 diff = start.daysTo(end); QCOMPARE(diff, 7); } void tst_TimeRangeFilter::set_this_month_test() { TimeRangeFilter filter; filter.setThisMonth(); QVERIFY(filter.isValid()); auto [start, end] = filter.resolveTimeRange(); QVERIFY(start.isValid()); QVERIFY(end.isValid()); // Should start on the 1st QCOMPARE(start.date().day(), 1); QCOMPARE(start.time(), QTime(0, 0, 0)); } void tst_TimeRangeFilter::set_last_month_test() { TimeRangeFilter filter; filter.setLastMonth(); QVERIFY(filter.isValid()); auto [start, end] = filter.resolveTimeRange(); QVERIFY(start.isValid()); QVERIFY(end.isValid()); // Should start on the 1st of last month QCOMPARE(start.date().day(), 1); QCOMPARE(start.time(), QTime(0, 0, 0)); } void tst_TimeRangeFilter::set_this_year_test() { TimeRangeFilter filter; filter.setThisYear(); QVERIFY(filter.isValid()); auto [start, end] = filter.resolveTimeRange(); QVERIFY(start.isValid()); QVERIFY(end.isValid()); // Should start on Jan 1st QCOMPARE(start.date().month(), 1); QCOMPARE(start.date().day(), 1); QCOMPARE(start.time(), QTime(0, 0, 0)); } void tst_TimeRangeFilter::set_last_year_test() { TimeRangeFilter filter; filter.setLastYear(); QVERIFY(filter.isValid()); auto [start, end] = filter.resolveTimeRange(); QVERIFY(start.isValid()); QVERIFY(end.isValid()); // Should be previous year QCOMPARE(start.date().year(), QDate::currentDate().year() - 1); } void tst_TimeRangeFilter::set_range_test() { TimeRangeFilter filter; QDateTime startDate(QDate(2025, 10, 1), QTime(0, 0, 0)); QDateTime endDate(QDate(2025, 10, 31), QTime(23, 59, 59)); filter.setRange(startDate, endDate); QVERIFY(filter.isValid()); QCOMPARE(filter.startTime(), startDate); QCOMPARE(filter.endTime(), endDate); auto [resolvedStart, resolvedEnd] = filter.resolveTimeRange(); QCOMPARE(resolvedStart, startDate); QCOMPARE(resolvedEnd, endDate); } void tst_TimeRangeFilter::boundary_control_test() { TimeRangeFilter filter; filter.setToday(); // Default: includeLower=true, includeUpper=false QVERIFY(filter.includeLower()); QVERIFY(!filter.includeUpper()); // Test fluent interface filter.setIncludeLower(false).setIncludeUpper(true); QVERIFY(!filter.includeLower()); QVERIFY(filter.includeUpper()); } void tst_TimeRangeFilter::clear_test() { TimeRangeFilter filter; filter.setToday(); QVERIFY(filter.isValid()); filter.clear(); QVERIFY(!filter.isValid()); QCOMPARE(filter.timeField(), TimeField::ModifyTime); QVERIFY(filter.includeLower()); QVERIFY(!filter.includeUpper()); } void tst_TimeRangeFilter::is_valid_test() { TimeRangeFilter filter; QVERIFY(!filter.isValid()); filter.setToday(); QVERIFY(filter.isValid()); filter.clear(); QVERIFY(!filter.isValid()); QDateTime start, end; filter.setRange(start, end); // Range with invalid datetimes is still "valid" in terms of being set QVERIFY(filter.isValid()); } void tst_TimeRangeFilter::resolve_time_range_test() { // Test static method auto [start, end] = TimeRangeFilter::resolveRelativeTimeRange(0, TimeUnit::Days); QVERIFY(start.isValid()); QVERIFY(end.isValid()); QCOMPARE(start.time(), QTime(0, 0, 0)); // Test with different units auto [start2, end2] = TimeRangeFilter::resolveRelativeTimeRange(30, TimeUnit::Minutes); QVERIFY(start2.isValid()); QVERIFY(end2.isValid()); } void tst_TimeRangeFilter::fluent_interface_test() { // Test method chaining TimeRangeFilter filter; filter.setTimeField(TimeField::BirthTime) .setLast(7, TimeUnit::Days) .setIncludeLower(true) .setIncludeUpper(true); QCOMPARE(filter.timeField(), TimeField::BirthTime); QVERIFY(filter.isValid()); QVERIFY(filter.includeLower()); QVERIFY(filter.includeUpper()); // Test clear returns reference filter.clear(); QVERIFY(!filter.isValid()); } QObject *create_tst_TimeRangeFilter() { return new tst_TimeRangeFilter(); } #include "tst_timerangefilter.moc" linuxdeepin-util-dfm-a4307d3/debian/000077500000000000000000000000001523477242100173355ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/debian/changelog000066400000000000000000000560471523477242100212230ustar00rootroot00000000000000util-dfm (1.4.3) unstable; urgency=medium * fix: add missing WeChat group term in Chinese rules * fix(dfm-search): exclude WeChat monthly archive dirs from search results * feat: add compound preset time rules for semantic search * fix(dfm-search): support Chinese-numeral years in semantic time parsing * fix: restrict path traversal check to file scheme in DEnumerator buildUrl * fix: narrow path traversal check pattern in DEnumerator buildUrl * chore: Sync by https://github.com/linuxdeepin/.github/commit/826bedc64f009f4d9ebb5fba0ffe1637f1f02667 * fix(dfm-search): eliminate cross-thread race on m_strategy causing heap corruption -- Zhang Sheng Thu, 06 Aug 2026 10:22:13 +0800 util-dfm (1.4.2) unstable; urgency=medium * chore: update GitHub actions workflow for cppcheck * fix: update test synonyms for Chinese file types * fix: filter excluded paths in recent search strategy -- Zhang Sheng Fri, 24 Jul 2026 13:16:10 +0800 util-dfm (1.4.1) unstable; urgency=medium * fix: intercept -h/--help to show custom help with highlight subcommand * refactor: rename highlight command to preview * refactor: improve preview command UX and flexibility * feat: enhance preview command with keyword-free mode * test: add boolean OR search tests and improve query parsing * feat: add precise content preview with offset support * docs: add dfm-searcher guide documentation * feat: return full content when no keyword and no --max-preview * fix: validate CLI option compatibility with search type and apply file-extensions filter for content/ocr * feat: add directory exclusion feature to search * feat: add dvd+rw-format support for DVD RW erase * feat: enhance semantic search with multiple paths and pattern filtering * feat: add --features option to list supported search features * fix: handle DConfig list values in search utility * fix: adjust order of time recognition patterns * fix(dfm-search): extend semantic filetype suffix coverage * fix(search): return plain verbose snippets for text search * feat: add character count support for search results * feat: enhance preview command with indexed content validation * fix: update Chinese semantic search pattern rules * fix: improve Chinese date parsing and handling * fix: enhance Chinese numeral time parsing * fix: update Chinese filetype matching patterns * fix: improve Chinese NLP filetype detection accuracy * feat: update file type matching patterns * fix: update Chinese location rule patterns -- Zhang Sheng Fri, 24 Jul 2026 09:49:33 +0800 util-dfm (1.3.60) unstable; urgency=medium * fix(dfm-io): drain GLib deferred callbacks after g_file_enumerate_children * fix(dfm-io): close FTS tree in destructor to prevent memory leak * perf(dfm-io): cache statx results in DFileInfoPrivate::attributesBySelf * fix: prevent BOM character loss in path concatenation * Revert "fix(dfm-io): drain GLib deferred callbacks after g_file_enumerate_children" * feat: add semantic query validation in search * feat: expand supported file extensions for search * refactor: update semantic search rules and tests * fix: improve IM received files search accuracy * feat: enhance Chinese NLP action recognition * feat: add hidden file search support for semantic search * feat: add hidden-only file search functionality * feat: add file extension filter support for searches * fix: update semantic search validation conditions * feat: add file extension field to search index * feat: implement semantic search routing * refactor: optimize file extension filtering in search strategies * refactor: convert HighlightOptions to Pimpl pattern * fix: refactor ParsedIntent to use pimpl pattern * refactor: refactor semantic types to use PIMPL pattern * feat: implement recent files search via DBus RecentManager * test: add recent search engine unit tests * refactor: update recent search D-Bus integration * fix: adjust search directory priority in semantic search * feat: add parseIntent API for semantic search preview * feat: expand supported document file extensions * feat: enhance semantic search detection logic * fix: handle hidden files search without keywords * refactor: refine semantic search classification logic * fix: enhance filetype matching rules and semantic detection * feat: resolve location-filetype conflict in search * feat: add version info to dfm-searcher client * fix: correct loading of string list from DConfig -- Zhang Sheng Thu, 09 Jul 2026 18:54:26 +0800 util-dfm (1.3.59) unstable; urgency=medium * fix: improve symlink handling in filename search * feat: add configurable keyword positioning in search results -- Zhang Sheng Wed, 17 Jun 2026 15:22:52 +0800 util-dfm (1.3.58) unstable; urgency=medium * fix: simplify file timestamp attribute handling -- Zhang Sheng Thu, 11 Jun 2026 18:36:34 +0800 util-dfm (1.3.57) unstable; urgency=medium * fix: change search worker connection to direct * feat: add filename search to content and OCR search * feat: add natural language semantic search * feat: add relative time support for Chinese search * feat: implement file size range filtering * feat: add file size constraint support in semantic search * feat: add action-based time field search support * fix: automatically handle hidden path search conditions * feat: add location-based search support for Chinese NLP * feat: add semantic query detection and multi-path search support * feat: add file size range filter to search strategies * fix: unify dfm-search library and path names * feat: add file metadata attributes to search results * fix: improve Chinese NLP search functionality * feat: add semantic search with detailed results * feat: enhance semantic search with explicit directories * feat: add max results limit for semantic search * test: add search target control tests * feat: add chinese NLP parsing for relative time and size constraints * feat: add NGram analyzer and tokenizer for Lucene++ * fix: improve content search engine validation and analyzer * refactor: optimize search filtering and query building * feat: add on-demand content highlight retrieval * refactor: improve NGramTokenizer and search factory * refactor: improve OCR text search validation and analyzer selection * perf: optimize search performance with field selector * refactor: disable unit tests in release builds * feat: optimize ngram search query building * refactor: remove NGram analyzer and tokenizer components * fix: adjust N-gram token position calculation * feat: enhance ContentRetriever with content fetching capabilities * test: add test utility libraries for content search * perf: optimize OCR text search document loading * perf: replace chinese analyzer with ngram search * test: add filename search engine test cases * docs: update license files and cleanup * refactor(mount): use memfd instead of pipe for password transfer -- Zhang Sheng Thu, 28 May 2026 14:03:12 +0800 util-dfm (1.3.56) unstable; urgency=medium * perf: cache resolved indexed directories to avoid repeated resolution -- Zhang Sheng Thu, 21 May 2026 22:11:26 +0800 util-dfm (1.3.55) unstable; urgency=medium * fix(build): detect Qt6 availability when os-version is missing * refactor: add file size and checksum fields -- Zhang Sheng Tue, 19 May 2026 20:47:42 +0800 util-dfm (1.3.54) unstable; urgency=medium * build: update debian package configuration for search component * refactor: centralize PROJECT_VERSION_MAJOR configuration in root CMakeLists * feat: add dfm-burner CLI tool for optical disc operations * feat: add SM3 checksum verification for disc burning * perf: optimize disc checksum verification process * fix: include hidden files in checksum manifest generation -- Zhang Sheng Thu, 07 May 2026 20:58:37 +0800 util-dfm (1.3.53) unstable; urgency=medium * chore: update SPDX header * refactor: optimize file sorting implementation * test: enhance file sorting tests with directories and symlinks * test: add TextSearchAPI test to test runner -- Zhang Sheng Thu, 23 Apr 2026 17:16:25 +0800 util-dfm (1.3.52) unstable; urgency=medium * fix: change content index directory path * refactor: centralize Lucene field names in constants * feat: add OCR text search support * fix: improve path matching logic for content indexing * feat: add birth time field name constant * refactor: correct field names and add modify_time * feat: implement time range filtering for search * feat: add CLI options and output formatters for search client * feat: enhance search result metadata and output * refactor: optimize file search strategy logic * chore: update search binary name and installation path * refactor: optimize search utility path checking functions * refactor: implement Pimpl pattern for TimeRangeFilter class -- Zhang Sheng Mon, 20 Apr 2026 11:23:35 +0800 util-dfm (1.3.51) unstable; urgency=medium * chore: reorder GNUInstallDirs include in CMakeLists * feat: add Qt Test based unit testing framework * test: restructure search test framework and cleanup client * chore: remove cpp-stub and stub-ext third-party dependencies * fix: filter hidden files in lucene query to prevent result loss -- Zhang Sheng Fri, 10 Apr 2026 16:08:36 +0800 util-dfm (1.3.50) unstable; urgency=medium * refactor: add Qt6 compatibility for path splitting * fix: preserve URL scheme when building file URLs -- Zhang Sheng Thu, 12 Mar 2026 20:14:22 +0800 util-dfm (1.3.49) unstable; urgency=medium * chore: update copyright years to 2026 * feat: add filename blacklist filtering for search index * Fix: [file-enumerator] use g_file_enumerator_next_file() -- Zhang Sheng Thu, 05 Mar 2026 19:12:34 +0800 util-dfm (1.3.48) unstable; urgency=medium * fix: Don't link boost_system for Boost 1.89+ * feat: add JSON output option for search client -- Zhang Sheng Thu, 05 Feb 2026 20:12:17 +0800 util-dfm (1.3.47) unstable; urgency=medium * feat: add blacklist paths configuration support * fix: refine pinyin validation rules -- Zhang Sheng Mon, 02 Feb 2026 11:05:12 +0800 util-dfm (1.3.46) unstable; urgency=medium * fix: improve pinyin validation logic * fix: remove unnecessary regex check in pinyin acronym detection -- Zhang Sheng Thu, 29 Jan 2026 17:45:22 +0800 util-dfm (1.3.45) unstable; urgency=medium * fix: replace g_unix_mount_for with g_unix_mount_at to avoid main thread blocking * fix: add timeout for mediainfo retrieval -- Zhang Sheng Thu, 22 Jan 2026 17:38:20 +0800 util-dfm (1.3.44) unstable; urgency=medium * feat: improve file rename error handling * fix: update DBus service names for Qt6 compatibility * refactor: secure password transmission by QDBusUnixFileDescriptor -- Zhang Sheng Thu, 15 Jan 2026 19:30:41 +0800 util-dfm (1.3.43) unstable; urgency=medium * refactor: update CMake configuration for Qt6/Qt5 compatibility * chore: add boost-system dependency to build system * fix: add Qt version compatibility for string splitting * chore: refactor debian packaging for qt version compatibility * chore: update pkg-config files with versioned library names * fix: update dtk dependency version variable * fix: correct pkg-config library linking syntax * refactor: replace direct method invocation with signal-based communication * fix: Add statvfs-based free size calculation in DBlockDevicePrivate::sizeFree * Refactor GFile creation and add codec validation for file paths * fix: add OperatorsFileUtils for delayed removal of copying file URLs * fix: improve symlink handling and file size calculation in DLocalHelper * fix: register meta types for search-related classes * feat(denumerator): add initEnumerator demo and improve error handling * fix: improve path handling and file size detection in dfm-io, merge develop/eagle-20250617 to master -- Zhang Sheng Thu, 25 Dec 2025 13:02:35 +0800 util-dfm (1.3.42) unstable; urgency=medium * perf: optimize search cancellation to reduce CPU consumption * feat: add index version detection and path query optimization * fix: improve search cancellation responsiveness -- Zhang Sheng Thu, 18 Dec 2025 09:52:16 +0800 util-dfm (1.3.41) unstable; urgency=medium * feat: implement cancellable search functionality * fix: fix crash issues in media info handling -- Zhang Sheng Thu, 11 Dec 2025 18:32:02 +0800 util-dfm (1.3.40) unstable; urgency=medium * length of disc filename and paths could be longer. * -- XuShitong Tue, 25 Nov 2025 16:26:55 +0800 util-dfm (1.3.39) unstable; urgency=medium * fix bugs -- Zhang Sheng Thu, 16 Oct 2025 19:13:04 +0800 util-dfm (1.3.38) unstable; urgency=medium * correct file name comparison logic -- Zhang Sheng Thu, 25 Sep 2025 16:26:12 +0800 util-dfm (1.3.37) unstable; urgency=medium * add pinyin acronym search support -- Zhang Sheng Thu, 04 Sep 2025 19:05:56 +0800 util-dfm (1.3.36) unstable; urgency=medium * fix bug -- Zhang Sheng Thu, 21 Aug 2025 15:47:19 +0800 util-dfm (1.3.35) unstable; urgency=medium * fix bug -- Zhang Sheng Fri, 08 Aug 2025 15:29:22 +0800 util-dfm (1.3.34) unstable; urgency=medium * fix double free -- Zhang Sheng Wed, 16 Jul 2025 17:11:25 +0800 util-dfm (1.3.33) unstable; urgency=medium * fix some bugs. -- Liuyangming Wed, 16 Jul 2025 10:40:31 +0800 util-dfm (1.3.32) unstable; urgency=medium * fix sort by filename -- Zhang Sheng Thu, 03 Jul 2025 15:29:50 +0800 util-dfm (1.3.31) unstable; urgency=medium * fix bugs -- Zhang Sheng Fri, 27 Jun 2025 16:15:42 +0800 util-dfm (1.3.30) unstable; urgency=medium * Enhance index availability checks and introduce readiness validation -- Zhang Sheng Mon, 23 Jun 2025 16:51:02 +0800 util-dfm (1.3.29) unstable; urgency=medium * feat: Add wildcard search functionality to search query * fix: Improve index availability checks in search utility -- Zhang Sheng Mon, 23 Jun 2025 15:11:33 +0800 util-dfm (1.3.28) unstable; urgency=medium * fix bugs -- Zhang Sheng Thu, 05 Jun 2025 20:03:05 +0800 util-dfm (1.3.27) unstable; urgency=medium * dfm-search: optimize search performance and fix dependencies -- Zhang Sheng Wed, 28 May 2025 16:37:42 +0800 util-dfm (1.3.26) unstable; urgency=medium * fix bugs -- Zhang Sheng Wed, 21 May 2025 18:13:11 +0800 util-dfm (1.3.25) unstable; urgency=medium * refactor searchWithCallback -- Zhang Sheng Fri, 16 May 2025 15:04:02 +0800 util-dfm (1.3.24) unstable; urgency=medium * fix bugs -- Zhang Sheng Thu, 15 May 2025 20:58:39 +0800 util-dfm (1.3.23) unstable; urgency=medium * Implement extended AND search behavior for filename and content fields -- Zhang Sheng Tue, 13 May 2025 15:41:21 +0800 util-dfm (1.3.22) unstable; urgency=medium * fix bugs -- Zhang Sheng Mon, 12 May 2025 16:50:57 +0800 util-dfm (1.3.21) unstable; urgency=medium * Introduce DConfig-based dynamic file handling, enhance Lucene query processing, improve keyword highlighting, and add search timeout configuration. -- Zhang Sheng Fri, 09 May 2025 17:15:59 +0800 util-dfm (1.3.20) unstable; urgency=medium * install dfm6-search-client -- Zhang Sheng Thu, 08 May 2025 12:00:31 +0800 util-dfm (1.3.19) unstable; urgency=medium * add new interface `fileNameIndexStatus` -- Zhang Sheng Tue, 29 Apr 2025 17:55:09 +0800 util-dfm (1.3.18) unstable; urgency=medium * FileNameRealTimeStrategy * fix bugs -- Zhang Sheng Sun, 27 Apr 2025 17:24:58 +0800 util-dfm (1.3.17) unstable; urgency=medium * fix bug -- Zhang Sheng Fri, 25 Apr 2025 12:49:54 +0800 util-dfm (1.3.16) unstable; urgency=medium * feat: Optimize dfm-search -- Zhang Sheng Tue, 22 Apr 2025 11:51:27 +0800 util-dfm (1.3.15) unstable; urgency=medium * feat: Impl dfm-search -- Zhang Sheng Thu, 10 Apr 2025 16:19:41 +0800 util-dfm (1.3.14) unstable; urgency=medium * fix: Update DBlockMonitorPrivate destructor to use deleteLater for watcher -- Liu Zhangjian Wed, 02 Apr 2025 17:15:50 +0800 util-dfm (1.3.13) unstable; urgency=medium * fix bug -- Liu Zhangjian Wed, 02 Apr 2025 16:04:11 +0800 util-dfm (1.3.12) unstable; urgency=medium * fix bug -- Zhang Sheng Thu, 20 Mar 2025 21:06:37 +0800 util-dfm (1.3.11) unstable; urgency=medium * fix bugs -- Zhang Sheng Tue, 11 Mar 2025 13:33:33 +0800 util-dfm (1.3.10) unstable; urgency=medium * Remove all Qt5 -- Zhang Sheng Mon, 03 Mar 2025 19:21:35 +0800 util-dfm (1.3.9) unstable; urgency=medium * fix cifs password save issue * -- XuShitong Thu, 26 Dec 2024 15:42:14 +0800 util-dfm (1.3.8) unstable; urgency=medium * fix bugs -- zhangsheng Fri, 13 Dec 2024 17:27:19 +0800 util-dfm (1.3.7) unstable; urgency=medium * Update pattern from "^/media/(.*)/smbmounts" to "^/(?:run/)?media/(.*)/smbmounts" -- zhangsheng Tue, 10 Dec 2024 20:28:48 +0800 util-dfm (1.3.6) unstable; urgency=medium * Support Block::Configuration field. * -- XuShitong Thu, 05 Dec 2024 20:17:19 +0800 util-dfm (1.3.5) unstable; urgency=medium * update version -- Liu zheng Mon, 02 Dec 2024 17:42:32 +0800 util-dfm (1.3.4) unstable; urgency=medium * Qt6 supported * -- zhangsheng Fri, 20 Sep 2024 18:04:31 +0800 util-dfm (1.3.3) unstable; urgency=medium * update version to 1.3.3. * -- LiYiGang Tues, 3 Sep 2024 14:48:06 +0800 util-dfm (1.3.2) unstable; urgency=medium * update version to 1.3.2. * -- XuShitong Thu, 25 Jul 2024 15:56:06 +0800 util-dfm (1.3.1) unstable; urgency=medium * update version to 1.3.1 -- liujinchang Wed, 10 Jul 2024 11:34:25 +0800 util-dfm (1.3.0) unstable; urgency=medium * v25 -- liujinchang Tue, 02 Jul 2024 15:06:32 +0800 util-dfm (1.2.24) unstable; urgency=medium * a new attribute is added. * -- xushitong Tue, 07 May 2024 16:16:26 +0800 util-dfm (1.2.23) unstable; urgency=medium * update version to 1.2.23 -- zhangsheng Wed, 27 Mar 2024 16:44:16 +0800 util-dfm (1.2.22) unstable; urgency=medium * update version to 1.2.22 -- zhangsheng Mon, 19 Feb 2024 14:11:20 +0800 util-dfm (1.2.21) unstable; urgency=medium * update version to 1.2.21 -- lvwujun Mon, 15 Jan 2024 14:32:16 +0800 util-dfm (1.2.20) unstable; urgency=medium * update version to 1.2.20 -- zhangsheng Thu, 28 Dec 2023 11:27:33 +0800 util-dfm (1.2.19) unstable; urgency=medium * update version to 1.2.19 -- zhangsheng Wed, 20 Dec 2023 14:48:05 +0800 util-dfm (1.2.18) unstable; urgency=medium * update version to 1.2.18 -- lvwujun Thur, 2 Nov 2023 11:19:21 +0800 util-dfm (1.2.17) unstable; urgency=medium * update version to 1.2.17 -- lvwujun Fri, 20 Oct 2023 15:21:22 +0800 util-dfm (1.2.16) unstable; urgency=medium * update version to 1.2.16 -- lvwujun Fri, 1 Sep 2023 10:21:12 +0800 util-dfm (1.2.15) unstable; urgency=medium * update version to 1.2.15 -- zhangsheng Thu, 24 Aug 2023 10:25:59 +0800 util-dfm (1.2.14) unstable; urgency=medium * update version to 1.2.14 -- lvwujun Fri, 4 Aug 2023 14:48:12 +0800 util-dfm (1.2.13) unstable; urgency=medium * update version to 1.2.13 -- lvwujun Tue, 1 Aug 2023 14:48:12 +0800 util-dfm (1.2.12) unstable; urgency=medium * update version to 1.2.12 -- lvwujun Wed, 28 June 2023 10:47:21 +0800 util-dfm (1.2.11) unstable; urgency=medium * update version to 1.2.11 -- lvwujun Wed, 07 June 2023 15:57:07 +0800 util-dfm (1.2.10) unstable; urgency=medium * update version. * --lvwujun Thur, 31 May 2023 15:09:45 +0800 util-dfm (1.2.9) unstable; urgency=medium * update version. * --lvwujun Fri, 28 Apr 2023 11:09:45 +0800 util-dfm (1.2.8) unstable; urgency=medium * update version. -- lvwujun Thur, 13 April 2023 15:16:29 +0800 util-dfm (1.2.6) unstable; urgency=medium * update version. -- zhangsheng Fri, 10 Mar 2023 16:29:38 +0800 util-dfm (1.2.5) unstable; urgency=medium * update version. * -- xust Mon, 16 Jan 2023 16:58:38 +0800 util-dfm (1.2.3) stable; urgency=high * update V6 6.0.7 baseline version -- lvwujun Wed, 04 Jan 2023 17:26:29 +0800 util-dfm (1.2.1.2) unstable; urgency=high * update V6 6.0.6.2 baseline version -- lvwujun Tue, 06 Dec 2022 13:51:08 +0800 util-dfm (1.2.1) unstable; urgency=high * update V6 6.0.6 baseline version -- lvwujun Thu, 24 Nov 2022 11:24:08 +0800 util-dfm (1.2.0) unstable; urgency=high * update V6 6.0.4 baseline version -- LanXuesong Tue, 01 Nov 2022 11:25:00 +0800 util-dfm (1.1.2) unstable; urgency=high * update V6 6.0.4 baseline version -- lvwujun Wed, 20 Oct 2022 17:39:08 +0800 util-dfm (1.1.1) unstable; urgency=high * update V6 6.0.2 baseline version -- lvwujun Wed, 14 Sep 2022 14:50:58 +0800 util-dfm (1.1.0) unstable; urgency=low * Initial release for v23 -- ZhangSheng Wed, 22 Jun 2022 09:44:40 +0000 util-dfm (1.0.0) unstable; urgency=medium * Initial release -- lanxs Mon, 16 Aug 2021 13:22:42 +0800 linuxdeepin-util-dfm-a4307d3/debian/compat000066400000000000000000000000021523477242100205330ustar00rootroot000000000000009 linuxdeepin-util-dfm-a4307d3/debian/control000066400000000000000000000057071523477242100207510ustar00rootroot00000000000000# WARNING: This file is auto-generated from debian/control.in by debian/rules # DO NOT EDIT THIS FILE DIRECTLY! # Edit debian/control.in instead and rebuild the package. # # The generation logic: # - If MajorVersion > 20 in /etc/os-version: generates Qt6 packages (libdfm6-*) # - If MajorVersion <= 20 in /etc/os-version: generates Qt5 packages (libdfm-*) # - If /etc/os-version doesn't exist: defaults to Qt6 packages Source: util-dfm Section: libs,utils Priority: optional Maintainer: Deepin Packages Builder Build-Depends: debhelper (>=9), cmake, pkg-config, qt6-base-dev | qtbase5-dev, qt6-base-private-dev | qtbase5-private-dev, libdtk6core-dev | libdtkcore-dev, libglibmm-2.4-dev, qt6-tools-dev-tools | qttools5-dev-tools, libglib2.0-dev, libudisks2-dev, libisoburn-dev, libssl-dev, libmediainfo-dev, libsecret-1-dev, liblucene++-dev, libboost-filesystem-dev, libboost-system-dev Standards-Version: 4.3.0 Homepage: http://www.deepin.org #Vcs-Browser: https://salsa.debian.org/debian/util-dfm #Vcs-Git: https://salsa.debian.org/debian/util-dfm.git Package: libdfm6-io Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: A library about I/O. A library that provides I/O interface. Package: libdfm6-io-dev Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, libdfm6-io (=${binary:Version}) Description: A library about I/O dev. A library that provides I/O interface dev. Package: libdfm6-mount Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: A library about mount and umount. A library that provides mount and umount interface. Package: libdfm6-mount-dev Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, libdfm6-mount (=${binary:Version}), Description: A library about mount and umount dev. A library that provides mount and umount interface dev. Package: libdfm6-burn Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Recommends: libudfburn Description: A library about burning CD. A library that provides burning CD interface. Package: libdfm6-burn-dev Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, libdfm6-burn (=${binary:Version}), Description: A library about mount burning CD. A library that provides burning CD interface dev. Package: libdfm6-search Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: A library about searching files. A library that provides file searching interface. Package: libdfm6-search-dev Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, libdfm6-search (=${binary:Version}) Description: A library about searching files dev. A library that provides file searching interface dev. linuxdeepin-util-dfm-a4307d3/debian/control.in000066400000000000000000000053601523477242100213510ustar00rootroot00000000000000Source: util-dfm Section: libs,utils Priority: optional Maintainer: Deepin Packages Builder Build-Depends: debhelper (>=9), cmake, pkg-config, qt6-base-dev | qtbase5-dev, qt6-base-private-dev | qtbase5-private-dev, libdtk6core-dev | libdtkcore-dev, libglibmm-2.4-dev, qt6-tools-dev-tools | qttools5-dev-tools, libglib2.0-dev, libudisks2-dev, libisoburn-dev, libssl-dev, libmediainfo-dev, libsecret-1-dev, liblucene++-dev, libboost-filesystem-dev, libboost-system-dev Standards-Version: 4.3.0 Homepage: http://www.deepin.org #Vcs-Browser: https://salsa.debian.org/debian/util-dfm #Vcs-Git: https://salsa.debian.org/debian/util-dfm.git Package: libdfm@QT_SUFFIX@-io Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, libglibmm-2.4-1v5 Description: A library about I/O. A library that provides I/O interface. Package: libdfm@QT_SUFFIX@-io-dev Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, libdfm@QT_SUFFIX@-io (=${binary:Version}) Description: A library about I/O dev. A library that provides I/O interface dev. Package: libdfm@QT_SUFFIX@-mount Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: A library about mount and umount. A library that provides mount and umount interface. Package: libdfm@QT_SUFFIX@-mount-dev Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, libdfm@QT_SUFFIX@-mount (=${binary:Version}), libsecret-1-dev, libudisks2-dev Description: A library about mount and umount dev. A library that provides mount and umount interface dev. Package: libdfm@QT_SUFFIX@-burn Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, dvd+rw-tools Recommends: libudfburn Description: A library about burning CD. A library that provides burning CD interface. Package: libdfm@QT_SUFFIX@-burn-dev Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, libdfm@QT_SUFFIX@-burn (=${binary:Version}), libisoburn-dev Description: A library about mount burning CD. A library that provides burning CD interface dev. Package: libdfm@QT_SUFFIX@-search Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: A library about searching files. A library that provides file searching interface. Package: libdfm@QT_SUFFIX@-search-dev Architecture: any Section: libdevel Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, libdfm@QT_SUFFIX@-search (=${binary:Version}) Description: A library about searching files dev. A library that provides file searching interface dev. linuxdeepin-util-dfm-a4307d3/debian/copyright000066400000000000000000000020651523477242100212730ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: util-dfm Files: * Copyright (C) 2020 ~ 2021 Uniontech Software Technology Co., Ltd. License: GPL-3.0+ Files: debian/* Copyright (C) 2020 ~ 2021 Uniontech Software Technology Co., Ltd. License: GPL-3.0+ License: GPL-3.0+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . On Debian systems, the complete text of the GNU General Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". linuxdeepin-util-dfm-a4307d3/debian/libdfm-burn-dev.install000066400000000000000000000001301523477242100236740ustar00rootroot00000000000000usr/include/dfm-burn/* usr/lib/*/pkgconfig/dfm-burn.pc usr/lib/*/cmake/dfm-burn/*.cmake linuxdeepin-util-dfm-a4307d3/debian/libdfm-burn.install000066400000000000000000000000571523477242100231300ustar00rootroot00000000000000/usr/lib/*/libdfm-burn*.so* usr/bin/dfm-burner linuxdeepin-util-dfm-a4307d3/debian/libdfm-io-dev.install000066400000000000000000000001221523477242100233360ustar00rootroot00000000000000usr/include/dfm-io/* usr/lib/*/pkgconfig/dfm-io.pc usr/lib/*/cmake/dfm-io/*.cmake linuxdeepin-util-dfm-a4307d3/debian/libdfm-io.install000066400000000000000000000000321523477242100225620ustar00rootroot00000000000000/usr/lib/*/libdfm-io*.so* linuxdeepin-util-dfm-a4307d3/debian/libdfm-mount-dev.install000066400000000000000000000001331523477242100240730ustar00rootroot00000000000000usr/include/dfm-mount/* usr/lib/*/pkgconfig/dfm-mount.pc usr/lib/*/cmake/dfm-mount/*.cmake linuxdeepin-util-dfm-a4307d3/debian/libdfm-mount.install000066400000000000000000000000351523477242100233200ustar00rootroot00000000000000/usr/lib/*/libdfm-mount*.so* linuxdeepin-util-dfm-a4307d3/debian/libdfm-search-dev.install000066400000000000000000000001361523477242100242010ustar00rootroot00000000000000usr/include/dfm-search/* usr/lib/*/pkgconfig/dfm-search.pc usr/lib/*/cmake/dfm-search/*.cmake linuxdeepin-util-dfm-a4307d3/debian/libdfm-search.install000066400000000000000000000001361523477242100234250ustar00rootroot00000000000000usr/lib/*/libdfm-search*.so* usr/bin/dfm-searcher usr/share/deepin/dfm-search/semantic/rules/*linuxdeepin-util-dfm-a4307d3/debian/libdfm6-burn-dev.install000066400000000000000000000001331523477242100237650ustar00rootroot00000000000000usr/include/dfm6-burn/* usr/lib/*/pkgconfig/dfm6-burn.pc usr/lib/*/cmake/dfm6-burn/*.cmake linuxdeepin-util-dfm-a4307d3/debian/libdfm6-burn.install000066400000000000000000000000601523477242100232100ustar00rootroot00000000000000/usr/lib/*/libdfm6-burn*.so* usr/bin/dfm-burner linuxdeepin-util-dfm-a4307d3/debian/libdfm6-io-dev.install000066400000000000000000000001251523477242100234270ustar00rootroot00000000000000usr/include/dfm6-io/* usr/lib/*/pkgconfig/dfm6-io.pc usr/lib/*/cmake/dfm6-io/*.cmake linuxdeepin-util-dfm-a4307d3/debian/libdfm6-io.install000066400000000000000000000000331523477242100226510ustar00rootroot00000000000000/usr/lib/*/libdfm6-io*.so* linuxdeepin-util-dfm-a4307d3/debian/libdfm6-mount-dev.install000066400000000000000000000001361523477242100241640ustar00rootroot00000000000000usr/include/dfm6-mount/* usr/lib/*/pkgconfig/dfm6-mount.pc usr/lib/*/cmake/dfm6-mount/*.cmake linuxdeepin-util-dfm-a4307d3/debian/libdfm6-mount.install000066400000000000000000000000361523477242100234070ustar00rootroot00000000000000/usr/lib/*/libdfm6-mount*.so* linuxdeepin-util-dfm-a4307d3/debian/libdfm6-search-dev.install000066400000000000000000000001411523477242100242630ustar00rootroot00000000000000usr/include/dfm6-search/* usr/lib/*/pkgconfig/dfm6-search.pc usr/lib/*/cmake/dfm6-search/*.cmake linuxdeepin-util-dfm-a4307d3/debian/libdfm6-search.install000066400000000000000000000001371523477242100235140ustar00rootroot00000000000000usr/lib/*/libdfm6-search*.so* usr/bin/dfm-searcher usr/share/deepin/dfm-search/semantic/rules/*linuxdeepin-util-dfm-a4307d3/debian/rules000077500000000000000000000026441523477242100204230ustar00rootroot00000000000000#!/usr/bin/make -f include /usr/share/dpkg/default.mk DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH) # 版本检测逻辑 - 读取 /etc/os-version 中的 MajorVersion OS_VERSION_FILE := /etc/os-version # 提取 MajorVersion,如果文件不存在或解析失败则默认为 99 (使用 Qt6) MAJOR_VERSION := $(shell if [ -f $(OS_VERSION_FILE) ]; then \ grep "^MajorVersion=" $(OS_VERSION_FILE) 2>/dev/null | cut -d'=' -f2; \ else \ echo 99; \ fi) # 版本判断: MajorVersion > 20 使用 Qt6, 否则使用 Qt5 USE_QT6 := $(shell if [ "$(MAJOR_VERSION)" -gt 20 ] 2>/dev/null; then echo yes; else echo no; fi) # MajorVersion 为 99 时(/etc/os-version 不存在),检查 Qt6 是否实际安装 ifeq ($(MAJOR_VERSION),99) HAS_QT6 := $(shell pkg-config --exists Qt6Core 2>/dev/null && echo yes || echo no) ifeq ($(HAS_QT6),yes) USE_QT6 := yes else USE_QT6 := no endif endif # 设置 QT_SELECT 环境变量 ifeq ($(USE_QT6),yes) export QT_SELECT=6 QT_SUFFIX := 6 else export QT_SELECT=5 QT_SUFFIX := endif # 从 control.in 生成 control 文件 $(shell sed -e 's/@QT_SUFFIX@/$(QT_SUFFIX)/g' \ debian/control.in > debian/control) %: dh $@ --parallel override_dh_auto_configure: dh_auto_configure -- \ -DHOST_MULTIARCH="$(DEB_HOST_MULTIARCH)" \ -DVERSION="$(DEB_VERSION_UPSTREAM)" linuxdeepin-util-dfm-a4307d3/debian/source/000077500000000000000000000000001523477242100206355ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/debian/source/format000066400000000000000000000000151523477242100220440ustar00rootroot000000000000003.0 (native) linuxdeepin-util-dfm-a4307d3/debian/source/lintian-overrides000066400000000000000000000000001523477242100242040ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/docs/000077500000000000000000000000001523477242100170435ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/docs/.gitkeep000066400000000000000000000000001523477242100204620ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/docs/dfm-searcher-guide.md000066400000000000000000000534431523477242100230310ustar00rootroot00000000000000# dfm-searcher 使用指南 > 从零开始,学会用 `dfm-searcher` 搜索文件和预览文件内容。 --- ## 它是什么 `dfm-searcher` 是深度文件管理器的搜索命令行工具,基于 Lucene++ 索引引擎,支持: - **按文件名搜索** — 用关键词、通配符、拼音找文件 - **按文件内容搜索** — 在文档内容中搜索关键词 - **按 OCR 文字搜索** — 在图片中识别的文字里搜索 - **语义搜索** — 用自然语言描述需求,自动解析意图 - **内容预览** — 按需读取已索引文件的内容片段 --- ## 两种模式 `dfm-searcher` 有两种工作模式: | 模式 | 命令格式 | 用途 | |------|---------|------| | **搜索模式** | `dfm-searcher [选项] ` | 搜索目录下匹配的文件 | | **预览模式** | `dfm-searcher preview [选项] [] [path...]` | 直接读取指定文件的内容片段 | --- # 第一部分:搜索 ## 一、文件名搜索 ### 1. 最基本的搜索 ```bash dfm-searcher "报告" /home/user ``` 在 `/home/user` 目录下搜索文件名包含"报告"的文件。 输出示例: ``` /home/user/工作报告.txt /home/user/2026年报告.docx /home/user/reports/年度报告.pdf ``` ### 2. 限制结果数量 ```bash dfm-searcher --max-results=10 "报告" /home/user ``` 最多返回 10 条结果。`--max-results=0` 表示不限制(默认)。 ### 3. 区分大小写 ```bash dfm-searcher --case-sensitive "README" /home/user ``` 默认搜索不区分大小写。加上 `--case-sensitive` 后 "readme" 不会匹配 "README"。 ### 4. 包含隐藏文件 ```bash dfm-searcher --include-hidden "config" /home/user ``` 默认不搜索隐藏文件(以 `.` 开头的文件或目录)。加上此选项后包含隐藏文件。 ### 5. 实时搜索 vs 索引搜索 ```bash # 索引搜索(默认):快,但依赖预建索引 dfm-searcher "报告" /home/user # 实时搜索:慢,但不需要索引,直接遍历文件系统 dfm-searcher --method=realtime "报告" /home/user ``` | 方法 | 速度 | 需要索引 | 适用场景 | |------|------|:---:|------| | `--method=indexed`(默认) | 快 | 是 | 日常使用 | | `--method=realtime` | 慢 | 否 | 索引未覆盖的路径、最新文件 | --- ## 二、内容搜索 ### 6. 搜索文件内容 ```bash dfm-searcher --type=content "会议纪要" /home/user/Documents ``` 在 `/home/user/Documents` 下搜索文件**内容**包含"会议纪要"的文件。 ### 7. 搜索内容并显示预览 ```bash dfm-searcher --type=content -v "会议纪要" /home/user/Documents ``` 加 `-v`(verbose)后,每条结果会附带内容预览片段(高亮显示关键词)。 ### 8. 控制预览长度 ```bash dfm-searcher --type=content --max-preview=500 -v "会议" /home/user/Documents ``` 预览片段最多 500 个字符(默认 200)。 --- ## 三、OCR 搜索 ### 9. 搜索图片中的文字 ```bash dfm-searcher --type=ocr "登录界面" /home/user/Pictures ``` 在图片中搜索 OCR 识别出的文字。支持 PNG、JPG 等格式。 ### 10. OCR 搜索 + 预览 ```bash dfm-searcher --type=ocr -v "验证码" /home/user/Pictures ``` 输出会包含匹配的 OCR 文字片段。 --- ## 四、通配符搜索 ### 11. 用 * 和 ? 搜索 ```bash # 匹配所有以 "report" 开头的文件名 dfm-searcher --wildcard "report*" /home/user # 匹配 "report" + 任意一个字符 + ".txt" dfm-searcher --wildcard "report?.txt" /home/user # 匹配所有 .pdf 文件名 dfm-searcher --wildcard "*.pdf" /home/user ``` | 通配符 | 含义 | 示例 | |--------|------|------| | `*` | 任意多个字符 | `report*` 匹配 report2026.txt、reports.pdf | | `?` | 任意一个字符 | `report?` 匹配 reports、reportA,不匹配 reportAB | --- ## 五、布尔搜索(多关键词) ### 12. AND 搜索(所有关键词都要匹配) ```bash # 用逗号分隔:同时包含 "报告" 和 "2026" dfm-searcher --query=boolean "报告,2026" /home/user # 用 & 分隔(同上) dfm-searcher --query=boolean "报告&2026" /home/user ``` ### 13. OR 搜索(任一关键词匹配即可) ```bash # 用 | 分隔:包含 "报告" 或 "总结" dfm-searcher --query=boolean "报告|总结" /home/user ``` | 分隔符 | 逻辑 | 示例 | |--------|------|------| | `\|` | OR(任一匹配) | `"报告\|总结"` → 匹配报告或总结 | | `&` | AND(全部匹配) | `"报告&2026"` → 同时包含报告和2026 | | `,` | AND(全部匹配) | `"报告,2026"` → 同上,向后兼容 | --- ## 六、拼音搜索 ### 14. 全拼搜索 ```bash dfm-searcher --pinyin "baogao" /home/user ``` 搜索文件名中拼音匹配 "baogao" 的文件,如"报告.txt"。 ### 15. 拼音首字母搜索 ```bash dfm-searcher --pinyin-acronym "bg" /home/user ``` "bg" 匹配 "baogao"(报告)、"binggan"(饼干)等拼音首字母为 b+g 的文件名。 ### 16. 拼音 + 布尔组合 ```bash dfm-searcher --pinyin --query=boolean "wendang,xinjian" /home/user ``` 搜索文件名拼音同时匹配 "wendang"(文档)和 "xinjian"(新建)的文件。 --- ## 七、文件类型过滤 ### 17. 按类型过滤 ```bash dfm-searcher --file-types=doc,pic "报告" /home/user ``` 只返回文档类型和图片类型的搜索结果。 支持的类型: | 类型 | 说明 | |------|------| | `app` | 应用程序文件 | | `archive` | 压缩文件 | | `audio` | 音频文件 | | `doc` | 文档文件 | | `pic` | 图片文件 | | `video` | 视频文件 | | `dir` | 目录 | | `other` | 其他 | ### 18. 按扩展名过滤 ```bash dfm-searcher --file-extensions=txt,pdf "报告" /home/user ``` 只返回 .txt 和 .pdf 文件。 ### 19. 类型 + 扩展名组合 ```bash dfm-searcher --file-types=doc --file-extensions=docx,odt "report" /home/user ``` --- ## 八、排除目录 ### 20. 排除指定目录 ```bash # 排除 /tmp 和 /var 目录 dfm-searcher --exclude=/tmp,/var "报告" /home/user ``` 搜索时跳过 `/tmp` 和 `/var` 目录及其子目录,不搜索其中的文件。用逗号分隔多个排除路径。 ### 21. 排除缓存等无关目录 ```bash # 排除缓存和回收站目录,减少无关结果 dfm-searcher --exclude=/home/user/.cache,/home/user/.local/share/Trash "报告" /home/user ``` 排除缓存、临时文件等目录可以加快搜索速度、减少无关结果。该选项在搜索模式和语义模式下均可使用。 --- ## 九、时间范围过滤 ### 22. 最近 N 天 ```bash # 最近 3 天内修改的文件 dfm-searcher --time-last=3d "报告" /home/user # 最近 2 小时 dfm-searcher --time-last=2h "报告" /home/user ``` 支持的时间单位: | 单位 | 含义 | 示例 | |------|------|------| | `m` | 分钟 | `--time-last=30m`(最近 30 分钟) | | `h` | 小时 | `--time-last=2h`(最近 2 小时) | | `d` | 天 | `--time-last=7d`(最近 7 天) | | `w` | 周 | `--time-last=2w`(最近 2 周) | | `M` | 月 | `--time-last=3M`(最近 3 个月) | | `y` | 年 | `--time-last=1y`(最近 1 年) | ### 23. 预设时间范围 ```bash # 今天的文件 dfm-searcher --time-today "报告" /home/user # 昨天的文件 dfm-searcher --time-yesterday "报告" /home/user # 本周的文件 dfm-searcher --time-this-week "报告" /home/user # 上个月的文件 dfm-searcher --time-last-month "报告" /home/user # 今年的文件 dfm-searcher --time-this-year "报告" /home/user ``` 所有预设选项: `--time-today` / `--time-yesterday` / `--time-this-week` / `--time-last-week` / `--time-this-month` / `--time-last-month` / `--time-this-year` / `--time-last-year` ### 24. 自定义时间范围 ```bash # 按日期范围 dfm-searcher --time-range="2025-01-01,2025-12-31" "报告" /home/user # 按精确时间范围 dfm-searcher --time-range="2025-06-01 09:00,2025-06-30 18:00" "报告" /home/user ``` ### 25. 按创建时间 vs 修改时间 ```bash # 默认按修改时间(modify) dfm-searcher --time-last=7d "报告" /home/user # 按创建时间(birth) dfm-searcher --time-field=birth --time-last=7d "报告" /home/user ``` ### 26. 时间 + 实时搜索组合 ```bash dfm-searcher --method=realtime --time-last=7d "报告" /home/user ``` --- ## 十、文件大小过滤 ### 27. 按大小范围过滤 ```bash # 1MB 到 100MB 之间的文件 dfm-searcher --size-min=1M --size-max=100M "视频" /home/user ``` 支持的大小单位: | 单位 | 含义 | 示例 | |------|------|------| | (无) | 字节 | `--size-min=512`(至少 512 字节) | | `K` | KB | `--size-min=1K`(至少 1KB) | | `M` | MB | `--size-min=10M`(至少 10MB) | | `G` | GB | `--size-max=1G`(最多 1GB) | | `T` | TB | `--size-max=1T`(最多 1TB) | ### 28. 只限最小或最大 ```bash # 至少 10MB dfm-searcher --size-min=10M "视频" /home/user # 最多 1KB dfm-searcher --size-max=1K "日志" /home/user ``` --- ## 十一、语义搜索 ### 29. 用自然语言搜索 ```bash dfm-searcher -s "最近3天的图片" /home/user ``` 语义搜索会自动解析你的自然语言描述,提取时间、类型、关键词等维度,转化为搜索条件。 ### 30. 语义搜索 + JSON ```bash dfm-searcher -s -j "最近3天的图片" /home/user ``` JSON 输出会包含解析出的意图(intent)信息。 ### 31. 语义搜索不要求路径 ```bash dfm-searcher -s "包含会议纪要的文档" ``` 语义模式可以不指定搜索路径。 ### 32. 语义搜索示例集 ```bash # 查找最近图片 dfm-searcher -s "最近的图片" /home/user # 查找上周的文档 dfm-searcher -s "上周的文档" /home/user/Documents # 查找大视频 dfm-searcher -s "大于1G的视频" /home/user # 查找特定内容 dfm-searcher -s "内容包含会议纪要的文件" /home/user # 语义 + 详细输出 dfm-searcher -s -v "最近3天的图片" /home/user ``` --- ## 十二、输出格式 ### 33. JSON 输出 ```bash dfm-searcher -j "报告" /home/user ``` 输出结构化 JSON,方便脚本解析: ```json { "type": "search", "searchType": "filename", "keyword": "报告", "totalResults": 2, "results": [ { "path": "/home/user/工作报告.txt", "filename": "工作报告.txt" }, { "path": "/home/user/2026年报告.docx", "filename": "2026年报告.docx" } ] } ``` ### 34. 详细输出(verbose) ```bash dfm-searcher -v --type=content "会议" /home/user/Documents ``` `-v` 启用详细模式,结果中会包含文件大小、修改时间、高亮内容预览等额外信息。 ### 35. JSON + verbose 组合 ```bash dfm-searcher -j -v --type=content "会议" /home/user/Documents ``` --- ## 十三、组合搜索 ### 36. 文件名 + 文件类型 + 时间 ```bash dfm-searcher --file-types=doc --time-last=7d "报告" /home/user ``` 最近 7 天内的文档类型文件,文件名包含"报告"。 ### 37. 内容搜索 + 文件名过滤 + 大小过滤 ```bash dfm-searcher --type=content --filename="季度" --size-min=1M "财务" /home/user ``` 在内容包含"财务"且文件名包含"季度"、大小至少 1MB 的文件中搜索。 ### 38. 实时搜索 + 时间 + 大小 + 类型 ```bash dfm-searcher --method=realtime --time-last=30d --size-min=10M --file-types=video "监控" /home/user ``` --- # 第二部分:内容预览(preview 子命令) `preview` 子命令用来**直接读取**指定文件的内容片段,不需要运行完整搜索。 ## 十四、基本用法 ### 39. 读取文件全部内容 ```bash dfm-searcher preview /home/user/notes.txt ``` 无 keyword 且未设 `--max-preview` 时,输出文档**全部内容**。 ``` /home/user/notes.txt 2026年7月10日 工作计划:1.完成需求文档 2.代码评审 3.修复bug 今天重点完成代码评审部分,明天开始写自动化测试… ``` ### 40. 读取文件内容(JSON 格式) ```bash dfm-searcher preview /home/user/notes.txt -j ``` ```json { "type": "preview", "searchType": "semantic", "keyword": "", "offset": 0, "maxLength": 0, "totalResults": 1, "results": [ { "path": "/home/user/notes.txt", "content": "2026年7月10日 工作计划:1.完成需求文档 2.代码评审 3.修复bug\n今天重点完成代码评审部分,明天开始写自动化测试…", "keywordOffset": -1 } ] } ``` > `maxLength: 0` 表示无限制,返回全文。 --- ## 十五、用 --offset 和 --max-preview 控制读取范围 无 keyword 时,默认输出全文。加 `--offset` 可以从指定位置开始读,加 `--max-preview` 可以限制读取长度。 ### 41. 从第 100 个字符开始读(不限长度) ```bash dfm-searcher preview /home/user/notes.txt --offset=100 ``` 从第 100 个字符开始,读到文件末尾。 ### 42. 只读 50 个字符 ```bash dfm-searcher preview /home/user/notes.txt --max-preview=50 ``` ### 43. 从第 100 个字符开始,只读 50 个字符 ```bash dfm-searcher preview /home/user/notes.txt --offset=100 --max-preview=50 ``` ### 44. 翻页式读取 第一次读取: ```bash dfm-searcher preview /home/user/notes.txt --max-preview=100 -j ``` 第二次,offset=100: ```bash dfm-searcher preview /home/user/notes.txt --offset=100 --max-preview=100 -j ``` 第三次,offset=200: ```bash dfm-searcher preview /home/user/notes.txt --offset=200 --max-preview=100 -j ``` 以此类推,每次读 100 个字符。 ### 45. offset 超过文件长度 → 返回空 ```bash dfm-searcher preview /home/user/notes.txt --offset=99999 -j ``` ```json { "results": [ { "content": "", "keywordOffset": -1 } ] } ``` 空内容说明已读到文件末尾。 --- ## 十六、用 keyword 搜索并预览 ### 46. 搜索关键词并预览 ```bash dfm-searcher preview "bug" /home/user/notes.txt ``` ``` /home/user/notes.txt bug修复:修复了文件管理器在删除大量文件时的崩溃问题 ``` 内容从关键词所在位置开始,往后最多 200 个字符。 ### 47. 搜索关键词 + JSON ```bash dfm-searcher preview "bug" /home/user/notes.txt -j ``` ```json { "type": "preview", "keyword": "bug", "offset": 0, "maxLength": 200, "results": [ { "path": "/home/user/notes.txt", "content": "bug修复:修复了文件管理器在删除大量文件时的崩溃问题", "keywordOffset": 156 } ] } ``` `keywordOffset: 156` 表示 "bug" 在全文第 156 个字符处。 ### 48. 从指定位置开始搜索关键词 如果 "bug" 在文件里出现多次,用 `--offset` 跳过前面的出现: ```bash # 从第 200 个字符开始搜索 "bug" dfm-searcher preview "bug" /home/user/notes.txt --offset=200 ``` ### 49. offset 后面没有关键词 → 返回空 ```bash dfm-searcher preview "bug" /home/user/notes.txt --offset=500 -j ``` ```json { "results": [ { "content": "", "keywordOffset": -1 } ] } ``` ### 50. 搭配搜索结果使用 搜索返回 `keywordOffset`,可以用它作为 preview 的 `--offset`: ```bash # 假设搜索返回 keywordOffset=320 dfm-searcher preview "关键词" /home/user/doc.txt --offset=320 --max-preview=200 -j ``` --- ## 十七、批量预览多个文件 ### 51. 同时预览多个文件 ```bash dfm-searcher preview "报告" doc1.txt doc2.txt doc3.txt ``` ``` /home/user/doc1.txt 报告:2026年Q1财务总结… /home/user/doc2.txt 报告:2026年Q2项目进展… /home/user/doc3.txt (no content) ``` ### 52. 批量预览 + JSON ```bash dfm-searcher preview "报告" doc1.txt doc2.txt doc3.txt -j ``` ```json { "type": "preview", "keyword": "报告", "offset": 0, "maxLength": 200, "totalResults": 3, "results": [ { "path": "/home/user/doc1.txt", "content": "报告:2026年Q1财务总结…", "keywordOffset": 0 }, { "path": "/home/user/doc2.txt", "content": "报告:2026年Q2项目进展…", "keywordOffset": 5 }, { "path": "/home/user/doc3.txt", "content": "", "keywordOffset": -1 } ] } ``` --- ## 十八、搜索类型 ### 53. 自动检测(默认) 不给 `--type`,程序根据文件扩展名自动判断: - `.txt` `.doc` `.md` 等 → 用内容索引(content) - `.png` `.jpg` 等 → 用 OCR 索引(ocr) ### 54. 强制指定搜索类型 ```bash # 强制用 OCR 索引 dfm-searcher preview --type=ocr "截图" photo.png # 强制用内容索引 dfm-searcher preview --type=content "会议" doc.txt ``` ### 55. 预览 OCR 图片中的文字 ```bash dfm-searcher preview "登录" screenshot.png -j ``` ```json { "type": "preview", "searchType": "ocr", "keyword": "登录", "results": [ { "path": "screenshot.png", "content": "登录界面请输入用户名和密码", "keywordOffset": 0 } ] } ``` --- ## 十九、无关键词,纯读取内容 ### 56. 读取文件前 200 个字符 ```bash dfm-searcher preview /home/user/doc.txt ``` ### 57. 从第 500 个字符读取 100 个字符 ```bash dfm-searcher preview /home/user/doc.txt --offset=500 --max-preview=100 ``` ### 58. 无关键词 + JSON ```bash dfm-searcher preview /home/user/doc.txt --offset=500 --max-preview=100 -j ``` ```json { "keyword": "", "offset": 500, "maxLength": 100, "results": [ { "content": "这里是第500到600个字符之间的原始内容", "keywordOffset": -1 } ] } ``` `keywordOffset: -1` 因为没有给关键词。 --- # 第三部分:参考 ## 选项速查表 ### 搜索模式选项 | 选项 | 默认值 | 说明 | |------|--------|------| | `--type=` | `filename` | 搜索类型 | | `--method=` | `indexed` | 搜索方法 | | `--query=` | `simple` | 查询类型 | | `--wildcard` | 关 | 启用通配符搜索 | | `--case-sensitive` | 关 | 区分大小写 | | `--include-hidden` | 关 | 包含隐藏文件 | | `--pinyin` | 关 | 启用拼音搜索 | | `--pinyin-acronym` | 关 | 启用拼音首字母搜索 | | `--file-types=` | 无 | 按文件类型过滤 | | `--file-extensions=` | 无 | 按扩展名过滤 | | `--exclude=` | 无 | 排除指定目录,逗号分隔 | | `--max-results=` | `0`(不限) | 最大结果数 | | `--max-preview=` | `200` | 内容预览长度 | | `--filename=` | 无 | 在内容/OCR 搜索中按文件名过滤 | | `--semantic`, `-s` | 关 | 启用语义搜索 | | `--time-field=` | `modify` | 时间字段 | | `--time-last=` | 无 | 最近 N 时间段 | | `--time-today` 等预设 | 无 | 预设时间范围 | | `--time-range=,` | 无 | 自定义时间范围 | | `--size-min=` | 无 | 最小文件大小 | | `--size-max=` | 无 | 最大文件大小 | | `--json`, `-j` | 关 | JSON 输出 | | `--verbose`, `-v` | 关 | 详细输出 | | `--version` | — | 显示版本 | | `--help` | — | 显示帮助 | ### Preview 模式选项 | 选项 | 默认值 | 说明 | |------|--------|------| | `--offset=` | `0` | 从文档第几个字符开始阅读/搜索 | | `--max-preview=` | 无限制(无keyword) / `200`(有keyword) | 本次最多读取的字符数。设为 0 表示无限制 | | `--type=` | 自动 | 强制指定搜索类型 | | `-j` / `--json` | 关 | JSON 输出 | --- ## JSON 输出字段说明 ### 搜索模式 JSON | 字段 | 位置 | 说明 | |------|------|------| | `type` | 顶层 | `"search"` | | `searchType` | 顶层 | `"filename"` / `"content"` / `"ocr"` / `"semantic"` | | `keyword` | 顶层 | 搜索关键词 | | `totalResults` | 顶层 | 结果总数 | | `path` | result 项 | 文件路径 | | `filename` | result 项 | 文件名(verbose 模式) | | `highlightedContent` | result 项 | 高亮内容预览(content/ocr + verbose) | ### Preview 模式 JSON | 字段 | 位置 | 说明 | |------|------|------| | `type` | 顶层 | `"preview"` | | `searchType` | 顶层 | `"content"` / `"ocr"` / `"semantic"` | | `keyword` | 顶层 | 搜索关键词,空字符串表示无关键词 | | `offset` | 顶层 | 本次使用的偏移量 | | `maxLength` | 顶层 | 本次最大读取长度 | | `totalResults` | 顶层 | 结果总数(等于文件数) | | `path` | result 项 | 文件路径 | | `content` | result 项 | 截取到的内容片段 | | `keywordOffset` | result 项 | keyword 在全文中的位置(-1 = 无 keyword 或未匹配) | --- ## 字符计数说明 `--offset` 以**字符**(QChar)为单位,不是字节。 一个汉字算一个字符,一个换行符 `\n` 也算一个字符。 例如: ``` 你好\n世界 ``` | 位置 | 字符 | |------|------| | 0 | 你 | | 1 | 好 | | 2 | \n | | 3 | 世 | | 4 | 界 | 要从"世"开始读,`--offset=3`。 --- ## 常见问题 ### Q: preview 报 "(no content)" 是什么意思? 可能原因: 1. 文件未被索引(需要先建索引) 2. offset 超出文件长度 3. 给了 keyword 但文件中没有这个词(或在 offset 之后没有) ### Q: preview 和普通搜索有什么区别? 普通搜索搜索整个目录返回匹配文件列表。preview 不搜索,而是直接读取指定文件的内容片段。 ### Q: 可以预览任意文件吗? 不能。preview 只能读取已被索引的文件。文件需要先通过文件管理器的内容索引功能建立索引。 ### Q: --offset 是字节还是字符? 是字符(QChar)。一个中文字算一个字符,一个换行符也算一个字符。不是字节。 ### Q: 索引搜索找不到文件怎么办? 可以改用实时搜索:`--method=realtime`,它会直接遍历文件系统,不需要索引。 ### Q: --version 和 -v 冲突吗? 不冲突。`--version` 是长选项显示版本,`-v` 是 `--verbose` 的短选项。两者不冲突。 ### Q: 选项可以放在任何位置吗? 可以。`dfm-searcher --type=content "hello" /home/user` 和 `dfm-searcher "hello" /home/user --type=content` 效果一样。 linuxdeepin-util-dfm-a4307d3/include/000077500000000000000000000000001523477242100175365ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-burn/000077500000000000000000000000001523477242100212505ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-burn/dfm-burn/000077500000000000000000000000001523477242100227625ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-burn/dfm-burn/dburn_global.h000066400000000000000000000023751523477242100255740ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DBURN_GLOBAL_H #define DBURN_GLOBAL_H #include #define DFMBURN dfmburn #define DFM_BURN_BEGIN_NS namespace DFMBURN { #define DFM_BURN_END_NS } #define DFM_BURN_USE_NS using namespace DFMBURN; DFM_BURN_BEGIN_NS enum class BurnOption : unsigned int { kKeepAppendable = 1, kVerifyDatas = 1 << 1, kEjectDisc = 1 << 2, // not used yet. kISO9660Only = 1 << 3, // default kJolietSupport = 1 << 4, // add joliet extension kRockRidgeSupport = 1 << 5, // add rockridge extension kUDF102Supported = 1 << 6, kChecksum = 1 << 7, // SM3 checksum verification kJolietAndRockRidge = kJolietSupport | kRockRidgeSupport // add both of them, not used yet }; Q_DECLARE_FLAGS(BurnOptions, BurnOption) enum class MediaType : unsigned int { kNoMedia, kCD_ROM, kCD_R, kCD_RW, kDVD_ROM, kDVD_R, kDVD_RW, kDVD_PLUS_R, kDVD_PLUS_R_DL, kDVD_R_DL, kDVD_RAM, kDVD_PLUS_RW, kBD_ROM, kBD_R, kBD_RE }; enum class JobStatus : int { kFailed = -1, kIdle, kRunning, kStalled, kFinished }; DFM_BURN_END_NS #endif // DBURN_GLOBAL_H linuxdeepin-util-dfm-a4307d3/include/dfm-burn/dfm-burn/dopticaldiscinfo.h000066400000000000000000000020601523477242100264470ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DOPTICALDISCINFO_H #define DOPTICALDISCINFO_H #include #include #include #include DFM_BURN_BEGIN_NS class DOpticalDiscInfoPrivate; class DOpticalDiscInfo { friend class DOpticalDiscInfoPrivate; friend class DOpticalDiscManager; public: DOpticalDiscInfo(const DOpticalDiscInfo &info); DOpticalDiscInfo &operator=(const DOpticalDiscInfo &info); ~DOpticalDiscInfo(); bool blank() const; QString device() const; QString volumeName() const; quint64 usedSize() const; quint64 availableSize() const; quint64 totalSize() const; quint64 dataBlocks() const; MediaType mediaType() const; QStringList writeSpeed() const; protected: DOpticalDiscInfo(); DOpticalDiscInfo(const QString &dev); protected: QSharedDataPointer d_ptr; }; DFM_BURN_END_NS #endif // DOPTICALDISCINFO_H linuxdeepin-util-dfm-a4307d3/include/dfm-burn/dfm-burn/dopticaldiscmanager.h000066400000000000000000000025521523477242100271340ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef OPTICALDISCMANAGER_H #define OPTICALDISCMANAGER_H #include #include #include #include DFM_BURN_BEGIN_NS class DOpticalDiscInfo; class DOpticalDiscManagerPrivate; class DOpticalDiscManager : public QObject { Q_OBJECT Q_DISABLE_COPY(DOpticalDiscManager) public: static DOpticalDiscInfo *createOpticalInfo(const QString &dev); public: explicit DOpticalDiscManager(const QString &dev, QObject *parent = nullptr); virtual ~DOpticalDiscManager() override; bool setStageFile(const QString &diskPath, const QString &isoPath = "/"); bool commit(const BurnOptions &opts, int speed = 0, const QString &volId = "ISOIMAGE"); bool erase(); bool checkmedia(double *qgood, double *qslow, double *qbad); bool writeISO(const QString &isoPath, int speed = 0); bool dumpISO(const QString &isoPath); bool generateChecksumManifest(const QString &savePath); bool verifyChecksum(const QString &manifestPath); QString lastError() const; Q_SIGNALS: void jobStatusChanged(JobStatus status, int progress, QString speed, QStringList message); private: QScopedPointer dptr; }; DFM_BURN_END_NS #endif // OPTICALDISCMANAGER_H linuxdeepin-util-dfm-a4307d3/include/dfm-burn/dfm-burn/dpacketwritingcontroller.h000066400000000000000000000020371523477242100302600ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DPACKETWRITINGCONTROLLER_H #define DPACKETWRITINGCONTROLLER_H #include #include DFM_BURN_BEGIN_NS class DPacketWritingControllerPrivate; class DPacketWritingController : public QObject { Q_OBJECT Q_DISABLE_COPY(DPacketWritingController) public: explicit DPacketWritingController(const QString &dev, const QString &workingPath, QObject *parent = nullptr); ~DPacketWritingController() override; QString device() const; QString localWorkingDirectory() const; QString lastError() const; bool isOpen() const; bool open(); void close(); bool put(const QString &fileName); bool mv(const QString &srcName, const QString &destName); bool rm(const QString &fileName); private: QScopedPointer dptr; }; DFM_BURN_END_NS #endif // DPACKETWRITINGCONTROLLER_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/000077500000000000000000000000001523477242100207115ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/000077500000000000000000000000001523477242100220645ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/denumerator.h000066400000000000000000000132451523477242100245670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DENUMERATOR_H #define DENUMERATOR_H #include #include #include #include #include BEGIN_IO_NAMESPACE class DEnumeratorPrivate; class DFileInfo; class DEnumeratorFuture; class DEnumerator : public QEnableSharedFromThis { public: enum class DirFilter : int16_t { kNoFilter = -1, // no filter kDirs = 0x001, // List directories that match the filters. kFiles = 0x002, // List files. kDrives = 0x004, // List disk drives (ignored under Unix). kAllEntries = 0x001 | 0x002 | 0x004, // List directories, files, drives and symlinks (this does not list broken symlinks unless you specify System). kNoSymLinks = 0x008, // Do not list symbolic links (ignored by operating systems that don't support symbolic links). kReadable = 0x010, // List files for which the application has read access. The Readable value needs to be combined with Dirs or Files. kWritable = 0x020, // List files for which the application has write access. The Writable value needs to be combined with Dirs or Files. kExecutable = 0x040, // List files for which the application has execute access. The Executable value needs to be combined with Dirs or Files. kModified = 0x080, // Only list files that have been modified (ignored on Unix). kHidden = 0x100, // List hidden files (on Unix, files starting with a "."). kSystem = 0x200, // List system files (on Unix, FIFOs, sockets and device files are included; on Windows, .lnk files are included) kAllDirs = 0x400, // List all directories; i.e. don't apply the filters to directory names. kCaseSensitive = 0x800, // The filter should be case sensitive. kNoDot = 0x2000, // Do not list the special entry ".". kNoDotDot = 0x4000, // Do not list the special entry "..". kNoDotAndDotDot = 0x2000 | 0x4000, // Do not list the special entries "." and "..". }; Q_DECLARE_FLAGS(DirFilters, DirFilter) enum class IteratorFlag : uint8_t { kNoIteratorFlags = 0x00, // The default value, representing no flags. The iterator will return entries for the assigned path. kFollowSymlinks = 0x01, // When combined with Subdirectories, this flag enables iterating through all subdirectories of the assigned path, following all symbolic links. Symbolic link loops (e.g., "link" => "." or "link" => "..") are automatically detected and ignored. kSubdirectories = 0x02, // List entries inside all subdirectories as well. }; Q_DECLARE_FLAGS(IteratorFlags, IteratorFlag) enum class SortRoleCompareFlag : uint8_t { kSortRoleCompareDefault = 0, // The default value kSortRoleCompareFileName = 1, // compare by file path kSortRoleCompareFileSize = 2, // compare by file size kSortRoleCompareFileLastModified = 3, // compare by last modified time kSortRoleCompareFileLastRead = 4, // compare by last read time }; struct SortFileInfo { QUrl url; qint64 filesize { 0 }; bool isFile { false }; bool isDir { false }; bool isSymLink { false }; bool isHide { false }; bool isReadable { false }; bool isWriteable { false }; bool isExecutable { false }; ino_t inode { 0 }; QUrl symlinkUrl; uint gid { 0 }; uint uid { 0 }; qint64 lastRead { 0 }; qint64 lastReadNs { 0 }; qint64 lastModifed { 0 }; qint64 lastModifedNs { 0 }; qint64 create { 0 }; qint64 createNs { 0 }; }; enum class EnumeratorType : uint8_t{ kEnumeratorGio = 0, // enumerator by gio kEnumeratorFts = 0, // enumerator by fts kEnumeratorSystem = 0, // enumerator by system dirent }; public: explicit DEnumerator(const QUrl &uri); explicit DEnumerator(const QUrl &uri, const QStringList &nameFilters, DirFilters filters, IteratorFlags flags); ~DEnumerator(); QUrl uri() const; void setNameFilters(const QStringList &filters); QStringList nameFilters() const; void setDirFilters(DirFilters filters); DirFilters dirFilters() const; void setIteratorFlags(IteratorFlags flags); IteratorFlags iteratorFlags() const; void setTimeout(ulong timeout); ulong timeout() const; void setSortRole(SortRoleCompareFlag role); SortRoleCompareFlag sortRole() const; void setSortOrder(Qt::SortOrder order); Qt::SortOrder sortOrder() const; void setSortMixed(bool mix); bool isSortMixed() const; void setQueryAttributes(const QString &attributes); QString queryAttributes() const; public: bool cancel(); bool hasNext() const; QUrl next() const; QSharedPointer fileInfo() const; quint64 fileCount(); QList> fileInfoList(); QList> sortFileInfoList(); DFMIOError lastError() const; DEnumeratorFuture *asyncIterator(); void startAsyncIterator(); bool isAsyncOver() const; bool initEnumerator(const bool oneByone = true); private: QSharedPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(DEnumerator::DirFilters); Q_DECLARE_OPERATORS_FOR_FLAGS(DEnumerator::IteratorFlags); END_IO_NAMESPACE Q_DECLARE_METATYPE(DFMIO::DEnumerator::SortRoleCompareFlag) Q_DECLARE_METATYPE(QSharedPointer) Q_DECLARE_METATYPE(QList>) #endif // DENUMERATOR_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/denumeratorfuture.h000066400000000000000000000022331523477242100260150ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DENUMERATORFUTURE_H #define DENUMERATORFUTURE_H #include #include #include BEGIN_IO_NAMESPACE class DEnumerator; // 使用这个异步时,创建的DEnumerator必须是QSharedPointer /*使用示例 * QSharedPointer enumerator(new DEnumerator(QUrl())); * DEnumeratorFuture *future = enumerator->asyncIterator(); * 这样才能迭代出来文件。enumerator这里不需要手动析构,future不使用了手动析构 */ class DEnumeratorFuture : public QObject { Q_OBJECT public: explicit DEnumeratorFuture(const QSharedPointer &enumerator, QObject *parent = nullptr); ~DEnumeratorFuture() override; public: void startAsyncIterator(); Q_SIGNALS: void asyncIteratorOver(); public: bool isFinished(); DFMIOError lastError() const; qint64 fileCount(); public Q_SLOTS: void onAsyncIteratorOver(); private: QSharedPointer enumerator { nullptr }; }; END_IO_NAMESPACE #endif // DENUMERATORFUTURE_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/dfile.h000066400000000000000000000124171523477242100233250ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFILE_H #define DFILE_H #include #include #include #include #include BEGIN_IO_NAMESPACE class DFileFuture; class DFilePrivate; class DFile { public: enum class OpenFlag : uint16_t { kNotOpen = 0x0000, kReadOnly = 0x0001, kWriteOnly = 0x0002, // auto create kReadWrite = kReadOnly | kWriteOnly, // auto create kAppend = 0x0004, kTruncate = 0x0008, kText = 0x0010, kUnbuffered = 0x0020, kNewOnly = 0x0040, kExistingOnly = 0x0080, kCustomStart = 0x0100, }; Q_DECLARE_FLAGS(OpenFlags, OpenFlag) enum class CopyFlag : uint8_t { kNone = 0, // No flags set. kOverwrite = 1, // Overwrite any existing files. kBackup = (1 << 1), // Make a backup of any existing files. kNoFollowSymlinks = (1 << 2), // Don’t follow symlinks. kAllMetadata = (1 << 3), // Copy all file metadata instead of just default set used for copy. kNoFallbackForMove = (1 << 4), // Don’t use copy and delete fallback if native move not supported. kTargetDefaultPerms = (1 << 5), // Leaves target file with default perms, instead of setting the source file perms. kUserFlag = 0x40 }; Q_DECLARE_FLAGS(CopyFlags, CopyFlag) enum class SeekType : uint8_t { kBegin = 0x00, kCurrent = 0x01, kEnd = 0x02 }; enum class Permission : uint16_t { kNoPermission = 0x0000, kExeOther = 0x0001, kWriteOther = 0x0002, kReadOther = 0x0004, kExeGroup = 0x0010, kWriteGroup = 0x0020, kReadGroup = 0x0040, kExeUser = 0x0100, kWriteUser = 0x0200, kReadUser = 0x0400, kExeOwner = 0x1000, kWriteOwner = 0x2000, kReadOwner = 0x4000, }; Q_DECLARE_FLAGS(Permissions, Permission) // callback, use function pointer using ReadCallbackFunc = void (*)(qint64, void *); using ReadQCallbackFunc = void (*)(QByteArray, void *); using ReadAllCallbackFunc = void (*)(QByteArray, void *); using WriteCallbackFunc = void (*)(qint64, void *); using WriteAllCallbackFunc = void (*)(qint64, void *); using WriteQCallbackFunc = void (*)(qint64, void *); public: explicit DFile(const QUrl &uri); explicit DFile(const QString &path); ~DFile(); QUrl uri() const; bool isOpen() const; qint64 size() const; bool exists() const; qint64 pos() const; Permissions permissions() const; DFMIOError lastError() const; bool open(OpenFlags mode); bool close(); bool cancel(); bool seek(qint64 pos, SeekType type = SeekType::kBegin) const; bool flush(); bool setPermissions(Permissions permission); // read and write qint64 read(char *data, qint64 maxSize); QByteArray read(qint64 maxSize); QByteArray readAll(); qint64 write(const char *data, qint64 len); qint64 write(const char *data); qint64 write(const QByteArray &byteArray); // async callback void readAsync(char *data, qint64 maxSize, int ioPriority = 0, ReadCallbackFunc func = nullptr, void *userData = nullptr); void readQAsync(qint64 maxSize, int ioPriority = 0, ReadQCallbackFunc func = nullptr, void *userData = nullptr); void readAllAsync(int ioPriority = 0, ReadAllCallbackFunc func = nullptr, void *userData = nullptr); void writeAsync(const char *data, qint64 maxSize, int ioPriority = 0, WriteCallbackFunc func = nullptr, void *userData = nullptr); void writeAllAsync(const char *data, int ioPriority = 0, WriteAllCallbackFunc func = nullptr, void *userData = nullptr); void writeQAsync(const QByteArray &byteArray, int ioPriority = 0, WriteQCallbackFunc func = nullptr, void *userData = nullptr); // future callback [[nodiscard]] DFileFuture *openAsync(OpenFlags mode, int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *closeAsync(int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *readAsync(quint64 maxSize, int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *readAllAsync(int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *writeAsync(const QByteArray &data, qint64 len, int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *writeAsync(const QByteArray &data, int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *flushAsync(int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *sizeAsync(int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *existsAsync(int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *permissionsAsync(int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *setPermissionsAsync(Permissions permission, int ioPriority, QObject *parent = nullptr); private: QScopedPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(DFile::OpenFlags); Q_DECLARE_OPERATORS_FOR_FLAGS(DFile::Permissions); Q_DECLARE_OPERATORS_FOR_FLAGS(DFile::CopyFlags); END_IO_NAMESPACE Q_DECLARE_METATYPE(dfmio::DFile::Permissions); #endif // DFILE_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/dfilefuture.h000066400000000000000000000025071523477242100245570ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFILEFUTURE_H #define DFILEFUTURE_H #include #include #include #include #include BEGIN_IO_NAMESPACE class DFuturePrivate; class DFileFuture : public QObject { Q_OBJECT public: explicit DFileFuture(QObject *parent = nullptr); ~DFileFuture(); bool cancel(); bool isFinished() const; bool hasError() const; int errorCode(); QString errorMessage(); // TODO(lanxs): setCancallable for cancel() void setError(DFMIOError error); Q_SIGNALS: void finished(); void infoAttribute(DFileInfo::AttributeID id, const QVariant &value); void infoAttribute(const QByteArray &key, const QVariant &value); void progressNotify(qint64 current, qint64 total); void infoExists(const bool exists); void infoPermissions(const DFile::Permissions permissions); void infoSize(const quint64 &size); void writeAsyncSize(const qint64 &size); void readData(const QByteArray &data); void infoMedia(const QUrl &url, const QMap &map); private: QScopedPointer d; }; END_IO_NAMESPACE #endif // DFILEFUTURE_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/dfileinfo.h000066400000000000000000000221041523477242100241730ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFILEINFO_H #define DFILEINFO_H #include #include #include #include #include #include #include #include #include BEGIN_IO_NAMESPACE class DFileFuture; class DFileInfoPrivate; class DFileInfo { public: enum class DFileType : uint16_t { kUnknown = 0, // File's type is unknown. kRegular = 1, // File handle represents a regular file. kDirectory = 2, // File handle represents a directory. kSymbolicLink = 3, // File handle represents a symbolic link (Unix systems). kSpecial = 4, // File is a "special" file, such as a socket, fifo, block device, or character device. kShortcut = 5, // File is a shortcut (Windows systems). kMountable = 6, // File is a mountable location. // Reserved kUserType = 0x100 }; enum class DFileAttributeType : uint8_t { kTypeInvalid = 0, // Indicates an invalid or uninitialized type kTypeString = 1, // A null terminated UTF8 string kTypeByteString = 2, // A zero terminated string of non-zero bytes kTypeBool = 3, // A boolean value kTypeUInt32 = 4, // An unsigned 4-byte/32-bit integer kTypeInt32 = 5, // A signed 4-byte/32-bit integer kTypeUInt64 = 6, // An unsigned 8-byte/64-bit integer kTypeInt64 = 7, // A signed 8-byte/64-bit integer kTypeObject = 8, // A Object kTypeStringV = 9 // A NULL terminated char ** }; enum class FileQueryInfoFlags : uint8_t { kTypeNone, kTypeNoFollowSymlinks }; enum class AttributeID : uint16_t { kStandardType = 0, // uint32 kStandardIsHidden = 1, // boolean kStandardIsBackup = 2, // boolean kStandardIsSymlink = 3, // boolean kStandardIsVirtual = 4, // boolean kStandardIsVolatile = 5, // boolean kStandardName = 6, // byte string kStandardDisplayName = 7, // string kStandardEditName = 8, // string kStandardCopyName = 9, // string kStandardIcon = 10, // QList kStandardSymbolicIcon = 11, // QList kStandardContentType = 12, // string kStandardFastContentType = 13, // string kStandardSize = 14, // uint64 kStandardAllocatedSize = 15, // uint64 kStandardSymlinkTarget = 16, // byte string kStandardTargetUri = 17, // string kStandardSortOrder = 18, // int32 kStandardDescription = 19, // string kOriginalUri = 20, kEtagValue = 40, // string kIdFile = 60, // string kIdFilesystem = 61, // string kAccessCanRead = 100, // boolean kAccessCanWrite = 101, // boolean kAccessCanExecute = 102, // boolean kAccessCanDelete = 103, // boolean kAccessCanTrash = 104, // boolean kAccessCanRename = 105, // boolean kAccessPermissions = 106, // DFile::Permissions kMountableCanMount = 130, // boolean kMountableCanUnmount = 131, // boolean kMountableCanEject = 132, // boolean kMountableUnixDevice = 133, // uint32 kMountableUnixDeviceFile = 134, // string kMountableHalUdi = 135, // string kMountableCanPoll = 136, // boolean kMountableIsMediaCheckAutomatic = 137, // boolean kMountableCanStart = 138, // boolean kMountableCanStartDegraded = 139, // boolean kMountableCanStop = 140, // boolean kMountableStartStopType = 141, // uint32 kTimeModified = 200, // uint64 kTimeModifiedUsec = 201, // uint32 kTimeAccess = 202, // uint64 kTimeAccessUsec = 203, // uint32 kTimeChanged = 204, // uint64 kTimeChangedUsec = 205, // uint32 kTimeCreated = 206, // uint64 kTimeCreatedUsec = 207, // uint32 kUnixDevice = 330, // uint32 kUnixInode = 331, // uint64 kUnixMode = 332, // uint32 kUnixNlink = 333, // uint32 kUnixUID = 334, // uint32 kUnixGID = 335, // uint32 kUnixRdev = 336, // uint32 kUnixBlockSize = 337, // uint32 kUnixBlocks = 338, // uint64 kUnixIsMountPoint = 339, // boolean kDosIsArchive = 360, // boolean kDosIsSystem = 361, // boolean kOwnerUser = 300, // string kOwnerUserReal = 301, // string kOwnerGroup = 302, // string kThumbnailPath = 390, // byte string kThumbnailFailed = 391, // boolean kThumbnailIsValid = 392, // boolean kPreviewIcon = 420, // object kFileSystemSize = 440, // uint64 kFileSystemFree = 441, // uint64 kFileSystemUsed = 442, // uint64 kFileSystemType = 443, // string kFileSystemReadOnly = 444, // boolean kFileSystemUsePreview = 445, // uint32 kFileSystemRemote = 446, // boolean kGvfsBackend = 470, // string kSelinuxContext = 490, // string kTrashItemCount = 510, // uint32 kTrashDeletionDate = 511, // string kTrashOrigPath = 512, // byte string kRecentModified = 540, // int64 kCustomStart = 600, kStandardIsFile = 610, kStandardIsDir = 611, kStandardIsRoot = 612, kStandardSuffix = 613, kStandardCompleteSuffix = 614, kStandardFilePath = 615, kStandardParentPath = 616, kStandardBaseName = 617, kStandardFileName = 618, kStandardCompleteBaseName = 619, kAttributeIDMax = 999, }; enum class MediaType : uint8_t { kGeneral, kVideo, kAudio, kText, kOther, kImage, kMenu, kMax, }; enum class AttributeExtendID : uint8_t { kExtendWordSize, // xattr::word-size kExtendMediaDuration, // xattr::media-duration kExtendMediaWidth, // xattr::media-width kExtendMediaHeight, // xattr::media-height }; // callback, use function pointer using InitQuerierAsyncCallback = std::function; using AttributeAsyncCallback = std::function; using AttributeExtendFuncCallback = std::function)>; public: explicit DFileInfo(const QUrl &uri, const char *attributes = "*", const FileQueryInfoFlags flag = FileQueryInfoFlags::kTypeNone); explicit DFileInfo(const QUrl &uri, void *fileInfo, const char *attributes = "*", const DFMIO::DFileInfo::FileQueryInfoFlags flag = DFMIO::DFileInfo::FileQueryInfoFlags::kTypeNone); DFileInfo(const DFileInfo &other); DFileInfo &operator=(const DFileInfo &other); ~DFileInfo(); bool initQuerier(); QVariant attribute(DFileInfo::AttributeID id, bool *success = nullptr) const; void initQuerierAsync(int ioPriority = 0, InitQuerierAsyncCallback func = nullptr, void *userData = nullptr); void attributeAsync(DFileInfo::AttributeID id, bool *success = nullptr, int ioPriority = 0, AttributeAsyncCallback func = nullptr, void *userData = nullptr); [[nodiscard]] DFileFuture *initQuerierAsync(int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *attributeAsync(AttributeID id, int ioPriority, QObject *parent = nullptr) const; [[nodiscard]] DFileFuture *attributeAsync(const QByteArray &key, const DFileAttributeType type, int ioPriority, QObject *parent = nullptr) const; [[nodiscard]] DFileFuture *existsAsync(int ioPriority, QObject *parent = nullptr) const; [[nodiscard]] DFileFuture *refreshAsync(int ioPriority, QObject *parent = nullptr); [[nodiscard]] DFileFuture *permissionsAsync(int ioPriority, QObject *parent = nullptr); [[nodiscard]] QFuture refreshAsync(); bool hasAttribute(DFileInfo::AttributeID id) const; bool exists() const; bool refresh(); DFile::Permissions permissions() const; // custom attribute bool setCustomAttribute(const char *key, const DFileAttributeType type, const void *value, const FileQueryInfoFlags flag = FileQueryInfoFlags::kTypeNone); QVariant customAttribute(const char *key, const DFileAttributeType type) const; DFMIOError lastError() const; // extend attribute void attributeExtend(MediaType type, QList ids, AttributeExtendFuncCallback callback = nullptr); [[nodiscard]] DFileFuture *attributeExtend(MediaType type, QList ids, int ioPriority, QObject *parent = nullptr); bool cancelAttributeExtend(); bool cancelAttributes(); QUrl uri() const; char *queryAttributes() const; DFileInfo::FileQueryInfoFlags queryInfoFlag() const; QString dump() const; bool queryAttributeFinished() const; private: mutable QSharedDataPointer d; }; END_IO_NAMESPACE #endif // DFILEINFO_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/dfmio_global.h000066400000000000000000000005471523477242100246610ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFMIO_GLOBAL_H #define DFMIO_GLOBAL_H // namespace #define DFMIO dfmio #define BEGIN_IO_NAMESPACE namespace DFMIO { #define USING_IO_NAMESPACE using namespace DFMIO; #define END_IO_NAMESPACE } #endif // DFMIO_GLOBAL_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/dfmio_utils.h000066400000000000000000000044641523477242100245630ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFMIO_UTILS_H #define DFMIO_UTILS_H #include #include #include class QUrl; BEGIN_IO_NAMESPACE class DEnumeratorFuture; enum class DGlibUserDirectory : quint8 { kUserDirectoryDesktop, kUserDirectoryDocuments, kUserDirectoryDownload, kUserDirectoryMusic, kUserDirectoryPictures, kUserDirectoryPublicShare, kUserDirectoryTemplates, kUserDirectoryVideos, kUserNDirectories }; Q_ENUMS(DGlibUserDirectory); class DFMUtils { public: static bool fileUnmountable(const QString &path); static QString devicePathFromUrl(const QUrl &url); static QString deviceNameFromUrl(const QUrl &url); static QString fsTypeFromUrl(const QUrl &url); static QString mountPathFromUrl(const QUrl &url); static QUrl directParentUrl(const QUrl &url, const bool localFirst = true); static bool fileIsRemovable(const QUrl &url); static QSet hideListFromUrl(const QUrl &url); /* * build file path, parameter should endwith "nullptr" * e.g.: buildPath("/", "Desktop", "nullptr"), then return "/Desktop" */ static QString buildFilePath(const char *segment, ...); static QStringList systemDataDirs(); static QString userSpecialDir(DGlibUserDirectory userDirectory); static QString userDataDir(); static QString bindPathTransform(const QString &path, bool toDevice); static int dirFfileCount(const QUrl &url); static QUrl bindUrlTransform(const QUrl &url); static QString BackslashPathToNormal(const QString &trash); static QString normalPathToBackslash(const QString &normal); // 通过迭代器去获取回收站数量,并做相同挂载点过滤 static DEnumeratorFuture *asyncTrashCount(); static int syncTrashCount(); static qint64 deviceBytesFree(const QUrl &url); static bool supportTrash(const QUrl &url); static bool isGvfsFile(const QUrl &url); // String comparison function for file names static bool compareFileName(const QString &str1, const QString &str2); static bool isInvalidCodecByPath(const char *path); private: static QMap fstabBindInfo(); }; END_IO_NAMESPACE #endif // DFMIO_UTILS_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/doperator.h000066400000000000000000000060001523477242100242300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DOPERATOR_H #define DOPERATOR_H #include #include #include #include #include #include BEGIN_IO_NAMESPACE class DOperatorPrivate; class DFileInfo; class DOperator { public: // callback, use function pointer using ProgressCallbackFunc = void (*)(int64_t, int64_t, void *); // current_num_bytes, total_num_bytes, user_data using FileOperateCallbackFunc = void (*)(bool, void *); public: explicit DOperator(const QUrl &uri); virtual ~DOperator(); QUrl uri() const; bool renameFile(const QString &newName); bool renameFile(const QUrl &toUrl); bool copyFile(const QUrl &destUri, DFile::CopyFlags flag, ProgressCallbackFunc func = nullptr, void *progressCallbackData = nullptr); bool moveFile(const QUrl &destUri, DFile::CopyFlags flag, ProgressCallbackFunc func = nullptr, void *progressCallbackData = nullptr); // async void renameFileAsync(const QString &newName, int ioPriority = 0, FileOperateCallbackFunc func = nullptr, void *userData = nullptr); void copyFileAsync(const QUrl &destUri, DFile::CopyFlags flag, ProgressCallbackFunc progressfunc = nullptr, void *progressCallbackData = nullptr, int ioPriority = 0, FileOperateCallbackFunc operatefunc = nullptr, void *userData = nullptr); void moveFileAsync(const QUrl &destUri, DFile::CopyFlags flag, ProgressCallbackFunc progressFunc = nullptr, void *progressCallbackData = nullptr, int ioPriority = 0, FileOperateCallbackFunc operatefunc = nullptr, void *userData = nullptr); QString trashFile(); bool deleteFile(); bool restoreFile(ProgressCallbackFunc func = nullptr, void *progressCallbackData = nullptr); // async void trashFileAsync(int ioPriority = 0, FileOperateCallbackFunc operatefunc = nullptr, void *userData = nullptr); void deleteFileAsync(int ioPriority = 0, FileOperateCallbackFunc operatefunc = nullptr, void *userData = nullptr); void restoreFileAsync(ProgressCallbackFunc func = nullptr, void *progressCallbackData = nullptr, int ioPriority = 0, FileOperateCallbackFunc operatefunc = nullptr, void *userData = nullptr); bool touchFile(); bool makeDirectory(); bool createLink(const QUrl &link); // async void touchFileAsync(int ioPriority = 0, FileOperateCallbackFunc operatefunc = nullptr, void *userData = nullptr); void makeDirectoryAsync(int ioPriority = 0, FileOperateCallbackFunc operatefunc = nullptr, void *userData = nullptr); void createLinkAsync(const QUrl &link, int ioPriority = 0, FileOperateCallbackFunc operatefunc = nullptr, void *userData = nullptr); bool setFileInfo(const DFileInfo &fileInfo); bool cancel(); DFMIOError lastError() const; private: QScopedPointer d; }; END_IO_NAMESPACE #endif // DOPERATOR_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/dwatcher.h000066400000000000000000000022431523477242100240370ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DWATCHER_H #define DWATCHER_H #include #include #include #include #include BEGIN_IO_NAMESPACE class DFileInfo; class DWatcherPrivate; class DWatcher : public QObject { Q_OBJECT public: enum class WatchType : uint8_t { kAuto = 0x00, kDir = 0x01, kFile = 0x02, }; public: explicit DWatcher(const QUrl &uri, QObject *parent = nullptr); virtual ~DWatcher() override; QUrl uri() const; void setTimeRate(int msec); int timeRate() const; void setWatchType(WatchType type); WatchType watchType() const; bool running() const; bool start(int timeRate = 200); bool stop(); DFMIOError lastError() const; Q_SIGNALS: void fileChanged(const QUrl &url); void fileDeleted(const QUrl &url); void fileAdded(const QUrl &url); void fileRenamed(const QUrl &fromUrl, const QUrl &toUrl); private: QScopedPointer d; }; END_IO_NAMESPACE #endif // DWATCHER_H linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/error/000077500000000000000000000000001523477242100232155ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/error/en.h000066400000000000000000000227571523477242100240050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFM_IO_ERROR_EN_H_ #define DFM_IO_ERROR_EN_H_ #include #include enum DFMIOErrorCode { DFM_IO_ERROR_NONE = -1, // No error. DFM_IO_ERROR_FAILED, // Generic error condition for when an operation fails and no more specific DFMIOErrorEnum value is defined. DFM_IO_ERROR_NOT_FOUND, // File not found. DFM_IO_ERROR_EXISTS, // File already exists. DFM_IO_ERROR_IS_DIRECTORY, // File is a directory. DFM_IO_ERROR_NOT_DIRECTORY, // File is not a directory. DFM_IO_ERROR_NOT_EMPTY, // File is a directory that isn't empty. DFM_IO_ERROR_NOT_REGULAR_FILE, // File is not a regular file. DFM_IO_ERROR_NOT_SYMBOLIC_LINK, // File is not a symbolic link. DFM_IO_ERROR_NOT_MOUNTABLE_FILE, // File cannot be mounted. DFM_IO_ERROR_FILENAME_TOO_LONG, // Filename is too many characters. DFM_IO_ERROR_INVALID_FILENAME, // Filename is invalid or contains invalid characters. DFM_IO_ERROR_TOO_MANY_LINKS, // File contains too many symbolic links. DFM_IO_ERROR_NO_SPACE, // No space left on drive. DFM_IO_ERROR_INVALID_ARGUMENT, // Invalid argument. DFM_IO_ERROR_PERMISSION_DENIED, // Permission denied. DFM_IO_ERROR_NOT_SUPPORTED, // Operation (or one of its parameters) not supported. DFM_IO_ERROR_NOT_MOUNTED, // File isn't mounted. DFM_IO_ERROR_ALREADY_MOUNTED, // File is already mounted. DFM_IO_ERROR_CLOSED, // File was closed. DFM_IO_ERROR_CANCELLED, // Operation was cancelled. DFM_IO_ERROR_PENDING, // Operations are still pending. DFM_IO_ERROR_READ_ONLY, // File is read only. DFM_IO_ERROR_CANT_CREATE_BACKUP, // Backup couldn't be created. DFM_IO_ERROR_WRONG_ETAG, // File's Entity Tag was incorrect. DFM_IO_ERROR_TIMED_OUT, // Operation timed out. DFM_IO_ERROR_WOULD_RECURSE, // Operation would be recursive. DFM_IO_ERROR_BUSY, // File is busy. DFM_IO_ERROR_WOULD_BLOCK, // Operation would block. DFM_IO_ERROR_HOST_NOT_FOUND, // Host couldn't be found (remote operations). DFM_IO_ERROR_WOULD_MERGE, // Operation would merge files. DFM_IO_ERROR_FAILED_HANDLED, // Operation failed and a helper program has already interacted with the user. // Do not display any error dialog. DFM_IO_ERROR_TOO_MANY_OPEN_FILES, // The current process has too many files open and can't open any more. // Duplicate descriptors do count toward this limit. DFM_IO_ERROR_NOT_INITIALIZED, // The object has not been initialized. DFM_IO_ERROR_ADDRESS_IN_USE, // The requested address is already in use. DFM_IO_ERROR_PARTIAL_INPUT, // Need more input to finish operation. DFM_IO_ERROR_INVALID_DATA, // The input data was invalid. DFM_IO_ERROR_DBUS_ERROR, // A remote object generated an error(dbus). DFM_IO_ERROR_HOST_UNREACHABLE, // Host unreachable. DFM_IO_ERROR_NETWORK_UNREACHABLE, // Network unreachable. DFM_IO_ERROR_CONNECTION_REFUSED, // Connection refused. DFM_IO_ERROR_PROXY_FAILED, // Connection to proxy server failed. DFM_IO_ERROR_PROXY_AUTH_FAILED, // Proxy authentication failed. DFM_IO_ERROR_PROXY_NEED_AUTH, // Proxy server needs authentication. DFM_IO_ERROR_PROXY_NOT_ALLOWED, // Proxy connection is not allowed by ruleset. DFM_IO_ERROR_BROKEN_PIPE, // Broken pipe. DFM_IO_ERROR_CONNECTION_CLOSED, // Connection closed by peer. DFM_IO_ERROR_NOT_CONNECTED, // Transport endpoint is not connected. DFM_IO_ERROR_MESSAGE_TOO_LARGE, // Message too large. DFM_IO_ERROR_USER_FAILED = 1000, // Generic error condition for when an operation fails and no more specific DFMIOErrorEnum value is defined. DFM_IO_ERROR_OPEN_FAILED, // File open failed DFM_IO_ERROR_OPEN_FLAG_ERROR, // File open flag is error DFM_IO_ERROR_INFO_NO_ATTRIBUTE, // File info has no attribute DFM_IO_ERROR_FTS_OPEN, // open file by fts failed DFM_IO_ERROR_HOST_IS_DOWN, // remote server maybe down }; inline const QString GetError_En(DFMIOErrorCode errorCode) { switch (errorCode) { case DFM_IO_ERROR_NONE: return QObject::tr("No error"); case DFM_IO_ERROR_NOT_FOUND: return QObject::tr("File not found"); case DFM_IO_ERROR_EXISTS: return QObject::tr("File already exists"); case DFM_IO_ERROR_IS_DIRECTORY: return QObject::tr("File is a directory"); case DFM_IO_ERROR_NOT_DIRECTORY: return QObject::tr("File is not a directory"); case DFM_IO_ERROR_NOT_EMPTY: return QObject::tr("File is a directory that isn't empty"); case DFM_IO_ERROR_NOT_REGULAR_FILE: return QObject::tr("File is not a regular file"); case DFM_IO_ERROR_NOT_SYMBOLIC_LINK: return QObject::tr("File is not a symbolic link"); case DFM_IO_ERROR_NOT_MOUNTABLE_FILE: return QObject::tr("File cannot be mounted"); case DFM_IO_ERROR_FILENAME_TOO_LONG: return QObject::tr("Filename has too many characters"); case DFM_IO_ERROR_INVALID_FILENAME: return QObject::tr("Filename is invalid or contains invalid characters"); case DFM_IO_ERROR_TOO_MANY_LINKS: return QObject::tr("File contains too many symbolic links"); case DFM_IO_ERROR_NO_SPACE: return QObject::tr("No space left on drive"); case DFM_IO_ERROR_INVALID_ARGUMENT: return QObject::tr("Invalid argument"); case DFM_IO_ERROR_PERMISSION_DENIED: return QObject::tr("Permission denied"); case DFM_IO_ERROR_NOT_SUPPORTED: return QObject::tr("Operation (or one of its parameters) not supported"); case DFM_IO_ERROR_NOT_MOUNTED: return QObject::tr("File isn't mounted"); case DFM_IO_ERROR_ALREADY_MOUNTED: return QObject::tr("File is already mounted"); case DFM_IO_ERROR_CLOSED: return QObject::tr("File was closed"); case DFM_IO_ERROR_CANCELLED: return QObject::tr("Operation was cancelled"); case DFM_IO_ERROR_PENDING: return QObject::tr("Operations are still pending"); case DFM_IO_ERROR_READ_ONLY: return QObject::tr("File is read-only"); case DFM_IO_ERROR_CANT_CREATE_BACKUP: return QObject::tr("Backup couldn't be created"); case DFM_IO_ERROR_WRONG_ETAG: return QObject::tr("File's Entity Tag was incorrect"); case DFM_IO_ERROR_TIMED_OUT: return QObject::tr("Operation timed out"); case DFM_IO_ERROR_WOULD_RECURSE: return QObject::tr("Operation would be recursive"); case DFM_IO_ERROR_BUSY: return QObject::tr("File is busy"); case DFM_IO_ERROR_WOULD_BLOCK: return QObject::tr("Operation would block"); case DFM_IO_ERROR_HOST_NOT_FOUND: return QObject::tr("Host couldn't be found (remote operations)"); case DFM_IO_ERROR_WOULD_MERGE: return QObject::tr("Operation would merge files"); case DFM_IO_ERROR_FAILED_HANDLED: return QObject::tr("Operation failed and a helper program has already interacted with the user. Do not display any error dialog"); case DFM_IO_ERROR_TOO_MANY_OPEN_FILES: return QObject::tr("The current process has too many files open and can't open any more. Duplicate descriptors do count toward this limit"); case DFM_IO_ERROR_NOT_INITIALIZED: return QObject::tr("The object has not been initialized"); case DFM_IO_ERROR_ADDRESS_IN_USE: return QObject::tr("The requested address is already in use"); case DFM_IO_ERROR_PARTIAL_INPUT: return QObject::tr("Need more input to finish operation"); case DFM_IO_ERROR_INVALID_DATA: return QObject::tr("The input data was invalid"); case DFM_IO_ERROR_DBUS_ERROR: return QObject::tr("A remote object generated an error(dbus)"); case DFM_IO_ERROR_HOST_UNREACHABLE: return QObject::tr("Host unreachable"); case DFM_IO_ERROR_NETWORK_UNREACHABLE: return QObject::tr("Network unreachable"); case DFM_IO_ERROR_CONNECTION_REFUSED: return QObject::tr("Connection refused"); case DFM_IO_ERROR_PROXY_FAILED: return QObject::tr("Connection to proxy server failed"); case DFM_IO_ERROR_PROXY_AUTH_FAILED: return QObject::tr("Proxy authentication failed"); case DFM_IO_ERROR_PROXY_NEED_AUTH: return QObject::tr("Proxy server needs authentication"); case DFM_IO_ERROR_PROXY_NOT_ALLOWED: return QObject::tr("Proxy connection is not allowed by ruleset"); case DFM_IO_ERROR_BROKEN_PIPE: return QObject::tr("Broken pipe"); case DFM_IO_ERROR_CONNECTION_CLOSED: return QObject::tr("Connection closed by peer"); case DFM_IO_ERROR_NOT_CONNECTED: return QObject::tr("Transport endpoint is not connected"); case DFM_IO_ERROR_MESSAGE_TOO_LARGE: return QObject::tr("Message too large"); case DFM_IO_ERROR_FAILED: return QObject::tr("Generic error condition for when an operation fails and no more specific DFMIOErrorEnum value is defined"); case DFM_IO_ERROR_OPEN_FAILED: return QObject::tr("Failed to open the file"); case DFM_IO_ERROR_OPEN_FLAG_ERROR: return QObject::tr("File open flag error"); case DFM_IO_ERROR_INFO_NO_ATTRIBUTE: return QObject::tr("File info has no attribute"); case DFM_IO_ERROR_FTS_OPEN: return QObject::tr("open file by fts failed"); case DFM_IO_ERROR_USER_FAILED: return QString(); case DFM_IO_ERROR_HOST_IS_DOWN: return QObject::tr("Host is down"); } return QString("Unknown error"); } #endif // DFM_IO_ERROR_EN_H_ linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/error/error.h000066400000000000000000000026001523477242100245150ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFM_IO_ERROR_ERROR_H_ #define DFM_IO_ERROR_ERROR_H_ #include "en.h" struct DFMIOError { public: // Default constructor, no error. DFMIOError() : errorCode(DFM_IO_ERROR_NONE) {} // Constructor to set an error. DFMIOError(DFMIOErrorCode code) : errorCode(code) {} //! Get the error code. DFMIOErrorCode code() const { return errorCode; } QString errorMsg() const { if (!errMsg.isEmpty()) return errMsg; return GetError_En(errorCode); } //! Conversion to \c bool, returns \c true, iff !\ref IsError(). operator bool() const { return errorCode != DFM_IO_ERROR_NONE; } //! Whether the result is an error. bool isError() const { return errorCode != DFM_IO_ERROR_NONE; } bool operator==(const DFMIOError &that) const { return errorCode == that.errorCode && errMsg == that.errMsg; } //! Reset error code. void clear() { setCode(DFM_IO_ERROR_NONE); } //! Update error code and offset. void setCode(DFMIOErrorCode code) { errorCode = code; } //! Update error message. void setMessage(const QString &msg) { errMsg = msg; } private: DFMIOErrorCode errorCode; QString errMsg; }; #endif // DFM_IO_ERROR_ERROR_H_ linuxdeepin-util-dfm-a4307d3/include/dfm-io/dfm-io/trashhelper.h000066400000000000000000000013451523477242100245610ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TRASHHELPER_H #define TRASHHELPER_H #include #include #include BEGIN_IO_NAMESPACE class TrashHelper { public: struct DeleteTimeInfo { qint64 startTime { 0 }; qint64 endTime { 0 }; }; public: TrashHelper(); ~TrashHelper(); void setDeleteInfos(const QMap> &infos); bool getTrashUrls(QList *trashUrls, QString *errorMsg = nullptr); private: QMap> deleteInfos; }; END_IO_NAMESPACE #endif // TRASHHELPER_H linuxdeepin-util-dfm-a4307d3/include/dfm-mount/000077500000000000000000000000001523477242100214445ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/000077500000000000000000000000001523477242100233525ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/base/000077500000000000000000000000001523477242100242645ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/base/ddevice.h000066400000000000000000000073771523477242100260560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DDEVICE_H #define DDEVICE_H #include #include #include #include #include #include #include DFM_MOUNT_BEGIN_NS class DDevicePrivate; class DDevice : public QObject { Q_OBJECT public: explicit DDevice(DDevicePrivate *dd, QObject *parent = nullptr); virtual ~DDevice(); DMNT_VIRTUAL QString path() const; DMNT_VIRTUAL QString mount(const QVariantMap &opts = {}); DMNT_VIRTUAL void mountAsync(const QVariantMap &opts = {}, DeviceOperateCallbackWithMessage cb = nullptr); DMNT_VIRTUAL bool unmount(const QVariantMap &opts = {}); DMNT_VIRTUAL void unmountAsync(const QVariantMap &opts = {}, DeviceOperateCallback cb = nullptr); DMNT_VIRTUAL bool rename(const QString &newName, const QVariantMap &opts = {}); DMNT_VIRTUAL void renameAsync(const QString &newName, const QVariantMap &opts = {}, DeviceOperateCallback cb = nullptr); DMNT_VIRTUAL QString mountPoint() const; DMNT_VIRTUAL QString fileSystem() const; DMNT_VIRTUAL qint64 sizeTotal() const; // use these two functions CAUTION!!! for block devices if you invoke just after mount sync, the // mount point of this device cannot be returned correctly, so there is no way to get the useage // via QStorageInfo cause cannot get the mountpoint right away. // already submit an issue to report this: https://github.com/storaged-project/udisks/issues/930 DMNT_VIRTUAL qint64 sizeFree() const; DMNT_VIRTUAL qint64 sizeUsage() const; DMNT_VIRTUAL DeviceType deviceType() const; DMNT_VIRTUAL QVariant getProperty(Property name) const; DMNT_VIRTUAL QString displayName() const; OperationErrorInfo lastError() const; public: // type definition using PathFunc = std::function; using MountFunc = std::function; using MountAsyncFunc = std::function; using UnmountFunc = std::function; using UnmountAsyncFunc = std::function; using RenameFunc = std::function; using RenameAsyncFunc = std::function; using MountPointFunc = std::function; using FileSystemFunc = std::function; using SizeTotalFunc = std::function; using SizeUsageFunc = std::function; using SizeFreeFunc = std::function; using DeviceTypeFunc = std::function; using GetPropertyFunc = std::function; using DisplayNameFunc = std::function; void registerPath(const PathFunc &func); void registerMount(const MountFunc &func); void registerMountAsync(const MountAsyncFunc &func); void registerUnmount(const UnmountFunc &func); void registerUnmountAsync(const UnmountAsyncFunc &func); void registerRename(const RenameFunc &func); void registerRenameAsync(const RenameAsyncFunc &func); void registerMountPoint(const MountPointFunc &func); void registerFileSystem(const FileSystemFunc &func); void registerSizeTotal(const SizeTotalFunc &func); void registerSizeUsage(const SizeUsageFunc &func); void registerSizeFree(const SizeFreeFunc &func); void registerDeviceType(const DeviceTypeFunc &func); void registerGetProperty(const GetPropertyFunc &func); void registerDisplayName(const DisplayNameFunc &func); protected: QScopedPointer d; }; DFM_MOUNT_END_NS #endif // DDEVICE_H linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/base/ddevicemonitor.h000066400000000000000000000040561523477242100274550ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DDEVICEMONITOR_H #define DDEVICEMONITOR_H #include #include #include #include DFM_MOUNT_BEGIN_NS class DDevice; class DDeviceMonitorPrivate; class DDeviceMonitor : public QObject { Q_OBJECT public: MonitorStatus status() const; DMNT_VIRTUAL bool startMonitor(); DMNT_VIRTUAL bool stopMonitor(); DMNT_VIRTUAL DeviceType monitorObjectType() const; DMNT_VIRTUAL QStringList getDevices() const; DMNT_VIRTUAL QSharedPointer createDeviceById(const QString &id) const; public: using StartMonitorFunc = std::function; using StopMonitorFunc = std::function; using MonitorObjectTypeFunc = std::function; using GetDevicesFunc = std::function; using CreateDeviceByIdFunc = std::function(const QString &)>; void registerStartMonitor(const StartMonitorFunc &func); void registerStopMonitor(const StopMonitorFunc &func); void registerMonitorObjectType(const MonitorObjectTypeFunc &func); void registerGetDevices(const GetDevicesFunc &func); void registerCreateDeviceById(const CreateDeviceByIdFunc &func); protected: DDeviceMonitor(DDeviceMonitorPrivate *dd, QObject *parent); virtual ~DDeviceMonitor(); Q_SIGNALS: // TODO: complete protocol device id. /*! * \brief deviceAdded * \param deviceId for block devices, this id is block object path in udisks, but for protocol devices, it's not. */ void deviceAdded(const QString &deviceId); void deviceRemoved(const QString &deviceId); void mountAdded(const QString &deviceId, const QString &mountPoint); void mountRemoved(const QString &deviceId); void propertyChanged(const QString &deviceId, const QMap &changes); protected: QScopedPointer d; }; DFM_MOUNT_END_NS #endif // DDEVICEMONITOR_H linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/base/dmount_global.h000066400000000000000000000441031523477242100272650ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DMOUNT_GLOBAL_H #define DMOUNT_GLOBAL_H #include #include #include #include #if defined(DMOUNT_LIBRARY) # define DMOUNTSHARED_EXPORT Q_DECL_EXPORT #else # define DMOUNTSHARED_EXPORT Q_DECL_IMPORT #endif #define DFMMOUNT dfmmount #define DFM_MOUNT_BEGIN_NS namespace DFMMOUNT { #define DFM_MOUNT_END_NS } #define DFM_MOUNT_USE_NS using namespace DFMMOUNT; #define DMNT_VIRTUAL #define DMNT_OVERRIDE DFM_MOUNT_BEGIN_NS #define ParamCancellable "cancellable" #define ParamMountOperation "operation" #define ParamForce "force" Q_NAMESPACE enum class DeviceType : uint16_t { kAllDevice = 0, kBlockDevice = 1, // normal block devices, like removable disks kProtocolDevice = 2, // protocol devices, like smb/mtp/ftp/ptp etc... kNetDevice = 3, }; Q_ENUM_NS(DeviceType) enum class NetworkMountPasswdSaveMode : uint8_t { kNeverSavePasswd = 0, kSaveBeforeLogout, kSavePermanently, }; Q_ENUM_NS(NetworkMountPasswdSaveMode) enum class MonitorStatus : uint16_t { kMonitoring = 0, kIdle = 1, kNotDefined = 0xff, }; Q_ENUM_NS(MonitorStatus) enum class Property : uint16_t { kNotInit = 0, // blocks property kBlockProperty, // this is invalid but just a placeholder kBlockConfiguration, // variant kBlockUserspaceMountOptions, // strlist kBlockCryptoBackingDevice, // str kBlockDevice, kBlockDrive, kBlockIDLabel, kBlockIDType, kBlockIDUsage, // uint64 kBlockIDUUID, // str kBlockIDVersion, kBlockDeviceNumber, // uint64 kBlockPreferredDevice, // str kBlockID, kBlockSize, // uint64 kBlockReadOnly, // bool kBlockSymlinks, // strlist kBlockHintPartitionable, // bool kBlockHintSystem, kBlockHintIgnore, kBlockHintAuto, kBlockHintName, // str kBlockHintIconName, kBlockHintSymbolicIconName, kBlockMdRaid, kBlockMdRaidMember, kBlockPropertyEND, kDriveProperty = 30, kDriveConnectionBus, // str kDriveRemovable, // bool kDriveEjectable, kDriveSeat, // str kDriveMedia, kDriveMediaCompatibility, // strlist kDriveMediaRemovable, // bool kDriveMediaAvailable, kDriveMediaChangeDetected, kDriveTimeDetected, // uint64 kDriveTimeMediaDetected, kDriveSize, kDriveOptical, // bool kDriveOpticalBlank, kDriveOpticalNumTracks, // uint kDriveOpticalNumAudioTracks, kDriveOpticalNumDataTracks, kDriveOpticalNumSessions, kDriveModel, // str kDriveRevision, kDriveRotationRate, // int kDriveSerial, // str kDriveVender, kDriveWWN, kDriveSortKey, kDriveConfiguration, // variant kDriveID, // str kDriveCanPowerOff, // bool kDriveSiblingID, // str kDrivePropertyEND, kFileSystemProperty, kFileSystemMountPoint, // strlist kFileSystemPropertyEND, kPartitionProperty, kPartitionNumber, // uint kPartitionType, // str kPartitionOffset, // uint64 kPartitionSize, kPartitionFlags, kPartitionName, // str kPartitionUUID, kPartitionTable, kPartitionIsContainer, // bool kPartitionIsContained, kPartitionPropertyEND, kEncryptedProperty, kEncryptedChildConfiguration, // variant kEncryptedCleartextDevice, // str kEncryptedHintEncryptionType, kEncryptedMetadataSize, // uint64 kEncryptedPropertyEnd, // protocol property kProtoProperty = 800, // this is invalid but just a placeholder }; Q_ENUM_NS(Property); #define UDISKS_ERR_DOMAIN "udisks-error-quark" #define UDISKS_ERR_START 400 #define GIO_ERR_DOMAIN "g-io-error-quark" #define GIO_ERR_START 800 #define GDBUS_ERR_DOMAIN "g-dbus-error-quark" #define GDBUS_ERR_START 1200 #define USER_ERR_START 1600 enum class DeviceError : int16_t { kNoError = 0, kUnhandledError = 10001, // < 0, daemon mount error kDaemonErrorNotSupportedScheme = -1, kDaemonErrorCannotGenerateMountPath = -2, kDaemonErrorCannotMkdirMountPoint = -3, // 0~400, system errno // ↓ these errors are transfered from udisks kUDisksErrorFailed = UDISKS_ERR_START, kUDisksErrorCancelled, kUDisksErrorAlreadyCancelled, kUDisksErrorNotAuthorized, kUDisksErrorNotAuthorizedCanObtain, kUDisksErrorNotAuthorizedDismissed, kUDisksErrorAlreadyMounted, kUDisksErrorNotMounted, kUDisksErrorOptionNotPermitted, kUDisksErrorMountedByOtherUser, kUDisksErrorAlreadyUnmounting, kUDisksErrorNotSupproted, kUDisksErrorTimedOut, kUDisksErrorWouldWakeup, kUDisksErrorDeviceBusy, kUDisksErrorScsiDaemonTransportFailed, kUDisksErrorScsiHostNotFound, kUDisksErrorScsiIDMB, kUDisksErrorScsiLoginFailed, kUDisksErrorScsiLoginAuthFailed, kUDisksErrorScsiLoginFatal, kUDisksErrorScsiLogoutFailed, kUDisksErrorScsiNoFirmware, kUDisksErrorScsiNoObjectsFound, kUDisksErrorScsiNotConnected, kUDisksErrorScsiTransportFailed, kUDisksErrorScsiUnknownDiscoveryType, // ↑ these errors are transfered from udisks kUDisksBusySMARTSelfTesting, kUDisksBusyDriveEjecting, kUDisksBusyEncryptedUnlocking, kUDisksBusyEncryptedLocking, kUDisksBusyEncryptedModifying, kUDisksBusyEncryptedResizing, kUDisksBusySwapSpaceStarting, kUDisksBusySwapSpaceStoping, kUDisksBusySwpaSpaceModifying, kUDisksBusyFileSystemUnmounting, kUDisksBusyFileSystemMounting, kUDisksBusyFileSystemModifying, kUDisksBusyFileSystemResizing, kUDisksBusyFormatErasing, kUDisksBusyFormatMkfsing, kUDisksBusyLoopSetuping, kUDisksBusyPartitionModifying, kUDisksBusyPartitionDeleting, kUDisksBusyPartitionCreating, kUDisksBusyCleanuping, kUDisksBusyATASecureErasing, kUDisksBusyATAEnhancedSecureErasing, kUDisksBusyMdRaidStarting, kUDisksBusyMdRaidStoping, kUDisksBusyMdRaidFaultingDevice, kUDisksBusyMdRaidRemovingDevice, kUDisksBusyMdRaidCreating, // these errors are from GIOErrorEnum kGIOError = GIO_ERR_START, kGIOErrorFailed = kGIOError, kGIOErrorNotFound, kGIOErrorExists, kGIOErrorIsDirectory, kGIOErrorNotDirertory, kGIOErrorNotEmpty, kGIOErrorNotRegularFile, kGIOErrorNotSymbolicLink, kGIOErrorNotMountableFile, kGIOErrorFilenameTooLong, kGIOErrorInvalidFilename, kGIOErrorTooManyLinks, kGIOErrorNoSpace, kGIOErrorInvalidArgument, kGIOErrorPermissionDenied, kGIOErrorNotSupported, kGIOErrorNotMounted, kGIOErrorAlreadyMounted, kGIOErrorClosed, kGIOErrorCancelled, kGIOErrorPending, kGIOErrorReadONly, kGIOErrorCantCreateBackup, kGIOErrorWrongETAG, kGIOErrorTimedOut, kGIOErrorWouldRecurse, kGIOErrorBusy, kGIOErrorWouldBlock, kGIOErrorHostNotFound, kGIOErrorWouldMerge, kGIOErrorFailedHandled, kGIOErrorTooManyOpenFiles, kGIOErrorNotInitilized, kGIOErrorAddressInUse, kGIOErrorPartialInput, kGIOErrorInvalidData, kGIOErrorDBusError, kGIOErrorHostUnreachable, kGIOErrorNetworkUnreachable, kGIOErrorConnectionRefused, kGIOErrorProxyFailed, kGIOErrorProxyAuthFailed, kGIOErrorProxyNeedAuth, kGIOErrorProxyNotAllowed, kGIOErrorBrokenPipe, kGIOErrorConnectionClosed = kGIOErrorBrokenPipe, kGIOErrorNotConnected, kGIOErrorMessageTooLarge, // these are from GDBusError kGDBusError = GDBUS_ERR_START, kGDBusErrorFailed = kGDBusError, kGDBusErrorNoMemory, kGDBusErrorServiceUnknown, kGDBusErrorNameHasNoOwner, kGDBusErrorNoReply, kGDBusErrorIOError, kGDBusErrorBadAddress, kGDBusErrorNotSupported, kGDBusErrorLimitsExceeded, kGDBusErrorAccessDenied, kGDBusErrorAuthFailed, kGDBusErrorNoServer, kGDBusErrorTimeout, kGDBusErrorNoNetwork, kGDBusErrorAddressInUse, kGDBusErrorDisconnected, kGDBusErrorInvalidArgs, kGDBusErrorFileNotFound, kGDBusErrorFileExists, kGDBusErrorUnknownMethod, kGDBusErrorTimedOut, kGDBusErrorMatchRuleNotFound, kGDBusErrorMatchRuleInvalid, kGDBusErrorSpawnExecFailed, kGDBusErrorSpawnForkFailed, kGDBusErrorSpawnChildExited, kGDBusErrorSpawnChildSignaled, kGDBusErrorSpawnFailed, kGDBusErrorSpawnSetupFailed, kGDBusErrorSpawnConfigInvalid, kGDBusErrorSpawnServiceInvalid, kGDBusErrorSpawnServiceNotFound, kGDBusErrorSpawnPermissionsInvalid, kGDBusErrorSpawnFileInvalid, kGDBusErrorSpawnNoMemory, kGDBusErrorUnixProcessIdUnkown, kGDBusErrorInvalidSignature, kGDBusErrorInvalidFileContent, kGDBusErrorSeLinuxSecurityContextUnknown, kGDBusErrorADTAuditDataUnknown, kGDBusErrorObjectPathInUse, kGDBusErrorUnknownObject, kGDBusErrorUnknownInterface, kGDBusErrorUnknownProperty, kGDBusErrorPropertyReadOnly, kUserError = USER_ERR_START, kUserErrorNotMountable, // ↓ these are by ourselves kUserErrorNotEjectable, kUserErrorNoDriver, kUserErrorNotEncryptable, kUserErrorNoPartition, kUserErrorNoBlock, kUserErrorNetworkWrongPasswd, kUserErrorNetworkAnonymousNotAllowed, kUserErrorUserCancelled, kUserErrorTimedOut, kUserErrorAlreadyMounted, kUserErrorNotMounted, kUserErrorNotPoweroffable, kUserErrorFailed, kUserErrorAuthenticationFailed }; Q_ENUM_NS(DeviceError) enum class MonitorError : uint16_t { kNoError = 0, kMonitorAlreadyRegistered, kMonitorNotRegister, }; Q_ENUM_NS(MonitorError) enum class PartitionType : uint16_t { kPartitionTypeNotFound = 65535, kMbrEmpty = 0x00, kMbrFAT12Type = 0x01, kMbrXENIX_root = 0x02, kMbrXENIX_usr = 0x03, kMbrFAT16_Less_32M = 0x04, kMbrExtended = 0x05, kMbrFAT16Type = 0x06, kMbrHPFS_NTFS = 0x07, kMbrAIX = 0x08, kMbrAIX_bootable = 0x09, kMbrOS2_Boot_Manager = 0x0a, kMbrWin95_FAT32 = 0x0b, kMbrWin95_FAT32_LBA = 0x0c, kMbrWin95_FAT16_LBA = 0x0e, kMbrWin95_Extended_LBA = 0x0f, kMbrOPUS = 0x10, kMbrHidden_FAT12 = 0x11, kMbrCompaq_diagnostics = 0x12, kMbrHidden_FAT16_Less_32M = 0x14, kMbrHidden_FAT16 = 0x16, kMbrHidden_HPFS_or_NTFS = 0x17, kMbrAST_SmartSleep = 0x18, kMbrHidden_Win95_FAT32 = 0x1b, kMbrHidden_Win95_FAT32_LBA = 0x1c, kMbrHidden_Win95_FAT16_LBA = 0x1e, kMbrNEC_DOS = 0x24, kMbrPlan9 = 0x39, kMbrPartitionMagic_recovery = 0x3c, kMbrVenix_80286 = 0x40, kMbrPPC_PReP_Boot = 0x41, kMbrSFS = 0x42, kMbrQNX4_dot_x = 0x4d, kMbrQNX4_dot_x_2nd_part = 0x4e, kMbrQNX4_dot_x_3rd_part = 0x4f, kMbrOnTrack_DM = 0x50, kMbrOnTrack_DM6_Aux1 = 0x51, kMbrCP_M = 0x52, kMbrOnTrack_DM6_Aux3 = 0x53, kMbrOnTrackDM6 = 0x54, kMbrEZ_Drive = 0x55, kMbrGolden_Bow = 0x56, kMbrPriam_Edisk = 0x5c, kMbrSpeedStor = 0x61, kMbrGNU_HURD_or_SysV = 0x63, kMbrNovell_Netware_286 = 0x64, kMbrNovell_Netware_386 = 0x65, kMbrDiskSecure_Multi_Boot = 0x70, kMbrPC_IX = 0x75, kMbrOld_Minix = 0x80, kMbrMinix_old_Linux = 0x81, kMbrLinux_swap = 0x82, kMbrLinux = 0x83, kMbrOS2_hidden_C_drive = 0x84, kMbrLinux_extended = 0x85, kMbrNTFS_volume_set_1 = 0x86, kMbrNTFS_volume_set_2 = 0x87, kMbrLinux_LVM = 0x8e, kMbrAmoeba = 0x93, kMbrAmoeba_BBT = 0x94, kMbrBSD_OS = 0x9f, kMbrIBM_Thinkpad_hibernation = 0xa0, kMbrFreeBSD = 0xa5, kMbrOpenBSD = 0xa6, kMbrNeXTSTEP = 0xa7, kMbrNetBSD = 0xa8, kMbrBSDI_fs = 0xa9, kMbrBSDI_swap = 0xb7, kMbrBoot_Wizard_hidden = 0xb8, kMbrDRDOS_sec_FAT12 = 0xbb, kMbrDRDOS_sec_FAT16_Less_32M = 0xc1, kMbrDRDOS_sec_FAT16 = 0xc4, kMbrDRDOS_sec_extend = 0xc5, kMbrSyrinx = 0xc6, kMbrNon_FS_data = 0xc7, kMbrMultiuser_DOS_extend = 0xd5, kMbrCP_M_CTOS_dot_dot_dot = 0xda, kMbrDell_Utility = 0xdb, kMbrBootIt = 0xde, kMbrDOS_access = 0xdf, kMbrDOS_R_O = 0xe1, kMbrSpeedStor_1 = 0xe3, kMbrBeOS_fs = 0xe4, kMbrEFI_GPT = 0xeb, kMbrEFI_FAT12_16_32 = 0xee, kMbrLinux_PA_RISC_boot = 0xef, kMbrSpeedStor_2 = 0xf0, kMbrSeppdStor_3 = 0xf4, kMbrDOS_secondary = 0xf2, kMbrLinux_raid_autodetect = 0xfd, kMbrLANstep = 0xfe, kMbrBBT = 0xff, // Gpt[Name][FileSystem] // https://en.wikipedia.org/wiki/GUID_Partition_Table kGptUnusedEntryNA, kGptMBRPartitionSchemeNA, kGptEFISystemPartitionNA, kGptBIOSBootPartitionNA, kGptIntelFastFlashPartitionNA, kGptSonyBootPartitionNA, kGptLenovoBootPartitionNA, kGptMicrosoftReservedPartitionWin, kGptBasicDataPartitionWin, kGptLogicalDiskManagerMetaDataPartitionWin, kGptLogicalDiskManagerDataPartitionWin, kGptWindowsRecoveryEnvironmentWin, kGptIBMGeneralParallelFileSystemPartitionWin, kGptStorageSpacesPartitionWin, kGptStorageReplicaPartitionWin, kGptDataPartitionHPUX, kGptServicePartitionHPUX, kGptLinuxFilesystemDataLinux, kGptRAIDPartitionLinux, kGptRootPartitionX86Linux, kGptRootPartitionX8664Linux, kGptRootPartitionArm32Linux, kGptRootPartitionArm64Linux, kGptBootPartitionLinux, kGptSwapPartitionLinux, kGptLogicalVolumeManagerPartitionLinux, kGptHomePartitionLinux, kGptServerDataPartitionLinux, kGptPlainDMCryptPartitionLinux, kGptLUKSPartitionLinux, kGptReservedLinux, kGptBootPartitionFreeBSD, kGptBSDDisklabelPartitionFreeBSD, kGptSwapPartitionFreeBSD, kGptUnixFileSystemPartitionFreeBSD, kGptVinumVolumeManagerPartitionFreeBSD, kGptZFSPartitionFreeBSD, kGptNandfsPartitionFreeBSD, kGptHierarchialFileSystemPlusPartitionMacOS, kGptAppleAPFSContainerMacOS, kGptAppleUFSContainerMacOS, kGptZFSMacOS, kGptAppleRAIDPartitionMacOS, kGptAppleRAIDPartitionOfflineMacOS, kGptAppleBootPartitionMacOS, kGptAppleLabelMacOS, kGptAppleTVRecoveryPartitionMacOS, kGptAppleCoreStorageContainerMacOS, kGptAppleAPFSPrebootPartitionMacOS, kGptAppleAPFSRecoveryPartitionMacOS, kGptBootPartitionSolaris, kGptRootPartitionSolaris, kGptSwapPartitionSolaris, kGptBackupPartitionSolaris, kGptUsrPartitionSolaris, kGptVarPartitionSolaris, kGptHomePartitionSolaris, kGptAlternateSectorSolaris, kGptReservedPartitionSolaris, kGptSwapPartitionNetBSD, kGptFFSPartitionNetBSD, kGptLFSPartitionNetBSD, kGptRAIDPartitionNetBSD, kGptConcatenatedPartitionNetBSD, kGptEncryptedPartitionNetBSD, kGptKernelChromeOS, kGptRootfsChromeOS, kGptFirmwareChromeOS, kGptFutureUseChromeOS, kGptMiniOSChromeOS, kGptHibernateChromeOS, kGptUsrPartitionCoreOS, kGptResizableRootfsCoreOS, kGptOEMCustomizationsCoreOS, kGptRootFilesystemOnRAIDCoreOS, kGptHaikuBFSHaiku, kGptBootPartitionMidnightBSD, kGptDataPartitionMidnightBSD, kGptSwapPartitionMidnightBSD, kGptUnixFileSystemPartitionMidnightBSD, kGptVinumVolumemanagerPartitionMidnightBSD, kGptZFSPartitionMidnightBSD, kGptJournalCeph, kGptDmCryptJournalCeph, kGptOSDCeph, kGptDmCryptOSDCeph, kGptDiskinCreationCeph, kGptDmCryptDiskinCreationCeph, kGptBlockCeph, kGptBlockDBCeph, kGptBlockWriteAheadlogCeph, kGptLockboxForDmCryptKeysCeph, kGptMultipathOSDCeph, kGptMultipathJournalCeph, kGptMultipathBlockCeph, kGptMultipathBlockDBCeph, kGptMultipathblockwriteAheadogCeph, kGptDmCryptBlockCeph, kGptDmCryptBlockDBCeph, kGptDmCryptBlockWriteAheadlogCeph, kGptDmCryptLUKSjournalCeph, kGptDmCryptLUKSBlockCeph, kGptDmCryptLUKSBlockDBCeph, kGptDmCryptLUKSBlockwriteAheadlogCeph, kGptDmCryptLUKSOSDCeph, kGptDataPartitionOpenBSD, kGptPowerSafeFilesystemQNX, kGptPlan9PartitionPlan9, kGptVmkCoreVMwareESX, kGptVMFSFilesystemPartitionVMwareESX, kGptVMWareReservedVMwareESX, kGptBootloaderAndroidIA, kGptBootloader2AndroidIA, kGptBootAndroidIA, kGptRecoveryAndroidIA, kGptMiscAndroidIA, kGptMetadataAndroidIA, kGptSystemAndroidIA, kGptCacheAndroidIA, kGptDataAndroidIA, kGptPersistentAndroidIA, kGptVendorAndroidIA, kGptConfigAndroidIA, kGptFactoryAndroidIA, kGptFactoryAltAndroidIA, kGptFastbootOrTertiaryAndroidIA, kGptOEMAndroidIA, kGptAndroidMetaAndroid6Arm, kGptAndroidEXTAndroid6Arm, kGptBootONIE, kGptConfigONIE, kGptPRePBootPowerPC, kGptSharedBootloaderConfigurationFreedesktop, kGptBasicDataPartitionAtariTOS, kGptEncryptedDataPartitionVeraCrypt, kGptArcaOSType1OS2, kGptSPDKBlockDeviceSPDK, kGptBareBoxStateBareboxBootloader, kGptUBootEnvironmentUBootBootloader, kGptStatusSoftRAID, kGptScratchSoftRAID, kGptVolumeSoftRAID, kGptCacheSoftRAID, kGptBootloaderFuchsiaStandard, kGptDurablemutableencryptedsystemdataFuchsiaStandard, kGptDurablemutablebootloaderdataFuchsiaStandard, kGptFactoryProvisionedreadOnlysystemdataFuchsiaStandard, kGptFactoryProvisionedreadOnlybootloaderdataFuchsiaStandard, kGptFuchsiaVolumeManagerFuchsiaStandard, kGptVerifiedbootmetadataFuchsiaStandard, kGptZirconbootimageFuchsiaStandard, kGptFuchsiaEspFuchsiaLegacy, kGptFuchsiaSystemFuchsiaLegacy, kGptFuchsiaDataFuchsiaLegacy, kGptFuchsiaInstallFuchsiaLegacy, kGptFuchsiaBlobFuchsiaLegacy, kGptFuchsiaFvmFuchsiaLegacy, kGptZirconbootimageSlotAFuchsiaLegacy, kGptZirconbootimageSlotBFuchsiaLegacy, kGptZirconbootimageSlotRFuchsiaLegacy, kGptSysConfigFuchsiaLegacy, kGptFactoryConfigFuchsiaLegacy, kGptBootloaderFuchsiaLegacy, kGptGuidTestFuchsiaLegacy, kGptVerifiedbootmetadataSlotAFuchsiaLegacy, kGptVerifiedbootmetadataSlotBFuchsiaLegacy, kGptVerifiedbootmetadataSlotRFuchsiaLegacy, kGptMiscFuchsiaLegacy, kGptEmmcBoot1FuchsiaLegacy, kGptEmmcBoot2FuchsiaLegacy, }; Q_ENUM_NS(PartitionType) struct OperationErrorInfo { DeviceError code { DeviceError::kNoError }; QString message; }; using DeviceOperateCallback = std::function; using DeviceOperateCallbackWithMessage = std::function; DFM_MOUNT_END_NS #endif // DMOUNT_GLOBAL_H linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/base/dmountutils.h000066400000000000000000000050301523477242100270220ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DMOUNTUTILS_H #define DMOUNTUTILS_H #include #include #include #include typedef struct _GVariant GVariant; typedef struct _GError GError; DFM_MOUNT_BEGIN_NS #define warningIfNotInMain() \ { \ if (qApp->thread() != QThread::currentThread()) \ qWarning() << "<" << __PRETTY_FUNCTION__ << ">\n" \ << "\t:( this function DOES NOT promise thread safe! please use it CAUTION or use *Async instead."; \ } class Utils { public: static QVariant castFromGVariant(GVariant *val); static GVariant *castFromQVariant(const QVariant &val); static GVariant *castFromQVariantMap(const QVariantMap &val); static GVariant *castFromQStringList(const QStringList &val); static GVariant *castFromList(const QList &val); static QString getNameByProperty(Property type); static Property getPropertyByName(const QString &name, const QString &iface); static PartitionType getPartitionTypeByGuid(const QString &guid); static DeviceError castFromGError(const GError *const err); static DeviceError castFromJobOperation(const QString &op); static QString errorMessage(DeviceError err); static QString errorMessage(MonitorError err); static OperationErrorInfo genOperateErrorInfo(DeviceError err, const QString &errMsg = ""); /*! * \brief gcharToQString * \param tmp: target char *, which will be free in function, use it caution * \return */ static QString gcharToQString(char *tmp); /*! * \brief gcharvToQStringList * \param tmp: target char *, which will be free in function, use it caution * \return */ static QStringList gcharvToQStringList(char **tmp); static QVariant gvariantToQVariant(GVariant *value); template static inline ToClass *castClassFromTo(FromClass *p) { auto pPointer = dynamic_cast(p); return pPointer; } static QString currentUser(); }; DFM_MOUNT_END_NS #endif linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/dblockdevice.h000066400000000000000000000041531523477242100261440ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DBLOCKDEVICE_H #define DBLOCKDEVICE_H #include #include #include typedef struct _UDisksClient UDisksClient; DFM_MOUNT_BEGIN_NS class DBlockDevicePrivate; class DBlockDevice final : public DDevice { Q_OBJECT friend class DBlockMonitorPrivate; public: DBlockDevice() = delete; DBlockDevice(const DBlockDevice &) = delete; ~DBlockDevice(); private: DBlockDevice(UDisksClient *cli, const QString &udisksObjPath, QObject *parent = nullptr); public: bool eject(const QVariantMap &opts = {}); void ejectAsync(const QVariantMap &opts = {}, DeviceOperateCallback cb = nullptr); bool powerOff(const QVariantMap &opts = {}); void powerOffAsync(const QVariantMap &opts = {}, DeviceOperateCallback cb = nullptr); bool lock(const QVariantMap &opts = {}); void lockAsync(const QVariantMap &opts = {}, DeviceOperateCallback cb = nullptr); bool unlock(const QString &passwd, QString &clearTextDev, const QVariantMap &opts = {}); void unlockAsync(const QString &passwd, const QVariantMap &opts = {}, DeviceOperateCallbackWithMessage cb = nullptr); bool rescan(const QVariantMap &opts = {}); void rescanAsync(const QVariantMap &opts = {}, DeviceOperateCallback cb = nullptr); // these are convinience methods QStringList mountPoints() const; QString device() const; QString drive() const; QString idLabel() const; QStringList mediaCompatibility() const; bool removable() const; bool optical() const; bool opticalBlank() const; bool canPowerOff() const; bool ejectable() const; bool isEncrypted() const; bool hasFileSystem() const; bool hasPartitionTable() const; bool hasPartition() const; bool isLoopDevice() const; bool hasBlock() const; bool hintIgnore() const; bool hintSystem() const; PartitionType partitionEType() const; QString partitionType() const; }; DFM_MOUNT_END_NS #endif // DBLOCKDEVICE_H linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/dblockmonitor.h000066400000000000000000000020471523477242100263740ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DBLOCKMONITOR_H #define DBLOCKMONITOR_H #include #include DFM_MOUNT_BEGIN_NS class DBlockMonitorPrivate; class DBlockMonitor final : public DDeviceMonitor { Q_OBJECT public: DBlockMonitor(QObject *parent = nullptr); ~DBlockMonitor(); QStringList resolveDevice(const QVariantMap &devspec, const QVariantMap &opts); QStringList resolveDeviceNode(const QString &node, const QVariantMap &opts); QStringList resolveDeviceFromDrive(const QString &drvObjPath); Q_SIGNALS: void driveAdded(const QString &drvObjPath); void driveRemoved(const QString &drvObjPath); void fileSystemAdded(const QString &blkObjPath); void fileSystemRemoved(const QString &blkObjPath); void blockLocked(const QString &blkObjPath); void blockUnlocked(const QString &blkObjPath, const QString &clearDevObjPath); }; DFM_MOUNT_END_NS #endif // DBLOCKMONITOR_H linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/ddevicemanager.h000066400000000000000000000030511523477242100264600ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2019 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DDEVICEMANAGER_H #define DDEVICEMANAGER_H #include #include DFM_MOUNT_BEGIN_NS class DDeviceMonitor; class DDevice; class DDeviceManagerPrivate; class DDeviceManager final : public QObject { Q_OBJECT public: static DDeviceManager *instance(); template bool registerMonitor(ConstructArgs &&...); QSharedPointer getRegisteredMonitor(DeviceType type) const; bool startMonitorWatch(); bool stopMonitorWatch(); MonitorError lastError() const; QMap devices(DeviceType type = DeviceType::kAllDevice); Q_SIGNALS: // these signals are transferred from monitors, you can use them or connect directly from monitors, // but if you want to cannect some special signals, find them in specific monitor. void deviceAdded(const QString &deviceKey, DeviceType type); void deviceRemoved(const QString &deviceKey, DeviceType type); void mounted(const QString &deviceKey, const QString &mountPoint, DeviceType type); void unmounted(const QString &deviceKey, DeviceType type); void propertyChanged(const QString &deviceKey, const QMap &changes, DeviceType type); private: DDeviceManager(QObject *parent = nullptr); ~DDeviceManager(); QScopedPointer d; }; DFM_MOUNT_END_NS #endif // DDEVICEMANAGER_H linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/dmount.h000066400000000000000000000007111523477242100250300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DMOUNT_H #define DMOUNT_H #include #include #include #include #include #include #include #endif // DMOUNT_H linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/dprotocoldevice.h000066400000000000000000000031701523477242100267110ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DPROTOCOLDEVICE_H #define DPROTOCOLDEVICE_H #include #include #include typedef struct _GVolumeMonitor GVolumeMonitor; DFM_MOUNT_BEGIN_NS struct MountPassInfo { QString userName {}; QString passwd {}; QString domain {}; bool anonymous { false }; bool cancelled { false }; int timeout = 0; NetworkMountPasswdSaveMode savePasswd { NetworkMountPasswdSaveMode::kNeverSavePasswd }; }; using GetMountPassInfo = std::function; using GetUserChoice = std::function; class DProtocolDevicePrivate; class DProtocolDevice final : public DDevice { Q_OBJECT friend class DProtocolMonitorPrivate; public: ~DProtocolDevice(); QStringList deviceIcons() const; static void mountNetworkDevice(const QString &address, GetMountPassInfo getPassInfo, GetUserChoice getUserChoice, DeviceOperateCallbackWithMessage mountResult, int secs = 0); static bool isMountByDaemon(const QString &address); void setOperatorTimeout(int msecs); private: DProtocolDevice(const QString &id, GVolumeMonitor *monitor, QObject *parent = nullptr); private Q_SLOTS: void mounted(const QString &id); void unmounted(const QString &id); }; DFM_MOUNT_END_NS #endif // DPROTOCOLDEVICE_H linuxdeepin-util-dfm-a4307d3/include/dfm-mount/dfm-mount/dprotocolmonitor.h000066400000000000000000000007531523477242100271450ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DPROTOCOLMONITOR_H #define DPROTOCOLMONITOR_H #include #include DFM_MOUNT_BEGIN_NS class DProtocolMonitorPrivate; class DProtocolMonitor : public DDeviceMonitor { Q_OBJECT public: DProtocolMonitor(QObject *parent = nullptr); ~DProtocolMonitor(); }; DFM_MOUNT_END_NS #endif // DBLOCKMONITOR_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/000077500000000000000000000000001523477242100215475ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/000077500000000000000000000000001523477242100235605ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/contentretriever.h000066400000000000000000000163031523477242100273360ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CONTENTRETRIEVER_H #define CONTENTRETRIEVER_H #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS class HighlightOptionsPrivate; class PreviewOptionsPrivate; class PreviewResultPrivate; /** * @brief Lightweight options for highlight extraction (Pimpl-based, ABI-stable) * * New fields can be added to HighlightOptionsPrivate without breaking ABI, * because the public class layout is fixed at one pointer width. * * Supports implicit sharing (COW) via QSharedDataPointer. */ class HighlightOptions { public: HighlightOptions(); ~HighlightOptions(); HighlightOptions(const HighlightOptions &other); HighlightOptions &operator=(const HighlightOptions &other); HighlightOptions(HighlightOptions &&other) noexcept = default; HighlightOptions &operator=(HighlightOptions &&other) noexcept = default; int maxPreviewLength() const; void setMaxPreviewLength(int length); int positioningMaxLength() const; void setPositioningMaxLength(int length); bool enableHtml() const; void setEnableHtml(bool enable); private: QSharedDataPointer d; }; /** * @brief Options for precise preview extraction (Pimpl-based, ABI-stable) * * Controls offset-based content reading: either a raw slice from offset * (no keyword) or a keyword search starting from offset. * * Supports implicit sharing (COW) via QSharedDataPointer. */ class PreviewOptions { public: PreviewOptions(); ~PreviewOptions(); PreviewOptions(const PreviewOptions &other); PreviewOptions &operator=(const PreviewOptions &other); PreviewOptions(PreviewOptions &&other) noexcept = default; PreviewOptions &operator=(PreviewOptions &&other) noexcept = default; int offset() const; void setOffset(int offset); int maxLength() const; void setMaxLength(int length); QString keyword() const; void setKeyword(const QString &keyword); private: QSharedDataPointer d; }; enum class PreviewStatus { Success = 0, NotIndexed = 1, }; /** * @brief Result of a preview extraction (Pimpl-based, ABI-stable) * * Contains the extraction status, content snippet, and the position of the * keyword in the full document text (-1 if no keyword or not matched). * * Supports implicit sharing (COW) via QSharedDataPointer. */ class PreviewResult { public: PreviewResult(); ~PreviewResult(); PreviewResult(const PreviewResult &other); PreviewResult &operator=(const PreviewResult &other); PreviewResult(PreviewResult &&other) noexcept = default; PreviewResult &operator=(PreviewResult &&other) noexcept = default; PreviewStatus status() const; QString content() const; int charCount() const; int keywordOffset() const; private: friend class ContentRetriever; QSharedDataPointer d; }; /** * @brief Retrieves highlighted content from Lucene index on demand * * Provides a standalone mechanism to fetch highlighted content snippets * for specific file paths without running a full search pipeline. * * Typical usage pattern: * 1. Perform a search with isFullTextRetrievalEnabled() = false (fast) * 2. Display path-only results to the user immediately * 3. On demand (e.g., scroll into view), call fetchHighlight() per path * * This decouples highlight extraction from the search pipeline, * enabling lazy-loading similar to thumbnail fetching. */ class ContentRetriever : public QObject { Q_OBJECT public: explicit ContentRetriever(QObject *parent = nullptr); ~ContentRetriever() override; /** * @brief Override the Lucene index directory for a given text search type. * * When @p indexDirectory is empty, the default global index directory for * the given type will be used. This is primarily useful for tests or * isolated business scenarios that need to point at a temporary index. */ void setIndexDirectory(SearchType type, const QString &indexDirectory); /** * @brief Return the effective index directory for the given text search type. */ QString indexDirectory(SearchType type) const; /** * @brief Synchronously fetch highlighted content for a single file * * Opens the Lucene index, locates the document by path, * extracts stored text, and runs ContentHighlighter to produce * a highlighted snippet. * * @param path Absolute file path * @param keyword Search keyword (supports comma-separated for multi-keyword) * @param type SearchType::Content or SearchType::Ocr * @param options Highlight configuration (preview length, HTML toggle) * @return Highlighted snippet, or empty string if not found */ QString fetchHighlight(const QString &path, const QString &keyword, SearchType type, const HighlightOptions &options = {}) const; /** * @brief Synchronously fetch highlights for multiple files * @return Mapping of path -> highlighted content (empty string if not found) */ QMap fetchHighlights(const QStringList &paths, const QString &keyword, SearchType type, const HighlightOptions &options = {}) const; /** * @brief Synchronously fetch full stored content for a single file * * Opens the Lucene index, locates the document by path, * and returns the full stored content field. * * @param path Absolute file path * @param type SearchType::Content or SearchType::Ocr * @return Full content text, or empty string if not found */ QString fetchContent(const QString &path, SearchType type) const; /** * @brief Synchronously fetch full stored contents for multiple files * @return Mapping of path -> full content (empty string if not found) */ QMap fetchContents(const QStringList &paths, SearchType type) const; /** * @brief Synchronously fetch a precise preview snippet for a single file * * Opens the Lucene index, locates the document by path, extracts stored * text, and runs ContentHighlighter::previewSnippet to produce a raw * content slice based on offset/maxLength/keyword. * * Unlike fetchHighlight, this does NOT simplify, add ellipsis, or * highlight — it returns the exact content at the requested position. * * @param path Absolute file path * @param type SearchType::Content or SearchType::Ocr * @param options Preview configuration (offset, maxLength, keyword) * @return PreviewResult with content snippet and keywordOffset */ PreviewResult fetchPreview(const QString &path, SearchType type, const PreviewOptions &options = {}) const; private: struct Private; std::unique_ptr d; }; DFM_SEARCH_END_NS #endif // CONTENTRETRIEVER_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/contentsearchapi.h000066400000000000000000000050741523477242100272710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CONTENTSEARCHAPI_H #define CONTENTSEARCHAPI_H #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief The ContentOptionsAPI class provides content search specific options * * This class extends TextSearchOptionsAPI with content search specific settings. */ class ContentOptionsAPI : public TextSearchOptionsAPI { public: /** * @brief Constructor * @param options The SearchOptions object to operate on */ explicit ContentOptionsAPI(SearchOptions &options); /** * @brief Sets whether the extended AND search behavior across 'contents' and 'filename' fields is enabled. * @param enabled True to enable the feature, false to disable it. * @see isFilenameContentMixedAndSearchEnabled() for a detailed description of the behavior. */ void setFilenameContentMixedAndSearchEnabled(bool enabled); /** * @brief Checks if the extended AND search behavior across 'contents' and 'filename' fields is enabled. * * When enabled (returns true), boolean AND queries will search for terms such that: * 1. All terms must be present, potentially distributed between the 'contents' and 'filename' fields. * (e.g., termA in 'contents', termB in 'filename'). * 2. A match is explicitly excluded if all search terms are found *only* within the 'filename' field. * (e.g., termA in 'filename', termB in 'filename' -- this specific case is excluded). * 3. Matches where all terms are in 'contents', or mixed between 'contents' and 'filename' (as in point 1), are included. * * If this option is disabled (returns false), or for boolean OR queries, * the boolean search will be performed exclusively on the 'contents' field, following the original logic. * * @return True if the filename-content mixed AND search is enabled, false otherwise. */ bool isFilenameContentMixedAndSearchEnabled() const; }; /** * @brief The ContentResultAPI class provides content search specific result handling * * This class extends TextSearchResultAPI for content search results. */ class ContentResultAPI : public TextSearchResultAPI { public: /** * @brief Constructor * @param result The SearchResult object to operate on */ explicit ContentResultAPI(SearchResult &result); }; DFM_SEARCH_END_NS #endif // CONTENTSEARCHAPI_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/dimensionextractor.h000066400000000000000000000014111523477242100276470ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DIMENSIONEXTRACTOR_H #define DIMENSIONEXTRACTOR_H #include DFM_SEARCH_BEGIN_NS class DimensionExtractor { public: virtual ~DimensionExtractor() = default; /** * @brief Extract a dimension from the input text and populate the intent. * @param input The raw natural language input * @param intent The intent to populate with extracted data */ virtual void extract(const QString &input, ParsedIntent &intent) = 0; /** * @brief Get the name of this extractor for debugging. */ virtual QString name() const = 0; }; DFM_SEARCH_END_NS #endif // DIMENSIONEXTRACTOR_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/dsearch_global.h000066400000000000000000000231361523477242100266670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DSEARCH_GLOBAL_H #define DSEARCH_GLOBAL_H #include #include #include #define DFMSEARCH dfmsearch #define DFM_SEARCH_BEGIN_NS namespace DFMSEARCH { #define DFM_SEARCH_END_NS } #define DFM_SEARCH_USE_NS using namespace DFMSEARCH; DFM_SEARCH_BEGIN_NS Q_NAMESPACE namespace Global { inline constexpr int kMinContentSearchKeywordLength = 2; /** * @brief Check if a string contains only pinyin characters * @param input The string to check * @return true if the string contains only pinyin, false otherwise */ bool isPinyinSequence(const QString &input); /** * @brief Check if a string is a valid pinyin acronym sequence * @param input The string to check * @return true if the string is a valid pinyin acronym, false otherwise */ bool isPinyinAcronymSequence(const QString &input); /** * Determines if the given absolute path represents a hidden file/directory * or is contained within a hidden directory. * * A path is considered hidden if any component (except root) starts with '.' * and is not the special "." (current) or ".." (parent) directory. * * @param absolutePath The absolute filesystem path to check (must be normalized) * @return true if the path is hidden or inside hidden directories, false otherwise */ bool isHiddenPathOrInHiddenDir(const QString &absolutePath); /** * @brief Check if the given file extension is supported for full-text search. * This function checks if the specified file extension is included in the list of supported extensions * for performing full-text searches within the application. * @param suffix The file extension to check. * @return True if the extension is supported, false otherwise. */ bool isSupportedContentSearchExtension(const QString &suffix); /** * @brief Get a list of default file extensions that are supported for full-text search. * This function returns a list of file extensions that the application can process for full-text search, * ensuring that users can search through various document types. * @return A QStringList containing the supported file extensions. */ QStringList defaultContentSearchExtensions(); /** * @brief Gets the list of default indexed directories with deduplicated parent/child paths. * * If the list contains both a parent directory (e.g. "/home") and its child ("/home/test"), * only the parent directory is kept. Paths are normalized using QDir::cleanPath. * * @return QStringList Deduplicated directory list (sorted alphabetically) */ QStringList defaultIndexedDirectory(); /** * @brief Get the list of blacklist paths from DConfig. * This function reads the blacklist_paths configuration from the anything DConfig * and returns it as-is without additional processing. * @return A QStringList containing the blacklist paths, or empty list if reading fails. */ QStringList defaultBlacklistPaths(); /** * @brief Check if the specified path is within the content index directory. * This function verifies whether a given file path is located within the designated content index directory, * which is important for ensuring that only relevant files are included in search operations. * @param path The file path to check. * @return True if the path is within the content index directory, false otherwise. */ bool isPathInContentIndexDirectory(const QString &path); /** * @brief Check if the content index is available. * This function checks the status of the content index to determine if it is accessible and ready for search operations. * @return True if the content index is available, false otherwise. */ bool isContentIndexAvailable(); /** * @brief Get the content index directory path. * This function provides the path to the directory where the content index is stored, * which is essential for performing searches on indexed content. * @return The path to the content index directory. */ QString contentIndexDirectory(); /** * @brief Check if the specified path is within the OCR text index directory. * This function verifies whether a given file path is located within the designated OCR text index directory, * which is important for ensuring that only relevant files are included in OCR text search operations. * @param path The file path to check. * @return True if the path is within the OCR text index directory, false otherwise. */ bool isPathInOcrTextIndexDirectory(const QString &path); /** * @brief Check if the OCR text index is available. * This function checks the status of the OCR text index to determine if it is accessible and ready for search operations. * @return True if the OCR text index is available, false otherwise. */ bool isOcrTextIndexAvailable(); /** * @brief Get the OCR text index directory path. * This function provides the path to the directory where the OCR text index is stored, * which is essential for performing searches on indexed OCR-extracted text from images. * @return The path to the OCR text index directory. */ QString ocrTextIndexDirectory(); /** * @brief Check if the specified path is within the filename index directory. * This function verifies whether a given file path is located within the designated filename index directory, * ensuring that only relevant files are included in filename search operations. * @param path The file path to check. * @return True if the path is within the filename index directory, false otherwise. */ bool isPathInFileNameIndexDirectory(const QString &path); /** * @brief Check if the filename index directory is available. * This function checks if the filename index directory physically exists. * @return True if the filename index directory is available, false otherwise. */ bool isFileNameIndexDirectoryAvailable(); /** * @brief Check if the filename index is ready for search operations. * This function checks both the physical existence of the index and its status to ensure * it's in the "monitoring" state and ready for search operations. * @return True if the filename index is ready for search, false otherwise. */ bool isFileNameIndexReadyForSearch(); /** * @brief Returns the current indexing status of the file name database. * * Possible status values: * - "loading" : Initial loading of existing index * - "scanning" : Actively scanning filesystem for changes * - "monitoring" : Scan complete, now watching for filesystem events * - "closed" : Normal shutdown state (anything terminated properly) * * @return QString The current status string (lowercase) */ std::optional fileNameIndexStatus(); /** * @brief Get the filename index directory path. * This function provides the path to the directory where the filename index is stored, * which is essential for performing searches on indexed filenames. * @return The path to the filename index directory. */ QString fileNameIndexDirectory(); /** * @brief Get the version of the filename index from the JSON configuration file. * This function reads the version field from the filename index JSON file and returns it as an integer. * @return The version number of the filename index, or -1 if the version cannot be retrieved. */ int fileNameIndexVersion(); /** * @brief Get the version of the content index from the JSON configuration file. * This function reads the version field from the content index JSON file and returns it as an integer. * @return The version number of the content index, or -1 if the version cannot be retrieved. */ int contentIndexVersion(); /** * @brief Get the version of the OCR text index from the JSON configuration file. * This function reads the version field from the OCR text index JSON file and returns it as an integer. * @return The version number of the OCR text index, or -1 if the version cannot be retrieved. */ int ocrTextIndexVersion(); } // namespace Global // Enumeration for different types of search methods enum SearchType { FileName, // Search by file name Content, // Search by content within files Ocr, // Search by OCR-extracted text from images Recent = 10, // Search recently-used files (DBus RecentManager data source) Semantic = 40, // Semantic / natural-language search (launches sub-engines internally) Custom = 50 // User-defined search type }; Q_ENUM_NS(SearchType) // Enumeration for the status of the search operation enum SearchStatus { Ready, // The search engine is ready to perform a search Searching, // The search operation is currently in progress Finished, // The search operation has completed Cancelled, // The search operation has been cancelled Error // An error occurred during the search operation // Pause ? // Optional: Indicates if the search can be paused }; Q_ENUM_NS(SearchStatus) // Enumeration for the method of searching enum SearchMethod { Indexed, // Search using pre-built indexes for faster results Realtime // Search the file system in real-time for the most current results }; Q_ENUM_NS(SearchMethod) // Enumeration for time field type enum class TimeField { Unspecified, // No specific time field (search both BirthTime and ModifyTime) BirthTime, // File creation time ModifyTime, // File modification time Both // Search both BirthTime and ModifyTime (union of results) }; Q_ENUM_NS(TimeField) // Enumeration for time unit enum class TimeUnit { Minutes, // Minute unit Hours, // Hour unit Days, // Day unit Weeks, // Week unit Months, // Month unit Years // Year unit }; Q_ENUM_NS(TimeUnit) DFM_SEARCH_END_NS Q_DECLARE_METATYPE(DFMSEARCH::SearchType); #endif // DSEARCH_GLOBAL_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/field_names.h000066400000000000000000000044051523477242100262020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include #include DFM_SEARCH_BEGIN_NS namespace LuceneFieldNames { // File name index field names namespace FileName { constexpr const wchar_t kFileType[] = L"file_type"; constexpr const wchar_t kFileExt[] = L"file_ext"; constexpr const wchar_t kFileName[] = L"file_name"; constexpr const wchar_t kFileNameLower[] = L"file_name_lower"; constexpr const wchar_t kFullPath[] = L"full_path"; constexpr const wchar_t kIsHidden[] = L"is_hidden"; constexpr const wchar_t kModifyTime[] = L"modify_time"; constexpr const wchar_t kBirthTime[] = L"birth_time"; constexpr const wchar_t kFileSize[] = L"file_size"; constexpr const wchar_t kFileSizeStr[] = L"file_size_str"; constexpr const wchar_t kPinyin[] = L"pinyin"; constexpr const wchar_t kPinyinAcronym[] = L"pinyin_acronym"; constexpr const wchar_t kAncestorPaths[] = L"ancestor_paths"; } // namespace FileName // Content index field names namespace Content { constexpr const wchar_t kContents[] = L"contents"; constexpr const wchar_t kFileExt[] = L"file_ext"; constexpr const wchar_t kFilename[] = L"filename"; constexpr const wchar_t kPath[] = L"path"; constexpr const wchar_t kIsHidden[] = L"is_hidden"; constexpr const wchar_t kAncestorPaths[] = L"ancestor_paths"; constexpr const wchar_t kBirthTime[] = L"birth_time"; constexpr const wchar_t kModifyTime[] = L"modify_time"; constexpr const wchar_t kFileSize[] = L"file_size"; constexpr const wchar_t kCheckSum[] = L"checksum"; } // namespace Content // OCR text index field names namespace OcrText { constexpr const wchar_t kOcrContents[] = L"ocr_contents"; constexpr const wchar_t kFileExt[] = L"file_ext"; constexpr const wchar_t kFilename[] = L"filename"; constexpr const wchar_t kPath[] = L"path"; constexpr const wchar_t kIsHidden[] = L"is_hidden"; constexpr const wchar_t kAncestorPaths[] = L"ancestor_paths"; constexpr const wchar_t kBirthTime[] = L"birth_time"; constexpr const wchar_t kModifyTime[] = L"modify_time"; constexpr const wchar_t kFileSize[] = L"file_size"; constexpr const wchar_t kCheckSum[] = L"checksum"; } // namespace OcrText } // namespace LuceneFieldNames DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/filenamesearchapi.h000066400000000000000000000155501523477242100273770ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef FILENAMESEARCHAPI_H #define FILENAMESEARCHAPI_H #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief The FileNameOptionsAPI class provides file name search specific options * * This class extends the base SearchOptions with file name search specific settings, * such as pinyin search and file type filtering. */ class FileNameOptionsAPI { public: /** * @brief Constructor * @param options The SearchOptions object to operate on */ explicit FileNameOptionsAPI(SearchOptions &options); /** * @brief Enable or disable pinyin search * @param enabled true to enable pinyin search, false to disable */ void setPinyinEnabled(bool enabled); /** * @brief Check if pinyin search is enabled * @return true if pinyin search is enabled, false otherwise */ bool pinyinEnabled() const; /** * @brief Enable or disable pinyin acronym search * @param enabled true to enable pinyin acronym search, false to disable */ void setPinyinAcronymEnabled(bool enabled); /** * @brief Check if pinyin acronym search is enabled * @return true if pinyin acronym search is enabled, false otherwise */ bool pinyinAcronymEnabled() const; /** * @brief Set file type filters * @param types List of file types to include in search * * Supported file types: * - app: Application files * - archive: Archive files * - audio: Audio files * - doc: Document files * - pic: Picture files * - video: Video files * - dir: folder * - other: other files */ void setFileTypes(const QStringList &types); /** * @brief Get the current file type filters * @return List of file types */ QStringList fileTypes() const; /** * @brief Set file extension filters * @param extensions List of file extensions to include in search * * Example: "txt", "pdf", "docx" */ void setFileExtensions(const QStringList &extensions); /** * @brief Get the current file extension filters * @return List of file extensions */ QStringList fileExtensions() const; private: SearchOptions &m_options; }; /** * @brief The FileNameResultAPI class provides file name search specific result handling * * This class extends the base SearchResult with file name search specific features, * such as file size, modification time, and file type information. * * When detailed results are enabled, this API provides access to all indexed fields * including filename, extension, hidden status, and both modification and birth times. */ class FileNameResultAPI { public: /** * @brief Constructor * @param result The SearchResult object to operate on */ explicit FileNameResultAPI(SearchResult &result); // ==================== Basic Attributes ==================== /** * @brief Get the file size * @return The file size as formatted string */ QString size() const; /** * @brief Set the file size * @param size The file size as formatted string */ void setSize(const QString &size); /** * @brief Get the file modification time (legacy, use modifyTimeString instead) * @return The modification time as formatted string * @deprecated Use modifyTimeString() for formatted string or modifyTimestamp() for raw timestamp */ QString modifiedTime() const; /** * @brief Set the file modification time (legacy) * @param time The modification time as formatted string * @deprecated Use setModifyTimestamp() instead */ void setModifiedTime(const QString &time); /** * @brief Check if the result is a directory * @return true if the result is a directory, false otherwise */ bool isDirectory() const; /** * @brief Set whether the result is a directory * @param isDir true if the result is a directory, false otherwise */ void setIsDirectory(bool isDir); /** * @brief Get the file type * @return The file type as string */ QString fileType() const; /** * @brief Set the file type * @param type The file type to set */ void setFileType(const QString &type) const; // ==================== Extended Attributes ==================== /** * @brief Get the file name (without path) * @return The file name */ QString filename() const; /** * @brief Set the file name * @param name The file name to set */ void setFilename(const QString &name); /** * @brief Get the file extension * @return The file extension (lowercase, without dot) */ QString fileExtension() const; /** * @brief Set the file extension * @param ext The file extension to set */ void setFileExtension(const QString &ext); /** * @brief Check if the file is hidden * @return true if the file is hidden, false otherwise */ bool isHidden() const; /** * @brief Set whether the file is hidden * @param hidden true if the file is hidden, false otherwise */ void setIsHidden(bool hidden); // ==================== Modification Time ==================== /** * @brief Set the modification time timestamp * @param timestamp Unix timestamp in seconds */ void setModifyTimestamp(qint64 timestamp); /** * @brief Get the modification time timestamp * @return Unix timestamp in seconds, 0 if not set */ qint64 modifyTimestamp() const; /** * @brief Get the modification time as a formatted string * @return Formatted time string (yyyy-MM-dd HH:mm:ss) */ QString modifyTimeString() const; // ==================== Birth/Creation Time ==================== /** * @brief Set the birth/creation time timestamp * @param timestamp Unix timestamp in seconds */ void setBirthTimestamp(qint64 timestamp); /** * @brief Get the birth/creation time timestamp * @return Unix timestamp in seconds, 0 if not set */ qint64 birthTimestamp() const; /** * @brief Get the birth/creation time as a formatted string * @return Formatted time string (yyyy-MM-dd HH:mm:ss) */ QString birthTimeString() const; // ==================== File Size (Numeric) ==================== /** * @brief Set the file size in bytes * @param bytes File size in bytes */ void setFileSizeBytes(qint64 bytes); /** * @brief Get the file size in bytes * @return File size in bytes, 0 if not set */ qint64 fileSizeBytes() const; private: SearchResult &m_result; }; DFM_SEARCH_END_NS #endif // FILENAMESEARCHAPI_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/ocrtextsearchapi.h000066400000000000000000000062741523477242100273120ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef OCRTEXTSEARCHAPI_H #define OCRTEXTSEARCHAPI_H #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief The OcrTextOptionsAPI class provides OCR text search specific options * * This class extends TextSearchOptionsAPI with OCR text search specific settings. */ class OcrTextOptionsAPI : public TextSearchOptionsAPI { public: /** * @brief Constructor * @param options The SearchOptions object to operate on */ explicit OcrTextOptionsAPI(SearchOptions &options); /** * @brief Sets whether the extended AND search behavior across 'ocr_contents' and 'filename' fields is enabled. * @param enabled True to enable the feature, false to disable it. * @see isFilenameOcrContentMixedAndSearchEnabled() for a detailed description of the behavior. */ void setFilenameOcrContentMixedAndSearchEnabled(bool enabled); /** * @brief Checks if the extended AND search behavior across 'ocr_contents' and 'filename' fields is enabled. * * When enabled (returns true), boolean AND queries will search for terms such that: * 1. All terms must be present, potentially distributed between the 'ocr_contents' and 'filename' fields. * (e.g., termA in 'ocr_contents', termB in 'filename'). * 2. A match is explicitly excluded if all search terms are found *only* within the 'filename' field. * (e.g., termA in 'filename', termB in 'filename' -- this specific case is excluded). * 3. Matches where all terms are in 'ocr_contents', or mixed between 'ocr_contents' and 'filename' (as in point 1), are included. * * If this option is disabled (returns false), or for boolean OR queries, * the boolean search will be performed exclusively on the 'ocr_contents' field, following the original logic. * * @return True if the filename-OCR content mixed AND search is enabled, false otherwise. */ bool isFilenameOcrContentMixedAndSearchEnabled() const; }; /** * @brief The OcrTextResultAPI class provides OCR text search specific result handling * * This class extends TextSearchResultAPI for OCR text search results. */ class OcrTextResultAPI : public TextSearchResultAPI { public: /** * @brief Constructor * @param result The SearchResult object to operate on */ explicit OcrTextResultAPI(SearchResult &result); /** * @brief Get the OCR extracted text content * @return The OCR extracted text as QString */ QString ocrContent() const; /** * @brief Set the OCR extracted text content * @param content The OCR extracted text to set */ void setOcrContent(const QString &content); /** * @brief Get the file checksum * @return The checksum string, or empty if not set */ QString checksum() const; /** * @brief Set the file checksum * @param checksum The checksum string to set */ void setChecksum(const QString &checksum); }; DFM_SEARCH_END_NS #endif // OCRTEXTSEARCHAPI_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/searchengine.h000066400000000000000000000100601523477242100263610ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEARCHENGINE_H #define SEARCHENGINE_H #include #include #include #include #include DFM_SEARCH_BEGIN_NS class AbstractSearchEngine; /** * @brief The SearchEngine class provides a unified interface for file searching * * This class serves as the main entry point for all search operations. It uses * the factory pattern to create specific search engine implementations based on * the search type. The class manages the search lifecycle and provides both * synchronous and asynchronous search capabilities. */ class SearchEngine : public QObject { Q_OBJECT public: /** * @brief Callback function type for search results * @param result The search result to process * @return bool If returns `true`, the search iteration will be interrupted */ using ResultCallback = std::function; /** * @brief Creates a search engine of the specified type * @param type The type of search engine to create * @param parent The parent QObject * @return A pointer to the created SearchEngine instance */ static SearchEngine *create(SearchType type, QObject *parent = nullptr); /** * @brief Destructor */ ~SearchEngine() override; /** * @brief Get the current search type * @return The current SearchType */ SearchType searchType() const; /** * @brief Set the search type * @param type The new SearchType */ void setSearchType(SearchType type); /** * @brief Get the current search options * @return The current SearchOptions */ SearchOptions searchOptions() const; /** * @brief Set the search options * @param options The new SearchOptions */ void setSearchOptions(const SearchOptions &options); /** * @brief Get the current search status * @return The current SearchStatus */ SearchStatus status() const; /** * @brief Perform an asynchronous search * @param query The search query to execute */ void search(const SearchQuery &query); /** * @brief Perform an asynchronous search with a callback * @param query The search query to execute * @param callback The callback function to process results */ void searchWithCallback(const SearchQuery &query, ResultCallback callback); /** * @brief Perform a synchronous search * @param query The search query to execute * @return A SearchResultExpected containing the results or an error */ SearchResultExpected searchSync(const SearchQuery &query); /** * @brief Cancel the current search operation */ void cancel(); Q_SIGNALS: /** * @brief Emitted when a search operation starts */ void searchStarted(); /** * @brief Emitted when a new search result is found * @param results The found search results */ void resultsFound(const DFMSEARCH::SearchResultList &results); /** * @brief Emitted when the search status changes * @param status The new search status */ void statusChanged(SearchStatus status); /** * @brief Emitted when a search operation completes * @param results The list of all search results */ void searchFinished(const DFMSEARCH::SearchResultList &results); /** * @brief Emitted when a search operation is cancelled */ void searchCancelled(); /** * @brief Emitted when an error occurs during search * @param error The SearchError that occurred */ void errorOccurred(const DFMSEARCH::SearchError &error); protected: explicit SearchEngine(QObject *parent = nullptr); SearchEngine(SearchType type, QObject *parent = nullptr); friend class SearchFactory; private: std::unique_ptr d_ptr; }; DFM_SEARCH_END_NS #endif // SEARCHENGINE_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/searcherror.h000066400000000000000000000151211523477242100262500ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFM_SEARCH_ERROR_H #define DFM_SEARCH_ERROR_H #include #include #include DFM_SEARCH_BEGIN_NS // Enumeration for basic search error codes enum class SearchErrorCode { Success = 0, // No error occurred // Errors related to system resources PermissionDenied = 1, // Access to the resource is denied // General query errors InvalidQuery = 100, // The query is invalid PathIsEmpty, // The search path is empty PathNotFound, // The search path does not exist SearchTimeout, // The search operation timed out InternalError, // An internal error occurred InvalidBoolean, // A boolean value is invalid InvalidSerchMethod, // ... other general errors }; // Enumeration for file name search specific error codes enum class FileNameSearchErrorCode { KeywordIsEmpty = 1000, // The search keyword is empty KeywordTooLong, // The search keyword exceeds the maximum length // Errors related to pinyin search InvalidPinyinFormat = 1050, // The format of the pinyin keyword is invalid // Errors related to file type filtering InvalidFileTypes = 1100, // The specified file types are invalid // Errors specific to file name indexing FileNameIndexNotFound = 1200, // The file name index could not be found FileNameIndexException // An exception occurred with the file name index }; // Enumeration for content search specific error codes enum class ContentSearchErrorCode { KeywordTooShort = 2000, // The search keyword is too short WildcardNotSupported = 2001, // Wildcard search is not supported for content search // Errors related to content indexing ContentIndexNotFound = 2200, // The content index could not be found ContentIndexException // An exception occurred with the content index }; // Enumeration for OCR text search specific error codes enum class OcrTextSearchErrorCode { KeywordTooShort = 3000, // The search keyword is too short WildcardNotSupported = 3001, // Wildcard search is not supported for OCR text search // Errors related to OCR text indexing OcrTextIndexNotFound = 3200, // The OCR text index could not be found OcrTextIndexException // An exception occurred with the OCR text index }; // Base class for search error categories class SearchErrorCategory : public std::error_category { public: const char *name() const noexcept override { return "search_error"; } // Returns the name of the error category std::string message(int ev) const override; // Returns a message corresponding to the error code virtual QString qMessage(int ev) const; // Returns a Qt-friendly error message }; // Class for file name search error categories class FileNameSearchErrorCategory : public SearchErrorCategory { public: const char *name() const noexcept override { return "filename_search_error"; } // Returns the name of the file name search error category std::string message(int ev) const override; // Returns a message corresponding to the file name search error code }; // Class for content search error categories class ContentSearchErrorCategory : public SearchErrorCategory { public: const char *name() const noexcept override { return "content_search_error"; } // Returns the name of the content search error category std::string message(int ev) const override; // Returns a message corresponding to the content search error code }; // Class for OCR text search error categories class OcrTextSearchErrorCategory : public SearchErrorCategory { public: const char *name() const noexcept override { return "ocrtext_search_error"; } // Returns the name of the OCR text search error category std::string message(int ev) const override; // Returns a message corresponding to the OCR text search error code }; // Functions to get singleton instances of error categories const SearchErrorCategory &search_category(); // Returns the singleton instance of the search error category const FileNameSearchErrorCategory &filename_search_category(); // Returns the singleton instance of the file name search error category const ContentSearchErrorCategory &content_search_category(); // Returns the singleton instance of the content search error category const OcrTextSearchErrorCategory &ocrtext_search_category(); // Returns the singleton instance of the OCR text search error category // Function to create an error code from a SearchErrorCode inline std::error_code make_error_code(SearchErrorCode ec) { return std::error_code((int)ec, search_category()); } // Function to create an error code from a FileNameSearchErrorCode inline std::error_code make_error_code(FileNameSearchErrorCode ec) { return std::error_code((int)ec, filename_search_category()); } // Function to create an error code from a ContentSearchErrorCode inline std::error_code make_error_code(ContentSearchErrorCode ec) { return std::error_code((int)ec, content_search_category()); } // Function to create an error code from a OcrTextSearchErrorCode inline std::error_code make_error_code(OcrTextSearchErrorCode ec) { return std::error_code((int)ec, ocrtext_search_category()); } // Class to wrap search error conditions class SearchError { public: SearchError() : m_code(make_error_code(SearchErrorCode::Success)) { } // Default constructor initializes to success SearchError(SearchErrorCode code) : m_code(make_error_code(code)) { } // Constructor initializes with a SearchErrorCode SearchError(FileNameSearchErrorCode code) : m_code(make_error_code(code)) { } // Constructor initializes with a FileNameSearchErrorCode SearchError(ContentSearchErrorCode code) : m_code(make_error_code(code)) { } // Constructor initializes with a ContentSearchErrorCode SearchError(OcrTextSearchErrorCode code) : m_code(make_error_code(code)) { } // Constructor initializes with a OcrTextSearchErrorCode bool isError() const { return m_code.value() != static_cast(SearchErrorCode::Success); } // Checks if there is an error const std::error_code &code() const { return m_code; } // Returns the error code QString message() const; // Returns the error message QString name() const; // Returns the error name private: std::error_code m_code; // Holds the error code }; DFM_SEARCH_END_NS // Register the SearchError type for Qt's meta-object system Q_DECLARE_METATYPE(DFMSEARCH::SearchError) #endif // DFM_SEARCH_ERROR_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/searchfactory.h000066400000000000000000000013441523477242100265700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEARCHFACTORY_H #define SEARCHFACTORY_H #include #include #include #include DFM_SEARCH_BEGIN_NS class SearchFactory { public: static SearchEngine *createEngine(SearchType type, QObject *parent = nullptr); static SearchQuery createQuery(const QString &keyword, SearchQuery::Type type = SearchQuery::Type::Simple); static SearchQuery createQuery(const QStringList &keywords, SearchQuery::Type type = SearchQuery::Type::Boolean); }; DFM_SEARCH_END_NS #endif // SEARCHFACTORY_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/searchoptions.h000066400000000000000000000234141523477242100266160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEARCHOPTIONS_H #define SEARCHOPTIONS_H #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS class SearchOptionsData; class SearchOptions { public: /** * @brief Constructor */ SearchOptions(); /** * @brief Copy constructor */ SearchOptions(const SearchOptions &other); /** * @brief Move constructor */ SearchOptions(SearchOptions &&other) noexcept; /** * @brief Destructor */ virtual ~SearchOptions(); /** * @brief Assignment operator */ SearchOptions &operator=(const SearchOptions &other); /** * @brief Move assignment operator */ SearchOptions &operator=(SearchOptions &&other) noexcept; /** * @brief Get the search method */ SearchMethod method() const; /** * @brief Set the search method * Indexed search is suitable for directories with established indexes, * it is fast but may not provide the latest results. * Real-time search directly scans the file system, * the results are the latest but may be slower. */ void setSearchMethod(SearchMethod method); /** * @brief Check if case sensitivity is enabled */ bool caseSensitive() const; /** * @brief Set case sensitivity */ void setCaseSensitive(bool sensitive); /** * @brief Get the starting search path * * Returns the first path from searchPaths(), or an empty string if no paths are set. * @return The primary search path */ QString searchPath() const; /** * @brief Set the starting search path * * This replaces all search paths with a single path. * @param path The search path */ void setSearchPath(const QString &path); /** * @brief Get all search paths * * When multiple paths are set, the search engine will search all of them * and combine results. Returns a list containing the single searchPath * if only one path was set via setSearchPath(). * * @return List of search paths */ QStringList searchPaths() const; /** * @brief Set multiple search paths * * When multiple paths are set, search engines that support multi-path * queries will build combined path prefix queries internally. * * @param paths List of search paths */ void setSearchPaths(const QStringList &paths); /** * @brief Returns the current list of excluded search paths. * * @return A list of absolute directory paths that are excluded from searches. * Returns an empty list if no exclusions are set. */ QStringList searchExcludedPaths() const; /** * @brief Sets the list of excluded paths for search operations. * * When a path is in this list, it and its contents will be skipped during searches. * Paths should be absolute and normalized (use QDir::cleanPath for consistency). * * @param excludedPaths A list of directory paths to exclude from searches. * Empty list means no exclusions. */ void setSearchExcludedPaths(const QStringList &excludedPaths); /** * @brief Set whether to include hidden files */ void setIncludeHidden(bool include); /** * @brief Check if hidden files are included */ bool includeHidden() const; /** * @brief Set whether to search hidden files only (exclude non-hidden files) */ void setHiddenOnly(bool hiddenOnly); /** * @brief Check if the search is restricted to hidden files only */ bool hiddenOnly() const; /** * @brief Get the maximum result count limit */ int maxResults() const; /** * @brief Set the maximum result count limit */ void setMaxResults(int count); /** * @brief Set a custom option */ void setCustomOption(const QString &key, const QVariant &value); /** * @brief Get a custom option */ QVariant customOption(const QString &key) const; /** * @brief Check if a specific custom option is set */ bool hasCustomOption(const QString &key) const; /** * @brief Enables or disables detailed per-file search result notifications. * * When enabled, the signal @c resultFound will be emitted for each matching file, * providing more detailed search results. Note that this may cause significant * performance overhead, especially in index-based search modes where it is * NOT RECOMMENDED. * * @param enable Set @c true to enable per-file notifications, @c false to disable. */ void setResultFoundEnabled(bool enable); /** * @brief Returns whether per-file result notifications are enabled. * * @return @c true if detailed per-file results are being emitted (with performance * overhead), @c false otherwise. */ bool resultFoundEnabled() const; /** * @brief Enables or disables the addition of detailed information to search results. * * When enabled, search results will include additional metadata such as file size, * modification time, file type, etc. When disabled, only the basic path information * is included, which improves performance and reduces memory usage. * * This is separate from resultFoundEnabled() which controls whether result signals * are emitted during the search process. * * @param enable Set @c true to include detailed information in results, @c false for basic results only. */ void setDetailedResultsEnabled(bool enable); /** * @brief Returns whether search results include detailed information. * * @return @c true if search results include detailed metadata, @c false if results * only contain basic path information. */ bool detailedResultsEnabled() const; /** * @brief Sets the synchronization search timeout period in seconds. * * This timeout controls how long the system waits for search operations to * complete before aborting. Set to 0 to disable timeout (not recommended * for production environments). * * @param seconds Timeout duration in seconds (minimum 1, maximum 300) * @sa syncSearchTimeout() */ void setSyncSearchTimeout(int seconds); /** * @brief Returns the current synchronization search timeout in seconds. * * @return Timeout duration in seconds (defualt is 60) * @sa setSyncSearchTimeout() */ int syncSearchTimeout() const; /** * @brief Sets the batch processing time interval in milliseconds. * * This controls how frequently search results are batched and emitted during * asynchronous search operations. A larger interval reduces the frequency of * signal emissions but may make the UI feel less responsive. A smaller interval * provides more responsive updates but may impact performance with high result volumes. * * @param milliseconds Batch time interval in milliseconds (minimum 50, maximum 5000) * @sa batchTime() */ void setBatchTime(int milliseconds); /** * @brief Returns the current batch processing time interval in milliseconds. * * @return Batch time interval in milliseconds (default is 1000) * @sa setBatchTime() */ int batchTime() const; /** * @brief Sets the time range filter for search operations. * * The time range filter allows filtering search results based on file * creation time or modification time. Both preset ranges (like "Today", * "Last 7 days") and custom datetime ranges are supported. * * @param filter The TimeRangeFilter to apply * @sa timeRangeFilter(), hasTimeRangeFilter(), clearTimeRangeFilter() */ void setTimeRangeFilter(const TimeRangeFilter &filter); /** * @brief Returns the current time range filter. * * @return The current TimeRangeFilter * @sa setTimeRangeFilter() */ TimeRangeFilter timeRangeFilter() const; /** * @brief Checks if a time range filter is set. * * @return true if a valid time range filter is set, false otherwise * @sa setTimeRangeFilter(), clearTimeRangeFilter() */ bool hasTimeRangeFilter() const; /** * @brief Clears the time range filter. * * @sa setTimeRangeFilter(), hasTimeRangeFilter() */ void clearTimeRangeFilter(); /** * @brief Sets the file size range filter for search operations. * * The size range filter allows filtering search results based on file size in bytes. * Supports setting minimum and/or maximum file size boundaries. * * @param filter The SizeRangeFilter to apply * @sa sizeRangeFilter(), hasSizeRangeFilter(), clearSizeRangeFilter() */ void setSizeRangeFilter(const SizeRangeFilter &filter); /** * @brief Returns the current file size range filter. * * @return The current SizeRangeFilter * @sa setSizeRangeFilter() */ SizeRangeFilter sizeRangeFilter() const; /** * @brief Checks if a file size range filter is set. * * @return true if a valid size range filter is set, false otherwise * @sa setSizeRangeFilter(), clearSizeRangeFilter() */ bool hasSizeRangeFilter() const; /** * @brief Clears the file size range filter. * * @sa setSizeRangeFilter(), hasSizeRangeFilter() */ void clearSizeRangeFilter(); private: std::unique_ptr d; // PIMPL }; DFM_SEARCH_END_NS Q_DECLARE_METATYPE(DFMSEARCH::SearchOptions); #endif // SEARCHOPTIONS_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/searchquery.h000066400000000000000000000077271523477242100263010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEARCHQUERY_H #define SEARCHQUERY_H #include DFM_SEARCH_BEGIN_NS class SearchQueryData; /** * @brief The SearchQuery class encapsulates search query conditions * * This class provides a flexible way to construct and manage search queries, * supporting both simple keyword searches and complex boolean queries. * It allows for combining multiple search terms using logical operators. */ class SearchQuery { public: /** * @brief Enumeration of query types */ enum class Type { Simple, ///< Simple keyword search Boolean, ///< Boolean query (AND, OR) Wildcard ///< Wildcard search with * and ? patterns }; /** * @brief Enumeration of boolean operators */ enum class BooleanOperator { AND, ///< Logical AND operation OR ///< Logical OR operation // TODO: Logical NOT operation }; /** * @brief Default constructor */ SearchQuery(); /** * @brief Constructor with keyword * @param keyword The search keyword */ explicit SearchQuery(const QString &keyword); /** * @brief Constructor with keyword and type * @param keyword The search keyword * @param type The query type */ SearchQuery(const QString &keyword, Type type); /** * @brief Copy constructor * @param other The SearchQuery object to copy from */ SearchQuery(const SearchQuery &other); /** * @brief Move constructor * @param other The SearchQuery object to move from */ SearchQuery(SearchQuery &&other) noexcept; /** * @brief Destructor */ ~SearchQuery(); /** * @brief Copy assignment operator * @param other The SearchQuery object to copy from * @return Reference to this object */ SearchQuery &operator=(const SearchQuery &other); /** * @brief Move assignment operator * @param other The SearchQuery object to move from * @return Reference to this object */ SearchQuery &operator=(SearchQuery &&other) noexcept; /** * @brief Get the search keyword * @return The search keyword */ QString keyword() const; /** * @brief Set the search keyword * @param keyword The new search keyword */ void setKeyword(const QString &keyword); /** * @brief Get the query type * @return The current query type */ Type type() const; /** * @brief Set the query type * @param type The new query type */ void setType(Type type); /** * @brief Get the boolean operator * @return The current boolean operator */ BooleanOperator booleanOperator() const; /** * @brief Set the boolean operator * @param op The new boolean operator */ void setBooleanOperator(BooleanOperator op); /** * @brief Add a sub-query for boolean operations * @param query The sub-query to add */ void addSubQuery(const SearchQuery &query); /** * @brief Get the list of sub-queries * @return List of sub-queries */ QList subQueries() const; /** * @brief Clear all sub-queries */ void clearSubQueries(); /** * @brief Create a simple search query * @param keyword The search keyword * @return A new SearchQuery object */ static SearchQuery createSimpleQuery(const QString &keyword); /** * @brief Create a boolean search query * @param keywords List of search keywords * @param op The boolean operator to use * @return A new SearchQuery object */ static SearchQuery createBooleanQuery(const QStringList &keywords, BooleanOperator op = BooleanOperator::AND); private: std::unique_ptr d; }; DFM_SEARCH_END_NS Q_DECLARE_METATYPE(DFMSEARCH::SearchQuery); #endif // SEARCHQUERY_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/searchresult.h000066400000000000000000000061101523477242100264330ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEARCHRESULT_H #define SEARCHRESULT_H #include #include #include DFM_SEARCH_BEGIN_NS class SearchResultData; /** * @brief The SearchResult class represents a single search result item. * * This class encapsulates all information about a search result, including its path * and any custom attributes that may be associated with it. It provides methods to * access and modify these properties. */ class SearchResult { public: /** * @brief Default constructor * Creates an empty search result object */ SearchResult(); /** * @brief Constructor with path * @param path The file path of the search result */ explicit SearchResult(const QString &path); /** * @brief Copy constructor * @param other The SearchResult object to copy from */ SearchResult(const SearchResult &other); /** * @brief Move constructor * @param other The SearchResult object to move from */ SearchResult(SearchResult &&other) noexcept; /** * @brief Virtual destructor */ virtual ~SearchResult(); /** * @brief Copy assignment operator * @param other The SearchResult object to copy from * @return Reference to this object */ SearchResult &operator=(const SearchResult &other); /** * @brief Move assignment operator * @param other The SearchResult object to move from * @return Reference to this object */ SearchResult &operator=(SearchResult &&other) noexcept; /** * @brief Get the file path of the search result * @return The file path as a QString */ QString path() const; /** * @brief Set the file path of the search result * @param path The new file path */ void setPath(const QString &path); /** * @brief Set a custom attribute for the search result * @param key The attribute key * @param value The attribute value */ void setCustomAttribute(const QString &key, const QVariant &value); /** * @brief Get a custom attribute value * @param key The attribute key * @return The attribute value as QVariant */ QVariant customAttribute(const QString &key) const; /** * @brief Check if a custom attribute exists * @param key The attribute key to check * @return true if the attribute exists, false otherwise */ bool hasCustomAttribute(const QString &key) const; /** * @brief Get all custom attributes * @return A map of all custom attributes */ QVariantMap customAttributes() const; protected: std::unique_ptr d; }; using SearchResultList = QList; using SearchResultExpected = Dtk::Core::DExpected, DFMSEARCH::SearchError>; DFM_SEARCH_END_NS Q_DECLARE_METATYPE(DFMSEARCH::SearchResult) Q_DECLARE_METATYPE(DFMSEARCH::SearchResultList) #endif // SEARCHRESULT_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/semantic_types.h000066400000000000000000000136131523477242100267640ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEMANTIC_TYPES_H #define SEMANTIC_TYPES_H #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS class ParsedIntentPrivate; class MatchSpanPrivate; class TimeConstraintPrivate; class SizeConstraintPrivate; /** * @brief Represents a consumed span in the input text matched by a rule. * * Pimpl-based (QSharedDataPointer) for ABI stability. */ class MatchSpan { public: MatchSpan(); ~MatchSpan(); MatchSpan(const MatchSpan &other); MatchSpan &operator=(const MatchSpan &other); MatchSpan(MatchSpan &&other) noexcept; MatchSpan &operator=(MatchSpan &&other) noexcept; int start() const; void setStart(int start); int end() const; void setEnd(int end); QString ruleId() const; void setRuleId(const QString &ruleId); bool isValid() const; private: QSharedDataPointer d; }; /** * @brief Enum for preset time periods. */ enum class TimePreset { Today, Yesterday, DayBeforeYesterday, ThisWeek, LastWeek, ThisMonth, LastMonth, ThisYear, LastYear }; /** * @brief Enum for time constraint kinds. */ enum class TimeConstraintKind { None, ///< No time constraint Preset, ///< Preset period (today, yesterday, etc.) Relative, ///< Relative time (last N days/hours) Custom ///< Custom datetime range }; /** * @brief Represents a parsed time constraint from natural language. * * Pimpl-based (QSharedDataPointer) for ABI stability. */ class TimeConstraint { public: TimeConstraint(); ~TimeConstraint(); TimeConstraint(const TimeConstraint &other); TimeConstraint &operator=(const TimeConstraint &other); TimeConstraint(TimeConstraint &&other) noexcept; TimeConstraint &operator=(TimeConstraint &&other) noexcept; TimeConstraintKind kind() const; void setKind(TimeConstraintKind kind); TimePreset preset() const; void setPreset(TimePreset preset); int relativeValue() const; void setRelativeValue(int value); TimeUnit relativeUnit() const; void setRelativeUnit(TimeUnit unit); QDateTime customStart() const; void setCustomStart(const QDateTime &start); QDateTime customEnd() const; void setCustomEnd(const QDateTime &end); TimeField timeField() const; void setTimeField(TimeField field); bool isValid() const; private: QSharedDataPointer d; }; /** * @brief Represents a parsed size constraint from natural language. * * Pimpl-based (QSharedDataPointer) for ABI stability. */ class SizeConstraint { public: SizeConstraint(); ~SizeConstraint(); SizeConstraint(const SizeConstraint &other); SizeConstraint &operator=(const SizeConstraint &other); SizeConstraint(SizeConstraint &&other) noexcept; SizeConstraint &operator=(SizeConstraint &&other) noexcept; qint64 minSize() const; void setMinSize(qint64 size); qint64 maxSize() const; void setMaxSize(qint64 size); bool includeLower() const; void setIncludeLower(bool include); bool includeUpper() const; void setIncludeUpper(bool include); bool isValid() const; private: QSharedDataPointer d; }; /** * @brief Represents the target scope for a semantic search. * * When a user explicitly specifies where to search (e.g. "文件名包含XX" * vs "文件内容包含XX"), this enum controls which search paths are enabled. */ enum class SearchTarget { All, ///< All search paths enabled (default) FileNameOnly, ///< Only filename search ContentOnly ///< Only content + OCR search }; /** * @brief Represents the parsed intent from natural language input. * * This is the intermediate representation between NLP parsing * and search query construction. Declared public for future * structured API extensibility. * * Pimpl-based (QSharedDataPointer) for ABI stability: * new fields can be added to ParsedIntentPrivate without * breaking binary compatibility. */ class ParsedIntent { public: ParsedIntent(); ~ParsedIntent(); ParsedIntent(const ParsedIntent &other); ParsedIntent &operator=(const ParsedIntent &other); ParsedIntent(ParsedIntent &&other) noexcept; ParsedIntent &operator=(ParsedIntent &&other) noexcept; // timeConstraint const TimeConstraint &timeConstraint() const; TimeConstraint &timeConstraint(); void setTimeConstraint(const TimeConstraint &tc); // sizeConstraint const SizeConstraint &sizeConstraint() const; SizeConstraint &sizeConstraint(); void setSizeConstraint(const SizeConstraint &sc); // fileExtensions const QStringList &fileExtensions() const; QStringList &fileExtensions(); void setFileExtensions(const QStringList &extensions); // searchDirectories const QStringList &searchDirectories() const; QStringList &searchDirectories(); void setSearchDirectories(const QStringList &dirs); // includeHidden bool includeHidden() const; void setIncludeHidden(bool include); // hiddenOnly bool hiddenOnly() const; void setHiddenOnly(bool hidden); // recentOnly — true means search recently-used files via DBus RecentManager bool recentOnly() const; void setRecentOnly(bool recent); // keywords const QStringList &keywords() const; QStringList &keywords(); void setKeywords(const QStringList &kw); // searchTarget SearchTarget searchTarget() const; void setSearchTarget(SearchTarget target); // consumedSpans const QList &consumedSpans() const; QList &consumedSpans(); void setConsumedSpans(const QList &spans); private: QSharedDataPointer d; }; DFM_SEARCH_END_NS #endif // SEMANTIC_TYPES_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/semanticsearcher.h000066400000000000000000000207141523477242100272550ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEMANTICSEARCHER_H #define SEMANTICSEARCHER_H #include #include #include #include #include DFM_SEARCH_BEGIN_NS class SemanticSearcherData; /** * @brief The SemanticSearcher class provides natural language based file search. * * This class parses natural language queries (e.g., "today's pdf documents") * into structured search conditions, then orchestrates parallel searches * across filename, content, and OCR indexes. * * Usage: * @code * SemanticSearcher *searcher = new SemanticSearcher(this); * connect(searcher, &SemanticSearcher::resultsFound, [](const SearchResultList &results) { * for (const auto &r : results) { * qDebug() << r.path(); * } * }); * searcher->search("today's pdf documents"); * @endcode */ class SemanticSearcher : public QObject { Q_OBJECT public: /** * @brief Construct a semantic searcher * @param parent Parent QObject */ explicit SemanticSearcher(QObject *parent = nullptr); /** * @brief Destructor */ ~SemanticSearcher() override; /** * @brief Get the current search status */ SearchStatus status() const; /** * @brief Set the search timeout in seconds * @param seconds Timeout duration (default 60, 0 to disable) */ void setSearchTimeout(int seconds); /** * @brief Get the search timeout in seconds */ int searchTimeout() const; /** * @brief Perform a semantic search with natural language input * @param naturalLanguage The natural language query string */ void search(const QString &naturalLanguage); /** * @brief Perform a semantic search with explicit search directories * * When @p searchDirectories is non-empty, those directories take priority * over any directories resolved from the natural language input. * If empty, falls back to NLP-parsed directories, then home directory. * * @param naturalLanguage The natural language query string * @param searchDirectories Explicit directories to search in */ void search(const QString &naturalLanguage, const QStringList &searchDirectories); /** * @brief Parse natural language input into a structured intent. * * Synchronously runs the full dimension extractor pipeline (time, size, * file type, action, location, keyword) and returns the result. Does NOT * start any search. Use this when you need to inspect what the NLP parser * understood without triggering a search — e.g., for intent preview UIs, * debugging, or composing custom search plans. * * Equivalent to the parsing performed internally by search() before * searchStarted fires; the same ParsedIntent is emitted via intentParsed(). * * @param input The natural language query string * @return The parsed intent. If parsing yields no constraints, the returned * ParsedIntent has an invalid timeConstraint, invalid sizeConstraint, * empty fileExtensions, empty searchDirectories, and empty consumedSpans. * * @see isSemanticQuery() for a boolean check built on top of this method. */ ParsedIntent parseIntent(const QString &input) const; /** * @brief Check if the input contains semantic intent beyond a plain keyword. * * Returns true for self-sufficient semantic actions/locations, structured keyword * rules, or constraint-plus-target combinations. Returns false for single-slot * inputs such as a lone time, size, or filetype constraint. * * This is a convenience wrapper around parseIntent(): it parses once and * checks whether any semantic dimension was extracted. The parsing result * is discarded; if you need the parsed intent, call parseIntent() directly * to avoid parsing twice. * * This allows callers to avoid unnecessary semantic search overhead when * the user is just typing a simple keyword. * * @param input The natural language query to check * @return true if the input contains semantic intent, false for plain keywords */ bool isSemanticQuery(const QString &input) const; /** * @brief Check whether a parsed intent should use semantic search. * * This overload lets callers reuse an already parsed intent and avoid * re-running the NLP pipeline. * * @param intent The parsed intent to evaluate * @return true if the parsed intent is semantic, false otherwise */ bool isSemanticQuery(const DFMSEARCH::ParsedIntent &intent) const; /** * @brief Perform a synchronous semantic search * * Blocks the calling thread until all search engines complete or timeout. * Uses QEventLoop internally, so it works from the GUI thread. * @param naturalLanguage The natural language query string * @return SearchResultExpected containing deduplicated results or an error */ SearchResultExpected searchSync(const QString &naturalLanguage); /** * @brief Perform a synchronous semantic search with explicit directories * @param naturalLanguage The natural language query string * @param searchDirectories Explicit directories to search in * @return SearchResultExpected containing deduplicated results or an error */ SearchResultExpected searchSync(const QString &naturalLanguage, const QStringList &searchDirectories); /** * @brief Cancel the current search operation */ void cancel(); /** * @brief Enable or disable detailed results for sub-engines * * When enabled, each sub-engine (FileName, Content, OCR) will populate * extra metadata fields (file type, size, timestamps, etc.) in results. * Must be called before search(). * * @param enable true to enable detailed results (default false) */ void setDetailedResultsEnabled(bool enable); /** * @brief Check whether detailed results are enabled */ bool isDetailedResultsEnabled() const; /** * @brief Set the maximum number of results to return * * Each sub-engine (FileName, Content, OCR) will be limited to this count. * After all engines finish, results are deduplicated and truncated * to this count. * * @param count Maximum result count (0 = unlimited, default 0) */ void setMaxResults(int count); /** * @brief Get the maximum number of results * @return Maximum result count (0 = unlimited) */ int maxResults() const; /** * @brief Set directories to exclude from search * * When non-empty, these paths are forwarded to each sub-engine's * SearchOptions so that files under the excluded directories are * skipped during search. Must be called before search(). * * @param paths List of directory paths to exclude */ void setSearchExcludedPaths(const QStringList &paths); Q_SIGNALS: /** * @brief Emitted after the natural language input is parsed into an intent * * This fires before searchStarted(), allowing callers to inspect * what the NLP parser understood from the input. * * @param intent The parsed intent structure */ void intentParsed(const DFMSEARCH::ParsedIntent &intent); /** * @brief Emitted when a search operation starts */ void searchStarted(); /** * @brief Emitted when search results are found * @param results The found search results */ void resultsFound(const DFMSEARCH::SearchResultList &results); /** * @brief Emitted when the search status changes * @param status The new search status */ void statusChanged(SearchStatus status); /** * @brief Emitted when a search operation completes * @param results The list of all search results (deduplicated) */ void searchFinished(const DFMSEARCH::SearchResultList &results); /** * @brief Emitted when a search operation is cancelled */ void searchCancelled(); /** * @brief Emitted when an error occurs during search * @param error The SearchError that occurred */ void errorOccurred(const DFMSEARCH::SearchError &error); private: Q_DISABLE_COPY(SemanticSearcher) std::unique_ptr d_ptr; }; DFM_SEARCH_END_NS #endif // SEMANTICSEARCHER_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/sizerangefilter.h000066400000000000000000000071451523477242100271350ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SIZERANGEFILTER_H #define SIZERANGEFILTER_H #include DFM_SEARCH_BEGIN_NS class SizeRangeFilterData; /** * @brief The SizeRangeFilter class provides file size range filtering for search operations. * * This class provides a fluent interface for specifying file size ranges. * Size values are in bytes. * * Example usage: * @code * // Files between 1KB and 10MB * SizeRangeFilter filter; * filter.setRange(1024, 10 * 1024 * 1024); * * // Files larger than 1MB (including 1MB) * SizeRangeFilter filter; * filter.setMin(1024 * 1024).setIncludeLower(true); * * // Files smaller than 100KB (excluding 100KB) * SizeRangeFilter filter; * filter.setMax(100 * 1024).setIncludeUpper(false); * @endcode */ class SizeRangeFilter { public: SizeRangeFilter(); SizeRangeFilter(const SizeRangeFilter &other); SizeRangeFilter(SizeRangeFilter &&other) noexcept; ~SizeRangeFilter(); SizeRangeFilter &operator=(const SizeRangeFilter &other); SizeRangeFilter &operator=(SizeRangeFilter &&other) noexcept; // ---------- Range Setting ---------- /** * @brief Set the minimum file size in bytes * @param minSize Minimum file size (0 means no lower bound) * @return Reference to this filter for method chaining */ SizeRangeFilter &setMin(qint64 minSize); /** * @brief Set the maximum file size in bytes * @param maxSize Maximum file size (0 means no upper bound) * @return Reference to this filter for method chaining */ SizeRangeFilter &setMax(qint64 maxSize); /** * @brief Set both min and max file size in bytes * @param minSize Minimum file size (0 means no lower bound) * @param maxSize Maximum file size (0 means no upper bound) * @return Reference to this filter for method chaining */ SizeRangeFilter &setRange(qint64 minSize, qint64 maxSize); // ---------- Accessors ---------- /** * @brief Get the minimum file size * @return Minimum file size in bytes (0 means no lower bound) */ qint64 minSize() const; /** * @brief Get the maximum file size * @return Maximum file size in bytes (0 means no upper bound) */ qint64 maxSize() const; // ---------- Boundary Control ---------- /** * @brief Set whether the lower bound is inclusive * @param include true to include the lower bound (default: true) * @return Reference to this filter for method chaining */ SizeRangeFilter &setIncludeLower(bool include); /** * @brief Set whether the upper bound is inclusive * @param include true to include the upper bound (default: true) * @return Reference to this filter for method chaining */ SizeRangeFilter &setIncludeUpper(bool include); /** * @brief Check if lower bound is inclusive * @return true if lower bound is inclusive */ bool includeLower() const; /** * @brief Check if upper bound is inclusive * @return true if upper bound is inclusive */ bool includeUpper() const; // ---------- Filter State ---------- /** * @brief Clear the filter (make it invalid) * @return Reference to this filter for method chaining */ SizeRangeFilter &clear(); /** * @brief Check if the filter is valid (has at least one bound set) * @return true if min or max is set (> 0) */ bool isValid() const; private: std::unique_ptr d; }; DFM_SEARCH_END_NS #endif // SIZERANGEFILTER_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/textsearchapi.h000066400000000000000000000163521523477242100266040ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TEXTSEARCHAPI_H #define TEXTSEARCHAPI_H #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief The TextSearchOptionsAPI class provides common text search options * * This class serves as a base for content and OCR text search options, * providing shared functionality for preview length, highlighting, and full-text retrieval. */ class TextSearchOptionsAPI { public: /** * @brief Constructor * @param options The SearchOptions object to operate on */ explicit TextSearchOptionsAPI(SearchOptions &options); // ==================== Preview and Highlight Settings ==================== /** * @brief Sets the maximum length for content preview in search results. * @param length The maximum preview length in characters. */ void setMaxPreviewLength(int length); /** * @brief Gets the maximum length for content preview in search results. * @return The maximum preview length in characters. */ int maxPreviewLength() const; /** * @brief Enables or disables HTML highlighting in search results. * * When enabled, matching keywords in search results will be wrapped in HTML tags * (e.g., `keyword`) for visual highlighting. * Note: Enabling this feature may incur additional processing overhead. * * @param enable Set to @c true to enable HTML highlighting, @c false to disable. */ void setSearchResultHighlightEnabled(bool enable); /** * @brief Returns whether HTML highlighting in search results is enabled. * * @return @c true if search results will include HTML highlighting tags, * @c false otherwise (plaintext results). */ bool isSearchResultHighlightEnabled() const; /** * @brief Enables or disables full-text content retrieval in search results. * * When enabled, search operations will return the complete text content along with metadata. * This provides more detailed results but significantly increases memory usage and processing time. * * @param enable Set to @c true to retrieve full text contents, @c false to return metadata only. */ void setFullTextRetrievalEnabled(bool enable); /** * @brief Returns whether full-text content retrieval is enabled. * * @return @c true if full-text content retrieval is enabled, @c false otherwise. */ bool isFullTextRetrievalEnabled() const; // ==================== File Extension Filter ==================== /** * @brief Sets file extensions to filter search results. * * When set, only files matching the specified extensions will be returned. * Extensions should be provided without the leading dot (e.g., "jpg", "png"). * * @param extensions The list of file extensions to filter by. */ void setFileExtensions(const QStringList &extensions); /** * @brief Gets the file extensions used to filter search results. * @return The list of file extensions, or empty list if not set. */ QStringList fileExtensions() const; // ==================== Filename Search ==================== /** * @brief Sets a keyword to search on the filename field. * * When set, the search will also match against the indexed filename field * in addition to (or instead of) the content field. If both a content keyword * and a filename keyword are provided, results must match both (AND logic). * * @param keyword The filename keyword to search for. */ void setFilenameKeyword(const QString &keyword); /** * @brief Gets the filename keyword for search. * @return The filename keyword, or empty string if not set. */ QString filenameKeyword() const; protected: SearchOptions &m_options; }; /** * @brief The TextSearchResultAPI class provides common text search result handling * * This class serves as a base for content and OCR text search results, * providing shared functionality for highlighted content and file metadata. */ class TextSearchResultAPI { public: /** * @brief Constructor * @param result The SearchResult object to operate on */ explicit TextSearchResultAPI(SearchResult &result); // ==================== Highlighted Content ==================== /** * @brief Get the highlighted content preview * @return The highlighted content as QString */ QString highlightedContent() const; /** * @brief Set the highlighted content preview * @param content The highlighted content to set */ void setHighlightedContent(const QString &content); // ==================== File Size ==================== /** * @brief Set the file size in bytes * @param bytes File size in bytes */ void setFileSizeBytes(qint64 bytes); /** * @brief Get the file size in bytes * @return File size in bytes, 0 if not set */ qint64 fileSizeBytes() const; /** * @brief Set the stored full-text character count * @param count Character count in the same unit as preview offsets */ void setCharCount(int count); /** * @brief Get the stored full-text character count * @return Character count, 0 if not set */ int charCount() const; // ==================== Extended Attributes ==================== /** * @brief Get the file name (without path) * @return The file name */ QString filename() const; /** * @brief Set the file name * @param name The file name to set */ void setFilename(const QString &name); /** * @brief Check if the file is hidden * @return true if the file is hidden, false otherwise */ bool isHidden() const; /** * @brief Set whether the file is hidden * @param hidden true if the file is hidden, false otherwise */ void setIsHidden(bool hidden); // ==================== Modification Time ==================== /** * @brief Set the modification time timestamp * @param timestamp Unix timestamp in seconds */ void setModifyTimestamp(qint64 timestamp); /** * @brief Get the modification time timestamp * @return Unix timestamp in seconds, 0 if not set */ qint64 modifyTimestamp() const; /** * @brief Get the modification time as a formatted string * @return Formatted time string (yyyy-MM-dd HH:mm:ss) */ QString modifyTimeString() const; // ==================== Birth/Creation Time ==================== /** * @brief Set the birth/creation time timestamp * @param timestamp Unix timestamp in seconds */ void setBirthTimestamp(qint64 timestamp); /** * @brief Get the birth/creation time timestamp * @return Unix timestamp in seconds, 0 if not set */ qint64 birthTimestamp() const; /** * @brief Get the birth/creation time as a formatted string * @return Formatted time string (yyyy-MM-dd HH:mm:ss) */ QString birthTimeString() const; protected: SearchResult &m_result; }; DFM_SEARCH_END_NS #endif // TEXTSEARCHAPI_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/timerangefilter.h000066400000000000000000000147041523477242100271200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TIMERANGEFILTER_H #define TIMERANGEFILTER_H #include #include #include #include DFM_SEARCH_BEGIN_NS class TimeRangeFilterData; /** * @brief The TimeRangeFilter class provides time range filtering for search operations. * * This class provides a fluent interface for specifying time ranges: * * Example usage: * @code * // Files modified in last 3 days * TimeRangeFilter filter; * filter.setTimeField(TimeField::ModifyTime).setLast(3, TimeUnit::Days); * * // Files created today * TimeRangeFilter filter; * filter.setTimeField(TimeField::BirthTime).setToday(); * * // Custom time range * TimeRangeFilter filter; * filter.setTimeField(TimeField::ModifyTime).setRange(startDate, endDate); * @endcode */ class TimeRangeFilter { public: /** * @brief Default constructor * Creates an invalid filter (no time range set) */ TimeRangeFilter(); /** * @brief Copy constructor */ TimeRangeFilter(const TimeRangeFilter &other); /** * @brief Move constructor */ TimeRangeFilter(TimeRangeFilter &&other) noexcept; /** * @brief Destructor */ ~TimeRangeFilter(); /** * @brief Assignment operator */ TimeRangeFilter &operator=(const TimeRangeFilter &other); /** * @brief Move assignment operator */ TimeRangeFilter &operator=(TimeRangeFilter &&other) noexcept; // ---------- Time Field ---------- /** * @brief Set the time field to filter on * @param field The time field (BirthTime or ModifyTime) * @return Reference to this filter for method chaining */ TimeRangeFilter &setTimeField(TimeField field); /** * @brief Get the time field being filtered on * @return The current time field */ TimeField timeField() const; // ---------- Relative Time Range (Fluent Interface) ---------- /** * @brief Set a relative time range from now * @param value The number of time units * @param unit The time unit (Minutes, Hours, Days, Weeks, Months, Years) * @return Reference to this filter for method chaining * * Example: setLast(3, TimeUnit::Days) means files from 3 days ago to now */ TimeRangeFilter &setLast(int value, TimeUnit unit); // ---------- Fixed Presets ---------- /** * @brief Set range to today (from 00:00:00 today to 00:00:00 tomorrow) * @return Reference to this filter for method chaining */ TimeRangeFilter &setToday(); /** * @brief Set range to yesterday * @return Reference to this filter for method chaining */ TimeRangeFilter &setYesterday(); /** * @brief Set range to this week (from Monday 00:00:00 to next Monday 00:00:00) * @return Reference to this filter for method chaining */ TimeRangeFilter &setThisWeek(); /** * @brief Set range to last week * @return Reference to this filter for method chaining */ TimeRangeFilter &setLastWeek(); /** * @brief Set range to this month (from 1st day 00:00:00 to 1st day of next month) * @return Reference to this filter for method chaining */ TimeRangeFilter &setThisMonth(); /** * @brief Set range to last month * @return Reference to this filter for method chaining */ TimeRangeFilter &setLastMonth(); /** * @brief Set range to this year (from Jan 1st to Jan 1st of next year) * @return Reference to this filter for method chaining */ TimeRangeFilter &setThisYear(); /** * @brief Set range to last year * @return Reference to this filter for method chaining */ TimeRangeFilter &setLastYear(); // ---------- Custom Time Range ---------- /** * @brief Set a custom time range * @param start The start datetime of the range * @param end The end datetime of the range * @return Reference to this filter for method chaining */ TimeRangeFilter &setRange(const QDateTime &start, const QDateTime &end); /** * @brief Get the custom start time * @return The start datetime */ QDateTime startTime() const; /** * @brief Get the custom end time * @return The end datetime */ QDateTime endTime() const; // ---------- Boundary Control ---------- /** * @brief Set whether the lower bound is inclusive * @param include true to include the lower bound * @return Reference to this filter for method chaining */ TimeRangeFilter &setIncludeLower(bool include); /** * @brief Set whether the upper bound is inclusive * @param include true to include the upper bound * @return Reference to this filter for method chaining */ TimeRangeFilter &setIncludeUpper(bool include); /** * @brief Check if lower bound is inclusive * @return true if lower bound is inclusive */ bool includeLower() const; /** * @brief Check if upper bound is inclusive * @return true if upper bound is inclusive */ bool includeUpper() const; // ---------- Filter State ---------- /** * @brief Clear the filter (make it invalid) * @return Reference to this filter for method chaining */ TimeRangeFilter &clear(); /** * @brief Check if the filter is valid * @return true if a time range is set */ bool isValid() const; /** * @brief Resolve the actual time range * Calculates the actual start/end times based on current time for relative ranges. * @return A pair of (start, end) datetimes */ QPair resolveTimeRange() const; /** * @brief Resolve a relative time range to actual datetime range * @param value The number of time units * @param unit The time unit * @return A pair of (start, end) datetimes */ static QPair resolveRelativeTimeRange(int value, TimeUnit unit); private: /** * @brief Resolve a fixed unit time range to actual datetime range * @param value The number of time units (0 = this unit, 1 = last unit, etc.) * @param unit The time unit * @return A pair of (start, end) datetimes */ static QPair resolveFixedUnitTimeRange(int value, TimeUnit unit); std::unique_ptr d; }; DFM_SEARCH_END_NS #endif // TIMERANGEFILTER_H linuxdeepin-util-dfm-a4307d3/include/dfm-search/dfm-search/timeresultapi.h000066400000000000000000000050431523477242100266220ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TIMERESULTAPI_H #define TIMERESULTAPI_H #include #include #include DFM_SEARCH_BEGIN_NS class SearchResult; /** * @brief The TimeResultAPI class provides shared time attribute access for search results * * This class provides a unified interface for accessing time-related attributes * (modification time and birth/creation time) in SearchResult objects. It follows * the DRY principle by centralizing time attribute handling across different * search types (filename, content, OCR). * * Time values are stored as Unix timestamps (seconds since epoch) internally, * and can be retrieved either as raw timestamps or as formatted time strings. */ class TimeResultAPI { public: /** * @brief Constructor * @param result The SearchResult object to operate on */ explicit TimeResultAPI(SearchResult &result); // ==================== Modification Time ==================== /** * @brief Set the modification time timestamp * @param timestamp Unix timestamp in seconds */ void setModifyTimestamp(qint64 timestamp); /** * @brief Get the modification time timestamp * @return Unix timestamp in seconds, 0 if not set */ qint64 modifyTimestamp() const; /** * @brief Get the modification time as a formatted string * @return Formatted time string (yyyy-MM-dd HH:mm:ss), empty if not set */ QString modifyTimeString() const; // ==================== Birth/Creation Time ==================== /** * @brief Set the birth/creation time timestamp * @param timestamp Unix timestamp in seconds */ void setBirthTimestamp(qint64 timestamp); /** * @brief Get the birth/creation time timestamp * @return Unix timestamp in seconds, 0 if not set */ qint64 birthTimestamp() const; /** * @brief Get the birth/creation time as a formatted string * @return Formatted time string (yyyy-MM-dd HH:mm:ss), empty if not set */ QString birthTimeString() const; // ==================== Utility Functions ==================== /** * @brief Format a Unix timestamp to a human-readable string * @param timestamp Unix timestamp in seconds * @return Formatted time string (yyyy-MM-dd HH:mm:ss) */ static QString formatTimestamp(qint64 timestamp); private: SearchResult &m_result; }; DFM_SEARCH_END_NS #endif // TIMERESULTAPI_H linuxdeepin-util-dfm-a4307d3/misc/000077500000000000000000000000001523477242100170465ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/misc/.gitkeep000066400000000000000000000000001523477242100204650ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/misc/dfm-burn/000077500000000000000000000000001523477242100205605ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/misc/dfm-burn/dfm-burn.pc.in000066400000000000000000000006131523477242100232230ustar00rootroot00000000000000Name: @BIN_NAME@ Description: @CMAKE_PROJECT_DESCRIPTION@ URL: @CMAKE_PROJECT_HOMEPAGE_URL@ Version: @PROJECT_VERSION@ Requires: libisoburn-1, Qt@QT_VERSION_MAJOR@Core Cflags: -I"@CMAKE_INSTALL_FULL_INCLUDEDIR@/@BIN_NAME@" Libs: -L"@CMAKE_INSTALL_FULL_LIBDIR@" -l:lib@BIN_NAME@.so.@PROJECT_VERSION_MAJOR@ Libs.private: -L"@CMAKE_INSTALL_FULL_LIBDIR@" -l:lib@BIN_NAME@.so.@PROJECT_VERSION_MAJOR@ linuxdeepin-util-dfm-a4307d3/misc/dfm-burn/dfm-burnConfig.cmake.in000066400000000000000000000002351523477242100250270ustar00rootroot00000000000000set(@BIN_NAME@_INCLUDE_DIR @CMAKE_INSTALL_FULL_INCLUDEDIR@/@BIN_NAME@) set(@BIN_NAME@_LIBRARIES @BIN_NAME@) include_directories("${@BIN_NAME@_INCLUDE_DIR}") linuxdeepin-util-dfm-a4307d3/misc/dfm-io/000077500000000000000000000000001523477242100202215ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/misc/dfm-io/dfm-io.pc.in000066400000000000000000000005321523477242100223250ustar00rootroot00000000000000Name: @BIN_NAME@ Description: @CMAKE_PROJECT_DESCRIPTION@ URL: @CMAKE_PROJECT_HOMEPAGE_URL@ Version: @PROJECT_VERSION@ Cflags: -I"@CMAKE_INSTALL_FULL_INCLUDEDIR@/@BIN_NAME@" Libs: -L"@CMAKE_INSTALL_FULL_LIBDIR@" -l:lib@BIN_NAME@.so.@PROJECT_VERSION_MAJOR@ Libs.private: -L"@CMAKE_INSTALL_FULL_LIBDIR@" -l:lib@BIN_NAME@.so.@PROJECT_VERSION_MAJOR@ linuxdeepin-util-dfm-a4307d3/misc/dfm-io/dfm-ioConfig.cmake.in000066400000000000000000000002351523477242100241310ustar00rootroot00000000000000set(@BIN_NAME@_INCLUDE_DIR @CMAKE_INSTALL_FULL_INCLUDEDIR@/@BIN_NAME@) set(@BIN_NAME@_LIBRARIES @BIN_NAME@) include_directories("${@BIN_NAME@_INCLUDE_DIR}") linuxdeepin-util-dfm-a4307d3/misc/dfm-mount/000077500000000000000000000000001523477242100207545ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/misc/dfm-mount/dfm-mount.pc.in000066400000000000000000000006001523477242100236070ustar00rootroot00000000000000Name: @BIN_NAME@ Description: @CMAKE_PROJECT_DESCRIPTION@ URL: @CMAKE_PROJECT_HOMEPAGE_URL@ Version: @PROJECT_VERSION@ Requires: udisks2 gio-2.0 libsecret-1 Cflags: -I"@CMAKE_INSTALL_FULL_INCLUDEDIR@/@BIN_NAME@" Libs: -L"@CMAKE_INSTALL_FULL_LIBDIR@" -l:lib@BIN_NAME@.so.@PROJECT_VERSION_MAJOR@ Libs.private: -L"@CMAKE_INSTALL_FULL_LIBDIR@" -l:lib@BIN_NAME@.so.@PROJECT_VERSION_MAJOR@ linuxdeepin-util-dfm-a4307d3/misc/dfm-mount/dfm-mountConfig.cmake.in000066400000000000000000000002351523477242100254170ustar00rootroot00000000000000set(@BIN_NAME@_INCLUDE_DIR @CMAKE_INSTALL_FULL_INCLUDEDIR@/@BIN_NAME@) set(@BIN_NAME@_LIBRARIES @BIN_NAME@) include_directories("${@BIN_NAME@_INCLUDE_DIR}") linuxdeepin-util-dfm-a4307d3/misc/dfm-search/000077500000000000000000000000001523477242100210575ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/misc/dfm-search/dfm-search.pc.in000066400000000000000000000007141523477242100240230ustar00rootroot00000000000000Name: @BIN_NAME@ Description: @CMAKE_PROJECT_DESCRIPTION@ URL: @CMAKE_PROJECT_HOMEPAGE_URL@ Version: @PROJECT_VERSION@ Requires: Qt@QT_VERSION_MAJOR@Core dtk@DFM_VERSION_MAJOR@core liblucene++ liblucene++-contrib Boostsystem Threads Cflags: -I"@CMAKE_INSTALL_FULL_INCLUDEDIR@/@BIN_NAME@" Libs: -L"@CMAKE_INSTALL_FULL_LIBDIR@" -l:lib@BIN_NAME@.so.@PROJECT_VERSION_MAJOR@ Libs.private: -L"@CMAKE_INSTALL_FULL_LIBDIR@" -l:lib@BIN_NAME@.so.@PROJECT_VERSION_MAJOR@ linuxdeepin-util-dfm-a4307d3/misc/dfm-search/dfm-searchConfig.cmake.in000066400000000000000000000014201523477242100256220ustar00rootroot00000000000000include(CMakeFindDependencyMacro) # 添加必要的依赖 find_dependency(Qt@QT_VERSION_MAJOR@ COMPONENTS Core) find_dependency(Dtk@DFM_VERSION_MAJOR@ COMPONENTS Core) find_dependency(Threads) find_package(Boost REQUIRED) if (Boost_VERSION_STRING VERSION_LESS "1.89.0") message(STATUS "Boost < 1.89 → using Boost::system") find_package(Boost REQUIRED COMPONENTS system) endif() # 查找 PkgConfig 和 liblucene++ 依赖 find_dependency(PkgConfig) if(PkgConfig_FOUND) pkg_check_modules(Lucene REQUIRED IMPORTED_TARGET liblucene++ liblucene++-contrib) endif() set(@BIN_NAME@_INCLUDE_DIR @CMAKE_INSTALL_FULL_INCLUDEDIR@/@BIN_NAME@) set(@BIN_NAME@_LIBRARIES @BIN_NAME@) if(NOT TARGET @BIN_NAME@) include("${CMAKE_CURRENT_LIST_DIR}/@BIN_NAME@Targets.cmake") endif() linuxdeepin-util-dfm-a4307d3/src/000077500000000000000000000000001523477242100167025ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/.gitkeep000066400000000000000000000000001523477242100203210ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/CMakeLists.txt000066400000000000000000000005351523477242100214450ustar00rootroot00000000000000#add_subdirectory(${PROJECT_SOURCE_DIR}/src/dfm-burn) # signals and slots keyword is disable, use Q_SIGNALS and Q_SLOTS instead add_compile_definitions(QT_NO_SIGNALS_SLOTS_KEYWORDS) add_subdirectory("dfm-io") add_subdirectory("dfm-mount") add_subdirectory("dfm-burn") add_subdirectory("dfm-search") #add_subdirectory(${PROJECT_SOURCE_DIR}/tests) linuxdeepin-util-dfm-a4307d3/src/dfm-burn/000077500000000000000000000000001523477242100204145ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/000077500000000000000000000000001523477242100221645ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/000077500000000000000000000000001523477242100241415ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/LICENCE.clearified.artistic000066400000000000000000000144041523477242100310400ustar00rootroot00000000000000 The Clarified Artistic License Preamble: --------- The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: ------------ "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Distribution fee" is a fee you charge for providing a copy of this Package to another party. "Freely Available" means that no fee is charged for the right to use the item, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. Licence: -------- 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain, or those made Freely Available, or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: 1. place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major network archive site allowing unrestricted access to them, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. 2. use the modified Package only within your corporation or organization. 3. rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. 4. make other distribution arrangements with the Copyright Holder. 5. permit and encourge anyone who receives a copy of the modified Package permission to make your modifications Freely Available in some specific way. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: 1. distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. 2. accompany the distribution with the machine-readable source of the Package with your modifications. 3. give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. 4. make other distribution arrangements with the Copyright Holder. 5. offer the machine-readable source of the Package, with your modifications, by mail order. 5. You may charge a distribution fee for any distribution of this Package. If you offer support for this Package, you may charge any fee you choose for that support. You may not charge a license fee for the right to use this Package itself. You may distribute this Package in aggregate with other (possibly commercial and possibly nonfree) programs as part of a larger (possibly commercial and possibly nonfree) software distribution, and charge license fees for other parts of that software distribution, provided that you do not advertise this Package as a product of your own. If the Package includes an interpreter, You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of the Standard Version of the Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/config.guess000077500000000000000000001430671523477242100264740ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2024 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2024-07-27' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system '$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2024 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi # Just in case it came from the environment. GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still # use 'HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039,SC3028 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c17 c99 c89 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD=$driver break fi done if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac } # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case $UNAME_SYSTEM in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #if defined(__ANDROID__) LIBC=android #else #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #elif defined(__LLVM_LIBC__) LIBC=llvm #else #include /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. if [ "$LIBC" = unknown ] && command -v ldd >/dev/null && ldd --version 2>&1 | grep -q ^musl; then LIBC=musl fi # If the system lacks a compiler, then just pick glibc. # We could probably try harder. if [ "$LIBC" = unknown ]; then LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` case $UNAME_MACHINE_ARCH in aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case $UNAME_MACHINE_ARCH in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case $UNAME_VERSION in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. GUESS=$machine-${os}${release}${abi-} ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE ;; *:SecBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE ;; *:MidnightBSD:*:*) GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE ;; *:ekkoBSD:*:*) GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE ;; *:SolidBSD:*:*) GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE ;; *:OS108:*:*) GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE ;; macppc:MirBSD:*:*) GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE ;; *:MirBSD:*:*) GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE ;; *:Sortix:*:*) GUESS=$UNAME_MACHINE-unknown-sortix ;; *:Twizzler:*:*) GUESS=$UNAME_MACHINE-unknown-twizzler ;; *:Redox:*:*) GUESS=$UNAME_MACHINE-unknown-redox ;; mips:OSF1:*.*) GUESS=mips-dec-osf1 ;; alpha:OSF1:*:*) # Reset EXIT trap before exiting to avoid spurious non-zero exit code. trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case $ALPHA_CPU_TYPE in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` GUESS=$UNAME_MACHINE-dec-osf$OSF_REL ;; Amiga*:UNIX_System_V:4.0:*) GUESS=m68k-unknown-sysv4 ;; *:[Aa]miga[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-amigaos ;; *:[Mm]orph[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-morphos ;; *:OS/390:*:*) GUESS=i370-ibm-openedition ;; *:z/VM:*:*) GUESS=s390-ibm-zvmoe ;; *:OS400:*:*) GUESS=powerpc-ibm-os400 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) GUESS=arm-acorn-riscix$UNAME_RELEASE ;; arm*:riscos:*:*|arm*:RISCOS:*:*) GUESS=arm-unknown-riscos ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) GUESS=hppa1.1-hitachi-hiuxmpp ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. case `(/bin/universe) 2>/dev/null` in att) GUESS=pyramid-pyramid-sysv3 ;; *) GUESS=pyramid-pyramid-bsd ;; esac ;; NILE*:*:*:dcosx) GUESS=pyramid-pyramid-svr4 ;; DRS?6000:unix:4.0:6*) GUESS=sparc-icl-nx6 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) GUESS=sparc-icl-nx7 ;; esac ;; s390x:SunOS:*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL ;; sun4H:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-hal-solaris2$SUN_REL ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris2$SUN_REL ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) GUESS=i386-pc-auroraux$UNAME_RELEASE ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$SUN_ARCH-pc-solaris2$SUN_REL ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris3$SUN_REL ;; sun4*:SunOS:*:*) case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like '4.1.3-JL'. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` GUESS=sparc-sun-sunos$SUN_REL ;; sun3*:SunOS:*:*) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case `/bin/arch` in sun3) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac ;; aushp:SunOS:*:*) GUESS=sparc-auspex-sunos$UNAME_RELEASE ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) GUESS=m68k-milan-mint$UNAME_RELEASE ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) GUESS=m68k-hades-mint$UNAME_RELEASE ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) GUESS=m68k-unknown-mint$UNAME_RELEASE ;; m68k:machten:*:*) GUESS=m68k-apple-machten$UNAME_RELEASE ;; powerpc:machten:*:*) GUESS=powerpc-apple-machten$UNAME_RELEASE ;; RISC*:Mach:*:*) GUESS=mips-dec-mach_bsd4.3 ;; RISC*:ULTRIX:*:*) GUESS=mips-dec-ultrix$UNAME_RELEASE ;; VAX*:ULTRIX*:*:*) GUESS=vax-dec-ultrix$UNAME_RELEASE ;; 2020:CLIX:*:* | 2430:CLIX:*:*) GUESS=clipper-intergraph-clix$UNAME_RELEASE ;; mips:*:*:UMIPS | mips:*:*:RISCos) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } GUESS=mips-mips-riscos$UNAME_RELEASE ;; Motorola:PowerMAX_OS:*:*) GUESS=powerpc-motorola-powermax ;; Motorola:*:4.3:PL8-*) GUESS=powerpc-harris-powermax ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) GUESS=powerpc-harris-powermax ;; Night_Hawk:Power_UNIX:*:*) GUESS=powerpc-harris-powerunix ;; m88k:CX/UX:7*:*) GUESS=m88k-harris-cxux7 ;; m88k:*:4*:R4*) GUESS=m88k-motorola-sysv4 ;; m88k:*:3*:R3*) GUESS=m88k-motorola-sysv3 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ test "$TARGET_BINARY_INTERFACE"x = x then GUESS=m88k-dg-dgux$UNAME_RELEASE else GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else GUESS=i586-dg-dgux$UNAME_RELEASE fi ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) GUESS=m88k-dolphin-sysv3 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 GUESS=m88k-motorola-sysv3 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) GUESS=m88k-tektronix-sysv3 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) GUESS=m68k-tektronix-bsd ;; *:IRIX*:*:*) IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` GUESS=mips-sgi-irix$IRIX_REL ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) GUESS=i386-ibm-aix ;; ia64:AIX:*:*) if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then GUESS=$SYSTEM_NAME else GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then GUESS=rs6000-ibm-aix3.2.4 else GUESS=rs6000-ibm-aix3.2 fi ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if test -x /usr/bin/lslpp ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$IBM_ARCH-ibm-aix$IBM_REV ;; *:AIX:*:*) GUESS=rs6000-ibm-aix ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) GUESS=romp-ibm-bsd4.4 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) GUESS=rs6000-bull-bosx ;; DPX/2?00:B.O.S.:*:*) GUESS=m68k-bull-sysv3 ;; 9000/[34]??:4.3bsd:1.*:*) GUESS=m68k-hp-bsd ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) GUESS=m68k-hp-bsd4.4 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` case $UNAME_MACHINE in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case $sc_cpu_version in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case $sc_kernel_bits in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if test "$HP_ARCH" = ""; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if test "$HP_ARCH" = hppa2.0w then set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi GUESS=$HP_ARCH-hp-hpux$HPUX_REV ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` GUESS=ia64-hp-hpux$HPUX_REV ;; 3050*:HI-UX:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } GUESS=unknown-hitachi-hiuxwe2 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) GUESS=hppa1.1-hp-bsd ;; 9000/8??:4.3bsd:*:*) GUESS=hppa1.0-hp-bsd ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) GUESS=hppa1.0-hp-mpeix ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) GUESS=hppa1.1-hp-osf ;; hp8??:OSF1:*:*) GUESS=hppa1.0-hp-osf ;; i*86:OSF1:*:*) if test -x /usr/sbin/sysversion ; then GUESS=$UNAME_MACHINE-unknown-osf1mk else GUESS=$UNAME_MACHINE-unknown-osf1 fi ;; parisc*:Lites*:*:*) GUESS=hppa1.1-hp-lites ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) GUESS=c1-convex-bsd ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) GUESS=c34-convex-bsd ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) GUESS=c38-convex-bsd ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) GUESS=c4-convex-bsd ;; CRAY*Y-MP:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=ymp-cray-unicos$CRAY_REL ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=t90-cray-unicos$CRAY_REL ;; CRAY*T3E:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=alphaev5-cray-unicosmk$CRAY_REL ;; CRAY*SV1:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=sv1-cray-unicos$CRAY_REL ;; *:UNICOS/mp:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=craynv-cray-unicosmp$CRAY_REL ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE ;; sparc*:BSD/OS:*:*) GUESS=sparc-unknown-bsdi$UNAME_RELEASE ;; *:BSD/OS:*:*) GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi else FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf fi ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL ;; i*:CYGWIN*:*) GUESS=$UNAME_MACHINE-pc-cygwin ;; *:MINGW64*:*) GUESS=$UNAME_MACHINE-pc-mingw64 ;; *:MINGW*:*) GUESS=$UNAME_MACHINE-pc-mingw32 ;; *:MSYS*:*) GUESS=$UNAME_MACHINE-pc-msys ;; i*:PW*:*) GUESS=$UNAME_MACHINE-pc-pw32 ;; *:SerenityOS:*:*) GUESS=$UNAME_MACHINE-pc-serenity ;; *:Interix*:*) case $UNAME_MACHINE in x86) GUESS=i586-pc-interix$UNAME_RELEASE ;; authenticamd | genuineintel | EM64T) GUESS=x86_64-unknown-interix$UNAME_RELEASE ;; IA64) GUESS=ia64-unknown-interix$UNAME_RELEASE ;; esac ;; i*:UWIN*:*) GUESS=$UNAME_MACHINE-pc-uwin ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) GUESS=x86_64-pc-cygwin ;; prep*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=powerpcle-unknown-solaris2$SUN_REL ;; *:GNU:*:*) # the GNU system GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL ;; *:GNU/*:*:*) # other systems with GNU libc and userland GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) GUESS="$UNAME_MACHINE-pc-managarm-mlibc" ;; *:[Mm]anagarm:*:*) GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) set_cc_for_build CPU=$UNAME_MACHINE LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then ABI=64 sed 's/^ //' << EOF > "$dummy.c" #ifdef __ARM_EABI__ #ifdef __ARM_PCS_VFP ABI=eabihf #else ABI=eabi #endif #endif EOF cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` eval "$cc_set_abi" case $ABI in eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; esac fi GUESS=$CPU-unknown-linux-$LIBCABI ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi ;; avr32*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; cris:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; crisv32:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; e2k:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; frv:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; hexagon:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:Linux:*:*) GUESS=$UNAME_MACHINE-pc-linux-$LIBC ;; ia64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; kvx:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; kvx:cos:*:*) GUESS=$UNAME_MACHINE-unknown-cos ;; kvx:mbr:*:*) GUESS=$UNAME_MACHINE-unknown-mbr ;; loongarch32:Linux:*:* | loongarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m68*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` eval "$cc_set_vars" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) GUESS=or1k-unknown-linux-$LIBC ;; or32:Linux:*:* | or1k*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; padre:Linux:*:*) GUESS=sparc-unknown-linux-$LIBC ;; parisc64:Linux:*:* | hppa64:Linux:*:*) GUESS=hppa64-unknown-linux-$LIBC ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; *) GUESS=hppa-unknown-linux-$LIBC ;; esac ;; ppc64:Linux:*:*) GUESS=powerpc64-unknown-linux-$LIBC ;; ppc:Linux:*:*) GUESS=powerpc-unknown-linux-$LIBC ;; ppc64le:Linux:*:*) GUESS=powerpc64le-unknown-linux-$LIBC ;; ppcle:Linux:*:*) GUESS=powerpcle-unknown-linux-$LIBC ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; s390:Linux:*:* | s390x:Linux:*:*) GUESS=$UNAME_MACHINE-ibm-linux-$LIBC ;; sh64*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sh*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sparc:Linux:*:* | sparc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; tile*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; vax:Linux:*:*) GUESS=$UNAME_MACHINE-dec-linux-$LIBC ;; x86_64:Linux:*:*) set_cc_for_build CPU=$UNAME_MACHINE LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then ABI=64 sed 's/^ //' << EOF > "$dummy.c" #ifdef __i386__ ABI=x86 #else #ifdef __ILP32__ ABI=x32 #endif #endif EOF cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` eval "$cc_set_abi" case $ABI in x86) CPU=i686 ;; x32) LIBCABI=${LIBC}x32 ;; esac fi GUESS=$CPU-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. GUESS=i386-sequent-sysv4 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION ;; i*86:OS/2:*:*) # If we were able to find 'uname', then EMX Unix compatibility # is probably installed. GUESS=$UNAME_MACHINE-pc-os2-emx ;; i*86:XTS-300:*:STOP) GUESS=$UNAME_MACHINE-unknown-stop ;; i*86:atheos:*:*) GUESS=$UNAME_MACHINE-unknown-atheos ;; i*86:syllable:*:*) GUESS=$UNAME_MACHINE-pc-syllable ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) GUESS=i386-unknown-lynxos$UNAME_RELEASE ;; i*86:*DOS:*:*) GUESS=$UNAME_MACHINE-pc-msdosdjgpp ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv32 fi ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. GUESS=i586-pc-msdosdjgpp ;; Intel:Mach:3*:*) GUESS=i386-pc-mach3 ;; paragon:*:*:*) GUESS=i860-intel-osf1 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi ;; mini*:CTIX:SYS*5:*) # "miniframe" GUESS=m68010-convergent-sysv ;; mc68k:UNIX:SYSTEM5:3.51m) GUESS=m68k-convergent-sysv ;; M680?0:D-NIX:5.3:*) GUESS=m68k-diab-dnix ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) GUESS=m68k-unknown-lynxos$UNAME_RELEASE ;; mc68030:UNIX_System_V:4.*:*) GUESS=m68k-atari-sysv4 ;; TSUNAMI:LynxOS:2.*:*) GUESS=sparc-unknown-lynxos$UNAME_RELEASE ;; rs6000:LynxOS:2.*:*) GUESS=rs6000-unknown-lynxos$UNAME_RELEASE ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) GUESS=powerpc-unknown-lynxos$UNAME_RELEASE ;; SM[BE]S:UNIX_SV:*:*) GUESS=mips-dde-sysv$UNAME_RELEASE ;; RM*:ReliantUNIX-*:*:*) GUESS=mips-sni-sysv4 ;; RM*:SINIX-*:*:*) GUESS=mips-sni-sysv4 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` GUESS=$UNAME_MACHINE-sni-sysv4 else GUESS=ns32k-sni-sysv fi ;; PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort # says GUESS=i586-unisys-sysv4 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm GUESS=hppa1.1-stratus-sysv4 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. GUESS=i860-stratus-sysv4 ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. GUESS=$UNAME_MACHINE-stratus-vos ;; *:VOS:*:*) # From Paul.Green@stratus.com. GUESS=hppa1.1-stratus-vos ;; mc68*:A/UX:*:*) GUESS=m68k-apple-aux$UNAME_RELEASE ;; news*:NEWS-OS:6*:*) GUESS=mips-sony-newsos6 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if test -d /usr/nec; then GUESS=mips-nec-sysv$UNAME_RELEASE else GUESS=mips-unknown-sysv$UNAME_RELEASE fi ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. GUESS=powerpc-be-beos ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. GUESS=powerpc-apple-beos ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. GUESS=i586-pc-beos ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; ppc:Haiku:*:*) # Haiku running on Apple PowerPC GUESS=powerpc-apple-haiku ;; *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) GUESS=$UNAME_MACHINE-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE ;; SX-5:SUPER-UX:*:*) GUESS=sx5-nec-superux$UNAME_RELEASE ;; SX-6:SUPER-UX:*:*) GUESS=sx6-nec-superux$UNAME_RELEASE ;; SX-7:SUPER-UX:*:*) GUESS=sx7-nec-superux$UNAME_RELEASE ;; SX-8:SUPER-UX:*:*) GUESS=sx8-nec-superux$UNAME_RELEASE ;; SX-8R:SUPER-UX:*:*) GUESS=sx8r-nec-superux$UNAME_RELEASE ;; SX-ACE:SUPER-UX:*:*) GUESS=sxace-nec-superux$UNAME_RELEASE ;; Power*:Rhapsody:*:*) GUESS=powerpc-apple-rhapsody$UNAME_RELEASE ;; *:Rhapsody:*:*) GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE ;; arm64:Darwin:*:*) GUESS=aarch64-apple-darwin$UNAME_RELEASE ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE ;; *:QNX:*:4*) GUESS=i386-pc-qnx ;; NEO-*:NONSTOP_KERNEL:*:*) GUESS=neo-tandem-nsk$UNAME_RELEASE ;; NSE-*:NONSTOP_KERNEL:*:*) GUESS=nse-tandem-nsk$UNAME_RELEASE ;; NSR-*:NONSTOP_KERNEL:*:*) GUESS=nsr-tandem-nsk$UNAME_RELEASE ;; NSV-*:NONSTOP_KERNEL:*:*) GUESS=nsv-tandem-nsk$UNAME_RELEASE ;; NSX-*:NONSTOP_KERNEL:*:*) GUESS=nsx-tandem-nsk$UNAME_RELEASE ;; *:NonStop-UX:*:*) GUESS=mips-compaq-nonstopux ;; BS2000:POSIX*:*:*) GUESS=bs2000-siemens-sysv ;; DS/*:UNIX_System_V:*:*) GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "${cputype-}" = 386; then UNAME_MACHINE=i386 elif test "x${cputype-}" != x; then UNAME_MACHINE=$cputype fi GUESS=$UNAME_MACHINE-unknown-plan9 ;; *:TOPS-10:*:*) GUESS=pdp10-unknown-tops10 ;; *:TENEX:*:*) GUESS=pdp10-unknown-tenex ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) GUESS=pdp10-dec-tops20 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) GUESS=pdp10-xkl-tops20 ;; *:TOPS-20:*:*) GUESS=pdp10-unknown-tops20 ;; *:ITS:*:*) GUESS=pdp10-unknown-its ;; SEI:*:*:SEIUX) GUESS=mips-sei-seiux$UNAME_RELEASE ;; *:DragonFly:*:*) DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case $UNAME_MACHINE in A*) GUESS=alpha-dec-vms ;; I*) GUESS=ia64-dec-vms ;; V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) GUESS=i386-pc-xenix ;; i*86:skyos:*:*) SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL ;; i*86:rdos:*:*) GUESS=$UNAME_MACHINE-pc-rdos ;; i*86:Fiwix:*:*) GUESS=$UNAME_MACHINE-pc-fiwix ;; *:AROS:*:*) GUESS=$UNAME_MACHINE-unknown-aros ;; x86_64:VMkernel:*:*) GUESS=$UNAME_MACHINE-unknown-esx ;; amd64:Isilon\ OneFS:*:*) GUESS=x86_64-unknown-onefs ;; *:Unleashed:*:*) GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE ;; *:Ironclad:*:*) GUESS=$UNAME_MACHINE-unknown-ironclad ;; esac # Do we have a guess based on uname results? if test "x$GUESS" != x; then echo "$GUESS" exit fi # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif int main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/config.h000066400000000000000000000000151523477242100255530ustar00rootroot00000000000000#define yes linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/config.h.in000066400000000000000000000000251523477242100261610ustar00rootroot00000000000000#define @HAVE_SCSI@ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/config.sub000077500000000000000000001154411523477242100261320ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2024 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale timestamp='2024-05-27' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2024 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Split fields of configuration type saved_IFS=$IFS IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in cloudabi*-eabi* \ | kfreebsd*-gnu* \ | knetbsd*-gnu* \ | kopensolaris*-gnu* \ | linux-* \ | managarm-* \ | netbsd*-eabi* \ | netbsd*-gnu* \ | nto-qnx* \ | os2-emx* \ | rtmk-nova* \ | storm-chaos* \ | uclinux-gnu* \ | uclinux-uclibc* \ | windows-* ) basic_machine=$field1 basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown basic_os=linux-android ;; *) basic_machine=$field1-$field2 basic_os=$field3 ;; esac ;; *-*) case $field1-$field2 in # Shorthands that happen to contain a single dash convex-c[12] | convex-c3[248]) basic_machine=$field2-convex basic_os= ;; decstation-3100) basic_machine=mips-dec basic_os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Do not treat sunos as a manufacturer sun*os*) basic_machine=$field1 basic_os=$field2 ;; # Manufacturers 3100* \ | 32* \ | 3300* \ | 3600* \ | 7300* \ | acorn \ | altos* \ | apollo \ | apple \ | atari \ | att* \ | axis \ | be \ | bull \ | cbm \ | ccur \ | cisco \ | commodore \ | convergent* \ | convex* \ | cray \ | crds \ | dec* \ | delta* \ | dg \ | digital \ | dolphin \ | encore* \ | gould \ | harris \ | highlevel \ | hitachi* \ | hp \ | ibm* \ | intergraph \ | isi* \ | knuth \ | masscomp \ | microblaze* \ | mips* \ | motorola* \ | ncr* \ | news \ | next \ | ns \ | oki \ | omron* \ | pc533* \ | rebel \ | rom68k \ | rombug \ | semi \ | sequent* \ | siemens \ | sgi* \ | siemens \ | sim \ | sni \ | sony* \ | stratus \ | sun \ | sun[234]* \ | tektronix \ | tti* \ | ultra \ | unicom* \ | wec \ | winbond \ | wrs) basic_machine=$field1-$field2 basic_os= ;; zephyr*) basic_machine=$field1-unknown basic_os=$field2 ;; *) basic_machine=$field1 basic_os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc basic_os=bsd ;; a29khif) basic_machine=a29k-amd basic_os=udi ;; adobe68k) basic_machine=m68010-adobe basic_os=scout ;; alliant) basic_machine=fx80-alliant basic_os= ;; altos | altos3068) basic_machine=m68k-altos basic_os= ;; am29k) basic_machine=a29k-none basic_os=bsd ;; amdahl) basic_machine=580-amdahl basic_os=sysv ;; amiga) basic_machine=m68k-unknown basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo basic_os=bsd ;; aros) basic_machine=i386-pc basic_os=aros ;; aux) basic_machine=m68k-apple basic_os=aux ;; balance) basic_machine=ns32k-sequent basic_os=dynix ;; blackfin) basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= ;; da30) basic_machine=m68k-da30 basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec basic_os= ;; delta88) basic_machine=m88k-motorola basic_os=sysv3 ;; dicos) basic_machine=i686-pc basic_os=dicos ;; djgpp) basic_machine=i586-pc basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson basic_os=ose ;; gmicro) basic_machine=tron-gmicro basic_os=sysv ;; go32) basic_machine=i386-pc basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi basic_os=hms ;; harris) basic_machine=m88k-harris basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp basic_os=osf ;; hppro) basic_machine=hppa1.1-hp basic_os=proelf ;; i386mach) basic_machine=i386-mach basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown basic_os=linux ;; magnum | m3230) basic_machine=mips-mips basic_os=sysv ;; merlin) basic_machine=ns32k-utek basic_os=sysv ;; mingw64) basic_machine=x86_64-pc basic_os=mingw64 ;; mingw32) basic_machine=i686-pc basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k basic_os=coff ;; morphos) basic_machine=powerpc-unknown basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown basic_os=moxiebox ;; msdos) basic_machine=i386-pc basic_os=msdos ;; msys) basic_machine=i686-pc basic_os=msys ;; mvs) basic_machine=i370-ibm basic_os=mvs ;; nacl) basic_machine=le32-unknown basic_os=nacl ;; ncr3000) basic_machine=i486-ncr basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony basic_os=newsos ;; news1000) basic_machine=m68030-sony basic_os=newsos ;; necv70) basic_machine=v70-nec basic_os=sysv ;; nh3000) basic_machine=m68k-harris basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris basic_os=cxux ;; nindy960) basic_machine=i960-intel basic_os=nindy ;; mon960) basic_machine=i960-intel basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson basic_os=ose ;; os68k) basic_machine=m68k-none basic_os=os68k ;; paragon) basic_machine=i860-intel basic_os=osf ;; parisc) basic_machine=hppa-unknown basic_os=linux ;; psp) basic_machine=mipsallegrexel-sony basic_os=psp ;; pw32) basic_machine=i586-unknown basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc basic_os=rdos ;; rdos32) basic_machine=i386-pc basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k basic_os=coff ;; sa29200) basic_machine=a29k-amd basic_os=udi ;; sei) basic_machine=mips-sei basic_os=seiux ;; sequent) basic_machine=i386-sequent basic_os= ;; sps7) basic_machine=m68k-bull basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem basic_os= ;; stratus) basic_machine=i860-stratus basic_os=sysv4 ;; sun2) basic_machine=m68000-sun basic_os= ;; sun2os3) basic_machine=m68000-sun basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun basic_os=sunos4 ;; sun3) basic_machine=m68k-sun basic_os= ;; sun3os3) basic_machine=m68k-sun basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun basic_os=sunos4 ;; sun4) basic_machine=sparc-sun basic_os= ;; sun4os3) basic_machine=sparc-sun basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun basic_os= ;; sv1) basic_machine=sv1-cray basic_os=unicos ;; symmetry) basic_machine=i386-sequent basic_os=dynix ;; t3e) basic_machine=alphaev5-cray basic_os=unicos ;; t90) basic_machine=t90-cray basic_os=unicos ;; toad1) basic_machine=pdp10-xkl basic_os=tops20 ;; tpf) basic_machine=s390x-ibm basic_os=tpf ;; udi29k) basic_machine=a29k-amd basic_os=udi ;; ultra3) basic_machine=a29k-nyu basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec basic_os=none ;; vaxv) basic_machine=vax-dec basic_os=sysv ;; vms) basic_machine=vax-dec basic_os=vms ;; vsta) basic_machine=i386-pc basic_os=vsta ;; vxworks960) basic_machine=i960-wrs basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs basic_os=vxworks ;; xbox) basic_machine=i686-pc basic_os=mingw32 ;; ymp) basic_machine=ymp-cray basic_os=unicos ;; *) basic_machine=$1 basic_os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300) cpu=m68k vendor=motorola ;; # This used to be dpx2*, but that gets the RS6000-based # DPX/20 and the x86-based DPX/2-100 wrong. See # https://oldskool.silicium.org/stations/bull_dpx20.htm # https://www.feb-patrimoine.com/english/bull_dpx2.htm # https://www.feb-patrimoine.com/english/unix_and_bull.htm dpx2 | dpx2[23]00 | dpx2[23]xx) cpu=m68k vendor=bull ;; dpx2100 | dpx21xx) cpu=i386 vendor=bull ;; dpx20) cpu=rs6000 vendor=bull ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv32 ;; i*86v4*) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv4 ;; i*86v) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv ;; i*86sol2) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $basic_os in irix*) ;; *) basic_os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs basic_os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond basic_os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) saved_IFS=$IFS IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if test x"$basic_os" != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. obj= case $basic_os in gnu/linux*) kernel=linux os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` ;; os2-emx) kernel=os2 os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` ;; nto-qnx*) kernel=nto os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` ;; *-*) saved_IFS=$IFS IFS="-" read kernel os <&2 fi ;; *) echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 exit 1 ;; esac case $obj in aout* | coff* | elf* | pe*) ;; '') # empty is fine ;; *) echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 exit 1 ;; esac # Here we handle the constraint that a (synthetic) cpu and os are # valid only in combination with each other and nowhere else. case $cpu-$os in # The "javascript-unknown-ghcjs" triple is used by GHC; we # accept it here in order to tolerate that, but reject any # variations. javascript-ghcjs) ;; javascript-* | *-ghcjs) echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os-$obj in linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ | linux-mlibc*- | linux-musl*- | linux-newlib*- \ | linux-relibc*- | linux-uclibc*- | linux-ohos*- ) ;; uclinux-uclibc*- | uclinux-gnu*- ) ;; managarm-mlibc*- | managarm-kernel*- ) ;; windows*-msvc*-) ;; -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \ | -uclibc*- ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 exit 1 ;; -kernel*- ) echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 exit 1 ;; *-kernel*- ) echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 exit 1 ;; *-msvc*- ) echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 exit 1 ;; kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-) ;; vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) ;; nto-qnx*-) ;; os2-emx-) ;; rtmk-nova-) ;; *-eabi*- | *-gnueabi*-) ;; none--*) # None (no kernel, i.e. freestanding / bare metal), # can be paired with an machine code file format ;; -*-) # Blank kernel with real OS is always fine. ;; --*) # Blank kernel and OS with real machine code file format is always fine. ;; *-*-*) echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$os in *-riscix*) vendor=acorn ;; *-sunos* | *-solaris*) vendor=sun ;; *-cnk* | *-aix*) vendor=ibm ;; *-beos*) vendor=be ;; *-hpux*) vendor=hp ;; *-mpeix*) vendor=hp ;; *-hiux*) vendor=hitachi ;; *-unos*) vendor=crds ;; *-dgux*) vendor=dg ;; *-luna*) vendor=omron ;; *-genix*) vendor=ns ;; *-clix*) vendor=intergraph ;; *-mvs* | *-opened*) vendor=ibm ;; *-os400*) vendor=ibm ;; s390-* | s390x-*) vendor=ibm ;; *-ptx*) vendor=sequent ;; *-tpf*) vendor=ibm ;; *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; *-aux*) vendor=apple ;; *-hms*) vendor=hitachi ;; *-mpw* | *-macos*) vendor=apple ;; *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; *-vos*) vendor=stratus ;; esac ;; esac echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/configure000077500000000000000000004436751523477242100260730ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for udfclient 0.8.12. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="as_nop=: if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else $as_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and reinoud@NetBSD.org $0: about your system, including any error possibly output $0: before this message. Then install a modern shell, or $0: manually run the script under such a shell if you do $0: have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='udfclient' PACKAGE_TARNAME='udfclient' PACKAGE_VERSION='0.8.12' PACKAGE_STRING='udfclient 0.8.12' PACKAGE_BUGREPORT='reinoud@NetBSD.org' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='LTLIBOBJS LIBOBJS POSTOBJ SCSI_LIB BUILD_APPS TIMELIB THREADLIB INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC host_os host_vendor host_cpu host build_os build_vendor build_cpu build target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures udfclient 0.8.12 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/udfclient] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of udfclient 0.8.12:";; esac cat <<\_ACEOF Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF udfclient configure 0.8.12 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 printf %s "checking for $2.$3... " >&6; } if eval test \${$4+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$4 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by udfclient $as_me 0.8.12, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Auxiliary files required by this configure script. ac_aux_files="install-sh config.guess config.sub" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu #AC_CONFIG_SRCDIR([udf.c]) # Make sure we can run config.sub. $SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 else $as_nop ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac #AC_CONFIG_HEADER([config.h]) # # Checks for programs. # ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. case $as_dir in #(( ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # # Check for OS dependent flags # case $host_os in netbsd*) POSTOBJ="-lprop -lutil" ;; linux*) CPPFLAGS="$CPPFLAGS -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=500 -D__USE_BSD -D_FILE_OFFSET_BITS=64" ;; darwin*) # CPPFLAGS="-D_POSIX_C_SOURCE" COPTS="$COPTS -fnested-functions" esac # # Checks for libraries. # # Rudimentary check for pthread library: if libpthread is found, then # link it in, otherwise pray it'll be in libc like FreeBSD's. # THREADLIB="$with_thread_libs" if test "$THREADLIB" = ""; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 printf %s "checking for pthread_create in -lpthread... " >&6; } if test ${ac_cv_lib_pthread_pthread_create+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char pthread_create (); int main (void) { return pthread_create (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_pthread_pthread_create=yes else $as_nop ac_cv_lib_pthread_pthread_create=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 printf "%s\n" "$ac_cv_lib_pthread_pthread_create" >&6; } if test "x$ac_cv_lib_pthread_pthread_create" = xyes then : THREADLIB="-lpthread" fi fi if test "$THREADLIB" = ""; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lc_r" >&5 printf %s "checking for pthread_create in -lc_r... " >&6; } if test ${ac_cv_lib_c_r_pthread_create+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lc_r $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char pthread_create (); int main (void) { return pthread_create (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_c_r_pthread_create=yes else $as_nop ac_cv_lib_c_r_pthread_create=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_r_pthread_create" >&5 printf "%s\n" "$ac_cv_lib_c_r_pthread_create" >&6; } if test "x$ac_cv_lib_c_r_pthread_create" = xyes then : THREADLIB="-lc_r" fi fi if test "$THREADLIB" = ""; then THREADLIB= fi CPPFLAGS="$CPPFLAGS $with_pthread_cflags" # # Check where to find clock_gettime. In Linux it is found in librt where # in BSD its in libc. # TIMELIB="$with_time_lib" if test "$TIMELIB" = ""; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lc" >&5 printf %s "checking for clock_gettime in -lc... " >&6; } if test ${ac_cv_lib_c_clock_gettime+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char clock_gettime (); int main (void) { return clock_gettime (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_c_clock_gettime=yes else $as_nop ac_cv_lib_c_clock_gettime=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_clock_gettime" >&5 printf "%s\n" "$ac_cv_lib_c_clock_gettime" >&6; } if test "x$ac_cv_lib_c_clock_gettime" = xyes then : TIMELIB="-lc" fi fi if test "$TIMELIB" = ""; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5 printf %s "checking for clock_gettime in -lrt... " >&6; } if test ${ac_cv_lib_rt_clock_gettime+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char clock_gettime (); int main (void) { return clock_gettime (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_rt_clock_gettime=yes else $as_nop ac_cv_lib_rt_clock_gettime=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5 printf "%s\n" "$ac_cv_lib_rt_clock_gettime" >&6; } if test "x$ac_cv_lib_rt_clock_gettime" = xyes then : TIMELIB="-lrt" fi fi if test "$TIMELIB" = ""; then TIMELIB= fi CPPFLAGS="$CPPFLAGS $with_time_cflags" # # Checks for various header files. # #AC_HEADER_STDC ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "endian.h" "ac_cv_header_endian_h" "$ac_includes_default" if test "x$ac_cv_header_endian_h" = xyes then : printf "%s\n" "#define HAVE_ENDIAN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/endian.h" "ac_cv_header_sys_endian_h" "$ac_includes_default" if test "x$ac_cv_header_sys_endian_h" = xyes then : printf "%s\n" "#define HAVE_SYS_ENDIAN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "machine/endian.h" "ac_cv_header_machine_endian_h" "$ac_includes_default" if test "x$ac_cv_header_machine_endian_h" = xyes then : printf "%s\n" "#define HAVE_MACHINE_ENDIAN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "machine/bswap.h" "ac_cv_header_machine_bswap_h" "$ac_includes_default" if test "x$ac_cv_header_machine_bswap_h" = xyes then : printf "%s\n" "#define HAVE_MACHINE_BSWAP_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/bswap.h" "ac_cv_header_sys_bswap_h" "$ac_includes_default" if test "x$ac_cv_header_sys_bswap_h" = xyes then : printf "%s\n" "#define HAVE_SYS_BSWAP_H 1" >>confdefs.h fi for ac_header in machine/int_fmtio.h do : ac_fn_c_check_header_compile "$LINENO" "machine/int_fmtio.h" "ac_cv_header_machine_int_fmtio_h" "$ac_includes_default" if test "x$ac_cv_header_machine_int_fmtio_h" = xyes then : printf "%s\n" "#define HAVE_MACHINE_INT_FMTIO_H 1" >>confdefs.h : else $as_nop CPPFLAGS="$CPPFLAGS -DNO_INT_FMTIO" fi done # # Check if we have the strlcpy function allready or have to emulate it # ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy" if test "x$ac_cv_func_strlcpy" = xyes then : : else $as_nop CPPFLAGS="$CPPFLAGS -DNO_STRLCPY" fi # # Check struct stat # stat_cppflags="" ac_fn_c_check_member "$LINENO" "struct stat" "st_atimespec" "ac_cv_member_struct_stat_st_atimespec" "$ac_includes_default" if test "x$ac_cv_member_struct_stat_st_atimespec" = xyes then : printf "%s\n" "#define HAVE_STRUCT_STAT_ST_ATIMESPEC 1" >>confdefs.h : else $as_nop # Non Posix struct stat, assume Linux ac_fn_c_check_member "$LINENO" "struct stat" "st_atim" "ac_cv_member_struct_stat_st_atim" "$ac_includes_default" if test "x$ac_cv_member_struct_stat_st_atim" = xyes then : printf "%s\n" "#define HAVE_STRUCT_STAT_ST_ATIM 1" >>confdefs.h stat_cppflags="-Dst_atimespec=st_atim -Dst_ctimespec=st_ctim -Dst_mtimespec=st_mtim" else $as_nop echo "Failed to determine struct stat's timespec names" exit fi fi ac_fn_c_check_member "$LINENO" "struct stat" "st_birthtimespec" "ac_cv_member_struct_stat_st_birthtimespec" "$ac_includes_default" if test "x$ac_cv_member_struct_stat_st_birthtimespec" = xyes then : printf "%s\n" "#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC 1" >>confdefs.h : else $as_nop stat_cppflags="$stat_cppflags -DNO_STAT_BIRTHTIME" fi CPPFLAGS="$CPPFLAGS $stat_cppflags" # # Check struct dirent # dirent_cppflags="" ac_fn_c_check_member "$LINENO" "struct dirent" "d_namlen" "ac_cv_member_struct_dirent_d_namlen" "$ac_includes_default" if test "x$ac_cv_member_struct_dirent_d_namlen" = xyes then : printf "%s\n" "#define HAVE_STRUCT_DIRENT_D_NAMLEN 1" >>confdefs.h : else $as_nop dirent_cppflags="$dirent_cppflags -DNO_DIRENT_NAMLEN" fi CPPFLAGS="$CPPFLAGS $dirent_cppflags" # # Check for SCSI implementations # HAVE_SCSI="yes" SCSI_CFLAGS="$with_scsi_cflags" SCSI_LIB="$with_uscsi_lib" if test "$SCSI_CFLAGS" = ""; then HAVE_SCSI=no fi # NetBSD SCSI stack if test "$HAVE_SCSI" = "no"; then for ac_header in dev/scsipi/scsipi_all.h do : ac_fn_c_check_header_compile "$LINENO" "dev/scsipi/scsipi_all.h" "ac_cv_header_dev_scsipi_scsipi_all_h" "$ac_includes_default" if test "x$ac_cv_header_dev_scsipi_scsipi_all_h" = xyes then : printf "%s\n" "#define HAVE_DEV_SCSIPI_SCSIPI_ALL_H 1" >>confdefs.h SCSI_CFLAGS="-DUSCSI_SCSIPI" HAVE_SCSI=yes fi done fi # Linux SCSI stack if test "$HAVE_SCSI" = "no"; then for ac_header in scsi/sg.h do : ac_fn_c_check_header_compile "$LINENO" "scsi/sg.h" "ac_cv_header_scsi_sg_h" "$ac_includes_default" if test "x$ac_cv_header_scsi_sg_h" = xyes then : printf "%s\n" "#define HAVE_SCSI_SG_H 1" >>confdefs.h SCSI_CFLAGS="-DUSCSI_LINUX_SCSI" HAVE_SCSI=yes fi done fi # FreeBSD SCSI stack if test "$HAVE_SCSI" = "no"; then for ac_header in camlib.h do : ac_fn_c_check_header_compile "$LINENO" "camlib.h" "ac_cv_header_camlib_h" "$ac_includes_default" if test "x$ac_cv_header_camlib_h" = xyes then : printf "%s\n" "#define HAVE_CAMLIB_H 1" >>confdefs.h SCSI_CFLAGS="-DUSCSI_FREEBSD_CAM" HAVE_SCSI=yes SCSI_LIB="-lcam" fi done fi # OpenBSD SCSI stack if test "$HAVE_SCSI" = "no"; then for ac_header in scsi/scsi_all.h do : ac_fn_c_check_header_compile "$LINENO" "scsi/scsi_all.h" "ac_cv_header_scsi_scsi_all_h" "$ac_includes_default" if test "x$ac_cv_header_scsi_scsi_all_h" = xyes then : printf "%s\n" "#define HAVE_SCSI_SCSI_ALL_H 1" >>confdefs.h CPPFLAGS="$CPPFLAGS -DUSCSI_SCSIPI" HAVE_SCSI=yes fi done fi # process SCSI stack presence results if test "$HAVE_SCSI" = "yes"; then CPPFLAGS="$CPPFLAGS -DSCSI $SCSI_CFLAGS" BUILD_APPS="\$(APPS) \$(SCSI_APPS)" else SCSI_LIB= BUILD_APPS="\$(APPS)" fi # # Generate output files # ac_config_files="$ac_config_files Makefile" # AC_CONFIG_FILES([config.h]) cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by udfclient $as_me 0.8.12, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ udfclient config.status 0.8.12 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi echo "" echo "Compile the project with Posix compliant make; possibly installed as bmake or pmake" linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/configure.ac000066400000000000000000000074131523477242100264340ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.57) AC_INIT([udfclient], [0.8.12], [reinoud@NetBSD.org]) #AC_CONFIG_SRCDIR([udf.c]) AC_CANONICAL_HOST #AC_CONFIG_HEADER([config.h]) # # Checks for programs. # AC_PROG_CC AC_PROG_INSTALL # # Check for OS dependent flags # case $host_os in netbsd*) POSTOBJ="-lprop -lutil" ;; linux*) CPPFLAGS="$CPPFLAGS -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=500 -D__USE_BSD -D_FILE_OFFSET_BITS=64" ;; darwin*) # CPPFLAGS="-D_POSIX_C_SOURCE" COPTS="$COPTS -fnested-functions" esac # # Checks for libraries. # # Rudimentary check for pthread library: if libpthread is found, then # link it in, otherwise pray it'll be in libc like FreeBSD's. # THREADLIB="$with_thread_libs" if test "$THREADLIB" = ""; then AC_CHECK_LIB(pthread, pthread_create, [THREADLIB="-lpthread"]) fi if test "$THREADLIB" = ""; then AC_CHECK_LIB(c_r, pthread_create, [THREADLIB="-lc_r"]) fi if test "$THREADLIB" = ""; then THREADLIB= fi AC_SUBST(THREADLIB) CPPFLAGS="$CPPFLAGS $with_pthread_cflags" # # Check where to find clock_gettime. In Linux it is found in librt where # in BSD its in libc. # TIMELIB="$with_time_lib" if test "$TIMELIB" = ""; then AC_CHECK_LIB(c, clock_gettime, [TIMELIB="-lc"]) fi if test "$TIMELIB" = ""; then AC_CHECK_LIB(rt, clock_gettime, [TIMELIB="-lrt"]) fi if test "$TIMELIB" = ""; then TIMELIB= fi AC_SUBST(TIMELIB) CPPFLAGS="$CPPFLAGS $with_time_cflags" # # Checks for various header files. # #AC_HEADER_STDC AC_CHECK_HEADERS([endian.h sys/endian.h machine/endian.h]) AC_CHECK_HEADERS([machine/bswap.h sys/bswap.h]) AC_CHECK_HEADERS([machine/int_fmtio.h], [:], [ CPPFLAGS="$CPPFLAGS -DNO_INT_FMTIO" ]) # # Check if we have the strlcpy function allready or have to emulate it # AC_CHECK_FUNC([strlcpy], [:], [ CPPFLAGS="$CPPFLAGS -DNO_STRLCPY" ]) # # Check struct stat # stat_cppflags="" AC_CHECK_MEMBERS([struct stat.st_atimespec], [:], [ # Non Posix struct stat, assume Linux AC_CHECK_MEMBERS([struct stat.st_atim], [ stat_cppflags="-Dst_atimespec=st_atim -Dst_ctimespec=st_ctim -Dst_mtimespec=st_mtim" ], [ echo "Failed to determine struct stat's timespec names" exit ]) ]) AC_CHECK_MEMBERS([struct stat.st_birthtimespec], [:], [ stat_cppflags="$stat_cppflags -DNO_STAT_BIRTHTIME" ]) CPPFLAGS="$CPPFLAGS $stat_cppflags" # # Check struct dirent # dirent_cppflags="" AC_CHECK_MEMBERS([struct dirent.d_namlen], [:], [ dirent_cppflags="$dirent_cppflags -DNO_DIRENT_NAMLEN" ]) CPPFLAGS="$CPPFLAGS $dirent_cppflags" # # Check for SCSI implementations # HAVE_SCSI="yes" SCSI_CFLAGS="$with_scsi_cflags" SCSI_LIB="$with_uscsi_lib" if test "$SCSI_CFLAGS" = ""; then HAVE_SCSI=no fi # NetBSD SCSI stack if test "$HAVE_SCSI" = "no"; then AC_CHECK_HEADERS([dev/scsipi/scsipi_all.h], [ SCSI_CFLAGS="-DUSCSI_SCSIPI" HAVE_SCSI=yes ]) fi # Linux SCSI stack if test "$HAVE_SCSI" = "no"; then AC_CHECK_HEADERS([scsi/sg.h], [ SCSI_CFLAGS="-DUSCSI_LINUX_SCSI" HAVE_SCSI=yes ]) fi # FreeBSD SCSI stack if test "$HAVE_SCSI" = "no"; then AC_CHECK_HEADERS([camlib.h], [ SCSI_CFLAGS="-DUSCSI_FREEBSD_CAM" HAVE_SCSI=yes SCSI_LIB="-lcam" ]) fi # OpenBSD SCSI stack if test "$HAVE_SCSI" = "no"; then AC_CHECK_HEADERS([scsi/scsi_all.h], [ CPPFLAGS="$CPPFLAGS -DUSCSI_SCSIPI" HAVE_SCSI=yes ]) fi # process SCSI stack presence results if test "$HAVE_SCSI" = "yes"; then CPPFLAGS="$CPPFLAGS -DSCSI $SCSI_CFLAGS" BUILD_APPS="\$(APPS) \$(SCSI_APPS)" else SCSI_LIB= BUILD_APPS="\$(APPS)" fi AC_SUBST(BUILD_APPS) AC_SUBST(SCSI_LIB) AC_SUBST(CPPFLAGS) AC_SUBST(POSTOBJ) # # Generate output files # AC_CONFIG_FILES([Makefile]) # AC_CONFIG_FILES([config.h]) AC_OUTPUT echo "" echo "Compile the project with Posix compliant make; possibly installed as bmake or pmake" linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/defs.h000066400000000000000000000056011523477242100252350ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf.h" is part of the UDFclient toolkit. * File $Id: defs.h,v 1.8 2016/04/26 19:35:20 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _DEFS_H_ #define _DEFS_H_ #include #include #include #include #include #include #ifdef NO_INT_FMTIO /* assume 32 bits :-/ */ #ifndef PRIu32 # define PRIu32 "u" # define PRIx32 "x" # define PRIu64 "lld" # define PRIx64 "llx" #endif #endif /* exported flags */ extern int udf_verbose; #define UDF_MNT_RDONLY 1 #define UDF_MNT_FORCE 2 #define UDF_VERBLEV_NONE 0 #define UDF_VERBLEV_ACTIONS 1 #define UDF_VERBLEV_TABLES 2 #define UDF_VERBLEV_MAX 3 /* constants to identify what kind of identifier we are dealing with */ #define UDF_REGID_DOMAIN 1 #define UDF_REGID_UDF 2 #define UDF_REGID_IMPLEMENTATION 3 #define UDF_REGID_APPLICATION 4 #define UDF_REGID_NAME 99 /* 99? */ /* DON'T change these: they identify 13thmonkey's UDF toolkit */ #define APP_NAME "*UDFtoolkit" #define APP_VERSION_MAIN 0 #define APP_VERSION_SUB 8 #define IMPL_NAME "*13thMonkey UDFtoolkit" /* RW content hint for allocation and other purposes */ #define UDF_C_DSCR 0 #define UDF_C_USERDATA 1 #define UDF_C_FIDS 2 #define UDF_C_NODE 3 /* end of Configuration */ #if 1 # define UDF_VERBOSE(op) if (udf_verbose) { op; } # define UDF_VERBOSE_LEVEL(level, op) if (udf_verbose >= (level)) { op; } # define UDF_VERBOSE_TABLES(op) UDF_VERBOSE_LEVEL(UDF_VERBLEV_TABLES, op) # define UDF_VERBOSE_MAX(op) UDF_VERBOSE_LEVEL(UDF_VERBLEV_MAX, op) #else # define UDF_VERBOSE(op) # define UDF_VERBOSE_LEVEL(level, op) # define UDF_VERBOSE_TABLES(op) # define UDF_VERBOSE_MAX(op) #endif #endif /* _DEFS_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/dirhash.h000066400000000000000000000062351523477242100257420ustar00rootroot00000000000000/* $NetBSD: dirhash.h,v 1.4 2008/10/30 23:02:12 rmind Exp $ */ /* * Copyright (c) 2008, 2011 Reinoud Zandijk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SYS_DIRHASH_H_ #define _SYS_DIRHASH_H_ #include #include "queue.h" #ifndef DIRHASH_SIZE #define DIRHASH_SIZE (1024*1024) #endif #define DIRHASH_HASHBITS 5 #define DIRHASH_HASHSIZE (1 << DIRHASH_HASHBITS) #define DIRHASH_HASHMASK (DIRHASH_HASHSIZE - 1) #ifdef NO_DIRENT_NAMLEN # define DIRENT_NAMLEN(d) strlen((d)->d_name) #else # define DIRENT_NAMLEN(d) (d)->d_namlen #endif #ifndef _DIRENT_SIZE /* guess */ # define _DIRENT_SIZE(d) (32 + DIRENT_NAMLEN(d)) #endif /* dirent's d_namlen is to avoid useless costly fid->dirent translations */ struct dirhash_entry { uint32_t hashvalue; uint64_t offset; uint32_t d_namlen; uint32_t entry_size; LIST_ENTRY(dirhash_entry) next; }; struct dirhash { uint32_t flags; uint32_t size; /* in bytes */ uint32_t refcnt; LIST_HEAD(, dirhash_entry) entries[DIRHASH_HASHSIZE]; LIST_HEAD(, dirhash_entry) free_entries; TAILQ_ENTRY(dirhash) next; }; #define DIRH_PURGED 0x0001 /* dirhash has been purged */ #define DIRH_COMPLETE 0x0002 /* dirhash is complete */ #define DIRH_BROKEN 0x0004 /* dirhash is broken on readin */ #define DIRH_FLAGBITS "\10\1DIRH_PURGED\2DIRH_COMPLETE\3DIRH_BROKEN" void dirhash_init(void); /* void dirhash_finish(void); */ void dirhash_purge(struct dirhash **); void dirhash_purge_entries(struct dirhash *); void dirhash_get(struct dirhash **); void dirhash_put(struct dirhash *); void dirhash_enter(struct dirhash *, struct dirent *, uint64_t, uint32_t, int); void dirhash_enter_freed(struct dirhash *, uint64_t, uint32_t); void dirhash_mark_freed(struct dirhash *, struct dirhash_entry *, struct dirent *); int dirhash_lookup(struct dirhash *, const char *, int, struct dirhash_entry **); int dirhash_lookup_freed(struct dirhash *, uint32_t, struct dirhash_entry **); #endif /* _SYS_DIRHASH_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/ecma167-udf.h000066400000000000000000000534471523477242100262460ustar00rootroot00000000000000/* $NetBSD: ecma167-udf.h,v 1.14 2011/07/07 17:45:38 reinoud Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2008, 2009, 2017, 2018 * Reinoud Zandijk * Copyright (c) 2001, 2002 Scott Long * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * * Extended and adapted for UDFv2.50+ bij Reinoud Zandijk based on the * original by Scott Long. * * 20030508 Made some small typo and explanatory comments * 20030510 Added UDF 2.01 structures * 20030519 Added/correct comments on multi-partitioned logical volume space * 20050616 Added pseudo overwrite * 20050624 Added the missing extended attribute types and `magic values'. * 20051106 Reworked some implementation use parts * */ #ifndef _FS_UDF_ECMA167_UDF_H_ #define _FS_UDF_ECMA167_UDF_H_ /* * in case of an older gcc versions, define the __packed as explicit * attribute */ /* * You may specify the `aligned' and `transparent_union' attributes either in * a `typedef' declaration or just past the closing curly brace of a complete * enum, struct or union type _definition_ and the `packed' attribute only * past the closing brace of a definition. You may also specify attributes * between the enum, struct or union tag and the name of the type rather than * after the closing brace. */ #ifndef __packed #define __packed __attribute__((packed)) #endif /* ecma167-udf.h */ /* Volume recognition sequence ECMA 167 rev. 3 16.1 */ struct vrs_desc { uint8_t struct_type; uint8_t identifier[5]; uint8_t version; uint8_t data[2041]; } __packed; #define VRS_NSR02 "NSR02" #define VRS_NSR03 "NSR03" #define VRS_BEA01 "BEA01" #define VRS_TEA01 "TEA01" #define VRS_CD001 "CD001" #define VRS_CDW02 "CDW02" /* Structure/definitions/constants a la ECMA 167 rev. 3 */ #define MAX_TAGID_VOLUMES 9 /* Tag identifiers */ enum { TAGID_SPARING_TABLE = 0, TAGID_PRI_VOL = 1, TAGID_ANCHOR = 2, TAGID_VOL = 3, TAGID_IMP_VOL = 4, TAGID_PARTITION = 5, TAGID_LOGVOL = 6, TAGID_UNALLOC_SPACE = 7, TAGID_TERM = 8, TAGID_LOGVOL_INTEGRITY= 9, TAGID_FSD = 256, TAGID_FID = 257, TAGID_ALLOCEXTENT = 258, TAGID_INDIRECTENTRY = 259, TAGID_ICB_TERM = 260, TAGID_FENTRY = 261, TAGID_EXTATTR_HDR = 262, TAGID_UNALL_SP_ENTRY = 263, TAGID_SPACE_BITMAP = 264, TAGID_PART_INTEGRITY = 265, TAGID_EXTFENTRY = 266, TAGID_MAX = 266 }; enum { UDF_DOMAIN_FLAG_HARD_WRITE_PROTECT = 1, UDF_DOMAIN_FLAG_SOFT_WRITE_PROTECT = 2 }; enum { UDF_ACCESSTYPE_NOT_SPECIFIED = 0, /* unknown */ UDF_ACCESSTYPE_PSEUDO_OVERWITE = 0, /* pseudo overwritable, e.g. BD-R's LOW */ UDF_ACCESSTYPE_READ_ONLY = 1, /* really only readable */ UDF_ACCESSTYPE_WRITE_ONCE = 2, /* write once and you're done */ UDF_ACCESSTYPE_REWRITEABLE = 3, /* may need extra work to rewrite */ UDF_ACCESSTYPE_OVERWRITABLE = 4 /* no limits on rewriting; e.g. harddisc*/ }; /* Descriptor tag [3/7.2] */ struct desc_tag { uint16_t id; uint16_t descriptor_ver; uint8_t cksum; uint8_t reserved; uint16_t serial_num; uint16_t desc_crc; uint16_t desc_crc_len; uint32_t tag_loc; } __packed; #define UDF_DESC_TAG_LENGTH 16 /* Recorded Address [4/7.1] */ struct lb_addr { /* within partition space */ uint32_t lb_num; uint16_t part_num; } __packed; /* Extent Descriptor [3/7.1] */ struct extent_ad { uint32_t len; uint32_t loc; } __packed; /* Short Allocation Descriptor [4/14.14.1] */ struct short_ad { uint32_t len; uint32_t lb_num; } __packed; /* Long Allocation Descriptor [4/14.14.2] */ struct UDF_ADImp_use { uint16_t flags; uint32_t unique_id; } __packed; #define UDF_ADIMP_FLAGS_EXTENT_ERASED 1 struct long_ad { uint32_t len; struct lb_addr loc; /* within a logical volume mapped partition space !! */ union { uint8_t bytes[6]; struct UDF_ADImp_use im_used; } impl; } __packed; #define longad_uniqueid impl.im_used.unique_id /* Extended Allocation Descriptor [4/14.14.3] ; identifies an extent of allocation descriptors ; also in UDF ? */ struct ext_ad { uint32_t ex_len; uint32_t rec_len; uint32_t inf_len; struct lb_addr ex_loc; uint8_t reserved[2]; } __packed; /* ICB : Information Control Block; positioning */ union icb { struct short_ad s_ad; struct long_ad l_ad; struct ext_ad e_ad; }; /* short/long/ext extent have flags encoded in length */ #define UDF_EXT_ALLOCATED (0U<<30) #define UDF_EXT_FREED (1U<<30) #define UDF_EXT_ALLOCATED_BUT_NOT_USED (1U<<30) #define UDF_EXT_FREE (2U<<30) #define UDF_EXT_REDIRECT (3U<<30) #define UDF_EXT_FLAGS(len) ((len) & (3U<<30)) #define UDF_EXT_LEN(len) ((len) & ((1U<<30)-1)) #define UDF_EXT_MAXLEN ((1U<<30)-1) /* Character set spec [1/7.2.1] */ struct charspec { uint8_t type; uint8_t inf[63]; } __packed; struct pathcomp { uint8_t type; uint8_t l_ci; uint16_t comp_filever; uint8_t ident[256]; } __packed; #define UDF_PATH_COMP_SIZE 4 #define UDF_PATH_COMP_RESERVED 0 #define UDF_PATH_COMP_ROOT 1 #define UDF_PATH_COMP_MOUNTROOT 2 #define UDF_PATH_COMP_PARENTDIR 3 #define UDF_PATH_COMP_CURDIR 4 #define UDF_PATH_COMP_NAME 5 /* Timestamp [1/7.3] */ struct timestamp { uint16_t type_tz; uint16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t minute; uint8_t second; uint8_t centisec; uint8_t hund_usec; uint8_t usec; } __packed; #define UDF_TIMESTAMP_SIZE 12 /* Entity Identifier [1/7.4] */ #define UDF_REGID_ID_SIZE 23 struct regid { uint8_t flags; uint8_t id[UDF_REGID_ID_SIZE]; uint8_t id_suffix[8]; } __packed; /* ICB Tag [4/14.6] */ struct icb_tag { uint32_t prev_num_dirs; uint16_t strat_type; union { uint8_t strat_param[2]; uint16_t strat_param16; }; uint16_t max_num_entries; uint8_t reserved; uint8_t file_type; struct lb_addr parent_icb; uint16_t flags; } __packed; #define UDF_ICB_TAG_FLAGS_ALLOC_MASK 0x03 #define UDF_ICB_SHORT_ALLOC 0x00 #define UDF_ICB_LONG_ALLOC 0x01 #define UDF_ICB_EXT_ALLOC 0x02 #define UDF_ICB_INTERN_ALLOC 0x03 #define UDF_ICB_TAG_FLAGS_DIRORDERED (1<< 3) #define UDF_ICB_TAG_FLAGS_NONRELOC (1<< 4) #define UDF_ICB_TAG_FLAGS_CONTIGUES (1<< 9) #define UDF_ICB_TAG_FLAGS_MULTIPLEVERS (1<<12) #define UDF_ICB_TAG_FLAGS_SETUID (1<< 6) #define UDF_ICB_TAG_FLAGS_SETGID (1<< 7) #define UDF_ICB_TAG_FLAGS_STICKY (1<< 8) #define UDF_ICB_FILETYPE_UNKNOWN 0 #define UDF_ICB_FILETYPE_UNALLOCSPACE 1 #define UDF_ICB_FILETYPE_PARTINTEGRITY 2 #define UDF_ICB_FILETYPE_INDIRECTENTRY 3 #define UDF_ICB_FILETYPE_DIRECTORY 4 #define UDF_ICB_FILETYPE_RANDOMACCESS 5 #define UDF_ICB_FILETYPE_BLOCKDEVICE 6 #define UDF_ICB_FILETYPE_CHARDEVICE 7 #define UDF_ICB_FILETYPE_EXTATTRREC 8 #define UDF_ICB_FILETYPE_FIFO 9 #define UDF_ICB_FILETYPE_SOCKET 10 #define UDF_ICB_FILETYPE_TERM 11 #define UDF_ICB_FILETYPE_SYMLINK 12 #define UDF_ICB_FILETYPE_STREAMDIR 13 #define UDF_ICB_FILETYPE_VAT 248 #define UDF_ICB_FILETYPE_REALTIME 249 #define UDF_ICB_FILETYPE_META_MAIN 250 #define UDF_ICB_FILETYPE_META_MIRROR 251 #define UDF_ICB_FILETYPE_META_BITMAP 252 /* Anchor Volume Descriptor Pointer [3/10.2] */ struct anchor_vdp { struct desc_tag tag; struct extent_ad main_vds_ex; /* to main volume descriptor set ; 16 sectors min */ struct extent_ad reserve_vds_ex; /* copy of main volume descriptor set ; 16 sectors min */ } __packed; /* Volume Descriptor Pointer [3/10.3] */ struct vol_desc_ptr { struct desc_tag tag; /* use for extending the volume descriptor space */ uint32_t vds_number; struct extent_ad next_vds_ex; /* points to the next block for volume descriptor space */ } __packed; /* Primary Volume Descriptor [3/10.1] */ struct pri_vol_desc { struct desc_tag tag; uint32_t seq_num; /* MAX prevail */ uint32_t pvd_num; /* assigned by author; 0 is special as in it may only occur once */ char vol_id[32]; /* KEY ; main identifier of this disc */ uint16_t vds_num; /* volume descriptor number; i.e. what volume number is it */ uint16_t max_vol_seq; /* maximum volume descriptor number known */ uint16_t ichg_lvl; uint16_t max_ichg_lvl; uint32_t charset_list; uint32_t max_charset_list; char volset_id[128]; /* KEY ; if part of a multi-disc set or a band of volumes */ struct charspec desc_charset; /* KEY according to ECMA 167 */ struct charspec explanatory_charset; struct extent_ad vol_abstract; struct extent_ad vol_copyright; struct regid app_id; struct timestamp time; struct regid imp_id; uint8_t imp_use[64]; uint32_t prev_vds_loc; /* location of predecessor _lov ? */ uint16_t flags; /* bit 0 : if set indicates volume set name is meaningful */ uint8_t reserved[22]; } __packed; /* UDF specific implementation use part of the implementation use volume descriptor */ struct udf_lv_info { struct charspec lvi_charset; char logvol_id[128]; char lvinfo1[36]; char lvinfo2[36]; char lvinfo3[36]; struct regid impl_id; uint8_t impl_use[128]; } __packed; /* Implementation use Volume Descriptor */ struct impvol_desc { struct desc_tag tag; uint32_t seq_num; struct regid impl_id; union { struct udf_lv_info lv_info; char impl_use[460]; } _impl_use; } __packed; /* Logical Volume Descriptor [3/10.6] */ struct logvol_desc { struct desc_tag tag; uint32_t seq_num; /* MAX prevail */ struct charspec desc_charset; /* KEY */ char logvol_id[128]; /* KEY */ uint32_t lb_size; struct regid domain_id; union { struct long_ad fsd_loc; /* to fileset descriptor SEQUENCE */ uint8_t logvol_content_use[16]; } _lvd_use; uint32_t mt_l; /* Partition map length */ uint32_t n_pm; /* Number of partition maps */ struct regid imp_id; uint8_t imp_use[128]; struct extent_ad integrity_seq_loc; uint8_t maps[1]; } __packed; #define lv_fsd_loc _lvd_use.fsd_loc #define UDF_INTEGRITY_OPEN 0 #define UDF_INTEGRITY_CLOSED 1 #define UDF_PMAP_SIZE 64 /* Type 1 Partition Map [3/10.7.2] */ struct part_map_1 { uint8_t type; uint8_t len; uint16_t vol_seq_num; uint16_t part_num; } __packed; /* Type 2 Partition Map [3/10.7.3] */ struct part_map_2 { uint8_t type; uint8_t len; uint8_t reserved[2]; struct regid part_id; uint16_t vol_seq_num; uint16_t part_num; uint8_t reserved2[24]; } __packed; /* Virtual Partition Map [UDF 2.01/2.2.8] */ struct part_map_virt { uint8_t type; uint8_t len; uint8_t reserved[2]; struct regid id; uint16_t vol_seq_num; uint16_t part_num; uint8_t reserved1[24]; } __packed; /* Sparable Partition Map [UDF 2.01/2.2.9] */ struct part_map_spare { uint8_t type; uint8_t len; uint8_t reserved[2]; struct regid id; uint16_t vol_seq_num; uint16_t part_num; uint16_t packet_len; uint8_t n_st; /* Number of redundant sparing tables range 1-4 */ uint8_t reserved1; uint32_t st_size; /* size of EACH sparing table */ uint32_t st_loc[1]; /* locations of sparing tables */ } __packed; /* Metadata Partition Map [UDF 2.50/2.2.10] */ struct part_map_meta { uint8_t type; uint8_t len; uint8_t reserved[2]; struct regid id; uint16_t vol_seq_num; uint16_t part_num; uint32_t meta_file_lbn; /* logical block number for file entry within part_num */ uint32_t meta_mirror_file_lbn; uint32_t meta_bitmap_file_lbn; uint32_t alloc_unit_size; /* allocation unit size in blocks */ uint16_t alignment_unit_size; /* alignment necessary in blocks */ uint8_t flags; uint8_t reserved1[5]; } __packed; #define METADATA_DUPLICATED 1 union udf_pmap { uint8_t data[UDF_PMAP_SIZE]; struct part_map_1 pm1; struct part_map_2 pm2; struct part_map_virt pmv; struct part_map_spare pms; struct part_map_meta pmm; }; /* Sparing Map Entry [UDF 2.01/2.2.11] */ struct spare_map_entry { uint32_t org; /* partition relative address */ uint32_t map; /* absolute disc address (!) can be in partition, but doesn't have to be */ } __packed; /* Sparing Table [UDF 2.01/2.2.11] */ struct udf_sparing_table { struct desc_tag tag; struct regid id; uint16_t rt_l; /* Relocation Table len */ uint8_t reserved[2]; uint32_t seq_num; struct spare_map_entry entries[1]; } __packed; #define UDF_NO_PREV_VAT 0xffffffff /* UDF 1.50 VAT suffix [UDF 2.2.10 (UDF 1.50 spec)] */ struct udf_oldvat_tail { struct regid id; /* "*UDF Virtual Alloc Tbl" */ uint32_t prev_vat; } __packed; /* VAT table [UDF 2.0.1/2.2.10] */ struct udf_vat { uint16_t header_len; uint16_t impl_use_len; char logvol_id[128]; /* newer version of the LVD one */ uint32_t prev_vat; uint32_t num_files; uint32_t num_directories; uint16_t min_udf_readver; uint16_t min_udf_writever; uint16_t max_udf_writever; uint16_t reserved; uint8_t data[1]; /* impl.use followed by VAT entries (uint32_t) */ } __packed; /* Space bitmap descriptor as found in the partition header descriptor */ struct space_bitmap_desc { struct desc_tag tag; /* TagId 264 */ uint32_t num_bits; /* number of bits */ uint32_t num_bytes; /* bytes that contain it */ uint8_t data[1]; } __packed; /* Unalloc space entry as found in the partition header descriptor */ struct space_entry_desc { struct desc_tag tag; /* TagId 263 */ struct icb_tag icbtag; /* type 1 */ uint32_t l_ad; /* in bytes */ uint8_t entry[1]; } __packed; /* Partition header descriptor; in the contents_use of part_desc */ struct part_hdr_desc { struct short_ad unalloc_space_table; struct short_ad unalloc_space_bitmap; struct short_ad part_integrity_table; /* has to be ZERO for UDF */ struct short_ad freed_space_table; struct short_ad freed_space_bitmap; uint8_t reserved[88]; } __packed; /* Partition Descriptor [3/10.5] */ struct part_desc { struct desc_tag tag; uint32_t seq_num; /* MAX prevailing */ uint16_t flags; /* bit 0 : if set the space is allocated */ uint16_t part_num; /* KEY */ struct regid contents; union { struct part_hdr_desc part_hdr; uint8_t contents_use[128]; } _impl_use; uint32_t access_type; /* R/W, WORM etc. */ uint32_t start_loc; /* start of partition with given length */ uint32_t part_len; struct regid imp_id; uint8_t imp_use[128]; uint8_t reserved[156]; } __packed; #define pd_part_hdr _impl_use.part_hdr #define UDF_PART_FLAG_ALLOCATED 1 /* Unallocated Space Descriptor (UDF 2.01/2.2.5) */ struct unalloc_sp_desc { struct desc_tag tag; uint32_t seq_num; /* MAX prevailing */ uint32_t alloc_desc_num; struct extent_ad alloc_desc[1]; } __packed; /* Logical Volume Integrity Descriptor [3/30.10] */ struct logvolhdr { uint64_t next_unique_id; /* rest reserved */ } __packed; struct udf_logvol_info { struct regid impl_id; uint32_t num_files; uint32_t num_directories; uint16_t min_udf_readver; uint16_t min_udf_writever; uint16_t max_udf_writever; } __packed; struct logvol_int_desc { struct desc_tag tag; struct timestamp time; uint32_t integrity_type; struct extent_ad next_extent; union { struct logvolhdr logvolhdr; int8_t reserved[32]; } _impl_use; uint32_t num_part; uint32_t l_iu; uint32_t tables[1]; /* Freespace table, Sizetable, Implementation use */ } __packed; #define lvint_next_unique_id _impl_use.logvolhdr.next_unique_id /* File Set Descriptor [4/14.1] */ struct fileset_desc { struct desc_tag tag; struct timestamp time; uint16_t ichg_lvl; uint16_t max_ichg_lvl; uint32_t charset_list; uint32_t max_charset_list; uint32_t fileset_num; /* key! */ uint32_t fileset_desc_num; struct charspec logvol_id_charset; char logvol_id[128]; /* for recovery */ struct charspec fileset_charset; char fileset_id[32]; /* Mountpoint !! */ char copyright_file_id[32]; char abstract_file_id[32]; struct long_ad rootdir_icb; /* to rootdir; icb->virtual ? */ struct regid domain_id; struct long_ad next_ex; /* to the next fileset_desc extent */ struct long_ad streamdir_icb; /* streamdir; needed? */ uint8_t reserved[32]; } __packed; /* File Identifier Descriptor [4/14.4] */ struct fileid_desc { struct desc_tag tag; uint16_t file_version_num; uint8_t file_char; uint8_t l_fi; /* Length of file identifier area */ struct long_ad icb; uint16_t l_iu; /* Length of implementation use area */ uint8_t data[0]; } __packed; #define UDF_FID_SIZE 38 #define UDF_FILE_CHAR_VIS (1 << 0) /* Invisible */ #define UDF_FILE_CHAR_DIR (1 << 1) /* Directory */ #define UDF_FILE_CHAR_DEL (1 << 2) /* Deleted */ #define UDF_FILE_CHAR_PAR (1 << 3) /* Parent Directory */ #define UDF_FILE_CHAR_META (1 << 4) /* Stream metadata */ /* Extended attributes [4/14.10.1] */ struct extattrhdr_desc { struct desc_tag tag; uint32_t impl_attr_loc; /* offsets within this descriptor */ uint32_t appl_attr_loc; /* ditto */ } __packed; #define UDF_IMPL_ATTR_LOC_NOT_PRESENT 0xffffffff #define UDF_APPL_ATTR_LOC_NOT_PRESENT 0xffffffff /* Extended attribute entry [4/48.10.2] */ struct extattr_entry { uint32_t type; uint8_t subtype; uint8_t reserved[3]; uint32_t a_l; } __packed; /* Extended attribute entry; type 2048 [4/48.10.8] */ struct impl_extattr_entry { struct extattr_entry hdr; uint32_t iu_l; struct regid imp_id; union { uint8_t data[1]; uint16_t data16; }; } __packed; /* Extended attribute entry; type 65 536 [4/48.10.9] */ struct appl_extattr_entry { struct extattr_entry hdr; uint32_t au_l; struct regid appl_id; uint8_t data[1]; } __packed; /* File Times attribute entry; type 5 or type 6 [4/48.10.5], [4/48.10.6] */ struct filetimes_extattr_entry { struct extattr_entry hdr; uint32_t d_l; /* length of times[] data following */ uint32_t existence; /* bitmask */ struct timestamp times[1]; /* in order of ascending bits */ } __packed; #define UDF_FILETIMES_ATTR_NO 5 #define UDF_FILETIMES_FILE_CREATION 1 #define UDF_FILETIMES_FILE_DELETION 4 #define UDF_FILETIMES_FILE_EFFECTIVE 8 #define UDF_FILETIMES_FILE_BACKUPED 16 #define UDF_FILETIMES_ATTR_SIZE(no) (20 + (no)*sizeof(struct timestamp)) /* Device Specification Extended Attribute [4/4.10.7] */ struct device_extattr_entry { struct extattr_entry hdr; uint32_t iu_l; /* length of implementation use */ uint32_t major; uint32_t minor; uint8_t data[1]; /* UDF: if nonzero length, contain developer ID regid */ } __packed; #define UDF_DEVICESPEC_ATTR_NO 12 /* VAT LV extension Extended Attribute [UDF 3.3.4.5.1.3] 1.50 errata */ struct vatlvext_extattr_entry { uint64_t unique_id_chk; /* needs to be copy of ICB's */ uint32_t num_files; uint32_t num_directories; char logvol_id[128]; /* replaces logvol name */ } __packed; /* File Entry [4/14.9] */ struct file_entry { struct desc_tag tag; struct icb_tag icbtag; uint32_t uid; uint32_t gid; uint32_t perm; uint16_t link_cnt; uint8_t rec_format; uint8_t rec_disp_attr; uint32_t rec_len; uint64_t inf_len; uint64_t logblks_rec; struct timestamp atime; struct timestamp mtime; struct timestamp attrtime; uint32_t ckpoint; struct long_ad ex_attr_icb; struct regid imp_id; uint64_t unique_id; uint32_t l_ea; /* Length of extended attribute area */ uint32_t l_ad; /* Length of allocation descriptors */ uint8_t data[1]; } __packed; #define UDF_FENTRY_SIZE 176 #define UDF_FENTRY_PERM_USER_MASK 0x07 #define UDF_FENTRY_PERM_GRP_MASK 0xE0 #define UDF_FENTRY_PERM_OWNER_MASK 0x1C00 /* Extended File Entry [4/48.17] */ struct extfile_entry { struct desc_tag tag; struct icb_tag icbtag; uint32_t uid; uint32_t gid; uint32_t perm; uint16_t link_cnt; uint8_t rec_format; uint8_t rec_disp_attr; uint32_t rec_len; uint64_t inf_len; uint64_t obj_size; uint64_t logblks_rec; struct timestamp atime; struct timestamp mtime; struct timestamp ctime; struct timestamp attrtime; uint32_t ckpoint; uint32_t reserved1; struct long_ad ex_attr_icb; struct long_ad streamdir_icb; struct regid imp_id; uint64_t unique_id; uint32_t l_ea; /* Length of extended attribute area */ uint32_t l_ad; /* Length of allocation descriptors */ uint8_t data[1]; } __packed; #define UDF_EXTFENTRY_SIZE 216 /* Indirect entry [ecma 48.7] */ struct indirect_entry { struct desc_tag tag; struct icb_tag icbtag; struct long_ad indirect_icb; } __packed; /* Allocation extent descriptor [ecma 48.5] */ struct alloc_ext_entry { struct desc_tag tag; uint32_t prev_entry; uint32_t l_ad; uint8_t data[1]; } __packed; union dscrptr { struct desc_tag tag; struct anchor_vdp avdp; struct vol_desc_ptr vdp; struct pri_vol_desc pvd; struct logvol_desc lvd; struct unalloc_sp_desc usd; struct logvol_int_desc lvid; struct impvol_desc ivd; struct part_desc pd; struct fileset_desc fsd; struct fileid_desc fid; struct file_entry fe; struct extfile_entry efe; struct extattrhdr_desc eahd; struct indirect_entry inde; struct alloc_ext_entry aee; struct udf_sparing_table spt; struct space_bitmap_desc sbd; struct space_entry_desc sed; }; #endif /* !_FS_UDF_ECMA167_UDF_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/hash.h000066400000000000000000000063121523477242100252370ustar00rootroot00000000000000/* $NetBSD: hash.h,v 1.6 2008/04/28 20:24:10 martin Exp $ */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SYS_HASH_H_ #define _SYS_HASH_H_ #include #ifdef __HAVE_MACHINE_HASH_H #include #endif #ifndef __HAVE_HASH32_BUF /* not overridden by MD hash */ #define HASH32_BUF_INIT 5381 /* * uint32_t * hash32_buf(const void *bf, size_t len, uint32_t hash) * return a 32 bit hash of the binary buffer buf (size len), * seeded with an initial hash value of hash (usually HASH32_BUF_INIT). */ static __inline uint32_t hash32_buf(const void *bf, size_t len, uint32_t hash) { const uint8_t *s = bf; while (len-- != 0) /* "nemesi": k=257, r=r*257 */ hash = hash * 257 + *s++; return (hash * 257); } #endif /* __HAVE_HASH32_BUF */ #ifndef __HAVE_HASH32_STR /* not overridden by MD hash */ #define HASH32_STR_INIT 5381 /* * uint32_t * hash32_str(const void *bf, uint32_t hash) * return a 32 bit hash of NUL terminated ASCII string buf, * seeded with an initial hash value of hash (usually HASH32_STR_INIT). */ static __inline uint32_t hash32_str(const void *bf, uint32_t hash) { const uint8_t *s = bf; uint8_t c; while ((c = *s++) != 0) hash = hash * 33 + c; /* "perl": k=33, r=r+r/32 */ return (hash + (hash >> 5)); } /* * uint32_t * hash32_strn(const void *bf, size_t len, uint32_t hash) * return a 32 bit hash of NUL terminated ASCII string buf up to * a maximum of len bytes, * seeded with an initial hash value of hash (usually HASH32_STR_INIT). */ static __inline uint32_t hash32_strn(const void *bf, size_t len, uint32_t hash) { const uint8_t *s = bf; uint8_t c; while ((c = *s++) != 0 && len-- != 0) hash = hash * 33 + c; /* "perl": k=33, r=r+r/32 */ return (hash + (hash >> 5)); } #endif /* __HAVE_HASH32_STR */ #endif /* !_SYS_HASH_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/install-sh000077500000000000000000000127361523477242100261560ustar00rootroot00000000000000#!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/osta.c000066400000000000000000000342461523477242100252640ustar00rootroot00000000000000/* $NetBSD$ */ /* * Various routines from the OSTA 2.01 specs. Copyrights are included with * each code segment. Slight whitespace modifications have been made for * formatting purposes. Typos/bugs have been fixed. * */ #include "osta.h" /*****************************************************************************/ /*********************************************************************** * OSTA compliant Unicode compression, uncompression routines. * Copyright 1995 Micro Design International, Inc. * Written by Jason M. Rinn. * Micro Design International gives permission for the free use of the * following source code. */ /*********************************************************************** * Takes an OSTA CS0 compressed unicode name, and converts * it to Unicode. * The Unicode output will be in the byte order * that the local compiler uses for 16-bit values. * NOTE: This routine only performs error checking on the compID. * It is up to the user to ensure that the unicode buffer is large * enough, and that the compressed unicode name is correct. * * RETURN VALUE * * The number of unicode characters which were uncompressed. * A -1 is returned if the compression ID is invalid. */ int udf_UncompressUnicode( int numberOfBytes, /* (Input) number of bytes read from media. */ byte *UDFCompressed, /* (Input) bytes read from media. */ unicode_t *unicode) /* (Output) uncompressed unicode characters. */ { unsigned int compID; int returnValue, unicodeIndex, byteIndex; /* Use UDFCompressed to store current byte being read. */ compID = UDFCompressed[0]; /* First check for valid compID. */ if (compID != 8 && compID != 16) { returnValue = -1; } else { unicodeIndex = 0; byteIndex = 1; /* Loop through all the bytes. */ while (byteIndex < numberOfBytes) { if (compID == 16) { /* Move the first byte to the high bits of the * unicode char. */ unicode[unicodeIndex] = UDFCompressed[byteIndex++] << 8; } else { unicode[unicodeIndex] = 0; } if (byteIndex < numberOfBytes) { /*Then the next byte to the low bits. */ unicode[unicodeIndex] |= UDFCompressed[byteIndex++]; } unicodeIndex++; } returnValue = unicodeIndex; } return(returnValue); } /*********************************************************************** * DESCRIPTION: * Takes a string of unicode wide characters and returns an OSTA CS0 * compressed unicode string. The unicode MUST be in the byte order of * the compiler in order to obtain correct results. Returns an error * if the compression ID is invalid. * * NOTE: This routine assumes the implementation already knows, by * the local environment, how many bits are appropriate and * therefore does no checking to test if the input characters fit * into that number of bits or not. * * RETURN VALUE * * The total number of bytes in the compressed OSTA CS0 string, * including the compression ID. * A -1 is returned if the compression ID is invalid. */ int udf_CompressUnicode( int numberOfChars, /* (Input) number of unicode characters. */ int compID, /* (Input) compression ID to be used. */ unicode_t *unicode, /* (Input) unicode characters to compress. */ byte *UDFCompressed) /* (Output) compressed string, as bytes. */ { int byteIndex, unicodeIndex; if (compID != 8 && compID != 16) { byteIndex = -1; /* Unsupported compression ID ! */ } else { /* Place compression code in first byte. */ UDFCompressed[0] = compID; byteIndex = 1; unicodeIndex = 0; while (unicodeIndex < numberOfChars) { if (compID == 16) { /* First, place the high bits of the char * into the byte stream. */ UDFCompressed[byteIndex++] = (unicode[unicodeIndex] & 0xFF00) >> 8; } /*Then place the low bits into the stream. */ UDFCompressed[byteIndex++] = unicode[unicodeIndex] & 0x00FF; unicodeIndex++; } } return(byteIndex); } /*****************************************************************************/ /* * CRC 010041 */ static unsigned short crc_table[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 }; uint16_t udf_cksum(uint8_t *s, int n) { uint16_t crc = 0; while (n-- > 0) crc = crc_table[(crc>>8 ^ *s++) & 0xff] ^ (crc<<8); return crc; } /* UNICODE Checksum */ uint16_t udf_unicode_cksum(uint16_t *s, int n) { uint16_t crc = 0; while (n-- > 0) { /* Take high order byte first--corresponds to a big endian * byte stream. */ crc = crc_table[(crc>>8 ^ (*s>>8)) & 0xff] ^ (crc<<8); crc = crc_table[(crc>>8 ^ (*s++ & 0xff)) & 0xff] ^ (crc<<8); } return crc; } /* * Calculates a 16-bit checksum of the Implementation Use * Extended Attribute header or Application Use Extended Attribute * header. The fields AttributeType through ImplementationIdentifier * (or ApplicationIdentifier) inclusively represent the * data covered by the checksum (48 bytes). * */ uint16_t udf_ea_cksum(uint8_t *data) { uint16_t checksum = 0; int count; for (count = 0; count < 48; count++) { checksum += *data++; } return checksum; } #ifdef MAIN unsigned char bytes[] = { 0x70, 0x6A, 0x77 }; main() { unsigned short x; x = cksum(bytes, sizeof bytes); printf("checksum: calculated=%4.4x, correct=%4.4x\en", x, 0x3299); exit(0); } #endif /*****************************************************************************/ #ifdef NEEDS_ISPRINT /*********************************************************************** * OSTA UDF compliant file name translation routine for OS/2, * Windows 95, Windows NT, Macintosh and UNIX. * Copyright 1995 Micro Design International, Inc. * Written by Jason M. Rinn. * Micro Design International gives permission for the free use of the * following source code. */ /*********************************************************************** * To use these routines with different operating systems. * * OS/2 * Define OS2 * Define MAXLEN = 254 * * Windows 95 * Define WIN_95 * Define MAXLEN = 255 * * Windows NT * Define WIN_NT * Define MAXLEN = 255 * * Macintosh: * Define MAC. * Define MAXLEN = 31. * * UNIX * Define UNIX. * Define MAXLEN as specified by unix version. */ #define ILLEGAL_CHAR_MARK 0x005F #define CRC_MARK 0x0023 #define EXT_SIZE 5 #define TRUE 1 #define FALSE 0 #define PERIOD 0x002E #define SPACE 0x0020 /*** PROTOTYPES ***/ int IsIllegal(unicode_t ch); /* Define a function or macro which determines if a Unicode character is * printable under your implementation. */ #include int UnicodeIsPrint(unicode_t ch) { return (ch >=' ') && (ch < 127); } int UnicodeLength(unicode_t *string, int maxlength) { int length; for (length = 0; length < maxlength; length++, string++) { if (!*string) break; } return length; } /*********************************************************************** * Translates a long file name to one using a MAXLEN and an illegal * char set in accord with the OSTA requirements. Assumes the name has * already been translated to Unicode. * * RETURN VALUE * * Number of unicode characters in translated name. */ int UDFTransName( unicode_t *newName, /* (Output)Translated name. Must be of length * MAXLEN */ unicode_t *udfName, /* (Input) Name from UDF volume.*/ int udfLen) /* (Input) Length of UDF Name. */ { int index, newIndex = 0, needsCRC = FALSE; int extIndex = 0, newExtIndex = 0, hasExt = FALSE; #if defined OS2 || defined WIN_95 || defined WIN_NT int trailIndex = 0; #endif unsigned short valueCRC; unicode_t current; const char hexChar[] = "0123456789ABCDEF"; for (index = 0; index < udfLen; index++) { current = udfName[index]; if (IsIllegal(current) || !UnicodeIsPrint(current)) { needsCRC = TRUE; /* Replace Illegal and non-displayable chars with * underscore. */ current = ILLEGAL_CHAR_MARK; /* Skip any other illegal or non-displayable * characters. */ while(index+1 < udfLen && (IsIllegal(udfName[index+1]) || !UnicodeIsPrint(udfName[index+1]))) { index++; } } /* Record position of extension, if one is found. */ if (current == PERIOD && (udfLen - index -1) <= EXT_SIZE) { if (udfLen == index + 1) { /* A trailing period is NOT an extension. */ hasExt = FALSE; } else { hasExt = TRUE; extIndex = index; newExtIndex = newIndex; } } #if defined OS2 || defined WIN_95 || defined WIN_NT /* Record position of last char which is NOT period or space. */ else if (current != PERIOD && current != SPACE) { trailIndex = newIndex; } #endif if (newIndex < MAXLEN) { newName[newIndex++] = current; } else { needsCRC = TRUE; } } #if defined OS2 || defined WIN_95 || defined WIN_NT /* For OS2, 95 & NT, truncate any trailing periods and\or spaces. */ if (trailIndex != newIndex - 1) { newIndex = trailIndex + 1; needsCRC = TRUE; hasExt = FALSE; /* Trailing period does not make an * extension. */ } #endif if (needsCRC) { unicode_t ext[EXT_SIZE]; int localExtIndex = 0; if (hasExt) { int maxFilenameLen; /* Translate extension, and store it in ext. */ for(index = 0; index maxFilenameLen) { newIndex = maxFilenameLen; } else { newIndex = newExtIndex; } } else if (newIndex > MAXLEN - 5) { /*If no extension, make sure to leave room for CRC. */ newIndex = MAXLEN - 5; } newName[newIndex++] = CRC_MARK; /* Add mark for CRC. */ /*Calculate CRC from original filename from FileIdentifier. */ valueCRC = udf_unicode_cksum(udfName, udfLen); /* Convert 16-bits of CRC to hex characters. */ newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12]; newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8]; newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4]; newName[newIndex++] = hexChar[(valueCRC & 0x000f)]; /* Place a translated extension at end, if found. */ if (hasExt) { newName[newIndex++] = PERIOD; for (index = 0;index < localExtIndex ;index++ ) { newName[newIndex++] = ext[index]; } } } return(newIndex); } #if defined OS2 || defined WIN_95 || defined WIN_NT /*********************************************************************** * Decides if a Unicode character matches one of a list * of ASCII characters. * Used by OS2 version of IsIllegal for readability, since all of the * illegal characters above 0x0020 are in the ASCII subset of Unicode. * Works very similarly to the standard C function strchr(). * * RETURN VALUE * * Non-zero if the Unicode character is in the given ASCII string. */ int UnicodeInString( unsigned char *string, /* (Input) String to search through. */ unicode_t ch) /* (Input) Unicode char to search for. */ { int found = FALSE; while (*string != '\0' && found == FALSE) { /* These types should compare, since both are unsigned * numbers. */ if (*string == ch) { found = TRUE; } string++; } return(found); } #endif /* OS2 */ /*********************************************************************** * Decides whether the given character is illegal for a given OS. * * RETURN VALUE * * Non-zero if char is illegal. */ int IsIllegal(unicode_t ch) { #ifdef MAC /* Only illegal character on the MAC is the colon. */ if (ch == 0x003A) { return(1); } else { return(0); } #elif defined UNIX /* Illegal UNIX characters are NULL and slash. */ if (ch == 0x0000 || ch == 0x002F) { return(1); } else { return(0); } #elif defined OS2 || defined WIN_95 || defined WIN_NT /* Illegal char's for OS/2 according to WARP toolkit. */ if (ch < 0x0020 || UnicodeInString("\\/:*?\"<>|", ch)) { return(1); } else { return(0); } #endif } #endif linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/osta.h000066400000000000000000000016041523477242100252610ustar00rootroot00000000000000/* $NetBSD$ */ /* * Prototypes for the OSTA functions * */ #ifndef _OSTA_H_ #define _OSTA_H_ #include #include #include #ifndef UNIX #define UNIX #endif #ifndef MAXLEN #define MAXLEN 255 #endif /*********************************************************************** * The following two typedef's are to remove compiler dependancies. * byte needs to be unsigned 8-bit, and unicode_t needs to be * unsigned 16-bit. */ typedef uint16_t unicode_t; typedef uint8_t byte; int udf_UncompressUnicode(int, byte *, unicode_t *); int udf_CompressUnicode(int, int, unicode_t *, byte *); uint16_t udf_cksum(uint8_t *s, int n); uint16_t udf_unicode_cksum(uint16_t *s, int n); uint16_t udf_ea_cksum(uint8_t *); int UDFTransName(unicode_t *, unicode_t *, int); int UnicodeLength(unicode_t *string, int maxlength); #endif /* _OSTA_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/queue.h000066400000000000000000000516261523477242100254500ustar00rootroot00000000000000/* $NetBSD: queue.h,v 1.35 2004/02/26 01:20:47 wiz Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * A singly-linked list is headed by a single forward pointer. The * elements are singly linked for minimum space and pointer manipulation * overhead at the expense of O(n) removal for arbitrary elements. New * elements can be added to the list after an existing element or at the * head of the list. Elements being removed from the head of the list * should use the explicit macro for this purpose for optimum * efficiency. A singly-linked list may only be traversed in the forward * direction. Singly-linked lists are ideal for applications with large * datasets and few or no removals or for implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so only elements can only be removed from the * head of the list. New elements can be added to the list after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List functions. */ #if defined(_KERNEL) && defined(QUEUEDEBUG) #define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ if ((head)->lh_first && \ (head)->lh_first->field.le_prev != &(head)->lh_first) \ panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); #define QUEUEDEBUG_LIST_OP(elm, field) \ if ((elm)->field.le_next && \ (elm)->field.le_next->field.le_prev != \ &(elm)->field.le_next) \ panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ if (*(elm)->field.le_prev != (elm)) \ panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__); #define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ (elm)->field.le_next = (void *)1L; \ (elm)->field.le_prev = (void *)1L; #else #define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) #define QUEUEDEBUG_LIST_OP(elm, field) #define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) #endif #define LIST_INIT(head) do { \ (head)->lh_first = NULL; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ QUEUEDEBUG_LIST_OP((listelm), field) \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ QUEUEDEBUG_LIST_OP((listelm), field) \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_HEAD(head, elm, field) do { \ QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (/*CONSTCOND*/0) #define LIST_REMOVE(elm, field) do { \ QUEUEDEBUG_LIST_OP((elm), field) \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ } while (/*CONSTCOND*/0) #define LIST_FOREACH(var, head, field) \ for ((var) = ((head)->lh_first); \ (var); \ (var) = ((var)->field.le_next)) /* * List access methods. */ #define LIST_EMPTY(head) ((head)->lh_first == NULL) #define LIST_FIRST(head) ((head)->lh_first) #define LIST_NEXT(elm, field) ((elm)->field.le_next) /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List functions. */ #define SLIST_EMPTY(head) ((head)->slh_first == NULL) #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) #define SLIST_INIT(head) do { \ (head)->slh_first = NULL; \ } while (/*CONSTCOND*/0) #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (/*CONSTCOND*/0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (/*CONSTCOND*/0) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (/*CONSTCOND*/0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } \ else { \ struct type *curelm = (head)->slh_first; \ while(curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ } \ } while (/*CONSTCOND*/0) /* * Singly-linked Tail queue declarations. */ #define STAILQ_HEAD(name, type) \ struct name { \ struct type *stqh_first; /* first element */ \ struct type **stqh_last; /* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).stqh_first } #define STAILQ_ENTRY(type) \ struct { \ struct type *stqe_next; /* next element */ \ } /* * Singly-linked Tail queue functions. */ #define STAILQ_INIT(head) do { \ (head)->stqh_first = NULL; \ (head)->stqh_last = &(head)->stqh_first; \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ (head)->stqh_last = &(elm)->field.stqe_next; \ (head)->stqh_first = (elm); \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.stqe_next = NULL; \ *(head)->stqh_last = (elm); \ (head)->stqh_last = &(elm)->field.stqe_next; \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ (head)->stqh_last = &(elm)->field.stqe_next; \ (listelm)->field.stqe_next = (elm); \ } while (/*CONSTCOND*/0) #define STAILQ_REMOVE_HEAD(head, field) do { \ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ (head)->stqh_last = &(head)->stqh_first; \ } while (/*CONSTCOND*/0) #define STAILQ_REMOVE(head, elm, type, field) do { \ if ((head)->stqh_first == (elm)) { \ STAILQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->stqh_first; \ while (curelm->field.stqe_next != (elm)) \ curelm = curelm->field.stqe_next; \ if ((curelm->field.stqe_next = \ curelm->field.stqe_next->field.stqe_next) == NULL) \ (head)->stqh_last = &(curelm)->field.stqe_next; \ } \ } while (/*CONSTCOND*/0) #define STAILQ_FOREACH(var, head, field) \ for ((var) = ((head)->stqh_first); \ (var); \ (var) = ((var)->field.stqe_next)) #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) #define STAILQ_FIRST(head) ((head)->stqh_first) #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (/*CONSTCOND*/0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_REMOVE(head, elm, type, field) do { \ if ((head)->sqh_first == (elm)) { \ SIMPLEQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->sqh_first; \ while (curelm->field.sqe_next != (elm)) \ curelm = curelm->field.sqe_next; \ if ((curelm->field.sqe_next = \ curelm->field.sqe_next->field.sqe_next) == NULL) \ (head)->sqh_last = &(curelm)->field.sqe_next; \ } \ } while (/*CONSTCOND*/0) #define SIMPLEQ_FOREACH(var, head, field) \ for ((var) = ((head)->sqh_first); \ (var); \ (var) = ((var)->field.sqe_next)) /* * Simple queue access methods. */ #define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * Tail queue functions. */ #if defined(_KERNEL) && defined(QUEUEDEBUG) #define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ if ((head)->tqh_first && \ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); #define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ if (*(head)->tqh_last != NULL) \ panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__); #define QUEUEDEBUG_TAILQ_OP(elm, field) \ if ((elm)->field.tqe_next && \ (elm)->field.tqe_next->field.tqe_prev != \ &(elm)->field.tqe_next) \ panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ if (*(elm)->field.tqe_prev != (elm)) \ panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__); #define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ if ((elm)->field.tqe_next == NULL && \ (head)->tqh_last != &(elm)->field.tqe_next) \ panic("TAILQ_PREREMOVE head %p elm %p %s:%d", \ (head), (elm), __FILE__, __LINE__); #define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ (elm)->field.tqe_next = (void *)1L; \ (elm)->field.tqe_prev = (void *)1L; #else #define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) #define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) #define QUEUEDEBUG_TAILQ_OP(elm, field) #define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) #define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) #endif #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ QUEUEDEBUG_TAILQ_OP((listelm), field) \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ QUEUEDEBUG_TAILQ_OP((listelm), field) \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_REMOVE(head, elm, field) do { \ QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ QUEUEDEBUG_TAILQ_OP((elm), field) \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ } while (/*CONSTCOND*/0) /* * Tail queue access methods. */ #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_FOREACH(var, head, field) \ for ((var) = ((head)->tqh_first); \ (var); \ (var) = ((var)->field.tqe_next)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ (var); \ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { (void *)&head, (void *)&head } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = (void *)(head); \ (head)->cqh_last = (void *)(head); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = (void *)(head); \ if ((head)->cqh_last == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = (void *)(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ } while (/*CONSTCOND*/0) #define CIRCLEQ_FOREACH(var, head, field) \ for ((var) = ((head)->cqh_first); \ (var) != (void *)(head); \ (var) = ((var)->field.cqe_next)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for ((var) = ((head)->cqh_last); \ (var) != (void *)(head); \ (var) = ((var)->field.cqe_prev)) /* * Circular queue access methods. */ #define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #endif /* !_SYS_QUEUE_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf.c000066400000000000000000007313151523477242100250750ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf.c" is part of the UDFclient toolkit. * File $Id: udf.c,v 1.307 2022/04/22 15:25:29 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011, 2020 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uscsilib.h" /* for locals */ #include "udf.h" #include "udf_bswap.h" #include "udf_discop.h" #include "udf_unix.h" #include "uio.h" #include "dirhash.h" #include /* for scsilib */ const char *dvname="UDF device"; #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) # define MIN(a,b) ((a)<(b)?(a):(b)) #endif /* #define DEBUG(a) { a; } */ #define DEBUG(a) if (0) { a; } /* global settings */ int udf_verbose = UDF_VERBLEV_ACTIONS; /* static structures shared over all programs */ struct discslist udf_discs_list; struct volumeset_list udf_volumeset_list; struct mountables_list udf_mountables; #define UDF_INODE_NUM_GUESS 2048 /* predefines */ int udf_validate_tag_and_crc_sums(union dscrptr *dscr); void udf_node_mark_dirty(struct udf_node *udf_node); static void udf_set_imp_id(struct regid *regid); static void udf_set_app_id(struct regid *regid); static void udf_node_unmark_dirty(struct udf_node *udf_node); static void udf_init_desc_tag(struct desc_tag *tag, uint16_t id, uint16_t dscr_ver, uint16_t serial_num); static int udf_translate_icb_filetype_to_dirent_filetype(int udf_filetype); static int udf_remove_directory_prim(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname); static int udf_remove_directory_entry(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname); /* external dumpers */ extern void udf_dump_descriptor(union dscrptr *dscrpt); extern void udf_dump_file_entry(struct file_entry *fe); extern void udf_dump_extfile_entry(struct extfile_entry *efe); extern void udf_dump_alloc_extent(struct alloc_ext_entry *ext, int addr_type); extern void udf_dump_vat_table(struct udf_part_mapping *udf_part_mapping); extern void udf_dump_disc_anchors(struct udf_discinfo *disc); extern void udf_dump_alive_sets(void); extern void udf_dump_root_dir(struct udf_mountpoint *mountpoint); extern void udf_dump_timestamp(char *dscr, struct timestamp *t); static size_t utf8_char_length(char c) { if ((c & 0x80) == 0x00) { return 1; } else if ((c & 0xE0) == 0xC0) { return 2; } else if ((c & 0xF0) == 0xE0) { return 3; } else if ((c & 0xF8) == 0xF0) { return 4; } else { return 0; } } static size_t string_to_unicode(const char *str, size_t length, uint16_t *unicode) { size_t i = 0, j = 0; while (i < length) { size_t char_len = utf8_char_length(str[i]); if (char_len == 0) { // 如果遇到无效的UTF-8字符,直接返回转换失败 return 0; } if (char_len == 1) { unicode[j++] = (uint16_t)str[i]; } else { uint32_t code_point = 0; for (size_t k = 0; k < char_len; k++) { code_point |= (str[i + k] & 0x3F) << ((char_len - k - 1) * 6); } unicode[j++] = (uint16_t)code_point; } i += char_len; } unicode[j] = 0; return j; } static void unicode_to_string(const uint16_t *unicode, size_t length, char *output) { size_t i, j = 0; for (i = 0; i < length; i++) { if (unicode[i] <= 0x7F) { output[j++] = (char)unicode[i]; } else if (unicode[i] <= 0x7FF) { output[j++] = (char)(0xC0 | (unicode[i] >> 6)); output[j++] = (char)(0x80 | (unicode[i] & 0x3F)); } else { output[j++] = (char)(0xE0 | (unicode[i] >> 12)); output[j++] = (char)(0x80 | ((unicode[i] >> 6) & 0x3F)); output[j++] = (char)(0x80 | (unicode[i] & 0x3F)); } } output[j] = '\0'; } /****************************************************************************************** * * Filename space conversion * ******************************************************************************************/ void udf_to_unix_name(char *result, char *id, int len, struct charspec *chsp) { uint16_t raw_name[1024], unix_name[1024]; uint8_t *outchp; int ucode_chars; assert(sizeof(char) == sizeof(uint8_t)); outchp = (uint8_t *) result; if ((chsp->type == 0) && (strcmp((char*) chsp->inf, "OSTA Compressed Unicode") == 0)) { *raw_name = *unix_name = 0; ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name); ucode_chars = UnicodeLength((unicode_t *) raw_name, ucode_chars); unicode_to_string(raw_name, (size_t)ucode_chars, result); } else { /* assume 8bit char length byte latin-1 */ assert(*id == 8); strncpy(result, id+1, len); } } void unix_to_udf_name(char *result, char *name, uint8_t *result_len, struct charspec *chsp) { uint16_t raw_name[1024]; int udf_chars, name_len; /* convert whatever to unicode-16 */ name_len = (int)string_to_unicode(name, strlen(name), raw_name); if ((chsp->type == 0) && (strcmp((char *) chsp->inf, "OSTA Compressed Unicode") == 0)) { udf_chars = udf_CompressUnicode(name_len, 16, (unicode_t *) raw_name, (byte *) result); } else { /* assume 8bit char length byte latin-1 */ *result++ = 8; udf_chars = 1; strcpy(result, name + 1); udf_chars += strlen(name); } *result_len = udf_chars; } static char *udf_get_compound_name(struct udf_mountpoint *mountpoint) { static char compound[128+128+32+32+1]; struct charspec *charspec; struct udf_log_vol *udf_log_vol; struct udf_pri_vol *udf_pri_vol; char *unix_name; udf_log_vol = mountpoint->udf_log_vol; udf_pri_vol = udf_log_vol->primary; charspec = &udf_pri_vol->pri_vol->desc_charset; assert(charspec->type == 0); assert(strcmp((const char *) charspec->inf, "OSTA Compressed Unicode")==0); unix_name = compound; udf_to_unix_name(unix_name, udf_pri_vol->pri_vol->volset_id, 128, charspec); strcat(unix_name, ":"); unix_name += strlen(unix_name); udf_to_unix_name(unix_name, udf_pri_vol->pri_vol->vol_id, 32, charspec); strcat(unix_name, ":"); unix_name += strlen(unix_name); udf_to_unix_name(unix_name, udf_log_vol->log_vol->logvol_id, 128, charspec); strcat(unix_name, ":"); unix_name += strlen(unix_name); udf_to_unix_name(unix_name, mountpoint->fileset_desc->fileset_id, 32, charspec); return compound; } /****************************************************************************************** * * Dump helpers for printing out information during parse * ******************************************************************************************/ void udf_dump_long_ad(char *prefix, struct long_ad *adr) { printf("%s at sector %d within partion space %d for %d bytes\n", prefix, udf_rw32(adr->loc.lb_num), udf_rw16(adr->loc.part_num), udf_rw32(adr->len) ); } void udf_dump_id(char *prefix, int len, char *id, struct charspec *chsp) { uint16_t raw_name[1024]; uint16_t *pos, ch; int ucode_chars; if (prefix) printf("%s ", prefix); if ((chsp->type == 0) && (strcmp((char *) chsp->inf, "OSTA Compressed Unicode") == 0)) { /* print the identifier using the OSTA compressed unicode */ printf("`"); ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name); for (pos = raw_name; ucode_chars > 0; pos++, ucode_chars--) { ch = *pos; /* OSTA code decompresses to machine endian */ if (!ch) break; if ((ch < 32) || (ch > 255)) { printf("[%d]", ch); } else { printf("%c", ch & 255); } } printf("`"); } else { printf("(roughly) `%s`", id+1); } if (prefix) printf("\n"); } void udf_dump_volume_name(char *prefix, struct udf_log_vol *udf_log_vol) { if (prefix) printf("%s%s", prefix, udf_log_vol->primary->udf_session->session_offset?" (local) ":" "); udf_dump_id(NULL, 128, udf_log_vol->primary->pri_vol->volset_id, &udf_log_vol->primary->pri_vol->desc_charset); printf(":"); udf_dump_id(NULL, 32, udf_log_vol->primary->pri_vol->vol_id, &udf_log_vol->primary->pri_vol->desc_charset); printf(":"); udf_dump_id(NULL, 128, udf_log_vol->log_vol->logvol_id, &udf_log_vol->log_vol->desc_charset); if (prefix) printf("\n"); } /****************************************************************************************** * * UDF tag checkers and size calculator * ******************************************************************************************/ /* not used extensively enough yet */ int udf_check_tag(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint8_t *pos, sum, cnt; /* check TAG header checksum */ pos = (uint8_t *) tag; sum = 0; for(cnt = 0; cnt < 16; cnt++) { if (cnt != 4) sum += *pos; pos++; } if (sum != tag->cksum) { /* bad tag header checksum; this is not a valid tag */ DEBUG(printf("Bad checksum\n")); return EINVAL; } return 0; } int udf_check_tag_payload(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint16_t crc; /* check payload CRC if applicable */ if (udf_rw16(tag->desc_crc_len) == 0) return 0; crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, udf_rw16(tag->desc_crc_len)); if (crc != udf_rw16(tag->desc_crc)) { DEBUG(printf("ERROR: CRC bad read 0x%0x calc 0x%0x\n", udf_rw16(tag->desc_crc), crc)); /* bad payload CRC; this is a broken tag */ return EINVAL; } return 0; } int udf_validate_tag_sum(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint8_t *pos, sum, cnt; /* calculate TAG header checksum */ pos = (uint8_t *) tag; sum = 0; for(cnt = 0; cnt < 16; cnt++) { if (cnt != 4) sum += *pos; pos++; } tag->cksum = sum; /* 8 bit */ return 0; } /* assumes sector number of descriptor to be allready present */ int udf_validate_tag_and_crc_sums(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint16_t crc; /* check payload CRC if applicable */ if (udf_rw16(tag->desc_crc_len) > 0) { crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, udf_rw16(tag->desc_crc_len)); tag->desc_crc = udf_rw16(crc); } /* calculate TAG header checksum */ return udf_validate_tag_sum(dscr); } int udf_check_tag_presence(union dscrptr *dscr, int TAG) { struct desc_tag *tag = &dscr->tag; int error; error = udf_check_tag(dscr); if (error) return error; if (udf_rw16(tag->id) != TAG) { DEBUG(fprintf(stderr, "looking for tag %d but found %d\n", TAG, udf_rw16(tag->id))); return ENOENT; } return 0; } /* * for malloc() purposes ... rather have an upperlimit than an exact size */ uint64_t udf_calc_tag_malloc_size(union dscrptr *dscr, uint32_t udf_sector_size) { uint32_t size, tag_id; tag_id = udf_rw16(dscr->tag.id); switch (tag_id) { case TAGID_LOGVOL : size = sizeof(struct logvol_desc) - 1; /* maps[1] */ size += udf_rw32(dscr->lvd.mt_l); break; case TAGID_UNALLOC_SPACE : size = sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad); /* alloc_desc[1] */ size += udf_rw32(dscr->usd.alloc_desc_num) * sizeof(struct extent_ad); break; case TAGID_FID : size = UDF_FID_SIZE + dscr->fid.l_fi + udf_rw16(dscr->fid.l_iu); size = (size + 3) & ~3; return size; /* RETURN !! */ case TAGID_LOGVOL_INTEGRITY : size = sizeof(struct logvol_int_desc) - sizeof(uint32_t); /* tables[1] */ size += udf_rw32(dscr->lvid.l_iu); size += (2 * udf_rw32(dscr->lvid.num_part) * sizeof(uint32_t)); break; case TAGID_SPACE_BITMAP : size = sizeof(struct space_bitmap_desc) - 1; /* data[1] */ size += udf_rw32(dscr->sbd.num_bytes); break; case TAGID_SPARING_TABLE : size = sizeof(struct udf_sparing_table) - sizeof(struct spare_map_entry); /* entries[1] */ size += udf_rw16(dscr->spt.rt_l) * sizeof(struct spare_map_entry); break; case TAGID_FENTRY : size = sizeof(struct file_entry); size += udf_rw32(dscr->fe.l_ea) + udf_rw32(dscr->fe.l_ad)-1; /* data[0] */ break; case TAGID_EXTFENTRY : size = sizeof(struct extfile_entry); size += udf_rw32(dscr->efe.l_ea) + udf_rw32(dscr->efe.l_ad)-1; /* data[0] */ break; case TAGID_FSD : size = sizeof(struct fileset_desc); break; default : size = sizeof(union dscrptr); break; } if ((size == 0) || (udf_sector_size == 0)) return 0; return ((size + udf_sector_size -1) / udf_sector_size) * udf_sector_size; } /* explicit only for FID's */ static int udf_fidsize(struct fileid_desc *fid) { int size; size = UDF_FID_SIZE + fid->l_fi + udf_rw16(fid->l_iu); size = (size + 3) & ~3; return size; } /****************************************************************************************** * * Logical to physical adres transformation * ******************************************************************************************/ /* convert (udf_log_vol, vpart_num) to a udf_partion structure */ int udf_logvol_vpart_to_partition(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, struct udf_part_mapping **udf_part_mapping_ptr, struct udf_partition **udf_partition_ptr) { struct udf_volumeset *udf_volumeset; struct udf_partition *udf_partition; struct udf_part_mapping *udf_part_mapping; uint32_t part_num; int found; assert(udf_log_vol); assert(!SLIST_EMPTY(&udf_log_vol->part_mappings)); /* clear result */ if (udf_part_mapping_ptr) *udf_part_mapping_ptr = NULL; if (udf_partition_ptr) *udf_partition_ptr = NULL; /* map the requested partition map to the physical udf partition */ found = 0; SLIST_FOREACH(udf_part_mapping, &udf_log_vol->part_mappings, next_mapping) { if (udf_part_mapping->udf_virt_part_num == vpart_num) { found = 1; break; } } if (!found) { printf("\t\t\tVirtual partition number %d not found!\n", vpart_num); return EINVAL; } assert(udf_part_mapping); /* call me paranoid */ part_num = udf_part_mapping->udf_phys_part_num; /* search for the physical partition information */ udf_volumeset = udf_log_vol->primary->volumeset; SLIST_FOREACH(udf_partition, &udf_volumeset->parts, next_partition) { if (udf_rw16(udf_partition->partition->part_num) == part_num) break; } if (!udf_partition) { printf("\t\t\tNo information known about partition %d yet!\n", part_num); printf("\t\t\t\tPlease insert volume %d of this volumeset and try again\n", udf_part_mapping->vol_seq_num); return ENOENT; } if (udf_part_mapping_ptr) *udf_part_mapping_ptr = udf_part_mapping; if (udf_partition_ptr) *udf_partition_ptr = udf_partition; return 0; } /* no recursive translations yet (UDF OK) */ /* All translation is done in 64 bits to prevent bitrot and returns the PARTITION relative address */ int udf_vpartoff_to_sessionoff(struct udf_log_vol *udf_log_vol, struct udf_part_mapping *udf_part_mapping, struct udf_partition *udf_partition, uint64_t offset, uint64_t *ses_off, uint64_t *trans_valid_len) { struct spare_map_entry *sp_entry; struct udf_node *udf_node; struct udf_allocentry *alloc_entry; uint64_t part_start, part_length; uint64_t eff_sector, eff_offset; uint64_t trans_sector; uint64_t cur_offset; uint32_t len, lb_num, block_offset; uint32_t entry, entries; uint32_t sector_size, lb_size; uint64_t packet_num, packet_rlb; uint64_t packet_len; uint32_t vat_entries, *vat_pos; int flags; assert(udf_part_mapping); assert(udf_partition); assert(ses_off); assert(trans_valid_len); /* not ok, but rather this than a dangling random value */ *ses_off = UINT_MAX; *trans_valid_len = 0; lb_size = udf_log_vol->lb_size; sector_size = udf_log_vol->sector_size; part_start = (uint64_t) udf_rw32(udf_partition->partition->start_loc) * sector_size; part_length = (uint64_t) udf_rw32(udf_partition->partition->part_len) * sector_size; /* get the offset (in bytes) in the partition and check its validity */ if (offset >= part_length) { printf("\t\toffset %"PRIu64" is outside partition %d!\n", offset, udf_rw16(udf_partition->partition->part_num)); return EFAULT; } /* do the address translations based on the partition mapping type */ /* translation of virt/sparable etc. is assumed to be done in logical block sizes */ switch (udf_part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_PHYSICAL : /* nothing to be done; physical is logical */ *ses_off = part_start + offset; /* 1:1 */ *trans_valid_len = part_length - offset; /* rest of partition */ return 0; case UDF_PART_MAPPING_VIRTUAL : vat_entries = udf_part_mapping->vat_entries; vat_pos = (uint32_t *) udf_part_mapping->vat_translation; /* this translation is dependent on logical sector numbers */ eff_sector = offset / lb_size; eff_offset = offset % lb_size; /* TODO check range for logical sector against VAT length */ assert(eff_sector < vat_entries); trans_sector = vat_pos[eff_sector]; *ses_off = part_start + (trans_sector * lb_size) + eff_offset; /* trans sectors are in lb->lb ? */ *trans_valid_len = lb_size - eff_offset; /* maximum one logical sector */ return 0; case UDF_PART_MAPPING_SPARABLE : /* this translation is dependent on logical sector numbers */ *ses_off = part_start + offset; /* 1:1 */ eff_sector = offset / lb_size; eff_offset = offset % lb_size; /* transform on packet-length base */ packet_len = udf_rw16(udf_part_mapping->udf_pmap->pms.packet_len); /* in lb */ entries = udf_rw16(udf_part_mapping->sparing_table->rt_l); packet_num = (eff_sector / packet_len) * packet_len; packet_rlb = eff_sector % packet_len; /* within packet */ /* translate this packet; source is in partition, destination is absolute disc address */ sp_entry = &udf_part_mapping->sparing_table->entries[0]; for (entry = 0; entry < entries; entry++) { if (udf_rw32(sp_entry->org) - packet_num == 0) { /* mappings contain absolute disc addresses, so no partition offsets please */ *ses_off = (uint64_t) (udf_rw32(sp_entry->map) + packet_rlb) * lb_size + eff_offset; break; } sp_entry++; } *trans_valid_len = (packet_len - packet_rlb) * lb_size; /* maximum one packet */ return 0; case UDF_PART_MAPPING_META : /* We follow the allocation entries to calculate our offset */ udf_node = udf_part_mapping->meta_file; assert(udf_node->addr_type != UDF_ICB_INTERN_ALLOC); /* find sector in the allocation space */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); cur_offset = 0; TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) { len = alloc_entry->len; lb_num = alloc_entry->lb_num; /* vpart_num = alloc_entry->vpart_num; */ flags = alloc_entry->flags; /* check overlap with this alloc entry */ if (cur_offset + len > offset) { assert(((offset - cur_offset) % lb_size) == 0); /* ought to be on sector boundary */ if (flags != UDF_EXT_ALLOCATED) break; block_offset = offset - cur_offset; *ses_off = part_start + lb_num * lb_size + block_offset; /* 1:1 within the block */ *trans_valid_len = len - block_offset; /* rest of this chunk */ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); return 0; } cur_offset += len; } /* FOREACH */ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); printf("\t\toffset %"PRIu64" is not translated within current metadata partition %d file descriptor!\n", offset, udf_rw16(udf_partition->partition->part_num)); return EFAULT; case UDF_PART_MAPPING_ERROR : default : break; } printf("Unsupported or bad mapping %d; can't translate\n", udf_part_mapping->udf_part_mapping_type); return EFAULT; } /****************************************************************************************** * * udf_node creator, destructor and syncer * ******************************************************************************************/ static mode_t udf_perm_to_unix_mode(uint32_t perm) { mode_t mode; mode = ((perm & UDF_FENTRY_PERM_USER_MASK) ); mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK ) >> 2); mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4); return mode; } static uint32_t unix_mode_to_udf_perm(mode_t mode) { uint32_t perm; perm = ((mode & S_IRWXO) ); perm |= ((mode & S_IRWXG) << 2); perm |= ((mode & S_IRWXU) << 4); perm |= ((mode & S_IWOTH) << 3); perm |= ((mode & S_IWGRP) << 5); perm |= ((mode & S_IWUSR) << 7); return perm; } /* * Fill in timestamp structure based on clock_gettime(). Time is reported back as a time_t * accompanied with a nano second field. * * The husec, usec and csec could be relaxed in type. */ static void udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp) { struct tm tm; uint64_t husec, usec, csec; bzero(timestamp, sizeof(struct timestamp)); gmtime_r(×pec->tv_sec, &tm); /* * Time type and time zone : see ECMA 1/7.3, UDF 2., 2.1.4.1, 3.1.1. * * Lower 12 bits are two complement signed timezone offset if bit 12 * (method 1) is clear. Otherwise if bit 12 is set, specify timezone * offset to -2047 i.e. unsigned `zero' */ timestamp->type_tz = udf_rw16((1<<12) + 0); /* has to be method 1 for CUT/GMT */ timestamp->year = udf_rw16(tm.tm_year + 1900); timestamp->month = tm.tm_mon + 1; /* `tm' structure uses 0..11 for months */ timestamp->day = tm.tm_mday; timestamp->hour = tm.tm_hour; timestamp->minute = tm.tm_min; timestamp->second = tm.tm_sec; usec = (timespec->tv_nsec + 500) / 1000; /* round (if possible) */ husec = usec / 100; usec -= husec * 100; /* we only want 0-99 in usec */ csec = husec / 100; /* we get 0-99 in csec */ husec -= csec * 100; /* we only want 0-99 in husec */ timestamp->centisec = csec; timestamp->hund_usec = husec; timestamp->usec = usec; } void udf_set_timestamp_now(struct timestamp *timestamp) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); udf_timespec_to_timestamp(&now, timestamp); } /* implemented as a seperate function to allow tuning */ void udf_set_timespec_now(struct timespec *timespec) { clock_gettime(CLOCK_REALTIME, timespec); } int udf_insanetimespec(struct timespec *check) { struct timespec now; struct tm tm; gmtime_r(&check->tv_sec, &tm); /* since our converters can only deal with timestamps after 1970 we * reject earlier */ if (tm.tm_year < 1970) return 1; /* don't accept values from the future; FFS or NFS might not mind, but * UDF does! */ clock_gettime(CLOCK_REALTIME, &now); if (now.tv_sec < check->tv_sec) return 1; if ((now.tv_sec == check->tv_sec) && (now.tv_nsec < check->tv_nsec)) return 1; return 0; } /* * Timestamp to timespec conversion code is taken with small modifications * from FreeBSD /sys/fs/udf by Scott Long */ static int mon_lens[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; static int udf_isaleapyear(int year) { int i; i = (year % 4) ? 0 : 1; i &= (year % 100) ? 1 : 0; i |= (year % 400) ? 0 : 1; return i; } static void udf_timestamp_to_timespec(struct timestamp *timestamp, struct timespec *timespec) { uint32_t usecs, secs, nsecs; uint16_t tz; int i, lpyear, daysinyear, year; timespec->tv_sec = secs = 0; timespec->tv_nsec = nsecs = 0; /* * DirectCD seems to like using bogus year values. * Distrust time->month especially, since it will be used for an array * index. */ year = udf_rw16(timestamp->year); if ((year < 1970) || (timestamp->month > 12)) { return; } /* Calculate the time and day */ usecs = timestamp->usec + 100*timestamp->hund_usec + 10000*timestamp->centisec; nsecs = usecs * 1000; secs = timestamp->second; secs += timestamp->minute * 60; secs += timestamp->hour * 3600; secs += (timestamp->day-1) * 3600 * 24; /* day : 1-31 */ /* Calclulate the month */ lpyear = udf_isaleapyear(year); for (i = 1; i < timestamp->month; i++) secs += mon_lens[lpyear][i-1] * 3600 * 24; /* month: 1-12 */ for (i = 1970; i < year; i++) { daysinyear = udf_isaleapyear(i) + 365 ; secs += daysinyear * 3600 * 24; } /* * Calculate the time zone. The timezone is 12 bit signed 2's * compliment, so we gotta do some extra magic to handle it right. */ tz = udf_rw16(timestamp->type_tz); tz &= 0x0fff; /* only lower 12 bits are significant */ if (tz & 0x0800) /* sign extention */ tz |= 0xf000; /* TODO check timezone conversion */ #if 1 /* check if we are specified a timezone to convert */ if (udf_rw16(timestamp->type_tz) & 0x1000) if ((int16_t) tz != -2047) secs -= (int16_t) tz * 60; #endif timespec->tv_sec = secs; timespec->tv_nsec = nsecs; } static void udf_node_get_fileinfo(struct udf_node *udf_node, union dscrptr *dscrptr) { struct stat *stat; struct file_entry *file_entry; struct extfile_entry *extfile_entry; struct timestamp *atime, *mtime, *ctime, *attrtime; uint64_t inf_len, unique_id; uint32_t uid, gid, udf_perm; uint16_t fe_tag; uint16_t udf_icbtag_flags, serial_num, link_cnt; uint8_t filetype; assert(udf_node); assert(dscrptr); stat = &udf_node->stat; /* check if its an normal file entry or a extended file entry ICB */ fe_tag = udf_rw16(dscrptr->tag.id); if (fe_tag == TAGID_FENTRY) { file_entry = &dscrptr->fe; #if 0 prev_direct_entries = udf_rw32(file_entry->icbtag.prev_num_dirs); strat_param16 = udf_rw16(* (uint16_t *) (file_entry->icbtag.strat_param)); entries = udf_rw16(file_entry->icbtag.max_num_entries); strategy = udf_rw16(file_entry->icbtag.strat_type); data_length = udf_rw32(file_entry->l_ad); addr_type = udf_rw16(file_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; pos = &file_entry->data[0] + udf_rw32(file_entry->l_ea); #endif filetype = file_entry->icbtag.file_type; inf_len = udf_rw64(file_entry->inf_len); uid = udf_rw32(file_entry->uid); gid = udf_rw32(file_entry->gid); udf_perm = udf_rw32(file_entry->perm); serial_num = udf_rw16(file_entry->tag.serial_num); udf_icbtag_flags = udf_rw16(file_entry->icbtag.flags); link_cnt = udf_rw16(file_entry->link_cnt); unique_id = udf_rw64(file_entry->unique_id); atime = &file_entry->atime; mtime = &file_entry->mtime; ctime = &file_entry->mtime; /* XXX assumption */ attrtime = &file_entry->attrtime; } else if (fe_tag == TAGID_EXTFENTRY) { extfile_entry = &dscrptr->efe; #if 0 prev_direct_entries = udf_rw32(extfile_entry->icbtag.prev_num_dirs); strat_param16 = udf_rw16(* (uint16_t *) (extfile_entry->icbtag.strat_param)); entries = udf_rw16(extfile_entry->icbtag.max_num_entries); strategy = udf_rw16(extfile_entry->icbtag.strat_type); data_length = udf_rw32(extfile_entry->l_ad); addr_type = udf_rw16(extfile_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; pos = &extfile_entry->data[0] + udf_rw32(extfile_entry->l_ea); #endif filetype = extfile_entry->icbtag.file_type; inf_len = udf_rw64(extfile_entry->inf_len); uid = udf_rw32(extfile_entry->uid); gid = udf_rw32(extfile_entry->gid); udf_perm = udf_rw32(extfile_entry->perm); serial_num = udf_rw16(extfile_entry->tag.serial_num); udf_icbtag_flags = udf_rw16(extfile_entry->icbtag.flags); link_cnt = udf_rw16(extfile_entry->link_cnt); /* how many FID's are linked to this (ext)fentry */ unique_id = udf_rw64(extfile_entry->unique_id); /* unique file ID */ atime = &extfile_entry->atime; mtime = &extfile_entry->mtime; ctime = &extfile_entry->ctime; attrtime = &extfile_entry->attrtime; } else { printf("udf_node_set_file_info : help! i can't be here!!! i got a %d tag\n", fe_tag); udf_dump_descriptor(dscrptr); return; } /* fill in (parts of) the stat structure */ /* XXX important info missing still like access mode, times etc. XXX */ udf_node->udf_filetype = filetype; udf_node->serial_num = serial_num; udf_node->udf_icbtag_flags = udf_icbtag_flags; udf_node->link_cnt = link_cnt; /* how many FID's are linked to this (ext)fentry */ udf_node->unique_id = unique_id; /* unique file ID */ /* fill in stat basics */ bzero(stat, sizeof(struct stat)); stat->st_ino = unique_id; /* lowest 32 bit(!) only */ stat->st_mode = udf_perm_to_unix_mode(udf_perm); /* CONVERT from udf_perm */ stat->st_mode |= (udf_translate_icb_filetype_to_dirent_filetype(filetype) & DT_DIR) ? S_IFDIR : S_IFREG; stat->st_uid = uid; stat->st_gid = gid; /* ... times */ udf_timestamp_to_timespec(atime, &stat->st_atimespec); udf_timestamp_to_timespec(mtime, &stat->st_mtimespec); udf_timestamp_to_timespec(attrtime, &stat->st_ctimespec); #ifndef NO_STAT_BIRTHTIME udf_timestamp_to_timespec(ctime, &stat->st_birthtimespec); #endif /* ... sizes */ stat->st_size = inf_len; stat->st_blksize = udf_node->udf_log_vol->lb_size; /* special: updatables */ stat->st_nlink = link_cnt; stat->st_blocks = (stat->st_size + 512 -1)/512; /* blocks are hardcoded 512 bytes/sector in stat :-/ */ return; } static void udf_node_set_fileinfo(struct udf_node *udf_node, union dscrptr *dscrptr) { struct stat *stat; struct file_entry *file_entry; struct extfile_entry *extfile_entry; struct timestamp *atime, *mtime, *ctime, *attrtime; uint64_t inf_len, unique_id; uint32_t uid, gid, udf_perm; uint16_t fe_tag, serial_num, link_cnt; uint8_t filetype; assert(udf_node); assert(dscrptr); stat = &udf_node->stat; /* set (parts of) the stat structure */ /* XXX important info missing still like times etc. XXX */ uid = stat->st_uid; gid = stat->st_gid; inf_len = stat->st_size; udf_perm = unix_mode_to_udf_perm(stat->st_mode); /* conversion to UDF perm. */ filetype = udf_node->udf_filetype; unique_id = udf_node->unique_id; serial_num = udf_node->serial_num; link_cnt = udf_node->link_cnt; /* check if its to be written in an normal file entry or a extended file entry ICB */ fe_tag = udf_rw16(dscrptr->tag.id); if (fe_tag == TAGID_FENTRY) { file_entry = &dscrptr->fe; #if 0 prev_direct_entries = udf_rw32(file_entry->icbtag.prev_num_dirs); strat_param16 = udf_rw16(* (uint16_t *) (file_entry->icbtag.strat_param)); entries = udf_rw16(file_entry->icbtag.max_num_entries); strategy = udf_rw16(file_entry->icbtag.strat_type); data_length = udf_rw32(file_entry->l_ad); addr_type = udf_rw16(file_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; pos = &file_entry->data[0] + udf_rw32(file_entry->l_ea); #endif file_entry->icbtag.file_type = filetype; file_entry->inf_len = udf_rw64(inf_len); file_entry->uid = udf_rw32(uid); file_entry->gid = udf_rw32(gid); file_entry->perm = udf_rw32(udf_perm); file_entry->tag.serial_num = udf_rw16(serial_num); file_entry->link_cnt = udf_rw16(link_cnt); file_entry->unique_id = udf_rw64(unique_id); atime = &file_entry->atime; mtime = &file_entry->mtime; ctime = mtime; /* XXX assumption */ attrtime = &file_entry->attrtime; } else if (fe_tag == TAGID_EXTFENTRY) { extfile_entry = &dscrptr->efe; #if 0 prev_direct_entries = udf_rw32(extfile_entry->icbtag.prev_num_dirs); strat_param16 = udf_rw16(* (uint16_t *) (extfile_entry->icbtag.strat_param)); entries = udf_rw16(extfile_entry->icbtag.max_num_entries); strategy = udf_rw16(extfile_entry->icbtag.strat_type); data_length = udf_rw32(extfile_entry->l_ad); addr_type = udf_rw16(extfile_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; pos = &extfile_entry->data[0] + udf_rw32(extfile_entry->l_ea); #endif extfile_entry->icbtag.file_type = filetype; extfile_entry->inf_len = udf_rw64(inf_len); extfile_entry->uid = udf_rw32(uid); extfile_entry->gid = udf_rw32(gid); extfile_entry->perm = udf_rw32(udf_perm); extfile_entry->tag.serial_num = udf_rw16(serial_num); extfile_entry->link_cnt = udf_rw16(link_cnt); /* how many FID's are linked to this (ext)fentry */ extfile_entry->unique_id = udf_rw64(unique_id); /* unique file ID */ atime = &extfile_entry->atime; mtime = &extfile_entry->mtime; ctime = &extfile_entry->ctime; attrtime = &extfile_entry->attrtime; } else { printf("udf_node_set_file_info : help! i can't be here!!! i got a %d tag\n", fe_tag); udf_dump_descriptor(dscrptr); return; } /* FILL in {atime, mtime, attrtime} TIMES! */ udf_timespec_to_timestamp(&stat->st_atimespec, atime); udf_timespec_to_timestamp(&stat->st_mtimespec, mtime); udf_timespec_to_timestamp(&stat->st_ctimespec, attrtime); #ifndef NO_STAT_BIRTHTIME udf_timespec_to_timestamp(&stat->st_birthtimespec, ctime); #else memcpy(ctime, mtime, sizeof(*ctime)); #endif return; } /* with 32 bits ino_t, a maximum of about 8 TB discs are supported (1<<32) * 2KB*/ ino_t udf_calc_hash(struct long_ad *icbptr) { /* TODO unique file-id would be better */ return (ino_t) udf_rw32(icbptr->loc.lb_num); } void udf_insert_node_in_hash(struct udf_node *udf_node) { struct udf_log_vol *log_vol; uint32_t bucket; ino_t hashkey; struct long_ad icb; icb.loc.lb_num = udf_rw32(TAILQ_FIRST(&udf_node->dscr_allocs)->lb_num); log_vol = udf_node->udf_log_vol; hashkey = udf_calc_hash(&icb); udf_node->hashkey = hashkey; bucket = hashkey & UDF_INODE_HASHMASK; LIST_INSERT_HEAD(&log_vol->udf_nodes[bucket], udf_node, next_node); } /* dispose udf_node's administration */ void udf_dispose_udf_node(struct udf_node *udf_node) { struct udf_allocentry *alloc_entry; struct udf_buf *buf_entry; struct udf_node *lookup; uint32_t bucket; ino_t hashkey; if (!udf_node) return; /* XXX locks? XXX */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); if (udf_node->dirty) { DEBUG(printf("Warning: disposing dirty node\n")); udf_node_unmark_dirty(udf_node); } /* free all associated buffers */ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->buf_mutex); /* due to this `trick' we don't need to use a marker */ while ((buf_entry = TAILQ_FIRST(&udf_node->vn_bufs))) { udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */ udf_detach_buf_from_node(udf_node, buf_entry); udf_free_buf_entry(buf_entry); } /* free in-node filedata blob if present */ if (udf_node->intern_data) free(udf_node->intern_data); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); /* free our extended attribute administration if present */ if (udf_node->extattrfile_icb) free(udf_node->extattrfile_icb); if (udf_node->streamdir_icb) free(udf_node->streamdir_icb); if (udf_node->extended_attr) free(udf_node->extended_attr); /* free dscr_allocs queue */ while ((alloc_entry = TAILQ_FIRST(&udf_node->dscr_allocs))) { TAILQ_REMOVE(&udf_node->dscr_allocs, alloc_entry, next_alloc); free(alloc_entry); } /* free allocation queue */ while ((alloc_entry = TAILQ_FIRST(&udf_node->alloc_entries))) { TAILQ_REMOVE(&udf_node->alloc_entries, alloc_entry, next_alloc); free(alloc_entry); } /* if its part of a logical volume, delete it in its hash table */ if (udf_node->udf_log_vol) { hashkey = udf_node->hashkey; bucket = hashkey & UDF_INODE_HASHMASK; LIST_FOREACH(lookup, &udf_node->udf_log_vol->udf_nodes[bucket], next_node) { /* hashkey doesn't matter; just remove same udf_node pointer */ if (lookup == udf_node) { assert(lookup->hashkey == hashkey); DEBUG(printf("removal of udf_node from the hash table\n")); LIST_REMOVE(lookup, next_node); break; } } } UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); /* free the node */ free(udf_node); } uint64_t udf_increment_unique_id(struct udf_log_vol *udf_log_vol) { uint64_t unique_id, next_unique_id; /* lock? */ unique_id = udf_log_vol->next_unique_id; /* increment according to UDF 3/3.2.1.1 */ next_unique_id = unique_id + 1; if (((next_unique_id << 32) >> 32) < 16) next_unique_id |= 16; udf_log_vol->next_unique_id = next_unique_id; DEBUG(printf("next unique_id <-- %"PRIu64"\n", udf_log_vol->next_unique_id)); return unique_id; } int udf_init_udf_node(struct udf_mountpoint *mountpoint, struct udf_log_vol *udf_log_vol, char *what, struct udf_node **udf_nodeptr) { struct udf_node *udf_node; uint32_t lb_size, data_space_avail; int descr_ver; what = what; /* not used yet */ assert(udf_log_vol); lb_size = udf_log_vol->lb_size; /* get ourselves some space */ udf_node = calloc(1, sizeof(struct udf_node)); if (!udf_node) return ENOMEM; /* setup basic udf_node */ udf_node->addr_type = UDF_ICB_LONG_ALLOC; udf_node->icb_len = sizeof(struct long_ad); udf_node->serial_num = 1; udf_node->udf_icbtag_flags = UDF_ICB_INTERN_ALLOC; udf_node->link_cnt = 1; /* how many FID's are linked to this node */ udf_node->unique_id = 0; /* unique file ID unknown */ /* get internal space available for internal nodes */ /* TODO keep in mind the extended attributes! XXX */ descr_ver = udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver); if (descr_ver == 2) { /* TODO reserve some space for the icb creation time */ data_space_avail = lb_size - sizeof(struct file_entry) - 0; /* udf_rw32(file_entry->l_ea) */ } else { data_space_avail = lb_size - sizeof(struct extfile_entry) - 0; /* udf_rw32(extfile_entry->l_ea) */ } udf_node->intern_free = data_space_avail; udf_node->intern_data = NULL; udf_node->intern_len = 0; /* finalise udf_node */ udf_node->mountpoint = mountpoint; udf_node->udf_log_vol = udf_log_vol; TAILQ_INIT(&udf_node->dscr_allocs); TAILQ_INIT(&udf_node->alloc_entries); TAILQ_INIT(&udf_node->vn_bufs); UDF_MUTEX_INIT(&udf_node->alloc_mutex); UDF_MUTEX_INIT(&udf_node->buf_mutex); /* XXX udf_node_lock NOT USED XXX */ /* pthread_rwlock_init(&udf_node->udf_node_lock, NULL); */ *udf_nodeptr = udf_node; return 0; } int udf_allocate_udf_node_on_disc(struct udf_node *udf_node) { struct udf_allocentry *alloc_entry; uint32_t lb_num, lb_size; uint16_t vpart_num; int error; assert(udf_node); assert(udf_node->udf_log_vol); assert(udf_node->udf_log_vol->log_vol); lb_size = udf_node->udf_log_vol->lb_size; assert(lb_size); /* pre-allocate node; its needed in directory linkage for now */ error = udf_allocate_lbs(udf_node->udf_log_vol, UDF_C_NODE, /*num lb */ 1, "New FID", &vpart_num, &lb_num, NULL); if (error) return error; alloc_entry = calloc(1, sizeof(struct udf_allocentry)); if (!alloc_entry) { return ENOMEM; } alloc_entry->len = lb_size; alloc_entry->vpart_num = vpart_num; alloc_entry->lb_num = lb_num; alloc_entry->flags = 0; TAILQ_INSERT_TAIL(&udf_node->dscr_allocs, alloc_entry, next_alloc); assert(error == 0); return error; } /* note: the udf_node (inode) is not stored in a hashtable! hash value is still invalid */ /* TODO remember our extended attributes and remember our streamdir long_ad */ int udf_readin_anon_udf_node(struct udf_log_vol *udf_log_vol, union dscrptr *given_dscrptr, struct long_ad *udf_icbptr, char *what, struct udf_node **udf_nodeptr) { union dscrptr *dscrptr; struct udf_node *udf_node; struct udf_allocentry *alloc_entry; struct udf_allocentry *cur_alloc, *next_alloc; struct file_entry *file_entry; struct extfile_entry *extfile_entry; struct alloc_ext_entry *alloc_ext_entry; struct long_ad *l_ad; struct short_ad *s_ad; uint64_t inf_len, calculated_len; uint32_t lb_size, entries; uint64_t data_length; uint32_t data_space_avail; uint32_t fe_tag; uint64_t len; uint32_t lb_num, vpart_num; uint32_t icb_len; int16_t addr_type; uint8_t *pos; uint8_t flags; int error, advance_sector; if ((udf_icbptr->loc.lb_num == 0) && (udf_icbptr->loc.part_num == 0) && (udf_icbptr->len == 0)) return ENOENT; DEBUG(printf("udf_readin_anon_udf_node for %s\n", what)); error = udf_init_udf_node(/*mountpoint*/ NULL, udf_log_vol, what, &udf_node); DEBUG( if (error) printf("While reading in `anononymous' udf_node : got error %s\n", strerror(error)); ); if (error) return error; *udf_nodeptr = udf_node; assert(udf_log_vol); lb_size = udf_log_vol->lb_size; /* read in descriptor if not provided to us */ dscrptr = NULL; len = lb_size; /* nodes are defined in lb_size only (?) */ lb_num = udf_rw32(udf_icbptr->loc.lb_num); vpart_num = udf_rw16(udf_icbptr->loc.part_num); if (!given_dscrptr) { error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, lb_num, what, &dscrptr, NULL); if (error) { *udf_nodeptr = NULL; return error; } } else { dscrptr = given_dscrptr; } fe_tag = udf_rw16(dscrptr->tag.id); if (fe_tag != TAGID_FENTRY && fe_tag != TAGID_EXTFENTRY) { /* something wrong with this address; it doesn't start with a file entry */ printf("UDF: bad udf_node for %s; got a %d tag\n", what, fe_tag); if (dscrptr != given_dscrptr) free(dscrptr); udf_dispose_udf_node(udf_node); *udf_nodeptr = NULL; return EFAULT; } /* get as much info as possible */ udf_node_get_fileinfo(udf_node, dscrptr); /* reset pending write count */ udf_node->v_numoutput = 0; /* initialise various variables for extracting allocation information */ inf_len = 0; data_length = 0; lb_num = 0; len = 0; vpart_num = 0; pos = NULL; data_space_avail = 0; next_alloc = calloc(1, sizeof(struct udf_allocentry)); if (!next_alloc) { if (dscrptr != given_dscrptr) free(dscrptr); udf_dispose_udf_node(udf_node); *udf_nodeptr = NULL; return ENOMEM; } next_alloc->len = lb_size; next_alloc->lb_num = udf_rw32(udf_icbptr->loc.lb_num); next_alloc->vpart_num = udf_rw16(udf_icbptr->loc.part_num); entries = 1; calculated_len = 0; error = 0; addr_type = -1; do { cur_alloc = next_alloc; next_alloc = calloc(1, sizeof(struct udf_allocentry)); if (!next_alloc) { if (dscrptr != given_dscrptr) free(dscrptr); udf_dispose_udf_node(udf_node); *udf_nodeptr = NULL; return ENOMEM; } memcpy(next_alloc, cur_alloc, sizeof(struct udf_allocentry)); TAILQ_INSERT_TAIL(&udf_node->dscr_allocs, cur_alloc, next_alloc); /* process this allocation descriptor */ /* note that we don't store the file descriptors -> XXX impl. use stuff gets lost here */ fe_tag = udf_rw16(dscrptr->tag.id); switch (fe_tag) { case TAGID_FENTRY : /* allocation descriptors follow this tag */ file_entry = &dscrptr->fe; entries = udf_rw16(file_entry->icbtag.max_num_entries); data_length = udf_rw32(file_entry->l_ad); pos = &file_entry->data[0] + udf_rw32(file_entry->l_ea); inf_len = udf_rw64(file_entry->inf_len); addr_type = udf_rw16(file_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; data_space_avail = lb_size - sizeof(struct file_entry) - udf_rw32(file_entry->l_ea); /* process extended attributes */ /* keep a trace of the descriptors in this udf_node */ UDF_VERBOSE_MAX(udf_dump_file_entry(file_entry)); break; case TAGID_EXTFENTRY : /* allocation descriptors follow this tag */ extfile_entry = &dscrptr->efe; entries = udf_rw16(extfile_entry->icbtag.max_num_entries); data_length = udf_rw32(extfile_entry->l_ad); pos = &extfile_entry->data[0] + udf_rw32(extfile_entry->l_ea); inf_len = udf_rw64(extfile_entry->inf_len); addr_type = udf_rw16(extfile_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; data_space_avail = lb_size - sizeof(struct extfile_entry) - udf_rw32(extfile_entry->l_ea); /* process extended attributes */ /* keep a trace of the descriptors in this udf_node */ UDF_VERBOSE_MAX(udf_dump_extfile_entry(extfile_entry)); break; case TAGID_ALLOCEXTENT : /* allocation descriptors follow this tag; treat as if continuation of a (ext)file entry*/ alloc_ext_entry = &dscrptr->aee; data_length = udf_rw32(alloc_ext_entry->l_ad); pos = &alloc_ext_entry->data[0]; assert(addr_type >= 0); /* keep a trace of the descriptors in this udf_node */ UDF_VERBOSE_MAX(udf_dump_alloc_extent(alloc_ext_entry, addr_type)); break; case TAGID_INDIRECTENTRY : printf("create_anon_udf_node called with indirect entry; following chain\n"); l_ad = &dscrptr->inde.indirect_icb; next_alloc->len = udf_rw32(l_ad->len); next_alloc->lb_num = udf_rw32(l_ad->loc.lb_num); next_alloc->vpart_num = udf_rw16(l_ad->loc.part_num); entries = 1; /* at least one more entry */ advance_sector = 0; /* don't advance to next sector */ UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); break; default: printf("read_file_part_extents: i can't be here! (tag = %d) in ICB hiargy of %s\n", fe_tag, what); udf_dump_descriptor(dscrptr); return EFAULT; } if (fe_tag != TAGID_INDIRECTENTRY) { udf_node->addr_type = addr_type; if (addr_type == UDF_ICB_INTERN_ALLOC) { udf_node->intern_len = inf_len; udf_node->intern_free = data_space_avail; udf_node->intern_data = calloc(1, udf_node->intern_free); if (udf_node->intern_data) { memcpy(udf_node->intern_data, pos, inf_len); } else { error = ENOMEM; } if (dscrptr != given_dscrptr) free(dscrptr); return error; } icb_len = 0; switch (udf_node->addr_type) { case UDF_ICB_SHORT_ALLOC : icb_len = sizeof(struct short_ad); break; case UDF_ICB_LONG_ALLOC : icb_len = sizeof(struct long_ad); break; default : printf("UDF encountered an unknown allocation type %d\n", udf_node->addr_type); break; } udf_node->icb_len = icb_len; advance_sector = 1; while (icb_len && data_length) { switch (udf_node->addr_type) { case UDF_ICB_SHORT_ALLOC : s_ad = (struct short_ad *) pos; len = udf_rw32(s_ad->len); lb_num = udf_rw32(s_ad->lb_num); vpart_num = cur_alloc->vpart_num; break; case UDF_ICB_LONG_ALLOC : l_ad = (struct long_ad *) pos; len = udf_rw32(l_ad->len); lb_num = udf_rw32(l_ad->loc.lb_num); vpart_num = udf_rw16(l_ad->loc.part_num); if (l_ad->impl.im_used.flags & UDF_ADIMP_FLAGS_EXTENT_ERASED) { printf("UDF: got a `extent erased' flag in a file's long_ad; ignoring\n"); } break; default : printf("UDF encountered an unknown allocation type %d\n", udf_node->addr_type); break; } /* ecma-167 48.14.1.1 */ flags = (uint8_t) ((uint32_t) (len >> 30) & 3); len = len & ((1<<30)-1); if (flags == UDF_SPACE_REDIRECT) { /* fill in next extent */ next_alloc->len = len; next_alloc->lb_num = lb_num; next_alloc->vpart_num = vpart_num; advance_sector = 0; /* don't advance to next sector */ icb_len = data_length; /* must be last */ DEBUG(printf("Continuing extent flagged at vpart = %d, lb_num = %d, len = %d\n", (int) vpart_num, (int) lb_num, (int) len)); } else { if (len) { alloc_entry = calloc(1, sizeof(struct udf_allocentry)); if (!alloc_entry) { if (dscrptr != given_dscrptr) free(dscrptr); return ENOMEM; } alloc_entry->len = len; alloc_entry->lb_num = lb_num; alloc_entry->vpart_num = vpart_num; alloc_entry->flags = flags; TAILQ_INSERT_TAIL(&udf_node->alloc_entries, alloc_entry, next_alloc); } calculated_len += len; } data_length -= icb_len; pos += icb_len; } /* while */ if (advance_sector) { /* Note: UDF descriptor length is maximised to one sector */ next_alloc->lb_num++; /* advance one sector */ entries--; } } /* indirect ICB check */ if (entries) { /* load in new dscrptr */ if (dscrptr != given_dscrptr) free(dscrptr); error = udf_read_logvol_descriptor(udf_log_vol, next_alloc->vpart_num, next_alloc->lb_num, what, &dscrptr, NULL); } } while (entries && !error); /* error from reading next sector in extent is not considered an error */ if (dscrptr != given_dscrptr && dscrptr) free(dscrptr); if (calculated_len != (uint64_t) udf_node->stat.st_size) { printf("UDF: create node length mismatch; stated as %g but calculated as %g bytes. fixing\n", (double) udf_node->stat.st_size, (double) calculated_len); udf_node->stat.st_size = calculated_len; } return 0; } int udf_readin_udf_node(struct udf_node *dir_node, struct long_ad *udf_icbptr, struct fileid_desc *fid, struct udf_node **res_sub_node) { struct udf_node *sub_node; char *fid_name; char entry_name[NAME_MAX]; uint32_t bucket; ino_t hashkey; int error; assert(dir_node); assert(udf_icbptr); assert(fid); assert(res_sub_node); /* check if its allready in the logical volume's udf_node cache (inodes) */ hashkey = udf_calc_hash(udf_icbptr); bucket = hashkey & UDF_INODE_HASHMASK; LIST_FOREACH(sub_node, &dir_node->udf_log_vol->udf_nodes[bucket], next_node) { if (sub_node->hashkey == hashkey) { *res_sub_node = sub_node; DEBUG(printf("found node in hashlist\n")); return 0; } } /* dump the FID we are trying to read in */ UDF_VERBOSE_MAX(udf_dump_descriptor((union dscrptr *) fid)); fid_name = (char *) fid->data + udf_rw16(fid->l_iu); udf_to_unix_name(entry_name, fid_name, fid->l_fi, &dir_node->udf_log_vol->log_vol->desc_charset); /* build missing vnode */ error = udf_readin_anon_udf_node(dir_node->udf_log_vol, NULL, udf_icbptr, entry_name, &sub_node); if (error) return error; if (!sub_node) { printf("sub_node = NULL? and no error? \n"); } assert(sub_node); /* link this UDF node to the mountpoint and remember its hash-key */ sub_node->mountpoint = dir_node->mountpoint; sub_node->hashkey = hashkey; /* XXX use file version number and filechar from fid ? */ sub_node->file_version_num = udf_rw16(fid->file_version_num); /* user set */ sub_node->udf_filechar = fid->file_char; /* insert/replace in mountpoint's udfnode hashtable with optional check for doubles */ if (0) { struct udf_node *lookup; LIST_FOREACH(lookup, &dir_node->udf_log_vol->udf_nodes[bucket], next_node) { if (lookup->hashkey == sub_node->hashkey) printf("DOUBLE hashnode?\n"); } } LIST_INSERT_HEAD(&dir_node->udf_log_vol->udf_nodes[bucket], sub_node, next_node); DEBUG(printf("inserting hash node for hash = %d\n", (int) hashkey)); *res_sub_node = sub_node; return 0; } void udf_syncnode_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) { /* struct udf_node *udf_node = (struct udf_node *) wrcallback->structure; */ wrcallback = wrcallback; /* unused for now */ sectordata = sectordata; if (reason == UDF_WRCALLBACK_REASON_PENDING) { /* what to do? */ return; } if (reason == UDF_WRCALLBACK_REASON_ANULATE) { /* what to do? */ return; } assert(reason == UDF_WRCALLBACK_REASON_WRITTEN); if (error) { printf("UDF error: syncnode writing failed, not fixing yet!\n"); return; } } /* XXX This mark node dirty code will simplify if we store nodes in buffers? XXX */ void udf_node_mark_dirty(struct udf_node *udf_node) { struct udf_allocentry *alloc_entry, *my_entry, *tail_entry; struct udf_node *search_node, *tail_node; if (udf_node->dirty) return; my_entry = TAILQ_FIRST(&udf_node->dscr_allocs); assert(my_entry); /* dscr locks ? */ UDF_MUTEX_LOCK(&udf_node->udf_log_vol->dirty_nodes_mutex); if (1) { /* insertion sort :-S */ tail_node = TAILQ_LAST(&udf_node->udf_log_vol->dirty_nodes, udf_node_list); if (!tail_node) { TAILQ_INSERT_TAIL(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty); } else { tail_entry = TAILQ_FIRST(&tail_node->dscr_allocs); if (tail_entry->lb_num < my_entry->lb_num) { TAILQ_INSERT_TAIL(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty); } else { /* find my place; could be done smarter */ TAILQ_FOREACH(search_node, &udf_node->udf_log_vol->dirty_nodes, next_dirty) { alloc_entry = TAILQ_FIRST(&tail_node->dscr_allocs); if (alloc_entry->lb_num > my_entry->lb_num) { TAILQ_INSERT_BEFORE(search_node, udf_node, next_dirty); break; /* foreach */ } } } } } else { /* dumb */ TAILQ_INSERT_TAIL(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty); } UDF_MUTEX_UNLOCK(&udf_node->udf_log_vol->dirty_nodes_mutex); udf_node->dirty = 1; } static void udf_node_unmark_dirty(struct udf_node *udf_node) { if (!udf_node->dirty) return; /* remove me just in case; why were we called otherwise? */ UDF_MUTEX_LOCK(&udf_node->udf_log_vol->dirty_nodes_mutex); TAILQ_REMOVE(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty); UDF_MUTEX_UNLOCK(&udf_node->udf_log_vol->dirty_nodes_mutex); udf_node->dirty = 0; } /* VOP_FSYNC : data FDATASYNC */ int udf_sync_udf_node(struct udf_node *udf_node, char *why) { DEBUG(printf("Syncing udf_node `%"PRIu64"` because of %s\n", udf_node->unique_id, why)); DEBUG(printf("sync udf node: dirty = %d, v_numoutput = %d\n", udf_node->dirty, udf_node->v_numoutput)); if (!udf_node->dirty) { /* Not dirty??!!! */ udf_node_unmark_dirty(udf_node); return 0; } if (!udf_node->udf_log_vol->writable) { fprintf(stderr, "encountered a dirty node on a read-only filingsystem!\n"); exit(1); } /* * We are really syncing disc but we are only continueing when the * node itself is clean... not breaking the semantics. */ /* XXX flushall flag magic XXX */ udf_bufcache->flushall = 1; udf_purgethread_kick("Sync node"); fflush(stdout); /* wait until all dirty bufs associated with this node are processed */ if (!udf_node->dirty) return 0; if (udf_node->v_numoutput) { usleep(100); } if (!udf_node->v_numoutput) return 0; UDF_VERBOSE(printf("(wait on node)")); while (udf_node->v_numoutput) { usleep(100); } return 0; } extern void udf_merge_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size); /* VOP_FSYNC : metadata FFILESYNC */ /* writeout the udf_node back to disc */ /* TODO don't forget to writeout our extended attributes and write out the link to the associated streamdir as well */ int udf_writeout_udf_node(struct udf_node *udf_node, char *why) { struct udf_wrcallback wr_callback; struct udf_allocentry *dscr_entry, *next_dscr_entry, *alloc_entry; union dscrptr *dscrptr; struct file_entry *fe; struct extfile_entry *efe; struct alloc_ext_entry *aee; struct lb_addr parent_icb; struct icb_tag *icbtag; struct long_ad *l_ad; struct short_ad *s_ad; uint32_t *l_adptr; /* points to length of alloc. descr. */ uint8_t *pos; char *what; uint32_t alloc_entries; uint64_t rest; /* in bytes */ uint64_t len; uint64_t logblks_rec; uint32_t lb_size, descr_ver; if (!udf_node->udf_log_vol->writable) { fprintf(stderr, "encountered a dirty node on a read-only filingsystem!\n"); exit(1); } lb_size = udf_node->udf_log_vol->lb_size; /* assure all file data is written out! and clean up */ udf_sync_udf_node(udf_node, why); /* XXX node lock? XXX */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); /* clean up allocentry queue so we get a nice clean layout */ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); /* allocate for descriptor */ dscrptr = calloc(1, lb_size); /* not a bit biggish? */ if (!dscrptr) return ENOMEM; /* calculate logical blocks recorded; zero for interns [UDF 2.3.6.5, ECMA 4/14.9.11, 4/14.6.8] */ logblks_rec = 0; if (udf_node->addr_type != UDF_ICB_INTERN_ALLOC) { UDF_MUTEX_LOCK(&udf_node->alloc_mutex); TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) { if (alloc_entry->flags == UDF_SPACE_ALLOCATED) { logblks_rec += (alloc_entry->len + lb_size-1) / lb_size; } } UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); } /* fill in (ext)fentry descriptor */ /* copy descriptor version from the logvol's */ bzero(&parent_icb, sizeof(struct lb_addr)); descr_ver = udf_rw16(udf_node->udf_log_vol->log_vol->tag.descriptor_ver); if (descr_ver == 3) { efe = &dscrptr->efe; efe->tag.id = udf_rw16(TAGID_EXTFENTRY); efe->tag.descriptor_ver = udf_rw16(descr_ver); efe->ckpoint = udf_rw32(1); /* [ECMA 4/14.17.17] */ udf_set_imp_id(&efe->imp_id); efe->logblks_rec = udf_rw64(logblks_rec); /* set additional fileinfo (access, uid/gid) etc. */ udf_node_set_fileinfo(udf_node, dscrptr); efe->obj_size = efe->inf_len; /* not true if there are streams [ECMA 4/48.17.11] */ icbtag = &efe->icbtag; } else if (descr_ver == 2) { fe = &dscrptr->fe; fe->tag.id = udf_rw16(TAGID_FENTRY); fe->tag.descriptor_ver = udf_rw16(descr_ver); fe->ckpoint = udf_rw32(1); /* [ECMA 4/14.17.17] */ udf_set_imp_id(&fe->imp_id); fe->logblks_rec = udf_rw64(logblks_rec); /* set additional fileinfo (access, uid/gid) etc. */ udf_node_set_fileinfo(udf_node, dscrptr); /* fe->obj_size doesn't exist */ icbtag = &fe->icbtag; } else { printf("UDF: i don't know this descriptor version %d\n", descr_ver); free(dscrptr); return EBADF; } /* update derived info */ udf_node->udf_icbtag_flags = (udf_node->udf_icbtag_flags & ~UDF_ICB_TAG_FLAGS_ALLOC_MASK) | udf_node->addr_type; /* strategy 4 has no parent nodes */ /* XXX NO extended attributes recorded YET (!!) XXX (like device nodes !!! ) */ dscr_entry = TAILQ_FIRST(&udf_node->dscr_allocs); /* ensure allocation of (ext) file descriptor */ if (!dscr_entry) { /* have to allocate one and add to queue ! */ printf("UDF: XXX no allocation of file descriptor entry yet in sync_udf_node\n"); return ENOENT; } /* XXX implement alloc entry walker? XXX */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); what = "UDF sync: File descriptor"; alloc_entry = TAILQ_FIRST(&udf_node->alloc_entries); do { assert(dscr_entry->len <= lb_size); if (icbtag) { /* fill in ICB fields */ /* XXX we're only writing out strategy type 4 !!! XXX */ icbtag->prev_num_dirs = udf_rw32(0); /* prev_alloc_entries = 0 due to strategy type 4 XXX */ icbtag->strat_type = udf_rw16(4); /* default UDF strategy type 4 XXX what about 4096? */ icbtag->parent_icb = parent_icb; icbtag->file_type = udf_node->udf_filetype; icbtag->flags = udf_rw16(udf_node->udf_icbtag_flags); icbtag->max_num_entries = udf_rw16(1); /* due to strategy type 4 ! XXX */ } pos = 0; rest = 0; l_adptr = NULL; /* invalid */ switch (udf_rw16(dscrptr->tag.id)) { case TAGID_FENTRY : /* not used now */ fe = &dscrptr->fe; pos = &fe->data[0] + udf_rw32(fe->l_ea); rest = dscr_entry->len - sizeof(struct file_entry) - udf_rw32(fe->l_ea); l_adptr = &fe->l_ad; break; case TAGID_EXTFENTRY : efe = &dscrptr->efe; pos = &efe->data[0] + udf_rw32(efe->l_ea); rest = dscr_entry->len - sizeof(struct extfile_entry) - udf_rw32(efe->l_ea); l_adptr = &efe->l_ad; break; case TAGID_ALLOCEXTENT : aee = &dscrptr->aee; pos = &aee->data[0]; rest = dscr_entry->len - sizeof(struct alloc_ext_entry); l_adptr = &aee->l_ad; break; case TAGID_INDIRECTENTRY : /* do we even do these in strat 4 ? */ printf("UDF: sanity check; request for writeout of indirect entry\n"); free(dscrptr); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); return ENOENT; /* panic really */ } assert(l_adptr); /* fill in remaining allocation entries */ /* remember to keep one SPARE for extending */ alloc_entries = 0; DEBUG(printf("Allocated at ")); if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { /* internal allocation -> copy in data */ assert(udf_node->intern_len <= rest); bzero(pos, rest); memcpy(pos, udf_node->intern_data, udf_node->intern_len); /* make sure the length for allocation entries is the same as the data it contains (4/8.8.2, 4/14.6.8) */ *l_adptr = udf_rw32(udf_node->intern_len); /* alloc_entry is zero, so it'll fall trough */ pos += udf_node->intern_len; /* advance pos for length calculation */ alloc_entry = NULL; /* not applicable */ } while ((rest > 2*udf_node->icb_len) && alloc_entry) { assert(udf_node->icb_len); DEBUG(printf("[%p, lb_num = %d, len = %d] ", alloc_entry, (uint32_t) alloc_entry->lb_num, (uint32_t) alloc_entry->len)); /* XXX assumption: UDF_SPACE_* is equal to UDF flags XXX */ len = alloc_entry->len | (((uint32_t) alloc_entry->flags) << 30); switch (udf_node->addr_type) { case UDF_ICB_SHORT_ALLOC : s_ad = (struct short_ad *) pos; s_ad->len = udf_rw32(len); s_ad->lb_num = udf_rw32(alloc_entry->lb_num); assert(alloc_entry->vpart_num == 0); if (alloc_entry->len == 0) s_ad->lb_num = udf_rw32(0); if (alloc_entry->flags == UDF_SPACE_FREE) s_ad->lb_num = udf_rw32(0); break; case UDF_ICB_LONG_ALLOC : l_ad = (struct long_ad *) pos; l_ad->len = udf_rw32(len); l_ad->loc.lb_num = udf_rw32(alloc_entry->lb_num); l_ad->loc.part_num = udf_rw16(alloc_entry->vpart_num); l_ad->impl.im_used.unique_id = udf_rw64(udf_node->unique_id); if (alloc_entry->len == 0) l_ad->loc.lb_num = udf_rw32(0); if (alloc_entry->flags == UDF_SPACE_FREE) l_ad->loc.lb_num = udf_rw32(0); break; } alloc_entries++; *l_adptr = udf_rw32(alloc_entries * udf_node->icb_len); alloc_entry = TAILQ_NEXT(alloc_entry, next_alloc); pos += udf_node->icb_len; rest -= udf_node->icb_len; } DEBUG(printf("\nend alloc\n\n")); next_dscr_entry = TAILQ_NEXT(dscr_entry, next_alloc); if (alloc_entry) { /* overflow */ /* ensure allocation of (ext) file descriptor */ if (!next_dscr_entry) { /* have to allocate one and add to queue ! */ printf("UDF: XXX no allocation of allocation extent descriptor yet in sync_udf_node\n"); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); return ENOENT; } /* flag next extent being specified */ len = next_dscr_entry->len | ((uint32_t) UDF_SPACE_REDIRECT << 30); switch (udf_node->addr_type) { case UDF_ICB_SHORT_ALLOC : s_ad = (struct short_ad *) pos; s_ad->len = udf_rw32(len); s_ad->lb_num = udf_rw32(next_dscr_entry->lb_num); assert(next_dscr_entry->vpart_num == 0); break; case UDF_ICB_LONG_ALLOC : l_ad = (struct long_ad *) pos; l_ad->len = udf_rw32(len); l_ad->loc.lb_num = udf_rw32(next_dscr_entry->lb_num); l_ad->loc.part_num = udf_rw16(next_dscr_entry->vpart_num); l_ad->impl.im_used.unique_id = udf_rw64(udf_node->unique_id); break; } alloc_entries++; pos += udf_node->icb_len; rest -= udf_node->icb_len; } /* manage lengths */ dscrptr->tag.desc_crc_len = udf_rw16((pos - (uint8_t *) dscrptr) - UDF_DESC_TAG_LENGTH); /* writeout */ wr_callback.function = udf_syncnode_callback; #if 0 wr_callback.structure = (void *) udf_node; wr_callback.vpart_num = dscr_entry->vpart_num; wr_callback.lb_num = dscr_entry->lb_num; wr_callback.offset = 0; /* ? */ wr_callback.length = dscr_entry->len; #endif UDF_VERBOSE_MAX( udf_validate_tag_and_crc_sums(dscrptr); /* for dumping */ udf_dump_descriptor(dscrptr); ); errno = udf_write_logvol_descriptor(udf_node->udf_log_vol, dscr_entry->vpart_num, dscr_entry->lb_num, what, dscrptr, &wr_callback); /* advance */ if (alloc_entry) { dscr_entry = next_dscr_entry; assert(dscr_entry); /* allocate and initialise new allocation extent descriptor */ /* also flag no icbtag follows */ bzero(dscrptr, lb_size); aee = &dscrptr->aee; aee->tag.id = udf_rw16(TAGID_ALLOCEXTENT); aee->tag.descriptor_ver = udf_node->udf_log_vol->log_vol->tag.descriptor_ver; icbtag = NULL; what = "UDF sync: allocation extent"; } } while (alloc_entry); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); /* XXX Free extra non used allocation extent descriptors XXX */ /* Mark me clean */ if (udf_node->mountpoint) { /* remove me just in case; why were we called otherwise? */ udf_node_unmark_dirty(udf_node); } return 0; } /****************************************************************************************** * * Space bitmap reader and writer * ******************************************************************************************/ /* tested OK */ int udf_read_in_space_bitmap(struct udf_alloc_entries *queue, struct space_bitmap_desc *sbd, uint32_t lb_size, uint64_t *freespace) { struct udf_allocentry *alloc_entry; uint64_t bits, from, now, start, end; uint8_t byte, bit, bitpos, state, *pos; int cnt; assert(udf_rw16(sbd->tag.id) == TAGID_SPACE_BITMAP); DEBUG(printf("processing space bitmap : \n");); bits = udf_rw32(sbd->num_bits); /* * Mark the disc as completely full; * Bugallert: use `udf_mark_allocentry_queue()' for the extent might * not fit in just one alloc entry for bigger discs */ assert(TAILQ_EMPTY(queue)); udf_mark_allocentry_queue(queue, lb_size, 0, bits * lb_size, UDF_SPACE_ALLOCATED, NULL, NULL); pos = sbd->data; from = 0; now = 0; bitpos = 0; byte = *pos; state = byte & 1; *freespace = 0; while (now < bits) { if (bitpos == 0) { byte = *pos++; } bit = byte & 1; if (bit != state) { if (state) { start = from; end = now-1; /* printf("[%08d - %08d]", start, end); */ udf_mark_allocentry_queue(queue, lb_size, start*lb_size, (end-start+1)*lb_size, UDF_SPACE_FREE, NULL, NULL); *freespace += (end-start+1)*lb_size; } from = now; state = bit; } byte >>= 1; bitpos = (bitpos+1) & 7; now++; } if (state) { start = from; end = now; /* printf("[%08d - %08d]", start, end); */ udf_mark_allocentry_queue(queue, lb_size, start*lb_size, (end-start)*lb_size, UDF_SPACE_FREE, NULL, NULL); *freespace += (end-start)*lb_size; } UDF_VERBOSE_TABLES( printf("\t\tFree space found on this partition"); cnt = 0; start = 0; TAILQ_FOREACH(alloc_entry, queue, next_alloc) { if (alloc_entry->flags == UDF_SPACE_ALLOCATED) { /* printf("... "); */ } else { if (cnt == 0) printf("\n\t\t\t"); printf("[%08"PRIu64" - %08"PRIu64"] ", start / lb_size, ((start + alloc_entry->len) / lb_size)-1); cnt++; if (cnt > 4) cnt = 0; } start += alloc_entry->len; } printf("\n"); ); /* merge is not nessisary */ return 0; } /* inverse of readin space bitmap; it synchronises the bitmap with the queue */ /* tested OK */ int udf_sync_space_bitmap(struct udf_alloc_entries *queue, struct space_bitmap_desc *sbd, uint32_t lb_size) { struct udf_allocentry *alloc_entry; uint32_t start, bits, total_bits; uint32_t cnt, byte; uint8_t bit, bitmask, setting; uint8_t *pos; /* merge it just in case */ udf_merge_allocentry_queue(queue, lb_size); total_bits = udf_rw32(sbd->num_bits); DEBUG(printf("SYNC SPACE BITMAP DEBUG: total bits = %d\n", total_bits)); alloc_entry = TAILQ_FIRST(queue); start = alloc_entry->lb_num; assert(start == 0); TAILQ_FOREACH(alloc_entry, queue, next_alloc) { DEBUG(printf(" [%d : %d + %d]", alloc_entry->flags, alloc_entry->lb_num, alloc_entry->len)); bits = alloc_entry->len / lb_size; assert(bits*lb_size == alloc_entry->len); byte = start / 8; bit = start - byte*8; pos = sbd->data + byte; if (byte*8 + bit + bits > total_bits) { /* XXX > or >= ? */ /* this should NEVER happen */ printf("UDF: not enough space writing back space bitmap! HELP!\n"); return EBADF; } cnt = 0; setting = (alloc_entry->flags != UDF_SPACE_FREE) ? 0 : 255; while (cnt < bits) { bitmask = (1 << bit); /* simple sanity check */ if (byte*8 + bit >= total_bits) { printf("IEEEE!!!! too big; %d instead of %d\n", (byte*8 + bit), total_bits); } *pos = (*pos & (~bitmask)) | (setting ? bitmask: 0); cnt++; bit++; if (bit == 8) { /* byte transition */ byte++; bit = 0; pos++; #if 0 /* speedup by doing bytes at a time */ while (bits-cnt > 8) { *pos = setting; cnt += 8; pos++; byte++; } #endif } } start += bits; } DEBUG(printf("\n\n")); return 0; } /****************************************************************************************** * * Filepart readers and writers * ******************************************************************************************/ /* part of VOP_STRATEGY */ /* internal function; reads in a new buffer */ /* !!! bufcache lock ought to be held on entry !!! */ int udf_readin_file_buffer(struct udf_node *udf_node, char *what, uint32_t sector, int cache_flags, struct udf_buf **buf_entry_p) { struct udf_allocentry *alloc_entry; struct udf_buf *buf_entry; uint64_t cur_offset; uint64_t overlap_length, overlap_sectors, transfer_length; uint32_t lb_size; uint32_t len, lb_num, vpart_num; int32_t error; uint8_t flags; assert(udf_node); assert(buf_entry_p); assert(udf_bufcache->bufcache_lock.locked); error = udf_get_buf_entry(udf_node, buf_entry_p); if (error) return error; buf_entry = *buf_entry_p; lb_size = udf_node->udf_log_vol->lb_size; /* internal node? */ if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { buf_entry->b_lblk = 0; buf_entry->b_flags = 0; /* not dirty, not needing alloc */ buf_entry->b_bcount = udf_node->intern_len; buf_entry->b_resid = lb_size - udf_node->intern_len; memcpy(buf_entry->b_data, udf_node->intern_data, udf_node->intern_len); UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_attach_buf_to_node(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); return error; } /* `normal' node */ /* find sector in the allocation space */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); cur_offset = 0; error = EIO; /* until proven readable */ TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) { len = alloc_entry->len; lb_num = alloc_entry->lb_num; vpart_num = alloc_entry->vpart_num; flags = alloc_entry->flags; /* check overlap with this alloc entry */ if (cur_offset + len > sector * lb_size) { /* found an extent that fits */ assert(((sector * lb_size - cur_offset) % lb_size) == 0); /* ought to be on sector boundary */ lb_num += sector - (cur_offset / lb_size); overlap_length = cur_offset + len - sector * lb_size; overlap_sectors = (overlap_length + lb_size -1) / lb_size; transfer_length = MIN(lb_size, overlap_length); /* max one logical sector */ /* fill in new buf info */ buf_entry->b_lblk = sector; buf_entry->b_bcount = transfer_length; buf_entry->b_resid = lb_size - transfer_length; /* sector transfered; mark valid */ buf_entry->b_flags = 0; /* not dirty and valid */ switch (flags) { case UDF_SPACE_ALLOCATED : /* on disc or in the write queue/cache to be written out; read one block at a time */ error = udf_read_logvol_sector(udf_node->udf_log_vol, vpart_num, lb_num, what, buf_entry->b_data, overlap_sectors, cache_flags); break; case UDF_SPACE_FREE : /* fall trough */ case UDF_SPACE_ALLOCATED_BUT_NOT_USED : error = 0; bzero(buf_entry->b_data, lb_size); break; default : fprintf(stderr, "Got an redirect flag, can't happen\n"); break; } if (error) { fprintf(stderr, "\tgot error from read_logvol_sector : %s\n", strerror(error)); break; /* FOREACH */ } UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_attach_buf_to_node(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); return 0; } /* looking for overlap */ cur_offset += len; } /* FOREACH */ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); *buf_entry_p = NULL; udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */ udf_free_buf_entry(buf_entry); return error; } /* internal function; shedule writing out of a buffer */ /* part of VOP_STRATEGY */ int udf_writeout_file_buffer(struct udf_node *udf_node, char *what, int cache_flags, struct udf_buf *buf_entry) { struct udf_allocentry *from_alloc, *to_alloc, *alloc_entry; uint32_t lb_num, lb_size, lblk; uint16_t vpart_num; int error; what = what; /* not used now */ if (!udf_node->udf_log_vol->writable) { /* ieek */ fprintf(stderr, "write request from non writable file buffer?\n"); } /* first get logical block offset and block size */ lblk = buf_entry->b_lblk; lb_size = udf_node->udf_log_vol->lb_size; error = 0; UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->alloc_mutex); UDF_MUTEX_LOCK(&udf_node->buf_mutex); /* maybe a bit too strict locking; code is not winning the beauty contest */ /* check if we can convert/save disc space */ if (udf_node->stat.st_size <= udf_node->intern_free) { /* convert to internal alloc with backup storage (writing there) */ if (udf_node->addr_type != UDF_ICB_INTERN_ALLOC) { DEBUG(printf("CONVERTING to intern alloc\n")); /* first free all previously allocated sectors (if any) */ error = udf_node_release_extent(udf_node, 0, udf_node->stat.st_size); } assert(!error); if (!udf_node->intern_data) { udf_node->intern_data = calloc(1, udf_node->intern_free); } if (udf_node->intern_data) { assert(buf_entry->b_bcount <= udf_node->intern_free); memcpy(udf_node->intern_data, buf_entry->b_data, buf_entry->b_bcount); udf_node->intern_len = buf_entry->b_bcount; udf_node->addr_type = UDF_ICB_INTERN_ALLOC; /* we don't write here: mark buffer clean */ udf_mark_buf_clean(udf_node, buf_entry); udf_mark_buf_allocated(udf_node, buf_entry); /* signal its allocated */ buf_entry->b_flags &= ~(B_ERROR); /* check if all buffers are gone now and mark node for writeout to indicate state change */ assert(udf_node->v_numoutput == 0); udf_node_mark_dirty(udf_node); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return 0; } /* fall trough ... for some reason it isn't converted */ } else { if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { DEBUG(printf("CONVERTING to normal alloc\n")); /* won't fit anymore, have to turn it into a `normal' alloced */ udf_node->intern_len = 0; if (udf_node->intern_data) free(udf_node->intern_data); udf_node->intern_data = NULL; udf_node->icb_len = sizeof(struct long_ad); udf_node->addr_type = UDF_ICB_LONG_ALLOC; udf_node_mark_dirty(udf_node); /* mark needalloc to make sure it gets an address */ udf_mark_buf_needing_allocate(udf_node, buf_entry); /* signal it needs allocation */ } } /* merge queue first to get better performance */ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size); /* get extent that it represents */ udf_mark_allocentry_queue(&udf_node->alloc_entries, lb_size, (uint64_t) lblk*lb_size, buf_entry->b_bcount, UDF_SPACE_ALLOCATED, &from_alloc, &to_alloc); alloc_entry = from_alloc; if (buf_entry->b_flags & B_NEEDALLOC) { /* need allocation */ error = udf_node_allocate_lbs(udf_node, /* num lb */ 1, &vpart_num, &lb_num, NULL); assert(!error); udf_mark_buf_allocated(udf_node, buf_entry); alloc_entry->lb_num = lb_num; alloc_entry->vpart_num = vpart_num; } assert(TAILQ_NEXT(alloc_entry, next_alloc) == to_alloc || (alloc_entry == to_alloc)); lb_num = alloc_entry->lb_num; vpart_num = alloc_entry->vpart_num; UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); /* printf("writing out filebuffer vpart %d, lb_num %d for %s\n", vpart_num, lb_num, what); */ error = udf_write_logvol_sector(udf_node->udf_log_vol, vpart_num, lb_num, "File contents", buf_entry->b_data, cache_flags, NULL); UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->buf_mutex); if (error) { printf("YIKES error during writing of logvol_sector\n"); udf_mark_buf_needing_allocate(udf_node, buf_entry); buf_entry->b_flags |= B_ERROR; /* buf_entry->b_errno = error; */ } else { udf_mark_buf_clean(udf_node, buf_entry); buf_entry->b_flags &= ~(B_ERROR); } UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return error; } /* VOP_READ */ int udf_read_file_part_uio(struct udf_node *udf_node, char *what, int content, struct uio *data_uio) { struct udf_buf *buf_entry; off_t offset; uint64_t sector, lb_size, data_length; uint8_t *base; int error, short_buf; if (!udf_node) return EINVAL; /* NOTE: we are NOT marking the node dirty only by reading it */ udf_set_timespec_now(&udf_node->stat.st_atimespec); if (udf_node->stat.st_size == 0) { if (data_uio->uio_resid) return EIO; /* reading past end by default */ return 0; } lb_size = udf_node->udf_log_vol->lb_size; error = 0; while (data_uio->uio_resid) { error = 0; short_buf = 0; sector = data_uio->uio_offset / lb_size; /* lookup sector in buffer set */ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); udf_lookup_node_buf(udf_node, sector, &buf_entry); if (!buf_entry || (buf_entry && (buf_entry->b_lblk != sector))) { /* `page in' sector from file */ error = udf_readin_file_buffer(udf_node, what, sector, content, &buf_entry); } if (!error && buf_entry) { offset = data_uio->uio_offset - (off_t) sector*lb_size; base = buf_entry->b_data; if (offset >= 0) { data_length = buf_entry->b_bcount - offset; data_length = MIN(data_length, data_uio->uio_resid); uiomove(base + offset, data_length, data_uio); } short_buf = (buf_entry->b_bcount < lb_size); /* short buf -> none will follow */ } assert(!error || (error && !buf_entry)); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); if (error) break; /* while */ if (data_uio->uio_resid == 0) return 0; /* finished? */ if (short_buf) break; /* while */ } /* while */ if (data_uio->uio_resid) { printf("UDF: WARNING file is truncated; missing %d bytes while reading %s\n", (int) data_uio->uio_resid, what); return EIO; } return error; } /* XXX meaning is to be changed; kept for reference still XXX */ void udf_filepart_write_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) { /* struct udf_node *udf_node = (struct udf_node *) wrcallback->structure; */ wrcallback = wrcallback; /* unused for now */ sectordata = sectordata; if (reason == UDF_WRCALLBACK_REASON_PENDING) { /* what to do? */ return; } if (reason == UDF_WRCALLBACK_REASON_ANULATE) { /* what to do? */ return; } assert(reason == UDF_WRCALLBACK_REASON_WRITTEN); if (error) { printf("UDF error: file part write failed, not fixing yet!\n"); return; } } /* VOP_TRUNCATE */ /* doesn't lock udf_node */ int udf_truncate_node(struct udf_node *udf_node, uint64_t length /* ,ioflags */) { struct udf_log_vol *udf_log_vol; struct udf_allocentry *alloc_entry, *cut_point; struct udf_buf *buf_entry, *marker; uint32_t lb_size; uint64_t cur_extent, block_extent, new_extent, too_much; uint32_t last_sector; int32_t error; if (!udf_node) return EINVAL; if (udf_open_logvol(udf_node->udf_log_vol)) return EROFS; udf_log_vol = udf_node->udf_log_vol; lb_size = udf_log_vol->lb_size; /* we might change this node AND access times ... */ if (!udf_node->dirty) { /* mark node as dirty */ udf_node_mark_dirty(udf_node); } /* XXX lock node !!! XXX */ /* merge known allocation entry queue */ new_extent = length; block_extent = (uint64_t) lb_size * ((new_extent + lb_size-1) / lb_size); /* inclusive */ too_much = block_extent - new_extent; assert(block_extent >= new_extent); UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->alloc_mutex); udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size); /* extend the file when nessisary */ if (new_extent > (uint64_t) udf_node->stat.st_size) { if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { /* XXX to seperate function XXX */ /* grow intern node by converting it to a normal buffer first */ /* First lookup if the node already had a buffer associated */ udf_lookup_node_buf(udf_node, 0, &buf_entry); if (!buf_entry) { error = udf_get_buf_entry(udf_node, &buf_entry); if (error) { UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return error; } buf_entry->b_lblk = 0; buf_entry->b_flags = 0; UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_attach_buf_to_node(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); } else { DEBUG(printf("found buffer associated with intern node!\n")); } buf_entry->b_bcount = MIN(lb_size, new_extent); buf_entry->b_resid = lb_size - buf_entry->b_bcount; memcpy(buf_entry->b_data, udf_node->intern_data, udf_node->intern_len); memset(buf_entry->b_data + udf_node->intern_len, 0, lb_size - udf_node->intern_len); UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_mark_buf_dirty(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); udf_node->intern_len = 0; if (udf_node->intern_data) free(udf_node->intern_data); udf_node->intern_data = NULL; udf_node->icb_len = sizeof(struct long_ad); udf_node->addr_type = UDF_ICB_LONG_ALLOC; } udf_cut_allocentry_queue(&udf_node->alloc_entries, lb_size, block_extent); if (new_extent < block_extent) { alloc_entry = TAILQ_LAST(&udf_node->alloc_entries, udf_alloc_entries); assert(alloc_entry->len > too_much); alloc_entry->len -= too_much; } /* XXX Andrey suggested buffer extension? XXX */ udf_node->stat.st_size = new_extent; } UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); /* `free' the extent when shrinking the extent */ if (new_extent < (uint64_t) udf_node->stat.st_size) { DEBUG(printf("TRIMMING file %"PRIu64" from %d to %d\n", udf_node->unique_id, (int32_t) udf_node->stat.st_size, (uint32_t) length)); /* free file buffers that are not needed anymore */ /* XXX NetBSD kernel : uvm_vnp_setsize() XXX */ marker = calloc(1, sizeof(struct udf_buf)); if (!marker) return ENOMEM; UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->buf_mutex); /* use marker as we are going to delete items in the list we are traversing */ last_sector = new_extent / lb_size; TAILQ_INSERT_HEAD(&udf_node->vn_bufs, marker, b_vnbufs); while ((buf_entry = TAILQ_NEXT(marker, b_vnbufs))) { /* advance marker */ TAILQ_REMOVE(&udf_node->vn_bufs, marker, b_vnbufs); TAILQ_INSERT_AFTER(&udf_node->vn_bufs, buf_entry, marker, b_vnbufs); /* process buf_entry */ if (buf_entry->b_lblk > last_sector) { udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */ udf_detach_buf_from_node(udf_node, buf_entry); udf_free_buf_entry(buf_entry); } /* trim last buffer entry */ /* XXX NetBSD kernel : uvm_vnp_zerorange(), vtruncbuf() XXX */ if (buf_entry->b_lblk == last_sector) { buf_entry->b_bcount = udf_node->stat.st_size % lb_size; buf_entry->b_resid = buf_entry->b_bufsize - buf_entry->b_bcount; /* still data to record? */ if (buf_entry->b_bcount == 0) { /* marker not an issue here */ udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */ udf_detach_buf_from_node(udf_node, buf_entry); udf_free_buf_entry(buf_entry); } } } TAILQ_REMOVE(&udf_node->vn_bufs, marker, b_vnbufs); free(marker); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->alloc_mutex); if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { /* shrink intern node */ too_much = udf_node->stat.st_size - new_extent; udf_node->intern_len -= too_much; udf_node->intern_free += too_much; memset(udf_node->intern_data + new_extent, 0, too_much); } else { /* shrink alloc entries _after_ deleting bufs to make sure we are not crossed */ error = udf_node_release_extent(udf_node, block_extent, udf_node->stat.st_size); /* after `cleanup', trim now unused entries */ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size); if (new_extent == 0) { /* remove all; most common case too */ while ((alloc_entry = TAILQ_FIRST(&udf_node->alloc_entries))) { TAILQ_REMOVE(&udf_node->alloc_entries, alloc_entry, next_alloc); free(alloc_entry); } cur_extent = 0; } else { /* move code to udf_allocentries.c ? */ udf_cut_allocentry_queue(&udf_node->alloc_entries, lb_size, block_extent); /* find cut-point */ cur_extent = 0; TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) { cur_extent += alloc_entry->len; if (cur_extent == block_extent) break; } cut_point = alloc_entry; /* all after this point need to be deleted */ assert(cut_point); while ((alloc_entry = TAILQ_NEXT(cut_point, next_alloc))) { TAILQ_REMOVE(&udf_node->alloc_entries, alloc_entry, next_alloc); free(alloc_entry); } /* clip last entry to the correct size */ if (new_extent < block_extent) { assert(too_much == block_extent - new_extent); alloc_entry = TAILQ_LAST(&udf_node->alloc_entries, udf_alloc_entries); assert(alloc_entry->len > too_much); alloc_entry->len -= too_much; cur_extent -= too_much; } } assert(cur_extent == new_extent); } udf_node->stat.st_size = new_extent; UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); } return 0; } /* VOP_WRITE */ int udf_write_file_part_uio(struct udf_node *udf_node, char *what, int content, struct uio *data_uio) { struct udf_buf *buf_entry; off_t offset; uint64_t new_possible_extent; uint64_t start_extent, end_extent; uint64_t lb_size, sector, data_length; uint8_t *base; int error, appending, allocated; if (!udf_node) return EINVAL; if (udf_open_logvol(udf_node->udf_log_vol)) return EROFS; /* write chanches both modification and file status times */ udf_set_timespec_now(&udf_node->stat.st_ctimespec); udf_set_timespec_now(&udf_node->stat.st_mtimespec); /* Zero length write -> finished */ if (data_uio->uio_resid == 0) return 0; /* TODO lock node directly to avoid multiple writers entering */ /* pthread_rwlock_wrlock(&udf_node->udf_node_lock); */ if (!udf_node->dirty) { udf_node_mark_dirty(udf_node); } /* auto-extent file */ appending = 0; allocated = 0; new_possible_extent = data_uio->uio_offset + data_uio->uio_resid; if (new_possible_extent >= (uint64_t) udf_node->stat.st_size) { /* BUGALERT: extra space can be pre-allocated AFTER file length */ error = udf_truncate_node(udf_node, new_possible_extent); appending = 1; } lb_size = udf_node->udf_log_vol->lb_size; while (data_uio->uio_resid) { error = 0; sector = data_uio->uio_offset / lb_size; /* lookup sector in buffer set */ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); udf_lookup_node_buf(udf_node, sector, &buf_entry); if (!buf_entry || (buf_entry && (buf_entry->b_lblk != sector))) { /* not found in cache; `page in' sector from file BUT ONLY if we don't completely overwrite it anyway */ if ((data_uio->uio_resid < lb_size) && (!appending)) { DEBUG(printf("Reading in file buffer for %s for %"PRIu64" bytes\n", what, (uint64_t) data_uio->uio_resid)); error = udf_readin_file_buffer(udf_node, what, sector, content, &buf_entry); } /* check if the extent is allocated; check assumption that the size of a buffer is a lb_size */ if (buf_entry) assert(buf_entry->b_bufsize == lb_size); UDF_MUTEX_LOCK(&udf_node->alloc_mutex); start_extent = (uint64_t) lb_size * sector; end_extent = MIN((uint64_t) udf_node->stat.st_size, start_extent + lb_size); error = udf_extent_properties(&udf_node->alloc_entries, lb_size, start_extent, end_extent, &allocated); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); /* check free space if space is to allocated */ if (!buf_entry || !allocated) { /* be coulant on metadata here to avoid not to easy to solve resource problems */ if (content == UDF_C_USERDATA) { /* check for space no space anymore for userdata (bit on the safe side really) */ assert(udf_node->udf_log_vol); if (!udf_confirm_freespace(udf_node->udf_log_vol, content, lb_size)) { UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return ENOSPC; } } } DEBUG(if (allocated) printf("Writing pre-allocated buffer\n")); if (!buf_entry) { /* create new buffer */ error = udf_get_buf_entry(udf_node, &buf_entry); if (error) { UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return error; } /* don't forget to set the relative block number! */ buf_entry->b_lblk = sector; /* add it to the buffer list */ UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_attach_buf_to_node(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); } assert(buf_entry); if (allocated) { /* * we could free the old allocated extent and mark it needing allocation * again */ } if (!allocated) { /* mark it needs to be allocated, locks are not very nice here */ UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_mark_buf_needing_allocate(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); } } if (error) { UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); break; /* while */ } assert(buf_entry); offset = data_uio->uio_offset - (off_t) sector*lb_size; base = buf_entry->b_data; assert(offset >= 0); if (offset >= 0) { UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_mark_buf_dirty(udf_node, buf_entry); data_length = buf_entry->b_bufsize - offset; data_length = MIN(data_length, data_uio->uio_resid); uiomove(base + offset, data_length, data_uio); buf_entry->b_bcount = MAX(buf_entry->b_bcount, offset + data_length); buf_entry->b_resid = buf_entry->b_bufsize - buf_entry->b_bcount; UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); } UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); if (data_uio->uio_resid == 0) return 0; /* finished? */ } /* while */ return 0; } /****************************************************************************************** * * UDF volume and descriptor logic * ******************************************************************************************/ struct udf_volumeset *udf_search_volumeset(char *volset_id) { struct udf_volumeset *volumeset; struct udf_pri_vol *primary; /* XXX this is a bit ugly XXX */ SLIST_FOREACH(volumeset, &udf_volumeset_list, next_volumeset) { primary = STAILQ_FIRST(&volumeset->primaries); assert(primary->pri_vol); if (memcmp(primary->pri_vol->volset_id, volset_id, 128) == 0) return volumeset; } return NULL; } struct udf_pri_vol *udf_search_primary(struct udf_volumeset *set, char *id) { struct udf_pri_vol *primary; STAILQ_FOREACH(primary, &set->primaries, next_primary) { assert(primary->pri_vol); if (memcmp(primary->pri_vol->vol_id, id, 32) == 0) return primary; } return NULL; } struct udf_log_vol *udf_search_logical_volume_in_primary(struct udf_pri_vol *primary, char *logvol_id) { struct udf_log_vol *here; SLIST_FOREACH(here, &primary->log_vols, next_logvol) { if (memcmp(here->log_vol->logvol_id, logvol_id, 128) == 0) return here; } return NULL; } /* called not very often ... ; free incomming when not needed */ int udf_proc_pri_vol(struct udf_session *udf_session, struct udf_pri_vol **current, struct pri_vol_desc *incomming) { struct udf_volumeset *volset; struct udf_pri_vol *primary; assert(current); volset = udf_search_volumeset(incomming->volset_id); if (!volset) { /* create a volume set */ volset = calloc(1, sizeof(struct udf_volumeset)); if (!volset) { free(incomming); return ENOMEM; } /* populate and link in */ volset->max_partnum = 0; STAILQ_INIT(&volset->primaries); SLIST_INSERT_HEAD(&udf_volumeset_list, volset, next_volumeset); } assert(volset); primary = udf_search_primary(volset, incomming->vol_id); *current = primary; if (!primary) { /* create a primary volume */ primary = calloc(1, sizeof(struct udf_pri_vol)); if (!primary) { free(incomming); return ENOMEM; } /* add to volset's primaries list */ STAILQ_INSERT_TAIL(&volset->primaries, primary, next_primary); *current = primary; } else { /* mark as current */ /* ok ... we now need to check if this new descriptor is a newer version */ if (udf_rw32(incomming->seq_num) <= udf_rw32(primary->pri_vol->seq_num)) { if (udf_session->session_num <= (*current)->udf_session->session_num) { DEBUG(printf("UDF: DISCARDING primary descriptor for its the same but higher session number\n")); /* its an older one; ignore */ free(incomming); return 0; } } DEBUG(printf("UPDATING primary descriptor for it has a higher session number\n")); } /* update the primary volume descriptor */ if (primary->pri_vol) free(primary->pri_vol); primary->volumeset = volset; primary->pri_vol = incomming; primary->udf_session = udf_session; return 0; } /* not called often ; free lvid when not used */ int udf_proc_logvol_integrity(struct udf_log_vol *udf_log_vol, struct logvol_int_desc *new_lvid) { struct udf_logvol_info *impl; uint64_t psize, pfree; uint32_t *free_space_pos, *size_pos; uint32_t lb_size, part_map; uint32_t integrity; int error, tagid; error = udf_check_tag((union dscrptr *) new_lvid); if (error) { return error; /* return error on faulty tag */ } tagid = udf_rw16(new_lvid->tag.id); /* getting a terminator tag or zero is an OK condition */ if ((tagid == TAGID_TERM) || (tagid == 0)) { return 0; } /* not getting an logical volume itegrity volume descriptor is an error now */ if (tagid != TAGID_LOGVOL_INTEGRITY) { printf("IEE! got a %d tag while searching for a logical volume integrity descriptor\n", tagid); return EINVAL; /* XXX error code? XXX */ } /* check CRC on the contents of the logvol integrity */ error = udf_check_tag_payload((union dscrptr *) new_lvid); if (error) { return error; } /* check logvol integrity validity */ integrity = udf_rw32(new_lvid->integrity_type); if ((integrity != UDF_INTEGRITY_OPEN) && (integrity != UDF_INTEGRITY_CLOSED)) return EINVAL; /* allways go for the next in line; silly but thats Ecma-167/UDF */ /* process information contained in logical volume integrity descriptor */ udf_log_vol->logvol_state = integrity; udf_log_vol->integrity_serial = udf_rw16(new_lvid->tag.serial_num); impl = (struct udf_logvol_info *) (new_lvid->tables + 2*udf_rw32(new_lvid->num_part)); udf_log_vol->min_udf_readver = udf_rw16(impl->min_udf_readver); udf_log_vol->min_udf_writever = udf_rw16(impl->min_udf_writever); udf_log_vol->max_udf_writever = udf_rw16(impl->max_udf_writever); udf_log_vol->num_files = udf_rw32(impl->num_files); udf_log_vol->num_directories = udf_rw32(impl->num_directories); udf_log_vol->next_unique_id = udf_rw64(new_lvid->lvint_next_unique_id); /* calculate free space from this integrity descritor */ lb_size = udf_log_vol->lb_size; /* init start positions */ free_space_pos = &new_lvid->tables[0]; size_pos = &new_lvid->tables[udf_log_vol->num_part_mappings]; /* init counters */ udf_log_vol->total_space = udf_log_vol->free_space = udf_log_vol->await_alloc_space = 0; for (part_map = 0; part_map < udf_log_vol->num_part_mappings; part_map++) { psize = udf_rw32(*size_pos); size_pos++; pfree = udf_rw32(*free_space_pos); free_space_pos++; if (pfree != UINT_MAX) { /* if UINT_MAX, its not applicable like virtual space partitions */ udf_log_vol->total_space += (uint64_t) psize * lb_size; udf_log_vol->free_space += (uint64_t) pfree * lb_size; } } UDF_VERBOSE( if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) { udf_dump_timestamp("\t\t\t\tmarked open at ", &new_lvid->time); } else { udf_dump_timestamp("\t\t\t\tmarked closed at ", &new_lvid->time); } ); return 0; } void udf_derive_new_logvol_integrity(struct udf_log_vol *udf_log_vol) { udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN; udf_log_vol->integrity_serial = 1; /* analyse the log vol to check out minimum and maximum read/write versions */ if (udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver) == 2) { udf_log_vol->min_udf_readver = 0x0102; udf_log_vol->min_udf_writever = 0x0150; udf_log_vol->max_udf_writever = 0x0150; } else { udf_log_vol->min_udf_readver = 0x0201; udf_log_vol->min_udf_writever = 0x0201; udf_log_vol->max_udf_writever = 0x0201; /* 2.50? */ } udf_log_vol->num_files = 0; udf_log_vol->num_directories = 0; udf_log_vol->next_unique_id = 16; /* zero first, rest 15/16+ minimum */ } int udf_proc_logvol_integrity_sequence(struct udf_log_vol *udf_log_vol) { union dscrptr *dscr; uint32_t sector, length, lvid_len, num_sectors; uint32_t lb_size; int error; sector = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.loc); length = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.len); lb_size = udf_log_vol->lb_size; /* go for the default `open' integrity first as initialisation */ udf_derive_new_logvol_integrity(udf_log_vol); if (!length) { fprintf(stderr, "UDF: no volume integrity descriptor sequence space defined... OK for Ecma-167, not for UDF; rejecting\n"); return EBADF; } error = 0; while (length) { error = udf_read_session_descriptor(udf_log_vol->primary->udf_session, sector, "Logical volume integrity descriptor (LVID)", &dscr, &lvid_len); if (error) { if (dscr) free(dscr); dscr = NULL; break; } UDF_VERBOSE_MAX(udf_dump_descriptor(dscr)); error = udf_proc_logvol_integrity(udf_log_vol, &dscr->lvid); if (error) break; if (udf_rw16(dscr->tag.id) == TAGID_TERM) break; num_sectors = (lvid_len + lb_size-1) / lb_size; length -= num_sectors * lb_size; sector += num_sectors; if (udf_rw32(dscr->lvid.next_extent.len)) { sector = udf_rw32(dscr->lvid.next_extent.loc); length = udf_rw32(dscr->lvid.next_extent.len); } /* free consumed descriptor */ free(dscr); dscr = NULL; } /* free dangling descriptor */ if (dscr) free(dscr); /* either an error has occured or we have processed all descriptors */ if (error) { fprintf(stderr, "WARNING: integrity sequence ended with a bad descriptor; creating new\n"); udf_derive_new_logvol_integrity(udf_log_vol); return ENOENT; } /* * If its marked closed we hope/assume all is fine otherwise it may be * marked closed later on when we are using a VAT and its found and * correct */ return 0; } /* Add partition mapping to specified logvol descriptor */ void udf_add_physical_to_logvol(struct logvol_desc *logvol, uint16_t vol_seq_num, uint16_t phys_part_num) { union udf_pmap *pmap; uint8_t *pmap_pos; pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); pmap = (union udf_pmap *) pmap_pos; pmap->pm1.type = 1; pmap->pm1.len = sizeof(struct part_map_1); pmap->pm1.vol_seq_num = udf_rw16(vol_seq_num); pmap->pm1.part_num = udf_rw16(phys_part_num); /* increment partition mapping count */ logvol->n_pm = udf_rw32(udf_rw32(logvol->n_pm) + 1); logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + sizeof(struct part_map_1)); logvol->tag.desc_crc_len = udf_rw16(udf_rw16(logvol->tag.desc_crc_len) + sizeof(struct part_map_1)); } #if 0 /* not yet */ void udf_add_sparable_to_logvol(struct logvol_desc *logvol, uint16_t vol_seq_num, uint16_t phys_part_num, uint16_t packet_len, ) { union udf_pmap *pmap; uint8_t *pmap_pos; pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); pmap = (union udf_pmap *) pmap_pos; pmap->pm1.type = 1; pmap->pm1.len = sizeof(struct part_map_1); pmap->pm1.vol_seq_num = vol_seq_num; pmap->pm1.part_num = phys_part_num; /* increment partition mapping count */ logvol->n_pm = udf_rw32(udf_rw32(logvol->n_pm) + 1); logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + sizeof(struct part_map_1)); logvol->tag.desc_crc_len = udf_rw16(udf_rw16(logvol->tag.desc_crc_len) + sizeof(struct part_map_1)); } #endif /* not called often; free incomming when not needed */ int udf_proc_log_vol(struct udf_pri_vol *primary, struct udf_log_vol **current, struct logvol_desc *incomming) { struct udf_log_vol *logical; struct udf_part_mapping *part_mapping, *data_part_mapping, *search_part_mapping; union udf_pmap *pmap; uint32_t part_cnt, pmap_type, pmap_size; uint32_t data_part_num; uint8_t *pmap_pos; logical = udf_search_logical_volume_in_primary(primary, incomming->logvol_id); if (!logical) { /* create a logical volume */ logical = calloc(1, sizeof(struct udf_log_vol)); if (!logical) { free(incomming); return ENOMEM; } /* link in */ SLIST_INSERT_HEAD(&primary->log_vols, logical, next_logvol); } else { /* ok ... we now need to check if this new descriptor is a newer version */ if (udf_rw32(incomming->seq_num) < udf_rw32(logical->log_vol->seq_num)) { /* its an older one; ignore */ free(incomming); return 0; } } /* update the logical volume descriptor and its mappings; first delete old partition mappings allocated before */ logical->primary = primary; if (current) *current = logical; part_mapping = SLIST_FIRST(&logical->part_mappings); while ((part_mapping = SLIST_FIRST(&logical->part_mappings))) { /* TODO cleanup old cruft ? (XXX while mounted? i don't think so!) */ /* free(part_mapping->sparing_table); free(part_mapping->vat_file_entry); free(part_mapping->vat); free(part_mapping->meta_file); free(part_mapping->meta_mirror_file); free(part_mapping->meta_bitmap_file); */ SLIST_REMOVE_HEAD(&logical->part_mappings, next_mapping); free(part_mapping); } SLIST_INIT(&logical->part_mappings); /* use the new logical volume and preprocess it */ if (logical->log_vol) free(logical->log_vol); logical->log_vol = incomming; logical->lb_size = udf_rw32(incomming->lb_size); logical->sector_size = primary->udf_session->disc->sector_size; /* build up the partion mappings */ logical->num_part_mappings = udf_rw32(incomming->n_pm); /* process partition mappings */ pmap_pos = &logical->log_vol->maps[0]; for (part_cnt = 0; part_cnt < logical->num_part_mappings; part_cnt++) { /* get a new part_mapping structure */ part_mapping = calloc(1, sizeof(struct udf_part_mapping)); assert(part_mapping); /* XXX check with partition mapping destructor etc XXX */ /* * BUG alert: add to *tail* of list for dependencies sake. * Since this is the only place that its needed, I decided * against changing the SLIST to a TAILQ. */ if (SLIST_EMPTY(&logical->part_mappings)) SLIST_INSERT_HEAD(&logical->part_mappings, part_mapping, next_mapping); else { search_part_mapping = SLIST_FIRST(&logical->part_mappings); while (SLIST_NEXT(search_part_mapping, next_mapping)) search_part_mapping = SLIST_NEXT(search_part_mapping, next_mapping); SLIST_INSERT_AFTER(search_part_mapping, part_mapping, next_mapping); } /* process */ pmap = (union udf_pmap *) pmap_pos; pmap_type = pmap->data[0]; pmap_size = pmap->data[1]; part_mapping->udf_virt_part_num = part_cnt; part_mapping->udf_pmap = pmap; switch (pmap_type) { case 1: part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_PHYSICAL; part_mapping->vol_seq_num = udf_rw16(pmap->pm1.vol_seq_num); part_mapping->udf_phys_part_num = udf_rw16(pmap->pm1.part_num); break; case 2: part_mapping->vol_seq_num = udf_rw16(pmap->pm2.vol_seq_num); part_mapping->udf_phys_part_num = udf_rw16(pmap->pm2.part_num); if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Virtual Partition", UDF_REGID_ID_SIZE) == 0) { part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_VIRTUAL; break; } if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Sparable Partition", UDF_REGID_ID_SIZE) == 0) { part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_SPARABLE; break; } if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Metadata Partition", UDF_REGID_ID_SIZE) == 0) { part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_META; break; } printf("HELP ... found unsupported type 2 partition mapping id `%s`; marking broken\n", pmap->pm2.part_id.id); default: part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; } pmap_pos += pmap_size; /* variable length array :( */ } /* flag all partion mappings data and metadata writable */ SLIST_FOREACH(part_mapping, &logical->part_mappings, next_mapping) { part_mapping->data_writable = 1; part_mapping->metadata_writable = 1; } /* update writable flags depending on mapping type */ SLIST_FOREACH(part_mapping, &logical->part_mappings, next_mapping) { switch (part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_ERROR : part_mapping->data_writable = 0; part_mapping->metadata_writable = 0; break; case UDF_PART_MAPPING_PHYSICAL : break; case UDF_PART_MAPPING_VIRTUAL : case UDF_PART_MAPPING_META : /* * These are special in that there is a special metadata partition where no data * is meant to be written on and vice versa */ /* find the associated data partition */ data_part_num = part_mapping->udf_phys_part_num; SLIST_FOREACH(data_part_mapping, &logical->part_mappings, next_mapping) { if (data_part_mapping->udf_phys_part_num == data_part_num) { if (data_part_mapping != part_mapping) { data_part_mapping->metadata_writable = 0; break; } } } part_mapping->data_writable = 0; break; case UDF_PART_MAPPING_SPARABLE : break; } } TAILQ_INIT(&logical->dirty_nodes); UDF_MUTEX_INIT(&logical->dirty_nodes_mutex); return 0; } /* not called often; free incomming when not needed */ int udf_proc_part(struct udf_pri_vol *primary, struct udf_partition **current, struct part_desc *incomming) { struct udf_partition *udf_partition; struct udf_volumeset *udf_volset; uint32_t new_part_num, sector_size; assert(primary); assert(primary->pri_vol); udf_volset = udf_search_volumeset(primary->pri_vol->volset_id); assert(udf_volset); new_part_num = udf_rw16(incomming->part_num); /* check if its a partition type we recognize */ if (strncmp((char *) incomming->contents.id, "+NSR0", 5) != 0) { fprintf(stderr, "Unrecognized partition content type %s encountered; ignoring\n", incomming->contents.id); free(incomming); return 0; } /* look if we allready got it */ SLIST_FOREACH(udf_partition, &udf_volset->parts, next_partition) { if (udf_rw16(udf_partition->partition->part_num) == new_part_num) break; } /* we have space... now check if this is a newer one than the one known */ if (udf_partition) { if (udf_rw32(incomming->seq_num) < udf_rw32(udf_partition->partition->seq_num)) { /* its an older version */ free(incomming); return 0; } } else { /* get us a new udf_partition */ udf_partition = calloc(1, sizeof(struct udf_partition)); if (!udf_partition) { free(incomming); return ENOMEM; } /* link it in */ SLIST_INSERT_HEAD(&udf_volset->parts, udf_partition, next_partition); } assert(udf_partition); /* copy this new partition descriptor in the list */ if (udf_partition->partition) free(udf_partition->partition); udf_partition->partition = incomming; udf_partition->udf_session = primary->udf_session; udf_volset->max_partnum = MAX(udf_volset->max_partnum, new_part_num+1); /* REVIEW why +1? */ /* initialise */ sector_size = primary->udf_session->disc->sector_size; UDF_MUTEX_INIT(&udf_partition->partition_space_mutex); TAILQ_INIT(&udf_partition->unalloc_space_queue); TAILQ_INIT(&udf_partition->freed_space_queue); udf_partition->part_offset = udf_rw32(incomming->start_loc) * sector_size; udf_partition->part_length = udf_rw32(incomming->part_len) * sector_size; /* udf_partition->access_type = udf_rw32(incomming->access_type); */ udf_partition->free_unalloc_space = udf_partition->free_freed_space = 0; if (current) *current = udf_partition; return 0; } /* not called often; free incomming when not needed */ int udf_proc_filesetdesc(struct udf_log_vol *udf_log_vol, struct fileset_desc *incomming) { struct udf_mountpoint *mp; if (udf_rw16(incomming->tag.id) != TAGID_FSD) { printf("IEEE! Encountered a non TAGID_FSD in this fileset descriptor sequence!!!\n"); free(incomming); return EFAULT; } /* lookup fileset descriptor in this logical volume; interestingly fileset_num is KEY! */ SLIST_FOREACH(mp, &udf_log_vol->mountpoints, logvol_next) { if (mp->fileset_desc->fileset_num == incomming->fileset_num) break; } if (!mp) { /* add a new mountpoint! */ mp = calloc(1, sizeof(struct udf_mountpoint)); if (!mp) { free(incomming); return ENOMEM; } mp->fileset_desc = incomming; /* insert into udf_log_vol and into mountables list */ SLIST_INSERT_HEAD(&udf_log_vol->mountpoints, mp, logvol_next); SLIST_INSERT_HEAD(&udf_mountables, mp, all_next); } else { /* should we update mountpoint? */ if (udf_rw32(incomming->fileset_desc_num) <= udf_rw32(mp->fileset_desc->fileset_desc_num)) { /* we allready got a newer one */ free(incomming); return 0; } fprintf(stderr, "UDF DEBUG: would be updating mountpoint... HELP!\n"); /* FIXME delete all inode hash entries */ /* XXX how to do that? inodes OK but associated vnodes? XXX */ #if 0 if (!SLIST_EMPTY(&mp->inodes)) { printf("UDF: asked to delete mountpoint with inodes in hashtable!\n"); printf("Can't cope with that... aborting\n"); exit(1); } #endif /* free old information (allready in lists though!) */ free(mp->fileset_desc); free(mp->mount_name); } mp->udf_log_vol = udf_log_vol; mp->fileset_desc = incomming; mp->mount_name = strdup(udf_get_compound_name(mp)); return 0; } int udf_retrieve_volume_space(struct udf_discinfo *disc, struct udf_session *udf_session, struct extent_ad *extent) { struct udf_pri_vol *udf_pri_vol; struct udf_log_vol *udf_log_vol; union dscrptr *dscr; uint32_t sector, length, dscr_len, num_sectors; uint32_t sector_size; int tag_id; int error; udf_pri_vol = NULL; sector = udf_rw32(extent->loc); length = udf_rw32(extent->len); sector_size = disc->sector_size; error = 0; /* XXX zero length area's possible? XXX */ while (length) { error = udf_read_session_descriptor(udf_session, sector, "volume descriptor", &dscr, &dscr_len); if (error) { if (dscr) free(dscr); break; } tag_id = udf_rw16(dscr->tag.id); num_sectors = (dscr_len + sector_size-1) / sector_size; /* proc volume descriptor starting at sector `volume_sector' */ UDF_VERBOSE_MAX(udf_dump_descriptor(dscr)); switch (tag_id) { case TAGID_PRI_VOL : error = udf_proc_pri_vol(udf_session, &udf_pri_vol, &dscr->pvd); break; case TAGID_PARTITION : error = udf_proc_part(udf_pri_vol, NULL, &dscr->pd); break; case TAGID_LOGVOL : error = udf_proc_log_vol(udf_pri_vol, &udf_log_vol, &dscr->lvd); if (!error) { /* first create empty integrity descriptor then modify it on input (for sanity) */ udf_derive_new_logvol_integrity(udf_log_vol); } break; case TAGID_TERM : free(dscr); return 0; /* terminator */ case TAGID_UNALLOC_SPACE : /* unallocated space descriptor */ /* Specifies space that is not claimed yet in partitions (!) */ UDF_VERBOSE(printf("\t\t`unallocated space descriptor' ignored\n")); break; case TAGID_IMP_VOL : /* implemenation use volume descriptor */ /* Specifies information relevant for the implementator */ UDF_VERBOSE_MAX(printf("\t\t`implementation use volume descriptor' ignored\n")); break; case TAGID_VOL : fprintf(stderr, "UDF : untested volume space extender encountered\n"); break; default : printf("XXX Unhandled volume sequence %d; freeing\n", tag_id); free(dscr); break; } length -= num_sectors * sector_size; sector += num_sectors; if (tag_id == TAGID_VOL) { sector = udf_rw32(dscr->vdp.next_vds_ex.loc); length = udf_rw32(dscr->vdp.next_vds_ex.len); free(dscr); } } return error; } int udf_get_filelength(union dscrptr *dscr, uint64_t *length) { int32_t fe_tag; fe_tag = udf_rw16(dscr->tag.id); if (fe_tag == TAGID_FENTRY) { *length = udf_rw64(dscr->fe.inf_len); return 0; } else if (fe_tag == TAGID_EXTFENTRY) { *length = udf_rw64(dscr->efe.inf_len); return 0; } return ENOENT; } /* can be passed either a file_entry or an extfil_entry trough fentry! */ int udf_check_for_vat(struct udf_log_vol *udf_log_vol, struct udf_part_mapping *part_mapping, uint32_t vat_lb, union dscrptr *dscr) { struct udf_part_mapping *s_part_mapping; struct udf_node *vat_udf_node; struct long_ad udf_icbptr; struct regid *regid; struct uio vat_uio; struct iovec vat_iovec; struct icb_tag *icbtag; struct timestamp *mtime; uint64_t vat_length, vat_entries; uint32_t *vat_pos, vpart_num; uint8_t *vat; int error, found; /* prepare a `uio' structure for reading in complete VAT file */ error = udf_get_filelength(dscr, &vat_length); if (error) return error; if (vat_length == 0) return EFAULT; vat = malloc(vat_length); if (!vat) return ENOMEM; /* move to uio_newuio(struct uio *uio) with fixed length uio_iovcnt? */ bzero(&vat_uio, sizeof(struct uio)); vat_uio.uio_rw = UIO_WRITE; /* WRITE into this space */ vat_uio.uio_iovcnt = 1; vat_uio.uio_iov = &vat_iovec; vat_uio.uio_offset = 0; /* begin at the start */ vat_uio.uio_resid = vat_length; /* fill in IO vector */ vat_uio.uio_iov->iov_base = vat; vat_uio.uio_iov->iov_len = vat_length; /* find our virtual partition number corresponding to our physical partition number; this sucks */ found = 0; vpart_num = 0; SLIST_FOREACH(s_part_mapping, &udf_log_vol->part_mappings, next_mapping) { if (s_part_mapping->udf_phys_part_num == part_mapping->udf_phys_part_num) { if (s_part_mapping->udf_part_mapping_type == UDF_PART_MAPPING_PHYSICAL) { /* found it ! */ found = 1; vpart_num = s_part_mapping->udf_virt_part_num; } } } if (!found) { printf("Can't find accompanied physical volume\n"); return ENOENT; } /* prepare udf_icbptr file node for easy file reading */ udf_icbptr.loc.part_num = vpart_num; udf_icbptr.loc.lb_num = udf_rw32(vat_lb); udf_icbptr.len = udf_log_vol->lb_size; /* not used, but may not be zero */ /* * this udf_node creation and disposing may look a bit inefficient but * its beneficiary for normal file access. its only used once for * reading in the VAT. */ /* create the udf_vat_node; anonymous since it can't be in a mountpoint */ error = udf_readin_anon_udf_node(udf_log_vol, dscr, &udf_icbptr, "VAT", &vat_udf_node); if (!error) { DEBUG(printf("READ FILE PART UIO for VAT\n")); error = udf_read_file_part_uio(vat_udf_node, "VAT contents", 0, &vat_uio); DEBUG(printf("vat_uio rest %d\n", (uint32_t) vat_uio.uio_resid)); } /* XXX allow for SHORT VAT's ? XXX */ if (!error) { if (vat_uio.uio_resid) { fprintf(stderr, "Warning: VAT file can't be read in completely\n"); } part_mapping->vat_udf_node = vat_udf_node; part_mapping->vat = (struct udf_vat *) vat; part_mapping->vat_length = vat_length; /* extract next unique file ID from the VAT file entry's unique ID incremented by one */ udf_log_vol->next_unique_id = vat_udf_node->unique_id; /* ok? */ udf_increment_unique_id(udf_log_vol); /* fentry is confirmed to be either an file_entry or an extfile_entry here */ if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { icbtag = &dscr->fe.icbtag; mtime = &dscr->fe.mtime; } else { icbtag = &dscr->efe.icbtag; mtime = &dscr->efe.mtime; } if (icbtag->file_type == UDF_ICB_FILETYPE_VAT) { /* we are in UDF 2.00+ userland */ part_mapping->vat_translation = ((uint8_t *) part_mapping->vat) + udf_rw16(part_mapping->vat->header_len); part_mapping->vat_entries = (vat_length - udf_rw16(part_mapping->vat->header_len))/4; udf_log_vol->num_files = udf_rw32(part_mapping->vat->num_files); udf_log_vol->num_directories = udf_rw32(part_mapping->vat->num_directories); udf_log_vol->min_udf_readver = udf_rw16(part_mapping->vat->min_udf_readver); udf_log_vol->min_udf_writever = udf_rw16(part_mapping->vat->min_udf_writever); udf_log_vol->max_udf_writever = udf_rw16(part_mapping->vat->max_udf_writever); /* TODO update logvol name */ } else { /* still in the old UDF 1.50 userland; update? its notoriously broken */ /* check the old UDF 1.50 VAT */ DEBUG(printf("CHECK UDF 1.50 VAT\n")); vat_pos = (uint32_t *) vat; vat_entries = (vat_length-36)/4; /* definition */ regid = (struct regid *) (vat_pos + vat_entries); error = (strncmp((char *) regid->id, "*UDF Virtual Alloc Tbl", 22) == 0) ? 0 : ENOENT; if (!error) { part_mapping->vat_entries = vat_entries; part_mapping->vat_translation = vat; part_mapping->vat = NULL; /* num files/dirs? */ } } if (!error) { UDF_VERBOSE(udf_dump_timestamp("\t\t\t\tmarked closed at ", mtime)); } } /* clean up uio structure */ if (error) { if (vat) free(vat); if (vat_udf_node) udf_dispose_udf_node(vat_udf_node); part_mapping->vat_udf_node = NULL; } return error; } int udf_retrieve_supporting_tables(struct udf_log_vol *udf_log_vol) { struct udf_partition *udf_partition; struct udf_part_mapping *part_mapping, *s_part_mapping; struct udf_session *udf_session; struct long_ad udf_icbptr; union dscrptr *possible_vat_fe; union dscrptr *sparing_table_dscr; uint32_t spar_loc; uint64_t first_vat_loc, vat_loc, last_vat_loc; uint32_t sector_size, lb_size; uint32_t part_num, spar_num, data_part_num, vpart_num; int session_num; int error; /* * if there are any virtual or sparable partition in this logical * volume, try to find their supporting tables so we can find the rest */ lb_size = udf_log_vol->lb_size; sector_size = udf_log_vol->sector_size; SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) { part_num = part_mapping->udf_virt_part_num; udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition); UDF_VERBOSE_TABLES(printf("\tFor partition mapping %d->%d\n", part_num, part_mapping->udf_phys_part_num)); switch (part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_ERROR : /* nothing to be done for these */ break; case UDF_PART_MAPPING_PHYSICAL : /* nothing to be done for these; no supporting tables */ break; case UDF_PART_MAPPING_VIRTUAL : /* * we have to find a good VAT at the END of the session. Since VAT's are * only to be used on WORM's and need to written as last, the strategy is * to go for the predicted end of this session and walk UP */ udf_session = udf_log_vol->primary->udf_session; session_num = udf_session->session_num; UDF_VERBOSE_TABLES(printf("\t\tSearching for the VAT :\n")); if (udf_session->session_length == 0) { UDF_VERBOSE( printf("\t\tThis virtual partition is inaccessible since its its size is not known;\n"); printf("\t\tTry to insert the disc in a CD or DVD recordable device to access it.\n"); ); part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; continue; } if (udf_session->disc->next_writable[session_num]) { last_vat_loc = udf_session->disc->next_writable[session_num]; } else { last_vat_loc = udf_session->disc->session_end[session_num]; } last_vat_loc += udf_session->disc->blockingnr; /* give some extra slack since sizes are not allways given up correctly */ first_vat_loc = last_vat_loc - 256; /* 8 blocks of 32 */ first_vat_loc = MAX(first_vat_loc, (uint64_t) udf_session->disc->session_start[session_num]); /* try to find the fileid for the VAT; NOTE that we are reading backwards :( */ vat_loc = last_vat_loc; do { DEBUG( printf("Trying VAT at sector %d in session\n", (int) vat_loc) ); error = udf_read_session_descriptor(udf_session, vat_loc, "VAT file entry", &possible_vat_fe, NULL); if (!error) { error = udf_check_tag_presence(possible_vat_fe, TAGID_FENTRY); if (error) error = udf_check_tag_presence(possible_vat_fe, TAGID_EXTFENTRY); } if (!error) error = udf_check_tag_payload( possible_vat_fe); if (!error) error = udf_check_for_vat(udf_log_vol, part_mapping, vat_loc, possible_vat_fe); if (!error) { break; } else { if (possible_vat_fe) free(possible_vat_fe); vat_loc--; if (vat_loc < first_vat_loc) error = EIO; } } while (error != EIO); if (error) { printf("WARNING: was looking for a VAT but didnt find it; marking logical volume broken\n"); part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN; udf_log_vol->broken = 1; continue; } UDF_VERBOSE_TABLES(printf("\t\t\tfound VAT file-entry at device logical sector %d\n", (uint32_t) vat_loc)); UDF_VERBOSE_TABLES(printf("\t\t\tFound %d byte VAT descriptor+table\n", (uint32_t) part_mapping->vat_length)); UDF_VERBOSE_TABLES(udf_dump_descriptor(possible_vat_fe)); UDF_VERBOSE_MAX(udf_dump_vat_table(part_mapping)); if (part_mapping->vat_translation) { /* the presence of a correct VAT means the logical volume is in a closed state */ udf_log_vol->logvol_state = UDF_INTEGRITY_CLOSED; UDF_VERBOSE(printf("\t\t\t\tmarked closed due to presence of VAT\n")); /* XXX update `free' space by requesting the device's free space? XXX */ udf_log_vol->free_space = udf_partition->part_offset + udf_partition->part_length - vat_loc*sector_size; } if (!udf_session->disc->sequential) { UDF_VERBOSE(printf("\t\t\t\tenabling sequential media emulation\n")); udf_session->disc->sequential = 1; } break; case UDF_PART_MAPPING_SPARABLE : /* we have to find a good sparing table; address are in device logical blocks */ udf_session = udf_log_vol->primary->udf_session; for(spar_num = 0; spar_num < part_mapping->udf_pmap->pms.n_st; spar_num++) { spar_loc = udf_rw32(part_mapping->udf_pmap->pms.st_loc[spar_num]); /* fetch spar_loc's table ; on THIS session. */ error = udf_read_session_descriptor(udf_session, spar_loc, "Sparing table", &sparing_table_dscr, NULL); if (!error) error = udf_check_tag_presence(sparing_table_dscr, TAGID_SPARING_TABLE); if (!error) { UDF_VERBOSE_TABLES(printf("\t\tFound the sparing table\n")); part_mapping->sparing_table = &sparing_table_dscr->spt; break; } else { if (sparing_table_dscr) free(sparing_table_dscr); } } if (!part_mapping->sparing_table) { part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; } UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->sparing_table)); break; case UDF_PART_MAPPING_META : /* * set up common locator parts; the files are located inside the `part_num' partion where * this partition is a added layer on. */ /* find the associated data partition */ data_part_num = udf_rw16(part_mapping->udf_pmap->pmm.part_num); /* find our virtual partition number corresponding to our physical partition number; this sucks */ vpart_num = data_part_num; SLIST_FOREACH(s_part_mapping, &udf_log_vol->part_mappings, next_mapping) { if (s_part_mapping->udf_phys_part_num == data_part_num) { if (s_part_mapping->udf_part_mapping_type != UDF_PART_MAPPING_META) { /* found it ! */ vpart_num = s_part_mapping->udf_virt_part_num; break; } } } udf_icbptr.loc.part_num = vpart_num; udf_icbptr.len = lb_size; /* defined as maximum size */ UDF_VERBOSE_TABLES(printf("Reading metadata partition filedescriptor\n")); udf_icbptr.loc.lb_num = udf_rw32(part_mapping->udf_pmap->pmm.meta_file_lbn); error = udf_readin_anon_udf_node(udf_log_vol, NULL, &udf_icbptr, "Metadata partition file descriptor", &part_mapping->meta_file); if (error == 0) UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->meta_file)); udf_icbptr.loc.lb_num = udf_rw32(part_mapping->udf_pmap->pmm.meta_mirror_file_lbn); if ((error == 0) && (udf_icbptr.loc.lb_num != (uint32_t) -1)) { UDF_VERBOSE_TABLES(printf("Reading metadata partition mirror filedescriptor\n")); error = udf_readin_anon_udf_node(udf_log_vol, NULL, &udf_icbptr, "Metadata partition mirror file descriptor", &part_mapping->meta_mirror_file); if (error == 0) UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->meta_mirror_file)); error = 0; /* XXX ignoring error code for now */ } udf_icbptr.loc.lb_num = udf_rw32(part_mapping->udf_pmap->pmm.meta_bitmap_file_lbn); if ((error == 0) && (udf_icbptr.loc.lb_num != (uint32_t) -1)) { UDF_VERBOSE_TABLES(printf("Reading metadata partition bitmap filedescriptor\n")); error = udf_readin_anon_udf_node(udf_log_vol, NULL, &udf_icbptr, "Metadata partition bitmap file descriptor", &part_mapping->meta_bitmap_file); if (error == 0) UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->meta_bitmap_file)); } /* if something is wrong, then mark it as a broken partition */ if (error) { /* TODO handle read-errors on the meta data and meta data mirror file descriptors. */ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; } break; } } UDF_VERBOSE_TABLES(printf("\n")); if (udf_log_vol->broken) return EIO; return 0; } int udf_retrieve_space_tables(struct udf_log_vol *udf_log_vol) { struct udf_partition *udf_partition; struct udf_part_mapping *part_mapping; struct part_hdr_desc *part_hdr_desc; union dscrptr *dscrptr; uint32_t sector; uint32_t lb_size; uint64_t length; int vpart_num, ppart_num; int error; lb_size = udf_log_vol->lb_size; SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) { vpart_num = part_mapping->udf_virt_part_num; ppart_num = part_mapping->udf_phys_part_num; UDF_VERBOSE_TABLES(printf("\tFor partition mapping %d->%d\n", vpart_num, ppart_num)); if ((part_mapping->udf_part_mapping_type != UDF_PART_MAPPING_PHYSICAL) && (part_mapping->udf_part_mapping_type != UDF_PART_MAPPING_SPARABLE)) { UDF_VERBOSE_TABLES(printf("\t\tDon't know how to load space tables for type %d\n", part_mapping->udf_part_mapping_type)); continue; } /* retrieve and process unallocated- and freed-space information for all used partitions of the logvol */ error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, NULL, &udf_partition); assert(udf_partition); part_hdr_desc = &udf_partition->partition->pd_part_hdr; sector = udf_rw32(part_hdr_desc->unalloc_space_table.lb_num); length = udf_rw32(part_hdr_desc->unalloc_space_table.len); /* needed? */ if (length) { error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, sector, "Unallocated space table", &dscrptr, NULL); UDF_VERBOSE_MAX(printf("\tUnalloced space table\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); /* udf_process_space_table(&udf_partition->unalloc_space, dscrptr); */ free(dscrptr); } sector = udf_rw32(part_hdr_desc->unalloc_space_bitmap.lb_num); length = udf_rw32(part_hdr_desc->unalloc_space_bitmap.len); if (length && (udf_partition->unalloc_space_bitmap == 0)) { error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, sector, "Unallocated space bitmap", &dscrptr, NULL); if (!error) { UDF_VERBOSE_MAX(printf("\tUnalloced space bitmap\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); udf_read_in_space_bitmap(&udf_partition->unalloc_space_queue, &dscrptr->sbd, lb_size, &udf_partition->free_unalloc_space); UDF_VERBOSE_TABLES(printf("\t\tPhysical partition's unallocated space : %"PRIu64"\n", udf_partition->free_unalloc_space)); udf_partition->unalloc_space_bitmap = &dscrptr->sbd; } else { printf("While reading in unallocated space bitmap : %s\n", strerror(error)); udf_partition->unalloc_space_bitmap = NULL; /* TODO mark read-only logvol */ } } sector = udf_rw32(part_hdr_desc->freed_space_table.lb_num); length = udf_rw32(part_hdr_desc->freed_space_table.len); if (length) { error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, sector, "Freed space table", &dscrptr, NULL); UDF_VERBOSE_MAX(printf("\tFreed space table\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); /* udf_process_space_table(&udf_partition->freed_space, dscrptr); */ free(dscrptr); } sector = udf_rw32(part_hdr_desc->freed_space_bitmap.lb_num); length = udf_rw32(part_hdr_desc->freed_space_bitmap.len); if (length && (udf_partition->freed_space_bitmap == NULL)) { error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, sector, "Freed space bitmap", &dscrptr, NULL); if (!error) { UDF_VERBOSE_MAX(printf("\tFreed space bitmap\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); udf_read_in_space_bitmap(&udf_partition->freed_space_queue, &dscrptr->sbd, lb_size, &udf_partition->free_freed_space); UDF_VERBOSE_TABLES(printf("\t\tPhysical partition's freed space : %"PRIu64"\n", udf_partition->free_unalloc_space)); udf_partition->freed_space_bitmap = &dscrptr->sbd; } else { printf("While reading in freed space bitmap : %s\n", strerror(error)); udf_partition->freed_space_bitmap = NULL; /* TODO mark read-only logvol */ } } } UDF_VERBOSE_TABLES(printf("\n")); return 0; } /* * Fileset descriptors are on a logical volume's partitions; since virtual * partitions are then also possible its OK to use the VAT for redefining the * fileset descriptors. */ int udf_retrieve_fileset_descriptor(struct udf_log_vol *udf_log_vol) { struct udf_mountpoint *mountable; struct long_ad *fsd_loc; struct fileset_desc *new_fsd; struct udf_node *vnode; uint32_t part_num, lb_num, length; int32_t error; error = 0; /* flag OK */ fsd_loc = &udf_log_vol->log_vol->_lvd_use.fsd_loc; part_num = udf_rw16(fsd_loc->loc.part_num); lb_num = udf_rw32(fsd_loc->loc.lb_num); length = udf_rw32(fsd_loc->len); while (length && !error) { UDF_VERBOSE_TABLES( printf("\tFileset descriptor extent at sector %d within partion %d for %d bytes\n", lb_num, part_num, length) ); /* only go for ONE fsb at a time */ error = udf_read_logvol_descriptor(udf_log_vol, part_num, lb_num, "Fileset descriptor", (union dscrptr **) &new_fsd, NULL); if (!error) error = udf_check_tag((union dscrptr *) new_fsd); /* TODO need a clearer handling unrecorded blocks here */ if (error || (!new_fsd) || (new_fsd && (udf_rw16(new_fsd->tag.id) == TAGID_TERM))) { /* end of sequence */ UDF_VERBOSE_TABLES( printf("\t\t(Terminator) "); if (!new_fsd || error) printf("; unrecorded"); else printf("; explicit"); printf("\n"); ); /* clear error to indicate end of sequence and free possible read in descriptor */ error = 0; if (new_fsd) free(new_fsd); break; } UDF_VERBOSE_MAX(udf_dump_descriptor((union dscrptr *) new_fsd)); udf_proc_filesetdesc(udf_log_vol, new_fsd); if (udf_rw32(new_fsd->next_ex.len) == 0) { /* next entry */ lb_num += 1; length -= udf_log_vol->lb_size; } else { /* follow the next extent */ fsd_loc = &new_fsd->next_ex; part_num = udf_rw16(fsd_loc->loc.part_num); lb_num = udf_rw32(fsd_loc->loc.lb_num); length = udf_rw32(fsd_loc->len); } } UDF_VERBOSE_TABLES(printf("\n")); if (error) return error; /* if no error occured, create rootdir udf_nodes */ SLIST_FOREACH(mountable, &udf_log_vol->mountpoints, logvol_next) { /* errors are OK */ udf_readin_anon_udf_node(udf_log_vol, NULL, &mountable->fileset_desc->rootdir_icb, "Rootdir", &mountable->rootdir_node); udf_readin_anon_udf_node(udf_log_vol, NULL, &mountable->fileset_desc->streamdir_icb, "Streamdir", &mountable->streamdir_node); /* keep names the same ? (duplicate code ahead ... ) */ if (mountable->rootdir_node) { vnode = mountable->rootdir_node; vnode->mountpoint = mountable; vnode->stat.st_uid = vnode->stat.st_gid = UINT_MAX; vnode->stat.st_mode = 0777 | S_IFDIR; udf_insert_node_in_hash(vnode); } if (mountable->streamdir_node) { vnode = mountable->streamdir_node; vnode->mountpoint = mountable; vnode->stat.st_uid = vnode->stat.st_gid = UINT_MAX; vnode->stat.st_mode = 0777 | S_IFDIR; udf_insert_node_in_hash(vnode); } } return 0; } int udf_check_writable_filesets(struct udf_log_vol *udf_log_vol, int mnt_flags) { struct udf_mountpoint *mp; struct udf_part_mapping *udf_part_mapping; int writable; writable = 1; if (mnt_flags & UDF_MNT_RDONLY) writable = 0; if (mnt_flags & UDF_MNT_FORCE) writable = 1; if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) { if (!(mnt_flags & UDF_MNT_FORCE)) { /* we explicitly DISABLE writing */ /* XXX do we even reach here? XXX */ if (udf_verbose) { printf("\t\t\t\tmounting READ-ONLY due to open integrity\n"); } else { printf("WARNING: mounting logical volume READ-ONLY due to open integrity\n"); } writable = 0; } else { printf("WARNING: ignoring open integrity\n"); } } SLIST_FOREACH(udf_part_mapping, &udf_log_vol->part_mappings, next_mapping) { if (udf_part_mapping->udf_part_mapping_type == UDF_PART_MAPPING_META) { writable = 0; fprintf(stderr, "\t\t\t\t*** marked read-only due to read-only support for Metadata partition ***\n"); } } /* follow all mountpoints of this logical volume and set if they are writable */ SLIST_FOREACH(mp, &udf_log_vol->mountpoints, logvol_next) { mp->writable = writable; } udf_log_vol->writable = writable; /* WAS: */ /* udf_log_vol->primary->udf_session->writable = mark; */ return 0; } /* * udf_eliminate_predescessor_volumesets() * * We are faced with a curious problem : we are to examine the partitions and * determine which are successors of eachother. This is propably most * relevant only on WORM media though. We could consider the following rules : * 1) `glue' according to strict UDF level 1 rules ? * 2) use heuristics info i.e. NERO, DirectCD and mkisofs quirks ? * 3) use overlapping partitions to detect relationships ? * * Using 1 would imply no multi-volume discs and thus glue everything but that * could easily be wrong. Selecting by volumeset names is not possible for * Nero f.e. just creates random volumeset names every session and uses no * volume version information and thus also violates the UDF rules. * * Using 2 would be tricky; we know a few programs but what if more are * developped? We then would be at loss. * * Using 3 would imply some calculation but is fail-safe in both supporting * multiple volumes on one disc (they seperate) and in supporting * multi-session WORM media for these will refer to eachother. Offcource NERO * could faul this by just extending the zero partion to the whole disc in its * ignorance and thus create false overlapping over other independent * sessions. This is to be investigated. I don't know how NERO will react on * this situation. * * Propably method 3 would be good to try : * * Follow the disc and check for all sessions in order to mark the ones with * overlapping partitions as `inactive' and keep the latest one active. * Sessions with the `local' quirk are seperate allmost by default; should * be change the offsets? would not be too difficult but possible. */ void udf_eliminate_predescessor_volumesets(struct udf_discinfo *disc) { struct udf_volumeset *anc_vol_set; struct udf_volumeset *sib_vol_set; struct pri_vol_desc *anc_pri_vol; struct pri_vol_desc *sib_pri_vol; struct udf_partition *anc_part; struct udf_partition *sib_part; int anc_partnum; int sib_partnum; uint32_t anc_start, anc_end; uint32_t sib_start, sib_end; uint32_t overlap_start, overlap_end; uint32_t anc_session; uint32_t sib_session; SLIST_FOREACH(anc_vol_set, &udf_volumeset_list, next_volumeset) { anc_pri_vol = STAILQ_FIRST(&anc_vol_set->primaries)->pri_vol; sib_vol_set = SLIST_NEXT(anc_vol_set, next_volumeset); while (sib_vol_set) { sib_pri_vol = STAILQ_FIRST(&sib_vol_set->primaries)->pri_vol; DEBUG( printf("checking volset %s with volset %s\n", anc_pri_vol->volset_id+1, sib_pri_vol->volset_id+1) ); /* compare these two volume sets but only process partitions on _this_ disc */ SLIST_FOREACH(anc_part, &anc_vol_set->parts, next_partition) { if (anc_part->udf_session->disc != disc) continue; anc_session = anc_part->udf_session->session_num; anc_start = 0; #if 0 if (disc->session_quirks[anc_session] & CD_SESS_QUIRK_SESSION_LOCAL) anc_start += disc->session_start[anc_session]; #endif anc_start += udf_rw32(anc_part->partition->start_loc); anc_end = anc_start + udf_rw32(anc_part->partition->part_len); SLIST_FOREACH(sib_part, &sib_vol_set->parts, next_partition) { if (sib_part->udf_session->disc != disc) continue; sib_session = sib_part->udf_session->session_num; #if 0 /* can `session local' volumes even be considered part/successor ? */ if (disc->session_quirks[sib_session] & CD_SESS_QUIRK_SESSION_LOCAL) continue; #endif sib_start = 0; #if 0 if (disc->session_quirks[sib_session] & CD_SESS_QUIRK_SESSION_LOCAL) sib_start += disc->session_start[sib_session]; #endif sib_start += udf_rw32(sib_part->partition->start_loc); sib_end = sib_start + udf_rw32(sib_part->partition->part_len); DEBUG( anc_partnum = udf_rw16(anc_part->partition->part_num); sib_partnum = udf_rw16(sib_part->partition->part_num); printf("\t\tchecking partition %d with partition %d ([%d-%d] x [%d-%d])\n", anc_partnum, sib_partnum, anc_start, anc_end, sib_start, sib_end) ); overlap_start = MAX(sib_start, anc_start); overlap_end = MIN(sib_end, sib_end); if (overlap_start < overlap_end) { DEBUG( printf("\t\t\tOVERLAP!\n") ); if (sib_session < anc_session) { /* the sibbling is older */ UDF_VERBOSE_TABLES( printf("\tVolume set "); udf_dump_id(NULL, 128, anc_pri_vol->vol_id, &anc_pri_vol->desc_charset); printf(" is a newer version of volume set "); udf_dump_id(NULL, 128, sib_pri_vol->vol_id, &sib_pri_vol->desc_charset); printf("\n"); ); sib_vol_set->obsolete = 1; break; } } /* overlap */ if (sib_vol_set->obsolete) break; } /* sibling partition */ if (sib_vol_set->obsolete) break; } /* ancestor partition */ sib_vol_set = SLIST_NEXT(sib_vol_set, next_volumeset); } /* sibling volume set */ } /* ancestor volume set */ } int udf_add_session_to_discinfo(struct udf_discinfo *disc, int session, struct anchor_vdp *avdp, int error) { struct udf_session *udf_session; udf_session = calloc(1, sizeof(struct udf_session)); assert(udf_session); if (!error) { memcpy(&udf_session->anchor, avdp, sizeof(struct anchor_vdp)); } udf_session->disc = disc; udf_session->session_num = session; udf_session->session_offset = 0; udf_session->session_length = disc->session_end[session] - disc->session_start[session]; disc->session_quirks[session] = 0; /* writable session administration */ udf_session->writable = 0; /* default off */ error = udf_init_session_caches(udf_session); if (!error) { /* detect quirks */ /* XXX session local disabled due to wrong heuristic XXX */ #if 0 if (disc->session_start[session] > 0) { if ((udf_session->anchor.main_vds_ex.loc < disc->session_start[session])) { disc->session_quirks[session] |= CD_SESS_QUIRK_SESSION_LOCAL; udf_session->session_offset = disc->session_start[session]; } } #endif } /* add to tail of session list */ STAILQ_INSERT_TAIL(&disc->sessions, udf_session, next_session); disc->num_udf_sessions++; /* record status of this volume */ disc->session_is_UDF[session] = error ? 0 : 1; return error; } int udf_get_anchors(struct udf_discinfo *disc) { uint8_t *sector; union dscrptr *dscr; uint32_t session_start, session_end; int session, error; /* Get all anchors */ STAILQ_INIT(&disc->sessions); sector = NULL; for (session = 0; session < disc->num_sessions; session++) { /* check for anchors ; no volume recognition data ? */ session_start = disc->session_start[session]; session_end = disc->session_end [session]-1; sector = calloc(1, disc->sector_size); if (!sector) return ENOMEM; dscr = (union dscrptr *) sector; error = udf_read_physical_sectors(disc, session_end, 1, "Anchor", sector); if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR); if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session end (%d)\n", session_end)); if (error) { error = udf_read_physical_sectors(disc, session_end - 256, 1, "Anchor", sector); if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR); if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session end - 256 (%d)\n", session_end - 256)); if (error) { error = udf_read_physical_sectors(disc, session_start + 256, 1, "Anchor", sector); if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR); if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session sector 256 (%d)\n", session_start + 256)); if (error) { /* unclosed CD recordable case due to track reservation for iso9660 filesystems */ error = udf_read_physical_sectors(disc, session_start + 512, 1, "Anchor", sector); if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR); if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session sector 512 (%d)\n", session_start + 512)); } } } if (!error) { udf_add_session_to_discinfo(disc, session, (struct anchor_vdp *) sector, error); } else { free(sector); } } return 0; } int udf_get_volumeset_space(struct udf_discinfo *disc) { struct udf_session *udf_session; int one_good_found; int error; /* Rip all volume spaces */ one_good_found = 0; UDF_VERBOSE(printf("\tretrieving volume space\n")); STAILQ_FOREACH(udf_session, &disc->sessions, next_session) { UDF_VERBOSE_MAX(printf("Session %d volumes : \n", udf_session->session_num)); error = udf_retrieve_volume_space(disc, udf_session, &udf_session->anchor.main_vds_ex); if (error) { printf("\nError retrieving session %d's volume space; prosessing reserve\n", udf_session->session_num); error = udf_retrieve_volume_space(disc, udf_session, &udf_session->anchor.reserve_vds_ex); } if (!error) one_good_found = 1; } return one_good_found ? 0 : ENOENT; } int udf_get_logical_volumes_supporting_tables(struct udf_discinfo *disc, int mnt_flags) { struct udf_volumeset *udf_volumeset; struct udf_pri_vol *udf_pri_vol; struct udf_log_vol *udf_log_vol; int logvolint_error; int one_good_found; int error; one_good_found = 0; SLIST_FOREACH(udf_volumeset, &udf_volumeset_list, next_volumeset) { if (!udf_volumeset->obsolete) { STAILQ_FOREACH(udf_pri_vol, &udf_volumeset->primaries, next_primary) { if (udf_pri_vol->udf_session->disc == disc) { SLIST_FOREACH(udf_log_vol, &udf_pri_vol->log_vols, next_logvol) { /* retrieving logical volume integrity sequence */ UDF_VERBOSE(udf_dump_volume_name("\t\tLogical volume ", udf_log_vol)); UDF_VERBOSE(printf("\t\t\tintegrity\n")); logvolint_error = udf_proc_logvol_integrity_sequence(udf_log_vol); /* load in supporting tables */ UDF_VERBOSE(printf("\t\t\tsupporting tables\n")); error = udf_retrieve_supporting_tables(udf_log_vol); /* if the state is still marked `open', its dirty and we mount read-only for safety */ if (logvolint_error) { printf("\t\t\t*** marked read-only due to logvol integrity error ***\n"); mnt_flags |= UDF_MNT_RDONLY; } if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) { printf("\t\t\t*** marked read-only due to open logical volume ***\n"); mnt_flags |= UDF_MNT_RDONLY; } /* get fileset descriptors */ UDF_VERBOSE(printf("\t\t\tfileset(s)\n")); if (!error) error = udf_retrieve_fileset_descriptor(udf_log_vol); /* check if the logical volume is writable */ UDF_VERBOSE(printf("\t\t\tchecking writable filesets\n")); if (!error) error = udf_check_writable_filesets(udf_log_vol, mnt_flags); /* load in free/used space tables for writable volsets */ UDF_VERBOSE(printf("\t\t\tused/freed space tables\n")); if (!error) error = udf_retrieve_space_tables(udf_log_vol); if (error) { udf_log_vol->broken = 1; } else { one_good_found = 1; } } /* logical */ } /* disc */ } /* primary */ } /* if */ } /* volumeset */ return one_good_found? 0 : ENOENT; } /****************************************************************************************** * * Disc sync * ******************************************************************************************/ void udf_sync_tables_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) { /* struct udf_node *udf_node = (struct udf_node *) wrcallback->structure; */ wrcallback = wrcallback; /* not used now */ sectordata = sectordata; if (reason == UDF_WRCALLBACK_REASON_PENDING) { /* what to do? */ return; } if (reason == UDF_WRCALLBACK_REASON_ANULATE) { /* what to do? */ return; } assert(reason == UDF_WRCALLBACK_REASON_WRITTEN); if (error) { printf("UDF error: sync tables write errors in syncnode not fixed!\n"); return; } } /* TODO space tables are not coupled on a logical volume but on a partition/disc, so call them on that instead of logvol */ int udf_sync_space_tables(struct udf_log_vol *udf_log_vol) { struct udf_partition *udf_partition; struct udf_part_mapping *part_mapping; struct part_hdr_desc *part_hdr_desc; struct udf_wrcallback wr_callback; union dscrptr *dscrptr; uint64_t length; uint32_t sector; uint32_t lb_size, part_len; uint16_t dscr_ver; int part_num; int error; lb_size = udf_log_vol->lb_size; wr_callback.function = udf_sync_tables_callback; SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) { part_num = part_mapping->udf_virt_part_num; UDF_VERBOSE_TABLES(printf("\tFor partition mapping %d->%d\n", part_num, part_mapping->udf_phys_part_num)); /* retrieve and process unallocated- and freed-space information for all used partitions of the logvol */ error = udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition); assert(udf_partition); part_hdr_desc = &udf_partition->partition->pd_part_hdr; // part_start = udf_rw32(udf_partition->partition->start_loc); part_len = udf_rw32(udf_partition->partition->part_len); dscr_ver = udf_rw16(udf_partition->partition->tag.descriptor_ver); sector = udf_rw32(part_hdr_desc->unalloc_space_table.lb_num); length = udf_rw32(part_hdr_desc->unalloc_space_table.len); /* needed? */ if (length) { printf("UDF: Can't write space tables yet\n"); #if 0 error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Unallocated space table", &dscrptr, NULL); UDF_VERBOSE_MAX(printf("\tUnalloced space table\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); //udf_process_space_table(&udf_partition->unalloc_space, dscrptr); free(dscrptr); #endif } sector = udf_rw32(part_hdr_desc->unalloc_space_bitmap.lb_num); length = udf_rw32(part_hdr_desc->unalloc_space_bitmap.len); /* printf("unalloc dscr at partition sector %d\n", sector); */ if (length) { /* read it in and modify */ dscrptr = (union dscrptr *) udf_partition->unalloc_space_bitmap; if (!dscrptr) { printf("Warning: creating empty unallocated space bitmap for partition's is gone\n"); error = udf_create_empty_space_bitmap(lb_size, dscr_ver, /* num_lbs */ part_len, (struct space_bitmap_desc **) &dscrptr); assert(!error); assert(udf_calc_tag_malloc_size(dscrptr, lb_size) <= length); udf_partition->unalloc_space_bitmap = &dscrptr->sbd; } udf_sync_space_bitmap(&udf_partition->unalloc_space_queue, &dscrptr->sbd, lb_size); UDF_VERBOSE_MAX(printf("\tWriteout unallocated space bitmap\n")); UDF_VERBOSE_MAX(udf_validate_tag_and_crc_sums((union dscrptr *) dscrptr); udf_dump_descriptor(dscrptr)); udf_write_partition_descriptor(udf_partition, sector, "Unallocated space bitmap", dscrptr, &wr_callback); /* SESSION descriptor!! */ } sector = udf_rw32(part_hdr_desc->freed_space_table.lb_num); length = udf_rw32(part_hdr_desc->freed_space_table.len); if (length) { printf("UDF: Can't write space tables yet\n"); #if 0 error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Freed space table", &dscrptr, NULL); UDF_VERBOSE_MAX(printf("\tFreed space table\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); //udf_process_space_table(&udf_partition->freed_space, dscrptr); free(dscrptr); #endif } sector = udf_rw32(part_hdr_desc->freed_space_bitmap.lb_num); length = udf_rw32(part_hdr_desc->freed_space_bitmap.len); /* printf("freed dscr at partition sector %d\n", sector); */ if (length) { /* read it in and modify */ dscrptr = (union dscrptr *) udf_partition->freed_space_bitmap; if (!dscrptr) { printf("Warning: creating empty freed space bitmap for partition's is gone\n"); error = udf_create_empty_space_bitmap(lb_size, dscr_ver, part_len, (struct space_bitmap_desc **) &dscrptr); assert(!error); assert(udf_calc_tag_malloc_size(dscrptr, lb_size) <= length); udf_partition->freed_space_bitmap = &dscrptr->sbd; } udf_sync_space_bitmap(&udf_partition->freed_space_queue, &dscrptr->sbd, lb_size); UDF_VERBOSE_MAX(printf("\tWriteout freed space bitmap\n")); UDF_VERBOSE_MAX(udf_validate_tag_and_crc_sums((union dscrptr *) dscrptr); udf_dump_descriptor(dscrptr)); udf_write_partition_descriptor(udf_partition, sector, "Freed space bitmap", dscrptr, &wr_callback); /* SESSION descriptor!! */ } } UDF_VERBOSE_TABLES(printf("\n")); return 0; } int udf_writeout_LVID(struct udf_log_vol *udf_log_vol, int type) { union dscrptr *dscr; struct logvol_int_desc *intdesc; struct udf_logvol_info *impl; struct udf_session *session; struct udf_partition *udf_partition; struct udf_part_mapping *part_mapping; struct desc_tag *terminator; struct udf_wrcallback wr_callback; uint32_t sector, lvid_sector, term_sector; uint32_t part_num, *free_space_pos, *size_pos, lb_size; uint32_t len, length, lvid_len, num_sectors; int error, dscr_ver, tagid; /* create a new `fresh' logvol integrity */ session = udf_log_vol->primary->udf_session; lb_size = udf_log_vol->lb_size; num_sectors = lb_size / session->disc->sector_size; sector = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.loc); length = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.len); if (!length) return ENOENT; intdesc = calloc(1, udf_log_vol->lb_size); if (!intdesc) return ENOMEM; /* search insertion place */ lvid_sector = 0; term_sector = 0; while (length) { error = udf_read_session_descriptor(udf_log_vol->primary->udf_session, sector, "Logical volume integrity descriptor (LVID)", &dscr, &lvid_len); /* getting a terminator tag or zero is an OK condition */ if (error) { tagid = 0; } else { tagid = udf_rw16(dscr->tag.id); } if ((tagid == TAGID_TERM) || (tagid == 0)) { lvid_sector = sector; if (length > lb_size) { /* space for a terminator */ term_sector = sector + num_sectors; } break; /* while */ } length -= lb_size; sector += num_sectors; if (udf_rw32(dscr->lvid.next_extent.len)) { sector = udf_rw32(dscr->lvid.next_extent.loc); length = udf_rw32(dscr->lvid.next_extent.len); } /* free consumed descriptor */ free(dscr); dscr = NULL; } if (dscr) free(dscr); if ((!lvid_sector) || (length == 0)) { sector = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.loc); length = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.len); lvid_sector = sector; if (length > lb_size) { /* space for a terminator */ term_sector = lvid_sector + num_sectors; } } assert(lvid_sector); /* build up integrity descriptor and write it out */ dscr_ver = udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver); udf_init_desc_tag(&intdesc->tag, TAGID_LOGVOL_INTEGRITY, dscr_ver, udf_log_vol->integrity_serial); udf_set_timestamp_now(&intdesc->time); intdesc->integrity_type = udf_rw32(type); intdesc->lvint_next_unique_id = udf_rw64(udf_log_vol->next_unique_id); /* calculate and fill in free space */ intdesc->num_part = udf_rw32(udf_log_vol->num_part_mappings); free_space_pos = &intdesc->tables[0]; size_pos = &intdesc->tables[udf_log_vol->num_part_mappings]; SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) { part_num = part_mapping->udf_virt_part_num; udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition); assert(udf_partition); *size_pos++ = udf_partition->partition->part_len; *free_space_pos++ = udf_rw32(udf_partition->free_unalloc_space / udf_log_vol->lb_size); } /* fill in UDF implementation use parameters */ impl = (struct udf_logvol_info *) (&intdesc->tables[2*udf_log_vol->num_part_mappings]); udf_set_imp_id(&impl->impl_id); impl->num_files = udf_rw32(udf_log_vol->num_files); impl->num_directories = udf_rw32(udf_log_vol->num_directories); impl->min_udf_readver = udf_rw16(udf_log_vol->min_udf_readver); impl->min_udf_writever = udf_rw16(udf_log_vol->min_udf_writever); impl->max_udf_writever = udf_rw16(udf_log_vol->max_udf_writever); intdesc->l_iu = udf_rw32(sizeof(struct udf_logvol_info)); /* ECMA 3/10.10.7, UDF 2.2.6.4. */ len = sizeof(struct logvol_int_desc) - sizeof(uint32_t); /* length of logvol_int_desc without the extra table entry */ len += sizeof(uint32_t) * 2 * udf_log_vol->num_part_mappings; /* size and free space */ len += sizeof(struct udf_logvol_info); /* extra implementation use area */ len -= UDF_DESC_TAG_LENGTH; /* without header */ intdesc->tag.desc_crc_len = udf_rw16(len); udf_write_session_descriptor(session, lvid_sector, "Logvol integrity descriptor (LVID)", (union dscrptr *) intdesc, &wr_callback); if (session->disc->rewritable && term_sector) { /* only when there is space and its a rewritable media add a terminor */ error = udf_create_empty_terminator_descriptor(lb_size, dscr_ver, &terminator); if (!error) { udf_write_session_descriptor(session, term_sector, "Logvol integrity sequence descriptor sequence terminator", (union dscrptr *) terminator, &wr_callback); free(terminator); } } free(intdesc); return 0; } /* mark the logical volume `open'; for non-rewritables (CD-R/DVD+R/DVD-R) this is allmost a no-op */ int udf_open_logvol(struct udf_log_vol *udf_log_vol) { int error; if (!udf_log_vol->writable) { udf_dump_volume_name("\nLogical volume marked read only: ", udf_log_vol); return EROFS; } /* will return many times for each write */ if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) return 0; /* * Opening and closing logical volumes is derived from the state of * the primaries disc. */ udf_dump_volume_name("Opening logical volume", udf_log_vol); if (!udf_log_vol->primary->udf_session->disc->sequential) { error = udf_writeout_LVID(udf_log_vol, UDF_INTEGRITY_OPEN); assert(!error); /* sync caches to make sure all is written out */ udf_sync_caches(udf_log_vol); /* FIXME (callback) XXX ought to wait until we get the ALL-OK signal from the writeout-LVID action XXX */ } else { /* sequential recordable; any write just opens it; the descriptor is allready marked open */ } /* mark it open */ udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN; return 0; } /* mark the logical volume in a `closed' state; close the integrity when possible for recordables writeout VAT */ int udf_close_logvol(struct udf_log_vol *udf_log_vol) { int error; if (udf_log_vol->logvol_state == UDF_INTEGRITY_CLOSED) { DEBUG(printf("close logvol: integrity allready closed\n")); return 0; } /* * Opening and closing logical volumes is derived from the state of * the primaries disc. */ udf_dump_volume_name("Closing logical volume", udf_log_vol); if (!udf_log_vol->primary->udf_session->disc->sequential) { error = udf_writeout_LVID(udf_log_vol, UDF_INTEGRITY_CLOSED); assert(!error); } else { /* XXX TODO XXX */ fprintf(stderr, "write out virtual sectors, compile VAT and write out VAT : not implemented\n"); return EIO; } /* sync caches to make sure all is written out */ udf_sync_caches(udf_log_vol); /* FIXME (callback) XXX ought to wait until we get the ALL-OK signal from the writeout-LVID action XXX */ /* mark it closed again */ udf_log_vol->logvol_state = UDF_INTEGRITY_CLOSED; return 0; } int udf_sync_logvol(struct udf_log_vol *udf_log_vol) { struct udf_node *udf_node; uint32_t num_dirty, count, prnt; int error; if (!udf_log_vol->writable) return 0; if (udf_log_vol->logvol_state == UDF_INTEGRITY_CLOSED) { DEBUG(printf("close logvol: its closed so no sync nessisary\n")); return 0; } UDF_VERBOSE(udf_dump_volume_name("\tsyncing ", udf_log_vol)); /* sync all nodes */ /* XXX syncing logvol sequential due to insertion sort in add node XXX */ num_dirty = 0; TAILQ_FOREACH(udf_node, &udf_log_vol->dirty_nodes, next_dirty) { num_dirty++; } /* * Purge all data out first, this will speed things up later (not * strickly nessissary since syncing a node will wait for all the data * to be written out first anyway */ count = num_dirty; prnt = 0; UDF_VERBOSE(printf("\t\tsyncing data\n")); TAILQ_FOREACH(udf_node, &udf_log_vol->dirty_nodes, next_dirty) { UDF_VERBOSE(printf("\r%8d", count); fflush(stdout)); udf_sync_udf_node(udf_node, "Sync Logvol"); count--; prnt = 1; } if (prnt) UDF_VERBOSE(printf("\r \r")); /* * Purge all nodes out... they ought to have no dirty buffers anymore * but they will write them out if deemed nessisary */ count = num_dirty; prnt = 0; UDF_VERBOSE(printf("\t\tsyncing nodes\n")); TAILQ_FOREACH(udf_node, &udf_log_vol->dirty_nodes, next_dirty) { UDF_VERBOSE(printf("\r%8d", count); fflush(stdout)); DEBUG(printf("N"); fflush(stdout)); udf_writeout_udf_node(udf_node, "Sync Logvol"); count--; prnt = 1; } if (prnt) UDF_VERBOSE(printf("\r \r")); /* shouldn't be nessisary */ udf_bufcache->flushall = 1; udf_purgethread_kick("Sync Logvol"); usleep(1); if (udf_bufcache->lru_len_dirty_metadata + udf_bufcache->lru_len_dirty_data) { printf("Warning: after syncing logvol dirty counts != 0 (%d, %d); please contact author.\n", udf_bufcache->lru_len_dirty_metadata, udf_bufcache->lru_len_dirty_data); } /* sync free and used space tables for writable volsets */ UDF_VERBOSE(printf("\t\tused/freed space tables\n")); error = udf_sync_space_tables(udf_log_vol); /* close logical volume */ udf_close_logvol(udf_log_vol); return error; } /* convenience routine */ int udf_sync_disc(struct udf_discinfo *disc) { struct udf_volumeset *udf_volumeset; struct udf_pri_vol *udf_pri_vol; struct udf_log_vol *udf_log_vol; SLIST_FOREACH(udf_volumeset, &udf_volumeset_list, next_volumeset) { if (!udf_volumeset->obsolete) { STAILQ_FOREACH(udf_pri_vol, &udf_volumeset->primaries, next_primary) { if (udf_pri_vol->udf_session->disc == disc) { SLIST_FOREACH(udf_log_vol, &udf_pri_vol->log_vols, next_logvol) { udf_sync_logvol(udf_log_vol); } /* logical */ } /* disc */ } /* primary */ } /* if */ } /* volumeset */ return 0; } /****************************************************************************************** * * UDF descriptor buildup and update functions * ******************************************************************************************/ static void udf_init_desc_tag(struct desc_tag *tag, uint16_t id, uint16_t dscr_ver, uint16_t serial_num) { bzero(tag, sizeof(struct desc_tag)); tag->id = udf_rw16(id); tag->descriptor_ver = udf_rw16(dscr_ver); tag->serial_num = udf_rw16(serial_num); /* the rest gets filled in when we write */ } static void udf_osta_charset(struct charspec *charspec) { bzero(charspec, sizeof(struct charspec)); charspec->type = 0; strcpy((char *) charspec->inf, "OSTA Compressed Unicode"); } static void udf_encode_osta_id(char *osta_id, uint16_t len, char *text) { uint16_t u16_name[1024]; uint8_t *pos; uint16_t *pos16; bzero(osta_id, len); if (!text) return; bzero(u16_name, sizeof(uint16_t) * 1023); /* convert ascii to 16 bits unicode */ pos = (uint8_t *) text; pos16 = u16_name; while (*pos) { *pos16 = *pos; pos++; pos16++; } *pos16 = 0; udf_CompressUnicode(len, 8, (unicode_t *) u16_name, (byte *) osta_id); /* Ecma 167/7.2.13 states that the length is recorded in the last byte */ osta_id[len-1] = strlen(text)+1; } static void udf_set_app_id(struct regid *regid) { bzero(regid, sizeof(struct regid)); regid->flags = 0; /* not dirty and not protected */ strcpy((char *) regid->id, APP_NAME); regid->id_suffix[0] = APP_VERSION_MAIN; regid->id_suffix[1] = APP_VERSION_SUB; } static void udf_set_imp_id(struct regid *regid) { bzero(regid, sizeof(struct regid)); regid->flags = 0; /* not dirty and not protected */ strcpy((char *) regid->id, IMPL_NAME); regid->id_suffix[0] = 4; /* unix */ regid->id_suffix[1] = 0; /* generic */ #if defined(__ANONYMOUSUDF__) #elif defined(__NetBSD__) regid->id_suffix[1] = 8; /* NetBSD */ #elif defined(__FreeBSD__) regid->id_suffix[1] = 7; /* FreeBSD */ #elif defined(LINUX) regid->id_suffix[1] = 5; /* Linux */ #endif } static void udf_set_entity_id(struct regid *regid, char *name, uint16_t UDF_version) { uint16_t *ver; bzero(regid, sizeof(struct regid)); regid->flags = 0; /* not dirty and not protected */ strcpy((char *) regid->id, name); ver = (uint16_t *) regid->id_suffix; *ver = udf_rw16(UDF_version); regid->id_suffix[2] = 4; /* unix */ regid->id_suffix[3] = 0; /* generic */ #if defined(__ANONYMOUSUDF__) #elif defined(__NetBSD__) regid->id_suffix[3] = 8; /* NetBSD */ #elif defined(__FreeBSD__) regid->id_suffix[3] = 7; /* FreeBSD */ #elif defined(LINUX) regid->id_suffix[3] = 5; /* Linux */ #endif } void udf_set_contents_id(struct regid *regid, char *content_id) { bzero(regid, sizeof(struct regid)); regid->flags = 0; strcpy((char *) regid->id, content_id); } /* XXX creators of empty descriptors could be externalised */ /* * result can be further processed using modify functions if demanded and then * processed trough udf_proc_pri_vol * [ int udf_proc_pri_vol(struct udf_session *udf_session, struct udf_pri_vol **current, struct pri_vol_desc *incomming); ] * */ int udf_create_empty_primary_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *volset_id, char *privol_name, int vds_num, int max_vol_seq, struct pri_vol_desc **dscrptr) { struct pri_vol_desc *dscr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty primary volume descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_PRI_VOL, dscr_ver, 1); dscr->pvd_num = udf_rw32(serial); udf_encode_osta_id(dscr->vol_id, 32, privol_name); dscr->vds_num = udf_rw16(vds_num); dscr->max_vol_seq = udf_rw16(max_vol_seq); if (max_vol_seq > 1) { dscr->ichg_lvl = udf_rw16(3); /* signal its a single volume intended to be in a set */ dscr->max_ichg_lvl = udf_rw16(3); /* ,, */ dscr->flags = udf_rw16(1); /* signal relevance volumeset id */ } else { dscr->ichg_lvl = udf_rw16(2); /* signal its volume intended not to be in a set */ dscr->max_ichg_lvl = udf_rw16(2); /* ,, */ dscr->flags = udf_rw16(0); /* signal relevance volumeset id */ } dscr->charset_list = udf_rw32(1); /* only CS0 */ dscr->max_charset_list = udf_rw32(1); udf_encode_osta_id(dscr->volset_id, 128, volset_id); udf_osta_charset(&dscr->desc_charset); udf_osta_charset(&dscr->explanatory_charset); udf_set_app_id(&dscr->app_id); udf_set_imp_id(&dscr->imp_id); udf_set_timestamp_now(&dscr->time); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct pri_vol_desc) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_partition_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, uint16_t part_num, uint32_t access_type, uint32_t start_loc, uint32_t part_len, uint32_t space_bitmap_size, uint32_t unalloc_space_bitmap, struct part_desc **dscrptr) { struct part_desc *dscr; struct part_hdr_desc *part_hdr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate empty partition descriptor */ dscr = malloc(sector_size); /* only descriptor, no bitmap! */ if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_PARTITION, dscr_ver, 1); dscr->seq_num = udf_rw32(serial); dscr->flags = udf_rw16(1); /* bit 0 : space is allocated */ dscr->part_num = udf_rw16(part_num); if (dscr_ver == 2) udf_set_contents_id(&dscr->contents, "+NSR02"); if (dscr_ver == 3) udf_set_contents_id(&dscr->contents, "+NSR03"); part_hdr = &dscr->pd_part_hdr; part_hdr->unalloc_space_bitmap.len = udf_rw32(space_bitmap_size); part_hdr->unalloc_space_bitmap.lb_num = udf_rw32(unalloc_space_bitmap); dscr->access_type = udf_rw32(access_type); dscr->start_loc = udf_rw32(start_loc); dscr->part_len = udf_rw32(part_len); udf_set_imp_id(&dscr->imp_id); /* why is this ignored? */ dscr->tag.desc_crc_len = udf_rw16(sizeof(struct part_desc) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_unallocated_space_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, struct unalloc_sp_desc **dscrptr) { struct unalloc_sp_desc *dscr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty unallocated space descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_UNALLOC_SPACE, dscr_ver, 1); dscr->seq_num = udf_rw32(serial); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_implementation_use_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, struct impvol_desc **dscrptr) { struct impvol_desc *dscr; struct udf_lv_info *lv_info; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty implementation use volume descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_IMP_VOL, dscr_ver, 1); dscr->seq_num = udf_rw32(serial); udf_set_entity_id(&dscr->impl_id, "*UDF LV Info", 0x102); /* just pick one; it'll be modifed later */ lv_info = &dscr->_impl_use.lv_info; udf_osta_charset(&lv_info->lvi_charset); udf_encode_osta_id(lv_info->logvol_id, 128, logvol_name); udf_encode_osta_id(lv_info->lvinfo1, 36, NULL); udf_encode_osta_id(lv_info->lvinfo2, 36, NULL); udf_encode_osta_id(lv_info->lvinfo3, 36, NULL); udf_set_imp_id(&lv_info->impl_id); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct impvol_desc) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_logical_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, uint32_t lb_size, uint32_t integrity_start, uint32_t integrity_length, struct logvol_desc **dscrptr) { struct logvol_desc *dscr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty logical volume descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_LOGVOL, dscr_ver, 1); dscr->seq_num = udf_rw32(serial); udf_osta_charset(&dscr->desc_charset); udf_encode_osta_id(dscr->logvol_id, 128, logvol_name); dscr->lb_size = udf_rw32(lb_size); udf_set_contents_id(&dscr->domain_id, "*OSTA UDF Compliant"); /* no fsd yet nor partition mapping */ udf_set_imp_id(&dscr->imp_id); dscr->integrity_seq_loc.loc = udf_rw32(integrity_start); dscr->integrity_seq_loc.len = udf_rw32(integrity_length * lb_size); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct logvol_desc) - 1 - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_space_bitmap(uint32_t sector_size, uint16_t dscr_ver, uint32_t num_lbs, struct space_bitmap_desc **dscrptr) { struct space_bitmap_desc *dscr; uint64_t bits; uint32_t bytes, space_bitmap_size; assert(dscrptr); *dscrptr = NULL; /* reserve space for unallocated space bitmap */ bits = num_lbs; bytes = (bits + 7)/8; space_bitmap_size = (bytes + sizeof(struct space_bitmap_desc)-1); /* round space bitmap size to sector size */ space_bitmap_size = ((space_bitmap_size + sector_size - 1) / sector_size) * sector_size; /* allocate and populate an empty space bitmap descriptor */ dscr = malloc(space_bitmap_size); if (!dscr) return ENOMEM; bzero(dscr, space_bitmap_size); udf_init_desc_tag(&dscr->tag, TAGID_SPACE_BITMAP, dscr_ver, 1); /* crc length 8 is recommended, UDF 2.3.1.2, 2.3.8.1, errata DCN-5108 for UDF 2.50 and lower. */ dscr->tag.desc_crc_len = udf_rw16(8); dscr->num_bits = udf_rw32(bits); dscr->num_bytes = udf_rw32(bytes); *dscrptr = dscr; return 0; } /* FIXME: no rootdir setting yet */ /* FIXME: fileset desc. is disc sector size or lb_size ? */ int udf_create_empty_fileset_desc(uint32_t sector_size, uint16_t dscr_ver, uint32_t fileset_num, char *logvol_name, char *fileset_name, struct fileset_desc **dscrptr) { struct fileset_desc *dscr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty logical volume descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_FSD, dscr_ver, 1); udf_set_timestamp_now(&dscr->time); dscr->ichg_lvl = udf_rw16(3); /* fixed? */ dscr->max_ichg_lvl = udf_rw16(3); /* fixed? */ dscr->charset_list = udf_rw32(1); /* only CS0 */ dscr->max_charset_list = udf_rw32(1); /* only CS0 */ dscr->fileset_num = udf_rw32(fileset_num); /* key for fileset */ dscr->fileset_desc_num = udf_rw32(0); /* fileset descriptor number as in copy # */ udf_osta_charset(&dscr->logvol_id_charset); udf_encode_osta_id(dscr->logvol_id, 128, logvol_name); udf_osta_charset(&dscr->fileset_charset); udf_encode_osta_id(dscr->fileset_id, 32, fileset_name); udf_encode_osta_id(dscr->copyright_file_id, 32, NULL); udf_encode_osta_id(dscr->abstract_file_id, 32, NULL); udf_set_contents_id(&dscr->domain_id, "*OSTA UDF Compliant"); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct fileset_desc) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_anchor_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint32_t main_vds_loc, uint32_t reserve_vds_loc, uint32_t length, struct anchor_vdp **vdp) { assert(vdp); assert(main_vds_loc - reserve_vds_loc >= length); *vdp = malloc(sector_size); if (!*vdp) return ENOMEM; bzero(*vdp, sector_size); udf_init_desc_tag(&(*vdp)->tag, TAGID_ANCHOR, dscr_ver, 1); (*vdp)->main_vds_ex.loc = udf_rw32(main_vds_loc); (*vdp)->main_vds_ex.len = udf_rw32(length * sector_size); (*vdp)->reserve_vds_ex.loc = udf_rw32(reserve_vds_loc); (*vdp)->reserve_vds_ex.len = udf_rw32(length * sector_size); (*vdp)->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); /* fixed size in Ecma */ return 0; } int udf_create_empty_terminator_descriptor(uint32_t sector_size, uint16_t dscr_ver, struct desc_tag **tag) { assert(tag); *tag = malloc(sector_size); if (!*tag) return ENOMEM; bzero(*tag, sector_size); udf_init_desc_tag(*tag, TAGID_TERM, dscr_ver, 1); (*tag)->desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); /* fixed size in Ecma */ return 0; } /****************************************************************************************** * * Basic `open' and `close' disc functions * ******************************************************************************************/ static void udf_process_session_range(struct udf_discinfo *disc, int *enabled, int low, int high) { int session; if (!disc) return; high = MIN(high, disc->num_sessions-1); session = low; for (session = low; session <= high; session++) { enabled[session] = 1; } } /* range is specified in -3,5,7 or 5-6,8- etc */ static int udf_process_session_range_string(struct udf_discinfo *disc, char *range) { struct udf_session *udf_session, *next_udf_session; char *pos, *nop; int low, high, len, session; int enabled[MAX_SESSIONS]; if (!range) return 0; DEBUG(printf("UDF range debugging string '%s'\n", range)); if (disc) { /* disable all */ for (session = 0; session < disc->num_sessions; session++) { enabled[session] = 0; } } /* parse string */ nop = strdup(range); pos = range; if (sscanf(pos, "-%u%n%s", &high, &len, nop) >= 1) { DEBUG(printf("UDF range match till %d\n", high)); udf_process_session_range(disc, enabled, 0, high); pos += len; } if (*pos && *pos == ',') pos++; while (*pos) { if (sscanf(pos, "%u%n%s", &low, &len, nop) >= 1) { pos += len; if (*pos == '-') { pos++; if (!*pos) { DEBUG(printf("UDF range match from %d\n", low)); udf_process_session_range(disc, enabled, low, INT_MAX); free(nop); return 0; } if (sscanf(pos, "%u%n%s", &high, &len, nop) >= 1) { pos += len; DEBUG(printf("UDF range match from %d to %d\n", low, high)); udf_process_session_range(disc, enabled, low, high); } } else { if (!*pos || (*pos == ',')) { DEBUG(printf("UDF range match %d\n", low)); udf_process_session_range(disc, enabled, low, low); } } if (*pos && (*pos != ',')) { fprintf(stderr, "UDF range matching : ',' expected at %s\n", pos); free(nop); return ENOENT; } pos++; } else { fprintf(stderr, "UDF range matching : number expected at %s\n", pos); free(nop); return ENOENT; } } free(nop); DEBUG(printf("UDF range matching : all ok till the end\n")); if (!disc) return 0; udf_session = STAILQ_FIRST(&disc->sessions); while (udf_session) { next_udf_session = STAILQ_NEXT(udf_session, next_session); session = udf_session->session_num; if (!enabled[session]) { /* remove this session */ fprintf(stderr, "UDF: disabling UDF session %d on request\n", session); STAILQ_REMOVE(&disc->sessions, udf_session, udf_session, next_session); free(udf_session); disc->session_is_UDF[session] = 0; } udf_session = next_udf_session; } return 0; } int udf_check_session_range(char *range) { return udf_process_session_range_string(NULL, range); } void udf_init(void) { udf_unix_init(); udf_start_unix_thread(); dirhash_init(); SLIST_INIT(&udf_discs_list); SLIST_INIT(&udf_volumeset_list); SLIST_INIT(&udf_mountables); } int udf_mount_disc(char *devname, char *range, uint32_t sector_size, int mnt_flags, struct udf_discinfo **disc) { int discop_flags, error; discop_flags = mnt_flags & UDF_MNT_BSWAP ? UDF_DISCOP_BSWAP : 0; error = udf_open_disc(devname, discop_flags, disc); if ((!error) && sector_size) error = udf_discinfo_alter_perception(*disc, sector_size, 0); if (error) return error; error = udf_get_anchors(*disc); UDF_VERBOSE(udf_dump_disc_anchors(*disc)); if (range) { UDF_VERBOSE(printf("Selecting UDF sessions '%s' as specified\n", range)); udf_process_session_range_string(*disc, range); UDF_VERBOSE(udf_dump_disc_anchors(*disc)); } /* no UDF partitions so bail out */ if ((*disc)->num_udf_sessions == 0) return 0; UDF_VERBOSE(printf("Start mounting\n")); error = udf_get_volumeset_space(*disc); if (error) return error; UDF_VERBOSE(printf("\teliminating predescessors\n")); udf_eliminate_predescessor_volumesets(*disc); UDF_VERBOSE_TABLES(udf_dump_alive_sets()); UDF_VERBOSE(printf("\tretrieving logical volume dependencies %p\n", *disc)); error = udf_get_logical_volumes_supporting_tables(*disc, mnt_flags); UDF_VERBOSE_TABLES(udf_dump_alive_sets()); /* insert disc in the disc list */ SLIST_INSERT_HEAD(&udf_discs_list, *disc, next_disc); return error; } int udf_dismount_disc(struct udf_discinfo *disc) { UDF_VERBOSE(printf("Dismounting disc\n")); if (!disc->recordable) { /* easy way out: it was a read-only system */ UDF_VERBOSE(printf("\tdismounting readonly disc\n")); udf_stop_unix_thread(); udf_close_disc(disc); return 0; } /* Sync disc before closing it */ UDF_VERBOSE(printf("\tsyncing disc\n")); udf_sync_disc(disc); /* wait for the disc to idle */ UDF_VERBOSE(printf("\twait for syncing disc to idle\n")); while (!udf_discinfo_check_disc_ready(disc)) { sleep(1); } /* stop threads and finish writing to it */ udf_stop_unix_thread(); UDF_VERBOSE(printf("\tsignal disc its finished with writing\n")); udf_discinfo_finish_writing(disc); /* wait for the disc to idle again */ UDF_VERBOSE(printf("\twait for final disc idling\n")); while (!udf_discinfo_check_disc_ready(disc)) { sleep(1); } UDF_VERBOSE(printf("\tclose device\n")); udf_close_disc(disc); return 0; } /****************************************************************************************** * * Directory and other conversion UDF logic * Move to udf_unix.c / udf_vnops.c one day? * ******************************************************************************************/ static int udf_translate_icb_filetype_to_dirent_filetype(int udf_filetype) { int d_type; switch (udf_filetype) { case UDF_ICB_FILETYPE_DIRECTORY : d_type = DT_DIR; break; case UDF_ICB_FILETYPE_STREAMDIR : d_type = DT_DIR; break; case UDF_ICB_FILETYPE_FIFO : d_type = DT_FIFO; break; case UDF_ICB_FILETYPE_CHARDEVICE : d_type = DT_CHR; break; case UDF_ICB_FILETYPE_BLOCKDEVICE : d_type = DT_BLK; break; case UDF_ICB_FILETYPE_RANDOMACCESS : d_type = DT_REG; break; case UDF_ICB_FILETYPE_SYMLINK : d_type = DT_LNK; break; case UDF_ICB_FILETYPE_SOCKET : d_type = DT_SOCK; break; default : d_type = DT_UNKNOWN; break; } return d_type; } /* VOP_GETATTR */ /* allmost NOOP since we remember the stat in the inode */ int udf_getattr(struct udf_node *udf_node, struct stat *stat) { *stat = udf_node->stat; /* special: updatables */ stat->st_nlink = udf_node->link_cnt; stat->st_blocks = (stat->st_size + 512 -1)/512; /* blocks are hardcoded 512 bytes/sector in stat :-/ */ return 0; } /* VOP_SETATTR */ /* allmost NOOP since we remember the stat in the inode */ /* note VOP_SETATTR can selectively set attrs */ int udf_setattr(struct udf_node *udf_node, struct stat *stat) { if (!udf_node) return ENOENT; if (udf_open_logvol(udf_node->udf_log_vol)) return EROFS; /* FIXME please don't just copy everything ... XXX */ udf_node->stat = *stat; /* not attribute change time */ udf_set_timespec_now(&udf_node->stat.st_ctimespec); udf_node_mark_dirty(udf_node); return 0; } void udf_resync_fid_stream(uint8_t *buffer, uint32_t *pfid_pos, uint32_t max_fid_pos, int *phas_fids) { struct fileid_desc *fid; uint32_t fid_pos; int has_fids; assert(buffer); assert(pfid_pos); assert(phas_fids); has_fids = 0; fid_pos = *pfid_pos; while (!has_fids) { while (fid_pos <= max_fid_pos) { fid = (struct fileid_desc *) (buffer + fid_pos); if (udf_rw16(fid->tag.id) == TAGID_FID) break; /* fid's can only exist 4 bytes aligned */ fid_pos += 4; } if (fid_pos > max_fid_pos) { /* shouldn't happen ! to prevent chaos, do nothing */ /* XXX ought to give a warning? XXX */ has_fids = 0; break; } else { /* check if we found a valid FID */ fid = (struct fileid_desc *) (buffer + fid_pos); has_fids = (udf_check_tag((union dscrptr *) fid) == 0); if (has_fids) { assert(udf_rw16(fid->tag.id) == TAGID_FID); break; } } } *pfid_pos = fid_pos; *phas_fids = has_fids; } /* read one fid and process it into a dirent and advance to the next */ /* (*fid) has to be allocated a logical block in size, (*dirent) struct dirent length */ int udf_read_fid_stream(struct udf_node *dir_node, uint64_t *offset, struct fileid_desc *fid, struct dirent *dirent) { struct uio dir_uio; struct iovec dir_iovec; char *fid_name; uint32_t entry_length, lb_size; int enough, error; assert(fid); assert(dirent); assert(dir_node); assert(offset); assert(*offset != 1); lb_size = dir_node->udf_log_vol->lb_size; entry_length = 0; bzero(dirent, sizeof(struct dirent)); bzero(fid, lb_size); if (*offset >= (uint64_t) dir_node->stat.st_size) return EINVAL; bzero(&dir_uio, sizeof(struct uio)); dir_uio.uio_rw = UIO_WRITE; /* write into this space */ dir_uio.uio_iovcnt = 1; dir_uio.uio_iov = &dir_iovec; dir_iovec.iov_base = fid; dir_iovec.iov_len = lb_size; dir_uio.uio_offset = *offset; dir_uio.uio_resid = MIN(dir_node->stat.st_size - (*offset), lb_size); error = udf_read_file_part_uio(dir_node, "file id" /* udf_node->dirent.d_name */, UDF_C_FIDS, &dir_uio); if (error) return error; /* * Check if we got a whole descriptor. * XXX Try to `resync' directory stream when something is very wrong. * */ enough = (dir_uio.uio_offset - (*offset) >= UDF_FID_SIZE); if (!enough) { /* short dir ... */ return EIO; } error = udf_check_tag((union dscrptr *) fid); if (!error) { entry_length = udf_calc_tag_malloc_size((union dscrptr *) fid, lb_size); enough = (dir_uio.uio_offset - (*offset) >= entry_length); } if (!enough) { /* short dir ... */ return EIO; } if (!error) error = udf_check_tag_payload((union dscrptr *) fid); if (error) { printf("BROKEN DIRECTORY ENTRY\n"); #if 0 // udf_dump_desc(&fid->tag); // udf_dump_fileid(fid); #endif /* RESYNC? */ /* TODO: use udf_resync_fid_stream */ return EIO; } /* we got a whole and valid descriptor */ /* create resulting dirent structure */ fid_name = (char *) fid->data + udf_rw16(fid->l_iu); dirent->d_fileno = udf_rw32(fid->icb.impl.im_used.unique_id); /* only 32 bits salvageable */ #if !defined(__DragonFly__) dirent->d_reclen = sizeof(struct dirent); #endif dirent->d_type = DT_UNKNOWN; udf_to_unix_name(dirent->d_name, fid_name, fid->l_fi, &dir_node->udf_log_vol->log_vol->desc_charset); #ifndef NO_DIRENT_NAMLEN dirent->d_namlen = strlen(dirent->d_name); #endif if (fid->file_char & UDF_FILE_CHAR_DIR) dirent->d_type = DT_DIR; if (fid->file_char & UDF_FILE_CHAR_PAR) strcpy(dirent->d_name, ".."); /* advance */ *offset += entry_length; return error; } /* VOP_READDIR */ /* read in dirent's until the result_uio can't hold another */ int udf_readdir(struct udf_node *dir_node, struct uio *result_uio, int *eof_res /* int *cookies, int ncookies */) { struct fileid_desc *fid; struct dirent dirent; uint64_t diroffset, transoffset; uint32_t lb_size; int eof; int error; assert(eof_res); if (!dir_node) return EINVAL; if (!dir_node->udf_log_vol) return EINVAL; assert(result_uio->uio_resid >= sizeof(struct dirent)); lb_size = dir_node->udf_log_vol->lb_size; fid = malloc(lb_size); if (!fid) return ENOMEM; /* check if we ought to insert dummy `.' node */ if (result_uio->uio_offset == 0) { bzero(&dirent, sizeof(struct dirent)); strcpy(dirent.d_name, "."); dirent.d_type = DT_DIR; #ifndef NO_DIRENT_NAMLEN dirent.d_namlen = 2; #endif uiomove(&dirent, sizeof(struct dirent), result_uio); /* mark with magic value (yeah it suxxs) that we have done the dummy */ result_uio->uio_offset = 1; } /* start directory reading */ diroffset = result_uio->uio_offset; transoffset = diroffset; while (diroffset < (uint64_t) dir_node->stat.st_size) { /* read just the offset when its flagged */ if (diroffset == 1) { diroffset = result_uio->uio_offset = 0; } /* read in FIDs */ error = udf_read_fid_stream(dir_node, &diroffset, fid, &dirent); if (error) { printf("Error while reading directory file: %s\n", strerror(error)); free(fid); return error; } /* if there is not enough space for the dirent break off read */ if (result_uio->uio_resid < sizeof(struct dirent)) break; /* remember the last entry we transfered */ transoffset = diroffset; /* skip deleted entries */ if (fid->file_char & UDF_FILE_CHAR_DEL) continue; /* skip not visible entries */ if (fid->file_char & UDF_FILE_CHAR_VIS) continue; uiomove(&dirent, sizeof(struct dirent), result_uio); } /* pass on last transfered offset */ result_uio->uio_offset = transoffset; free(fid); eof = (result_uio->uio_offset >= (int64_t) dir_node->stat.st_size); if (eof_res) *eof_res = 1; *eof_res = eof; return 0; } static int dirhash_fill(struct udf_node *dir_node) { struct dirhash *dirh; struct fileid_desc *fid; struct dirent *dirent; uint64_t file_size, pre_diroffset, diroffset; uint32_t lb_size; int error; /* make sure we have a dirhash to work on */ dirh = dir_node->dir_hash; assert(dirh); assert(dirh->refcnt > 0); if (dirh->flags & DIRH_BROKEN) return EIO; if (dirh->flags & DIRH_COMPLETE) return 0; /* make sure we have a clean dirhash to add to */ dirhash_purge_entries(dirh); /* get directory filesize */ file_size = dir_node->stat.st_size; /* allocate temporary space for fid */ lb_size = dir_node->udf_log_vol->lb_size; fid = malloc(lb_size); assert(fid); /* allocate temporary space for dirent */ dirent = malloc(sizeof(struct dirent)); assert(dirent); error = 0; diroffset = 0; while (diroffset < file_size) { /* transfer a new fid/dirent */ pre_diroffset = diroffset; error = udf_read_fid_stream(dir_node, &diroffset, fid, dirent); if (error) { /* TODO what to do? continue but not add? */ dirh->flags |= DIRH_BROKEN; dirhash_purge_entries(dirh); break; } if ((fid->file_char & UDF_FILE_CHAR_DEL)) { /* register deleted extent for reuse */ dirhash_enter_freed(dirh, pre_diroffset, udf_fidsize(fid)); } else { /* append to the dirhash */ dirhash_enter(dirh, dirent, pre_diroffset, udf_fidsize(fid), 0); /* XXX speedup HACK: preread in our nodes to compensate for too lazy backend */ { struct udf_node *res_node; error = udf_readin_udf_node(dir_node, &fid->icb, fid, &res_node); } } } dirh->flags |= DIRH_COMPLETE; free(fid); free(dirent); return error; } /* XXX yes, move namelen to unsigned int */ int udf_lookup_name_in_dir(struct udf_node *dir_node, char *name, int namelen, struct long_ad *icb_loc, struct fileid_desc *fid, int *found) { struct dirhash *dirh; struct dirhash_entry *dirh_ep; struct dirent *dirent; uint64_t diroffset; int hit, error; /* set default return */ *found = 0; /* get our dirhash and make sure its read in */ dirhash_get(&dir_node->dir_hash); error = dirhash_fill(dir_node); if (error) { dirhash_put(dir_node->dir_hash); return error; } dirh = dir_node->dir_hash; /* allocate temporary space for dirent */ dirent = malloc(sizeof(struct dirent)); if (!dirent) return ENOMEM; DEBUG(printf("dirhash_lookup looking for `%*.*s`\n", namelen, namelen, name)); /* search our dirhash hits */ memset(icb_loc, 0, sizeof(*icb_loc)); dirh_ep = NULL; for (;;) { hit = dirhash_lookup(dirh, name, namelen, &dirh_ep); /* if no hit, abort the search */ if (!hit) break; /* check this hit */ diroffset = dirh_ep->offset; /* transfer a new fid/dirent */ error = udf_read_fid_stream(dir_node, &diroffset, fid, dirent); if (error) break; DEBUG(printf("dirhash_lookup\tchecking `%*.*s`\n", (int) DIRENT_NAMLEN(dirent), (int) DIRENT_NAMLEN(dirent), dirent->d_name)); /* see if its our entry */ assert(DIRENT_NAMLEN(dirent) == (unsigned int) namelen); if (strncmp(dirent->d_name, name, namelen) == 0) { *found = 1; *icb_loc = fid->icb; break; } } free(dirent); dirhash_put(dir_node->dir_hash); return error; } static int udf_count_direntries(struct udf_node *dir_node, int count_dotdot, uint32_t *dir_entries) { struct fileid_desc *fid; struct dirent dirent; uint64_t pos; uint32_t lb_size; int eof; int error; if (!dir_node) return EINVAL; lb_size = dir_node->udf_log_vol->lb_size; /* count all directory entries with optional the dotdot too */ /* only defined in directories XXX DT_COMP also possible XXX */ if ((dir_node->stat.st_mode & S_IFDIR) == 0) return ENOTDIR; /* get space to read fid in */ fid = malloc(lb_size); if (!fid) return ENOMEM; /* start directory reading */ *dir_entries = 0; pos = 0; eof = (pos == (uint64_t) dir_node->stat.st_size); while (!eof) { /* read in FIDs */ error = udf_read_fid_stream(dir_node, &pos, fid, &dirent); if (error) { printf("Error while counting directory entries : %s\n", strerror(error)); free(fid); return error; } /* process this FID/dirent */ if ((fid->file_char & UDF_FILE_CHAR_DEL) == 0) { if (fid->file_char & UDF_FILE_CHAR_PAR) { if (count_dotdot) *dir_entries = *dir_entries + 1; } else { *dir_entries = *dir_entries + 1; } } /* pos is automatically advanced */ eof = (pos == (uint64_t) dir_node->stat.st_size); } /* end of directory */ free(fid); return 0; } static int udf_writeout_fid_info(struct udf_node *dir_node, struct fileid_desc *fid, uint64_t offset, uint16_t fid_len) { struct uio uio; struct iovec iovec; int flags; bzero(&uio, sizeof(struct uio)); uio.uio_rw = UIO_READ; /* read from this space */ uio.uio_iovcnt = 1; uio.uio_iov = &iovec; iovec.iov_base = fid; iovec.iov_len = fid_len; uio.uio_offset = offset; uio.uio_resid = fid_len; flags = UDF_C_FIDS; return udf_write_file_part_uio(dir_node, "file id.", flags, &uio); } /* search for a space to record the fid in, not checking if it is allready in it ! */ /* ALERT: not to be used to update a fid ... use writeout_fid_info for that */ /* ONLY used by udf_create_directory_entry */ static int udf_insert_fid_info(struct udf_node *dir_node, struct udf_node *udf_node, struct fileid_desc *i_fid, uint16_t fidsize) { struct dirhash *dirh; struct dirhash_entry *dirh_ep; struct fileid_desc *fid; struct dirent dirent; uint64_t dir_size, fid_pos, chosen_fid_pos, end_fid_pos; uint32_t this_fidsize, chosen_size; uint32_t lb_size, lb_rest; uint32_t size_diff, chosen_size_diff; char *fid_name; int descr_ver, hit, error; udf_node = udf_node; /* passed only for printing diagnostic info if required */ if (!dir_node) return EINVAL; /* only defined in directories XXX DT_COMP also possible XXX */ if ((dir_node->stat.st_mode & S_IFDIR) == 0) return ENOTDIR; /* needs to be 4 bytes aligned to be legal! if not, something is seriously wrong so abort */ assert((fidsize & 3) == 0); /* get our dirhash and make sure its read in */ dirhash_get(&dir_node->dir_hash); error = dirhash_fill(dir_node); if (error) { dirhash_put(dir_node->dir_hash); return error; } dirh = dir_node->dir_hash; /* get info */ lb_size = dir_node->udf_log_vol->lb_size; dir_size = dir_node->stat.st_size; descr_ver = udf_rw16(dir_node->udf_log_vol->log_vol->tag.descriptor_ver); /* get space to read fid in */ fid = malloc(lb_size); if (!fid) return ENOMEM; /* find position that will fit the FID */ chosen_fid_pos = dir_size; chosen_size = 0; chosen_size_diff = UINT_MAX; /* shut up gcc */ #ifndef NO_DIRENT_NAMLEN dirent.d_namlen = 0; #endif /* search our dirhash hits */ error = 0; dirh_ep = NULL; for (;;) { hit = dirhash_lookup_freed(dirh, fidsize, &dirh_ep); /* if no hit, abort the search */ if (!hit) break; /* check this hit for size */ this_fidsize = dirh_ep->entry_size; /* check this hit */ fid_pos = dirh_ep->offset; end_fid_pos = fid_pos + this_fidsize; size_diff = this_fidsize - fidsize; lb_rest = lb_size - (end_fid_pos % lb_size); /* select if not splitting the tag and its smaller */ if ((size_diff <= chosen_size_diff) && (lb_rest >= sizeof(struct desc_tag))) { /* UDF 2.3.4.2+3 specifies rules for iu size */ if ((size_diff == 0) || (size_diff >= 32)) { chosen_fid_pos = fid_pos; chosen_size = this_fidsize; chosen_size_diff = size_diff; } } } /* extend directory if no other candidate found */ if (chosen_size == 0) { chosen_fid_pos = dir_size; chosen_size = fidsize; /* special case UDF 2.00+ 2.3.4.4, no splitting up fid tag */ if (dir_node->addr_type == UDF_ICB_INTERN_ALLOC) { /* pre-grow directory to see if we're to switch */ // udf_grow_node(dir_node, dir_size + chosen_size); error = udf_truncate_node(dir_node, chosen_fid_pos + chosen_size); assert(!error); } /* make sure the next fid desc_tag won't be splitted */ if (dir_node->addr_type != UDF_ICB_INTERN_ALLOC) { end_fid_pos = chosen_fid_pos + chosen_size; lb_rest = lb_size - (end_fid_pos % lb_size); /* pad with implementation use regid if needed */ if (lb_rest < sizeof(struct desc_tag)) chosen_size += 32; } } chosen_size_diff = chosen_size - fidsize; /* populate the FID */ memset(fid, 0, lb_size); udf_init_desc_tag(&fid->tag, TAGID_FID, descr_ver, 1); /* tag serial number */ fid->file_version_num = i_fid->file_version_num; fid->file_char = i_fid->file_char; fid->icb = i_fid->icb; fid->l_iu = udf_rw16(0); if (chosen_size > fidsize) { /* insert implementation-use regid to space it correctly */ fid->l_iu = udf_rw16(chosen_size_diff); /* set implementation use */ udf_set_imp_id((struct regid *) fid->data); } /* copy name */ fid->l_fi = i_fid->l_fi; memcpy(fid->data + udf_rw16(fid->l_iu), i_fid->data, fid->l_fi); fid->tag.desc_crc_len = chosen_size - UDF_DESC_TAG_LENGTH; /* writeout modified piece */ udf_validate_tag_and_crc_sums((union dscrptr *) fid); error = udf_writeout_fid_info(dir_node, fid, chosen_fid_pos, chosen_size); assert(!error); /* append to the dirhash */ fid_name = (char *) fid->data + udf_rw16(fid->l_iu); dirent.d_fileno = udf_rw32(fid->icb.impl.im_used.unique_id); /* only 32 bits salvageable */ #if !defined(__DragonFly__) dirent.d_reclen = sizeof(struct dirent); #endif dirent.d_type = DT_UNKNOWN; udf_to_unix_name(dirent.d_name, fid_name, fid->l_fi, &dir_node->udf_log_vol->log_vol->desc_charset); #ifndef NO_DIRENT_NAMLEN dirent.d_namlen = strlen(dirent.d_name); #endif if (fid->file_char & UDF_FILE_CHAR_DIR) dirent.d_type = DT_DIR; if (fid->file_char & UDF_FILE_CHAR_PAR) strcpy(dirent.d_name, ".."); dirhash_enter(dirh, &dirent, chosen_fid_pos, udf_fidsize(fid), 1); free(fid); dirhash_put(dir_node->dir_hash); return error; } /* create a file in the given directory with the given name and attributes using udf's file_char and udf'd filetype */ /* note * 1) that with `refering' node specified its effectively `link()' * 2) that with `refering' node specified, `filetype' is discarded as it ought to be the same as the `refering' one * * XXX this function needs to be splitted into node creation and directory * attachment; its now doing both in one go. */ int udf_create_directory_entry(struct udf_node *dir_node, char *name, int filetype, int filechar, struct udf_node *refering, struct stat *stat, struct udf_node **new_node) { struct udf_allocentry *alloc_entry; struct udf_log_vol *udf_log_vol; struct udf_node *udf_node; struct charspec osta_charspec; struct fileid_desc *fid; struct long_ad icb_loc; uint32_t lb_num, lb_size; uint16_t vpart_num, descr_ver, len; int found, error; assert(dir_node); assert(name); assert(dir_node->udf_log_vol); udf_log_vol = dir_node->udf_log_vol; lb_size = udf_log_vol->lb_size; descr_ver = udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver); *new_node = NULL; /* lookup if it allready exists (sanity mainly) */ fid = malloc(lb_size); assert(fid); error = udf_lookup_name_in_dir(dir_node, name, strlen(name), &icb_loc, fid, &found); if (!error && found) { /* it existed! allready there */ free(fid); return EEXIST; } if (!refering) { /* * Get ourselves an empty node and space to record file * descriptor in. */ error = udf_init_udf_node(dir_node->mountpoint, udf_log_vol, "New direntry", &udf_node); if (error) { free(fid); return error; } udf_node->udf_filetype = filetype; udf_node->udf_filechar = filechar; udf_node->unique_id = udf_increment_unique_id(udf_log_vol); /* snif */ error = udf_allocate_udf_node_on_disc(udf_node); if (error) { assert(udf_node != dir_node); udf_dispose_udf_node(udf_node); free(fid); return error; } udf_node->stat = *stat; /* note passed creation times; do sanitise them */ #ifndef NO_STAT_BIRTHTIME if (udf_insanetimespec(&stat->st_birthtimespec)) udf_set_timespec_now(&udf_node->stat.st_birthtimespec); #endif if (udf_insanetimespec(&stat->st_ctimespec)) udf_set_timespec_now(&udf_node->stat.st_ctimespec); if (udf_insanetimespec(&stat->st_atimespec)) udf_set_timespec_now(&udf_node->stat.st_atimespec); if (udf_insanetimespec(&stat->st_mtimespec)) udf_set_timespec_now(&udf_node->stat.st_mtimespec); } else { /* refering->ignore passed stat info */ udf_node = refering; filetype = udf_node->udf_filetype; /* linking changes metadata modification */ udf_set_timespec_now(&udf_node->stat.st_ctimespec); } alloc_entry = TAILQ_FIRST(&udf_node->dscr_allocs); vpart_num = alloc_entry->vpart_num; lb_num = alloc_entry->lb_num; /* build up new directory entry */ memset(fid, 0, lb_size); udf_osta_charset(&osta_charspec); udf_init_desc_tag(&fid->tag, TAGID_FID, descr_ver, 1); /* tag serial number */ if (filechar & UDF_FILE_CHAR_PAR) { /* parent or `..' is not allowed to have a name length ... wierd but ok */ fid->l_fi = 0; } else { unix_to_udf_name((char *) fid->data, name, &fid->l_fi, &osta_charspec); } fid->file_version_num = udf_rw16(1); /* new file/dir; version starts at 1 */ fid->file_char = filechar; /* what is it */ fid->l_iu = udf_rw32(0); /* no impl. use */ fid->icb.len = udf_rw32(lb_size); /* fill in location */ fid->icb.loc.part_num = udf_rw16(vpart_num); fid->icb.loc.lb_num = udf_rw32(lb_num); /* fill in lower 32 bits of unique ID (UDF 3/3.2.2.1) in the impl use part of the FID's long_ad */ fid->icb.impl.im_used.unique_id = udf_rw32(((udf_node->unique_id << 32) >> 32)); /* calculate minimum size needed for directory entry */ len = UDF_FID_SIZE + fid->l_fi; len = (len + 3) & ~3; fid->tag.desc_crc_len = udf_rw16(len - UDF_DESC_TAG_LENGTH); error = udf_insert_fid_info(dir_node, udf_node, fid, len); free(fid); /* Ahum... easily forgotten here */ if (error) { fprintf(stderr, "UDF: fid insertion failed : %s\n", strerror(error)); if (!refering) udf_dispose_udf_node(udf_node); return error; } if (udf_node) { /* only insert file in hashlist if its not an explicit reference */ if (!refering) { udf_insert_node_in_hash(udf_node); } else { refering->link_cnt++; udf_node_mark_dirty(refering); } udf_node_mark_dirty(udf_node); } *new_node = udf_node; return error; } /* * Rename file from old_name to new_name. `present' is the file to be replaced * if found present allready. Care should be taken that the directory tree is * kept intact. To prevent this no path should be possible from the new parent * to the node to be renamed if it considers a directory and the new_parent is * not equal to the old parent. */ /* * VOP_RENAME(struct vnode *fdvp, struct vnode *vp, * struct componentname *fcnp, struct componentname *tdvp, * struct vnode *tvp, struct componentname *tcnp * ); */ int udf_rename(struct udf_node *old_parent, struct udf_node *rename_me, char *old_name, struct udf_node *new_parent, struct udf_node *present, char *new_name) { struct udf_node *new_node; int error; /* sanity */ if (!old_parent) return ENOENT; if (!new_parent) return ENOENT; if (!rename_me) return ENOENT; if (!(old_parent->stat.st_mode & S_IFDIR)) return ENOTDIR; if (!(new_parent->stat.st_mode & S_IFDIR)) return ENOTDIR; if (udf_open_logvol(old_parent->udf_log_vol)) return EROFS; if (udf_open_logvol(new_parent->udf_log_vol)) return EROFS; if ((present && (present->stat.st_mode & S_IFDIR)) || (old_parent != new_parent)) { /* cross directory moves */ fprintf(stderr, "Cross directory renaming is not implemented yet.\n"); return ENOTSUP; } /* if it was present, delete old contents; reference counting is done */ if (present) { /* TODO what about non dir, non file entries? */ if (present->stat.st_mode & S_IFDIR) { error = udf_remove_directory(new_parent, present, new_name); } else { error = udf_remove_file(new_parent, present, new_name); } if (error) return error; } /* insert new_name HARD-linked to the `rename_me' node */ error = udf_create_directory_entry(new_parent, new_name, rename_me->udf_filetype, rename_me->udf_filechar, rename_me, NULL, &new_node); if (error) return error; /* extra sanity */ if (!new_node) return ENOENT; /* 3) remove old link and mark directories dirty */ error = udf_remove_directory_entry(old_parent, rename_me, old_name); udf_node_mark_dirty(old_parent); udf_node_mark_dirty(new_parent); return error; } /* VOP_CREATE */ int udf_create_file(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node) { struct udf_log_vol *udf_log_vol; struct udf_node *udf_node; uint32_t lb_size; int error; if (!dir_node) return EINVAL; udf_log_vol = dir_node->udf_log_vol; if (!udf_log_vol) return EINVAL; lb_size = udf_log_vol->lb_size; if (!udf_confirm_freespace(udf_log_vol, UDF_C_NODE, lb_size)) return ENOSPC; if (udf_open_logvol(dir_node->udf_log_vol)) return EROFS; error = udf_create_directory_entry(dir_node, componentname, UDF_ICB_FILETYPE_RANDOMACCESS, 0, NULL, stat, new_node); if ((!error) && (*new_node)) { udf_node = *new_node; /* update sizes */ udf_node->stat.st_size = 0; udf_node->stat.st_blksize = dir_node->udf_log_vol->lb_size; udf_node->stat.st_blocks = 0; /* not 1? */ udf_node->udf_log_vol->num_files++; udf_node_mark_dirty(udf_node); } return error; } /* VOP_MKDIR */ int udf_create_directory(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node) { struct udf_log_vol *udf_log_vol; struct udf_node *udf_node, *dummy_node; uint32_t lb_size; int error; if (!dir_node) return EINVAL; udf_log_vol = dir_node->udf_log_vol; if (!udf_log_vol) return EINVAL; lb_size = udf_log_vol->lb_size; if (!udf_confirm_freespace(udf_log_vol, UDF_C_NODE, 2*lb_size)) return ENOSPC; if (udf_open_logvol(dir_node->udf_log_vol)) return EROFS; stat->st_mode |= S_IFDIR; error = udf_create_directory_entry(dir_node, componentname, UDF_ICB_FILETYPE_DIRECTORY, UDF_FILE_CHAR_DIR, NULL, stat, new_node); if ((!error) && (*new_node)) { udf_node = *new_node; /* update sizes */ udf_node->stat.st_size = 0; udf_node->stat.st_blksize = dir_node->udf_log_vol->lb_size; udf_node->stat.st_blocks = 0; /* not 1? */ udf_node->udf_log_vol->num_directories++; udf_node_mark_dirty(udf_node); /* create `..' directory entry */ error = udf_create_directory_entry(udf_node, "..", UDF_ICB_FILETYPE_DIRECTORY, UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR, dir_node, stat, &dummy_node); if (error) { /* use of _prim for dir counting might not go well due to aborted creation */ error = udf_remove_directory_prim(dir_node, udf_node, componentname); } } return error; } /* really deletes all space referenced to this udf node including descriptor spaces and removes it from the administration */ int udf_unlink_node(struct udf_node *udf_node) { struct udf_allocentry *alloc_entry; uint32_t lbnum, len; uint16_t vpart; int error; /* just in case its called from outside */ if (udf_open_logvol(udf_node->udf_log_vol)) return EROFS; /* unlinking changes metadata modification */ udf_set_timespec_now(&udf_node->stat.st_ctimespec); udf_node->link_cnt--; udf_node_mark_dirty(udf_node); if (udf_node->link_cnt > 0) return 0; /* trunc node */ udf_truncate_node(udf_node, (uint64_t) 0); /* get rid of file contents */ /* free descriptors from dscr_allocs queue */ TAILQ_FOREACH(alloc_entry, &udf_node->dscr_allocs, next_alloc) { vpart = alloc_entry->vpart_num; lbnum = alloc_entry->lb_num; /* flags = alloc_entry->flags; */ len = alloc_entry->len; error = udf_release_lbs(udf_node->udf_log_vol, vpart, lbnum, len); /* what if an error occures? */ assert(error == 0); } /* delete from administration */ udf_dispose_udf_node(udf_node); return 0; } /* NOTE: Dont use the EXTENT erased part; its for non sequential WORM only */ /* UDF 2.3.10.1, ECMA 4/48.14.1.1 */ /* fid->icb.impl.im_used.flags = udf_rw16(UDF_ADIMP_FLAGS_EXTENT_ERASED); */ static int udf_remove_directory_entry(struct udf_node *dir_node, struct udf_node *udf_node, char *name) { struct dirhash *dirh; struct dirhash_entry *dirh_ep; struct fileid_desc *fid; struct dirent *dirent; uint64_t diroffset; uint32_t lb_size, namelen, fidsize; int hit, found; int error; assert(dir_node); assert(udf_node); assert(udf_node->udf_log_vol); assert(name); namelen = strlen(name); if (strncmp(name, "..", 3) == 0) { printf("Asked to remove `..' parent directory identifier; not allowed!\n"); return ENOENT; } if (strncmp(name, ".", 2) == 0) { printf("Asked to remove `.' current directory identifier; not allowed!\n"); return ENOENT; } /* only lookup in directories XXX DT_COMP also possible XXX */ if ((dir_node->stat.st_mode & S_IFDIR) == 0) return ENOTDIR; /* get our dirhash and make sure its read in */ dirhash_get(&dir_node->dir_hash); error = dirhash_fill(dir_node); if (error) { dirhash_put(dir_node->dir_hash); return error; } dirh = dir_node->dir_hash; /* allocate temporary space for fid */ lb_size = udf_node->udf_log_vol->lb_size; fid = malloc(lb_size); dirent = malloc(sizeof(struct dirent)); if (!fid || !dirent) { error = ENOMEM; goto error_out; } /* search our dirhash hits */ found = 0; dirh_ep = NULL; for (;;) { hit = dirhash_lookup(dirh, name, namelen, &dirh_ep); /* if no hit, abort the search */ if (!hit) break; /* check this hit */ diroffset = dirh_ep->offset; /* transfer a new fid/dirent */ error = udf_read_fid_stream(dir_node, &diroffset, fid, dirent); if (error) break; /* see if its our entry */ assert(DIRENT_NAMLEN(dirent) == namelen); if (strncmp(dirent->d_name, name, namelen) == 0) { found = 1; break; } } if (!found) error = ENOENT; if (error) goto error_out; /* get size of fid and compensate for the read_fid_stream advance */ fidsize = udf_fidsize(fid); diroffset -= fidsize; /* mark fid as deleted */ fid->file_char |= UDF_FILE_CHAR_DEL; bzero(&fid->icb, sizeof(struct long_ad)); udf_validate_tag_and_crc_sums((union dscrptr *) fid); udf_writeout_fid_info(dir_node, fid, diroffset, fidsize); /* remove from the dirhash */ dirhash_mark_freed(dirh, dirh_ep, dirent); /* delete node and its administration if refcount indicates so */ udf_unlink_node(udf_node); error_out: if (fid) free(fid); if (dirent) free(dirent); dirhash_put(dir_node->dir_hash); return error; } /* VOP_REMOVE */ int udf_remove_file(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) { int error; if (udf_open_logvol(dir_node->udf_log_vol)) return EROFS; if (udf_node->stat.st_mode & S_IFDIR) { /* only remove files with this call */ return EISDIR; } error = udf_remove_directory_entry(dir_node, udf_node, componentname); if (!error) { dir_node->udf_log_vol->num_files--; } /* else? */ return error; } static int udf_remove_directory_prim(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) { int error; if (udf_open_logvol(dir_node->udf_log_vol)) return EROFS; /* remove the entry */ error = udf_remove_directory_entry(dir_node, udf_node, componentname); if (!error) { dir_node->link_cnt--; udf_node_mark_dirty(dir_node); dir_node->udf_log_vol->num_directories--; } else { /* whoah! something went wrong, mark the .. as present again */ printf("UDF warning: filesystem might by in compromised state\n"); assert(udf_node); udf_node->link_cnt++; } return error; } /* VOP_RMDIR */ int udf_remove_directory(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) { uint32_t num_nodes; int error; if (!(udf_node->stat.st_mode & S_IFDIR)) { /* only remove directories with this call */ return ENOTDIR; } error = udf_count_direntries(udf_node, 0, &num_nodes); if (error) return error; if (num_nodes != 0) return ENOTEMPTY; error = udf_remove_directory_prim(dir_node, udf_node, componentname); return error; } /* end of udf.c */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf.h000066400000000000000000000640471523477242100251030ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf.h" is part of the UDFclient toolkit. * File $Id: udf.h,v 1.150 2020/06/10 09:41:30 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _UDF_H_ #define _UDF_H_ #include #include #include #include #include #include #include "dirhash.h" #ifdef NO_INT_FMTIO /* assume 32 bits :-/ */ #ifndef PRIu32 # define PRIu32 "u" # define PRIx32 "x" # define PRIu64 "lld" # define PRIx64 "llx" #endif #endif /* exported flags */ extern int udf_verbose; #define UDF_MNT_RDONLY 1 #define UDF_MNT_FORCE 2 #define UDF_MNT_BSWAP 4 #define UDF_VERBLEV_NONE 0 #define UDF_VERBLEV_ACTIONS 1 #define UDF_VERBLEV_TABLES 2 #define UDF_VERBLEV_MAX 3 /* constants to identify what kind of identifier we are dealing with */ #define UDF_REGID_DOMAIN 1 #define UDF_REGID_UDF 2 #define UDF_REGID_IMPLEMENTATION 3 #define UDF_REGID_APPLICATION 4 #define UDF_REGID_NAME 99 /* 99? */ /* RW content hint for allocation and other purposes */ #define UDF_C_DSCR 0 #define UDF_C_USERDATA 1 #define UDF_C_FIDS 2 #define UDF_C_NODE 3 /* Configuration */ #define UDF_MINFREE_LOGVOL (128*1024) /* max sector is 64kb, give 2 (sorry) slack; use with care */ #define UDF_LOOKUP_READAHEAD 64 /* rewrite lookahead; set lookahead 0 to disable */ /* misc. configuration; don't change unless you know what you're doing */ #define UDF_READWRITE_LINE_LENGTH 32 /* DONT change this! 32 sectors for CD-RW/DVD-RW fixed packets */ #define UDF_READWRITE_ALL_PRESENT 0xffffffff #define UDF_INODE_HASHBITS 10 #define UDF_INODE_HASHSIZE (1<= (level)) { op; } # define UDF_VERBOSE_TABLES(op) UDF_VERBOSE_LEVEL(UDF_VERBLEV_TABLES, op) # define UDF_VERBOSE_MAX(op) UDF_VERBOSE_LEVEL(UDF_VERBLEV_MAX, op) #else # define UDF_VERBOSE(op) # define UDF_VERBOSE_LEVEL(level, op) # define UDF_VERBOSE_TABLES(op) # define UDF_VERBOSE_MAX(op) #endif #define UDF_MUTEX(name) struct { \ pthread_mutex_t mutex;\ int locked;\ char *status;\ char *file;\ int line;\ } name #define UDF_MUTEX_INIT(name) { \ pthread_mutex_init(&(name)->mutex, 0); \ (name)->locked = 0; \ (name)->status = "initialised as " #name;\ (name)->file = __FILE__;\ (name)->line = __LINE__;\ } #define UDF_MUTEX_LOCK(name) { \ if (0 && (name)->locked) printf("Waiting for lock " #name " at line %d of file %s marked %s in %s at line %d\n", __LINE__, __FILE__, (name)->status, (name)->file, (name)->line);\ pthread_mutex_lock(&(name)->mutex);\ (name)->locked = 1; \ (name)->status = "locked as " #name;\ (name)->file = __FILE__;\ (name)->line = __LINE__;\ } #define UDF_MUTEX_TRYLOCK(name) { \ if (0) printf("Trying lock " #name " at line %d of file %s marked %s in %s at line %d\n", __LINE__, __FILE__, (name)->status, (name)->file, (name)->line);\ if (pthread_mutex_trylock(&(name)->mutex) != EBUSY) {\ (name)->locked = 1;\ (name)->status = "locked as " #name;\ (name)->file = __FILE__;\ (name)->line = __LINE__;\ };\ } #define UDF_MUTEX_UNLOCK(name) { \ if (0 && !(name)->locked) printf("Unlocking lock " #name " at line %d of file %s marked %s in %s at line %d\n", __LINE__, __FILE__, (name)->status, (name)->file, (name)->line);\ (name)->locked = 0; \ (name)->status = "unlocked as " #name;\ (name)->file = __FILE__;\ (name)->line = __LINE__;\ pthread_mutex_unlock(&(name)->mutex);\ } /* Predefines */ struct udf_node; struct udf_mountpoint; #include "osta.h" #include "ecma167-udf.h" #include "queue.h" #include "udf_discop.h" #include "udf_unix.h" #include "uio.h" #include /*---------------------------------------------------------------------*/ /* * Provides :: write action -> signal written OK or signal write error * NOTE: not used anymore but kept for convenience when write error resolution * is build. */ struct udf_wrcallback; typedef void (udf_wrcallback_func)(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata); struct udf_wrcallback { udf_wrcallback_func *function; struct udf_buf *udf_buf; /* associated buffer */ struct udf_node *udf_node; /* associated vnode */ uint32_t flags; /* some reserved flags but other bits are OK */ }; #define UDF_WRCALLBACK_REASON_PENDING 0 #define UDF_WRCALLBACK_REASON_ANULATE 1 #define UDF_WRCALLBACK_REASON_WRITTEN 2 #define UDF_WRCALLBACK_FLAG_DESCRIPTOR (1<<0) #define UDF_WRCALLBACK_FLAG_BUFFER (1<<1) /* * Provides :: ``allocentry'' an ordered list of allocation extents * */ struct udf_allocentry { uint32_t len; uint32_t lb_num; uint16_t vpart_num; uint8_t flags; TAILQ_ENTRY(udf_allocentry) next_alloc; }; #define UDF_SPACE_ALLOCATED 0 #define UDF_SPACE_FREED 1 #define UDF_SPACE_ALLOCATED_BUT_NOT_USED 1 #define UDF_SPACE_FREE 2 #define UDF_SPACE_REDIRECT 3 TAILQ_HEAD(udf_alloc_entries, udf_allocentry); /* * Provides :: ``inode'' or rather filing system dependent v_data node linked under vnode structure * */ struct udf_node { struct udf_mountpoint *mountpoint; /* foreign; provides logvol and fileset */ struct udf_log_vol *udf_log_vol; /* foreign; backup if its not associated to mountpoint */ int dirty; /* flags if (ext)fentry needs to be synced (safeguard) */ int hold; /* node is on hold i.e. don't recycle nor its buffers */ ino_t hashkey; /* for hashing to lookup inode by vpart & lbnum */ struct stat stat; /* holds unix file attributes/filestats */ struct udf_alloc_entries dscr_allocs; /* where my complete descriptor space is located */ uint8_t udf_filetype; /* filetype of this node; raw, block, char, softlink... */ uint8_t udf_filechar; /* udf file characteristics (vis, meta, ... */ uint16_t serial_num; /* serial number of this descriptor on disc */ uint16_t file_version_num; /* file version number of this descriptor on disc */ uint16_t udf_icbtag_flags; /* serial, setuid, setguid, stickybit etc */ uint16_t link_cnt; /* how many FID's are linked to this (ext)fentry */ uint64_t unique_id; /* unique file ID */ /* extended attributes and subfiles */ struct udf_alloc_entry *extattrfile_icb; /* associated extended attribute file */ struct udf_alloc_entry *streamdir_icb; /* associated streamdir */ /* internal in-node storage of extended attributes copy. */ uint8_t *extended_attr; uint32_t extended_attr_len; /* internal in-node storage of data copy */ uint8_t *intern_data; /* descriptor internal data */ uint32_t intern_len; /* length of descriptor internal data */ uint32_t intern_free; /* free space amount in the descriptor besides extattr. */ /* extents of discspace that make up this file/directory */ uint32_t addr_type; /* storage type of allocation descriptors */ uint32_t icb_len; /* length of an icb descriptor */ UDF_MUTEX(alloc_mutex); struct udf_alloc_entries alloc_entries; /* all associated vn_bufs for this node */ UDF_MUTEX(buf_mutex); struct udf_buf_queue vn_bufs; uint32_t v_numoutput; /* dir hasing */ struct dirhash *dir_hash; /* lists */ TAILQ_ENTRY(udf_node) next_dirty; /* next in dirty node list */ LIST_ENTRY(udf_node) next_node; /* next in hash node list */ }; TAILQ_HEAD(udf_node_list, udf_node); /*---------------------------------------------------------------------*/ /* * Provides :: mountpoint -> fileset descriptor and logical volume * */ struct udf_mountpoint { char *mount_name; /* identifier */ struct udf_log_vol *udf_log_vol; /* foreign */ struct fileset_desc *fileset_desc; /* fileset belonging to this mountpoint */ struct udf_node *rootdir_node; struct udf_node *streamdir_node; int writable; /* flags if its a writable fileset */ SLIST_ENTRY(udf_mountpoint) all_next; /* for overall mountpoint list */ SLIST_ENTRY(udf_mountpoint) logvol_next; /* for list of mountpoints in a logvol */ }; /* * Provides :: logvol_partition -> volumeset physical partition */ struct udf_part_mapping { uint32_t udf_part_mapping_type; uint32_t vol_seq_num; uint32_t udf_virt_part_num; uint32_t udf_phys_part_num; union udf_pmap *udf_pmap; /* foreign */ int data_writable; /* flags if its suited for data */ int metadata_writable; /* flags if its for meta-data */ /* supporting tables */ struct udf_sparing_table *sparing_table; /* virtual space */ struct udf_node *vat_udf_node; struct udf_vat *vat; uint8_t *vat_translation; uint32_t vat_entries; uint32_t vat_length; /* needs to be updated; metadata partition disabled for now */ struct udf_node *meta_file; struct udf_node *meta_mirror_file; struct udf_node *meta_bitmap_file; SLIST_ENTRY(udf_part_mapping) next_mapping; /* for list of partition mappings */ }; #define UDF_PART_MAPPING_ERROR 0 #define UDF_PART_MAPPING_PHYSICAL 1 #define UDF_PART_MAPPING_VIRTUAL 2 #define UDF_PART_MAPPING_SPARABLE 3 #define UDF_PART_MAPPING_META 4 #define UDF_PART_MAPPING_PSEUDO_RW 5 /* * Provides :: log_vol -> ... * :: MOUNTPOINTS :) */ struct udf_log_vol { int broken; /* primary volume this logical volume is recorded on */ struct udf_pri_vol *primary; /* foreign */ /* logical volume info */ struct logvol_desc *log_vol; uint32_t lb_size; /* constant over logvol in Ecma 167 */ uint32_t sector_size; /* constant over logvol in Ecma 167 */ /* logical volume integrity/VAT information */ uint32_t logvol_state; /* maintained */ uint16_t integrity_serial; uint32_t min_udf_readver; uint32_t min_udf_writever; uint32_t max_udf_writever; uint32_t num_files; /* maintained */ uint32_t num_directories; /* maintained */ uint64_t next_unique_id; /* maintained */ int writable; /* flags if its writable */ /* dirty nodes administration */ UDF_MUTEX(dirty_nodes_mutex); struct udf_node_list dirty_nodes; /* hash table to lookup ino_t -> udf_node */ LIST_HEAD(inodes, udf_node) udf_nodes[UDF_INODE_HASHSIZE]; /* estimated free space summation; from logvol integrity */ uint64_t total_space; uint64_t free_space; uint64_t await_alloc_space; /* consisting of */ uint32_t data_vpart, metadata_vpart; uint32_t num_mountpoints; /* display only */ SLIST_HEAD(mountpoints_list, udf_mountpoint) mountpoints; /* list of mountables in logvol */ uint32_t num_part_mappings; /* display only */ SLIST_HEAD(part_mappings_list, udf_part_mapping) part_mappings; /* list of partition mappings */ /* next in list */ SLIST_ENTRY(udf_log_vol) next_logvol; /* for list of logical volumes in a primary volume */ }; /* * Provides :: pri_vol -> [log_vol], [part],[ ...] * :: { volumeset -> [pri_vols] } */ struct udf_pri_vol { struct pri_vol_desc *pri_vol; struct udf_session *udf_session; struct impvol_desc *implemation; /* most likely reduntant */ struct udf_volumeset *volumeset; /* foreign ; nesissary? */ struct unalloc_sp_desc *unallocated; /* associated logical volumes */ SLIST_HEAD(logvols, udf_log_vol) log_vols; /* list of associated logical volumes */ STAILQ_ENTRY(udf_pri_vol) next_primary; /* for primary list in volumeset */ }; /* * Provides :: partion -> [partition info, session] */ struct udf_partition { struct part_desc *partition; struct udf_session *udf_session; /* foreign */ uint64_t part_offset; uint64_t part_length; UDF_MUTEX(partition_space_mutex); /* MUTEX for unalloc and freed space */ uint64_t free_unalloc_space; struct udf_alloc_entries unalloc_space_queue; /* authorative */ struct space_bitmap_desc *unalloc_space_bitmap; /* placeholder! does NOT have to be up-to-date */ uint64_t free_freed_space; struct udf_alloc_entries freed_space_queue; /* authorative */ struct space_bitmap_desc *freed_space_bitmap; /* placeholder! does NOT have to be up-to-date */ SLIST_ENTRY(udf_partition) next_partition; /* for partition list in volumeset */ }; /* * Provides :: volumeset -> [pri_vol] * :: [volumeset] */ struct udf_volumeset { int obsolete; uint32_t max_partnum; STAILQ_HEAD(primaries, udf_pri_vol) primaries; /* linked list of primary volumes associated */ SLIST_HEAD(parts, udf_partition) parts; /* linked list of partitions descriptors */ SLIST_ENTRY(udf_volumeset) next_volumeset; /* for volumeset list */ }; /* * Provides udf_session :: -> [(disc, anchor, tracknum)] */ struct udf_session { struct udf_discinfo *disc; struct anchor_vdp anchor; uint16_t session_num; uint32_t session_offset; uint32_t session_length; int writable; /* physical layer read/write cache */ UDF_MUTEX(session_cache_lock); /* SIMPLE cache */ uint32_t cache_line_r_start; uint32_t cache_line_r_present; uint8_t *cache_line_read; uint32_t cache_line_w_start; uint32_t cache_line_w_present; uint32_t cache_line_w_dirty; uint8_t *cache_line_write; struct udf_wrcallback cache_write_callbacks[UDF_READWRITE_LINE_LENGTH+1]; STAILQ_ENTRY(udf_session) next_session; /* sessions are added at tail to preserve order */ }; /*---------------------------------------------------------------------*/ /* exported functions */ extern int udf_check_tag(union dscrptr *dscr); extern int udf_check_tag_payload(union dscrptr *dscr); extern int udf_check_tag_presence(union dscrptr *dscr, int TAG); extern int udf_check_session_range(char *range); /* XXX new kernel like interface XXX */ extern int udf_read_file_part_uio(struct udf_node *udf_node, char *what, int cachehints, struct uio *data_uio); extern int udf_write_file_part_uio(struct udf_node *udf_node, char *what, int cachehints, struct uio *data_uio); extern void udf_dispose_udf_node(struct udf_node *udf_node); extern int udf_getattr(struct udf_node *udf_node, struct stat *stat); extern int udf_readdir(struct udf_node *udf_node, struct uio *result_uio, int *eof_res /* int *cookies, int ncookies */); //extern int udf_lookup_name_in_dir(struct udf_node *dir_node, struct udf_node **vnode, char *name); /* not fully VOP_LOOKUP yet */ extern int udf_lookup_name_in_dir(struct udf_node *dir_node, char *name, int namelen, struct long_ad *icb_loc, struct fileid_desc *fid, int *found); extern int udf_readin_udf_node(struct udf_node *dir_node, struct long_ad *udf_icbptr, struct fileid_desc *fid, struct udf_node **res_sub_node); extern int udf_sync_udf_node(struct udf_node *udf_node, char *why); /* writeout node */ extern int udf_truncate_node(struct udf_node *udf_node, uint64_t length /* ,ioflags */); extern int udf_remove_file(struct udf_node *parent_node, struct udf_node *udf_node, char *componentname); extern int udf_remove_directory(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname); extern int udf_create_file(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node); extern int udf_create_directory(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node); extern int udf_rename(struct udf_node *old_parent, struct udf_node *rename_me, char *old_name, struct udf_node *new_parent, struct udf_node *present, char *new_name); extern int udf_unlink_node(struct udf_node *udf_node); extern int udf_read_session_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *buffer, int prefetch_sectors, int rwflags); extern int udf_write_session_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *source, int rwflags, struct udf_wrcallback *wrcallback); extern int udf_read_logvol_sector(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, uint8_t *buffer, uint32_t prefetch_sectors, int rwflags); extern int udf_write_logvol_sector(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, uint8_t *buffer, int rwflags, struct udf_wrcallback *wrcallback); /* call back */ extern int udf_writeout_file_buffer(struct udf_node *udf_node, char *what, int rwflags, struct udf_buf *buf_entry); /* special cases like VRS */ extern int udf_write_session_cache_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *source, int flags, struct udf_wrcallback *wrcallback); /* device/disc opener, closer and read/write operations */ extern void udf_init(void); /* call me first! */ extern int udf_mount_disc(char *devname, char *range, uint32_t sector_size, int mnt_flags, struct udf_discinfo **disc); extern int udf_dismount_disc(struct udf_discinfo *disc); extern int udf_open_disc(char *devname, int discop_flags, struct udf_discinfo **disc); extern int udf_close_disc(struct udf_discinfo *disc); extern int udf_sync_disc(struct udf_discinfo *disc); extern int udf_sync_logvol(struct udf_log_vol *udf_log_vol); extern int udf_sync_caches(struct udf_log_vol *udf_log_vol); extern int udf_open_logvol(struct udf_log_vol *udf_log_vol); extern int udf_close_logvol(struct udf_log_vol *udf_log_vol); extern int udf_sync_logvol(struct udf_log_vol *udf_log_vol); /* readers/writers helper functions */ extern int udf_init_session_caches(struct udf_session *udf_session); extern int udf_writeout_udf_node(struct udf_node *udf_node, char *why); extern int udf_sync_space_tables(struct udf_log_vol *udf_log_vol); /* read comment on definition */ extern int udf_logvol_vpart_to_partition(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, struct udf_part_mapping **udf_part_mapping_ptr, struct udf_partition **udf_partition_ptr); extern int udf_vpartoff_to_sessionoff(struct udf_log_vol *udf_log_vol, struct udf_part_mapping *udf_part_mapping, struct udf_partition *udf_partition, uint64_t offset, uint64_t *ses_off, uint64_t *trans_valid_len); extern int udf_read_session_descriptor(struct udf_session *udf_session, uint32_t lb_num, char *what, union dscrptr **dscr, uint32_t *length); extern int udf_read_logvol_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, union dscrptr **dscr, uint32_t *length); extern int udf_write_session_descriptor(struct udf_session *udf_session, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback); extern int udf_write_partition_descriptor(struct udf_partition *udf_partition, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback); extern int udf_write_logvol_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback); /* exported text-dump functions */ extern void udf_dump_volume_name(char *prefix, struct udf_log_vol *udf_log_vol); extern void udf_dump_long_ad(char *prefix, struct long_ad *adr); extern void udf_dump_id(char *prefix, int len, char *id, struct charspec *chsp); extern void udf_to_unix_name(char *result, char *id, int len, struct charspec *chsp); /* exported descriptor creators */ extern int udf_validate_tag_sum(union dscrptr *dscr); extern int udf_validate_tag_and_crc_sums(union dscrptr *dscr); extern uint64_t udf_calc_tag_malloc_size(union dscrptr *dscr, uint32_t udf_sector_size); extern int udf_read_fid_stream(struct udf_node *dir_node, uint64_t *offset, struct fileid_desc *fid, struct dirent *dirent); extern void udf_resync_fid_stream(uint8_t *buffer, uint32_t *fid_pos, uint32_t max_fid_pos, int *fid_found); extern int udf_create_empty_anchor_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint32_t main_vds_loc, uint32_t reserve_vds_loc, uint32_t length, struct anchor_vdp **vdp); extern int udf_create_empty_primary_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *volset_id, char *privol_name, int vds_num, int max_vol_seq, struct pri_vol_desc **dscrptr); extern int udf_create_empty_partition_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, uint16_t part_num, uint32_t access_type, uint32_t start_loc, uint32_t part_len, uint32_t space_bitmap_size, uint32_t unalloc_space_bitmap, struct part_desc **dscrptr); extern int udf_create_empty_unallocated_space_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, struct unalloc_sp_desc **dscrptr); extern int udf_create_empty_implementation_use_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, struct impvol_desc **dscrptr); extern int udf_create_empty_logical_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, uint32_t lb_size, uint32_t integrity_start, uint32_t integrity_length, struct logvol_desc **dscrptr); extern int udf_create_empty_space_bitmap(uint32_t sector_size, uint16_t dscr_ver, uint32_t num_lbs, struct space_bitmap_desc **dscrptr); extern int udf_create_empty_terminator_descriptor(uint32_t sector_size, uint16_t dscr_ver, struct desc_tag **tag); extern int udf_create_empty_fileset_desc(uint32_t sector_size, uint16_t dscr_ver, uint32_t fileset_num, char *logvol_name, char *fileset_name, struct fileset_desc **dscrptr); extern void udf_add_physical_to_logvol(struct logvol_desc *logvol, uint16_t vol_seq_num, uint16_t phys_part_num); extern void udf_derive_new_logvol_integrity(struct udf_log_vol *udf_log_vol); /* time related creator functions */ extern void udf_set_timespec_now(struct timespec *timespec); extern void udf_set_timestamp_now(struct timestamp *timestamp); /* exported processing functions */ extern int udf_proc_pri_vol(struct udf_session *udf_session, struct udf_pri_vol **current, struct pri_vol_desc *incomming); extern int udf_proc_part(struct udf_pri_vol *primary, struct udf_partition **current, struct part_desc *incomming); extern int udf_proc_log_vol(struct udf_pri_vol *primary, struct udf_log_vol ** current, struct logvol_desc *incomming); extern int udf_proc_filesetdesc(struct udf_log_vol *udf_log_vol, struct fileset_desc *incomming); extern int udf_sync_space_bitmap(struct udf_alloc_entries *queue, struct space_bitmap_desc *sbd, uint32_t lb_size); /* exported builders */ extern int udf_init_udf_node(struct udf_mountpoint *mountpoint, struct udf_log_vol *udf_log_vol, char *what, struct udf_node **udf_nodeptr); extern void udf_insert_node_in_hash(struct udf_node *udf_node); extern int udf_allocate_udf_node_on_disc(struct udf_node *udf_node); extern void udf_node_mark_dirty(struct udf_node *udf_node); extern int udf_allocate_lbs(struct udf_log_vol *udf_log_vol, int content, uint32_t req_lbs, char *what, uint16_t *res_vpart_num, uint32_t *res_start_lb, uint32_t *res_num_lbs); extern int udf_node_allocate_lbs(struct udf_node *udf_node, int req_lbs, uint16_t *res_vpart_num, uint32_t *res_start_lb, uint32_t *res_num_lbs); extern int udf_release_lbs(struct udf_log_vol *udf_log_vol, uint16_t vpart_num, uint32_t lb_num, uint64_t size); extern int udf_node_release_extent(struct udf_node *udf_node, uint64_t from, uint64_t to); extern int udf_confirm_freespace(struct udf_log_vol *udf_log_vol, int content, uint64_t size); extern int udf_create_directory_entry(struct udf_node *dir_node, char *componentname, int filetype, int filechar, struct udf_node *refering, struct stat *stat, struct udf_node **new_node); extern int udf_unlink_node(struct udf_node *udf_node); extern uint64_t udf_increment_unique_id(struct udf_log_vol *udf_log_vol); /* exported (temp) allocentries */ extern void udf_merge_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size); extern int udf_cut_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t offset); extern void udf_dump_allocentry_queue(char *msg, struct udf_alloc_entries *queue, uint32_t lb_size); extern int udf_filepart_mark_extent(struct udf_node *udf_node, uint64_t data_offset, uint64_t data_length, int mark); extern int udf_splitup_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t data_offset, uint64_t data_length, struct udf_allocentry **res_firstae, struct udf_allocentry **res_lastae); extern int udf_mark_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t data_offset, uint64_t data_length, int mark, struct udf_allocentry **res_firstae, struct udf_allocentry **res_lastae); extern int udf_extent_properties(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t from, uint64_t to, int *res_all_allocated); /* define static list types and structures */ SLIST_HEAD(discslist, udf_discinfo); SLIST_HEAD(volumeset_list, udf_volumeset); SLIST_HEAD(mountables_list, udf_mountpoint); extern struct discslist udf_discs_list; extern struct volumeset_list udf_volumeset_list; extern struct mountables_list udf_mountables; #endif /* _UDF_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf_allocentries.c000066400000000000000000000261761523477242100276430ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf_allocentries.c" is part of the UDFclient toolkit. * File $Id: udf_allocentries.c,v 1.13 2016/04/25 21:01:40 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* XXX strip list to bare minimum XXX */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uscsilib.h" /* for locals */ #include "udf.h" #include "udf_bswap.h" #include "udf_discop.h" #include "uio.h" #include /* for scsilib */ extern const char *dvname; #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) # define MIN(a,b) ((a)<(b)?(a):(b)) #endif /* #define DEBUG(a) { a; } */ #define DEBUG(a) if (0) { a; } /* XXX TODO support non lb_size splits ?? TODO XXX */ /****************************************************************************************** * * Basic operations on udf_alloc_entries queues * ******************************************************************************************/ void udf_merge_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size) { struct udf_allocentry *alloc_entry, *next_alloc; uint64_t this_end, next_start; int merge; TAILQ_FOREACH(alloc_entry, queue, next_alloc) { do { merge = 0; /* only non busy ; prolly old cruft? */ if (alloc_entry->flags == UDF_SPACE_FREED) break; next_alloc = TAILQ_NEXT(alloc_entry, next_alloc); if (next_alloc) { if (next_alloc->flags != alloc_entry->flags) break; if (alloc_entry->flags == UDF_SPACE_ALLOCATED) { /* merge on virtual/physical lb_num base; they are automatically adjacent on offset */ if (next_alloc->vpart_num != alloc_entry->vpart_num) break; this_end = alloc_entry->lb_num * lb_size + alloc_entry->len; next_start = next_alloc->lb_num * lb_size; if (this_end != next_start) break; } /* Only merge if merge would result in a legal UDF allocation size */ if (((uint64_t) alloc_entry->len + (uint64_t) next_alloc->len) > ((uint64_t) 1<<30)-1) break; /* merge! */ alloc_entry->len = alloc_entry->len + next_alloc->len; TAILQ_REMOVE(queue, next_alloc, next_alloc); free(next_alloc); merge = 1; } } while (merge); } /* foreach */ } /* Splits up allocated pieces so that there is a break on `offset' */ int udf_cut_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t offset) { struct udf_allocentry *entry, *new_entry; uint64_t cur_offset; uint64_t total_length, extra_length, max_slot, new_length; uint64_t entry_offset; total_length = 0; TAILQ_FOREACH(entry, queue, next_alloc) { total_length += entry->len; } /* printf("Cutting up at offset %ld (lb %ld)\n", offset, offset / lb_size); */ if (offset < total_length) { /* split */ cur_offset = 0; TAILQ_FOREACH(entry, queue, next_alloc) { if ((offset >= cur_offset) && (offset < cur_offset + entry->len)) { /* overlap */ entry_offset = offset - cur_offset; entry_offset = (entry_offset / lb_size) * lb_size; assert(entry_offset % lb_size == 0); if (entry_offset == 0) return 0; /* clone the current space */ new_entry = calloc(1, sizeof(struct udf_allocentry)); if (!new_entry) return ENOMEM; memcpy(new_entry, entry, sizeof(struct udf_allocentry)); /* split! */ /* printf("split up lb %d + lb %d due to lb offset = %ld\n", entry->lb_num, entry->len / lb_size, entry_offset); */ entry->len = entry_offset; new_entry->len -= entry_offset; new_entry->lb_num += entry_offset / lb_size; TAILQ_INSERT_AFTER(queue, entry, new_entry, next_alloc); DEBUG(printf("split up due to lb offset = %d\n", (int) entry_offset)); return 0; } cur_offset += entry->len; } printf("Sanity check: i can't be here\n"); exit(1); } /* no use to do more if we're there */ if (offset == total_length) return 0; /* * Glue extra piece on the queue (auto-extending) * see if we reached our `goal'; see if we can just extent the last * allocation entry but NEVER more or equal to one block size for that * would alter semantics. */ entry = TAILQ_LAST(queue, udf_alloc_entries); if (!TAILQ_EMPTY(queue)) { extra_length = (uint64_t) lb_size*(((uint64_t) entry->len + lb_size -1) / lb_size) - entry->len; extra_length = MIN(extra_length, (offset - total_length)); /* keep semantics: only meant for extending upto blocksize */ if (extra_length < lb_size) { entry->len += extra_length; total_length += extra_length; } } max_slot = ((((uint64_t) 1<<30)-1) / lb_size) * lb_size; while (offset > total_length) { /* grow queue by adding difference as a zero unallocated space */ new_length = offset - total_length; new_length = MIN(max_slot, new_length); new_entry = calloc(1, sizeof(struct udf_allocentry)); if (!new_entry) return ENOMEM; new_entry->len = new_length; new_entry->flags = UDF_SPACE_FREE; TAILQ_INSERT_TAIL(queue, new_entry, next_alloc); total_length += new_entry->len; } /* while */ return 0; } /* Splits up allocated pieces so that there is a break on `data_offset' and on `data_offset + data_length' */ int udf_splitup_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t data_offset, uint64_t data_length, struct udf_allocentry **res_firstae, struct udf_allocentry **res_lastae) { struct udf_allocentry *entry, *prev_entry; uint64_t cur_offset, len; entry = prev_entry = NULL; #if 0 printf("Split %ld + %ld : \n", data_offset, data_length); printf("PRE SPLIT block = lb %d + lb %d (%ld bytes)\n", (int) (data_offset / lb_size), (int) (data_length / lb_size), data_length); TAILQ_FOREACH(entry, queue, next_alloc) { printf("\t(lb %08d + lb %08d[+ %d]) flag %d\n", entry->lb_num, entry->len/lb_size, entry->len % lb_size, entry->flags); } printf("END PRE\n"); #endif /* cut the string at the specified places */ (void) udf_cut_allocentry_queue(queue, lb_size, data_offset); (void) udf_cut_allocentry_queue(queue, lb_size, data_offset + data_length); #if 0 printf("POST SPLIT\n"); TAILQ_FOREACH(entry, queue, next_alloc) { printf("\t(lb %08d + lb %08d[+ %d]) flag %d\n", entry->lb_num, entry->len/lb_size, entry->len % lb_size, entry->flags); } printf("END POST\n\n"); #endif if ((res_firstae == NULL) && (res_lastae == NULL)) return 0; if (res_firstae) *res_firstae = NULL; if (res_lastae) *res_lastae = NULL; DEBUG(printf("SEARCH SPLIT block = %"PRIu64" + %"PRIu64"\n", (data_offset / lb_size), (data_length / lb_size))); /* search the element-range this splitting induced */ cur_offset = 0; entry = TAILQ_FIRST(queue); while (entry) { len = entry->len; DEBUG(printf("\t(%d + %d) flag %d\n", (int) entry->lb_num, (int) entry->len, (int) entry->flags)); if (cur_offset + len > data_offset) { if (res_firstae) *res_firstae = entry; DEBUG(printf("\t\tReturned as first\n")); break; } /* advance */ cur_offset += len; entry = TAILQ_NEXT(entry, next_alloc); } prev_entry = entry; while (entry) { len = entry->len; if (cur_offset + len > data_offset + data_length) { break; } DEBUG(printf("\t(%d + %d) flag %d\n", (int) entry->lb_num, (int) entry->len, (int) entry->flags)); /* advance */ cur_offset += len; prev_entry = entry; entry = TAILQ_NEXT(entry, next_alloc); } if (res_lastae) *res_lastae = prev_entry; DEBUG(printf("\t\tReturned as last\n\t\n")); DEBUG(printf("END POST\n\n")); if (res_firstae) assert(*res_firstae); if (res_lastae) assert(*res_lastae); return 0; } /* mark a piece with the specified `mark' with no side-effects */ /* tested OK */ int udf_mark_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t data_offset, uint64_t data_length, int mark, struct udf_allocentry **res_firstae, struct udf_allocentry **res_lastae) { struct udf_allocentry *alloc_entry, *first_alloc_entry, *last_alloc_entry; int error; DEBUG(printf("mark %d\n", mark)); /* first split up so we don't have to worry about boundaries */ error = udf_splitup_allocentry_queue(queue, lb_size, data_offset, data_length, &first_alloc_entry, &last_alloc_entry); assert(error == 0); alloc_entry = first_alloc_entry; /* inclusive last_alloc_entry */ last_alloc_entry = TAILQ_NEXT(last_alloc_entry, next_alloc); while (alloc_entry != last_alloc_entry) { DEBUG(printf("marking %d + %d into type %d\n", alloc_entry->lb_num, alloc_entry->len/lb_size, mark);) alloc_entry->flags = mark; alloc_entry = TAILQ_NEXT(alloc_entry, next_alloc); } if (res_firstae) *res_firstae = first_alloc_entry; if (res_lastae) *res_lastae = last_alloc_entry; return 0; } int udf_extent_properties(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t from, uint64_t to, int *res_all_allocated) { struct udf_allocentry *alloc_entry, *first_alloc_entry, *last_alloc_entry; int all_allocated, error; /* first split up so we don't have to worry about boundaries */ error = udf_splitup_allocentry_queue(queue, lb_size, from, to-from, &first_alloc_entry, &last_alloc_entry); assert(error == 0); /* inclusive last_alloc_entry */ alloc_entry = first_alloc_entry; last_alloc_entry = TAILQ_NEXT(last_alloc_entry, next_alloc); all_allocated = 1; while (alloc_entry != last_alloc_entry) { all_allocated = all_allocated && ((alloc_entry->flags == UDF_SPACE_ALLOCATED) || (alloc_entry->flags == UDF_SPACE_ALLOCATED_BUT_NOT_USED)); alloc_entry = TAILQ_NEXT(alloc_entry, next_alloc); } if (res_all_allocated) *res_all_allocated = all_allocated; return 0; } void udf_dump_allocentry_queue(char *msg, struct udf_alloc_entries *queue, uint32_t lb_size) { struct udf_allocentry *entry; uint64_t offset; printf("\n%s :", msg); offset = 0; TAILQ_FOREACH(entry, queue, next_alloc) { printf(" [%d : lb %08d till lb %08d] mapped on (lb %d + %d bytes) ", entry->flags, (uint32_t) (offset/lb_size), (uint32_t) (offset + entry->len)/lb_size-1, (uint32_t) (entry->lb_num/lb_size), (uint32_t) entry->len); offset += entry->len; } printf("\n"); } /* end of udf_allocentries.c */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf_bmap.c000066400000000000000000000326421523477242100260710ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf_bmap.c" is part of the UDFclient toolkit. * File $Id: udf_bmap.c,v 1.28 2016/04/25 21:01:40 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* XXX strip list to bare minimum XXX */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uscsilib.h" /* for locals */ #include "udf.h" #include "udf_bswap.h" #include "udf_discop.h" #include "uio.h" #include #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) # define MIN(a,b) ((a)<(b)?(a):(b)) #endif //#define DEBUG(a) { a; } #define DEBUG(a) if (0) { a; } void udf_dump_allocentry_queue(char *msg, struct udf_alloc_entries *queue, uint32_t lb_size); /****************************************************************************************** * * Get and release free logical blocks from the free space tables * ******************************************************************************************/ /* * simple metadata distribution algorithm; first part of each block is * metadata followed by data; the offset this blocking can be tweaked by * part_offset. */ static int udf_allocate_lbs_on_rewritables(struct udf_partition *udf_partition, uint64_t part_offset, uint64_t metadata_granularity, uint32_t lb_size, uint64_t req_size, int content, uint32_t *res_start_lb, uint32_t *res_num_lbs) { struct udf_allocentry *alloc_entry, *chosen; struct udf_alloc_entries *queue; uint64_t start; uint64_t chosen_offset; uint64_t size; int error, is_meta; /* allways unalloc_space_queue ? */ queue = &udf_partition->unalloc_space_queue; /* TODO this is a simplification; no multiple queues yet XXX */ is_meta = (content != UDF_C_USERDATA); /* start from the beginning of the unallocated space queue */ alloc_entry = TAILQ_FIRST(queue); start = alloc_entry->lb_num; assert(start == 0); chosen = NULL; chosen_offset = 0; size = 0; TAILQ_FOREACH(alloc_entry, queue, next_alloc) { /* we can only start on lb_size (extra sanity check) */ assert(start % lb_size == 0); /* piecewise if nessisary */ size = MIN(alloc_entry->len, req_size); size = (uint64_t) lb_size * (size / lb_size); if (size == 0) continue; /* we can only give out on lb_size boundaries */ assert(size % lb_size == 0); if (alloc_entry->flags == UDF_SPACE_FREE) { DEBUG(printf("got free entry from %"PRIu64" + %"PRIu64"\n", start / lb_size, (start + alloc_entry->len)/lb_size)); if (is_meta) { if (start + req_size + part_offset <= metadata_granularity) { chosen = alloc_entry; chosen_offset = 0; } } else { chosen_offset = 0; if (start + part_offset < metadata_granularity) { chosen_offset = metadata_granularity - (start + part_offset); } if (chosen_offset + size <= alloc_entry->len) { chosen = alloc_entry; } } DEBUG(if (chosen && (size > 0)) printf("chosen\n")); } if (size <= 0) chosen = NULL; if (chosen) break; /* foreach */ start = start + alloc_entry->len; } /* FOREACH */ if (!chosen) return ENOSPC; assert(size > 0); assert(size % lb_size == 0); assert(chosen->len % lb_size == 0); assert(start + chosen_offset + size <= start + chosen->len); start += chosen_offset; *res_start_lb = start / lb_size; *res_num_lbs = size / lb_size; error = udf_mark_allocentry_queue(queue, lb_size, start, size, UDF_SPACE_ALLOCATED, NULL, NULL); return error; } /* get at most req_lbs from the partition and mark the area used */ static int udf_allocate_lbs_on_partition(struct udf_log_vol *udf_log_vol, uint16_t vpart_num, uint32_t req_lbs, int content, uint32_t *res_start_lb, uint32_t *res_num_lbs) { struct udf_partition *udf_partition; struct udf_part_mapping *udf_part_mapping; struct udf_alloc_entries *queue; uint64_t part_length; uint64_t metadata_granularity, metadata_blk_len; uint64_t part_offset; uint32_t lb_size, num_lbs; uint64_t req_size; uint32_t cnt; int error; assert(udf_log_vol); lb_size = udf_log_vol->lb_size; error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition); if (error) return error; // part_start = (uint64_t) lb_size * udf_rw32(udf_partition->partition->start_loc); part_length = (uint64_t) lb_size * udf_rw32(udf_partition->partition->part_len); req_size = (uint64_t) lb_size * req_lbs; /* lock against corruption with free */ UDF_MUTEX_LOCK(&udf_partition->partition_space_mutex); switch (udf_part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_PHYSICAL : case UDF_PART_MAPPING_SPARABLE : /* sparable is just like the physical mapping */ /* allways unalloc_space_queue ? */ queue = &udf_partition->unalloc_space_queue; udf_merge_allocentry_queue(queue, lb_size); DEBUG(udf_dump_allocentry_queue("Alloc ", queue, lb_size)); metadata_granularity = part_length / 1024; metadata_blk_len = metadata_granularity / 16; part_offset = 0; for (cnt = 0; cnt <= (metadata_granularity / metadata_blk_len); cnt++) { num_lbs = 0; error = udf_allocate_lbs_on_rewritables(udf_partition, part_offset, metadata_granularity, lb_size, req_size, content, res_start_lb, &num_lbs); if (error) { assert(error == ENOSPC); /* disc is most likely getting full with (meta) data; shift window */ part_offset += metadata_blk_len; num_lbs = 0; } else { assert(num_lbs >= 1); udf_partition->free_unalloc_space -= num_lbs * lb_size; udf_log_vol->free_space -= num_lbs * lb_size; break; /* for */ } } *res_num_lbs = num_lbs; break; case UDF_PART_MAPPING_VIRTUAL : /* strict increasing virtual adress giveout */ printf("UDF: get lbs from virtual partition mapping not implemented yet\n"); return EBADF; case UDF_PART_MAPPING_META : /* select blobs from the metadata file */ if (udf_part_mapping->meta_bitmap_file == NULL) return EROFS; printf("UDF: get lbs from metadata partition mapping not implemented yet\n"); break; case UDF_PART_MAPPING_PSEUDO_RW : /* strict increasing adress giveout from the pseudo over. track */ printf("UDF: get lbs from pseudo overwritable partition not implemented yet\n"); break; } /* sanity */ if (*res_num_lbs == 0) *res_start_lb = 0; /* release partition unalloced and freed space again */ UDF_MUTEX_UNLOCK(&udf_partition->partition_space_mutex); return error; } /****************************************************************************************** * * Upper level free space allocation and releasing. * ******************************************************************************************/ int udf_allocate_lbs(struct udf_log_vol *udf_log_vol, int content, uint32_t req_lbs, char *what, uint16_t *res_vpart_num, uint32_t *res_start_lb, uint32_t *res_num_lbs) { struct udf_partition *udf_partition; struct udf_part_mapping *udf_part_mapping; uint32_t num_lbs; uint16_t vpart_num; int is_meta, ok; int error; assert(udf_log_vol); num_lbs = 0; /* shutup gcc */ /* select udf partition to write to depending on the contents */ is_meta = ((content == UDF_C_FIDS) || (content == UDF_C_NODE)); vpart_num = is_meta ? udf_log_vol->metadata_vpart : udf_log_vol->data_vpart; /* TODO what about when space is freed in an earlier partition ? reset the vpart's ? */ DEBUG(printf("UDF allocate %d lbs for node `%s`\n", req_lbs, what)); do { DEBUG(printf("do: vpart_num = %d\n", vpart_num)); error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition); ok = (udf_partition != NULL) && (udf_part_mapping != NULL); if (ok && !is_meta) ok = udf_part_mapping->data_writable; if (ok && is_meta) ok = udf_part_mapping->metadata_writable; if (ok) { /* try to snoop from this vpart */ error = udf_allocate_lbs_on_partition(udf_log_vol, vpart_num, req_lbs, content, res_start_lb, &num_lbs); if (!error) { *res_vpart_num = vpart_num; if (res_num_lbs) *res_num_lbs = num_lbs; } ok = !error; } if (!ok) { DEBUG(printf("advance vpart\n")); vpart_num = is_meta ? ++(udf_log_vol->metadata_vpart) : ++(udf_log_vol->data_vpart); if (vpart_num >= udf_log_vol->num_part_mappings) { udf_log_vol->metadata_vpart = udf_log_vol->data_vpart = 0; printf("UDF: logvol discs full ?\n"); return ENOSPC; } } } while (!ok); DEBUG(printf("udf_allocate_lbs: got space on vpart_num = %d; req %d, got %d\n", vpart_num, req_lbs, num_lbs)); assert((*res_start_lb != 0) && (num_lbs != 0)); return 0; } int udf_node_allocate_lbs(struct udf_node *udf_node, int req_lbs, uint16_t *res_vpart_num, uint32_t *res_start_lb, uint32_t *res_num_lbs) { char *what; int content; /* content can be USERDATA or FID stream here; checking on udf filetype */ switch (udf_node->udf_filetype) { case UDF_ICB_FILETYPE_DIRECTORY : case UDF_ICB_FILETYPE_STREAMDIR : content = UDF_C_FIDS; what = "FID stream"; break; default : content = UDF_C_USERDATA; what = "file content"; break; } return udf_allocate_lbs(udf_node->udf_log_vol, content, req_lbs, what, res_vpart_num, res_start_lb, res_num_lbs); } /* returns non zero if space is available */ int udf_confirm_freespace(struct udf_log_vol *udf_log_vol, int content, uint64_t size) { content = content; /* not used now */ /* generic check for now */ if (udf_log_vol->free_space >= udf_log_vol->await_alloc_space + size + UDF_MINFREE_LOGVOL) { return 1; } return 0; } int udf_release_lbs(struct udf_log_vol *udf_log_vol, uint16_t vpart_num, uint32_t lb_num, uint64_t size) { struct udf_partition *udf_partition; struct udf_part_mapping *udf_part_mapping; struct udf_alloc_entries *queue; uint32_t lb_size; int error; if (!udf_log_vol) return 0; lb_size = udf_log_vol->lb_size; error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition); if (error) return error; /* space can only be freed in lb_size chuncks by definition */ size = (uint64_t) lb_size * ((size + lb_size -1) / lb_size); switch (udf_part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_PHYSICAL : case UDF_PART_MAPPING_SPARABLE : /* sparable is just like the physical mapping */ queue = &udf_partition->unalloc_space_queue; /* TODO freed- for non sequential MO media */ UDF_MUTEX_LOCK(&udf_partition->partition_space_mutex); error = udf_mark_allocentry_queue(queue, lb_size, (uint64_t) lb_num * lb_size, size, UDF_SPACE_FREE, NULL, NULL); udf_partition->free_unalloc_space += size; udf_log_vol->free_space += size; UDF_MUTEX_UNLOCK(&udf_partition->partition_space_mutex); return error; case UDF_PART_MAPPING_VIRTUAL : /* freeing is not applicable */ return 0; case UDF_PART_MAPPING_META : /* free blobs in the metadata file */ printf("UDF: freeing lbs from metadata partition mapping not implemented yet\n"); break; case UDF_PART_MAPPING_PSEUDO_RW : /* do we have to keep a space bitmap? */ printf("UDF: freeing lbs from pseudo rewritable partition mapping not implemented yet\n"); break; } return 0; } /* !!! needs to be called with alloc entry mutex held !!! */ int udf_node_release_extent(struct udf_node *udf_node, uint64_t from, uint64_t to) { struct udf_allocentry *from_ae, *to_ae, *alloc_entry, *last_alloc_entry; uint32_t lb_size, lbnum, len; uint16_t vpart; int error, flags; assert(udf_node->alloc_mutex.locked); assert(udf_node->udf_log_vol); lb_size = udf_node->udf_log_vol->lb_size; error = udf_splitup_allocentry_queue(&udf_node->alloc_entries, lb_size, from, to-from, &from_ae, &to_ae); if (!error) { alloc_entry = from_ae; last_alloc_entry = TAILQ_NEXT(to_ae, next_alloc); while (alloc_entry != last_alloc_entry) { vpart = alloc_entry->vpart_num; lbnum = alloc_entry->lb_num; flags = alloc_entry->flags; len = alloc_entry->len; if (flags == UDF_SPACE_ALLOCATED) { error = udf_release_lbs(udf_node->udf_log_vol, vpart, lbnum, len); assert(!error); alloc_entry->flags = UDF_SPACE_FREE; /* WORM: or freed? */ } else { DEBUG(printf("udf_filepart_free_extent :freeing a non allocated piece : flags = %d\n", flags)); } alloc_entry = TAILQ_NEXT(alloc_entry, next_alloc); } } else { fprintf(stderr, "udf_filepart_free_extent: splitup failed\n"); } return 0; } /* end of udf_bmap.c */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf_bswap.h000066400000000000000000000077001523477242100262700ustar00rootroot00000000000000/* $NetBSD: udf_bswap.h,v 1.12 2003/04/16 14:27:03 yamt Exp $ */ /* * Copyright (c) 1998 Manuel Bouyer. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * `ported' for UDF by Reinoud Zandijk * */ #ifndef _UDF_BSWAP_H_ #define _UDF_BSWAP_H_ #if HAVE_ENDIAN_H #include #else #if HAVE_SYS_ENDIAN_H #include #else #if HAVE_MACHINE_ENDIAN_H #include #endif #endif #endif /* rest only relevant for big endian machines */ #if (BYTE_ORDER == BIG_ENDIAN) //#warning BIG ENDIAN //#if (BYTE_ORDER == LITTLE_ENDIAN) /* ENDIAN SWAP... only for testing!!!!! */ /* inlines for access to swapped data */ static __inline uint16_t udf_rw16 __P((uint16_t)); static __inline uint32_t udf_rw32 __P((uint32_t)); static __inline uint64_t udf_rw64 __P((uint64_t)); #ifdef HAVE_SYS_BSWAP_H #include #endif #ifdef HAVE_MACHINE_BSWAP_H #include #endif #if (defined(HAVE_SYS_BSWAP_H) || defined(HAVE_MACHINE_BSWAP_H)) /* bswap macro's defined */ // #warning BSWAP's defined static __inline uint16_t udf_rw16(a) uint16_t a; { return bswap16(a); } static __inline uint32_t udf_rw32(a) uint32_t a; { return bswap32(a); } static __inline uint64_t udf_rw64(a) uint64_t a; { return bswap64(a); } #else /* no bswap macro's */ // #warning _NO_ BSWAP's defined union _bswap_data { uint16_t u16; uint32_t u32; uint64_t u64; uint8_t b[8]; }; static __inline uint16_t udf_rw16(uint16_t a) { union _bswap_data b; b.u16 = a; return b.b[0] | (b.b[1] << 8); } static __inline uint32_t udf_rw32(uint32_t a) { union _bswap_data b; b.u32 = a; return b.b[0] | (b.b[1] << 8) | (b.b[2] << 16) | (b.b[3] << 24); } static __inline uint64_t udf_rw64(uint64_t a) { union _bswap_data b; uint32_t low, high; b.u64 = a; high = b.b[4] | (b.b[5] << 8) | (b.b[6] << 16) | (b.b[7] << 24); low = b.b[0] | (b.b[1] << 8) | (b.b[2] << 16) | (b.b[3] << 24); return ((uint64_t) low) | ((uint64_t) high) << 32; } #endif /* bswap macro's */ #else /* little endian */ #define udf_rw16(a) ((uint16_t)(a)) #define udf_rw32(a) ((uint32_t)(a)) #define udf_rw64(a) ((uint64_t)(a)) #endif #define udf_add16(a, b) \ (a) = udf_rw16(udf_rw16((a)) + (b)) #define udf_add32(a, b) \ (a) = udf_rw32(udf_rw32((a)) + (b)) #define udf_add64(a, b) \ (a) = udf_rw64(udf_rw64((a)) + (b)) #endif /* !_UDF_BSWAP_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf_discop.c000066400000000000000000001267431523477242100264410ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf_discinfo.c" is part of the UDFclient toolkit. * File $Id: udf_discop.c,v 1.82 2022/02/06 14:30:49 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* XXX strip this XXX */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uscsilib.h" /* for locals */ #include "uscsilib.h" #include "udf_discop.h" #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) # define MIN(a,b) ((a)<(b)?(a):(b)) #endif /* #define DEBUG(a) { a; } */ #define DEBUG(a) if (0) { a; } /* globals */ /****************************************************************************************** * * Tables and helper functions section * ******************************************************************************************/ int read_cd_hex2(int val) { int nl, nh; nl = val & 15; nh = val >> 4; if (nl >= 'A') nl -= 'A' + 10; if (nh >= 'A') nh -= 'A' + 10; return (nh*16) + nl; } int read_cd_bcd(int val) { int nl, nh; nl = (val & 15) - '0'; nh = (val >> 4) - '0'; if ((nl < 0 || nl > 9) || (nh < 0 || nh > 9)) return val; return nh*10 + nl; } int32_t cd_msf2lba(int h, int m, int s, int f) { return 270000*h + 4500*m + 75*s + f - 150; } /****************************************************************************************** * * Disc level operations * ******************************************************************************************/ int udf_discinfo_is_cd_or_dvd(struct udf_discinfo *disc) { /* check device type */ switch (disc->devdrv_class & UDF_DEVDRV_CLASS) { case UDF_DEVDRV_CLASS_FILE : case UDF_DEVDRV_CLASS_DISC : /* not nessisary */ return 0; case UDF_DEVDRV_CLASS_CD : case UDF_DEVDRV_CLASS_DVD : /* it really is */ return 1; default : break; } return ENODEV; } int udf_discinfo_check_disc_ready(struct udf_discinfo *disc) { scsicmd cmd; uint8_t buf[36]; int error; if (!udf_discinfo_is_cd_or_dvd(disc)) return 1; bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0; /* test unit ready */ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 6, buf, 0, 30000, NULL); return (error == 0); } #define blk_len 10000 int udf_discinfo_set_recording_parameters(struct udf_discinfo *discinfo, int testwriting) { scsicmd cmd; uint8_t res[blk_len]; uint8_t *pos; uint32_t blockingnr; int val_len, packet; int error; if (!udf_discinfo_is_cd_or_dvd(discinfo)) return 0; /* Set up CD/DVD recording parameters */ if (!discinfo->recordable) return 0; blockingnr = discinfo->blockingnr; DEBUG(printf("Setting device's recording parameters\n")); packet = discinfo->packet; val_len = 0x32+2+8; bzero(res, val_len); pos = res + 8; bzero(cmd, SCSI_CMD_LEN); if (!packet) { pos[ 0] = 0x05; /* page code 5 : cd writing */ pos[ 1] = 0x32; /* length in bytes */ pos[ 2] = 64 + 0; /* BUFE + write type 1 : track at once */ if (testwriting) pos[ 2] += 16; pos[ 3] = (3<<6) | 5; /* next session OK, data packet, rec. incr. var packets */ pos[ 4] = 8; /* ISO mode 1 */ pos[ 8] = 0; /* normal CD-DA/CD-ROM or data disc */ DEBUG(printf("\tsetting up for sequential writing\n")); } else { pos[ 0] = 0x05; /* page code 5 : cd writing */ pos[ 1] = 0x32; /* length in bytes */ pos[ 2] = 0; /* write type 0 : packet/incremental */ if (testwriting) pos[ 2] += 16; pos[ 3] = (3<<6) | 32 | 5; /* next session OK, data packet, rec. incr. fixed packets */ pos[ 4] = 10; /* ISO mode 2; XA form 1 */ pos[ 8] = 0x20; /* CD-ROM XA disc or DDCD disc */ pos[10] = (blockingnr >> 24) & 0xff; /* MSB packet size */ pos[11] = (blockingnr >> 16) & 0xff; pos[12] = (blockingnr >> 8) & 0xff; pos[13] = (blockingnr ) & 0xff; /* LSB packet size in SECTORS */ DEBUG(printf("\tsetting up for packet writing with packet size %d\n", blockingnr)); } bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x55; /* MODE SELECT (10) */ cmd[1] = 16; /* PF format */ cmd[7] = val_len >> 8; /* length of blob */ cmd[8] = val_len & 0xff; cmd[9] = 0; /* control */ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, res, val_len, 3000, NULL); if (error) { perror("While WRTITING parameter page 5"); return error; } #if 0 /* Set CD/DVD speed to 'optimal' for it doesnt seem to do it automatically */ bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0xBB; /* Set CD speed */ cmd[ 1] = 1; /* select CAV (1) or CLV (0) recording */ cmd[ 2] = 0xff; cmd[ 3] = 0xff; /* max read performance speed */ cmd[ 4] = 0xff; cmd[ 5] = 0xff; /* max write performance speed; applic? */ cmd[11] = 0; /* control */ error = scsi_call(SCSI_WRITECMD, discinfo, cmd, 12, NULL, 0, NULL); if (error) { /* CAV not possible? then go for CLV */ cmd[ 1] = 0; /* select CAV (1) or CLV (0) recording */ error = scsi_call(SCSI_WRITECMD, discinfo, cmd, 12, NULL, 0, NULL); if (error) { perror("While setting speed"); return error; } } #endif /* flag OK */ return 0; } #undef blk_len int udf_discinfo_synchronise_caches(struct udf_discinfo *discinfo) { scsicmd cmd; int error; /* bail out when we're in sequential emulation */ if (!udf_discinfo_is_cd_or_dvd(discinfo)) return 0; bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0x35; /* Synchronise cache */ cmd[ 9] = 0; /* control */ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL); if (error) { perror("While synchronising write cache"); } return error; } /* * important: it must be called after write operations before read operations * are allowed again. When its allready finished with writing this call has no * effect and can be called at start to make sure the device knows that we're * going to read. */ int udf_discinfo_finish_writing(struct udf_discinfo *discinfo) { int error; if (!udf_discinfo_is_cd_or_dvd(discinfo)) return 0; error = udf_discinfo_synchronise_caches(discinfo); return error; } int udf_discinfo_reserve_track_in_logic_units(struct udf_discinfo *discinfo, uint32_t logic_units) { scsicmd cmd; int error; if (!udf_discinfo_is_cd_or_dvd(discinfo)) return ENODEV; bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0x53; /* reserve track */ cmd[ 5] = (logic_units >> 24) & 0xff; /* size MSB */ cmd[ 6] = (logic_units >> 16) & 0xff; cmd[ 7] = (logic_units >> 8) & 0xff; cmd[ 8] = (logic_units ) & 0xff; /* size LSB */ cmd[ 9] = 0; /* control */ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL); return error; } int udf_discinfo_close_track(struct udf_discinfo *discinfo, uint16_t trackno) { scsicmd cmd; int error; if (!udf_discinfo_is_cd_or_dvd(discinfo)) return ENODEV; bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0x5B; /* close session/track */ cmd[ 2] = 1; /* track */ cmd[ 4] = (trackno >> 8) & 0xff; /* specify trackno MSB */ cmd[ 5] = (trackno ) & 0xff; /* trackno LSB */ cmd[ 9] = 0; /* control */ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL); return error; } /* can only close last session */ int udf_discinfo_close_session(struct udf_discinfo *discinfo) { scsicmd cmd; int error; if (!udf_discinfo_is_cd_or_dvd(discinfo)) return ENODEV; bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0x5B; /* close session/track */ cmd[ 2] = 2; /* session */ cmd[ 9] = 0; /* control */ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL); return error; } /* * Repair a damaged track when suspected. It'll try to make it writable again. * A track can be broken when sudden stops are made and the track end is left * in a inconsistent state in the ATIP/PMA/TOC. */ int udf_discinfo_repair_track(struct udf_discinfo *discinfo, uint16_t trackno) { scsicmd cmd; int error; if (!udf_discinfo_is_cd_or_dvd(discinfo)) return ENODEV; bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0x58; /* repair track */ cmd[ 4] = (trackno >> 8) & 0xff; /* specify trackno MSB */ cmd[ 5] = (trackno ) & 0xff; /* trackno LSB */ cmd[ 9] = 0; /* control */ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL); return error; } /* * This routine 'get disc type' tries to get operational information from the * disc/drive combination and its capabilities. Fills in devdrv_class, MMC * profile and the various flags; no track info. */ int udf_discinfo_get_disc_type(struct udf_discinfo *disc) { struct stat stat; scsicmd cmd; const int feat_tbl_len = 0x200; uint8_t buf[256]; uint8_t features[feat_tbl_len], *rpos, *fpos; uint32_t pos, features_len, val_len; uint32_t feature, last_feature; uint32_t feature_ver, feature_pers, feature_cur, feature_len; int error; /* assume generic CD-ROM with no known MMC profile */ disc->devdrv_class = UDF_DEVDRV_CLASS_CD; disc->mmc_profile = 0; /* check if its a regular file */ fstat(disc->dev->fhandle, &stat); if (S_ISREG(stat.st_mode)) { UDF_VERBOSE(printf("UDF device %s is a regular file\n", disc->dev->dev_name)); disc->devdrv_class = UDF_DEVDRV_CLASS_FILE; disc->sequential = 0; /* full r/w */ disc->recordable = 1; /* assuming rw access */ disc->blankable = 0; /* not applicable */ disc->rewritable = 1; disc->packet = 0; disc->blockingnr = 1; disc->strict_overwrite = 0; disc->sector_size = DISC_SECTOR_SIZE; return 0; } /* check if its a ATAPI/SCSI device */ error = uscsi_check_for_scsi(disc->dev); if (error) { /* obviously no ATIPI/SCSI device -> has to be IDE disc or other but no file either */ disc->devdrv_class = UDF_DEVDRV_CLASS_DISC; disc->sequential = 0; /* full r/w */ disc->recordable = 1; /* assuming rw access */ disc->blankable = 0; /* not applicable */ disc->rewritable = 1; disc->packet = 0; disc->blockingnr = 1; disc->strict_overwrite = 0; disc->sector_size = DISC_SECTOR_SIZE; UDF_VERBOSE(printf("Got error executing SCSI command, assuming IDE disc\n")); return 0; } /* check if its a SCSI disc -> if so, do NOT issue mmc profile check */ bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x12; /* INQUIRY */ cmd[1] = 0; /* basic inquiry */ cmd[2] = 0; /* no page or operation code */ cmd[3] = 0; /* reserved/MSB result */ cmd[4] = 96; /* all but vendor specific */ cmd[5] = 0; /* control */ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 6, buf, 96, 30000, NULL); if (error) { fprintf(stderr, "Device claims to be SCSI but does NOT respond to inquiry!\n"); return ENOENT; } disc->scsi_device_type = buf[0] & 0x1f; switch (disc->scsi_device_type) { case 0x05 : break; /* MMC */ case 0x00 : /* Direct access device */ case 0x0E : /* Simplified direct access device */ /* read-write possible */ disc->devdrv_class = UDF_DEVDRV_CLASS_DISC; disc->sequential = 0; /* full r/w */ disc->recordable = 1; /* assuming rw access */ disc->blankable = 0; /* not applicable */ disc->rewritable = 1; disc->packet = 0; disc->blockingnr = 1; disc->strict_overwrite = 0; disc->sector_size = DISC_SECTOR_SIZE; return 0; case 0x04 : case 0x07 : /* Non MMC read only optical media */ disc->devdrv_class = UDF_DEVDRV_CLASS_DISC; disc->sequential = 0; /* */ disc->recordable = 0; /* assuming ro access */ disc->blankable = 0; /* not applicable */ disc->rewritable = 0; disc->packet = 0; disc->blockingnr = 1; disc->strict_overwrite = 0; disc->sector_size = DISC_SECTOR_SIZE; return 0; default: fprintf(stderr, "Device type 0x%02x not suitable for mass storage\n", disc->scsi_device_type); return ENOENT; } /* get MMC profile */ bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0x46; /* Get configuration */ cmd[ 8] = 32; /* just a small buffer size */ cmd[ 9] = 0; /* control */ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, buf, 32, 30000, NULL); if (!error) { disc->mmc_profile = buf[7] | (buf[6] << 8); } else { disc->mmc_profile = 0; /* mark unknown MMC profile */ } UDF_VERBOSE_MAX(printf("Device has MMC profile 0x%02x\n", disc->mmc_profile)); /* determine CD sector size */ bzero(buf, 8); bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x25; /* CD READ RECORDED CAPACITY */ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, buf, 8, 30000, NULL); if (error) { fprintf(stderr, "Can't read CD recorded capacity; assuming sector size %d : %s", CD_SECTOR_SIZE, strerror(error)); disc->sector_size = CD_SECTOR_SIZE; } else { disc->sector_size = buf[7] | (buf[6]<<8) | (buf[5]<<16) | (buf[4]<<24); /* packet size is recorded for each track seperately */ } /* * Read in features to determine device flags. First take some initial * values. */ disc->sequential = 0; disc->recordable = 0; disc->erasable = 0; disc->blankable = 0; disc->formattable = 0; disc->rewritable = 0; disc->mrw = 0; disc->packet = 0; disc->strict_overwrite = 0; disc->blockingnr = 1; /* not relevant if non packet write */ last_feature = feature = 0; do { bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x46; /* Get configuration */ cmd[1] = 0; /* RT=0 -> all independent of current setting */ cmd[2] = (last_feature) >> 8; /* MSB feature number */ cmd[3] = (last_feature) & 0xff; /* LSB feature number */ cmd[7] = (feat_tbl_len) >> 8; /* MSB buffersize */ cmd[8] = (feat_tbl_len) & 0xff; /* LSB buffersize */ cmd[9] = 0; /* control */ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, features, feat_tbl_len, 30000, NULL); if (error) { fprintf(stderr, "While reading feature table : %s\n", strerror(error)); return EIO; } features_len = features[3] | (features[2]<<8) | (features[1]<<16) | (features[0]<<24); disc->mmc_profile = features[7] | (features[6]<<8); pos = 8; while (pos < features_len) { fpos = &features[pos]; feature = fpos[1] | (fpos[0] << 8); feature_ver = (fpos[2] >> 2) & 15; feature_cur = (fpos[2] & 1); feature_pers= (fpos[2] & 2); feature_len = fpos[3]; if (feature_cur == 1) { rpos = &fpos[4]; switch (feature) { case 0x0010 : /* random readable feature */ disc->sector_size = rpos[3] | (rpos[2] << 8) | (rpos[1] << 16) | (rpos[0] << 24); disc->blockingnr = rpos[5] | (rpos[4] << 8); /* RW error page */ break; case 0x0020 : /* random writable feature */ disc->recordable = 1; disc->rewritable = 1; break; case 0x0021 : /* incremental streaming write feature */ disc->recordable = 1; disc->sequential = 1; disc->link_size = rpos[7]; if (rpos[6] & 1) disc->link_size = 0; break; case 0x0022 : /* (obsolete) erase support feature */ disc->recordable = 1; disc->erasable = 1; break; case 0x0023 : /* formatting media support feature */ disc->recordable = 1; disc->formattable = 1; break; case 0x0024 : /* hardware assisted defect management feature */ /* XXX set mrw ? */ break; case 0x0025 : /* write once */ disc->recordable = 1; break; case 0x0026 : /* restricted overwrite feature */ disc->recordable = 1; disc->rewritable = 1; disc->strict_overwrite = 1; break; case 0x0028 : /* MRW formatted media support feature */ disc->mrw = 1; break; case 0x002b : /* read/write DVD+R formatted media */ disc->sequential = 1; if (rpos[0] & 1) /* write support */ disc->recordable = 1; break; case 0x002c : /* regid restricted overwrite feature */ disc->recordable = 1; disc->rewritable = 1; disc->strict_overwrite = 1; if (rpos[0] & 1) /* blank bit */ disc->blankable = 1; break; case 0x002d : /* track at once recording feature */ disc->recordable = 1; disc->sequential = 1; break; case 0x002f : /* DVD-R/-RW write feature */ disc->recordable = 1; if (rpos[0] & 2) /* DVD-RW bit */ disc->blankable = 1; break; case 0x0038 : /* pseuro overwritable */ break; default : /* ignore */ break; } } last_feature = MAX(last_feature, feature); if (feature_len & 3) { UDF_VERBOSE(printf("Drive returned feature %d %swith bad length %d\n", feature, (feature_cur == 1? "(current) ":""), feature_len)); feature_len = (feature_len + 3) & ~3; } pos += 4 + feature_len; } } while (features_len >= 0xffff); /* * fixup DVD and CD-RW drives that are on crack. */ if (disc->mmc_profile == 0x0a) { /* some forget to mention strict overwrite when not sequential and forget the blocking size */ if (!disc->sequential) { disc->strict_overwrite = 1; disc->blockingnr = 32; /* fixed for CD-RW */ } /* some say they are strict overwrite but also sequential */ if (disc->strict_overwrite) disc->sequential = 0; /* some forget that if they are mrw they can't the be other */ if (disc->mrw) { disc->sequential = 0; disc->strict_overwrite = 0; } } /* some think a CD-R is rewritable */ if (disc->mmc_profile == 0x09) { disc->rewritable = 0; } /* derivatives */ if (disc->blockingnr > 1) disc->packet = 1; switch (disc->mmc_profile) { case 0x01 : case 0x02 : /* SCSI discs class; treat like normal discs */ disc->devdrv_class = UDF_DEVDRV_CLASS_DISC; UDF_VERBOSE(printf("SCSI disc detected; treating like normal disc device\n")); return 0; case 0x03 : /* Magneto Optical with sector erase */ case 0x04 : /* Magneto Optical write once */ case 0x05 : /* Advance Storage Magneto Optical */ disc->devdrv_class = UDF_DEVDRV_CLASS_MO; break; /* 0x00, 0x08, 0x09, 0x0a : different types of CD-ROM devices like CD-R/RW etc. */ case 0x00 : disc->devdrv_class = UDF_DEVDRV_CLASS_CD; /* not allways clear */ break; case 0x08 : /* CD-ROM */ case 0x09 : /* CD-R */ case 0x0a : /* CD-RW */ disc->devdrv_class = UDF_DEVDRV_CLASS_CD; break; /* 0x10...0x14 DVD-ROM and DVD- devices */ case 0x10 : /* DVD-ROM */ case 0x11 : /* DVD-R */ case 0x12 : /* DVD-RAM */ case 0x13 : /* DVD-RW (restricted overwrite) */ case 0x14 : /* DVD-RW (sequential) */ /* 0x1a..0x1b DVD+ devices */ case 0x1a : /* DVD+RW */ case 0x1b : /* DVD+R */ case 0x2b : /* DVD+R double layer */ disc->devdrv_class = UDF_DEVDRV_CLASS_DVD; break; case 0x40 : /* BD-ROM */ case 0x41 : /* BD-R Sequential Recording (SRM) */ case 0x42 : /* BD-R Random Recording (RRM) */ case 0x43 : /* BD-RE */ disc->devdrv_class = UDF_DEVDRV_CLASS_BD; break; default : fprintf(stderr, "Not recognized MMC profile %d encountered, marking readonly\n", disc->mmc_profile); disc->devdrv_class = UDF_DEVDRV_CLASS_CD; /* go for the `dummy' */ break; } /* * ok, if we're on an CD-MRW or DVD+MRW, we ought to have switched * automatically to DMA space. However, this drive at times just * refuses with all messy things around. */ /* Select `Defect managed area' LBA space */ if (!disc->mrw) return 0; DEBUG(printf("Setting DMA LBA space")); val_len = 6 + 8 + 2; /* 2 for 4 byte alignment */ rpos = buf + 8; bzero(buf, val_len); rpos[ 0] = 0x03; /* GAA/DMA space select */ rpos[ 1] = 6; /* page length */ rpos[ 3] = 0; /* select GAA bit in bit 0 */ bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x55; /* MODE SELECT (10) */ cmd[1] = 16; /* PF format */ cmd[7] = val_len >> 8; /* length of blob */ cmd[8] = val_len & 0xff; cmd[9] = 0; /* control */ error = uscsi_command(SCSI_WRITECMD, disc->dev, cmd, 10, buf, val_len, 30000, NULL); if (error) { perror("While WRTITING parameter page 3"); return error; } /* shutup gcc */ feature_pers = feature_pers; feature_ver = feature_ver; return 0; } #if defined(__NetBSD__) #include "partutil.h" #include "partutil.c" int udf_get_partition_info(struct udf_discinfo *disc) { struct disk_geom geo; struct dkwedge_info dkw; int error; /* get our disc info */ error = getdiskinfo("name", disc->dev->fhandle, NULL, &geo, &dkw); if (error) { warn("retrieving disc info failed"); return EIO; } disc->sector_size = geo.dg_secsize; disc->session_start[0] = 0; disc->session_end [0] = dkw.dkw_size -1; return 0; } #elif defined(__OpenBSD__) #include #include #include int udf_get_partition_info(struct udf_discinfo *disc) { struct disklabel disklab; struct partition *dp; struct stat st; int partnr; /* read disklabel partition */ if (ioctl(disc->dev->fhandle, DIOCGDINFO, &disklab) == -1) { /* failed to get disclabel! */ perror("disklabel"); return errno; } /* get disk partition it refers to */ fstat(disc->dev->fhandle, &st); partnr = DISKPART(st.st_rdev); dp = &disklab.d_partitions[partnr]; if (dp->p_size == 0) { perror("faulty disklabel partition returned, check label\n"); return EIO; } disc->sector_size = disklab.d_secsize; disc->session_start [0] = 0; disc->session_end [0] = dp->p_size - 1; return 0; } #elif defined(__linux__) #include int udf_get_partition_info(struct udf_discinfo *disc) { long p_size, p_size512; int secsize; /* get device length and sector size */ if (ioctl(disc->dev->fhandle, BLKSSZGET, &secsize) == -1) { perror("Can't read my sector size\n"); return errno; } if (ioctl(disc->dev->fhandle, BLKGETSIZE, &p_size512) == -1) { perror("Can't read my partition size\n"); return errno; } p_size = p_size512 * (secsize / 512); disc->sector_size = secsize; disc->session_start [0] = 0; disc->session_end [0] = p_size - 1; return 0; } #else int udf_get_partition_info(struct udf_discinfo *disc) { perror(" UDF: no explicit support for disc devices yet for this operating system.\n"); perror("Trying readonly access...\n"); disc->recordable = disc->rewritable = 0; return 0; } #endif /* 10000 is arbitrary */ /* TODO split up one day for its updating values unnessisarily */ #define res_len 10000 int udf_get_disc_info(struct udf_discinfo *disc) { scsicmd cmd; struct stat stat; uint32_t val_len; uint32_t first_track, last_track; uint32_t first_session, last_session; uint32_t next_writable_addr, packet_size; uint32_t cntrl, addr, tno, point, min, sec, frame, pmin, psec, pframe; uint32_t data_length, pos; uint8_t res[res_len]; int first_track_last_session, last_track_last_session; int track, session, sector_size; int nwa_valid; off_t track_start, track_end, track_size, disc_size, free_blocks; int error; if (disc->devdrv_class == UDF_DEVDRV_CLASS_FILE) { sector_size = disc->alt_sector_size ? disc->alt_sector_size : disc->sector_size; fstat(disc->dev->fhandle, &stat); disc->link_size = 0; /* no link lossage */ disc->disc_state = DISC_STATE_NOT_SERIAL; disc->last_session_state = SESSION_STATE_INCOMPLETE; disc->num_sessions = 1; disc->session_start [0] = 0; disc->session_end [0] = (stat.st_size / sector_size); /* inclusive */ disc->next_writable [0] = (stat.st_size / sector_size) + 1; disc->packet_size [0] = stat.st_blksize / sector_size; return 0; } if (disc->devdrv_class == UDF_DEVDRV_CLASS_DISC) { disc->link_size = 0; /* no link lossage */ disc->disc_state = DISC_STATE_NOT_SERIAL; disc->last_session_state = SESSION_STATE_COMPLETE; disc->num_sessions = 1; disc->session_start [0] = 0; disc->session_end [0] = 0; error = udf_get_partition_info(disc); if (error) return error; fprintf(stderr, "UDF: warning... reading/writing on 'disc' device\n"); return 0; } /* classes CD and DVD remain; we can be on a DVD/CD recordable or on a legacy CD-ROM */ /* Get track information */ val_len = 12; bzero(res, val_len); bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x51; /* Read disc information */ cmd[7] = val_len >> 8; /* MSB allocation length */ cmd[8] = val_len & 0xff; /* LSB allocation length */ cmd[9] = 0; /* control */ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, val_len, 30000, NULL); if (!error) { /* we are a MMC3+ device! */ disc->disc_state = res[2] & 3; /* just 'happends' to be the same as we use */ disc->last_session_state = (res[2] >> 2) & 3; /* ditto */ disc->blankable = res[2] & 16; /* blankable -> update possibility */ disc->num_sessions = res[4] | (res[ 9] << 8); first_track = res[3]; first_track_last_session = res[5] | (res[10] << 8); /* to build up the last 'session' */ last_track_last_session = res[6] | (res[11] << 8); /* Initialise the sessions to be taken as overspanning tracks */ for (session = 0; session < disc->num_sessions; session++) { disc->session_start[session] = INT_MAX; disc->session_end [session] = 0; } for (track = first_track; track <= last_track_last_session; track++) { /* each track record is 36 bytes long */ val_len = 36; bzero(res, val_len); bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x52; /* Read track information */ cmd[1] = 1; /* indexed on track */ cmd[4] = track >> 8; /* track number 0-0xff ? */ cmd[5] = track & 0xff; cmd[7] = val_len >> 8; /* MSB length resultbuf */ cmd[8] = val_len & 0xff; /* LSB ,, */ cmd[9] = 0; error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, val_len, 30000, NULL); if (error) { perror("While reading track info"); break; } #if 0 data_length = res[1] | (res[0] << 8); // track_number = res[2] | (res[32] << 8); /* why? */ session = res[3] | (res[33] << 8); // is_track_mode = res[5] & 15; // is_copy = res[5] & 16; // is_damage = res[5] & 32; // is_fixed_packet = res[6] & 16; // is_packet_or_inc = res[6] & 32; // is_blank = res[6] & 64; // is_reserved = res[6] & 128; // is_data_mode = res[6] & 15; nwa_valid = res[7] & 1; // lra_valid = res[7] & 2; #endif data_length = res[1] | (res[0] << 8); session = res[3] | (res[33] << 8); nwa_valid = res[7] & 1; track_start = res[11] | (res[10]<<8) | (res[ 9]<<16) | (res[ 8]<<24); next_writable_addr = res[15] | (res[14]<<8) | (res[13]<<16) | (res[12]<<24); free_blocks = res[19] | (res[18]<<8) | (res[17]<<16) | (res[16]<<24); packet_size = res[23] | (res[22]<<8) | (res[21]<<16) | (res[20]<<24); track_size = res[27] | (res[26]<<8) | (res[25]<<16) | (res[24]<<24); /* last_recorded_addr = res[32] | (res[30]<<8) | (res[29]<<16) | (res[28]<<24); */ track_end = track_start + track_size; if (data_length <= 30) session &= 0xff; disc->session_start[session-1] = MIN(disc->session_start[session-1], track_start); disc->session_end [session-1] = MAX(disc->session_end [session-1], track_end); disc->free_blocks [session-1] = free_blocks; disc->packet_size [session-1] = packet_size; if (nwa_valid) disc->next_writable[session-1] = next_writable_addr; } if (disc->session_start[disc->num_sessions-1] == INT_MAX) { if (disc->disc_state == DISC_STATE_FULL) { if (disc->last_session_state == SESSION_STATE_COMPLETE) disc->num_sessions--; } } /* XXX * initialise default link size to zero; only set DEFAULT * link lossage for CD-R we are using `Burn Free' for writing * but if it fails its good to know the penalty for recovery * XXX */ disc->link_size = 0; if (disc->mmc_profile == 0x09) { disc->link_size = 7; } return 0; } /* Start with legacy CD-ROM .... trying to get as much info from it as possible */ disc->sequential = 0; disc->recordable = 0; disc->blankable = 0; disc->packet = 0; disc->blockingnr = 32; disc->strict_overwrite = 0; disc->disc_state = DISC_STATE_FULL; disc->last_session_state = SESSION_STATE_COMPLETE; disc->link_size = 7; /* DEFAULT link lossage for CDs */ /* Read session range */ val_len = 4; /* only head */ bzero(res, val_len); bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */ cmd[1] = 2; /* no LBA's are defined */ cmd[2] = 2; /* format 2 also this time */ cmd[6] = 0; /* obligatory zero */ cmd[7] = val_len >> 8; cmd[8] = val_len & 0xff; cmd[9] = 0; error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, val_len, 30000, NULL); if (error) { perror("TOC reading of sessions failed"); return error; } first_session = read_cd_hex2(res[2]); last_session = read_cd_hex2(res[3]); disc->num_sessions = last_session - first_session + 1; /* Initialise the sessions to be taken as overspanning tracks */ for(session = 0; session <= disc->num_sessions; session++) { disc->session_start[session] = INT_MAX; disc->session_end [session] = 0; } /* calculate how big the result buffer ought to be to get the whole TOC */ /* NOTE: don't count the 2 length bytes */ val_len = res[1] + (res[0] << 8); /* fix length for ATAPI drives */ if (val_len & 1) val_len++; assert(val_len < res_len); /* Read the complete TOC and extract information */ bzero(res, val_len); bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */ cmd[1] = 2; /* LBA's are not defined */ cmd[2] = 2; /* format 2; full TOC */ cmd[6] = first_session; /* start at first session */ cmd[7] = val_len >> 8; cmd[8] = val_len & 0xff; cmd[9] = 0; error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, val_len, 30000, NULL); if (error) { perror("TOC reading of full TOC failed"); return error; } data_length =res[1] | (res[0] << 8); if (data_length != val_len) { fprintf(stderr, "Warning: device didn't return all data requested when reading full TOC\nn"); fprintf(stderr, "\treturned %d bytes instead of %d\n", data_length, val_len); } pos = 4; track_start = INT_MAX; track_end = 0; while (pos < data_length + 2) { session = read_cd_bcd(res[pos+ 0]); cntrl = res[pos+1] & 15; addr = res[pos+1] >> 4; tno = read_cd_bcd(res[pos+ 2]); point = read_cd_bcd(res[pos+ 3]); min = read_cd_bcd(res[pos+ 4]); sec = read_cd_bcd(res[pos+ 5]); frame = read_cd_bcd(res[pos+ 6]); pmin = read_cd_bcd(res[pos+ 8]); psec = read_cd_bcd(res[pos+ 9]); pframe = read_cd_bcd(res[pos+10]); /* extract information; explicit writeout. See SCSI docs */ if (tno == 0 && session && addr == 1) { switch (point) { case 0xa0 : first_track = pmin; break; case 0xa1 : last_track = pmin; break; case 0xa2 : track_end = cd_msf2lba(0, pmin, psec, pframe); disc->session_end [session-1] = MAX(disc->session_end [session-1], track_end); break; default : track_start = cd_msf2lba(0, pmin, psec, pframe); disc->session_start[session-1] = MIN(disc->session_start[session-1], track_start); break; } } if (tno == 0 && session && addr == 5) { if (point == 0xb0) { next_writable_addr = cd_msf2lba(0, min, sec, frame); disc_size = cd_msf2lba(0, pmin, psec, pframe); /* TODO use nwa & size */ printf("UDF: ignoring B0 Q channel : next writable address; pre MMC3 device; fix me\n"); } } pos += 11; } /* Last session information is notoriously flawed in TOC format so update it */ bzero(res, 8); bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x25; /* CD READ RECORDED CAPACITY */ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, 8, 30000, NULL); if (error) { fprintf(stderr, "Can't read CD recorded capacity, last session end might not be OK : %s\n", strerror(error)); return 0; } session = disc->num_sessions-1; disc->session_end[session] = res[3] | (res[2]<<8) | (res[1]<<16) | (res[0]<<24); /* shut up gcc */ disc_size = disc_size; first_track_last_session = first_track_last_session; cntrl = cntrl; last_track = last_track; return 0; } #undef res_len int udf_open_disc(char *dev_name, int discop_flags, struct udf_discinfo **discptr) { struct udf_discinfo *disc; if (!discptr) return EINVAL; *discptr = NULL; /* determine what kind of file/device we are dealing with */ disc = calloc(1, sizeof(struct udf_discinfo)); if (!disc) return ENOMEM; disc->dev = calloc(1, sizeof(struct uscsi_dev)); if (!disc->dev) { free(disc); return ENOMEM; } /* fill in the name */ disc->dev->dev_name = strdup(dev_name); if (uscsi_open(disc->dev) != 0) { perror("Failure to open device or file"); free(disc->dev); free(disc); return ENODEV; } /* determine disc type */ if (udf_discinfo_get_disc_type(disc)) { perror("Error during classification of disc; skipping disc\n"); uscsi_close(disc->dev); free(disc->dev); free(disc); return ENODEV; } /* get disc info */ if (udf_get_disc_info(disc)) { fprintf(stderr, "Can't get disc info"); uscsi_close(disc->dev); free(disc->dev); free(disc); return ENODEV; } /* process discop_flags */ if (discop_flags & UDF_DISCOP_BSWAP) disc->bswap_sectors = 1; /* return the pointer */ *discptr = disc; /* set recording parameters */ udf_discinfo_set_recording_parameters(disc, 0); /* no testwrite */ return 0; } int udf_close_disc(struct udf_discinfo *disc) { if (!disc) return 0; /* udf_stop_disc_thread(disc); */ uscsi_close(disc->dev); printf("Disc access statistics\n"); printf("\tsector reads %8"PRIu64" (%"PRIu64" Kbyte)\n", disc->sectors_read, ((uint64_t) (disc->sectors_read) * disc->sector_size) / 1024); printf("\tsector written %8"PRIu64" (%"PRIu64" Kbyte)\n", disc->sectors_written, ((uint64_t) (disc->sectors_written) * disc->sector_size) / 1024); printf("\tswitches %8d\n", disc->switchings); return 0; } int udf_discinfo_alter_perception(struct udf_discinfo *disc, uint32_t sec_size, uint32_t num_sectors) { struct stat stat; assert(disc); if ((disc->devdrv_class & UDF_DEVDRV_CLASS) != UDF_DEVDRV_CLASS_FILE) { return EINVAL; } fstat(disc->dev->fhandle, &stat); if (sec_size == 0) sec_size = disc->sector_size; if (num_sectors == 0) num_sectors = stat.st_size / sec_size; if (((sec_size % 512) != 0) || (sec_size == 0)) { fprintf(stderr, "Size of blocks need to be a multiple of 512\n"); return EINVAL; } if (stat.st_size / sec_size >= (uint32_t) -1) { fprintf(stderr, "Disc needs to many logical sectors, please increase blocksize\n"); return EINVAL; } if (num_sectors < 300) { fprintf(stderr, "Disc size too small to put an UDF filingsystem on\n"); return EINVAL; } if (stat.st_size != (off_t) sec_size * num_sectors) { fprintf(stderr, "Size of image file is not equal to specified size parameters\n"); return EINVAL; } disc->sequential = 0; /* full r/w */ disc->recordable = 1; /* assuming rw access */ disc->rewritable = 1; disc->sector_size = sec_size; disc->alt_sector_size = sec_size; /* altered value */ disc->link_size = 0; /* no link lossage */ disc->disc_state = DISC_STATE_NOT_SERIAL; disc->last_session_state = SESSION_STATE_INCOMPLETE; disc->num_sessions = 1; disc->session_start [0] = 0; disc->session_end [0] = num_sectors; disc->next_writable [0] = num_sectors + 1; disc->packet_size [0] = stat.st_blksize / sec_size; /* best blocking size */ return 0; } /****************************************************************************************** * * Sector readers and writers * ******************************************************************************************/ /* read an extent of sectors in the `result' buffer */ int udf_read_physical_sectors(struct udf_discinfo *disc, off_t sector, uint32_t num_sectors, char *what, uint8_t *result) { struct uscsi_sense sense; scsicmd cmd; int size, size_read, chunk; uint32_t session, skipped, sector_size; int pos, lb, hb, error; /* protect us */ if (((long) result) & 3) { printf("Unaligned read of sector : possible panic() on some systems avoided\n"); return EIO; } sector_size = disc->sector_size; /* read one UDF sector at a physical address specified in UDF_SECTOR size units */ size = num_sectors * disc->sector_size; size_read = 0; bzero(result, size); /* just in case */ /* has to go? */ assert(sector_size); assert(num_sectors <= 0xffff); /* statistics and cache control */ if (disc->am_writing) { disc->switchings++; /* XXX how about pseudo-overwrite? is this nessisary then too ? XXX */ if (disc->sequential) { /* * we need to synchronise the write caches before we * are allowed to read from the disc again. */ error = udf_discinfo_synchronise_caches(disc); while (error) { printf("udf_discinfo: failed to sync caches, retrying\n"); error = udf_discinfo_synchronise_caches(disc); } /* we need to update our NWA's after the sync on sequentials */ udf_get_disc_info(disc); } /* mark reading access only */ disc->am_writing = 0; } error = 0; DEBUG(printf("\r%08d : %s; read %d bytes\n", (int) sector, what, size)); while (num_sectors) { switch (disc->devdrv_class & UDF_DEVDRV_CLASS) { case UDF_DEVDRV_CLASS_CD : case UDF_DEVDRV_CLASS_DVD : /* * Use a SCSI command to read it so we can go past the last session; * allthough READ (12) is available, some older CD-ROM * devices only want to do READ (10). */ /* limited by MAXPHYS, taking 64kb as limitation */ chunk = MIN(64*1024 / sector_size, num_sectors); size_read = chunk * sector_size; /* definition */ skipped = session = 0; bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x28; /* READ (10) command */ cmd[1] = 0; /* normal access */ cmd[2] = (sector >> 24) & 0xff; cmd[3] = (sector >> 16) & 0xff; cmd[4] = (sector >> 8) & 0xff; cmd[5] = (sector ) & 0xff; cmd[6] = 0; /* reserved */ cmd[7] = (chunk >> 8) & 0xff; /* MSB transfer */ cmd[8] = (chunk ) & 0xff; /* number of logical block(s) */ cmd[9] = 0; /* control */ do { error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, result, size_read - skipped, 30000, &sense); /* TODO: if busy, ask drive how long it'll take to be available again and wait */ if (sense.asc == 4) usleep(5000); } while (sense.asc == 4); if (error) return ENOENT; break; default : /* XXX first and only reference to {p}read() XXX */ if (sector>=0) size_read = pread(disc->dev->fhandle, result, (uint64_t) num_sectors * sector_size, sector * sector_size); break; } /* statistics */ disc->sectors_read += size_read / sector_size; /* swap space if requested */ if (disc->bswap_sectors) { for (pos = 0; pos < size_read;) { lb = result[pos ]; hb = result[pos+1]; result[pos ] = hb; result[pos+1] = lb; pos += 2; } } /* advance */ num_sectors -= size_read / sector_size; sector += size_read / sector_size; result += size_read; if (size_read <= 0) { UDF_VERBOSE_MAX( if (what) { printf("Can't read sectors %d+%d for %s\n", (int) sector, num_sectors, what); } ); if (size_read == 0) return ENOENT; return error; } } return 0; /* flag ok */ } /* write an extent of sectors to disc */ int udf_write_physical_sectors(struct udf_discinfo *disc, off_t sector, uint32_t num_sectors, char *what, uint8_t *source) { struct uscsi_sense sense; scsicmd cmd; int trans_length, size, size_written, chunk; uint32_t sector_size; uint8_t *buffer; int lb, hb, pos, error; /* if (!disc->udf_recording) return ENODEV; */ /* protect us */ if (((long) source) & 3) { printf("Unaligned write of sector : possible panic() on some systems avoided\n"); return EIO; } sector_size = disc->sector_size; /* XXX clean up XXX */ assert(sector_size); assert(num_sectors <= 0xffff); /* compatible with WRITE (10)? */ DEBUG(printf("\r%08d : %s ;WRITE %d bytes\n", (int) sector, what, num_sectors * sector_size)); /* swap space if requested */ buffer = source; if (disc->bswap_sectors) { size = num_sectors * sector_size; buffer = malloc(num_sectors * sector_size); for (pos = 0; pos < size;) { lb = source[pos ]; hb = source[pos+1]; buffer[pos ] = hb; buffer[pos+1] = lb; pos += 2; } } error = 0; while (num_sectors) { size_written = 0; switch (disc->devdrv_class & UDF_DEVDRV_CLASS) { case UDF_DEVDRV_CLASS_CD : case UDF_DEVDRV_CLASS_DVD : /* * Use WRITE (12) command to write to the disc; we * might have to downgrade later to using WRITE (10) * on older discs though :-/ */ /* limited by MAXPHYS, taking 64kb as limitation */ chunk = MIN(64*1024 / sector_size, num_sectors); /* in sectors */ trans_length = chunk; /* in sectors */ bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0xAA; /* WRITE (12) */ cmd[ 1] = 0; /* no force unit access */ cmd[ 2] = (sector >> 24) & 0xff; cmd[ 3] = (sector >> 16) & 0xff; cmd[ 4] = (sector >> 8) & 0xff; cmd[ 5] = (sector ) & 0xff; cmd[ 6] = (trans_length >> 24) & 0xff; /* MSB */ cmd[ 7] = (trans_length >> 16) & 0xff; cmd[ 8] = (trans_length >> 8) & 0xff; cmd[ 9] = (trans_length ) & 0xff; /* LSB */ cmd[10] = 0; /* no streaming */ cmd[11] = 0; /* control */ do { error = uscsi_command(SCSI_WRITECMD, disc->dev, cmd, 12, buffer, trans_length * sector_size, 30000, &sense); /* TODO: if busy, ask drive how long it'll take to be available again and wait */ if (sense.asc == 4) usleep(5000); } while (sense.asc == 4); /* write one UDF sector at a physical address specified in UDF_SECTOR size units */ size = trans_length * sector_size; size_written = error ? 0 : size; break; default : /* XXX first and only reference to {p}write() XXX */ DEBUG(printf("udf_discop: pwrite %"PRIu64" + %d\n", (uint64_t) sector * sector_size, (int) num_sectors * sector_size)); /* write one UDF sector at a physical address specified in UDF_SECTOR size units */ size = num_sectors * sector_size; size_written = pwrite(disc->dev->fhandle, buffer, (uint64_t) num_sectors * sector_size, sector * sector_size); if (size_written < 0) size_written = 0; break; } /* free our copy if we created one */ if (buffer != source) free(buffer); /* statistics */ disc->sectors_written += size_written / sector_size; if (!disc->am_writing) disc->switchings++; disc->am_writing = 1; num_sectors -= size_written / sector_size; sector += size_written / sector_size; buffer += size_written; if ((size_written < size) || error) { DEBUG(if (error) printf("Writing %s at sectors %d+%d failed\n", what, (int) sector, num_sectors)); return EIO; } } return 0; } /* End of udf_discinfo.c */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf_discop.h000066400000000000000000000126661523477242100264440ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf_discop.h" is part of the UDFclient toolkit. * File $Id: udf_discop.h,v 1.30 2011/02/01 20:43:41 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _UDF_DISCOP_H_ #define _UDF_DISCOP_H_ #include #include "defs.h" #include "uscsilib.h" #include "queue.h" /* setup constants */ #define UDF_DISCOP_BSWAP 1 /* Implementation constants */ #define MAX_SESSIONS 100 #define CD_SECTOR_SIZE 2048 #define DISC_SECTOR_SIZE 512 /* State and driver constants */ #define DISC_STATE_EMPTY 0 /* nothing on the disc */ #define DISC_STATE_INCOMPLETE 1 /* can be appended */ #define DISC_STATE_FULL 2 /* no appending possible */ #define DISC_STATE_NOT_SERIAL 3 /* not serial */ #define SESSION_STATE_EMPTY 0 /* still empty */ #define SESSION_STATE_INCOMPLETE 1 /* session is open */ #define SESSION_STATE_CLOSED 2 /* we need a new session */ #define SESSION_STATE_COMPLETE 3 /* no appending possible */ #define UDF_DEVDRV_CLASS 0xFF #define UDF_DEVDRV_CLASS_FILE 0x00 #define UDF_DEVDRV_CLASS_DISC 0x01 #define UDF_DEVDRV_CLASS_CD 0x02 #define UDF_DEVDRV_CLASS_DVD 0x04 #define UDF_DEVDRV_CLASS_MO 0x08 #define UDF_DEVDRV_CLASS_BD 0x10 /* will go? */ struct udf_session; /* * The complete disc, all cd sessions and how to access the drive */ struct udf_discinfo { struct uscsi_dev *dev; /* uscsi device associated */ int scsi_device_type; /* in case of SCSI */ int mmc_profile; /* in case of SCSI */ int devdrv_class; int sequential; /* media is sequential writable only */ int recordable; /* media is record-able; i.e. not static */ int erasable; /* drive can erase sectors */ int blankable; /* media can be blanked by the drive */ int formattable; /* media can be formatted by the drive */ int rewritable; /* media can be rewritten */ int mrw; /* media is identified as being MRW formatted */ int packet; /* media is using packet recording */ int strict_overwrite; /* media can only be written a packet at a time */ int blockingnr; /* blocking size for ECC/modification */ int sector_size; /* sector size in bytes */ int alt_sector_size; /* if non zero, override normal sectorsize */ int link_size; /* link size lossage when having underruns */ int disc_state; /* empty - incomplete - full - not_serial */ int last_session_state; /* empty - incomplete - closed - complete */ /* flags that alter udf_discop's behaviour */ int udf_recording; /* flags if a recording is initialised */ int bswap_sectors; /* swap all bytes in a sector */ /* statistics */ int am_writing; uint64_t sectors_read; uint64_t sectors_written; uint32_t switchings; /* sessions */ int num_sessions; int num_udf_sessions; int session_is_UDF[MAX_SESSIONS]; int session_quirks[MAX_SESSIONS]; off_t session_start [MAX_SESSIONS]; off_t session_end [MAX_SESSIONS]; uint32_t next_writable [MAX_SESSIONS]; uint32_t free_blocks [MAX_SESSIONS]; uint32_t packet_size [MAX_SESSIONS]; STAILQ_HEAD(udf_sessions, udf_session) sessions; SLIST_ENTRY(udf_discinfo) next_disc; }; /* exported functions */ extern int udf_open_disc(char *dev_name, int discop_flags, struct udf_discinfo **discptr); extern int udf_close_disc(struct udf_discinfo *disc); extern int udf_discinfo_get_type(struct udf_discinfo *disc); extern int udf_discinfo_check_disc_ready(struct udf_discinfo *disc); extern int udf_discinfo_is_cd_or_dvd(struct udf_discinfo *disc); extern int udf_discinfo_set_recording_parameters(struct udf_discinfo *discinfo, int testwriting); extern int udf_discinfo_alter_perception(struct udf_discinfo *disc, uint32_t sec_size, uint32_t num_sectors); extern int udf_discinfo_finish_writing(struct udf_discinfo *discinfo); extern int udf_discinfo_reserve_track_in_logic_units(struct udf_discinfo *discinfo, uint32_t logic_units); extern int udf_discinfo_close_track(struct udf_discinfo *discinfo, uint16_t trackno); extern int udf_discinfo_repair_track(struct udf_discinfo *discinfo, uint16_t trackno); extern int udf_read_physical_sectors(struct udf_discinfo *disc, off_t sector, uint32_t num_sectors, char *what, uint8_t *result); extern int udf_write_physical_sectors(struct udf_discinfo *disc, off_t sector, uint32_t num_sectors, char *what, uint8_t *source); #endif /* _UDF_DISCOP_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf_readwrite.c000066400000000000000000000700731523477242100271400ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf_readwrite.c" is part of the UDFclient toolkit. * File $Id: udf_readwrite.c,v 1.50 2016/04/25 21:01:40 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* XXX strip list to bare minimum XXX */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uscsilib.h" /* for locals */ #include "udf.h" #include "udf_bswap.h" #include "udf_discop.h" #include "uio.h" #include #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) # define MIN(a,b) ((a)<(b)?(a):(b)) #endif /* #define DEBUG(a) { a; } */ #define DEBUG(a) if (0) { a; } /* predefines */ #if 1 extern void udf_dump_descriptor(union dscrptr *dscrpt); #else void udf_dump_descriptor(union dscrptr *dscrptr) {} #endif int udf_writeout_session_cache(struct udf_session *udf_session); /****************************************************************************************** * * Session-cache init and syncing * ******************************************************************************************/ int udf_init_session_caches(struct udf_session *udf_session) { uint32_t sector_size; sector_size = udf_session->disc->sector_size; UDF_MUTEX_INIT(&udf_session->session_cache_lock); udf_session->cache_line_read = malloc(UDF_READWRITE_LINE_LENGTH * sector_size); udf_session->cache_line_write = malloc(UDF_READWRITE_LINE_LENGTH * sector_size); assert(udf_session->cache_line_read); assert(udf_session->cache_line_write); bzero(udf_session->cache_write_callbacks, UDF_READWRITE_LINE_LENGTH * sizeof(struct udf_wrcallback)); return 0; } void udf_sync_session_cache(struct udf_session *udf_session) { UDF_MUTEX_LOCK(&udf_session->session_cache_lock); /* hmm... have to write out current write-cache */ udf_writeout_session_cache(udf_session); UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock); } int udf_sync_caches(struct udf_log_vol *udf_log_vol) { struct udf_volumeset *udf_volumeset; struct udf_partition *udf_partition; struct udf_part_mapping *udf_part_mapping; uint32_t part_num; /* XXX need to force writeout of session caches... XXX */ /* process all `partions->sessions' */ DEBUG( printf("SYNC statistics\n"); printf("\tbufcache lru_len_data %d\n", udf_bufcache->lru_len_data); printf("\tbufcache lru_len_metadata %d\n", udf_bufcache->lru_len_metadata); printf("\tbufcache claimed/released %d\n", udf_bufcache->bcnt); ); udf_volumeset = udf_log_vol->primary->volumeset; SLIST_FOREACH(udf_part_mapping, &udf_log_vol->part_mappings, next_mapping) { part_num = udf_part_mapping->udf_virt_part_num; SLIST_FOREACH(udf_partition, &udf_volumeset->parts, next_partition) { if (udf_rw16(udf_partition->partition->part_num) == part_num) { /* sync session */ DEBUG(printf("Syncing session cache for vpart %d, part %d\n", part_num, udf_partition->udf_session->session_num)); udf_sync_session_cache(udf_partition->udf_session); } } } return 0; } /****************************************************************************************** * * Session and logvol sector reading/writing (simple caching) * ******************************************************************************************/ int udf_read_session_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *buffer, int prefetch_sectors, int rwflags) { uint32_t eff_sector, bit, sector_size; int32_t cache_diff; int error; rwflags = rwflags; /* unused here */ /* maximise 'prefetch_sectors' to cache line length */ prefetch_sectors = MIN(UDF_READWRITE_LINE_LENGTH, prefetch_sectors); sector_size = udf_session->disc->sector_size; /* XXX cache coherency ???? XXX */ UDF_MUTEX_LOCK(&udf_session->session_cache_lock); eff_sector = udf_session->session_offset + sector; /* snoop write cache */ cache_diff = eff_sector - udf_session->cache_line_w_start; if ((cache_diff >= 0) && (cache_diff < UDF_READWRITE_LINE_LENGTH)) { bit = (1 << cache_diff); if (udf_session->cache_line_w_present & bit) { /* return cached value */ memcpy(buffer, udf_session->cache_line_write + cache_diff * sector_size, sector_size); UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock); return 0; } /* not present */ } /* check read cache */ cache_diff = eff_sector - udf_session->cache_line_r_start; if ((cache_diff >= 0) && (cache_diff < UDF_READWRITE_LINE_LENGTH)) { bit = (1 << cache_diff); if (udf_session->cache_line_r_present & bit) { /* return cached value */ memcpy(buffer, udf_session->cache_line_read + cache_diff * sector_size, sector_size); UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock); return 0; } /* not present */ } /* read in from this sector on for the prefetch length */ /* XXX use `pending' and `unalloc'/`freed' allocentry queue to minimise read/write misses in streams ? XXX */ /* XXX use 3 write streams ? XXX */ error = udf_read_physical_sectors(udf_session->disc, eff_sector, prefetch_sectors, what, udf_session->cache_line_read); if (!error) { udf_session->cache_line_r_start = eff_sector; memcpy(buffer, udf_session->cache_line_read, sector_size); udf_session->cache_line_r_present = 0; for (cache_diff=0; cache_diff < prefetch_sectors; cache_diff++) { bit = (1 << cache_diff); udf_session->cache_line_r_present |= bit; } UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock); return 0; } /* what now? */ DEBUG( printf("ERROR! reading chunk\n"); ); udf_session->cache_line_r_present = 0; error = udf_read_physical_sectors(udf_session->disc, eff_sector, 1, what, buffer); if (!error) { udf_session->cache_line_r_start = eff_sector; udf_session->cache_line_r_present = 1; } DEBUG( if (error) printf("ERROR reading sector %d\n", eff_sector) ); UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock); return error; } int udf_writeout_session_cache(struct udf_session *udf_session) { struct udf_wrcallback *callback; uint32_t bit, error_bits, sector_size; uint32_t num_sectors; int32_t cache_diff; uint32_t start_sector; uint8_t *from, *to; int error, report_error; if (udf_session->cache_line_w_dirty == 0) return 0; error_bits = 0; sector_size = udf_session->disc->sector_size; num_sectors = UDF_READWRITE_LINE_LENGTH; start_sector = 0; error = 0; report_error = 0; if (udf_session->disc->strict_overwrite) { /* Have to do our own Read-Modify-Write :( */ assert((udf_session->cache_line_w_start % UDF_READWRITE_LINE_LENGTH) == 0); /* all present ? */ if (udf_session->cache_line_w_dirty && (udf_session->cache_line_w_present != UDF_READWRITE_ALL_PRESENT)) { /* could snoop read buffer for missed sectors */ } /* double check all present */ if (udf_session->cache_line_w_dirty && (udf_session->cache_line_w_present != UDF_READWRITE_ALL_PRESENT)) { /* read in from media :-S */ udf_session->cache_line_r_present = 0; error = udf_read_physical_sectors(udf_session->disc, udf_session->cache_line_w_start, UDF_READWRITE_LINE_LENGTH, "cache line", udf_session->cache_line_read); if (error) { /* TODO try to fix-up please */ printf("Error reading physical sectors for cache for line_w_start %d ? : %s\n", udf_session->cache_line_w_start, strerror(error)); } assert(!error); udf_session->cache_line_r_start = udf_session->cache_line_w_start; udf_session->cache_line_r_present = UDF_READWRITE_ALL_PRESENT; for (cache_diff = 0; cache_diff < UDF_READWRITE_LINE_LENGTH; cache_diff++) { bit = (1 << cache_diff); if ((udf_session->cache_line_w_present & bit) == 0) { from = udf_session->cache_line_read + cache_diff * sector_size; to = udf_session->cache_line_write + cache_diff * sector_size; memcpy(to, from, sector_size); } udf_session->cache_line_w_present |= bit; } } assert(udf_session->cache_line_w_present == UDF_READWRITE_ALL_PRESENT); } assert(udf_session->cache_line_w_dirty); if (udf_session->cache_line_w_present != UDF_READWRITE_ALL_PRESENT) { /* count number of sectors present * (SEQUENTIAL?) */ start_sector = 0; cache_diff = 0; DEBUG(printf("Writing out non complete line\n")); DEBUG(printf("present %032o\n", udf_session->cache_line_w_present)); /* write out individual sectors */ while (cache_diff < UDF_READWRITE_LINE_LENGTH) { bit = (1 << cache_diff); if (udf_session->cache_line_w_present & bit) { start_sector = cache_diff; num_sectors = 1; /* calculate memory address and disc address */ from = udf_session->cache_line_write + start_sector * sector_size; start_sector += udf_session->session_offset + udf_session->cache_line_w_start; /* write! */ error = udf_write_physical_sectors(udf_session->disc, start_sector, num_sectors, "cache line (bits)", from); if (error) { error_bits |= bit; report_error = error; } else { udf_session->cache_line_w_dirty &= ~bit; } } cache_diff++; } } else { /* All present : calculate memory address and disc address */ from = udf_session->cache_line_write + start_sector * sector_size; start_sector += udf_session->session_offset + udf_session->cache_line_w_start; /* write! */ assert(num_sectors == UDF_READWRITE_LINE_LENGTH); error = udf_write_physical_sectors(udf_session->disc, start_sector, num_sectors, "cache line", from); if (error) { error_bits = UDF_READWRITE_ALL_PRESENT; } else { udf_session->cache_line_w_dirty = 0; } report_error = error; } if (error_bits) { /* ABORT/ROLLBACK */ for (cache_diff = 0; cache_diff < UDF_READWRITE_LINE_LENGTH; cache_diff++) { bit = (1 << cache_diff); if (error_bits & bit) { from = udf_session->cache_line_write + cache_diff * sector_size; callback = &udf_session->cache_write_callbacks[cache_diff]; udf_session->cache_line_w_present &= ~bit; if (callback->function) { callback->function(UDF_WRCALLBACK_REASON_ANULATE, callback, report_error, from); } else { fprintf(stderr, "WARNING: error encountered with NULL callback function\n"); } } } } return error; } /* XXX called directly OR called by purging dirty buffers out trough VOP_STRATEGY or trough VOP_INACTIVE XXX */ int udf_write_session_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *source, int rwflags, struct udf_wrcallback *wrcallback) { uint32_t eff_sector, bit, sector_size; int32_t cache_diff; int error; rwflags = rwflags; /* unused here */ what = what; /* unused for now */ assert(udf_session); assert(udf_session->cache_line_read); assert(udf_session->cache_line_write); sector_size = udf_session->disc->sector_size; /* XXX cache coherency ???? XXX */ error = 0; UDF_MUTEX_LOCK(&udf_session->session_cache_lock); eff_sector = udf_session->session_offset + sector; cache_diff = eff_sector - udf_session->cache_line_w_start; if (udf_session->cache_line_w_dirty && ((cache_diff < 0) || (cache_diff >= UDF_READWRITE_LINE_LENGTH))) { /* hmm... have to write out current write-cache */ udf_writeout_session_cache(udf_session); } if (udf_session->cache_line_w_dirty == 0) { if (udf_session->disc->strict_overwrite) { udf_session->cache_line_w_start = eff_sector & ~(UDF_READWRITE_LINE_LENGTH-1); } else { udf_session->cache_line_w_start = eff_sector; } cache_diff = eff_sector - udf_session->cache_line_w_start; udf_session->cache_line_w_present = 0; } if ((cache_diff >= 0) && (cache_diff < UDF_READWRITE_LINE_LENGTH)) { /* its in the cache range: overwrite current value */ bit = (1 << cache_diff); udf_session->cache_line_w_present |= bit; udf_session->cache_line_w_dirty |= bit; memcpy(udf_session->cache_line_write + cache_diff * sector_size, source, sector_size); if (wrcallback) memcpy(&udf_session->cache_write_callbacks[cache_diff], wrcallback, sizeof(struct udf_wrcallback)); else bzero(&udf_session->cache_write_callbacks[cache_diff], sizeof(struct udf_wrcallback)); ; UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock); return 0; } UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock); return error; } /* reads in 'logvol->lb_size' logical sector size bytes */ int udf_read_logvol_sector(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, uint8_t *buffer, uint32_t prefetch_sectors, int rwflags) { struct udf_partition *udf_partition; struct udf_part_mapping *udf_part_mapping; struct udf_session *udf_session; uint64_t ses_off, trans_valid_len; uint64_t offset; uint32_t length, trans_length, trans_sectors, readahead; uint32_t lb_size, sector_size; uint32_t ses_sector, ses_offset; int error; lb_size = udf_log_vol->lb_size; sector_size = udf_log_vol->sector_size; DEBUG( printf("Read logvol space for %s, from vpart %d, lb_num %d for logical sector size %d\n", what, vpart_num, (int)lb_num, (int) lb_size); ); error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition); if (error) return error; /* get the offset (in bytes) in the partition for translational purposes */ offset = (uint64_t) lb_num * lb_size; length = lb_size; udf_session = udf_partition->udf_session; do { trans_length = length; ses_sector = 0; /* TODO optimalisation: could use `trans_valid_len' and `prefetch_sectors' */ /* determine the translated address and its translation validity length */ error = udf_vpartoff_to_sessionoff(udf_log_vol, udf_part_mapping, udf_partition, offset, &ses_off, &trans_valid_len); if (error) break; ses_sector = ses_off / sector_size; ses_offset = ses_off % sector_size; assert(ses_offset == 0); trans_length = sector_size; trans_sectors = 1; /* estimate how much we could read-ahead given prefetch sectors and translation validation */ readahead = MIN(trans_valid_len, prefetch_sectors * lb_size); readahead = (readahead + sector_size -1) / sector_size; /* XXX could use partition_sector defs XXX */ error = udf_read_session_sector(udf_session, ses_sector, what, buffer + ses_offset, readahead, rwflags); if (error) break; /* advance to next block */ offset += trans_length; length -= trans_length; buffer += trans_length; prefetch_sectors -= trans_sectors; if (length == 0) return error; } while (length && !error); return EFAULT; } /* internal function; sector is allready a partition sector */ void udf_fillin_fids_sector(uint8_t *buffer, uint32_t *fid_pos, uint32_t max_fidpos, uint32_t sector, uint32_t sector_size) { struct fileid_desc *fid; uint32_t rfid_pos; uint32_t fid_len; assert(fid_pos); assert(buffer); rfid_pos = (*fid_pos) % sector_size; while (rfid_pos + sizeof(struct desc_tag) <= sector_size) { if ((*fid_pos) + sizeof(struct desc_tag) > max_fidpos) { return; } fid = (struct fileid_desc *) (buffer + (*fid_pos)); fid_len = udf_calc_tag_malloc_size((union dscrptr *) fid, sector_size); /* update sector number and recalculate checkum */ fid->tag.tag_loc = udf_rw32(sector); udf_validate_tag_sum((union dscrptr *) fid); *fid_pos += fid_len; rfid_pos += fid_len; } } /* writes out 'logvol->lb_size' logical sector size bytes */ /* XXX it ASSUMES that the translation is allready known/filled in (!) (offcource) XXX */ int udf_write_logvol_sector(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, uint8_t *buffer, int rwflags, struct udf_wrcallback *wrcallback) { struct udf_partition *udf_partition; struct udf_part_mapping *udf_part_mapping; struct udf_session *udf_session; union dscrptr *dscrptr; uint64_t ses_off, trans_valid_len; uint64_t offset; uint64_t length, trans_length; uint32_t lb_size, sector_size; uint32_t ses_sector, ses_offset; uint32_t fid_pos, max_fid_pos; int error, has_fids, recalc_crc, file_type; lb_size = udf_log_vol->lb_size; sector_size = udf_log_vol->sector_size; DEBUG( printf("Write logvol space for %s, rwflags = %d, from vpart %d, lb_num %d for logical sector size %d\n", what, rwflags, vpart_num, (int)lb_num, (int) lb_size); ); error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition); if (error) return error; /* get the offset (in bytes) in the partition for translational purposes */ offset = (uint64_t) lb_num * lb_size; length = lb_size; fid_pos = max_fid_pos = 0; has_fids = recalc_crc = 0; dscrptr = (union dscrptr *) buffer; /* doesn't have to be valid */ if (rwflags == UDF_C_FIDS) { /* FIDs in this sector need to be updated, so search the first FID by using the resync function */ DEBUG(printf("C_FIDS\n")); max_fid_pos = lb_size; udf_resync_fid_stream(buffer, &fid_pos, max_fid_pos, &has_fids); recalc_crc = 0; } if (rwflags == UDF_C_NODE) { DEBUG(printf("C_NODE\n")); /* if NODE with possibly an embedded FID stream -> have to patch up the FIDs (max one lbnum though) */ file_type = 0; if (udf_rw16(dscrptr->tag.id) == TAGID_FENTRY) { if ((udf_rw16(dscrptr->fe.icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) == UDF_ICB_INTERN_ALLOC) { DEBUG(printf("\tINTERN FE\n")); fid_pos = (dscrptr->fe.data - buffer) + udf_rw32(dscrptr->fe.l_ea); max_fid_pos = fid_pos + udf_rw64(dscrptr->fe.inf_len); has_fids = 1; recalc_crc = 1; file_type = dscrptr->fe.icbtag.file_type; /* 8 bit */ } } else { if ((udf_rw16(dscrptr->fe.icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) == UDF_ICB_INTERN_ALLOC) { DEBUG(printf("\tINTERN EFE\n")); fid_pos = (dscrptr->efe.data - buffer) + udf_rw32(dscrptr->efe.l_ea); max_fid_pos = fid_pos + udf_rw64(dscrptr->efe.inf_len); has_fids = 1; recalc_crc = 1; file_type = dscrptr->efe.icbtag.file_type; /* 8 bit */ } } if (!((file_type == UDF_ICB_FILETYPE_DIRECTORY) || (file_type == UDF_ICB_FILETYPE_STREAMDIR))) { has_fids = 0; } } DEBUG( if (rwflags == UDF_C_USERDATA) { printf("C_USERDATA\n"); } printf("has_fids = %d, fid_pos = %d, max_fid_pos = %d\n", has_fids, fid_pos, max_fid_pos); ); udf_session = udf_partition->udf_session; do { trans_length = length; ses_sector = 0; /* determine the translated address and its translation validity length */ error = udf_vpartoff_to_sessionoff(udf_log_vol, udf_part_mapping, udf_partition, offset, &ses_off, &trans_valid_len); if (error) break; ses_sector = ses_off / sector_size; ses_offset = ses_off % sector_size; assert(ses_offset == 0); /* FIDs need to be updated to include the correct physical sector */ if (has_fids) { udf_fillin_fids_sector(buffer, &fid_pos, max_fid_pos, lb_num, sector_size); if (recalc_crc) { udf_validate_tag_and_crc_sums(dscrptr); recalc_crc = 0; } } /* XXX optimalisation: could use more of `trans_valid_len' XXX */ trans_length = sector_size; /* XXX could use partition_sector defs XXX */ error = udf_write_session_sector(udf_session, ses_sector, what, buffer, rwflags, wrcallback); if (error) break; /* advance to next physical sector */ offset += trans_length; length -= trans_length; buffer += trans_length; /* really? */ DEBUG( printf("write logvol sector loop: recalc_crc = %d, offset = %d, length = %d, buffer = %p\n", recalc_crc, (uint32_t) offset, (uint32_t) length, buffer); ); if (length == 0) { return error; } } while (length && !error); return EFAULT; } /****************************************************************************************** * * Descriptor readers and writers * ******************************************************************************************/ /* * Read in an descriptor in either logvol space or in session space determined * by the specification of log_vol. * * In logvol space, lb_num specifies the logical block number in the logical * volume wich can be bigger than a sector. * * In session space, lb_num specifies a distinct sector. * * The function returns the read in descriptor blob and its length; it deals * with both short and long descriptors. */ int udf_read_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, struct udf_session *udf_session, uint32_t lb_num, char *what, uint32_t cache_flags, union dscrptr **dscr, uint32_t *length) { union dscrptr *cur_dscr, *new_dscr; void *sector0; uint32_t sector_size, num_sectors, sector; uint32_t cur_length, new_length; uint8_t *pos; int error; assert(dscr); if (length) *length = 0; *dscr = NULL; assert((udf_log_vol && !udf_session) || (!udf_log_vol && udf_session)); sector_size = udf_log_vol ? udf_log_vol->lb_size : (uint32_t) udf_session->disc->sector_size; /* All discriptors have a mimimum size of one sector be it logical or physical */ cur_length = sector_size; num_sectors = 1; sector0 = malloc(cur_length); cur_dscr = sector0; if (!sector0) { printf("\t\t\tOut of memory claiming memory for %s\n", what); return ENOMEM; } /* start reading in sector; read at offset 0 into the logic block */ if (udf_log_vol) { /* could read more in advance? */ error = udf_read_logvol_sector(udf_log_vol, vpart_num, lb_num, what, (uint8_t *) cur_dscr, num_sectors, cache_flags); } else { error = udf_read_session_sector(udf_session, lb_num, what, (uint8_t *) cur_dscr, num_sectors, cache_flags); } if (!error) error = udf_check_tag(cur_dscr); if (!error) { new_length = udf_calc_tag_malloc_size(cur_dscr, sector_size); DEBUG( if (new_length < (uint32_t) udf_rw16(cur_dscr->tag.desc_crc_len) + UDF_DESC_TAG_LENGTH) { printf("UDF warning: reading in %s for %d bytes but descriptor crc len is %d bytes\n", what, new_length, udf_rw16(cur_dscr->tag.desc_crc_len) + UDF_DESC_TAG_LENGTH); udf_dump_descriptor(cur_dscr); } ); if (new_length > cur_length) { /* extent the current descriptor; length is multiple of (logical or session) sector size */ num_sectors = (new_length + sector_size -1) / sector_size; new_length = num_sectors * sector_size; new_dscr = malloc(new_length); if (new_dscr) { /* copy read-in stuff into the new allocated space */ memcpy(new_dscr, sector0, cur_length); free(sector0); /* read in the additional sectors */ cur_dscr = new_dscr; cur_length = new_length; for (sector = 1; sector < num_sectors; sector++) { pos = ((uint8_t *) cur_dscr) + sector * sector_size; if (udf_log_vol) { /* could read more in advance? */ error = udf_read_logvol_sector(udf_log_vol, vpart_num, lb_num + sector, what, pos, num_sectors - sector, cache_flags); } else { error = udf_read_session_sector(udf_session, lb_num + sector, what, pos, num_sectors - sector, cache_flags); } } } else { free(sector0); } } } if (!error) { *dscr = cur_dscr; if (length) *length = cur_length; /* if requested return length */ error = udf_check_tag(*dscr); if (!error) error = udf_check_tag_payload(*dscr); } return error; } /* Reads descriptor as in currenly recorded on disc or as is in the cache */ int udf_read_session_descriptor(struct udf_session *udf_session, uint32_t lb_num, char *what, union dscrptr **dscr, uint32_t *length) { uint32_t cache_flags; cache_flags = UDF_C_DSCR; return udf_read_descriptor(NULL, 0, udf_session, lb_num, what, cache_flags, dscr, length); } /* Reads descriptor as in currenly recorded on disc or as is in the cache */ int udf_read_logvol_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, union dscrptr **dscr, uint32_t *length) { uint32_t cache_flags; cache_flags = UDF_C_DSCR; return udf_read_descriptor(udf_log_vol, vpart_num, NULL, lb_num, what, cache_flags, dscr, length); } static int udf_write_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, struct udf_session *udf_session, uint32_t lb_num, uint32_t dscr_lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback) { uint32_t dscr_length; uint32_t sector_size; uint32_t sector, num_sectors; uint8_t *pos; int error, rwflags; assert(dscr); assert((udf_log_vol && !udf_session) || (!udf_log_vol && udf_session)); sector_size = udf_log_vol ? udf_log_vol->lb_size : (uint32_t) udf_session->disc->sector_size; /* All discriptors have a mimimum size of one sector be it logical or physical */ num_sectors = 1; dscr_length = udf_calc_tag_malloc_size(dscr, sector_size); /* extent the current descriptor; length is multiple of (logical or session) sector size */ num_sectors = (dscr_length + sector_size -1) / sector_size; /* set the rwflags according to what kind of descriptor we are writing */ wrcallback->flags |= UDF_WRCALLBACK_FLAG_DESCRIPTOR; /* not needed? */ rwflags = UDF_C_DSCR; if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) rwflags = UDF_C_NODE; if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) rwflags = UDF_C_NODE; /* write out descriptor */ error = 0; if (udf_log_vol) { /* prepare descriptor for writing */ dscr->tag.tag_loc = udf_rw32(dscr_lb_num); udf_validate_tag_and_crc_sums(dscr); /* write sectors */ for (sector = 0; sector < num_sectors; sector++) { pos = ((uint8_t *) dscr) + sector * sector_size; /* wrcallback->function is given */ #if 0 wrcallback->udf_node = wrcallback->lb_num = lb_num + sector; wrcallback->length = sector_size; wrcallback->vpart_num = vpart_num; #endif DEBUG(printf("writing logical sector %8d for %s (sector offset %d)\n", lb_num + sector, what, sector)); error = udf_write_logvol_sector(udf_log_vol, vpart_num, lb_num + sector, what, pos, rwflags, wrcallback); if (error) break; } } else { /* prepare descriptor for writing */ dscr->tag.tag_loc = udf_rw32(dscr_lb_num); udf_validate_tag_and_crc_sums(dscr); /* write sectors */ for (sector = 0; sector < num_sectors; sector++) { pos = ((uint8_t *) dscr) + sector * sector_size; /* wrcallback->function is given */ #if 0 wrcallback->lb_num = lb_num; wrcallback->length = sector_size; #endif DEBUG(printf("writing sector %8d for %s (sector offset %d)\n", lb_num + sector, what, sector)); error = udf_write_session_sector(udf_session, lb_num + sector, what, pos, rwflags, wrcallback); if (error) break; } } return error; } /* Write descriptor trough cache if present */ int udf_write_session_descriptor(struct udf_session *udf_session, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback) { return udf_write_descriptor(NULL, 0, udf_session, lb_num, lb_num, what, dscr, wrcallback); } int udf_write_partition_descriptor(struct udf_partition *udf_partition, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback) { uint32_t dscr_lb_num; dscr_lb_num = udf_rw32(lb_num + udf_partition->partition->start_loc); return udf_write_descriptor(NULL, 0, udf_partition->udf_session, dscr_lb_num, lb_num, what, dscr, wrcallback); } /* Write descriptor trough cache if present */ int udf_write_logvol_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback) { return udf_write_descriptor(udf_log_vol, vpart_num, NULL, lb_num, lb_num, what, dscr, wrcallback); } /* end of udf_readwrite.c */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf_unix.c000066400000000000000000000470461523477242100261410ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf_unix.c" is part of the UDFclient toolkit. * File $Id: udf_unix.c,v 1.17 2016/04/25 21:01:40 reinoud Exp $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* XXX strip list to bare minimum XXX */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uscsilib.h" /* for locals */ #include "udf.h" #include "udf_bswap.h" #include "udf_discop.h" #include "udf_unix.h" #include "uio.h" #include #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) # define MIN(a,b) ((a)<(b)?(a):(b)) #endif /* #define DEBUG(a) { a; } */ #define DEBUG(a) if (0) { a; } /****************************************************************************************** * * Bufcache emulation * ******************************************************************************************/ /* shared bufcache structure */ struct udf_bufcache *udf_bufcache = NULL; int udf_unix_init(void) { if (udf_bufcache) { fprintf(stderr, "reinit unix_init?\n"); return 0; } udf_bufcache = calloc(1, sizeof(struct udf_bufcache)); assert(udf_bufcache); UDF_MUTEX_INIT(&udf_bufcache->bufcache_lock); TAILQ_INIT(&udf_bufcache->lru_bufs_data); TAILQ_INIT(&udf_bufcache->lru_bufs_metadata); pthread_cond_init(&udf_bufcache->purgethread_signal, NULL); pthread_mutex_init(&udf_bufcache->purgethread_lock, NULL); pthread_cond_init(&udf_bufcache->processed_signal, NULL); pthread_mutex_init(&udf_bufcache->processed_lock, NULL); return 0; } /* delete the buf entry */ void udf_free_buf_entry(struct udf_buf *buf_entry) { assert(udf_bufcache); buf_entry->b_vp = NULL; /* detach, i.e. recycle */ buf_entry->b_flags = 0; /* just in case */ udf_bufcache->bcnt--; free(buf_entry->b_data); free(buf_entry); } /* XXX knowledge of LBSIZE, FILETYPE, INTERNAL NODE (?) ! XXX */ /* must be called with bufcache lock ! */ int udf_get_buf_entry(struct udf_node *udf_node, struct udf_buf **buf_entry_p) { struct udf_log_vol *log_vol; struct udf_buf *buf_entry; uint32_t lb_size; assert(udf_node); assert(udf_bufcache); assert(buf_entry_p); log_vol = udf_node->udf_log_vol; lb_size = log_vol->lb_size; *buf_entry_p = NULL; buf_entry = NULL; if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) { if (udf_bufcache->lru_len_metadata >= UDF_LRU_METADATA_MIN) { /* kick writeout of data; if past max wait for space to continue */ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); udf_purgethread_kick("Data buffer surplus"); while (udf_bufcache->lru_len_metadata >= UDF_LRU_METADATA_MAX) { udf_purgethread_kick("Metadata buffer surplus"); /* wait for processed signal */ pthread_mutex_lock(&udf_bufcache->processed_lock); pthread_cond_wait(&udf_bufcache->processed_signal, &udf_bufcache->processed_lock); pthread_mutex_unlock(&udf_bufcache->processed_lock); } UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); } } else { if (udf_bufcache->lru_len_data >= UDF_LRU_DATA_MIN) { /* kick writeout of data; if past max wait for space to continue */ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); udf_purgethread_kick("Data buffer surplus"); while (udf_bufcache->lru_len_data >= UDF_LRU_DATA_MAX) { udf_purgethread_kick("Data buffer surplus"); /* wait for processed signal */ pthread_mutex_lock(&udf_bufcache->processed_lock); pthread_cond_wait(&udf_bufcache->processed_signal, &udf_bufcache->processed_lock); pthread_mutex_unlock(&udf_bufcache->processed_lock); } UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); } } /* create new buf_entry */ buf_entry = calloc(1, sizeof(struct udf_buf)); if (!buf_entry) return ENOMEM; buf_entry->b_data = calloc(1, lb_size); if (!buf_entry->b_data) { *buf_entry_p = NULL; free(buf_entry); return ENOMEM; } *buf_entry_p = buf_entry; /* fill in details */ buf_entry->b_bufsize = lb_size; buf_entry->b_bcount = 0; buf_entry->b_resid = lb_size; buf_entry->b_lblk = 0; buf_entry->b_flags = B_INVAL; buf_entry->b_vp = udf_node; /* not just NULL ? */ udf_bufcache->bcnt++; return 0; } /* really `out of the sky' hash formula */ uint32_t udf_calc_bufhash(struct udf_node *udf_node, uint32_t b_lblk) { return (udf_node->hashkey * 5 + b_lblk); } /* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */ void udf_mark_buf_needing_allocate(struct udf_node *udf_node, struct udf_buf *buf_entry) { uint32_t lb_size; assert(udf_node); /* assert(udf_node->buf_mutex.locked && udf_bufcache->bufcache_lock.locked); */ lb_size = udf_node->udf_log_vol->lb_size; /* if it isnt allready marked to eb allocated, allocate it and claim space */ if (!(buf_entry->b_flags & B_NEEDALLOC)) { udf_node->udf_log_vol->await_alloc_space += lb_size; buf_entry->b_flags |= B_NEEDALLOC; } } /* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */ void udf_mark_buf_allocated(struct udf_node *udf_node, struct udf_buf *buf_entry) { uint32_t lb_size; assert(udf_node); /* assert(udf_node->buf_mutex.locked && udf_bufcache->bufcache_lock.locked); */ lb_size = udf_node->udf_log_vol->lb_size; /* if it needed allocation, clear the flag and release the space */ if (buf_entry->b_flags & B_NEEDALLOC) { udf_node->udf_log_vol->await_alloc_space -= lb_size; buf_entry->b_flags &= ~B_NEEDALLOC; } } /* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */ void udf_mark_buf_dirty(struct udf_node *udf_node, struct udf_buf *buf_entry) { assert(udf_node); assert(buf_entry); assert(udf_node->buf_mutex.locked); assert(udf_bufcache->bufcache_lock.locked); if (buf_entry->b_flags & B_DIRTY) return; if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { udf_mark_buf_needing_allocate(udf_node, buf_entry); /* signal it needs allocation */ } if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) { udf_bufcache->lru_len_dirty_metadata++; } else { udf_bufcache->lru_len_dirty_data++; } buf_entry->b_flags |= B_DIRTY; udf_node->v_numoutput++; } /* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */ void udf_mark_buf_clean(struct udf_node *udf_node, struct udf_buf *buf_entry) { assert(udf_node); assert(buf_entry); assert(udf_node->buf_mutex.locked); assert(udf_bufcache->bufcache_lock.locked); if ((buf_entry->b_flags & B_DIRTY) == 0) return; if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) { udf_bufcache->lru_len_dirty_metadata--; } else { udf_bufcache->lru_len_dirty_data--; } buf_entry->b_flags &= ~B_DIRTY; assert(udf_node->v_numoutput >= 1); udf_node->v_numoutput--; } /* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */ int udf_attach_buf_to_node(struct udf_node *udf_node, struct udf_buf *buf_entry) { struct udf_buf_queue *lru_chain; struct udf_buf *buf, *lbuf; uint32_t hashkey, bucket; assert(udf_node); assert(buf_entry); assert(udf_node->buf_mutex.locked && udf_bufcache->bufcache_lock.locked); buf_entry->b_vp = udf_node; if (0) { /* * Insert ordered in list. In KERNEL: vnode->v_dirtyblkhd is a list * that can be reverse-sorted. Ordering not used yet thus commented * out. */ lbuf = TAILQ_LAST(&udf_node->vn_bufs, udf_buf_queue); if (lbuf) { if (buf_entry->b_lblk > lbuf->b_lblk) { TAILQ_INSERT_TAIL(&udf_node->vn_bufs, buf_entry, b_vnbufs); } else { buf = TAILQ_FIRST(&udf_node->vn_bufs); while (buf->b_lblk < buf_entry->b_lblk) { buf = TAILQ_NEXT(buf, b_vnbufs); } assert((buf->b_lblk != buf_entry->b_lblk) && (buf->b_vp == udf_node)); TAILQ_INSERT_BEFORE(buf, buf_entry, b_vnbufs); } } else { TAILQ_INSERT_HEAD(&udf_node->vn_bufs, buf_entry, b_vnbufs); } } else { TAILQ_INSERT_TAIL(&udf_node->vn_bufs, buf_entry, b_vnbufs); } /* fill buf into the bufcache */ hashkey = udf_calc_bufhash(udf_node, buf_entry->b_lblk); bucket = hashkey & UDF_BUFCACHE_HASHMASK; DEBUG( struct udf_buf *buf; /* checking for doubles */ LIST_FOREACH(buf, &udf_bufcache->udf_bufs[bucket], b_hash) { if ((buf->b_vp == udf_node) && (buf->b_lblk == buf_entry->b_lblk)) { printf("DOUBLE hashnode in UDF_BUFS!?\n"); exit(1); } } ); LIST_INSERT_HEAD(&udf_bufcache->udf_bufs[bucket], buf_entry, b_hash); /* queue it in the lru chain */ if ((udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) && (udf_node->udf_filetype != UDF_ICB_FILETYPE_REALTIME)) { lru_chain = &udf_bufcache->lru_bufs_metadata; udf_bufcache->lru_len_metadata++; } else { lru_chain = &udf_bufcache->lru_bufs_data; udf_bufcache->lru_len_data++; } TAILQ_INSERT_TAIL(lru_chain, buf_entry, b_lru); return 0; } /* kind of brelse() ? */ /* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */ int udf_detach_buf_from_node(struct udf_node *udf_node, struct udf_buf *buf_entry) { struct udf_buf_queue *lru_chain; assert(udf_node); assert(buf_entry); assert(udf_node->buf_mutex.locked && udf_bufcache->bufcache_lock.locked); /* remove from vnode admin */ TAILQ_REMOVE(&udf_node->vn_bufs, buf_entry, b_vnbufs); /* please don't forget this one */ if (buf_entry->b_flags & B_DIRTY) udf_node->v_numoutput--; /* remove from buffer cache */ LIST_REMOVE(buf_entry, b_hash); /* remove from lru lists */ if ((udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) && (udf_node->udf_filetype != UDF_ICB_FILETYPE_REALTIME)) { lru_chain = &udf_bufcache->lru_bufs_metadata; TAILQ_REMOVE(lru_chain, buf_entry, b_lru); udf_bufcache->lru_len_metadata--; } else { lru_chain = &udf_bufcache->lru_bufs_data; TAILQ_REMOVE(lru_chain, buf_entry, b_lru); udf_bufcache->lru_len_data--; } return 0; } /* bufcache lock has to be held! */ int udf_lookup_node_buf(struct udf_node *udf_node, uint32_t lblk, struct udf_buf **buf_p) { #ifdef UDF_METADATA_LRU struct udf_buf_queue *lru_chain; #endif struct udf_buf *buf; uint32_t hashkey, bucket; assert(udf_node); assert(udf_bufcache->bufcache_lock.locked); *buf_p = NULL; hashkey = udf_calc_bufhash(udf_node, lblk); bucket = hashkey & UDF_BUFCACHE_HASHMASK; LIST_FOREACH(buf, &udf_bufcache->udf_bufs[bucket], b_hash) { if ((buf->b_vp == udf_node) && (buf->b_lblk == lblk)) { *buf_p = buf; #ifdef UDF_METADATA_LRU lru_chain = &udf_bufcache->lru_bufs_data; if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) lru_chain = &udf_bufcache->lru_bufs_metadata; TAILQ_REMOVE(lru_chain, buf, b_lru); TAILQ_INSERT_TAIL(lru_chain, buf, b_lru); #endif break; /* for each */ } } return 0; } void *udf_purger(void *arg) { struct timespec wakeup; struct udf_buf *buf_entry, *marker; struct udf_node *udf_node; arg = arg; /* paramter not used */ marker = calloc(1, sizeof(struct udf_buf)); assert(marker); UDF_VERBOSE(printf("\tbufcache thread initialising\n")); while (1) { DEBUG(printf("UDF bufcache sync thread: waiting for lock\n")); /* * If we are not asked to finish up our writing, block to * wait for more data. Signal the reader to continue just in * case it is still stuck. */ if (!udf_bufcache->finish_purgethread) { do { /* determine the time we want to wake up again * */ clock_gettime(CLOCK_REALTIME, &wakeup); wakeup.tv_sec += UDF_BUFCACHE_IDLE_SECS; /* ask for more requests */ pthread_cond_signal(&udf_bufcache->processed_signal); pthread_mutex_lock(&udf_bufcache->purgethread_lock); pthread_cond_timedwait(&udf_bufcache->purgethread_signal, &udf_bufcache->purgethread_lock, &wakeup); pthread_mutex_unlock(&udf_bufcache->purgethread_lock); if (!udf_bufcache->purgethread_kicked) { /* UDF_VERBOSE_MAX(printf("\nUDF purger woke up due to timeout\n")); */ /* see if we would want to do something */ if (udf_bufcache->flushall) /* shouldn't happen */ break; if (udf_bufcache->lru_len_data >= UDF_LRU_DATA_MIN) break; /* we have something to do */ if (udf_bufcache->lru_len_metadata >= UDF_LRU_METADATA_MIN) break; /* we have something to do */ } /* else : we have been explicitly asked to do something */ } while (!udf_bufcache->purgethread_kicked && !udf_bufcache->finish_purgethread); } udf_bufcache->purgethread_kicked = 0; DEBUG(printf("UDF read/write thread: got activate\n")); UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); /* writeout surplus of dirty buffers */ /* PURGE surplus bufs if possible */ if ((udf_bufcache->lru_len_data >= UDF_LRU_DATA_MIN) || udf_bufcache->flushall) { DEBUG(printf("UDF read/write thread: purging dirty buffers\n")); /* getting too many dirty data buffers */ TAILQ_INSERT_HEAD(&udf_bufcache->lru_bufs_data, marker, b_lru); while ((buf_entry = TAILQ_NEXT(marker, b_lru))) { /* advance marker */ TAILQ_REMOVE(&udf_bufcache->lru_bufs_data, marker, b_lru); TAILQ_INSERT_AFTER(&udf_bufcache->lru_bufs_data, buf_entry, marker, b_lru); /* process buf_entry */ if ((buf_entry->b_flags & B_DIRTY) != 0) { if ((udf_bufcache->lru_len_dirty_data >= UDF_READWRITE_LINE_LENGTH*2) || udf_bufcache->flushall) { UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); /* signal there is time/space ahead and write out buffer */ pthread_cond_signal(&udf_bufcache->processed_signal); udf_writeout_file_buffer(buf_entry->b_vp, "dirty data buf", UDF_C_USERDATA, buf_entry); DEBUG(printf("."); fflush(stdout)); UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); } } /* if there are too many, drop least used */ if (udf_bufcache->lru_len_data >= UDF_LRU_DATA_MIN) { TAILQ_FOREACH(buf_entry, &udf_bufcache->lru_bufs_data, b_lru) { /* process buf_entry */ if ((buf_entry != marker) && ((buf_entry->b_flags & B_DIRTY) == 0)) { /* lock node bufs (locking protocol) */ udf_node = buf_entry->b_vp; if (udf_node) { UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_detach_buf_from_node(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); } else { printf("\n\nWARNING: got a NULL udf_node freeing dataspace\n\n"); } udf_free_buf_entry(buf_entry); /* signal there is time/space ahead */ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); pthread_cond_signal(&udf_bufcache->processed_signal); UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); break; /* mandatory leaving FOREACH */ } if (udf_bufcache->lru_len_data < UDF_LRU_DATA_MIN) break; /* foreach */ } } } TAILQ_REMOVE(&udf_bufcache->lru_bufs_data, marker, b_lru); } if ((udf_bufcache->lru_len_metadata >= UDF_LRU_METADATA_MIN) || udf_bufcache->flushall) { /* getting too many dirty metadata buffers */ TAILQ_INSERT_HEAD(&udf_bufcache->lru_bufs_metadata, marker, b_lru); while ((buf_entry = TAILQ_NEXT(marker, b_lru))) { /* advance marker */ TAILQ_REMOVE(&udf_bufcache->lru_bufs_metadata, marker, b_lru); TAILQ_INSERT_AFTER(&udf_bufcache->lru_bufs_metadata, buf_entry, marker, b_lru); /* process buf_entry */ if ((buf_entry->b_flags & B_DIRTY) != 0) { if ((udf_bufcache->lru_len_dirty_metadata >= UDF_READWRITE_LINE_LENGTH*2) || udf_bufcache->flushall) { UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); /* signal there is time/space ahead and writeout buffer */ pthread_cond_signal(&udf_bufcache->processed_signal); udf_writeout_file_buffer(buf_entry->b_vp, "dirty metadata buf", UDF_C_FIDS, buf_entry); DEBUG(printf("+"); fflush(stdout)); UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); } } /* if there are too many, drop least used */ if (udf_bufcache->lru_len_metadata >= UDF_LRU_METADATA_MIN) { TAILQ_FOREACH(buf_entry, &udf_bufcache->lru_bufs_metadata, b_lru) { /* process buf_entry */ if ((buf_entry != marker) && ((buf_entry->b_flags & B_DIRTY)) == 0) { /* lock node bufs (locking protocol); don't drop metadata from `held' nodes */ udf_node = buf_entry->b_vp; if (udf_node && ((!udf_node->hold) || udf_bufcache->flushall)) { UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_detach_buf_from_node(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); udf_free_buf_entry(buf_entry); /* signal there is time/space ahead */ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); pthread_cond_signal(&udf_bufcache->processed_signal); UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); break; /* mandatory leaving FOREACH */ } else { if (!udf_node) { printf("\n\nWARNING: got a NULL udf_node freeing METAspace\n\n"); } #ifdef UDF_METADATA_LRU TAILQ_REMOVE(&udf_bufcache->lru_bufs_metadata, buf_entry, b_lru); TAILQ_INSERT_TAIL(&udf_bufcache->lru_bufs_metadata, buf_entry, b_lru); #endif } } } } } TAILQ_REMOVE(&udf_bufcache->lru_bufs_metadata, marker, b_lru); } /* can only be used once */ udf_bufcache->flushall = 0; UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); /* PURGE nodes? (or only on request?) */ /* if asked to quit break out of the loop */ if (udf_bufcache->finish_purgethread) break; } UDF_VERBOSE(printf("\tbufcache thread joining\n")); pthread_exit(0); /* join */ /* not reached */ return NULL; } int udf_start_unix_thread(void) { /* * start up bufcache purger thread and crudely kick it into * existence. */ if (udf_bufcache->thread_active) { fprintf(stderr,"\tlogvol bufcache thread asked to start again; ignoring\n"); return 0; } DEBUG(printf("\tstarting logvol bufcache thread\n")); udf_bufcache->thread_active = 1; pthread_create(&udf_bufcache->purgethread_id, NULL, udf_purger, NULL); sleep(1); DEBUG(printf("\n\n")); return 0; } int udf_stop_unix_thread(void) { /* stop all associated threads */ UDF_VERBOSE(printf("\tstopping bufcache thread\n")); if (udf_bufcache->thread_active) { udf_bufcache->purgethread_kicked = 1; udf_bufcache->finish_purgethread = 1; pthread_cond_signal(&udf_bufcache->purgethread_signal); pthread_join(udf_bufcache->purgethread_id, NULL); /* wait for join */ } udf_bufcache->thread_active = 0; return 0; } int udf_purgethread_kick(char *why) { /* * Kick the cache purger into existence in case its not active and wait * for it to signal there is space left. */ DEBUG(printf("\npurgethread kick! because of %s\n", why)); udf_bufcache->purgethread_kicked = 1; pthread_cond_signal(&udf_bufcache->purgethread_signal); return 0; } /* end of udf_unix.c */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf_unix.h000066400000000000000000000146471523477242100261470ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf.h" is part of the UDFclient toolkit. * File $Id: udf_unix.h,v 1.5 2011/02/01 20:43:41 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _UDF_UNIX_H_ #define _UDF_UNIX_H_ #include "udf.h" /* Predefines */ struct udf_pri_vol; struct udf_node; /* * Provides :: system buffer structure for file/metadata caching * * ... free after struct buf ... */ struct udf_buf { UDF_MUTEX(b_interlock); uint32_t b_lblk; /* logical block number for vnode */ struct udf_node *b_vp; /* points to its parent udf_node */ uint32_t b_flags; /* B_* flags */ uint8_t *b_data; /* buffer itself */ uint8_t *b_private; /* private pointer for FS */ uint32_t b_bufsize; /* allocated size */ uint32_t b_bcount; /* size used */ uint32_t b_resid; /* size remaining */ LIST_ENTRY(udf_buf) b_hash; TAILQ_ENTRY(udf_buf) b_vnbufs; /* used for vnode bufs */ TAILQ_ENTRY(udf_buf) b_lru; /* lru queue (reuse) */ }; TAILQ_HEAD(udf_buf_queue, udf_buf); /* * These flags are kept in b_flags. Copied from NetBSD's */ #define B_AGE 0x00000001 /* Move to age queue when I/O done. */ #define B_ASYNC 0x00000004 /* Start I/O, do not wait. */ #define B_BAD 0x00000008 /* Bad block revectoring in progress. */ #define B_BUSY 0x00000010 /* I/O in progress. */ #define B_SCANNED 0x00000020 /* Block already pushed during sync */ #define B_CALL 0x00000040 /* Call b_iodone from biodone. */ #define B_DELWRI 0x00000080 /* Delay I/O until buffer reused. */ #define B_DIRTY 0x00000100 /* Dirty page to be pushed out async. */ #define B_DONE 0x00000200 /* I/O completed. */ #define B_EINTR 0x00000400 /* I/O was interrupted */ #define B_ERROR 0x00000800 /* I/O error occurred. */ #define B_GATHERED 0x00001000 /* LFS: already in a segment. */ #define B_INVAL 0x00002000 /* Does not contain valid info. */ #define B_LOCKED 0x00004000 /* Locked in core (not reusable). */ #define B_NOCACHE 0x00008000 /* Do not cache block after use. */ #define B_CACHE 0x00020000 /* Bread found us in the cache. */ #define B_PHYS 0x00040000 /* I/O to user memory. */ #define B_RAW 0x00080000 /* Set by physio for raw transfers. */ #define B_READ 0x00100000 /* Read buffer. */ #define B_TAPE 0x00200000 /* Magnetic tape I/O. */ #define B_WANTED 0x00800000 /* Process wants this buffer. */ #define B_WRITE 0x00000000 /* Write buffer (pseudo flag). */ #define B_XXX 0x02000000 /* Debugging flag. */ #define B_VFLUSH 0x04000000 /* Buffer is being synced. */ #define B_NEEDALLOC 0x08000000 /* TEMP */ #define BUF_FLAGBITS \ "\20\1AGE\3ASYNC\4BAD\5BUSY\6SCANNED\7CALL\10DELWRI" \ "\11DIRTY\12DONE\13EINTR\14ERROR\15GATHERED\16INVAL\17LOCKED\20NOCACHE" \ "\22CACHE\23PHYS\24RAW\25READ\26TAPE\30WANTED\32XXX\33VFLUSH" struct udf_bufcache { /* udf_node buf's multiplexed in one hashtable on (inode, b_lblk) */ LIST_HEAD(bufcache, udf_buf) udf_bufs[UDF_BUFCACHE_HASHSIZE]; UDF_MUTEX(bufcache_lock); int32_t bcnt; /* temp var counting alloc/free */ uint32_t lru_len_data, lru_len_metadata; uint32_t lru_len_dirty_data, lru_len_dirty_metadata; struct udf_buf_queue lru_bufs_data; struct udf_buf_queue lru_bufs_metadata; /* thread support for bufs & nodes purge (bufcache emulation) */ pthread_t purgethread_id; pthread_mutex_t purgethread_lock; /* lock the thread code out */ pthread_cond_t purgethread_signal; /* signal there is work to be done */ int purgethread_kicked; /* sanity for spurious wakeups */ pthread_mutex_t processed_lock; /* lock for main threads to wait on */ pthread_cond_t processed_signal; /* signals an action has been done */ int thread_active; int finish_purgethread; /* ask thread to stop what its doing and exit */ int flushall; /* flusha all dirty buffers */ }; extern struct udf_bufcache *udf_bufcache; /* bufcache emulation */ extern int udf_get_buf_entry(struct udf_node *udf_node, struct udf_buf **buf_entry_p); extern void udf_free_buf_entry(struct udf_buf *buf_entry); extern int udf_attach_buf_to_node(struct udf_node *udf_node, struct udf_buf *buf_entry); extern int udf_detach_buf_from_node(struct udf_node *udf_node, struct udf_buf *buf_entry); extern int udf_build_udf_node(struct udf_node *dir_node, struct fileid_desc *fid, struct udf_node **res_sub_node); extern int udf_lookup_node_buf(struct udf_node *udf_node, uint32_t lblk, struct udf_buf **buf_p); extern void udf_mark_buf_clean(struct udf_node *udf_node, struct udf_buf *buf_entry); extern void udf_mark_buf_dirty(struct udf_node *udf_node, struct udf_buf *buf_entry); /* maybe not strictly udf_unix */ extern void udf_mark_buf_needing_allocate(struct udf_node *udf_node, struct udf_buf *buf_entry); extern void udf_mark_buf_allocated(struct udf_node *udf_node, struct udf_buf *buf_entry); extern int udf_unix_init(void); extern int udf_start_unix_thread(void); extern int udf_stop_unix_thread(void); /* HELP ! need good clear closedown */ /* TEMP */ extern int udf_purgethread_kick(char *why); #endif /* _UDF_UNIX_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udf_verbose.c000066400000000000000000001617551523477242100266270ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udf_verbose.c" is part of the UDFclient toolkit. * File $Id: udf_verbose.c,v 1.121 2022/04/22 15:24:24 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include "udf.h" #include "udf_bswap.h" /* globals */ void udf_dump_id(char *prefix, int len, char *id, struct charspec *chsp); void udf_dump_long_ad(char *prefix, struct long_ad *adr); void udf_dump_descriptor(union dscrptr *dscrpt); void udf_dump_vat_table(struct udf_part_mapping *udf_part_mapping); void udf_dump_unimpl(union dscrptr *dscrpt, char *name) { dscrpt = dscrpt; printf("\t\t(unimplemented dump of `%s` tag)\n", name); } void udf_dump_desc(struct desc_tag *tag) { printf("\tTAG: descriptor %d, serial_num %d at sector %d, crc length %d bytes\n", udf_rw16(tag->id), udf_rw16(tag->serial_num), udf_rw32(tag->tag_loc), udf_rw16(tag->desc_crc_len)); } void udf_dump_anchor(struct anchor_vdp *vdp) { printf("\t\tAnchor\n"); printf("\t\t\tMain volume descriptor set at %d for %d bytes\n", udf_rw32(vdp->main_vds_ex.loc), udf_rw32(vdp->main_vds_ex.len)); printf("\t\t\tReserve volume descriptor set at %d for %d bytes\n", udf_rw32(vdp->reserve_vds_ex.loc), udf_rw32(vdp->reserve_vds_ex.len)); } void udf_dump_disc_anchors(struct udf_discinfo *disc) { int session; printf("\nUDF Dump of disc in device %s\n", disc->dev->dev_name); printf("UDF sessions : "); for (session = 0; session < disc->num_sessions; session++) { if (disc->session_is_UDF[session]) { printf("Yes"); #if 0 if (disc->session_quirks[session] & CD_SESS_QUIRK_SESSION_LOCAL) { printf("(local)"); } #endif printf(" "); } else { printf("No "); } } printf("\n\n"); UDF_VERBOSE_TABLES( struct udf_session *udf_session; /* Dump anchors */ STAILQ_FOREACH(udf_session, &disc->sessions, next_session) { printf("UDF session %d (lba %d + %d sectors) anchor dump : \n", udf_session->session_num, (uint32_t) disc->session_start[udf_session->session_num], (uint32_t) udf_session->session_length); udf_dump_descriptor((union dscrptr *) &udf_session->anchor); } ) } char *udf_messy_unicode_conv(char *buf) { static char out_buf[1024]; uint16_t *uni_pos, uni_char; char *pos; pos = out_buf; uni_pos = (uint16_t *) buf; while ((uni_char = *uni_pos++)) { if (uni_char & 0xff00) uni_char='_'; *pos++ = uni_char; } return out_buf; } char *udf_get_osname(int os_class, int os_id) { static char buffer[40]; switch (os_class) { case 0 : return "undefined OS"; case 1 : return "DOS/Windows 3.x"; case 2 : return "OS/2"; case 3 : return "MacOS"; case 4 : switch (os_id) { case 0 : return "UNIX"; case 1 : return "IBM AIX"; case 2 : return "SunOS/Solaris"; case 3 : return "HP/UX"; case 4 : return "Silicon Graphics Irix"; case 5 : return "Linux"; case 6 : return "MKLinux"; case 7 : return "FreeBSD"; case 8 : return "NetBSD"; default : sprintf(buffer, "unknown UNIX (%d)", os_id); return buffer; } case 5 : return "MS Windows 9x"; case 6 : return "MS Windows NT"; case 7 : return "OS/400"; case 8 : return "BeOS"; case 9 : return "MS Windows CE"; default : break; } sprintf(buffer, "unknown OS (%d, %d)", os_class, os_id); return buffer; } void udf_dump_regid(char *prefix, struct regid *id, int regid_type) { char buffer[UDF_REGID_ID_SIZE+1]; int cnt, version; uint8_t *pos; memcpy(buffer, id->id, UDF_REGID_ID_SIZE); buffer[UDF_REGID_ID_SIZE] = 0; printf("%s `%s`", prefix, buffer); if (regid_type == UDF_REGID_NAME) { printf("\n"); return; } printf(" ("); pos = id->id_suffix; switch (regid_type) { case UDF_REGID_DOMAIN : version = udf_rw16(*((uint16_t *) pos)); printf("UDFv %x; ", version); if ((pos[2]) & UDF_DOMAIN_FLAG_HARD_WRITE_PROTECT) printf("HARD "); if ((pos[2]) & UDF_DOMAIN_FLAG_SOFT_WRITE_PROTECT) printf("SOFT"); if (((pos[2]) & 3) == 0) printf("no"); printf(" write protect "); if ((pos[2]) & ~3) printf("; also undefined flags 0x%d", pos[2] & ~3); break; case UDF_REGID_UDF : version = udf_rw16(*((uint16_t *) pos)); printf("UDFv %x; ", version); printf("%s ", udf_get_osname(pos[2], pos[3])); break; case UDF_REGID_IMPLEMENTATION : printf("%s [", udf_get_osname(pos[0], pos[1])); for(cnt=2; cnt < 8; cnt++) { printf("%02x ", *pos++); } printf("]"); break; case UDF_REGID_NAME : break; case UDF_REGID_APPLICATION : default : printf("["); for(cnt=0; cnt < 8; cnt++) { printf("%02x ", *pos++); } printf("]"); break; } printf(") (flags=%d)\n", id->flags); } void udf_dump_timestamp(char *prefix, struct timestamp *t) { printf("%s (%4d %02d %02d at %02d:%02d:%02d.%02d.%02d.%02d)\n", prefix, udf_rw16(t->year), t->month, t->day, t->hour, t->minute, t->second, t->centisec, t->hund_usec, t->usec); } #if 0 void udf_dump_charspec(char *prefix, struct charspec *chsp) { int cnt, ch; printf("%s type CS%d (", prefix, chsp->type); for (cnt=0; cnt<63; cnt++) { ch = chsp->inf[cnt]; if (ch < 32 || ch > 126) { printf("."); } else { printf("%c", ch); } } printf(")\n"); } #endif void udf_dump_sparing_table(struct udf_sparing_table *spt) { struct spare_map_entry *sp_entry; uint32_t entry, entries; printf("\t\tSparing table descriptor\n"); udf_dump_regid("\t\t\tSparing table Id ", &spt->id, UDF_REGID_UDF); printf("\t\t\tRelocation table entries %d\n", udf_rw16(spt->rt_l)); printf("\t\t\tSequence number %d\n", udf_rw32(spt->seq_num)); printf("\t\t\tMappings :"); entries = udf_rw16(spt->rt_l); for(entry = 0; entry < entries; entry++) { if (entry % 4 == 0) printf("\n\t\t\t\t"); sp_entry = &spt->entries[entry]; printf("[%08x -> %08x] ", udf_rw32(sp_entry->org), udf_rw32(sp_entry->map)); } printf("\n"); } void udf_dump_pri_vol(struct pri_vol_desc *pvd) { struct extent_ad *ext; printf("\t\tPrimary volume descriptor\n"); printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(pvd->seq_num)); printf("\t\t\tPrimary volume descriptor number %d\n", udf_rw32(pvd->pvd_num)); udf_dump_id("\t\t\tVolume Id ", 32, pvd->vol_id, &pvd->desc_charset); printf("\t\t\tVolume sequence number %d\n", udf_rw16(pvd->vds_num)); printf("\t\t\tMaximum volume sequence number %d\n", udf_rw16(pvd->max_vol_seq)); printf("\t\t\tInterchange level %d\n", udf_rw16(pvd->ichg_lvl)); printf("\t\t\tMaximum interchange level %d\n", udf_rw16(pvd->max_ichg_lvl)); udf_dump_id("\t\t\tVolume set Id ", 128, pvd->volset_id, &pvd->desc_charset); /* udf_dump_charspec("\t\t\tCharspec for this descriptor ", &pvd->desc_charset); */ /* udf_dump_charspec("\t\t\tCharspec for the explaination ", &pvd->explanatory_charset); */ ext = &pvd->vol_abstract; printf("\t\t\tVolume abstract at %d for %d bytes\n", udf_rw32(ext->loc), udf_rw32(ext->len)); ext = &pvd->vol_copyright; printf("\t\t\tVolume copyright at %d for %d bytes\n", udf_rw32(ext->loc), udf_rw32(ext->len)); udf_dump_regid("\t\t\tApplication id", &pvd->app_id, UDF_REGID_APPLICATION); udf_dump_timestamp("\t\t\tTimestamp", &pvd->time); udf_dump_regid("\t\t\tImplementator id", &pvd->imp_id, UDF_REGID_IMPLEMENTATION); printf("\t\t\tPrevious volume descriptor sequence locator at sector %d\n", udf_rw32(pvd->prev_vds_loc)); printf("\t\t\tFlags %d\n", udf_rw16(pvd->flags)); } void udf_dump_implementation_volume(struct impvol_desc *ivd) { struct charspec *charspec; printf("\t\tImplementation use volume descriptor\n"); printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(ivd->seq_num)); udf_dump_regid("\t\t\tImplementator identifier", &ivd->impl_id, UDF_REGID_UDF); /* check on UDF implementation info ... */ if (strcmp((char *) ivd->impl_id.id, "*UDF LV Info") == 0) { charspec = &ivd->_impl_use.lv_info.lvi_charset; /* udf_dump_charspec("\t\t\tLV info charspec ", charspec); */ udf_dump_id("\t\t\tLogical volume identifier ", 128, ivd->_impl_use.lv_info.logvol_id, charspec); udf_dump_id("\t\t\tLV info 1 ", 36, ivd->_impl_use.lv_info.lvinfo1, charspec); udf_dump_id("\t\t\tLV info 2 ", 36, ivd->_impl_use.lv_info.lvinfo2, charspec); udf_dump_id("\t\t\tLV info 3 ", 36, ivd->_impl_use.lv_info.lvinfo3, charspec); udf_dump_regid("\t\t\tImplementation identifier", &ivd->_impl_use.lv_info.impl_id, UDF_REGID_IMPLEMENTATION); } } char *udf_dump_partition_access_type(int type) { switch (type) { case UDF_ACCESSTYPE_PSEUDO_OVERWITE : return "Pseudo overwiteable"; case UDF_ACCESSTYPE_READ_ONLY : return "Read only"; case UDF_ACCESSTYPE_WRITE_ONCE : return "Write once"; case UDF_ACCESSTYPE_REWRITEABLE : return "Rewritable (blocked or with erase)"; case UDF_ACCESSTYPE_OVERWRITABLE : return "Overwritable"; } return "Unknown partion access type"; } void udf_dump_part(struct part_desc *pd) { struct part_hdr_desc *part_hdr_desc; printf("\t\tPartition descriptor\n"); printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(pd->seq_num)); printf("\t\t\tFlags %d\n", udf_rw16(pd->flags)); printf("\t\t\tPartition number %d\n", udf_rw16(pd->part_num)); udf_dump_regid("\t\t\tContents", &pd->contents, UDF_REGID_APPLICATION); printf("\t\t\tAccessType %s\n", udf_dump_partition_access_type(udf_rw32(pd->access_type))); printf("\t\t\tPartition starts at sector %u for %u sectors\n", udf_rw32(pd->start_loc), udf_rw32(pd->part_len)); udf_dump_regid("\t\t\tImplementator id", &pd->imp_id, UDF_REGID_IMPLEMENTATION); printf("\t\t\tPartition contents use (file) descriptors:\n"); if (strncmp((char *) pd->contents.id, "+NSR0", 5) == 0) { part_hdr_desc = &pd->pd_part_hdr; printf("\t\t\t\tUnallocated space table at logic block %u for %u bytes\n", udf_rw32(part_hdr_desc->unalloc_space_table.lb_num), udf_rw32(part_hdr_desc->unalloc_space_table.len) ); printf("\t\t\t\tUnallocated space bitmap at logic block %u for %u bytes\n", udf_rw32(part_hdr_desc->unalloc_space_bitmap.lb_num), udf_rw32(part_hdr_desc->unalloc_space_bitmap.len) ); printf("\t\t\t\tPartition integrity table at logic block %u for %u bytes\n", udf_rw32(part_hdr_desc->part_integrity_table.lb_num), udf_rw32(part_hdr_desc->part_integrity_table.len) ); printf("\t\t\t\tReusable (freed) space table at logic block %u for %u bytes\n", udf_rw32(part_hdr_desc->freed_space_table.lb_num), udf_rw32(part_hdr_desc->freed_space_table.len) ); printf("\t\t\t\tReusable (freed) space bitmap at logic block %u for %u bytes\n", udf_rw32(part_hdr_desc->freed_space_bitmap.lb_num), udf_rw32(part_hdr_desc->freed_space_bitmap.len) ); } else { printf("\t\t\t\tWARNING: Unknown or unused contents\n"); } } void udf_dump_log_vol(struct logvol_desc *lvd) { union udf_pmap *pmap; uint8_t pmap_type, pmap_size; uint8_t *pmap_pos; uint32_t map, sparing_table; uint32_t lb_size, packet_len; lb_size = udf_rw32(lvd->lb_size); printf("\t\tLogical volume descriptor\n"); printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(lvd->seq_num)); udf_dump_id("\t\t\tLogical volume id ", 128, lvd->logvol_id, &lvd->desc_charset); printf("\t\t\tLogical block size %d\n", udf_rw32(lvd->lb_size)); udf_dump_regid("\t\t\tDomainId", &lvd->domain_id, UDF_REGID_DOMAIN); udf_dump_long_ad("\t\t\tFileset descriptor at", &lvd->_lvd_use.fsd_loc); printf("\t\t\tMap table length %d\n", udf_rw32(lvd->mt_l)); printf("\t\t\tNumber of part maps %d\n", udf_rw32(lvd->n_pm)); udf_dump_regid("\t\t\tImplementation id", &lvd->imp_id, UDF_REGID_IMPLEMENTATION); printf("\t\t\tIntegrety sequence at %d for %d bytes\n", udf_rw32(lvd->integrity_seq_loc.loc), udf_rw32(lvd->integrity_seq_loc.len)); printf("\t\t\tPartion maps follow\n"); pmap_pos = &lvd->maps[0]; for (map = 0; map < udf_rw32(lvd->n_pm); map++) { pmap = (union udf_pmap *) pmap_pos; pmap_type = pmap->data[0]; pmap_size = pmap->data[1]; printf("\t\t\t\tPartion map type %d length %d \n", pmap_type, pmap_size); /* only pmap types 1 and pmap types 2 are to be used */ printf("\t\t\t\t\tLogical %d maps to ", map); switch (pmap_type) { case 1 : printf("partition %d on volume seq. number %d directly\n", udf_rw16(pmap->pm1.part_num), udf_rw16(pmap->pm1.vol_seq_num)); break; case 2 : printf("partition %d on volume seq. number %d using\n", udf_rw16(pmap->pm2.part_num), udf_rw16(pmap->pm2.vol_seq_num)); udf_dump_regid("\t\t\t\t\tmapping type", &pmap->pm2.part_id, UDF_REGID_UDF); if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Virtual Partition", UDF_REGID_ID_SIZE) == 0) { /* nothing to print... */ } if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Sparable Partition", UDF_REGID_ID_SIZE) == 0) { packet_len = udf_rw16(pmap->pms.packet_len); printf("\t\t\t\t\t\tPacket length %d sectors (%d bytes)\n", packet_len, packet_len * lb_size); printf("\t\t\t\t\t\tNumber of sparing tables %d\n", pmap->pms.n_st); printf("\t\t\t\t\t\tSize of each sparing table %d\n", udf_rw32(pmap->pms.st_size)); if (pmap->pms.n_st) { printf("\t\t\t\t\t\tSparing tables at sectors "); for (sparing_table = 0; sparing_table < pmap->pms.n_st; sparing_table++) { printf("%d ", udf_rw32(pmap->pms.st_loc[sparing_table])); } printf("\n"); } } if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Metadata Partition", UDF_REGID_ID_SIZE) == 0) { printf("\t\t\t\t\t\tMetadata is %sduplicated on disc\n", pmap->pmm.flags & METADATA_DUPLICATED ? "":"NOT "); printf("\t\t\t\t\t\tAllocation unit size %d sectors\n", udf_rw32(pmap->pmm.alloc_unit_size)); printf("\t\t\t\t\t\tAlignment unit size %d sectors\n", udf_rw32(pmap->pmm.alignment_unit_size)); printf("\t\t\t\t\t\tMetadata file at part. sector %d\n", udf_rw32(pmap->pmm.meta_file_lbn)); if (udf_rw32(pmap->pmm.meta_mirror_file_lbn) != (uint32_t) -1) printf("\t\t\t\t\t\tMetadata mirror file at part. sector %d\n", udf_rw32(pmap->pmm.meta_mirror_file_lbn)); if (udf_rw32(pmap->pmm.meta_bitmap_file_lbn) != (uint32_t) -1) printf("\t\t\t\t\t\tMetadata bitmap file at part. sector %d\n", udf_rw32(pmap->pmm.meta_bitmap_file_lbn)); } break; default : break; } pmap_pos += pmap_size; } } void udf_dump_unalloc_space(struct unalloc_sp_desc *usd) { struct extent_ad *alloc_desc; uint32_t desc_num; printf("\t\tUnallocated space descriptor\n"); printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(usd->seq_num)); printf("\t\t\tNumber of free space slots %d\n", udf_rw32(usd->alloc_desc_num)); if (udf_rw32(usd->alloc_desc_num)) { printf("\t\t\tFree space at : "); for (desc_num = 0; desc_num < udf_rw32(usd->alloc_desc_num); desc_num++) { alloc_desc = &usd->alloc_desc[desc_num]; printf("[%d %d] ", udf_rw32(alloc_desc->loc), udf_rw32(alloc_desc->loc)+udf_rw32(alloc_desc->len)); } printf("\n"); } } void udf_dump_terminating_desc(union dscrptr *desc) { desc = desc; printf("\t\tTerminating descriptor\n"); } void udf_dump_logvol_integrity(struct logvol_int_desc *lvid) { struct udf_logvol_info *impl; uint32_t part, num_part; uint32_t *pos1, *pos2; uint32_t free, size, rest_bytes; uint32_t version; const char *inttp; printf("\t\tLogical volume integrity descriptor\n"); udf_dump_timestamp("\t\t\tTimestamp ", &lvid->time); inttp = "UNKNOWN/INVALID"; if (udf_rw32(lvid->integrity_type) == UDF_INTEGRITY_CLOSED) inttp = "closed"; if (udf_rw32(lvid->integrity_type) == UDF_INTEGRITY_OPEN) inttp = "closed"; printf("\t\t\tIntegrity type %s\n", inttp); printf("\t\t\tNext integrity sequence at %d for %d bytes\n", udf_rw32(lvid->next_extent.loc), udf_rw32(lvid->next_extent.len)); printf("\t\t\tNext free unique file ID %d\n", (uint32_t) udf_rw64(lvid->lvint_next_unique_id)); printf("\t\t\tLength of implementation use area %d bytes\n", udf_rw32(lvid->l_iu)); num_part = udf_rw32(lvid->num_part); printf("\t\t\tNumber of partitions %d\n", num_part); for (part=0; part < num_part; part++) { pos1 = &lvid->tables[0] + part; pos2 = &lvid->tables[0] + num_part + part; free = udf_rw32(*pos1); size = udf_rw32(*pos2); printf("\t\t\tPartition %d : %u blocks free space out of %u blocks\n", part, free, size); } /* printout the implementation use field */ impl = (struct udf_logvol_info *) (lvid->tables + 2*num_part); udf_dump_regid("\t\t\tImplemenator Id", &impl->impl_id, UDF_REGID_IMPLEMENTATION ); printf("\t\t\tNumber of files %d\n", udf_rw32(impl->num_files)); printf("\t\t\tNumber of directories %d\n", udf_rw32(impl->num_directories)); version = udf_rw16(impl->min_udf_readver); printf("\t\t\tMinimum readversion UDFv %x\n", version); version = udf_rw16(impl->min_udf_writever); printf("\t\t\tMinimum writeversion UDFv %x\n", version); version = udf_rw16(impl->max_udf_writever); printf("\t\t\tMaximum writeversion UDFv %x\n", version); rest_bytes = udf_rw32(lvid->l_iu)-sizeof(struct udf_logvol_info); if (rest_bytes > 0) printf("\t\t\t<%d bytes of undumped extra implementation use area>", rest_bytes); printf("\n"); } void udf_dump_fileset_desc(struct fileset_desc *fsd) { printf("\t\tFileset descriptor\n"); udf_dump_timestamp("\t\t\tTimestamp ", &fsd->time); printf("\t\t\tInterchange level %d\n", udf_rw16(fsd->ichg_lvl)); printf("\t\t\tMax interchange level %d\n", udf_rw16(fsd->max_ichg_lvl)); printf("\t\t\tCharset lists %d\n", udf_rw32(fsd->charset_list)); printf("\t\t\tMax charset lists %d\n", udf_rw32(fsd->max_charset_list)); printf("\t\t\tFileset number %d\n", udf_rw32(fsd->fileset_num)); printf("\t\t\tFileset descriptor number %d\n", udf_rw32(fsd->fileset_desc_num)); /* udf_dump_charspec("\t\t\tLogical volume id charspec ", &fsd->logvol_id_charset); */ /* udf_dump_charspec("\t\t\tFileset id charspec ", &fsd->fileset_charset); */ udf_dump_id("\t\t\tLogical volume id ", 128, fsd->logvol_id, &fsd->logvol_id_charset); udf_dump_id("\t\t\tFileset id ", 32, fsd->fileset_id, &fsd->fileset_charset); udf_dump_id("\t\t\tCopyright file id ", 32, fsd->copyright_file_id, &fsd->fileset_charset); udf_dump_id("\t\t\tAbstract file id ", 32, fsd->abstract_file_id, &fsd->fileset_charset); udf_dump_regid("\t\t\tDomainId", &fsd->domain_id, UDF_REGID_DOMAIN); udf_dump_long_ad("\t\t\tRootdir ICB found ", &fsd->rootdir_icb); udf_dump_long_ad("\t\t\tNext extend for fileset ", &fsd->next_ex); udf_dump_long_ad("\t\t\tStreamdir ICB found ", &fsd->streamdir_icb); } void udf_dump_fileid_in_charspec(struct fileid_desc *fid, struct charspec *chsp) { char *pos, file_char; printf("\tFile id entry\n"); printf("\t\tFile version number %d\n", udf_rw16(fid->file_version_num)); file_char = fid->file_char; printf("\t\tFile characteristics %d :\t", file_char); if (file_char & UDF_FILE_CHAR_VIS) printf("hidden "); if (file_char & UDF_FILE_CHAR_DEL) printf("deleted "); if (file_char & UDF_FILE_CHAR_PAR) printf("parent(..) "); if (file_char & UDF_FILE_CHAR_DIR) printf("directory "); if (file_char & UDF_FILE_CHAR_META) printf("METADATA "); printf("\n"); udf_dump_long_ad("\t\tFile ICB", &fid->icb); printf("\t\tLength of file identifier area %d\n", fid->l_fi); printf("\t\tOSTA UDF Unique ID %d\n", udf_rw32(fid->icb.impl.im_used.unique_id)); printf("\t\tOSTA UDF fileflags %d\n", udf_rw16(fid->icb.impl.im_used.flags)); printf("\t\tImplementation use length %d\n", udf_rw16(fid->l_iu)); if (udf_rw16(fid->l_iu)) { /* Ecma 1/7.4 demands a (padded if wanted) implementation identifier */ if (udf_rw16(fid->l_iu) >= sizeof(struct regid)) { udf_dump_regid("\t\t\tModified by", (struct regid *) &fid->data, UDF_REGID_IMPLEMENTATION); } else { printf("\t\t\tBROKEN fid, expected at least enough space for implementation regid\n"); } } pos = (char *) fid->data + udf_rw16(fid->l_iu); if (file_char & UDF_FILE_CHAR_PAR) { printf("\t\tParent directory ..\n"); } else { udf_dump_id("\t\tFilename", fid->l_fi, pos, chsp); } } void udf_dump_fileid(struct fileid_desc *fid) { struct charspec chsp; /* prolly OSTA compressed unicode anyway */ chsp.type = 0; strcpy((char *) chsp.inf, "OSTA Compressed Unicode"); udf_dump_fileid_in_charspec(fid, &chsp); } void udf_dump_icb_tag(struct icb_tag *icb_tag) { uint32_t flags, strat_param16; flags = udf_rw16(icb_tag->flags); strat_param16 = udf_rw16(icb_tag->strat_param16); printf("\t\tICB Prior direct entries recorded (excl.) %d\n", udf_rw32(icb_tag->prev_num_dirs)); printf("\t\tICB Strategy type %d\n", udf_rw16(icb_tag->strat_type)); printf("\t\tICB Strategy type flags %d %d\n", icb_tag->strat_param[0], icb_tag->strat_param[1]); printf("\t\tICB Maximum number of entries (non strat 4) %d\n", udf_rw16(icb_tag->max_num_entries)); printf("\t\tICB indirect entries/depth %d\n", strat_param16); printf("\t\tICB File type %d\n", icb_tag->file_type); printf("\t\tICB Parent ICB in logical block %d of mapped partition %d\n", udf_rw32(icb_tag->parent_icb.lb_num), udf_rw16(icb_tag->parent_icb.part_num)); printf("\t\tICB Flags %d\n", udf_rw16(icb_tag->flags)); printf("\t\t\tFile/directory information using : "); switch (flags & UDF_ICB_TAG_FLAGS_ALLOC_MASK) { case UDF_ICB_SHORT_ALLOC : printf("short allocation descriptor\n"); break; case UDF_ICB_LONG_ALLOC : printf("long allocation descriptor\n"); break; case UDF_ICB_EXT_ALLOC : printf("extended allocation descriptor (out of specs)\n"); break; case UDF_ICB_INTERN_ALLOC : printf("internal in the ICB\n"); break; } if (icb_tag->file_type == UDF_ICB_FILETYPE_DIRECTORY) if (flags & UDF_ICB_TAG_FLAGS_DIRORDERED) printf("\t\t\tOrdered directory\n"); if (flags & UDF_ICB_TAG_FLAGS_NONRELOC) printf("\t\t\tNot relocatable\n"); printf("\t\t\tFile flags :"); if (flags & UDF_ICB_TAG_FLAGS_SETUID) printf("setuid() "); if (flags & UDF_ICB_TAG_FLAGS_SETGID) printf("setgid() "); if (flags & UDF_ICB_TAG_FLAGS_STICKY) printf("sticky "); printf("\n"); if (flags & UDF_ICB_TAG_FLAGS_CONTIGUES) printf("\t\t\tFile is contigues i.e. in one piece effectively \n"); if (flags & UDF_ICB_TAG_FLAGS_MULTIPLEVERS) printf("\t\t\tExpect multiple versions of a file in this directory\n"); } void udf_dump_indirect_entry(struct indirect_entry *inde) { printf("\tIndirect (ICB) entry\n"); udf_dump_icb_tag(&inde->icbtag); udf_dump_long_ad("\t\tPointing at", &inde->indirect_icb); printf("\n"); } void udf_dump_allocation_entries(uint8_t addr_type, uint8_t *pos, uint32_t data_length) { union icb *icb; uint32_t size, piece_length, piece_flags; uint32_t entry; entry = 0; size = 0; while (data_length) { if (entry % 1 == 0) printf("\t\t\t"); printf(" [ "); printf("blob at "); /* what to do with strat type == 3 ? or is all set up ok then ? */ icb = (union icb *) pos; switch (addr_type) { case UDF_ICB_SHORT_ALLOC : piece_length = udf_rw32(icb->s_ad.len) & (((uint32_t) 1<<30)-1); piece_flags = udf_rw32(icb->s_ad.len) >> 30; /* XXX ecma167 48.14.1.1 XXX */ printf("sector %8u for %8d bytes", udf_rw32(icb->s_ad.lb_num), piece_length); if (piece_flags) printf(" flags %d", piece_flags); size = sizeof(struct short_ad); if (piece_length == 0) size = data_length; break; case UDF_ICB_LONG_ALLOC : piece_length = udf_rw32(icb->l_ad.len) & (((uint32_t) 1<<30)-1); piece_flags = udf_rw32(icb->l_ad.len) >> 30; /* XXX ecma167 48.14.1.1 XXX */ printf("sector %8d for %8d bytes in logical partion %d", udf_rw32(icb->l_ad.loc.lb_num), piece_length, udf_rw16(icb->l_ad.loc.part_num)); if (piece_flags) printf(" flags %d", piece_flags); size = sizeof(struct long_ad); if (piece_length == 0) size = data_length; break; case UDF_ICB_EXT_ALLOC : printf("extended alloc (help)"); size = sizeof(struct ext_ad); break; case UDF_ICB_INTERN_ALLOC : printf("internal blob here for %d bytes", data_length); size = data_length; break; } printf(" ] \n"); entry++; pos += size; data_length -=size; } printf("\n"); } /* TODO create a read-in/insert/cleanup etc. for extra attributes */ void udf_dump_extattrseq(uint8_t *start, uint32_t offset, uint32_t impl_offset, uint32_t appl_offset, uint32_t length) { struct impl_extattr_entry *impl_extattr; struct appl_extattr_entry *appl_extattr; struct filetimes_extattr_entry *filetimes_extattr; struct device_extattr_entry *device_extattr; struct vatlvext_extattr_entry *vatlvext_extattr; struct extattr_entry *extattr; struct timestamp *timestamp; struct charspec chsp; uint32_t extattr_len, au_l, iu_l, d_l; uint32_t type, subtype, chksum, attr_space, print_attr_space; uint32_t existence; uint8_t *pos; char *type_txt, what[256]; int is_free_ea_space, is_free_app_ea_space, is_vatlvext_space, bit; /* if used its OSTA compressed unicode anyway */ chsp.type = 0; strcpy((char *) chsp.inf, "OSTA Compressed Unicode"); /* if one of the offsets is `-1' (0xffffffff), it indicates that its not present; God i hate magic values */ if (impl_offset == UDF_IMPL_ATTR_LOC_NOT_PRESENT) printf("\t\tNOTE: indicated no implementation related attributes are recorded in this extent\n"); if (appl_offset == UDF_IMPL_ATTR_LOC_NOT_PRESENT) printf("\t\tNOTE: indicated no application related attributes are recorded in this extent\n"); pos = start; attr_space = UDF_REGID_UDF; /* really? */ while (length > 0) { extattr = (struct extattr_entry *) pos; extattr_len = udf_rw32(extattr->a_l); type = udf_rw32(extattr->type); subtype = extattr->subtype; if (pos == start) printf("\t\tStart of extended file related attributes area\n"); if (offset == impl_offset) printf("\t\tStart of implementation related attributes area\n"); if (offset == appl_offset) printf("\t\tStart of application related attributes area\n"); if (pos == start) attr_space = UDF_REGID_UDF; if (offset == impl_offset) attr_space = UDF_REGID_IMPLEMENTATION; if (offset == appl_offset) attr_space = UDF_REGID_APPLICATION; if (subtype != 1) printf("\t\t\tWARNING: unknown subtype %d\n", subtype); print_attr_space = attr_space; switch (type) { case 65536 : /* [4/48.10.8] application use extended attributes */ appl_extattr = (struct appl_extattr_entry *) pos; au_l = udf_rw32(appl_extattr->au_l); printf("\t\t\tApplication use extended attribute\n"); if (attr_space != UDF_REGID_APPLICATION) printf("\t\t\t\t*** application use extended attribute found in non application use area ***\n"); printf("\t\t\t\tLength of application use space %d\n", au_l); udf_dump_regid("\t\t\t\tApplication use Id", &appl_extattr->appl_id, attr_space); break; case 2048 : /* [4/48.10.9] implementation use extended attributes */ impl_extattr = (struct impl_extattr_entry *) pos; iu_l = udf_rw32(impl_extattr->iu_l); chksum = udf_rw16(impl_extattr->data16); printf("\t\t\tImplementation use extended attribute\n"); if (chksum != udf_ea_cksum(pos)) printf("\t\t\t\t*** header checksum failed (%d should be %d) ***\n", chksum, udf_ea_cksum(pos)); if (attr_space != UDF_REGID_IMPLEMENTATION) printf("\t\t\t\t*** implementation use extended attribute found in non implementation use area ***\n"); if (strncmp((char *) impl_extattr->imp_id.id, "*UDF", 4) == 0) print_attr_space = UDF_REGID_UDF; printf("\t\t\t\tLength of implementation use space %d\n", iu_l); udf_dump_regid("\t\t\t\tImplementation use Id", &impl_extattr->imp_id, print_attr_space); is_free_ea_space = (strcmp((char *) impl_extattr->imp_id.id, "*UDF FreeEASpace") == 0); is_free_app_ea_space = (strcmp((char *) impl_extattr->imp_id.id, "*UDF FreeAppEASpace") == 0); is_vatlvext_space = (strcmp((char *) impl_extattr->imp_id.id, "*UDF VAT LVExtension") == 0); if (is_free_ea_space || is_free_app_ea_space) { printf("\t\t\t\tFree space for new extended attributes (%d bytes total)\n", extattr_len); } else if (is_vatlvext_space) { vatlvext_extattr = (struct vatlvext_extattr_entry *) (impl_extattr->data + iu_l); printf("\t\t\t\t\tUniqueID check %"PRIu64"\n", udf_rw64(vatlvext_extattr->unique_id_chk)); printf("\t\t\t\t\tNumber of files %d\n", udf_rw32(vatlvext_extattr->num_files)); printf("\t\t\t\t\tNumber of directories %d\n", udf_rw32(vatlvext_extattr->num_directories)); udf_dump_id("\t\t\t\t\tLogical volume id ", 128, vatlvext_extattr->logvol_id, &chsp); } else { printf("\t\t\t\t\n", iu_l); } break; case 1 : /* [4/48.10.3] : Character set information; UDF does allow/disallow explicitly */ printf("\t\t\tCharacter set information attribute\n"); printf("\t\t\t\t\n", extattr_len); break; case 3 : /* [4/48.10.4] : Alternate permissions; UDF 3.3.4.2: not to be recorded */ printf("\t\t\tAlternate permission attribute\n"); printf("\t\t\t\t\n", extattr_len); break; case 5 : /* [4/48.10.5] : File Times Extended Attribute */ case 6 : /* [4/48.10.6] : Information Times Extended Attribute; recorded in UDF ? */ /* ASSUMPTION : bit fields are not exlusive */ filetimes_extattr = (struct filetimes_extattr_entry *) pos; d_l = udf_rw32(filetimes_extattr->d_l); existence = udf_rw32(filetimes_extattr->existence); type_txt = "File"; if (type == 6) type_txt = "File information"; printf("\t\t\t%s times extended attribute\n", type_txt); timestamp = &filetimes_extattr->times[0]; for (bit = 0; bit < 32; bit++) { if (d_l == 0) break; if ((existence & (1 << bit)) == 0) continue; switch (bit) { case 0 : /* File Creation Date and Time: the date and time of the day at which the file was created. */ sprintf(what, "\t\t\t\t%s created at ", type_txt); break; case 1 : /* Information Last Modification Date and Time: the date and time of the day at which the information in the file was last modified. */ sprintf(what, "\t\t\t\t%s last modified at ", type_txt); break; case 2 : /* File Deletion Date and Time: the date and time of the day after which the file may be deleted. */ sprintf(what, "\t\t\t\t%s may be deleted after ", type_txt); break; case 3 : /* File Effective Date and Time: the date and time of the day after which the file may be used. */ sprintf(what, "\t\t\t\t%s may only be used after ", type_txt); break; case 5 : /* File Last Backup Date and Time: the date and time of the day at which the file was last backed up. */ sprintf(what, "\t\t\t\t%s last backuped at ", type_txt); break; default : /* unspec */ sprintf(what, "\t\t\t\tUndefined meaning for %s time stamp ", type_txt); break; } udf_dump_timestamp(what, timestamp); d_l -= sizeof(struct timestamp); timestamp++; /* advance */ } break; case 12 : /* [4/48.10.7] : Device Specification Extended Attribute */ device_extattr = (struct device_extattr_entry *) pos; iu_l = udf_rw32(device_extattr->iu_l); printf("\t\t\tDevice node extended attribute\n"); printf("\t\t\t\tMajor %d\n", udf_rw32(device_extattr->major)); printf("\t\t\t\tMinor %d\n", udf_rw32(device_extattr->minor)); if (iu_l >= sizeof(struct regid)) { udf_dump_regid("\t\t\t\tImplementator", (struct regid *) (device_extattr->data), UDF_REGID_IMPLEMENTATION); } break; default : printf("\t\t\tUndumped extended attribute type %d\n", type); printf("\t\t\t\tSubtype %d\n", subtype); printf("\t\t\t\tLength %d\n", extattr_len); break; } if (extattr_len == 0) { printf("\t\t\tABORTing dump\n"); break; } pos += extattr_len; offset += extattr_len; length -= extattr_len; } printf("\n"); } void udf_dump_extattr_hdr(struct extattrhdr_desc *eahd, uint32_t length) { uint32_t hdr_len, impl_attr_loc, appl_attr_loc; uint8_t *pos; hdr_len = (uint32_t) sizeof(struct extattrhdr_desc); impl_attr_loc = udf_rw32(eahd->impl_attr_loc); appl_attr_loc = udf_rw32(eahd->appl_attr_loc); printf("\t\tExtended attributes header:\n"); printf("\t\t\tLength %d bytes\n", length); printf("\t\t\tImplementation attributes at offset %d\n", impl_attr_loc); printf("\t\t\tApplication attributes at offset %d\n", appl_attr_loc); printf("\t\t\tBytes remaining after header %d\n", length - hdr_len); /* determine length of file related attributes space */ pos = (uint8_t *) eahd; pos += hdr_len; length -= hdr_len; udf_dump_extattrseq(pos, hdr_len, impl_attr_loc, appl_attr_loc, length); } void udf_dump_file_entry(struct file_entry *fe) { uint8_t *pos; uint32_t length; uint8_t addr_type; /* direct_entries = udf_rw32(fe->icbtag.prev_num_dirs); */ //strat_param16 = udf_rw16(* (uint16_t *) (fe->icbtag.strat_param)); //entries = udf_rw16(fe->icbtag.max_num_entries); //strategy = udf_rw16(fe->icbtag.strat_type); addr_type = udf_rw16(fe->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; printf("\tFile entry\n"); udf_dump_icb_tag(&fe->icbtag); printf("\t\tUid %d\n", udf_rw32(fe->uid)); printf("\t\tGid %d\n", udf_rw32(fe->gid)); printf("\t\tPermissions %x\n", udf_rw32(fe->perm)); printf("\t\tLink count %d\n", udf_rw16(fe->link_cnt)); printf("\t\tRecord format %d\n", fe->rec_format); printf("\t\tRecord display attributes %d\n", fe->rec_disp_attr); printf("\t\tRecord length %d\n", fe->rec_len); printf("\t\tInformation length %"PRIu64"\n", (uint64_t) udf_rw64(fe->inf_len)); printf("\t\tLogical blocks recorded %"PRIu64"\n", (uint64_t) udf_rw64(fe->logblks_rec)); udf_dump_timestamp("\t\tAccess time ", &fe->atime); udf_dump_timestamp("\t\tModification time ", &fe->mtime); udf_dump_timestamp("\t\tAttribute time ", &fe->attrtime); printf("\t\tCheckpoint %d\n", udf_rw32(fe->ckpoint)); udf_dump_long_ad("\t\tExtended attributes ICB at", &fe->ex_attr_icb); udf_dump_regid("\t\tImplementation", &fe->imp_id, UDF_REGID_IMPLEMENTATION); printf("\t\tUniqueID %d\n", (uint32_t) udf_rw64(fe->unique_id)); printf("\t\tLength of extended attribute area %d\n", udf_rw32(fe->l_ea)); printf("\t\tLength of allocation descriptors %d\n", udf_rw32(fe->l_ad)); if (udf_rw32(fe->l_ea)) { udf_dump_extattr_hdr((struct extattrhdr_desc *) &fe->data[0], udf_rw32(fe->l_ea)); } if (udf_rw32(fe->ex_attr_icb.len)) { printf("\t\tex_attr_icb.len)); } printf("\t\tAllocation descriptors : \n"); pos = &fe->data[0] + udf_rw32(fe->l_ea); length = udf_rw32(fe->l_ad); udf_dump_allocation_entries(addr_type, pos, length); } void udf_dump_extfile_entry(struct extfile_entry *efe) { uint8_t *pos; uint32_t length; uint8_t addr_type; /* direct_entries = udf_rw32(efe->icbtag.prev_num_dirs); */ //strat_param16 = udf_rw16(* (uint16_t *) (efe->icbtag.strat_param)); //entries = udf_rw16(efe->icbtag.max_num_entries); //strategy = udf_rw16(efe->icbtag.strat_type); addr_type = udf_rw16(efe->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; printf("\tExtended file entry\n"); udf_dump_icb_tag(&efe->icbtag); printf("\t\tUid %d\n", udf_rw32(efe->uid)); printf("\t\tGid %d\n", udf_rw32(efe->gid)); printf("\t\tPermissions %x\n", udf_rw32(efe->perm)); printf("\t\tLink count %d\n", udf_rw16(efe->link_cnt)); printf("\t\tRecord format %d\n", efe->rec_format); printf("\t\tRecord display attributes %d\n", efe->rec_disp_attr); printf("\t\tRecord length %d\n", efe->rec_len); printf("\t\tInformation length %"PRIu64"\n", (uint64_t) udf_rw64(efe->inf_len)); printf("\t\tObject size %"PRIu64"\n", (uint64_t) udf_rw64(efe->obj_size)); printf("\t\tLogical blocks recorded %"PRIu64"\n", (uint64_t) udf_rw64(efe->logblks_rec)); udf_dump_timestamp("\t\tAccess time ", &efe->atime); udf_dump_timestamp("\t\tModification time ", &efe->mtime); udf_dump_timestamp("\t\tCreation time ", &efe->ctime); udf_dump_timestamp("\t\tAttribute time ", &efe->attrtime); printf("\t\tCheckpoint %d\n", udf_rw32(efe->ckpoint)); udf_dump_long_ad("\t\tExtended attributes ICB at", &efe->ex_attr_icb); udf_dump_long_ad("\t\tStreamdir ICB at", &efe->streamdir_icb); udf_dump_regid("\t\tImplementation", &efe->imp_id, UDF_REGID_IMPLEMENTATION); printf("\t\tUniqueID %d\n", (uint32_t) udf_rw64(efe->unique_id)); printf("\t\tLength of extended attribute area %d\n", udf_rw32(efe->l_ea)); printf("\t\tLength of allocation descriptors %d\n", udf_rw32(efe->l_ad)); if (udf_rw32(efe->l_ea)) { udf_dump_extattr_hdr((struct extattrhdr_desc *) &efe->data[0], udf_rw32(efe->l_ea)); } if (udf_rw32(efe->ex_attr_icb.len)) { printf("\t\tex_attr_icb.len)); } printf("\t\tAllocation descriptors : \n"); pos = &efe->data[0] + udf_rw32(efe->l_ea); length = udf_rw32(efe->l_ad); udf_dump_allocation_entries(addr_type, pos, length); } /* dump allocation extention descriptor */ void udf_dump_alloc_extent(struct alloc_ext_entry *ext, int addr_type) { uint8_t *pos; uint32_t length; int isshort, islong; /* note we DONT know if its filled with short_ad's or long_ad's! */ printf("\tAllocation Extent descriptor\n"); printf("\t\tPrevious entry %d\n", udf_rw32(ext->prev_entry)); printf("\t\tLength of allocation descriptors %d\n", udf_rw32(ext->l_ad)); pos = &ext->data[0]; length = udf_rw32(ext->l_ad); if (addr_type < 0) { isshort = ((length % sizeof(struct short_ad)) == 0); islong = ((length % sizeof(struct long_ad)) == 0); if (isshort) addr_type = UDF_ICB_SHORT_ALLOC; if (islong) addr_type = UDF_ICB_LONG_ALLOC; if (!(isshort ^ islong)) { printf("\t\tCan't determine if its filled with long_ad's or short_ad's !\n"); return; } } udf_dump_allocation_entries(addr_type, pos, length); } /* dump a space table(entry) descriptor */ void udf_dump_space_entry(struct space_entry_desc *sed) { union icb *icb; uint32_t addr_type, size, bytes; uint32_t piece_sector, piece_length, piece_part; uint8_t *pos; printf("\tSpace entry table\n"); udf_dump_icb_tag(&sed->icbtag); printf("\t\tSize in bytes %d\n", udf_rw32(sed->l_ad)); pos = &sed->entry[0]; bytes = udf_rw32(sed->l_ad); addr_type = udf_rw16(sed->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; while (bytes) { size = piece_length = piece_sector = piece_part = 0; icb = (union icb *) pos; switch (addr_type) { case UDF_ICB_SHORT_ALLOC : piece_length = udf_rw32(icb->s_ad.len) & (((uint32_t) 1<<31)-1); piece_sector = udf_rw32(icb->s_ad.lb_num); printf("[at sec %u for %d bytes] ", piece_sector, piece_length); size = sizeof(struct short_ad); break; case UDF_ICB_LONG_ALLOC : piece_length = udf_rw32(icb->l_ad.len) & (((uint32_t) 1<<31)-1); piece_sector = udf_rw32(icb->l_ad.loc.lb_num); piece_part = udf_rw16(icb->l_ad.loc.part_num); size = sizeof(struct long_ad); printf("[at sec %u for %d bytes at partition %d] ", piece_sector, piece_length, piece_part); break; case UDF_ICB_EXT_ALLOC : case UDF_ICB_INTERN_ALLOC : printf("\t\t\tWARNING : an internal alloc in a space entry?\n"); return; } bytes -= size; } } /* dump a space bitmap descriptor */ void udf_dump_space_bitmap(struct space_bitmap_desc *sbd) { uint32_t bits, from, now, cnt; uint8_t byte, bit, bitpos, state, *pos; printf("\t\tSpace bitmap\n"); printf("\t\t\tNumber of bits %u\n", udf_rw32(sbd->num_bits)); printf("\t\t\tNumber of bytes %u\n", udf_rw32(sbd->num_bytes)); printf("\t\t\tMarked parts at :\n"); pos = sbd->data; bits = udf_rw32(sbd->num_bits); /* shield */ /* if (bits > 2000*8) bits = 2000*8; */ printf("\t\t\t\t"); cnt = 0; from = 0; now = 0; bitpos = 0; byte = *pos; state = byte & 1; while (now < bits) { if (bitpos == 0) { byte = *pos++; } bit = byte & 1; if (bit != state) { if (state) { printf("[%08u - %08u]", from, now-1); if (cnt % 4 == 3) printf("\n\t\t\t\t"); else printf(" "); cnt++; } from = now; state = bit; } byte >>= 1; bitpos = (bitpos+1) & 7; now++; } if (state) printf("[%08u - %08u]", from, now); if (bits < udf_rw32(sbd->num_bits)) printf(" .... \n"); } /* main descriptor `dump' function */ void udf_dump_descriptor(union dscrptr *dscrpt) { struct desc_tag *tag = &dscrpt->tag; int error; if (!dscrpt) return; /* check if its a valid descritor */ if (udf_rw16(tag->id == 0) && udf_rw16(tag->descriptor_ver) == 0) return; udf_dump_desc(tag); error = udf_check_tag(dscrpt); if (error) { printf("\tBAD TAG\n"); return; } switch (udf_rw16(tag->id)) { case TAGID_SPARING_TABLE : udf_dump_sparing_table(&dscrpt->spt); break; case TAGID_PRI_VOL : udf_dump_pri_vol(&dscrpt->pvd); break; case TAGID_ANCHOR : udf_dump_anchor(&dscrpt->avdp); break; case TAGID_VOL : udf_dump_unimpl(dscrpt, "volume descriptor"); break; case TAGID_IMP_VOL : udf_dump_implementation_volume(&dscrpt->ivd); break; case TAGID_PARTITION : udf_dump_part(&dscrpt->pd); break; case TAGID_LOGVOL : udf_dump_log_vol(&dscrpt->lvd); break; case TAGID_UNALLOC_SPACE : udf_dump_unalloc_space(&dscrpt->usd); break; case TAGID_TERM : udf_dump_terminating_desc(dscrpt); break; case TAGID_LOGVOL_INTEGRITY : udf_dump_logvol_integrity(&dscrpt->lvid); break; case TAGID_FSD : udf_dump_fileset_desc(&dscrpt->fsd); break; case TAGID_FID : udf_dump_fileid(&dscrpt->fid); break; case TAGID_ALLOCEXTENT : udf_dump_alloc_extent(&dscrpt->aee, -1); break; case TAGID_INDIRECTENTRY : udf_dump_indirect_entry(&dscrpt->inde); break; case TAGID_FENTRY : udf_dump_file_entry(&dscrpt->fe); break; case TAGID_EXTATTR_HDR : udf_dump_extattr_hdr(&dscrpt->eahd, sizeof(struct extattrhdr_desc)); break; case TAGID_UNALL_SP_ENTRY : udf_dump_space_entry(&dscrpt->sed); break; case TAGID_SPACE_BITMAP : udf_dump_space_bitmap(&dscrpt->sbd); break; case TAGID_PART_INTEGRITY : udf_dump_unimpl(dscrpt, "partition integrity"); break; case TAGID_EXTFENTRY : udf_dump_extfile_entry(&dscrpt->efe); break; default : break; } printf("\n"); } /* this one is special since the VAT table has no tag but is a file */ void udf_dump_vat_table(struct udf_part_mapping *udf_part_mapping) { struct charspec chsp; struct udf_vat *vat; uint32_t previous_vat, entry, vat_entries, *vat_pos, version; /* prolly OSTA compressed unicode anyway */ chsp.type = 0; strcpy((char *) chsp.inf, "OSTA Compressed Unicode"); vat = udf_part_mapping->vat; printf("\tVAT table: "); printf("%s UDF 2.00 format\n", vat?"post":"pre"); vat_entries = udf_part_mapping->vat_entries; vat_pos = (uint32_t *) udf_part_mapping->vat_translation; if (vat) { printf("\t\tHeader length %d\n", udf_rw16(vat->header_len)); printf("\t\tImplementation use length %d\n", udf_rw16(vat->impl_use_len)); udf_dump_id("\t\tLogical volume id ", 128, vat->logvol_id, &chsp); printf("\t\tNumber of files %d\n", udf_rw32(vat->num_files)); printf("\t\tNumber of directories %d\n", udf_rw32(vat->num_directories)); version = udf_rw16(vat->min_udf_readver); printf("\t\tMinimum readversion UDFv %x\n", version); version = udf_rw16(vat->min_udf_writever); printf("\t\tMinimum writeversion UDFv %x\n", version); version = udf_rw16(vat->max_udf_writever); printf("\t\tMaximum writeversion UDFv %x\n", version); if (udf_rw16(vat->impl_use_len)) printf("\t\t"); previous_vat = udf_rw32(vat->prev_vat); } else { udf_dump_regid("\t\tIdentifier id (can be wrong) ", (struct regid *) (vat_pos+vat_entries), UDF_REGID_NAME); previous_vat = udf_rw32(*(vat_pos + vat_entries + 32/4)); /* definition */ } if (previous_vat == 0xffffffff) { printf("\t\tNo previous VAT recorded\n"); } else { printf("\t\tPrevious VAT recorded at offset %d\n", previous_vat); } printf("\t\tNumber of VAT entries %d\n", vat_entries); printf("\t\tVAT dump :"); for (entry=0; entry < vat_entries; entry++) { if ((entry % 4) == 0) printf("\n\t"); printf("[0x%08x -> 0x%08x] ", entry, *vat_pos++); } printf("\n"); } void udf_dump_volumeset_info(struct udf_volumeset *udf_volumeset) { struct udf_pri_vol *primary; struct udf_log_vol *logical; struct udf_partition *udf_partition; struct udf_part_mapping *udf_part_mapping; struct udf_discinfo *disc; char *name; int num_volumes, num_partitions; int subvolume, part_num, track_num; num_volumes = 0; /* shut up gcc */ if (udf_volumeset->obsolete) return; primary = STAILQ_FIRST(&udf_volumeset->primaries); if (primary) { num_volumes = udf_rw16(primary->pri_vol->max_vol_seq); if (udf_volumeset->obsolete) printf("OBSOLETE\n"); /* XXX */ printf("Volume set "); udf_dump_id(NULL, 32, primary->pri_vol->volset_id, &primary->pri_vol->desc_charset); printf(" (%d volume%s) ", num_volumes, num_volumes>1?"s":""); num_partitions = udf_volumeset->max_partnum; printf("with %d partition%s\n", num_partitions, (num_partitions!=1)?"s":""); /* better loop trough the partition numbers to display them in a defined order */ SLIST_FOREACH(udf_partition, &udf_volumeset->parts, next_partition) { part_num = udf_rw16(udf_partition->partition->part_num); if (udf_partition) { /* there is information */ assert(udf_partition->udf_session); assert(udf_partition->udf_session->disc); assert(udf_partition->partition); assert(part_num == udf_rw16(udf_partition->partition->part_num)); track_num = udf_partition->udf_session->session_num; disc = udf_partition->udf_session->disc; printf("\tPartition number %d at device `%s' session %d from sector %d(+%d) for %u sectors\n", part_num, disc->dev->dev_name, track_num, udf_rw32(udf_partition->partition->start_loc), udf_partition->udf_session->session_offset, udf_rw32(udf_partition->partition->part_len) ); } else { printf("\tUnknown partition %d [unknown]\n", part_num); } } } STAILQ_FOREACH(primary, &udf_volumeset->primaries, next_primary) { subvolume = udf_rw16(primary->pri_vol->vds_num); printf("\tPrimary volume "); udf_dump_id(NULL, 32, primary->pri_vol->vol_id, &primary->pri_vol->desc_charset); printf(" (part %d/%d) ", subvolume, num_volumes); printf("created by implementator `%s' ", primary->pri_vol->imp_id.id); if (*primary->pri_vol->app_id.id) printf("by/for application `%s' ",primary->pri_vol->app_id.id); printf("\n"); SLIST_FOREACH(logical, &primary->log_vols, next_logvol) { name = logical->log_vol->logvol_id; udf_dump_id("\t\tcontains logical volume ", 128, name, &logical->log_vol->desc_charset); if (logical->broken) { printf("\t\t\tBROKEN\n"); continue; } SLIST_FOREACH(udf_part_mapping, &logical->part_mappings, next_mapping) { printf("\t\t\tmapping %d on %d as ", udf_part_mapping->udf_virt_part_num, udf_part_mapping->udf_phys_part_num); switch (udf_part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_ERROR : printf("bad partition"); break; case UDF_PART_MAPPING_PHYSICAL : printf("direct"); break; case UDF_PART_MAPPING_VIRTUAL : printf("virtual partition"); break; case UDF_PART_MAPPING_SPARABLE : printf("sparable"); break; case UDF_PART_MAPPING_META : printf("metadata only"); } printf(" recording"); if (udf_part_mapping->data_writable) printf(" data"); if (udf_part_mapping->metadata_writable) printf(" metadata"); if (!udf_part_mapping->data_writable && !udf_part_mapping->metadata_writable) printf(" nothing"); printf("\n"); } } printf("\n"); } } void udf_dump_alive_sets(void) { struct udf_volumeset *udf_volumeset; printf("UDF volume sets marked alive :\n"); SLIST_FOREACH(udf_volumeset, &udf_volumeset_list, next_volumeset) { udf_dump_volumeset_info(udf_volumeset); } printf("\n"); } /* * extern defined read_logvol_descriptor breaks splitting rules but how * otherwise to provide a detailed description of the file entry udf_node */ #define DUMP_DIRBUFFER_SIZE (16*1024) void udf_dump_file_entry_node(struct udf_node *udf_node, char *prefix) { struct long_ad udf_icbptr; struct uio dir_uio; struct iovec dir_iovec; struct dirent *dirent; struct fileid_desc *fid; struct udf_node *entry_node; uint32_t pos, lb_size; uint8_t *buffer; char fullpath[1024]; /* XXX arbitrary length XXX */ int isdot, isdotdot, isdir, found, eof; int error; if (!udf_node) return; /* XXX could pass on dirent XXX */ isdir = (udf_node->udf_filetype == UDF_ICB_FILETYPE_DIRECTORY); isdir |= (udf_node->udf_filetype == UDF_ICB_FILETYPE_STREAMDIR); if (isdir) { buffer = malloc(DUMP_DIRBUFFER_SIZE); if (!buffer) return; lb_size = udf_node->udf_log_vol->lb_size; fid = malloc(lb_size); assert(fid); /* or just return? */ /* recurse into this directory */ dir_uio.uio_offset = 0; /* begin at start */ do { dir_iovec.iov_base = buffer; dir_iovec.iov_len = DUMP_DIRBUFFER_SIZE; dir_uio.uio_resid = DUMP_DIRBUFFER_SIZE; dir_uio.uio_iovcnt = 1; dir_uio.uio_iov = &dir_iovec; dir_uio.uio_rw = UIO_WRITE; error = udf_readdir(udf_node, &dir_uio, &eof); if (error) { printf("While reading in dirbuffer for dumping file entry udf_node : %s\n", strerror(error)); break; } pos = 0; while (pos < DUMP_DIRBUFFER_SIZE - dir_uio.uio_resid) { dirent = (struct dirent *) (buffer + pos); sprintf(fullpath, "%s/%s", prefix, dirent->d_name); /* looking for '.' or '..' ? */ isdot = (strncmp(dirent->d_name, ".", DIRENT_NAMLEN(dirent)) == 0); isdotdot = (strncmp(dirent->d_name, "..", DIRENT_NAMLEN(dirent)) == 0); pos += sizeof(struct dirent); /* XXX variable size dirents possible XXX */ if (isdotdot) continue; if (isdot) continue; error = udf_lookup_name_in_dir(udf_node, dirent->d_name, DIRENT_NAMLEN(dirent), &udf_icbptr, fid, &found); if (!error) { error = ENOENT; if (found) error = udf_readin_udf_node(udf_node, &udf_icbptr, fid, &entry_node); } if (!error) udf_dump_file_entry_node(entry_node, fullpath); } } while (!eof); free(fid); free(buffer); return; } /* leaf udf_node */ printf("%s\n", prefix); } #undef DUMP_DIRBUFFER_SIZE void udf_dump_root_dir(struct udf_mountpoint *mountpoint) { printf("\n\nRoot dir dump\n"); if (mountpoint->rootdir_node) udf_dump_file_entry_node(mountpoint->rootdir_node, ":Rootdir"); printf("\n\nStreamdir dump\n"); if (mountpoint->streamdir_node) udf_dump_file_entry_node(mountpoint->streamdir_node, ":Streamdir"); } /* XXX These should move to form one cd verbose file with cd_discect XXX */ static char *print_disc_state(int state) { switch (state) { case 0: return "empty disc"; case 1: return "incomplete (appendable)"; case 2: return "full (not appendable)"; case 3: return "random writable"; } return "unknown disc state"; } static char *print_session_state(int state) { switch (state) { case 0 : return "empty"; case 1 : return "incomplete"; case 2 : return "reserved/damaged"; case 3 : return "complete/closed disc"; } return "unknown session_state"; } static char *print_mmc_profile(int profile) { static char scrap[100]; switch (profile) { case 0x00 : return "Unknown[0] profile"; case 0x01 : return "Non removable disc"; case 0x02 : return "Removable disc"; case 0x03 : return "Magneto Optical with sector erase"; case 0x04 : return "Magneto Optical write once"; case 0x05 : return "Advance Storage Magneto Optical"; case 0x08 : return "CD-ROM"; case 0x09 : return "CD-R recordable"; case 0x0a : return "CD-RW rewritable"; case 0x10 : return "DVD-ROM"; case 0x11 : return "DVD-R sequential"; case 0x12 : return "DVD-RAM rewritable"; case 0x13 : return "DVD-RW restricted overwrite"; case 0x14 : return "DVD-RW sequential"; case 0x1a : return "DVD+RW rewritable"; case 0x1b : return "DVD+R recordable"; case 0x20 : return "DDCD readonly"; case 0x21 : return "DDCD-R recodable"; case 0x22 : return "DDCD-RW rewritable"; case 0x2b : return "DVD+R double layer"; case 0x40 : return "BD-ROM"; case 0x41 : return "BD-R Sequential Recording (SRM)"; case 0x42 : return "BD-R Random Recording (RRM)"; case 0x43 : return "BD-RE rewritable"; } sprintf(scrap, "Reserved profile 0x%02x", profile); return scrap; } void udf_dump_discinfo(struct udf_discinfo *disc) { int session; printf("Disc info for disc in device %s\n", disc->dev->dev_name); printf("\tMMC profile : %s\n", print_mmc_profile(disc->mmc_profile)); printf("\tsequential : %s\n", disc->sequential ?"yes":" no"); printf("\trecordable : %s\n", disc->recordable ?"yes":" no"); printf("\terasable : %s\n", disc->erasable ?"yes":" no"); printf("\tblankable : %s\n", disc->blankable ?"yes":" no"); printf("\tformattable : %s\n", disc->formattable ?"yes":" no"); printf("\trewritable : %s\n", disc->rewritable ?"yes":" no"); printf("\tmount raineer : %s\n", disc->mrw ?"yes":" no"); printf("\tpacket writing : %s\n", disc->packet ?"yes":" no"); printf("\tstrict overwrite : %s\n", disc->strict_overwrite ?"yes":" no"); printf("\tblocking number : %d\n", disc->blockingnr); printf("\tdisc state : %s\n", print_disc_state(disc->disc_state)); printf("\tlast session state : %s\n", print_session_state(disc->last_session_state)); printf("\tsectorsize : %d\n", disc->sector_size); printf("\tNumber of sessions %d\n", disc->num_sessions); for (session = 0; session < disc->num_sessions; session++) { printf("\tSession %d\n", session); printf("\t\tstart at %u\n", (uint32_t) disc->session_start[session]); printf("\t\tends at %u\n", (uint32_t) disc->session_end[session]); printf("\t\tlength for %u\n", (uint32_t) (disc->session_end[session] - disc->session_start[session])); printf("\t\tnext writable at %u\n", disc->next_writable[session]); printf("\t\tfree blocks %u\n", disc->free_blocks[session]); printf("\t\tpacket size %u\n", disc->packet_size[session]); printf("\n"); } } /* end of udf_verbose.c */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/udfclient.c000066400000000000000000001402141523477242100262640ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "udfclient.c" is part of the UDFclient toolkit. * File $Id: udfclient.c,v 1.104 2016/04/25 21:28:00 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * ChangeLog * - hidden `main` and `usage` * */ #include #include #include #include #include #include #include #include #include #include #include #include "udf.h" #include "udf_bswap.h" /* switches */ /* #define DEBUG(a) (a) */ #define DEBUG(a) if (0) { a; } #ifndef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b)) #endif /* include timeval to timespec conversion macro's for systems that don't provide them */ #ifndef TIMEVAL_TO_TIMESPEC # define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ (ts)->tv_sec = (tv)->tv_sec; \ (ts)->tv_nsec = (tv)->tv_usec * 1000; \ } while (/*CONSTCOND*/0) #endif #ifndef TIMESPEC_TO_TIMEVAL # define TIMESPEC_TO_TIMEVAL(tv, ts) do { \ (tv)->tv_sec = (ts)->tv_sec; \ (tv)->tv_usec = (ts)->tv_nsec / 1000; \ } while (/*CONSTCOND*/0) #endif /* include the dump parts ... in order to get a more sane splitting */ extern void udf_dump_alive_sets(void); /* globals */ extern int udf_verbose; extern int uscsilib_verbose; int read_only; #define MAX_ARGS 100 struct curdir { char *name; struct udf_mountpoint *mountpoint; /* foreign */ struct udf_node *udf_node; /* foreign */ struct hash_entry *udf_mountpoint_entry; /* `current' mountpoint entry */ } curdir; /* * XXX * FTP client; de volumes vooraan zetten in de VFS ; evt. in meerdere subdirs. * general file name format * * /volset:pri:log/udfpath * of * /volset/pri/log/udfpath/ * * XXX */ int udfclient_readdir(struct udf_node *udf_node, struct uio *result_uio, int *eof_res) { struct dirent entry; struct udf_mountpoint *mountable; assert(result_uio); if (!udf_node) { /* mountables */ /* XXX result_uio needs to be long enough to hold all mountables!!!! XXX */ SLIST_FOREACH(mountable, &udf_mountables, all_next) { strcpy(entry.d_name, mountable->mount_name); entry.d_type = DT_DIR; uiomove(&entry, sizeof(struct dirent), result_uio); } if (eof_res) *eof_res = 1; return 0; } /* intree nodes : pass on to udf_readdir */ return udf_readdir(udf_node, result_uio, eof_res); } /* VOP_LOOKUP a-like */ int udfclient_lookup(struct udf_node *dir_node, struct udf_node **resnode, char *name) { struct udf_mountpoint *mountable; struct fileid_desc *fid; struct long_ad udf_icbptr; int lb_size, found, error; assert(resnode); assert(name); *resnode = NULL; if (!dir_node) { /* mountables */ SLIST_FOREACH(mountable, &udf_mountables, all_next) { if (strcmp(mountable->mount_name, name) == 0) { /* found `root' of a mountable */ *resnode = mountable->rootdir_node; return 0; } } return ENOENT; } /* intree nodes : pass on to udf_lookup_name_in_dir */ lb_size = dir_node->udf_log_vol->lb_size; fid = malloc(lb_size); assert(fid); error = udf_lookup_name_in_dir(dir_node, name, strlen(name), &udf_icbptr, fid, &found); if (!error) { error = ENOENT; if (found) error = udf_readin_udf_node(dir_node, &udf_icbptr, fid, resnode); } free(fid); return error; } int udfclient_getattr(struct udf_node *udf_node, struct stat *stat) { int error; error = 0; if (udf_node) { error = udf_getattr(udf_node, stat); /* print? */ if (error) fprintf(stderr, "Can't stat file\n"); } else { /* dummy entry for `root' in VFS */ stat->st_mode = 0744 | S_IFDIR; stat->st_size = 0; stat->st_uid = 0; stat->st_gid = 0; } return error; } /* higher level of lookup; walk tree */ /* results in a `held'/locked node upto `root' */ int udfclient_lookup_pathname(struct udf_node *cur_node, struct udf_node **res_node, char *restpath_given) { struct udf_node *sub_node; char *restpath, *next_element, *slashpos, *pathpos; int error; assert(restpath_given); restpath = strdup(restpath_given); /* start at root */ *res_node = NULL; pathpos = restpath; assert(*pathpos == '/'); pathpos++; /* strip leading '/' */ next_element = pathpos; while (next_element && (strlen(next_element) > 0)) { /* determine next slash position */ slashpos = strchr(next_element, '/'); if (slashpos) *slashpos++ = 0; error = udfclient_lookup(cur_node, &sub_node, next_element); if (error) { free(restpath); return error; } /* advance */ cur_node = sub_node; next_element = slashpos; } /* we are at the end; return result */ *res_node = cur_node; free(restpath); return 0; } char *udfclient_realpath(char *cur_path, char *relpath, char **leaf) { char *resultpath, *here, *pos; resultpath = calloc(1, sizeof(cur_path) + sizeof(relpath) +1024); assert(resultpath); strcpy(resultpath, "/"); strcat(resultpath, cur_path); strcat(resultpath, "/"); /* check if we are going back to `root' */ if (relpath && *relpath == '/') { strcpy(resultpath, ""); } strcat(resultpath, relpath); /* now clean up the resulting string by removing double slashes */ here = resultpath; while (*here) { pos = here; while (strncmp(pos, "//", 2) == 0) pos++; if (pos != here) strcpy(here, pos); here++; } /* remove '.' and '..' sequences */ here = resultpath; while (*here) { /* printf("transformed to %s\n", resultpath); */ /* check for internal /./ and trailing /. */ if (strncmp(here, "/./", 3) == 0) { strcpy(here+1, here + 3); continue; } if (strcmp(here, "/.")==0) { strcpy(here+1, here + 2); continue; } if (strncmp(here, "/../", 4) == 0) { strcpy(here+1, here + 4); /* go for the parent */ pos = here-1; while (*pos && *pos != '/') pos--; pos++; strcpy(pos, here+1); here = pos; continue; } if (strcmp(here, "/..")==0) { strcpy(here+1, here + 3); /* go for the parent */ pos = here-1; while (*pos && *pos != '/') pos--; pos++; strcpy(pos, here+1); here = pos; continue; } here++; } if (leaf) { /* find the leaf name */ here = resultpath; while (*here) { if (strncmp(here, "/", 1) == 0) *leaf = here+1; here++; } } return resultpath; } static void print_dir_entry(struct udf_node *udf_node, char *name) { struct stat stat; uint64_t size; int mode, this_mode, uid, gid; int error; error = udfclient_getattr(udf_node, &stat); if (error) return; size = stat.st_size; mode = stat.st_mode; uid = stat.st_uid; gid = stat.st_gid; if (mode & S_IFDIR) printf("d"); else printf("-"); mode = mode & 511; this_mode = (mode >> 6) & 7; printf("%c%c%c", "----rrrr"[this_mode & 4], "--www"[this_mode & 2], "-x"[this_mode & 1]); this_mode = (mode >> 3) & 7; printf("%c%c%c", "----rrrr"[this_mode & 4], "--www"[this_mode & 2], "-x"[this_mode & 1]); this_mode = mode & 7; printf("%c%c%c", "----rrrr"[this_mode & 4], "--www"[this_mode & 2], "-x"[this_mode & 1]); printf(" %5d %5d %10"PRIu64" %s\n", gid, uid, size, name); } #define LS_SUBTREE_DIR_BUFFER_SIZE (16*1024) void udfclient_ls(int args, char *arg1) { struct udf_node *udf_node, *entry_node; uint8_t *buffer; struct uio dir_uio; struct iovec dir_uiovec; struct dirent *dirent; struct stat stat; uint32_t pos; int eof; char *node_name, *leaf_name; int error; if (args > 1) { printf("Syntax: ls [file | dir]\n"); return; } if (args == 0) arg1 = ""; node_name = udfclient_realpath(curdir.name, arg1, &leaf_name); error = udfclient_lookup_pathname(NULL, &udf_node, node_name); if (error) { fprintf(stderr, "%s : %s\n", arg1, strerror(error)); free(node_name); return; } error = udfclient_getattr(udf_node, &stat); if (stat.st_mode & S_IFDIR) { printf("Directory listing of %s\n", udf_node ? leaf_name : "/"); /* start at the start of the directory */ dir_uio.uio_offset = 0; dir_uio.uio_iov = &dir_uiovec; dir_uio.uio_iovcnt = 1; buffer = calloc(1, LS_SUBTREE_DIR_BUFFER_SIZE); if (!buffer) return; do { dir_uiovec.iov_base = buffer; dir_uiovec.iov_len = LS_SUBTREE_DIR_BUFFER_SIZE; dir_uio.uio_resid = LS_SUBTREE_DIR_BUFFER_SIZE; dir_uio.uio_rw = UIO_WRITE; error = udfclient_readdir(udf_node, &dir_uio, &eof); if (error) { fprintf(stderr, "error during readdir: %s\n", strerror(error)); break; } pos = 0; while (pos < LS_SUBTREE_DIR_BUFFER_SIZE - dir_uio.uio_resid) { dirent = (struct dirent *) (buffer + pos); error = udfclient_lookup(udf_node, &entry_node, dirent->d_name); print_dir_entry(entry_node, dirent->d_name); pos += sizeof(struct dirent); } } while (!eof); free(buffer); } else { print_dir_entry(udf_node, leaf_name); } free(node_name); } #undef LS_SUBTREE_DIR_BUFFER_SIZE void udfclient_pwd(int args) { char pwd[1024]; char *res; if (args) { printf("Syntax: pwd\n"); return; } res = getcwd(pwd, 1024); assert(res); printf("UDF working directory is %s\n", curdir.name); printf("Current FS working directory %s\n", pwd); } static void udfclient_print_free_amount(char *prefix, uint64_t value, uint64_t max_value) { printf("%s %10"PRIu64" Kb (%3"PRIu64" %%) (%8.2f Mb) (%5.2f Gb)\n", prefix, value/1024, (100*value)/max_value, (double) value/(1024*1024), (double) value/(1024*1024*1024)); } void udfclient_free(int args) { struct udf_part_mapping *part_mapping; struct udf_partition *udf_partition; struct udf_log_vol *udf_log_vol; struct logvol_desc *lvd; uint64_t part_size, unalloc_space, freed_space; uint64_t total_space, free_space, await_alloc_space; uint32_t lb_size; int part_num; if (args) { printf("Syntax: free\n"); return; } if (!curdir.udf_node || !(udf_log_vol = curdir.udf_node->udf_log_vol)) { printf("Can only report free space in UDF mountpoints\n"); return; } lb_size = udf_log_vol->lb_size; // sector_size = udf_log_vol->sector_size; lvd = udf_log_vol->log_vol; udf_dump_id("Logical volume ", 128, lvd->logvol_id, &lvd->desc_charset); total_space = udf_log_vol->total_space; free_space = udf_log_vol->free_space; await_alloc_space = udf_log_vol->await_alloc_space; SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) { part_num = part_mapping->udf_virt_part_num; udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition); assert(udf_partition); unalloc_space = udf_partition->free_unalloc_space; freed_space = udf_partition->free_freed_space; part_size = udf_partition->part_length; switch (part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_PHYSICAL : printf("\tphysical partition %d\n", part_num); printf("\t\t%8"PRIu64" K (%"PRIu64" pages) size\n", part_size/1024, part_size / lb_size); printf("\t\t%8"PRIu64" K (%"PRIu64" pages) unallocated\n", unalloc_space/1024, unalloc_space / lb_size); printf("\t\t%8"PRIu64" K (%"PRIu64" pages) freed\n", freed_space/1024, freed_space / lb_size); break; case UDF_PART_MAPPING_VIRTUAL : printf("\tvirtual partition mapping %d\n", part_num); printf("\t\tnot applicable\n"); break; case UDF_PART_MAPPING_SPARABLE : printf("\tsparable partition %d\n", part_num); printf("\t\t%8"PRIu64" K (%"PRIu64" pages) size\n", part_size/1024, part_size / lb_size); printf("\t\t%8"PRIu64" K (%"PRIu64" pages) unallocated\n", unalloc_space/1024, unalloc_space / lb_size); printf("\t\t%8"PRIu64" K (%"PRIu64" pages) freed\n", freed_space/1024, freed_space / lb_size); break; case UDF_PART_MAPPING_META : printf("\tmetadata 'partition' %d\n", part_num); printf("\t\t%8"PRIu64" K (%"PRIu64" pages) unallocated\n", unalloc_space/1024, unalloc_space / lb_size); printf("\t\t%8"PRIu64" K (%"PRIu64" pages) freed\n", freed_space/1024, freed_space / lb_size); break; case UDF_PART_MAPPING_ERROR : printf("\terror partiton %d\n", part_num); break; default: break; } } printf("\n"); udfclient_print_free_amount("\tConfirmed free space ", free_space, total_space); udfclient_print_free_amount("\tAwaiting allocation ", await_alloc_space, total_space); udfclient_print_free_amount("\tEstimated free space ", free_space - await_alloc_space, total_space); udfclient_print_free_amount("\tEstimated total used ", total_space - free_space + await_alloc_space, total_space); printf("\n"); udfclient_print_free_amount("\tTotal size ", total_space, total_space); } void udfclient_cd(int args, char *arg1) { struct udf_node *udf_node; struct stat stat; char *node_name, *new_curdir_name; int error; if (args > 1) { printf("Syntax: cd [dir]\n"); return; } new_curdir_name = udfclient_realpath(curdir.name, arg1, NULL); node_name = strdup(new_curdir_name); /* working copy */ error = udfclient_lookup_pathname(NULL, &udf_node, node_name); if (error) { fprintf(stderr, "%s : %s\n", arg1, strerror(error)); free(new_curdir_name); free(node_name); return; } error = udfclient_getattr(udf_node, &stat); if (stat.st_mode & S_IFDIR) { free(curdir.name); curdir.name = new_curdir_name; curdir.udf_node = udf_node; free(node_name); udfclient_pwd(0); } else { fprintf(stderr, "%s is not a directory\n", arg1); free(new_curdir_name); free(node_name); } } void udfclient_lcd(int args, char *arg1) { char pwd[1024]; char *res; if (args > 1) { printf("Syntax: lcd [dir]\n"); return; } if (strcmp(arg1, "" )==0) arg1 = getenv("HOME"); if (strcmp(arg1, "~")==0) arg1 = getenv("HOME"); if (chdir(arg1)) { fprintf(stderr, "While trying to go to %s :", arg1); perror(""); } res = getcwd(pwd, 1024); assert(res); printf("Changing local directory to %s\n", pwd); } void udfclient_lls(int args) { if (args) { printf("Syntax: lls\n"); return; } if (system("/bin/ls")) { perror("While listing current directory\n"); } } uint64_t getmtime(void) { struct timeval tp; gettimeofday(&tp, NULL); return 1000000*tp.tv_sec + tp.tv_usec; } int udfclient_get_file(struct udf_node *udf_node, char *fullsrcname, char *fulldstname) { struct uio file_uio; struct iovec file_iov; struct stat stat; struct timeval times[2]; uint64_t file_length; uint64_t start, now, then, eta; uint64_t cur_speed, avg_speed, data_transfered; uint64_t file_block_size, file_transfer_size, written; uint8_t *file_block; char cur_txt[32], avg_txt[32], eta_txt[32]; int fileh, len; int notok, error; assert(udf_node); assert(fullsrcname); assert(strlen(fullsrcname) >= 1); error = 0; /* terminal directory node? */ error = udfclient_getattr(udf_node, &stat); if (stat.st_mode & S_IFDIR) { len = strlen(fulldstname); if (strcmp(fulldstname + len -2, "/.") == 0) fulldstname[len-2] = 0; if (strcmp(fulldstname + len -3, "/..") == 0) return 0; error = mkdir(fulldstname, (udf_node->stat.st_mode) & 07777); if (!error) { /* set owner attribute and times; access permissions allready set on creation.*/ notok = chown(fulldstname, stat.st_uid, stat.st_gid); if (notok && (udf_verbose > UDF_VERBLEV_ACTIONS)) fprintf(stderr, "failed to set owner of directory, ignoring\n"); TIMESPEC_TO_TIMEVAL(×[0], &stat.st_atimespec); /* access time */ TIMESPEC_TO_TIMEVAL(×[1], &stat.st_mtimespec); /* modification time */ notok = utimes(fulldstname, times); if (notok) fprintf(stderr, "failed to set times on directory, ignoring\n"); } if (error) fprintf(stderr, "While creating directory `%s' : %s\n", fulldstname, strerror(errno)); return 0; } /* terminal file node; setting mode correctly */ fileh = open(fulldstname, O_WRONLY | O_CREAT | O_TRUNC, udf_node->stat.st_mode); if (fileh >= 0) { file_length = udf_node->stat.st_size; file_block_size = 256*1024; /* block read in length */ file_block = malloc(file_block_size); if (!file_block) { printf("Out of memory claiming file buffer\n"); return ENOMEM; } /* move to uio_newuio(struct uio *uio) with fixed length uio_iovcnt? */ bzero(&file_uio, sizeof(struct uio)); file_uio.uio_rw = UIO_WRITE; /* WRITE into this space */ file_uio.uio_iovcnt = 1; file_uio.uio_iov = &file_iov; start = getmtime(); then = now = start; eta = data_transfered = 0; strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---"); file_uio.uio_offset = 0; /* begin at the start */ do { /* fill in IO vector space; reuse blob file_block over and over */ file_transfer_size = MIN(file_block_size, file_length - file_uio.uio_offset); file_uio.uio_resid = file_transfer_size; file_uio.uio_iov->iov_base = file_block; file_uio.uio_iov->iov_len = file_block_size; error = udf_read_file_part_uio(udf_node, fullsrcname, UDF_C_USERDATA, &file_uio); if (error) { fprintf(stderr, "While retrieving file block : %s\n", strerror(error)); printf("\n\n\n"); /* XXX */ break; } written = write(fileh, file_block, file_transfer_size); assert(written == file_transfer_size); if ((getmtime() - now > 1000000) || ((uint64_t) file_uio.uio_offset >= file_length)) { if (strlen(fulldstname) < 45) { printf("\r%-45s ", fulldstname); } else { printf("\r...%-42s ", fulldstname + strlen(fulldstname)-42); } printf("%10"PRIu64" / %10"PRIu64" bytes ", (uint64_t) file_uio.uio_offset, (uint64_t) file_length); if (file_length) printf("(%3d%%) ", (int) (100.0*(float) file_uio.uio_offset / file_length)); then = now; now = getmtime(); cur_speed = 0; avg_speed = 0; if (now-start > 0) avg_speed = (1000000 * file_uio.uio_offset) / (now-start); if (now-then > 0) cur_speed = (1000000 * (file_uio.uio_offset - data_transfered)) / (now-then); if (avg_speed > 0) eta = (file_length - file_uio.uio_offset) / avg_speed; data_transfered = file_uio.uio_offset; strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---"); if (avg_speed > 0) sprintf(avg_txt, "%d", (int32_t) avg_speed/1000); if (cur_speed > 0) sprintf(cur_txt, "%d", (int32_t) cur_speed/1000); if (eta > 0) sprintf(eta_txt, "%02d:%02d:%02d", (int) (eta/3600), (int) (eta/60) % 60, (int) eta % 60); printf("%6s KB/s (%6s KB/s) ETA %s", avg_txt, cur_txt, eta_txt); fflush(stdout); } } while ((uint64_t) file_uio.uio_offset < file_length); printf(" finished\n"); free(file_block); /* set owner attribute and times; access permissions allready set on creation.*/ notok = fchown(fileh, stat.st_uid, stat.st_gid); if (notok && (udf_verbose > UDF_VERBLEV_ACTIONS)) fprintf(stderr, "failed to set owner of file, ignoring\n"); TIMESPEC_TO_TIMEVAL(×[0], &stat.st_atimespec); /* access time */ TIMESPEC_TO_TIMEVAL(×[1], &stat.st_mtimespec); /* modification time */ notok = futimes(fileh, times); if (notok) fprintf(stderr, "failed to set times on directory, ignoring\n"); close(fileh); } else { printf("Help! can't open file %s for output\n", fulldstname); } return error; } #define GET_SUBTREE_DIR_BUFFER_SIZE (16*1024) void udfclient_get_subtree(struct udf_node *udf_node, char *srcprefix, char *dstprefix, int recurse, uint64_t *total_size) { struct uio dir_uio; struct iovec dir_iovec; uint8_t *buffer; uint32_t pos; char fullsrcpath[1024], fulldstpath[1024]; /* XXX arbitrary length XXX */ struct dirent *dirent; struct stat stat; struct udf_node *entry_node; struct fileid_desc *fid; struct long_ad udf_icbptr; int lb_size, eof; int found, isdot, isdotdot, error; if (!udf_node) return; udf_node->hold++; error = udfclient_getattr(udf_node, &stat); if ((stat.st_mode & S_IFDIR) && recurse) { buffer = malloc(GET_SUBTREE_DIR_BUFFER_SIZE); if (!buffer) { udf_node->hold--; return; } lb_size = udf_node->udf_log_vol->lb_size; fid = malloc(lb_size); assert(fid); /* recurse into this directory */ dir_uio.uio_offset = 0; /* begin at start */ do { dir_iovec.iov_base = buffer; dir_iovec.iov_len = GET_SUBTREE_DIR_BUFFER_SIZE; dir_uio.uio_resid = GET_SUBTREE_DIR_BUFFER_SIZE; dir_uio.uio_iovcnt = 1; dir_uio.uio_iov = &dir_iovec; dir_uio.uio_rw = UIO_WRITE; error = udf_readdir(udf_node, &dir_uio, &eof); pos = 0; while (pos < GET_SUBTREE_DIR_BUFFER_SIZE - dir_uio.uio_resid) { dirent = (struct dirent *) (buffer + pos); sprintf(fullsrcpath, "%s/%s", srcprefix, dirent->d_name); sprintf(fulldstpath, "%s/%s", dstprefix, dirent->d_name); /* looking for '.' or '..' ? */ isdot = (strcmp(dirent->d_name, "." ) == 0); isdotdot = (strcmp(dirent->d_name, "..") == 0); pos += sizeof(struct dirent); /* XXX variable size dirents possible XXX */ if (isdotdot) continue; if (isdot) { /* hack */ udfclient_get_subtree(udf_node, fullsrcpath, fulldstpath, 0, total_size); continue; } error = udf_lookup_name_in_dir(udf_node, dirent->d_name, DIRENT_NAMLEN(dirent), &udf_icbptr, fid, &found); if (!error) { error = ENOENT; if (found) error = udf_readin_udf_node(udf_node, &udf_icbptr, fid, &entry_node); } if (!error) udfclient_get_subtree(entry_node, fullsrcpath, fulldstpath, 1, total_size); } } while (!eof); udf_node->hold--; free(buffer); free(fid); return; } /* leaf node : prefix is complete name but with `/' prefix */ if (*srcprefix == '/') srcprefix++; error = udfclient_get_file(udf_node, srcprefix, dstprefix); udf_node->hold--; if (!error) *total_size += udf_node->stat.st_size; } #undef GET_SUBTREE_DIR_BUFFER_SIZE void udfclient_get(int args, char *arg1, char *arg2) { struct udf_node *udf_node; char *source_name, *target_name; uint64_t start, now, totalsize, avg_speed; int error; if (args > 2) { printf("Syntax: get remote [local]\n"); return; } source_name = arg1; target_name = arg1; if (args == 2) target_name = arg2; /* source name gets substituted */ source_name = udfclient_realpath(curdir.name, source_name, NULL); DEBUG(printf("Attempting to retrieve %s\n", source_name)); error = udfclient_lookup_pathname(NULL, &udf_node, source_name); if (error) { fprintf(stderr, "%s : %s\n", arg1, strerror(error)); free(source_name); return; } /* get the file/dir tree */ totalsize = 0; start = getmtime(); udfclient_get_subtree(udf_node, source_name, target_name, 1, &totalsize); now = getmtime(); if (now-start > 0) { avg_speed = (1000000 * totalsize) / (now-start); printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t) (totalsize/1024), (uint32_t) (avg_speed/1024)); } else { printf("Transfered %d kb\n", (uint32_t)(totalsize/1024)); } /* bugalert: not releasing target_name for its not substituted */ free(source_name); } void udfclient_mget(int args, char *argv[]) { struct udf_node *udf_node; uint64_t start, now, totalsize, avg_speed; char *node_name, *source_name, *target_name; int arg, error; if (args == 0) { printf("Syntax: mget (file | dir)*\n"); return; } /* retrieve the series of file/dir trees and measure time/seed */ totalsize = 0; start = getmtime(); /* process all args */ arg = 0; node_name = NULL; while (args) { source_name = target_name = argv[arg]; node_name = udfclient_realpath(curdir.name, source_name, NULL); DEBUG(printf("Attempting to retrieve %s\n", node_name)); error = udfclient_lookup_pathname(NULL, &udf_node, node_name); printf("%d: mget trying %s\n", error, node_name); if (!error) { udfclient_get_subtree(udf_node, source_name, target_name, 1, &totalsize); } if (node_name) { free(node_name); node_name = NULL; } if (error) break; /* TODO continuation flag? */ /* advance */ arg++; args--; } now = getmtime(); if (now-start > 0) { avg_speed = (1000000 * totalsize) / (now-start); printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t) (totalsize/1024), (uint32_t) (avg_speed/1024)); } else { printf("Transfered %d kb\n", (uint32_t) (totalsize/1024)); } if (node_name) free(node_name); } int udfclient_put_file(struct udf_node *udf_node, char *fullsrcname, char *fulldstname) { struct uio file_uio; struct iovec file_iov; uint64_t file_length; uint64_t start, now, then, eta; uint64_t cur_speed, avg_speed, data_transfered; uint64_t file_block_size, file_transfer_size; uint8_t *file_block; char cur_txt[32], avg_txt[32], eta_txt[32]; int fileh; int error, printed; assert(udf_node); assert(fullsrcname); DEBUG(printf("Attempting to write %s\n", fullsrcname)); fileh = open(fullsrcname, O_RDONLY, 0666); if (fileh == -1) { fprintf(stderr, "Can't open local file %s for reading: %s\n", fullsrcname, strerror(errno)); return ENOENT; } /* get file length */ file_length = lseek(fileh, 0, SEEK_END); lseek(fileh, 0, SEEK_SET); /* check if file will fit; give it a bit of slack space until the space issue is found and fixed */ if (udf_node->udf_log_vol->free_space < file_length + udf_node->udf_log_vol->await_alloc_space + UDF_MINFREE_LOGVOL) { return ENOSPC; } /* allocate file block to transfer file with */ file_block_size = 128*1024; file_block = malloc(file_block_size); if (!file_block) { fprintf(stderr, "Out of memory claiming file buffer\n"); return ENOMEM; } /* move to uio_newuio(struct uio *uio) with fixed length uio_iovcnt? */ bzero(&file_uio, sizeof(struct uio)); file_uio.uio_rw = UIO_READ; /* READ from this space */ file_uio.uio_iovcnt = 1; file_uio.uio_iov = &file_iov; /* ------------ */ start = getmtime(); then = now = start; eta = data_transfered = 0; printed = 0; strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---"); /* ------------ */ error = 0; error = udf_truncate_node(udf_node, 0); while (!error && ((uint64_t) file_uio.uio_offset < file_length)) { file_transfer_size = MIN(file_block_size, file_length - file_uio.uio_offset); error = read(fileh, file_block, file_transfer_size); if (error<0) { fprintf(stderr, "While reading in file block for writing : %s\n", strerror(errno)); error = errno; break; } file_uio.uio_resid = file_transfer_size; file_uio.uio_iov->iov_base = file_block; file_uio.uio_iov->iov_len = file_block_size; error = udf_write_file_part_uio(udf_node, fullsrcname, UDF_C_USERDATA, &file_uio); if (error) { fprintf(stderr, "\nError while writing file : %s\n", strerror(error)); break; } /* ------------ */ if ((getmtime() - now > 1000000) || ((uint64_t) file_uio.uio_offset >= file_length)) { printed = 1; if (strlen(fulldstname) < 45) { printf("\r%-45s ", fulldstname); } else { printf("\r...%-42s ", fulldstname+strlen(fulldstname)-42); } printf("%10"PRIu64" / %10"PRIu64" bytes ", (uint64_t) file_uio.uio_offset, (uint64_t) file_length); if (file_length) printf("(%3d%%) ", (int) (100.0*(float) file_uio.uio_offset / file_length)); then = now; now = getmtime(); cur_speed = 0; avg_speed = 0; if (now-start > 0) avg_speed = (1000000 * file_uio.uio_offset) / (now-start); if (now-then > 0) cur_speed = (1000000 * (file_uio.uio_offset - data_transfered)) / (now-then); if (avg_speed > 0) eta = (file_length - file_uio.uio_offset) / avg_speed; data_transfered = file_uio.uio_offset; strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---"); if (avg_speed > 0) sprintf(avg_txt, "%d", (int32_t) avg_speed/1024); if (cur_speed > 0) sprintf(cur_txt, "%d", (int32_t) cur_speed/1024); if (eta > 0) sprintf(eta_txt, "%02d:%02d:%02d", (int) (eta/3600), (int) (eta/60) % 60, (int) eta % 60); printf("%6s KB/s (%6s KB/s) ETA %s", avg_txt, cur_txt, eta_txt); fflush(stdout); } /* ------------ */ } if (!error && printed) printf(" finished\n"); close(fileh); free(file_block); return error; } int udfclient_put_subtree(struct udf_node *parent_node, char *srcprefix, char *srcname, char *dstprefix, char *dstname, uint64_t *totalsize) { struct udf_node *file_node, *dir_node; struct dirent *dirent; struct stat stat; DIR *dir; char fullsrcpath[1024], fulldstpath[1024]; int error; sprintf(fullsrcpath, "%s/%s", srcprefix, srcname); sprintf(fulldstpath, "%s/%s", dstprefix, dstname); /* stat source file */ bzero(&stat, sizeof(struct stat)); error = lstat(fullsrcpath, &stat); if (error) { error = errno; /* lstat symantics; returns -1 on error */ fprintf(stderr, "Can't stat file/dir \"%s\"! : %s\n", fullsrcpath, strerror(error)); return error; } /* test if `srcname' refers to a directory */ dir = opendir(fullsrcpath); if (dir) { error = udfclient_lookup(parent_node, &dir_node, dstname); if (error) { DEBUG(printf("Create dir %s on UDF\n", fulldstpath)); error = udf_create_directory(parent_node, dstname, &stat, &dir_node); if (error) { closedir(dir); fprintf(stderr, "UDF: couldn't create new directory %s : %s\n", dstname, strerror(error)); return error; } } dir_node->hold++; dirent = readdir(dir); while (dirent) { if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, "..")) { /* skip `.' and ',,' */ error = udfclient_put_subtree(dir_node, fullsrcpath, dirent->d_name, fulldstpath, dirent->d_name, totalsize); if (error) break; } dirent = readdir(dir); } closedir(dir); dir_node->hold--; return error; } /* leaf node : prefix is complete name but with `/' prefix */ DEBUG(printf("Put leaf: %s\n", fulldstpath)); error = udfclient_lookup(parent_node, &file_node, dstname); if (!file_node) { error = udf_create_file(parent_node, dstname, &stat, &file_node); if (error) { fprintf(stderr, "UDF: couldn't add new file entry in directory %s for %s : %s\n", dstprefix, dstname, strerror(error)); return error; } } file_node->hold++; error = udfclient_put_file(file_node, fullsrcpath, fulldstpath); file_node->hold--; if (error) fprintf(stderr, "UDF: Couldn't write file %s : %s\n", fulldstpath, strerror(error)); if (error) udf_remove_file(parent_node, file_node, dstname); if (!error) *totalsize += file_node->stat.st_size; return error; } void udfclient_put(int args, char *arg1, char *arg2) { struct udf_node *curdir_node; uint64_t start, now, totalsize, avg_speed; char *source_name, *target_name; int error; if (args > 2) { printf("Syntax: put source [destination]\n"); return; } if (read_only) { printf("Modifying this filingsystem is prevented; use -W flag to enable writing on your own risk!\n"); return; } error = udfclient_lookup_pathname(NULL, &curdir_node, curdir.name); if (error) { printf("Current directory not found?\n"); return; } DEBUG(printf("Attempting to copy %s\n", arg1)); /* determine source and destination entities */ source_name = arg1; target_name = arg1; if (args == 2) target_name = arg2; /* writeout file/dir tree and measure the time/speed */ totalsize = 0; start = getmtime(); error = udfclient_put_subtree(curdir_node, ".", source_name, ".", target_name, &totalsize); now = getmtime(); if (now-start > 0) { avg_speed = (1000000 * totalsize) / (now-start); printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t) (totalsize/1024), (uint32_t) (avg_speed/1024)); } else { printf("Transfered %d kb\n", (uint32_t) (totalsize/1024)); } } /* args start at position 0 of argv */ void udfclient_mput(int args, char **argv) { struct udf_node *curdir_node; uint64_t start, now, totalsize, avg_speed; char *source_name, *target_name; int arg, error; if (args == 0) { printf("Syntax: mput (file | dir)*\n"); return; } if (read_only) { printf("Modifying this filingsystem is prevented; use -W flag to enable writing on your own risk!\n"); return; } error = udfclient_lookup_pathname(NULL, &curdir_node, curdir.name); if (error) { printf("Current directory not found?\n"); return; } /* writeout file/dir trees and measure the time/speed */ totalsize = 0; start = getmtime(); /* process all args */ arg = 0; while (args) { source_name = target_name = argv[arg]; error = udfclient_put_subtree(curdir_node, ".", source_name, ".", target_name, &totalsize); if (error) { fprintf(stderr, "While writing file %s : %s\n", source_name, strerror(error)); break; /* TODO continuation flag? */ } /* advance */ arg++; args--; } now = getmtime(); if (now-start > 0) { avg_speed = (1000000 * totalsize) / (now-start); printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t)(totalsize/1024), (uint32_t)(avg_speed/1024)); } else { printf("Transfered %d kb\n", (uint32_t)(totalsize/1024)); } } void udfclient_trunc(int args, char *arg1, char *arg2) { struct udf_node *udf_node; char *node_name; uint64_t length; int error; if (args != 2) { printf("Syntax: trunc file length\n"); return; } length = strtoll(arg2, NULL, 10); node_name = udfclient_realpath(curdir.name, arg1, NULL); error = udfclient_lookup_pathname(NULL, &udf_node, node_name); if (error || !udf_node) { printf("Can only truncate existing file!\n"); free(node_name); return; } udf_truncate_node(udf_node, length); free(node_name); } void udfclient_sync(void) { struct udf_discinfo *udf_disc; SLIST_FOREACH(udf_disc, &udf_discs_list, next_disc) { udf_sync_disc(udf_disc); } } #define RM_SUBTREE_DIR_BUFFER_SIZE (32*1024) int udfclient_rm_subtree(struct udf_node *parent_node, struct udf_node *dir_node, char *name, char *full_parent_name) { struct uio dir_uio; struct iovec dir_iovec; uint8_t *buffer; uint32_t pos; char *fullpath; struct dirent *dirent; struct stat stat; struct udf_node *entry_node; struct fileid_desc *fid; struct long_ad udf_icbptr; int lb_size, eof, found, isdot, isdotdot; int error; if (!dir_node) return ENOENT; error = udfclient_getattr(dir_node, &stat); if (stat.st_mode & S_IFDIR) { buffer = malloc(RM_SUBTREE_DIR_BUFFER_SIZE); if (!buffer) return ENOSPC; lb_size = dir_node->udf_log_vol->lb_size; fid = malloc(lb_size); if (!fid) { free(buffer); return ENOSPC; } /* recurse into this directory */ dir_uio.uio_offset = 0; /* begin at start */ do { dir_iovec.iov_base = buffer; dir_iovec.iov_len = RM_SUBTREE_DIR_BUFFER_SIZE; dir_uio.uio_resid = RM_SUBTREE_DIR_BUFFER_SIZE; dir_uio.uio_iovcnt = 1; dir_uio.uio_iov = &dir_iovec; dir_uio.uio_rw = UIO_WRITE; error = udf_readdir(dir_node, &dir_uio, &eof); pos = 0; while (pos < RM_SUBTREE_DIR_BUFFER_SIZE - dir_uio.uio_resid) { dirent = (struct dirent *) (buffer + pos); pos += sizeof(struct dirent); /* XXX variable size dirents possible XXX */ /* looking for '.' or '..' ? */ isdot = (strcmp(dirent->d_name, "." ) == 0); isdotdot = (strcmp(dirent->d_name, "..") == 0); if (isdot || isdotdot) continue; error = udf_lookup_name_in_dir(dir_node, dirent->d_name, DIRENT_NAMLEN(dirent), &udf_icbptr, fid, &found); if (!error) { error = ENOENT; if (found) error = udf_readin_udf_node(dir_node, &udf_icbptr, fid, &entry_node); } if (error) break; error = udfclient_getattr(entry_node, &stat); if (error) break; /* check if the direntry is a directory or a file */ if (stat.st_mode & S_IFDIR) { fullpath = malloc(strlen(full_parent_name) + strlen(dirent->d_name)+2); if (fullpath) { sprintf(fullpath, "%s/%s", full_parent_name, dirent->d_name); error = udfclient_rm_subtree(dir_node, entry_node, dirent->d_name, fullpath); } else { error = ENOMEM; } free(fullpath); } else { error = udf_remove_file(dir_node, entry_node, dirent->d_name); if (!error) printf("rm %s/%s\n", full_parent_name, dirent->d_name); } if (error) break; } } while (!eof); free(buffer); free(fid); /* leaving directory -> delete directory itself */ if (!error) { error = udf_remove_directory(parent_node, dir_node, name); if (!error) printf("rmdir %s/%s\n", full_parent_name, name); } return error; } return ENOTDIR; } #undef RM_SUBTREE_DIR_BUFFER_SIZE void udfclient_rm(int args, char *argv[]) { struct udf_node *remove_node, *parent_node; struct stat stat; char *target_name, *leaf_name, *full_parent_name; int error, len, arg; if (args == 0) { printf("Syntax: rm (file | dir)*\n"); return; } /* process all args; effectively multiplying an `rm' command */ arg = 0; while (args) { leaf_name = argv[arg]; /* lookup node; target_name gets substituted */ target_name = udfclient_realpath(curdir.name, leaf_name, &leaf_name); error = udfclient_lookup_pathname(NULL, &remove_node, target_name); if (error || !remove_node) { printf("rm %s : %s\n", target_name, strerror(error)); free(target_name); return; /* TODO continuation flag */ /* continue; */ } full_parent_name = udfclient_realpath(target_name, "..", NULL); error = udfclient_lookup_pathname(NULL, &parent_node, full_parent_name); if (error || !parent_node) { printf("rm %s : parent lookup failed : %s\n", target_name, strerror(error)); free(target_name); free(full_parent_name); return; /* TODO continuation flag */ /* continue; */ } error = udfclient_getattr(remove_node, &stat); if (!error) { if (stat.st_mode & S_IFDIR) { len = strlen(target_name); if (target_name[len-1] == '/') target_name[len-1] = '\0'; error = udfclient_rm_subtree(parent_node, remove_node, leaf_name, target_name); } else { error = udf_remove_file(parent_node, remove_node, leaf_name); if (!error) printf("rm %s/%s\n", full_parent_name, leaf_name); } } if (error) fprintf(stderr, "While removing file/dir : %s\n", strerror(error)); free(target_name); free(full_parent_name); if (error) break; /* TODO continuation flag */ /* advance */ arg++; args--; } } void udfclient_mv(int args, char *from, char *to) { struct udf_node *rename_me, *present, *old_parent, *new_parent; char *rename_from_name, *rename_to_name, *old_parent_name, *new_parent_name; int error; if (args != 2) { printf("Syntax: mv source destination\n"); return; } /* `from' gets substituted by its leaf name */ rename_from_name = udfclient_realpath(curdir.name, from, &from); error = udfclient_lookup_pathname(NULL, &rename_me, rename_from_name); if (error || !rename_me) { printf("Can't find file/dir to be renamed\n"); free(rename_from_name); return; } old_parent_name = udfclient_realpath(rename_from_name, "..", NULL); error = udfclient_lookup_pathname(NULL, &old_parent, old_parent_name); if (error || !old_parent) { printf("Can't determine rootdir of renamed file?\n"); free(rename_from_name); free(old_parent_name); return; } /* `to' gets substituted by its leaf name */ rename_to_name = udfclient_realpath(curdir.name, to, &to); udfclient_lookup_pathname(NULL, &present, rename_to_name); new_parent_name = udfclient_realpath(rename_to_name, "..", NULL); error = udfclient_lookup_pathname(NULL, &new_parent, new_parent_name); if (error || !new_parent) { printf("Can't determine rootdir of destination\n"); free(rename_from_name); free(rename_to_name); free(old_parent_name); free(new_parent_name); return; } error = udf_rename(old_parent, rename_me, from, new_parent, present, to); if (error) { printf("Can't move file or directory: %s\n", strerror(error)); return; } free(rename_from_name); free(rename_to_name); free(old_parent_name); free(new_parent_name); } void udfclient_mkdir(int args, char *arg1) { struct stat stat; struct udf_node *udf_node, *parent_node; char *full_create_name, *dirname, *parent_name; int error; if (args != 1) { printf("Syntax: mkdir dir\n"); return; } /* get full name of dir to be created */ full_create_name = udfclient_realpath(curdir.name, arg1, &dirname); parent_name = udfclient_realpath(full_create_name, "..", NULL); error = udfclient_lookup_pathname(NULL, &parent_node, parent_name); if (error || !parent_node) { printf("Can't determine directory the new directory needs to be created in %d <%s> <%s> <%s>\n", error, parent_name, full_create_name, curdir.name); free(full_create_name); free(parent_name); return; } bzero(&stat, sizeof(struct stat)); stat.st_uid = UINT_MAX; stat.st_gid = UINT_MAX; stat.st_mode = 0755 | S_IFDIR; /* don't forget this! */ error = udf_create_directory(parent_node, dirname, &stat, &udf_node); if (error) { printf("Can't create directory %s : %s\n", arg1, strerror(error)); } free(full_create_name); free(parent_name); } /* `line' gets more and more messed up in the proces */ char *udfclient_get_one_arg(char *line, char **result) { unsigned char chr, limiter; char *end_arg; *result = NULL; /* get prepending whitespace */ while (*line && (*line <= ' ')) line++; chr= '\0'; limiter = ' '; if (*line == '"') { line++; limiter = '"'; } *result = line; while (*line) { chr = *line; if (chr && (chr < ' ')) chr = ' '; if (chr == 0 || chr == limiter) { break; } else { *line = chr; } line++; } end_arg = line; if (chr == limiter) line++; /* get appended whitespace */ while (*line && (*line <= ' ')) line++; *end_arg = 0; return line; } int udfclient_get_args(char *line, char *argv[]) { int arg, args; for (arg = 0; arg < MAX_ARGS+1; arg++) { argv[arg] = ""; } /* get all arguments */ args = 0; while (args < MAX_ARGS+1) { line = udfclient_get_one_arg(line, &argv[args]); args++; if (!*line) { return args; } } printf("UDFclient implementation limit: too many arguments\n"); return 0; } void udfclient_interact(void) { int args, params; char *cmd; char *argv[MAX_ARGS+1]; char line[4096]; udfclient_pwd(0); while (1) { printf("UDF> "); clearerr(stdin); *line = 0; (void) fgets(line, 4096, stdin); if ((*line == 0) && feof(stdin)) { printf("quit\n"); return; } args = udfclient_get_args(line, argv); cmd = argv[0]; params = args -1; if (args) { if (strcmp(cmd, "")==0) continue; if (strcmp(cmd, "ls")==0) { udfclient_ls(params, argv[1]); continue; } if (strcmp(cmd, "cd")==0) { udfclient_cd(params, argv[1]); continue; } if (strcmp(cmd, "lcd")==0) { udfclient_lcd(params, argv[1]); continue; } if (strcmp(cmd, "lls")==0) { udfclient_lls(params); continue; } if (strcmp(cmd, "pwd")==0) { udfclient_pwd(params); continue; } if (strcmp(cmd, "free")==0) { udfclient_free(params); continue; } if (strcmp(cmd, "get")==0) { udfclient_get(params, argv[1], argv[2]); continue; } if (strcmp(cmd, "mget")==0) { udfclient_mget(params, argv + 1); continue; } if (strcmp(cmd, "put")==0) { /* can get destination file/dir (one day) */ udfclient_put(params, argv[1], argv[2]); continue; } if (strcmp(cmd, "mput")==0) { udfclient_mput(params, argv + 1); continue; } if (strcmp(cmd, "trunc")==0) { udfclient_trunc(params, argv[1], argv[2]); continue; } if (strcmp(cmd, "mkdir")==0) { udfclient_mkdir(params, argv[1]); continue; } if (strcmp(cmd, "rm")==0) { udfclient_rm(params, argv + 1); continue; } if (strcmp(cmd, "mv")==0) { udfclient_mv(params, argv[1], argv[2]); continue; } if (strcmp(cmd, "sync")==0) { udfclient_sync(); continue; } if (strcmp(cmd, "help")==0) { printf("Selected commands available (use \" pair for filenames with spaces) :\n" "ls [file | dir]\tlists the UDF directory\n" "cd [dir]\t\tchange current UDF directory\n" "lcd [dir]\t\tchange current directory\n" "lls\t\t\tlists current directory\n" "pwd\t\t\tdisplay current directories\n" "free\t\t\tdisplay free space on disc\n" "get source [dest]\tretrieve a file / directory from disc\n" "mget (file | dir)*\tretrieve set of files / directories\n" "put source [dest]\twrite a file / directory to disc\n" "mput (file | dir)*\twrite a set of files / directories\n" "trunc file length\ttrunc file to length\n" "mkdir dir\t\tcreate directory\n" "rm (file | dir)*\tdelete set of files / directories\n" "mv source dest\t\trename a file (limited)\n" "sync\t\t\tsync filingsystem\n" "quit\t\t\texits program\n" "exit\t\t\talias for quit\n" ); continue; } if (strcmp(cmd, "quit")==0 || strcmp(cmd, "exit")==0) { return; } printf("\nUnknown command %s\n", cmd); } } } #if 1 int usage(char *program) { fprintf(stderr, "Usage: %s [options] devicename [devicename]*)\n", program); fprintf(stderr, "-u level UDF system verbose level\n" "-r range use only selected sessions like -3,5,7 or 6-\n" "-W allow writing (temporary flag)\n" "-F force mount writable when marked dirty (use with cause)\n" "-b blocksize use alternative sectorsize; use only on files/discs\n" "-D debug/verbose SCSI command errors\n" "-s byteswap read sectors (for PVRs)\n" ); return 1; } extern char *optarg; extern int optind; extern int optreset; int main(int argc, char *argv[]) { struct udf_discinfo *disc, *next_disc; uint32_t alt_sector_size; char *progname, *range; int flag, mnt_flags; int error; progname = argv[0]; if (argc == 1) { return usage(progname); } /* be a bit more verbose */ udf_verbose = UDF_VERBLEV_ACTIONS; uscsilib_verbose= 0; mnt_flags = UDF_MNT_RDONLY; range = NULL; alt_sector_size = 0; while ((flag = getopt(argc, argv, "u:Dr:WFb:s")) != -1) { switch (flag) { case 'u' : udf_verbose = atoi(optarg); break; case 'D' : uscsilib_verbose = 1; break; case 'r' : range = strdup(optarg); if (udf_check_session_range(range)) { fprintf(stderr, "Invalid range %s\n", range); return usage(progname); } break; case 'W' : mnt_flags &= ~UDF_MNT_RDONLY; break; case 'F' : mnt_flags |= UDF_MNT_FORCE; break; case 'b' : alt_sector_size = atoi(optarg); break; case 's' : mnt_flags |= UDF_MNT_BSWAP; break; default : return usage(progname); } } argv += optind; argc -= optind; if (argc == 0) return usage(progname); if (!(mnt_flags & UDF_MNT_RDONLY)) { printf("--------------------------------\n"); printf("WARNING: writing enabled, use on own risc\n"); printf("\t* DONT cancel program or data-loss might occure\n"); printf("\t* set dataspace userlimits very high when writing thousands of files\n"); printf("\nEnjoy your writing!\n"); printf("--------------------------------\n\n\n"); printf("%c", 7); fflush(stdout); sleep(1); printf("%c", 7); fflush(stdout); sleep(1); printf("%c", 7); fflush(stdout); } /* all other arguments are devices */ udf_init(); while (argc) { printf("Opening device %s\n", *argv); error = udf_mount_disc(*argv, range, alt_sector_size, mnt_flags, &disc); if (error) { fprintf(stderr, "Can't open my device; bailing out : %s\n", strerror(error)); exit(1); } if (read_only) disc->recordable = 0; if (read_only) disc->rewritable = 0; argv++; argc--; if (udf_verbose) printf("\n\n"); } printf("\n"); printf("Resulting list of alive sets :\n\n"); udf_dump_alive_sets(); /* interactive part */ bzero(&curdir, sizeof(struct curdir)); curdir.mountpoint = NULL; curdir.name = strdup("/"); udfclient_ls(0, ""); udfclient_interact(); /* close part */ printf("Closing discs\n"); disc = SLIST_FIRST(&udf_discs_list); while (disc) { next_disc = SLIST_NEXT(disc, next_disc); udf_dismount_disc(disc); disc = next_disc; } return 0; } #endif linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/uio.c000066400000000000000000000045321523477242100251050ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "uio.c" is part of the UDFclient toolkit. * File $Id: uio.c,v 1.7 2011/02/01 20:43:41 reinoud Exp $ $Name: $ * * Copyright (c) 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include "uio.h" /* modelled after NetBSD's uiomove */ int uiomove(void *buf, size_t amount, struct uio *uio) { struct iovec *iov; char *cp = buf; size_t cnt; assert(buf); assert(uio); assert(uio->uio_iov); while (amount > 0 && uio->uio_resid > 0) { iov = uio->uio_iov; cnt = iov->iov_len; if (cnt == 0) { assert(uio->uio_iovcnt > 0); uio->uio_iov++; uio->uio_iovcnt--; if (uio->uio_iovcnt == 0) return 0; /* end of buffers */ continue; } /* not more than requested please */ if (cnt > amount) cnt = amount; /* move the information */ if (uio->uio_rw == UIO_READ) { /* read into the uio */ memcpy(cp, iov->iov_base, cnt); } else { /* write from the uio */ memcpy(iov->iov_base, cp, cnt); } /* update the uio structure to reflect move */ iov->iov_base = (caddr_t) iov->iov_base + cnt; iov->iov_len -= cnt; uio->uio_resid -= cnt; uio->uio_offset += cnt; cp += cnt; assert(cnt <= amount); amount -= cnt; } return 0; } /* end of uio.c */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/uio.h000066400000000000000000000042401523477242100251060ustar00rootroot00000000000000/* $NetBSD$ */ /* * File "uio.h" is part of the UDFclient toolkit. * File $Id: uio.h,v 1.6 2011/02/01 20:43:41 reinoud Exp $ $Name: $ * * Copyright (c) 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _SYS_UIO_H_ #define _SYS_UIO_H_ #include #include /* define an uio structure for fragmenting reading in and writing data out */ /* modelled after BSD's kernel structures */ enum uio_rw { UIO_READ, UIO_WRITE }; struct iovec { void *iov_base; /* base if this chunk */ size_t iov_len; /* length */ }; /* this is consumed !! i.e. keep a copy */ struct uio { struct iovec *uio_iov; /* pointer to array of iovecs */ int uio_iovcnt; /* number of iovecs in array */ off_t uio_offset; /* current offset */ size_t uio_resid; /* residual i/o count */ enum uio_rw uio_rw; /* read/write direction */ /* ... rest obmitted ... */ }; #endif /* _SYS_UIO_H_ */ /* allways declare the functions */ /* move data from/to a uio structure to a blob */ extern int uiomove(void *buf, size_t amount, struct uio *uio); /* end of uio.h */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/uscsi_sense.c000066400000000000000000001056751523477242100266460ustar00rootroot00000000000000/* $NetBSD: scsipi_verbose.c,v 1.27 2005/05/29 22:00:50 christos Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Small changes made by Reinoud Zandijk */ /* * SCSI sense interpretation. Lifted from src/sys/dev/scsipi/scsipi_verbose.c, * and modified for userland. */ #include #include #include #include #include "uscsilib.h" static const char *sense_keys[16] = { "No Additional Sense", "Recovered Error", "Not Ready", "Media Error", "Hardware Error", "Illegal Request", "Unit Attention", "Write Protected", "Blank Check", "Vendor Unique", "Copy Aborted", "Aborted Command", "Equal Error", "Volume Overflow", "Miscompare Error", "Reserved" }; /* * The current version of this list can be obtained from * */ static const struct { unsigned char asc; unsigned char ascq; const char *description; } adesc[] = { { 0x00, 0x00, "No Additional Sense Information" }, { 0x00, 0x01, "Filemark Detected" }, { 0x00, 0x02, "End-Of-Partition/Medium Detected" }, { 0x00, 0x03, "Setmark Detected" }, { 0x00, 0x04, "Beginning-Of-Partition/Medium Detected" }, { 0x00, 0x05, "End-Of-Data Detected" }, { 0x00, 0x06, "I/O Process Terminated" }, { 0x00, 0x11, "Audio Play Operation In Progress" }, { 0x00, 0x12, "Audio Play Operation Paused" }, { 0x00, 0x13, "Audio Play Operation Successfully Completed" }, { 0x00, 0x14, "Audio Play Operation Stopped Due to Error" }, { 0x00, 0x15, "No Current Audio Status To Return" }, { 0x00, 0x16, "Operation In Progress" }, { 0x00, 0x17, "Cleaning Requested" }, { 0x00, 0x18, "Erase Operation In Progress" }, { 0x00, 0x19, "Locate Operation In Progress" }, { 0x00, 0x1A, "Rewind Operation In Progress" }, { 0x00, 0x1B, "Set Capacity Operation In Progess" }, { 0x00, 0x1C, "Verify Operation In Progress" }, { 0x01, 0x00, "No Index/Sector Signal" }, { 0x02, 0x00, "No Seek Complete" }, { 0x03, 0x00, "Peripheral Device Write Fault" }, { 0x03, 0x01, "No Write Current" }, { 0x03, 0x02, "Excessive Write Errors" }, { 0x04, 0x00, "Logical Unit Not Ready, Cause Not Reportable" }, { 0x04, 0x01, "Logical Unit Is in Process Of Becoming Ready" }, { 0x04, 0x02, "Logical Unit Not Ready, Initialization Command Required" }, { 0x04, 0x03, "Logical Unit Not Ready, Manual Intervention Required" }, { 0x04, 0x04, "Logical Unit Not Ready, Format In Progress" }, { 0x04, 0x05, "Logical Unit Not Ready, Rebuild In Progress" }, { 0x04, 0x06, "Logical Unit Not Ready, Recalculation In Progress" }, { 0x04, 0x07, "Logical Unit Not Ready, Operation In Progress" }, { 0x04, 0x08, "Logical Unit Not Ready, Long Write In Progress" }, { 0x04, 0x09, "Logical Unit Not Ready, Self-Test In Progress" }, { 0x04, 0x0A, "Logical Unit Not Accessible, Asymmetric Access State " "Transition" }, { 0x04, 0x0B, "Logical Unit Not Accessible, Target Port In Standby State" }, { 0x04, 0x0C, "Logical Unit Not Accessible, Target Port In Unavailable State" }, { 0x04, 0x10, "Logical Unit Not Ready, Auxiliary Memory Not Accessible" }, { 0x04, 0x11, "Logical Unit Not Ready, Notify (Enable_Spinup) Required" }, { 0x05, 0x00, "Logical Unit Does Not Respond To Selection" }, { 0x06, 0x00, "No Reference Position Found" }, { 0x07, 0x00, "Multiple Peripheral Devices Selected" }, { 0x08, 0x00, "Logical Unit Communication Failure" }, { 0x08, 0x01, "Logical Unit Communication Timeout" }, { 0x08, 0x02, "Logical Unit Communication Parity Error" }, { 0x08, 0x03, "Logical Unit Communication CRC Error" }, { 0x08, 0x04, "Unreachable Copy Target" }, { 0x09, 0x00, "Track Following Error" }, { 0x09, 0x01, "Tracking Servo Failure" }, { 0x09, 0x02, "Focus Servo Failure" }, { 0x09, 0x03, "Spindle Servo Failure" }, { 0x09, 0x04, "Head Select Fault" }, { 0x0A, 0x00, "Error Log Overflow" }, { 0x0B, 0x00, "Warning" }, { 0x0B, 0x01, "Warning - Specified Temperature Exceeded" }, { 0x0B, 0x02, "Warning - Enclosure Degraded" }, { 0x0C, 0x00, "Write Error" }, { 0x0C, 0x01, "Write Error - Recovered with Auto Reallocation" }, { 0x0C, 0x02, "Write Error - Auto Reallocate Failed" }, { 0x0C, 0x03, "Write Error - Recommend Reassignment" }, { 0x0C, 0x04, "Compression Check Miscompare Error" }, { 0x0C, 0x05, "Data Expansion Occurred During Compression" }, { 0x0C, 0x06, "Block Not Compressible" }, { 0x0C, 0x07, "Write Error - Recovery Needed" }, { 0x0C, 0x08, "Write Error - Recovery Failed" }, { 0x0C, 0x09, "Write Error - Loss Of Streaming" }, { 0x0C, 0x0A, "Write Error - Padding Blocks Added" }, { 0x0C, 0x0B, "Auxiliary Memory Write Error" }, { 0x0C, 0x0C, "Write Error - Unexpected Unsolicited Data" }, { 0x0C, 0x0D, "Write Error - Not Enough Unsolicited Data" }, { 0x0D, 0x00, "Error Detected By Third Party Temporary Initiator" }, { 0x0D, 0x01, "Third Party Device Failure" }, { 0x0D, 0x02, "Copy Target Device Not Reachable" }, { 0x0D, 0x03, "Incorrect Copy Target Device Type" }, { 0x0D, 0x04, "Copy Target Device Data Underrun" }, { 0x0D, 0x05, "Copy Target Device Data Overrun" }, { 0x0E, 0x00, "Invalid Information Unit" }, { 0x0E, 0x01, "Information Unit Too Short" }, { 0x0E, 0x02, "Information Unit Too Long" }, { 0x10, 0x00, "ID CRC Or ECC Error" }, { 0x11, 0x00, "Unrecovered Read Error" }, { 0x11, 0x01, "Read Retries Exhausted" }, { 0x11, 0x02, "Error Too Long To Correct" }, { 0x11, 0x03, "Multiple Read Errors" }, { 0x11, 0x04, "Unrecovered Read Error - Auto Reallocate Failed" }, { 0x11, 0x05, "L-EC Uncorrectable Error" }, { 0x11, 0x06, "CIRC Unrecovered Error" }, { 0x11, 0x07, "Data Re-synchronization Error" }, { 0x11, 0x08, "Incomplete Block Read" }, { 0x11, 0x09, "No Gap Found" }, { 0x11, 0x0A, "Miscorrected Error" }, { 0x11, 0x0B, "Uncorrected Read Error - Recommend Reassignment" }, { 0x11, 0x0C, "Uncorrected Read Error - Recommend Rewrite the Data" }, { 0x11, 0x0D, "De-Compression CRC Error" }, { 0x11, 0x0E, "Cannot Decompress Using Declared Algorithm" }, { 0x11, 0x0F, "Error Reading UPC/EAN Number" }, { 0x11, 0x10, "Error Reading ISRC Number" }, { 0x11, 0x11, "Read Error - Loss Of Streaming" }, { 0x11, 0x12, "Auxiliary Memory Read Error" }, { 0x11, 0x13, "Read Error - Failed Retransmission Request" }, { 0x12, 0x00, "Address Mark Not Found for ID Field" }, { 0x13, 0x00, "Address Mark Not Found for Data Field" }, { 0x14, 0x00, "Recorded Entity Not Found" }, { 0x14, 0x01, "Record Not Found" }, { 0x14, 0x02, "Filemark or Setmark Not Found" }, { 0x14, 0x03, "End-Of-Data Not Found" }, { 0x14, 0x04, "Block Sequence Error" }, { 0x14, 0x05, "Record Not Found - Recommend Reassignment" }, { 0x14, 0x06, "Record Not Found - Data Auto-Reallocated" }, { 0x14, 0x07, "Locate Operation Failure" }, { 0x15, 0x00, "Random Positioning Error" }, { 0x15, 0x01, "Mechanical Positioning Error" }, { 0x15, 0x02, "Positioning Error Detected By Read of Medium" }, { 0x16, 0x00, "Data Synchronization Mark Error" }, { 0x16, 0x01, "Data Sync Error - Data Rewritten" }, { 0x16, 0x02, "Data Sync Error - Recommend Rewrite" }, { 0x16, 0x03, "Data Sync Error - Data Auto-Reallocated" }, { 0x16, 0x04, "Data Sync Error - Recommend Reassignment" }, { 0x17, 0x00, "Recovered Data With No Error Correction Applied" }, { 0x17, 0x01, "Recovered Data With Retries" }, { 0x17, 0x02, "Recovered Data With Positive Head Offset" }, { 0x17, 0x03, "Recovered Data With Negative Head Offset" }, { 0x17, 0x04, "Recovered Data With Retries and/or CIRC Applied" }, { 0x17, 0x05, "Recovered Data Using Previous Sector ID" }, { 0x17, 0x06, "Recovered Data Without ECC - Data Auto-Reallocated" }, { 0x17, 0x07, "Recovered Data Without ECC - Recommend Reassignment" }, { 0x17, 0x08, "Recovered Data Without ECC - Recommend Rewrite" }, { 0x17, 0x09, "Recovered Data Without ECC - Data Rewritten" }, { 0x18, 0x00, "Recovered Data With Error Correction Applied" }, { 0x18, 0x01, "Recovered Data With Error Correction & Retries Applied" }, { 0x18, 0x02, "Recovered Data - Data Auto-Reallocated" }, { 0x18, 0x03, "Recovered Data With CIRC" }, { 0x18, 0x04, "Recovered Data With LEC" }, { 0x18, 0x05, "Recovered Data - Recommend Reassignment" }, { 0x18, 0x06, "Recovered Data - Recommend Rewrite" }, { 0x18, 0x07, "Recovered Data With ECC - Data Rewritten" }, { 0x18, 0x08, "Recovered Data With Linking" }, { 0x19, 0x00, "Defect List Error" }, { 0x19, 0x01, "Defect List Not Available" }, { 0x19, 0x02, "Defect List Error in Primary List" }, { 0x19, 0x03, "Defect List Error in Grown List" }, { 0x1A, 0x00, "Parameter List Length Error" }, { 0x1B, 0x00, "Synchronous Data Transfer Error" }, { 0x1C, 0x00, "Defect List Not Found" }, { 0x1C, 0x01, "Primary Defect List Not Found" }, { 0x1C, 0x02, "Grown Defect List Not Found" }, { 0x1D, 0x00, "Miscompare During Verify Operation" }, { 0x1E, 0x00, "Recovered ID with ECC Correction" }, { 0x1F, 0x00, "Partial Defect List Transfer" }, { 0x20, 0x00, "Invalid Command Operation Code" }, { 0x20, 0x01, "Access Denied - Initiator Pending-Enrolled" }, { 0x20, 0x02, "Access Denied - No Access Rights" }, { 0x20, 0x03, "Access Denied - Invalid Mgmt ID Key" }, { 0x20, 0x04, "Illegal Command While In Write Capable State" }, { 0x20, 0x06, "Illegal Command While In Explicit Address Mode" }, { 0x20, 0x07, "Illegal Command While In Implicit Address Mode" }, { 0x20, 0x08, "Access Denied - Enrollment Conflict" }, { 0x20, 0x09, "Access Denied - Invalid LU Identifer" }, { 0x20, 0x0A, "Access Denied - Invalid Proxy Token" }, { 0x20, 0x0B, "Access Denied - ACL LUN Conflict" }, { 0x21, 0x00, "Logical Block Address Out of Range" }, { 0x21, 0x01, "Invalid Element Address" }, { 0x21, 0x02, "Invalid Address For Write" }, { 0x22, 0x00, "Illegal Function (Use 20 00, 24 00, or 26 00)" }, { 0x24, 0x00, "Illegal Field in CDB" }, { 0x24, 0x01, "CDB Decryption Error" }, { 0x24, 0x04, "Security Audit Value Frozen" }, { 0x24, 0x05, "Security Working Key Frozen" }, { 0x24, 0x06, "Nonce Not Unique" }, { 0x24, 0x07, "Nonce Timestamp Out Of Range" }, { 0x25, 0x00, "Logical Unit Not Supported" }, { 0x26, 0x00, "Invalid Field In Parameter List" }, { 0x26, 0x01, "Parameter Not Supported" }, { 0x26, 0x02, "Parameter Value Invalid" }, { 0x26, 0x03, "Threshold Parameters Not Supported" }, { 0x26, 0x04, "Invalid Release Of Persistent Reservation" }, { 0x26, 0x05, "Data Decryption Error" }, { 0x26, 0x06, "Too Many Target Descriptors" }, { 0x26, 0x07, "Unsupported Target Descriptor Type Code" }, { 0x26, 0x08, "Too Many Segment Descriptors" }, { 0x26, 0x09, "Unsupported Segment Descriptor Type Code" }, { 0x26, 0x0A, "Unexpected Inexact Segment" }, { 0x26, 0x0B, "Inline Data Length Exceeded" }, { 0x26, 0x0C, "Invalid Operation For Copy Source Or Destination" }, { 0x26, 0x0D, "Copy Segment Granularity Violation" }, { 0x26, 0x0E, "Invalid Parameter While Port Is Enabled" }, { 0x27, 0x00, "Write Protected" }, { 0x27, 0x01, "Hardware Write Protected" }, { 0x27, 0x02, "Logical Unit Software Write Protected" }, { 0x27, 0x03, "Associated Write Protect" }, { 0x27, 0x04, "Persistent Write Protect" }, { 0x27, 0x05, "Permanent Write Protect" }, { 0x27, 0x06, "Conditional Write Protect" }, { 0x28, 0x00, "Not Ready To Ready Transition (Medium May Have Changed)" }, { 0x28, 0x01, "Import Or Export Element Accessed" }, { 0x29, 0x00, "Power On, Reset, or Bus Device Reset Occurred" }, { 0x29, 0x01, "Power On Occurred" }, { 0x29, 0x02, "SCSI Bus Reset Occurred" }, { 0x29, 0x03, "Bus Device Reset Function Occurred" }, { 0x29, 0x04, "Device Internal Reset" }, { 0x29, 0x05, "Transceiver Mode Changed To Single-Ended" }, { 0x29, 0x06, "Transceiver Mode Changed To LVD" }, { 0x29, 0x07, "I_T Nexus Loss Occurred" }, { 0x2A, 0x00, "Parameters Changed" }, { 0x2A, 0x01, "Mode Parameters Changed" }, { 0x2A, 0x02, "Log Parameters Changed" }, { 0x2A, 0x03, "Reservations Preempted" }, { 0x2A, 0x04, "Reservations Released" }, { 0x2A, 0x05, "Registrations Preempted" }, { 0x2A, 0x06, "Asymmetric Access State Changed" }, { 0x2A, 0x07, "Implicit Asymmetric Access State Transition Failed" }, { 0x2B, 0x00, "Copy Cannot Execute Since Host Cannot Disconnect" }, { 0x2C, 0x00, "Command Sequence Error" }, { 0x2C, 0x01, "Too Many Windows Specified" }, { 0x2C, 0x02, "Invalid Combination of Windows Specified" }, { 0x2C, 0x03, "Current Program Area Is Not Empty" }, { 0x2C, 0x04, "Current Program Area Is Empty" }, { 0x2C, 0x05, "Illegal Power Condition Request" }, { 0x2C, 0x06, "Persistent Prevent Conflict" }, { 0x2C, 0x07, "Previous Busy Status" }, { 0x2C, 0x08, "Previous Task Set Full Status" }, { 0x2C, 0x09, "Previous Reservation Conflict Status" }, { 0x2C, 0x0A, "Partition or Collection Contains User Objects" }, { 0x2D, 0x00, "Overwrite Error On Update In Place" }, { 0x2E, 0x00, "Insufficient Time For Operation" }, { 0x2F, 0x00, "Commands Cleared By Another Initiator" }, { 0x30, 0x00, "Incompatible Medium Installed" }, { 0x30, 0x01, "Cannot Read Medium - Unknown Format" }, { 0x30, 0x02, "Cannot Read Medium - Incompatible Format" }, { 0x30, 0x03, "Cleaning Cartridge Installed" }, { 0x30, 0x04, "Cannot Write Medium - Unknown Format" }, { 0x30, 0x05, "Cannot Write Medium - Incompatible Format" }, { 0x30, 0x06, "Cannot Format Medium - Incompatible Medium" }, { 0x30, 0x07, "Cleaning Failure" }, { 0x30, 0x08, "Cannot Write - Application Code Mismatch" }, { 0x30, 0x09, "Current Session Not Fixated For Append" }, { 0x30, 0x0A, "Cleaning Request Rejected" }, { 0x30, 0x10, "Medium Not Formatted" }, { 0x31, 0x00, "Medium Format Corrupted" }, { 0x31, 0x01, "Format Command Failed" }, { 0x31, 0x02, "Zoned Formatting Failed Due To Spare Linking" }, { 0x32, 0x00, "No Defect Spare Location Available" }, { 0x32, 0x01, "Defect List Update Failure" }, { 0x33, 0x00, "Tape Length Error" }, { 0x34, 0x00, "Enclosure Failure" }, { 0x35, 0x00, "Enclosure Services Failure" }, { 0x35, 0x01, "Unsupported Enclosure Function" }, { 0x35, 0x02, "Enclosure Services Unavailable" }, { 0x35, 0x03, "Enclosure Services Transfer Failed" }, { 0x35, 0x04, "Enclosure Services Transfer Refused" }, { 0x36, 0x00, "Ribbon, Ink, or Toner Failure" }, { 0x37, 0x00, "Rounded Parameter" }, { 0x39, 0x00, "Saving Parameters Not Supported" }, { 0x3A, 0x00, "Medium Not Present" }, { 0x3A, 0x01, "Medium Not Present - Tray Closed" }, { 0x3A, 0x02, "Medium Not Present - Tray Open" }, { 0x3A, 0x03, "Medium Not Present - Loadable" }, { 0x3A, 0x04, "Medium Not Present - Medium Auxiliary Memory Accessible" }, { 0x3B, 0x00, "Sequential Positioning Error" }, { 0x3B, 0x01, "Tape Position Error At Beginning-of-Medium" }, { 0x3B, 0x02, "Tape Position Error At End-of-Medium" }, { 0x3B, 0x03, "Tape or Electronic Vertical Forms Unit Not Ready" }, { 0x3B, 0x04, "Slew Failure" }, { 0x3B, 0x05, "Paper Jam" }, { 0x3B, 0x06, "Failed To Sense Top-Of-Form" }, { 0x3B, 0x07, "Failed To Sense Bottom-Of-Form" }, { 0x3B, 0x08, "Reposition Error" }, { 0x3B, 0x09, "Read Past End Of Medium" }, { 0x3B, 0x0A, "Read Past Beginning Of Medium" }, { 0x3B, 0x0B, "Position Past End Of Medium" }, { 0x3B, 0x0C, "Position Past Beginning Of Medium" }, { 0x3B, 0x0D, "Medium Destination Element Full" }, { 0x3B, 0x0E, "Medium Source Element Empty" }, { 0x3B, 0x0F, "End Of Medium Reached" }, { 0x3B, 0x11, "Medium Magazine Not Accessible" }, { 0x3B, 0x12, "Medium Magazine Removed" }, { 0x3B, 0x13, "Medium Magazine Inserted" }, { 0x3B, 0x14, "Medium Magazine Locked" }, { 0x3B, 0x15, "Medium Magazine Unlocked" }, { 0x3B, 0x16, "Mechanical Positioning Or Changer Error" }, { 0x3D, 0x00, "Invalid Bits In IDENTIFY Message" }, { 0x3E, 0x00, "Logical Unit Has Not Self-Configured Yet" }, { 0x3E, 0x01, "Logical Unit Failure" }, { 0x3E, 0x02, "Timeout On Logical Unit" }, { 0x3E, 0x03, "Logical Unit Failed Self-Test" }, { 0x3E, 0x04, "Logical Unit Unable To Update Self-Test Log" }, { 0x3F, 0x00, "Target Operating Conditions Have Changed" }, { 0x3F, 0x01, "Microcode Has Been Changed" }, { 0x3F, 0x02, "Changed Operating Definition" }, { 0x3F, 0x03, "INQUIRY Data Has Changed" }, { 0x3F, 0x04, "Component Device Attached" }, { 0x3F, 0x05, "Device Identifier Changed" }, { 0x3F, 0x06, "Redundancy Group Created Or Modified" }, { 0x3F, 0x07, "Redundancy Group Deleted" }, { 0x3F, 0x08, "Spare Created Or Modified" }, { 0x3F, 0x09, "Spare Deleted" }, { 0x3F, 0x0A, "Volume Set Created Or Modified" }, { 0x3F, 0x0B, "Volume Set Deleted" }, { 0x3F, 0x0C, "Volume Set Deassigned" }, { 0x3F, 0x0D, "Volume Set Reassigned" }, { 0x3F, 0x0E, "Reported LUNs Data Has Changed" }, { 0x3F, 0x0F, "Echo Buffer Overwritten" }, { 0x3F, 0x10, "Medium Loadable" }, { 0x3F, 0x11, "Medium Auxiliary Memory Accessible" }, { 0x40, 0x00, "RAM FAILURE (Should Use 40 NN)" }, { 0x41, 0x00, "Data Path FAILURE (Should Use 40 NN)" }, { 0x42, 0x00, "Power-On or Self-Test FAILURE (Should Use 40 NN)" }, { 0x43, 0x00, "Message Error" }, { 0x44, 0x00, "Internal Target Failure" }, { 0x45, 0x00, "Select Or Reselect Failure" }, { 0x46, 0x00, "Unsuccessful Soft Reset" }, { 0x47, 0x00, "SCSI Parity Error" }, { 0x47, 0x01, "Data Phase CRC Error Detected" }, { 0x47, 0x02, "SCSI Parity Error Detected During ST Data Phase" }, { 0x47, 0x03, "Information Unit iuCRC Error Detected" }, { 0x47, 0x04, "Asynchronous Information Protection Error Detected" }, { 0x47, 0x05, "Protocol Service CRC Error" }, { 0x47, 0x7F, "Some Commands Cleared by iSCSI Protocol Event" }, { 0x48, 0x00, "INITIATOR DETECTED ERROR Message Received" }, { 0x49, 0x00, "Invalid Message Error" }, { 0x4A, 0x00, "Command Phase Error" }, { 0x4B, 0x00, "Data Phase Error" }, { 0x4B, 0x01, "Illegal Target Port Transfer Tag Received" }, { 0x4B, 0x02, "Too Much Write Data" }, { 0x4B, 0x03, "ACK/NAK Timeout" }, { 0x4B, 0x04, "NAK Reveived" }, { 0x4B, 0x05, "Data Offset Error" }, { 0x4B, 0x06, "Initiator Response Timeout" }, { 0x4C, 0x00, "Logical Unit Failed Self-Configuration" }, { 0x4D, 0x00, "Tagged Overlapped Commands (NN = Queue Tag)" }, { 0x4E, 0x00, "Overlapped Commands Attempted" }, { 0x50, 0x00, "Write Append Error" }, { 0x50, 0x01, "Write Append Position Error" }, { 0x50, 0x02, "Position Error Related To Timing" }, { 0x51, 0x00, "Erase Failure" }, { 0x51, 0x01, "Erase Failure - Incomplete Erase Operation Detected" }, { 0x52, 0x00, "Cartridge Fault" }, { 0x53, 0x00, "Media Load or Eject Failed" }, { 0x53, 0x01, "Unload Tape Failure" }, { 0x53, 0x02, "Medium Removal Prevented" }, { 0x54, 0x00, "SCSI To Host System Interface Failure" }, { 0x55, 0x00, "System Resource Failure" }, { 0x55, 0x01, "System Buffer Full" }, { 0x55, 0x02, "Insufficient Reservation Resources" }, { 0x55, 0x03, "Insufficient Resources" }, { 0x55, 0x04, "Insufficient Registration Resources" }, { 0x55, 0x05, "Insufficient Access Control Resources" }, { 0x55, 0x06, "Auxiliary Memory Out Of Space" }, { 0x57, 0x00, "Unable To Recover Table-Of-Contents" }, { 0x58, 0x00, "Generation Does Not Exist" }, { 0x59, 0x00, "Updated Block Read" }, { 0x5A, 0x00, "Operator Request or State Change Input (Unspecified)" }, { 0x5A, 0x01, "Operator Medium Removal Requested" }, { 0x5A, 0x02, "Operator Selected Write Protect" }, { 0x5A, 0x03, "Operator Selected Write Permit" }, { 0x5B, 0x00, "Log Exception" }, { 0x5B, 0x01, "Threshold Condition Met" }, { 0x5B, 0x02, "Log Counter At Maximum" }, { 0x5B, 0x03, "Log List Codes Exhausted" }, { 0x5C, 0x00, "RPL Status Change" }, { 0x5C, 0x01, "Spindles Synchronized" }, { 0x5C, 0x02, "Spindles Not Synchronized" }, { 0x5D, 0x00, "Failure Prediction Threshold Exceeded" }, { 0x5D, 0x01, "Media Failure Prediction Threshold Exceeded" }, { 0x5D, 0x02, "Logical Unit Failure Prediction Threshold Exceeded" }, { 0x5D, 0x03, "Spare Area Exhaustion Prediction Threshold Exceeded" }, { 0x5D, 0x10, "Hardware Impending Failure General Hard Drive Failure" }, { 0x5D, 0x11, "Hardware Impending Failure Drive Error Rate Too High" }, { 0x5D, 0x12, "Hardware Impending Failure Data Error Rate Too High" }, { 0x5D, 0x13, "Hardware Impending Failure Seek Error Rate Too High" }, { 0x5D, 0x14, "Hardware Impending Failure Too Many Block Reassigns" }, { 0x5D, 0x15, "Hardware Impending Failure Access Times Too High" }, { 0x5D, 0x16, "Hardware Impending Failure Start Unit Times Too High" }, { 0x5D, 0x17, "Hardware Impending Failure Channel Parametrics" }, { 0x5D, 0x18, "Hardware Impending Failure Controller Detected" }, { 0x5D, 0x19, "Hardware Impending Failure Throughput Performance" }, { 0x5D, 0x1A, "Hardware Impending Failure Seek Time Performance" }, { 0x5D, 0x1B, "Hardware Impending Failure Spin-Up Retry Count" }, { 0x5D, 0x1C, "Hardware Impending Failure Drive Calibration Retry Count" }, { 0x5D, 0x20, "Controller Impending Failure General Hard Drive Failure" }, { 0x5D, 0x21, "Controller Impending Failure Drive Error Rate Too High" }, { 0x5D, 0x22, "Controller Impending Failure Data Error Rate Too High" }, { 0x5D, 0x23, "Controller Impending Failure Seek Error Rate Too High" }, { 0x5D, 0x24, "Controller Impending Failure Too Many Block Reassigns" }, { 0x5D, 0x25, "Controller Impending Failure Access Times Too High" }, { 0x5D, 0x26, "Controller Impending Failure Start Unit Times Too High" }, { 0x5D, 0x27, "Controller Impending Failure Channel Parametrics" }, { 0x5D, 0x28, "Controller Impending Failure Controller Detected" }, { 0x5D, 0x29, "Controller Impending Failure Throughput Performance" }, { 0x5D, 0x2A, "Controller Impending Failure Seek Time Performance" }, { 0x5D, 0x2B, "Controller Impending Failure Spin-Up Retry Count" }, { 0x5D, 0x2C, "Controller Impending Failure Drive Calibration Retry Count" }, { 0x5D, 0x30, "Data Channel Impending Failure General Hard Drive Failure" }, { 0x5D, 0x31, "Data Channel Impending Failure Drive Error Rate Too High" }, { 0x5D, 0x32, "Data Channel Impending Failure Data Error Rate Too High" }, { 0x5D, 0x33, "Data Channel Impending Failure Seek Error Rate Too High" }, { 0x5D, 0x34, "Data Channel Impending Failure Too Many Block Reassigns" }, { 0x5D, 0x35, "Data Channel Impending Failure Access Times Too High" }, { 0x5D, 0x36, "Data Channel Impending Failure Start Unit Times Too High" }, { 0x5D, 0x37, "Data Channel Impending Failure Channel Parametrics" }, { 0x5D, 0x38, "Data Channel Impending Failure Controller Detected" }, { 0x5D, 0x39, "Data Channel Impending Failure Throughput Performance" }, { 0x5D, 0x3A, "Data Channel Impending Failure Seek Time Performance" }, { 0x5D, 0x3B, "Data Channel Impending Failure Spin-Up Retry Count" }, { 0x5D, 0x3C, "Data Channel Impending Failure Drive Calibration Retry Count" }, { 0x5D, 0x40, "Servo Impending Failure General Hard Drive Failure" }, { 0x5D, 0x41, "Servo Impending Failure Drive Error Rate Too High" }, { 0x5D, 0x42, "Servo Impending Failure Data Error Rate Too High" }, { 0x5D, 0x43, "Servo Impending Failure Seek Error Rate Too High" }, { 0x5D, 0x44, "Servo Impending Failure Too Many Block Reassigns" }, { 0x5D, 0x45, "Servo Impending Failure Access Times Too High" }, { 0x5D, 0x46, "Servo Impending Failure Start Unit Times Too High" }, { 0x5D, 0x47, "Servo Impending Failure Channel Parametrics" }, { 0x5D, 0x48, "Servo Impending Failure Controller Detected" }, { 0x5D, 0x49, "Servo Impending Failure Throughput Performance" }, { 0x5D, 0x4A, "Servo Impending Failure Seek Time Performance" }, { 0x5D, 0x4B, "Servo Impending Failure Spin-Up Retry Count" }, { 0x5D, 0x4C, "Servo Impending Failure Drive Calibration Retry Count" }, { 0x5D, 0x50, "Spindle Impending Failure General Hard Drive Failure" }, { 0x5D, 0x51, "Spindle Impending Failure Drive Error Rate Too High" }, { 0x5D, 0x52, "Spindle Impending Failure Data Error Rate Too High" }, { 0x5D, 0x53, "Spindle Impending Failure Seek Error Rate Too High" }, { 0x5D, 0x54, "Spindle Impending Failure Too Many Block Reassigns" }, { 0x5D, 0x55, "Spindle Impending Failure Access Times Too High" }, { 0x5D, 0x56, "Spindle Impending Failure Start Unit Times Too High" }, { 0x5D, 0x57, "Spindle Impending Failure Channel Parametrics" }, { 0x5D, 0x58, "Spindle Impending Failure Controller Detected" }, { 0x5D, 0x59, "Spindle Impending Failure Throughput Performance" }, { 0x5D, 0x5A, "Spindle Impending Failure Seek Time Performance" }, { 0x5D, 0x5B, "Spindle Impending Failure Spin-Up Retry Count" }, { 0x5D, 0x5C, "Spindle Impending Failure Drive Calibration Retry Count" }, { 0x5D, 0x60, "Firmware Impending Failure General Hard Drive Failure" }, { 0x5D, 0x61, "Firmware Impending Failure Drive Error Rate Too High" }, { 0x5D, 0x62, "Firmware Impending Failure Data Error Rate Too High" }, { 0x5D, 0x63, "Firmware Impending Failure Seek Error Rate Too High" }, { 0x5D, 0x64, "Firmware Impending Failure Too Many Block Reassigns" }, { 0x5D, 0x65, "Firmware Impending Failure Access Times Too High" }, { 0x5D, 0x66, "Firmware Impending Failure Start Unit Times Too High" }, { 0x5D, 0x67, "Firmware Impending Failure Channel Parametrics" }, { 0x5D, 0x68, "Firmware Impending Failure Controller Detected" }, { 0x5D, 0x69, "Firmware Impending Failure Throughput Performance" }, { 0x5D, 0x6A, "Firmware Impending Failure Seek Time Performance" }, { 0x5D, 0x6B, "Firmware Impending Failure Spin-Up Retry Count" }, { 0x5D, 0x6C, "Firmware Impending Failure Drive Calibration Retry Count" }, { 0x5D, 0xFF, "Failure Prediction Threshold Exceeded (False)" }, { 0x5E, 0x00, "Low Power Condition On" }, { 0x5E, 0x01, "Idle Condition Activated By Timer" }, { 0x5E, 0x02, "Standby Condition Activated By Timer" }, { 0x5E, 0x03, "Idle Condition Activated By Command" }, { 0x5E, 0x04, "Standby Condition Activated By Command" }, { 0x5E, 0x41, "Power State Change To Active" }, { 0x5E, 0x42, "Power State Change To Idle" }, { 0x5E, 0x43, "Power State Change To Standby" }, { 0x5E, 0x45, "Power State Change To Sleep" }, { 0x5E, 0x47, "Power State Change To Device Control" }, { 0x60, 0x00, "Lamp Failure" }, { 0x61, 0x00, "Video Acquisition Error" }, { 0x61, 0x01, "Unable To Acquire Video" }, { 0x61, 0x02, "Out Of Focus" }, { 0x62, 0x00, "Scan Head Positioning Error" }, { 0x63, 0x00, "End Of User Area Encountered On This Track" }, { 0x63, 0x01, "Packet Does Not Fit In Available Space" }, { 0x64, 0x00, "Illegal Mode For This Track" }, { 0x64, 0x01, "Invalid Packet Size" }, { 0x65, 0x00, "Voltage Fault" }, { 0x66, 0x00, "Automatic Document Feeder Cover Up" }, { 0x66, 0x01, "Automatic Document Feeder Lift Up" }, { 0x66, 0x02, "Document Jam In Automatic Document Feeder" }, { 0x66, 0x03, "Document Misfeed In Automatic Document Feeder" }, { 0x67, 0x00, "Configuration Failure" }, { 0x67, 0x01, "Configuration Of Incapable Logical Units Failed" }, { 0x67, 0x02, "Add Logical Unit Failed" }, { 0x67, 0x03, "Modification Of Logical Unit Failed" }, { 0x67, 0x04, "Exchange Of Logical Unit Failed" }, { 0x67, 0x05, "Remove Of Logical Unit Failed" }, { 0x67, 0x06, "Attachment Of Logical Unit Failed" }, { 0x67, 0x07, "Creation of Logical Unit Failed" }, { 0x67, 0x08, "Assign Failure Occurred" }, { 0x67, 0x09, "Multiply Assigned Logical Unit" }, { 0x67, 0x0A, "Set Target Port Groups Command Failed" }, { 0x68, 0x00, "Logical Unit Not Configured" }, { 0x69, 0x00, "Data Loss On Logical Unit" }, { 0x69, 0x01, "Multiple Logical Unit Failures" }, { 0x69, 0x02, "Parity/Data Mismatch" }, { 0x6A, 0x00, "Informational, Refer To Log" }, { 0x6B, 0x00, "State Change Has Occurred" }, { 0x6B, 0x01, "Redundancy Level Got Better" }, { 0x6B, 0x02, "Redundancy Level Got Worse" }, { 0x6C, 0x00, "Rebuild Failure Occurred" }, { 0x6D, 0x00, "Recalculate Failure Occurred" }, { 0x6E, 0x00, "Command To Logical Unit Failed" }, { 0x6F, 0x00, "Copy Protection Key Exchange Failure - Authentication Failure" }, { 0x6F, 0x01, "Copy Protection Key Exchange Failure - Key Not Present" }, { 0x6F, 0x02, "Copy Protection Key Exchange Failure - Key Not Established" }, { 0x6F, 0x03, "Read Of Scrambled Sector Without Authentication" }, { 0x6F, 0x04, "Media Region Code Is Mismatched To Logical Unit Region" }, { 0x6F, 0x05, "Drive Region Must Be Permanent/Region Reset Count Error" }, { 0x70, 0x00, "Decompression Exception Short Algorithm ID Of NN" }, { 0x71, 0x00, "Decompression Exception Long Algorithm ID" }, { 0x72, 0x00, "Session Fixation Error" }, { 0x72, 0x01, "Session Fixation Error Writing Lead-In" }, { 0x72, 0x02, "Session Fixation Error Writing Lead-Out" }, { 0x72, 0x03, "Session Fixation Error - Incomplete Track In Session" }, { 0x72, 0x04, "Empty Or Partially Written Reserved Track" }, { 0x72, 0x05, "No More Track Reservations Allowed" }, { 0x73, 0x00, "CD Control Error" }, { 0x73, 0x01, "Power Calibration Area Almost Full" }, { 0x73, 0x02, "Power Calibration Area Is Full" }, { 0x73, 0x03, "Power Calibration Area Error" }, { 0x73, 0x04, "Program Memory Area Update Failure" }, { 0x73, 0x05, "Program Memory Area Is Full" }, { 0x73, 0x06, "RMA/PMA Is Almost Full" }, { 0x00, 0x00, NULL } }; /* needs to move to a compat.c one day */ #ifdef NO_STRLCPY size_t strlcpy(char *dst, const char *src, size_t size) { snprintf(dst, size, "%s", src); return strlen(src); } #endif static void asc2ascii(u_char asc, u_char ascq, char *result, size_t l) { int i = 0; while (adesc[i].description != NULL) { if (adesc[i].asc == asc && adesc[i].ascq == ascq) break; i++; } if (adesc[i].description == NULL) { if (asc == 0x40 && ascq != 0) (void) snprintf(result, l, "Diagnostic Failure on Component 0x%02x", ascq & 0xff); else (void) snprintf(result, l, "ASC 0x%02x ASCQ 0x%02x", asc & 0xff, ascq & 0xff); } else { (void) strlcpy(result, adesc[i].description, l); } } void uscsi_print_sense_data(uint8_t *s, int slen, int verbosity) { int32_t info; int i, j, k; char *sbs; /* * Basics - print out SENSE KEY */ printf(" SENSE KEY: %s", uscsi_decode_sense(s, 0)); /* * Print out, unqualified but aligned, FMK, EOM and ILI status. */ if (s[2] & 0xe0) { char pad; printf("\n "); pad = ' '; if (s[2] & SSD_FILEMARK) { printf("%c Filemark Detected", pad); pad = ','; } if (s[2] & SSD_EOM) { printf("%c EOM Detected", pad); pad = ','; } if (s[2] & SSD_ILI) printf("%c Incorrect Length Indicator Set", pad); } /* * Now we should figure out, based upon device type, how * to format the information field. Unfortunately, that's * not convenient here, so we'll print it as a signed * 32 bit integer. */ info = (s[3] << 24) | (s[4] << 16) | (s[5] << 8) | s[6]; if (info) printf("\n INFO FIELD: %d", info); /* * Now we check additional length to see whether there is * more information to extract. */ /* enough for command specific information? */ if (((unsigned int) s[7]) < 4) { printf("\n"); return; } info = (s[8] << 24) | (s[9] << 16) | (s[10] << 8) | s[11]; if (info) printf("\n COMMAND INFO: %d (0x%x)", info, info); /* * Decode ASC && ASCQ info, plus FRU, plus the rest... */ sbs = uscsi_decode_sense(s, 1); if (sbs) printf("\n ASC/ASCQ: %s", sbs); if (s[14] != 0) printf("\n FRU CODE: 0x%x", s[14] & 0xff); sbs = uscsi_decode_sense(s, 3); if (sbs) printf("\n SKSV: %s", sbs); printf("\n"); if (verbosity == 0) { printf("\n"); return; } /* * Now figure whether we should print any additional informtion. * * Where should we start from? If we had SKSV data, * start from offset 18, else from offset 15. * * From that point until the end of the buffer, check for any * nonzero data. If we have some, go back and print the lot, * otherwise we're done. */ if (sbs) i = 18; else i = 15; for (j = i; j < slen; j++) if (s[j]) break; if (j == slen) return; printf("\n Additional Sense Information (byte %d out...):\n", i); if (i == 15) { printf("\n\t%2d:", i); k = 7; } else { printf("\n\t%2d:", i); k = 2; j -= 2; } while (j > 0) { if (i >= slen) break; if (k == 8) { k = 0; printf("\n\t%2d:", i); } printf(" 0x%02x", s[i] & 0xff); k++; j--; i++; } printf("\n\n"); } char * uscsi_decode_sense(void *sinfo, int flag) { unsigned char *snsbuf; unsigned char skey; static char rqsbuf[132]; skey = 0; snsbuf = (unsigned char *) sinfo; if (flag == 0 || flag == 2 || flag == 3) skey = snsbuf[2] & 0xf; if (flag == 0) { /* Sense Key Only */ (void) strlcpy(rqsbuf, sense_keys[skey], sizeof(rqsbuf)); return (rqsbuf); } else if (flag == 1) { /* ASC/ASCQ Only */ asc2ascii(snsbuf[12], snsbuf[13], rqsbuf, sizeof(rqsbuf)); return (rqsbuf); } else if (flag == 2) { /* Sense Key && ASC/ASCQ */ auto char localbuf[64]; asc2ascii(snsbuf[12], snsbuf[13], localbuf, sizeof(localbuf)); (void) snprintf(rqsbuf, sizeof(rqsbuf), "%s, %s", sense_keys[skey], localbuf); return (rqsbuf); } else if (flag == 3 && snsbuf[7] >= 9 && (snsbuf[15] & 0x80)) { /* * SKSV Data */ switch (skey) { case SKEY_ILLEGAL_REQUEST: if (snsbuf[15] & 0x8) (void)snprintf(rqsbuf, sizeof(rqsbuf), "Error in %s, Offset %d, bit %d", (snsbuf[15] & 0x40)? "CDB" : "Parameters", (snsbuf[16] & 0xff) << 8 | (snsbuf[17] & 0xff), snsbuf[15] & 0x7); else (void)snprintf(rqsbuf, sizeof(rqsbuf), "Error in %s, Offset %d", (snsbuf[15] & 0x40)? "CDB" : "Parameters", (snsbuf[16] & 0xff) << 8 | (snsbuf[17] & 0xff)); return (rqsbuf); case SKEY_RECOVERED_ERROR: case SKEY_MEDIUM_ERROR: case SKEY_HARDWARE_ERROR: (void)snprintf(rqsbuf, sizeof(rqsbuf), "Actual Retry Count: %d", (snsbuf[16] & 0xff) << 8 | (snsbuf[17] & 0xff)); return (rqsbuf); case SKEY_NOT_READY: (void)snprintf(rqsbuf, sizeof(rqsbuf), "Progress Indicator: %d", (snsbuf[16] & 0xff) << 8 | (snsbuf[17] & 0xff)); return (rqsbuf); default: break; } } return (NULL); } void uscsi_print_sense(const char *name, u_char *req_cmd, int req_cmdlen, u_char *req_sense, int req_senselen_used, int verbosity) { int i; printf("%s: Check Condition on CDB:", name); for (i = 0; i < req_cmdlen; i++) printf(" %02x", req_cmd[i]); printf("\n"); uscsi_print_sense_data(req_sense, req_senselen_used, verbosity); } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/uscsi_subr.c000066400000000000000000000354571523477242100265040ustar00rootroot00000000000000/* $NetBSD: uscsi_subr.c,v 1.7 2002/10/08 20:17:06 soren Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum; Jason R. Thorpe of the Numerical Aerospace * Simulation Facility, NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Small changes, generalisations and Linux support by Reinoud Zandijk * . * */ /* * SCSI support subroutines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "uscsilib.h" int uscsilib_verbose = 0; #ifdef USCSI_SCSIPI /* * scsipi is a integrated SCSI and ATAPI layer under NetBSD and exists * in a modified form under OpenBSD and possibly also under other * operating systems. */ #include #ifdef __OpenBSD__ #include #else #include #endif int uscsi_open(struct uscsi_dev *disc) { struct stat stat; disc->fhandle = open(disc->dev_name, O_RDWR, 0); /* no create */ if (disc->fhandle<0) { perror("Failure to open device or file"); return ENODEV; } if (fstat(disc->fhandle, &stat) < 0) { perror("Can't stat device or file"); uscsi_close(disc); return ENODEV; } return 0; } int uscsi_close(struct uscsi_dev * disc) { close(disc->fhandle); disc->fhandle = -1; return 0; } int uscsi_command(int flags, struct uscsi_dev *disc, void *cmd, size_t cmdlen, void *data, size_t datalen, uint32_t timeout, struct uscsi_sense *uscsi_sense) { scsireq_t req; memset(&req, 0, sizeof(req)); if (uscsi_sense) bzero(uscsi_sense, sizeof(struct uscsi_sense)); memcpy(req.cmd, cmd, cmdlen); req.cmdlen = cmdlen; req.databuf = data; req.datalen = datalen; req.timeout = timeout; req.flags = flags; req.senselen = SENSEBUFLEN; if (ioctl(disc->fhandle, SCIOCCOMMAND, &req) == -1) return EIO; if (req.retsts == SCCMD_OK) return 0; /* Some problem; report it and exit. */ if (req.retsts == SCCMD_TIMEOUT) { if (uscsilib_verbose) fprintf(stderr, "%s: SCSI command timed out\n", disc->dev_name); return EAGAIN; } else if (req.retsts == SCCMD_BUSY) { if (uscsilib_verbose) fprintf(stderr, "%s: device is busy\n", disc->dev_name); return EBUSY; } else if (req.retsts == SCCMD_SENSE) { if (uscsi_sense) { uscsi_sense->asc = req.sense[12]; uscsi_sense->ascq = req.sense[13]; uscsi_sense->skey_valid = req.sense[15] & 128; uscsi_sense->sense_key = (req.sense[16] << 8) | (req.sense[17]); } if (uscsilib_verbose) uscsi_print_sense((char *) disc->dev_name, req.cmd, req.cmdlen, req.sense, req.senselen_used, 1); return EIO; } else if (uscsilib_verbose) fprintf(stderr, "%s: device had unknown status %x\n", disc->dev_name, req.retsts); return EFAULT; } /* * The reasoning behind this explicit copy is for compatibility with changes * in our uscsi_addr structure. */ int uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr) { struct scsi_addr raddr; int error; bzero(saddr, sizeof(struct scsi_addr)); error = ioctl(disc->fhandle, SCIOCIDENTIFY, &raddr); if (error) { saddr->type = USCSI_TYPE_UNKNOWN; return error; } #ifdef __NetBSD__ /* scsi and atapi are split up like in uscsi_addr */ if (raddr.type == 0) { saddr->type = USCSI_TYPE_SCSI; saddr->addr.scsi.scbus = raddr.addr.scsi.scbus; saddr->addr.scsi.target = raddr.addr.scsi.target; saddr->addr.scsi.lun = raddr.addr.scsi.lun; } else { saddr->type = USCSI_TYPE_ATAPI; saddr->addr.atapi.atbus = raddr.addr.atapi.atbus; saddr->addr.atapi.drive = raddr.addr.atapi.drive; } #endif #ifdef __OpenBSD__ /* atapi's are shown as SCSI devices */ if (raddr.type == 0) { saddr->type = USCSI_TYPE_SCSI; saddr->addr.scsi.scbus = raddr.scbus; saddr->addr.scsi.target = raddr.target; saddr->addr.scsi.lun = raddr.lun; } else { saddr->type = USCSI_TYPE_ATAPI; saddr->addr.atapi.atbus = raddr.scbus; /* overload */ saddr->addr.atapi.drive = raddr.target; /* overload */ } #endif return 0; } #endif /* SCSILIB_SCSIPI */ #ifdef USCSI_LINUX_SCSI /* * Support code for Linux SCSI code. It uses the ioctl() way of * communicating since this is more close to the origional NetBSD * scsipi implementation. */ #include #include #define SENSEBUFLEN 48 int uscsi_open(struct uscsi_dev * disc) { int flags; struct stat stat; /* in Linux we are NOT allowed to open it blocking */ /* no create! */ disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0); if (disc->fhandle < 0) disc->fhandle = open(disc->dev_name, O_RDONLY | O_NONBLOCK, 0); if (disc->fhandle < 0) { perror("Failure to open device or file"); return ENODEV; } /* explicitly mark it non blocking (again) (silly Linux) */ flags = fcntl(disc->fhandle, F_GETFL); flags &= ~O_NONBLOCK; fcntl(disc->fhandle, F_SETFL, flags); if (fstat(disc->fhandle, &stat) < 0) { perror("Can't stat device or file"); uscsi_close(disc); return ENODEV; } return 0; } int uscsi_close(struct uscsi_dev * disc) { close(disc->fhandle); disc->fhandle = -1; return 0; } int uscsi_command(int flags, struct uscsi_dev *disc, void *cmd, size_t cmdlen, void *data, size_t datalen, uint32_t timeout, struct uscsi_sense *uscsi_sense) { struct sg_io_hdr req; uint8_t sense_buffer[SENSEBUFLEN]; int error; bzero(&req, sizeof(req)); if (flags == SG_DXFER_FROM_DEV) bzero(data, datalen); req.interface_id = 'S'; req.dxfer_direction = flags; req.cmd_len = cmdlen; req.mx_sb_len = SENSEBUFLEN; req.iovec_count = 0; req.dxfer_len = datalen; req.dxferp = data; req.cmdp = cmd; req.sbp = sense_buffer; req.flags = 0; req.timeout = timeout; error = ioctl(disc->fhandle, SG_IO, &req); if (req.status) { /* Is this OK? */ if (uscsi_sense) { uscsi_sense->asc = sense_buffer[12]; uscsi_sense->ascq = sense_buffer[13]; uscsi_sense->skey_valid = sense_buffer[15] & 128; uscsi_sense->sense_key = (sense_buffer[16] << 8) | (sense_buffer[17]); } if (uscsilib_verbose) { uscsi_print_sense((char *) disc->dev_name, cmd, cmdlen, sense_buffer, req.sb_len_wr, 1); } } return error; } int uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr) { struct sg_scsi_id sg_scsi_id; struct sg_id { /* target | lun << 8 | channel << 16 | low_ino << 24 */ uint32_t tlci; uint32_t uniq_id; } sg_id; int emulated; int error; /* clean result */ bzero(saddr, sizeof(struct uscsi_addr)); /* check if its really SCSI or emulated SCSI (ATAPI f.e.) */ saddr->type = USCSI_TYPE_SCSI; ioctl(disc->fhandle, SG_EMULATED_HOST, &emulated); if (emulated) saddr->type = USCSI_TYPE_ATAPI; /* try 2.4 kernel or older */ error = ioctl(disc->fhandle, SG_GET_SCSI_ID, &sg_scsi_id); if (!error) { saddr->addr.scsi.target = sg_scsi_id.scsi_id; saddr->addr.scsi.lun = sg_scsi_id.lun; saddr->addr.scsi.scbus = sg_scsi_id.channel; return 0; } /* 2.6 kernel or newer */ error = ioctl(disc->fhandle, SCSI_IOCTL_GET_IDLUN, &sg_id); if (error) return error; saddr->addr.scsi.target = (sg_id.tlci ) & 0xff; saddr->addr.scsi.lun = (sg_id.tlci >> 8) & 0xff; saddr->addr.scsi.scbus = (sg_id.tlci >> 16) & 0xff; return 0; } #endif /* USCSI_LINUX_SCSI */ #ifdef USCSI_FREEBSD_CAM int uscsi_open(struct uscsi_dev *disc) { disc->devhandle = cam_open_device(disc->dev_name, O_RDWR); if (disc->devhandle == NULL) { disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0); if (disc->fhandle < 0) { perror("Failure to open device or file"); return ENODEV; } } return 0; } int uscsi_close(struct uscsi_dev *disc) { if (disc->devhandle != NULL) { cam_close_device(disc->devhandle); disc->devhandle = NULL; } else { close(disc->fhandle); disc->fhandle = -1; } return 0; } int uscsi_command(int flags, struct uscsi_dev *disc, void *cmd, size_t cmdlen, void *data, size_t datalen, uint32_t timeout, struct uscsi_sense *uscsi_sense) { struct cam_device *cam_dev; struct scsi_sense_data *cam_sense_data; union ccb ccb; uint32_t cam_sense; uint8_t *keypos; int camflags; memset(&ccb, 0, sizeof(ccb)); cam_dev = (struct cam_device *) disc->devhandle; if (cam_dev == 0) return EIO; if (datalen == 0) flags = SCSI_NODATACMD; /* optional : */ /* if (data) assert(flags == SCSI_NODATACMD); */ camflags = CAM_DIR_NONE; if (flags & SCSI_READCMD) camflags = CAM_DIR_IN; if (flags & SCSI_WRITECMD) camflags = CAM_DIR_OUT; cam_fill_csio( &ccb.csio, 0, /* retries */ NULL, /* cbfcnp */ camflags, /* flags */ MSG_SIMPLE_Q_TAG, /* tag_action */ (u_int8_t *) data, /* data_ptr */ datalen, /* dxfer_len */ SSD_FULL_SIZE, /* sense_len */ cmdlen, /* cdb_len */ timeout /* timeout */ ); /* Disable freezing the device queue */ ccb.ccb_h.flags |= CAM_DEV_QFRZDIS; memcpy(ccb.csio.cdb_io.cdb_bytes, cmd, cmdlen); /* Send the command down via the CAM interface */ if (cam_send_ccb(cam_dev, &ccb) < 0) { err(1, "cam_send_ccb"); } if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) return 0; /* print error using the uscsi_sense routines? */ cam_sense = (ccb.ccb_h.status & (CAM_STATUS_MASK | CAM_AUTOSNS_VALID)); if (cam_sense != (CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID)) return EFAULT; /* drive responds with sense information */ if (!uscsilib_verbose) return EFAULT; /* print sense info */ cam_sense_data = &ccb.csio.sense_data; if (uscsi_sense) { uscsi_sense->asc = cam_sense_data->add_sense_code; uscsi_sense->ascq = cam_sense_data->add_sense_code_qual; keypos = cam_sense_data->sense_key_spec; uscsi_sense->skey_valid = keypos[0] & 128; uscsi_sense->sense_key = (keypos[1] << 8) | (keypos[2]); } uscsi_print_sense((char *) disc->dev_name, cmd, cmdlen, (uint8_t *) cam_sense_data, 8 + cam_sense_data->extra_len, 1); return EFAULT; } int uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr) { struct cam_device *cam_dev; /* clean result */ bzero(saddr, sizeof(struct uscsi_addr)); cam_dev = (struct cam_device *) disc->devhandle; if (!cam_dev) return ENODEV; /* check if its really SCSI or emulated SCSI (ATAPI f.e.) ? */ saddr->type = USCSI_TYPE_SCSI; saddr->addr.scsi.target = cam_dev->target_id; saddr->addr.scsi.lun = cam_dev->target_lun; saddr->addr.scsi.scbus = cam_dev->bus_id; return 0; } #endif /* USCSI_FREEBSD_CAM */ #ifndef SCSI int uscsi_open(struct uscsi_dev *disc) { disc->devhandle = NULL; disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0); if (disc->fhandle < 0) { perror("Failure to open device or file"); return ENODEV; } return 0; } int uscsi_close(struct uscsi_dev *disc) { close(disc->fhandle); disc->fhandle = -1; return 0; } int uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr) { bzero(saddr, sizeof(struct uscsi_addr)); saddr->type = USCSI_TYPE_UNKNOWN; } int uscsi_command(int flags, struct uscsi_dev *disc, void *cmd, size_t cmdlen, void *data, size_t datalen, uint32_t timeout, struct uscsi_sense *uscsi_sense) { return ENODEV; } #endif /* !SCSI */ /* * Checks if SCSI is available by issueing the obligatory INQUIRY. */ int uscsi_check_for_scsi(struct uscsi_dev *disc) { scsicmd cmd; uint8_t buf[256]; int error; bzero(cmd, SCSI_CMD_LEN); cmd[0] = 0x12; /* INQUIRY */ cmd[1] = 0; /* basic inquiry */ cmd[2] = 0; /* no page or operation code */ cmd[3] = 0; /* reserved/MSB result */ cmd[4] = 96; /* all but vendor specific */ cmd[5] = 0; /* control */ error = uscsi_command(SCSI_READCMD, disc, cmd, 6, buf, 96, 30000, NULL); return error; } /* * Generic SCSI funtions also used by the sense printing functionality. * FreeBSD support has it allready asked for by the CAM. */ int uscsi_mode_sense(struct uscsi_dev *dev, uint8_t pgcode, uint8_t pctl, void *buf, size_t len) { scsicmd cmd; bzero(buf, len); /* initialise recieving buffer */ bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0x1a; /* MODE SENSE */ cmd[ 1] = 0; /* - */ cmd[ 2] = pgcode | pctl; /* page code and control flags */ cmd[ 3] = 0; /* - */ cmd[ 4] = len; /* length of recieve buffer */ cmd[ 5] = 0; /* control */ return uscsi_command(SCSI_READCMD, dev, &cmd, 6, buf, len, 10000, NULL); } int uscsi_mode_select(struct uscsi_dev *dev, uint8_t byte2, void *buf, size_t len) { scsicmd cmd; bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0x15; /* MODE SELECT */ cmd[ 1] = 0x10 | byte2; /* SCSI-2 page format select */ cmd[ 4] = len; /* length of page settings */ cmd[ 5] = 0; /* control */ return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len, 10000, NULL); } int uscsi_request_sense(struct uscsi_dev *dev, void *buf, size_t len) { scsicmd cmd; bzero(buf, len); /* initialise recieving buffer */ bzero(cmd, SCSI_CMD_LEN); cmd[ 0] = 0x03; /* REQUEST SENSE */ cmd[ 4] = len; /* length of data to be read */ cmd[ 5] = 0; /* control */ return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len, 10000, NULL); } /* end of uscsi_subr.c */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/uscsilib.h000066400000000000000000000076471523477242100261450ustar00rootroot00000000000000/* $NetBSD: extern.h,v 1.4 2002/06/26 16:04:11 mjacob Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Small changes made by Reinoud Zandijk * */ #ifndef _SCSILIB_H_ #define _SCSILIB_H_ #define SCSI_CMD_LEN 12 typedef unsigned char scsicmd[SCSI_CMD_LEN]; #include "defs.h" #include "uscsilib_machdep.h" extern int uscsilib_verbose; /* * Unified structure copied and modified from NetBSD's for ease */ struct uscsi_addr { int type; /* bus type */ #define USCSI_TYPE_SCSI 0 #define USCSI_TYPE_ATAPI 1 #define USCSI_TYPE_UNKNOWN 2 union { struct { int scbus; /* -1 if wildcard */ int target; /* -1 if wildcard */ int lun; /* -1 if wildcard */ } scsi; struct { int atbus; /* -1 if wildcard */ int drive; /* -1 if wildcard */ } atapi; } addr; }; struct uscsi_sense { int asc; /* Additional sense code */ int ascq; /* Additional sense code quality */ int skey_valid; /* sense key valid */ int sense_key; /* sense key; interpret on (asc, ascq) pair */ }; struct uscsi_dev { char *dev_name; int fhandle; void *devhandle; /* for if a fhandle is not enough */ }; /* uscsi_sense.c */ extern char *uscsi_decode_sense(void *sinfo, int flag); extern void uscsi_print_sense(const char *name, u_char *req_cmd, int req_cmdlen, u_char *req_sense, int req_senselen_used, int verbosity); /* scsi_subr.c */ extern int uscsi_open(struct uscsi_dev *); extern int uscsi_close(struct uscsi_dev *); extern int uscsi_command(int flags, struct uscsi_dev *disc, void *cmd, size_t cmdlen, void *data, size_t datalen, uint32_t timeout, struct uscsi_sense *uscsi_sense); extern int uscsi_check_for_scsi(struct uscsi_dev *); extern int uscsi_identify(struct uscsi_dev *, struct uscsi_addr *saddr); extern int uscsi_mode_sense(struct uscsi_dev *, u_int8_t, u_int8_t, void *, size_t); extern int uscsi_mode_select(struct uscsi_dev *, u_int8_t, void *, size_t); extern int uscsi_request_sense(struct uscsi_dev *, void *, size_t); #endif /* _SCSILIB_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/uscsilib_machdep.h000066400000000000000000000054061523477242100276150ustar00rootroot00000000000000/* * File "cd_disect.c" is part of the UDFclient toolkit. * File $Id: uscsilib_machdep.h,v 1.3 2011/02/01 20:43:41 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006, 2011 * Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _USCSILIB_MACHDEP_H_ #define _USCSILIB_MACHDEP_H_ #ifndef _DEV_SCSIPI_SCSIPI_ALL_H_ # define SSD_KEY 0x0F # define SSD_ILI 0x20 # define SSD_EOM 0x40 # define SSD_FILEMARK 0x80 # define SKEY_NO_SENSE 0x00 # define SKEY_RECOVERED_ERROR 0x01 # define SKEY_NOT_READY 0x02 # define SKEY_MEDIUM_ERROR 0x03 # define SKEY_HARDWARE_ERROR 0x04 # define SKEY_ILLEGAL_REQUEST 0x05 # define SKEY_UNIT_ATTENTION 0x06 # define SKEY_WRITE_PROTECT 0x07 # define SKEY_BLANK_CHECK 0x08 # define SKEY_VENDOR_UNIQUE 0x09 # define SKEY_COPY_ABORTED 0x0A # define SKEY_ABORTED_COMMAND 0x0B # define SKEY_EQUAL 0x0C # define SKEY_VOLUME_OVERFLOW 0x0D # define SKEY_MISCOMPARE 0x0E # define SKEY_RESERVED 0x0F #endif #ifndef SCSI # define SCSI_READCMD 0 # define SCSI_WRITECMD 0 struct scsi_addr; int scsilib_verbose; #endif #ifdef USCSI_SCSIPI # include # define SCSI_READCMD SCCMD_READ # define SCSI_WRITECMD SCCMD_WRITE # define SCSI_NODATACMD SCCMD_WRITE #endif #ifdef USCSI_LINUX_SCSI # include # define SCSI_READCMD SG_DXFER_FROM_DEV # define SCSI_WRITECMD SG_DXFER_TO_DEV # define SCSI_NODATACMD SC_DXFER_NONE #endif #ifdef USCSI_FREEBSD_CAM # include # include # define SCSI_READCMD 1 # define SCSI_WRITECMD 2 # define SCSI_NODATACMD 0 #endif #endif /* _USCSILIB_MACHDEP_H_ */ linuxdeepin-util-dfm-a4307d3/src/dfm-burn/3rdparty/udfclient/vfs_dirhash.c000066400000000000000000000302221523477242100266040ustar00rootroot00000000000000/* $NetBSD: vfs_dirhash.c,v 1.8 2008/10/31 16:04:59 reinoud Exp $ */ /* * Copyright (c) 2008, 2011 Reinoud Zandijk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #if 0 #include __KERNEL_RCSID(0, "$NetBSD: vfs_dirhash.c,v 1.8 2008/10/31 16:04:59 reinoud Exp $"); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uscsilib.h" #include "queue.h" #include "hash.h" #include "dirhash.h" #if 1 # define DPRINTF(a) ; #else # define DPRINTF(a) printf(a); #endif /* compat */ #define KASSERT(a) assert(a) #define mutex_enter(mtx) pthread_mutex_lock(mtx) #define mutex_exit(mtx) pthread_mutex_unlock(mtx) /* * The locking protocol of the dirhash structures is fairly simple: * * The global dirhash_queue is protected by the dirhashmutex. This lock is * internal only and is FS/mountpoint/vnode independent. On exit of the * exported functions this mutex is not helt. * * The dirhash structure is considered part of the vnode/inode/udf_node * structure and will thus use the lock that protects that vnode/inode. * * The dirhash entries are considered part of the dirhash structure and thus * are on the same lock. */ //static struct sysctllog *sysctl_log; //static struct pool dirhash_pool; //static struct pool dirhash_entry_pool; static pthread_mutex_t dirhashmutex; static uint32_t maxdirhashsize = DIRHASH_SIZE; static uint32_t dirhashsize = 0; static TAILQ_HEAD(_dirhash, dirhash) dirhash_queue; void dirhash_init(void) { // const struct sysctlnode *rnode, *cnode; // size_t sz; // uint32_t max_entries; /* initialise dirhash queue */ TAILQ_INIT(&dirhash_queue); pthread_mutex_init(&dirhashmutex, 0); #if 0 /* init dirhash pools */ sz = sizeof(struct dirhash); pool_init(&dirhash_pool, sz, 0, 0, 0, "dirhpl", NULL, IPL_NONE); sz = sizeof(struct dirhash_entry); pool_init(&dirhash_entry_pool, sz, 0, 0, 0, "dirhepl", NULL, IPL_NONE); mutex_init(&dirhashmutex, MUTEX_DEFAULT, IPL_NONE); max_entries = maxdirhashsize / sz; pool_sethiwat(&dirhash_entry_pool, max_entries); dirhashsize = 0; /* create sysctl knobs and dials */ sysctl_log = NULL; sysctl_createv(&sysctl_log, 0, NULL, &rnode, CTLFLAG_PERMANENT, CTLTYPE_NODE, "dirhash", NULL, NULL, 0, NULL, 0, CTL_VFS, VFS_GENERIC, CTL_CREATE, CTL_EOL); sysctl_createv(&sysctl_log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_INT, "memused", SYSCTL_DESCR("current dirhash memory usage"), NULL, 0, &dirhashsize, 0, CTL_CREATE, CTL_EOL); sysctl_createv(&sysctl_log, 0, &rnode, &cnode, CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, "maxmem", SYSCTL_DESCR("maximum dirhash memory usage"), NULL, 0, &maxdirhashsize, 0, CTL_CREATE, CTL_EOL); #endif } #if 0 void dirhash_finish(void) { pool_destroy(&dirhash_pool); pool_destroy(&dirhash_entry_pool); mutex_destroy(&dirhashmutex); /* sysctl_teardown(&sysctl_log); */ } #endif /* * generic dirhash implementation */ void dirhash_purge_entries(struct dirhash *dirh) { struct dirhash_entry *dirh_e; uint32_t hashline; if (dirh == NULL) return; if (dirh->size == 0) return; for (hashline = 0; hashline < DIRHASH_HASHSIZE; hashline++) { dirh_e = LIST_FIRST(&dirh->entries[hashline]); while (dirh_e) { LIST_REMOVE(dirh_e, next); //pool_put(&dirhash_entry_pool, dirh_e); free(dirh_e); dirh_e = LIST_FIRST(&dirh->entries[hashline]); } } while ((dirh_e = LIST_FIRST(&dirh->free_entries)) != NULL) { LIST_REMOVE(dirh_e, next); //pool_put(&dirhash_entry_pool, dirh_e); free(dirh_e); } dirh->flags &= ~DIRH_COMPLETE; dirh->flags |= DIRH_PURGED; dirhashsize -= dirh->size; dirh->size = 0; } void dirhash_purge(struct dirhash **dirhp) { struct dirhash *dirh = *dirhp; if (dirh == NULL) return; /* purge its entries */ dirhash_purge_entries(dirh); /* recycle */ mutex_enter(&dirhashmutex); TAILQ_REMOVE(&dirhash_queue, dirh, next); mutex_exit(&dirhashmutex); //pool_put(&dirhash_pool, dirh); free(dirh); *dirhp = NULL; } void dirhash_get(struct dirhash **dirhp) { struct dirhash *dirh; uint32_t hashline; /* if no dirhash was given, allocate one */ dirh = *dirhp; if (dirh == NULL) { // dirh = pool_get(&dirhash_pool, PR_WAITOK); dirh = malloc(sizeof(struct dirhash)); assert(dirh); memset(dirh, 0, sizeof(struct dirhash)); for (hashline = 0; hashline < DIRHASH_HASHSIZE; hashline++) { LIST_INIT(&dirh->entries[hashline]); } } /* implement LRU on the dirhash queue */ mutex_enter(&dirhashmutex); if (*dirhp) { /* remove from queue to be requeued */ TAILQ_REMOVE(&dirhash_queue, dirh, next); } dirh->refcnt++; TAILQ_INSERT_HEAD(&dirhash_queue, dirh, next); mutex_exit(&dirhashmutex); *dirhp = dirh; } void dirhash_put(struct dirhash *dirh) { mutex_enter(&dirhashmutex); dirh->refcnt--; mutex_exit(&dirhashmutex); } void dirhash_enter(struct dirhash *dirh, struct dirent *dirent, uint64_t offset, uint32_t entry_size, int new) { struct dirhash *del_dirh, *prev_dirh; struct dirhash_entry *dirh_e; uint32_t hashvalue, hashline; int entrysize; /* make sure we have a dirhash to work on */ KASSERT(dirh); KASSERT(dirh->refcnt > 0); /* are we trying to re-enter an entry? */ if (!new && (dirh->flags & DIRH_COMPLETE)) return; /* calculate our hash */ hashvalue = hash32_strn(dirent->d_name, DIRENT_NAMLEN(dirent), HASH32_STR_INIT); hashline = hashvalue & DIRHASH_HASHMASK; /* lookup and insert entry if not there yet */ LIST_FOREACH(dirh_e, &dirh->entries[hashline], next) { /* check for hash collision */ if (dirh_e->hashvalue != hashvalue) continue; if (dirh_e->offset != offset) continue; /* got it already */ KASSERT(dirh_e->d_namlen == DIRENT_NAMLEN(dirent)); KASSERT(dirh_e->entry_size == entry_size); return; } DPRINTF(("dirhash enter %"PRIu64", %d, %d for `%*.*s`\n", offset, entry_size, DIRENT_NAMLEN(dirent), DIRENT_NAMLEN(dirent), DIRENT_NAMLEN(dirent), dirent->d_name)); /* check if entry is in free space list */ LIST_FOREACH(dirh_e, &dirh->free_entries, next) { if (dirh_e->offset == offset) { DPRINTF(("\tremoving free entry\n")); LIST_REMOVE(dirh_e, next); //pool_put(&dirhash_entry_pool, dirh_e); free(dirh_e); break; } } /* ensure we are not passing the dirhash limit */ entrysize = sizeof(struct dirhash_entry); if (dirhashsize + entrysize > maxdirhashsize) { /* we walk the dirhash_queue, so need to lock it */ mutex_enter(&dirhashmutex); del_dirh = TAILQ_LAST(&dirhash_queue, _dirhash); KASSERT(del_dirh); while (dirhashsize + entrysize > maxdirhashsize) { /* no use trying to delete myself */ if (del_dirh == dirh) break; prev_dirh = TAILQ_PREV(del_dirh, _dirhash, next); if (del_dirh->refcnt == 0) dirhash_purge_entries(del_dirh); del_dirh = prev_dirh; } mutex_exit(&dirhashmutex); } /* add to the hashline */ // dirh_e = pool_get(&dirhash_entry_pool, PR_WAITOK); dirh_e = malloc(sizeof(struct dirhash_entry)); assert(dirh_e); memset(dirh_e, 0, sizeof(struct dirhash_entry)); dirh_e->hashvalue = hashvalue; dirh_e->offset = offset; dirh_e->d_namlen = DIRENT_NAMLEN(dirent); dirh_e->entry_size = entry_size; dirh->size += sizeof(struct dirhash_entry); dirhashsize += sizeof(struct dirhash_entry); LIST_INSERT_HEAD(&dirh->entries[hashline], dirh_e, next); } void dirhash_enter_freed(struct dirhash *dirh, uint64_t offset, uint32_t entry_size) { struct dirhash_entry *dirh_e; /* make sure we have a dirhash to work on */ KASSERT(dirh); KASSERT(dirh->refcnt > 0); /* check for double entry of free space */ LIST_FOREACH(dirh_e, &dirh->free_entries, next) { KASSERT(dirh_e->offset != offset); } DPRINTF(("dirhash enter FREED %"PRIu64", %d\n", offset, entry_size)); //dirh_e = pool_get(&dirhash_entry_pool, PR_WAITOK); dirh_e = malloc(sizeof(struct dirhash_entry)); assert(dirh_e); memset(dirh_e, 0, sizeof(struct dirhash_entry)); dirh_e->hashvalue = 0; /* not relevant */ dirh_e->offset = offset; dirh_e->d_namlen = 0; /* not relevant */ dirh_e->entry_size = entry_size; /* XXX it might be preferable to append them at the tail */ LIST_INSERT_HEAD(&dirh->free_entries, dirh_e, next); dirh->size += sizeof(struct dirhash_entry); dirhashsize += sizeof(struct dirhash_entry); } void dirhash_mark_freed(struct dirhash *dirh, struct dirhash_entry *dirh_e, struct dirent *dirent) { uint64_t offset; uint32_t entry_size; /* make sure we have a dirhash to work on */ KASSERT(dirh_e); KASSERT(dirh); KASSERT(dirh->refcnt > 0); KASSERT(dirent); DPRINTF(("dirhash remove `%*.*s`\n", DIRENT_NAMLEN(dirent), DIRENT_NAMLEN(dirent), dirent->d_name)); offset = dirh_e->offset; entry_size = dirh_e->entry_size; LIST_REMOVE(dirh_e, next); free(dirh_e); dirh->size -= sizeof(struct dirhash_entry); dirhashsize -= sizeof(struct dirhash_entry); dirhash_enter_freed(dirh, offset, entry_size); return; } /* * BUGALERT: don't use result longer than needed, never past the node lock. * Call with NULL *result initially and it will return nonzero if again. */ int dirhash_lookup(struct dirhash *dirh, const char *d_name, int d_namlen, struct dirhash_entry **result) { struct dirhash_entry *dirh_e; uint32_t hashvalue, hashline; /* make sure we have a dirhash to work on */ KASSERT(dirh); KASSERT(dirh->refcnt > 0); /* start where we were */ if (*result) { dirh_e = *result; /* retrieve information to avoid recalculation and advance */ hashvalue = dirh_e->hashvalue; dirh_e = LIST_NEXT(*result, next); } else { /* calculate our hash and lookup all entries in hashline */ hashvalue = hash32_strn(d_name, d_namlen, HASH32_STR_INIT); hashline = hashvalue & DIRHASH_HASHMASK; dirh_e = LIST_FIRST(&dirh->entries[hashline]); } for (; dirh_e; dirh_e = LIST_NEXT(dirh_e, next)) { /* check for hash collision */ if (dirh_e->hashvalue != hashvalue) continue; if (dirh_e->d_namlen != (uint32_t) d_namlen) continue; /* might have an entry in the cache */ *result = dirh_e; return 1; } *result = NULL; return 0; } /* * BUGALERT: don't use result longer than needed, never past the node lock. * Call with NULL *result initially and it will return nonzero if again. */ int dirhash_lookup_freed(struct dirhash *dirh, uint32_t min_entrysize, struct dirhash_entry **result) { struct dirhash_entry *dirh_e; /* make sure we have a dirhash to work on */ KASSERT(dirh); KASSERT(dirh->refcnt > 0); /* start where we were */ if (*result) { dirh_e = LIST_NEXT(*result, next); } else { /* lookup all entries that match */ dirh_e = LIST_FIRST(&dirh->free_entries); } for (; dirh_e; dirh_e = LIST_NEXT(dirh_e, next)) { /* check for minimum size */ if (dirh_e->entry_size < min_entrysize) continue; /* might be a candidate */ *result = dirh_e; return 1; } *result = NULL; return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/CMakeLists.txt000066400000000000000000000004771523477242100231640ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) #SET(CMAKE_BUILD_TYPE "Debug") SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb") SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") add_subdirectory("dfm-burn-lib") add_subdirectory("dfm-burn-client") linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-client/000077500000000000000000000000001523477242100234025ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-client/CMakeLists.txt000066400000000000000000000006551523477242100261500ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) project(dfm-burner) set(SRCS main.cpp cli_options.cpp cli_options.h burn_utils.cpp burn_utils.h ) find_package(Qt${QT_VERSION_MAJOR}Core REQUIRED) add_executable(${PROJECT_NAME} ${SRCS}) target_link_libraries( ${PROJECT_NAME} Qt${QT_VERSION_MAJOR}::Core dfm${DFM_VERSION_MAJOR}-burn ) install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-client/README.md000066400000000000000000000106661523477242100246720ustar00rootroot00000000000000# Optical Disc Burner Utilities A command-line tool for optical disc operations in Deepin File Manager, powered by libisoburn. ## Features - **Disc information**: Query device, media type, capacity, write speeds - **Burn files**: Burn files and directories to disc with various filesystem options - **ISO operations**: Write ISO images to disc, or dump disc content to ISO - **Disc erase**: Erase rewritable discs (CD-RW, DVD-RW, DVD+RW, BD-RE) - **Quality check**: Verify disc media quality with detailed statistics - **Packet writing**: UDF packet writing for incremental file operations ## Supported Media Types CD-ROM, CD-R, CD-RW, DVD-ROM, DVD-R, DVD-RW, DVD+R, DVD+R DL, DVD-R DL, DVD-RAM, DVD+RW, BD-ROM, BD-R, BD-RE ## Usage ``` dfm-burner [options] [arguments] ``` ### Commands | Command | Description | |---------|-------------| | `info` | Show optical disc information | | `burn` | Burn files to disc | | `write-iso` | Write an ISO image to disc | | `dump-iso` | Dump disc content to an ISO image | | `erase` | Erase a rewritable disc | | `check` | Check media quality | | `pw` | Packet writing (UDF) operations | ### info — Show disc information ``` dfm-burner info [--json] ``` ```bash # Show disc info dfm-burner info /dev/sr0 # JSON output for scripting dfm-burner info --json /dev/sr0 ``` ### burn — Burn files to disc ``` dfm-burner burn [options] [...] ``` Options: - `--volume-id=` — Set disc label shown in file manager (default: ISOIMAGE) - `--speed=` — Write speed in CD/DVD multiplier, 0 = auto (default) Filesystem (pick one, default is iso9660): - `--iso9660` — Basic format, works on virtually all systems - `--joliet` — Long filenames and CJK characters, recommended for Windows - `--rockridge` — Preserves Linux permissions and symlinks, Linux-only - `--udf` — Modern format, files > 2GB, Unicode names. Best for mixed-OS Behavior: - `--appendable` — Leave disc open to add more data later (multi-session) - `--verify` — Read back data after burning to detect errors ```bash # Burn a directory dfm-burner burn /dev/sr0 /home/user/data # Burn with custom volume label dfm-burner burn --volume-id=BACKUP /dev/sr0 /home/user/files # Burn with UDF and appendable (multi-session) dfm-burner burn --udf --appendable /dev/sr0 /home/user/archive # Burn with Joliet + Rock Ridge + verification dfm-burner burn --joliet --rockridge --verify /dev/sr0 file1.txt file2.txt ``` ### write-iso — Write ISO to disc ``` dfm-burner write-iso [--speed=] ``` ```bash dfm-burner write-iso /dev/sr0 /home/user/image.iso dfm-burner write-iso --speed=8 /dev/sr0 /home/user/image.iso ``` ### dump-iso — Dump disc to ISO ``` dfm-burner dump-iso ``` ```bash dfm-burner dump-iso /dev/sr0 /home/user/backup.iso ``` ### erase — Erase disc ``` dfm-burner erase ``` ```bash dfm-burner erase /dev/sr0 ``` ### check — Check media quality ``` dfm-burner check [--json] ``` ```bash dfm-burner check /dev/sr0 dfm-burner check --json /dev/sr0 ``` ### pw — Packet writing (UDF) ``` dfm-burner pw [additional args] ``` Actions: - `open ` — Open a packet writing session - `close ` — Close the session - `put ` — Add a file to the disc - `mv ` — Rename a file on the disc - `rm ` — Remove a file from the disc ```bash # Open packet writing session dfm-burner pw open /dev/sr0 /home/user/pw-work # Add files incrementally dfm-burner pw put /dev/sr0 /home/user/pw-work /home/user/data.txt dfm-burner pw put /dev/sr0 /home/user/pw-work /home/user/photo.jpg # Rename a file on disc dfm-burner pw mv /dev/sr0 /home/user/pw-work old_name.txt new_name.txt # Remove a file from disc dfm-burner pw rm /dev/sr0 /home/user/pw-work unwanted.txt # Close session dfm-burner pw close /dev/sr0 /home/user/pw-work ``` ## Implementation The tool wraps all public APIs of the dfm-burn library: - `DOpticalDiscInfo` — Disc properties and capabilities - `DOpticalDiscManager` — Burn, erase, check, and ISO operations - `DPacketWritingController` — UDF packet writing operations Async operations (burn, write-iso, dump-iso, erase, check) display real-time progress via Qt's event loop. Sync operations (info, pw-*) return immediately. ## License GPL-3.0-or-later linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-client/burn_utils.cpp000066400000000000000000000057441523477242100263060ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "burn_utils.h" DFM_BURN_BEGIN_NS QString formatSize(quint64 bytes) { if (bytes == 0) return "0 B"; const QStringList units = { "B", "KB", "MB", "GB", "TB" }; int idx = 0; double size = bytes; while (size >= 1024.0 && idx < units.size() - 1) { size /= 1024.0; ++idx; } if (idx == 0) return QString("%1 B").arg(bytes); return QString("%1 %2").arg(size, 0, 'f', 1).arg(units.at(idx)); } QString mediaTypeName(MediaType type) { switch (type) { case MediaType::kNoMedia: return "No Media"; case MediaType::kCD_ROM: return "CD-ROM"; case MediaType::kCD_R: return "CD-R"; case MediaType::kCD_RW: return "CD-RW"; case MediaType::kDVD_ROM: return "DVD-ROM"; case MediaType::kDVD_R: return "DVD-R"; case MediaType::kDVD_RW: return "DVD-RW"; case MediaType::kDVD_PLUS_R: return "DVD+R"; case MediaType::kDVD_PLUS_R_DL: return "DVD+R DL"; case MediaType::kDVD_R_DL: return "DVD-R DL"; case MediaType::kDVD_RAM: return "DVD-RAM"; case MediaType::kDVD_PLUS_RW: return "DVD+RW"; case MediaType::kBD_ROM: return "BD-ROM"; case MediaType::kBD_R: return "BD-R"; case MediaType::kBD_RE: return "BD-RE"; } return "Unknown"; } QString jobStatusName(JobStatus status) { switch (status) { case JobStatus::kIdle: return "Idle"; case JobStatus::kRunning: return "Running"; case JobStatus::kStalled: return "Stalled"; case JobStatus::kFinished: return "Finished"; case JobStatus::kFailed: return "Failed"; } return "Unknown"; } QString burnOptionsSummary(BurnOptions options) { QStringList parts; // Filesystem if (options.testFlag(BurnOption::kUDF102Supported)) parts << "UDF filesystem"; else if (options.testFlag(BurnOption::kJolietSupport) && options.testFlag(BurnOption::kRockRidgeSupport)) parts << "ISO9660 + Joliet + RockRidge"; else if (options.testFlag(BurnOption::kJolietSupport)) parts << "ISO9660 + Joliet"; else if (options.testFlag(BurnOption::kRockRidgeSupport)) parts << "ISO9660 + RockRidge"; else parts << "ISO9660 filesystem"; // Behavior if (options.testFlag(BurnOption::kKeepAppendable)) parts << "multi-session (disc stays open)"; if (options.testFlag(BurnOption::kVerifyDatas)) parts << "verify after burn"; return parts.join(", "); } /** * @brief Check if a media type is rewritable */ bool isRewritable(MediaType type) { switch (type) { case MediaType::kCD_RW: case MediaType::kDVD_RW: case MediaType::kDVD_PLUS_RW: case MediaType::kDVD_RAM: case MediaType::kBD_RE: return true; default: return false; } } DFM_BURN_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-client/burn_utils.h000066400000000000000000000017031523477242100257420ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef BURN_UTILS_H #define BURN_UTILS_H #include #include DFM_BURN_BEGIN_NS /** * @brief Format byte size to human-readable string * @param bytes Size in bytes * @return Formatted string (e.g., "702.5 MB") */ QString formatSize(quint64 bytes); /** * @brief Convert MediaType enum to display name */ QString mediaTypeName(MediaType type); /** * @brief Convert JobStatus enum to display name */ QString jobStatusName(JobStatus status); /** * @brief Convert BurnOptions flags to human-readable description * Returns one-line summary like "UDF filesystem, multi-session, verify after burn" */ QString burnOptionsSummary(BurnOptions options); /** * @brief Check if a media type is rewritable (can be erased) */ bool isRewritable(MediaType type); DFM_BURN_END_NS #endif // BURN_UTILS_H linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-client/cli_options.cpp000066400000000000000000000671341523477242100264430ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "cli_options.h" #include #include DFM_BURN_USE_NS using namespace std; // ── Main parse entry ─────────────────────────────────────────── bool CliOptions::parse(int argc, char *argv[], BurnCliConfig &config) { if (argc < 2) { printHelp(); return false; } // Collect remaining arguments (skip program name) QStringList args; for (int i = 2; i < argc; ++i) args.append(QString::fromLocal8Bit(argv[i])); QString cmd = QString::fromLocal8Bit(argv[1]); if (cmd == "--help" || cmd == "-h") { printHelp(); return false; } // ── Route to subcommand parser ── if (cmd == "info") return parseInfoArgs(args, config); if (cmd == "burn") return parseBurnArgs(args, config); if (cmd == "write-iso") return parseWriteIsoArgs(args, config); if (cmd == "dump-iso") return parseDumpIsoArgs(args, config); if (cmd == "erase") return parseEraseArgs(args, config); if (cmd == "check") return parseCheckArgs(args, config); if (cmd == "checksum") { if (args.isEmpty()) { cerr << "Error: 'checksum' requires a subcommand (gen, verify)" << endl; cerr << "Run 'dfm-burner checksum --help' for details." << endl; return false; } QString checksumAction = args.takeFirst(); if (checksumAction == "gen") return parseChecksumGenArgs(args, config); if (checksumAction == "verify") return parseChecksumVerifyArgs(args, config); if (checksumAction == "--help" || checksumAction == "-h") { printCommandHelp(BurnCommand::ChecksumGen); return false; } cerr << "Error: Unknown checksum action '" << checksumAction.toStdString() << "'" << endl; cerr << "Valid actions: gen, verify" << endl; return false; } if (cmd == "pw") { if (args.isEmpty()) { cerr << "Error: 'pw' requires a subcommand (open, close, put, mv, rm)" << endl; cerr << "Run 'dfm-burner pw --help' for details." << endl; return false; } QString pwAction = args.takeFirst(); return parsePwArgs(pwAction, args, config); } cerr << "Error: Unknown command '" << cmd.toStdString() << "'" << endl; printHelp(); return false; } // ── Help output ──────────────────────────────────────────────── void CliOptions::printHelp() const { cout << "Usage: dfm-burner [options] [arguments]" << endl; cout << endl; cout << "Optical disc burning tool for Deepin File Manager." << endl; cout << "Use 'dfm-burner info /dev/sr0' to check disc status before burning." << endl; cout << endl; cout << "Commands:" << endl; cout << " info Show disc info (type, capacity, free space)" << endl; cout << " burn Burn files or folders to disc" << endl; cout << " write-iso Write an .iso image to disc" << endl; cout << " dump-iso Read disc and save as .iso image" << endl; cout << " erase Erase a rewritable disc (CD-RW, DVD-RW, BD-RE)" << endl; cout << " check Scan disc for read errors" << endl; cout << " checksum SM3 checksum verification (gen / verify)" << endl; cout << " pw Incremental file operations (UDF packet writing)" << endl; cout << endl; cout << "Tips:" << endl; cout << " - Find your device: ls /dev/sr*" << endl; cout << " - Get started: dfm-burner info /dev/sr0" << endl; cout << " - Quick burn: dfm-burner burn /dev/sr0 ./my-folder" << endl; cout << endl; cout << "Run 'dfm-burner --help' for details." << endl; } void CliOptions::printCommandHelp(BurnCommand cmd) const { switch (cmd) { case BurnCommand::Info: cout << "Usage: dfm-burner info [--json] " << endl; cout << endl; cout << "Show disc type, capacity, used/free space, and write speeds." << endl; cout << "Run this first to check what disc is in the drive before burning." << endl; cout << endl; cout << "Arguments:" << endl; cout << " device Device path (e.g., /dev/sr0). Use 'ls /dev/sr*' to find it." << endl; cout << endl; cout << "Options:" << endl; cout << " --json, -j Output in JSON format (for scripting)" << endl; cout << endl; cout << "Examples:" << endl; cout << " dfm-burner info /dev/sr0 # Check disc status" << endl; cout << " dfm-burner info --json /dev/sr0 # Machine-readable output" << endl; break; case BurnCommand::Burn: cout << "Usage: dfm-burner burn [options] [...]" << endl; cout << endl; cout << "Burn files and directories to an optical disc." << endl; cout << "Use 'dfm-burner info /dev/sr0' first to check disc type and free space." << endl; cout << endl; cout << "Arguments:" << endl; cout << " device Device path (e.g., /dev/sr0). Use 'ls /dev/sr*' to find it." << endl; cout << " file_or_dir One or more files or folders to burn." << endl; cout << endl; cout << "Options:" << endl; cout << " --volume-id= Set disc label shown in file manager (default: ISOIMAGE)" << endl; cout << " --speed= Write speed in CD/DVD multiplier. 0 = auto pick (default)" << endl; cout << endl; cout << "Filesystem (pick one, default is iso9660):" << endl; cout << " --iso9660 Basic format, works on virtually all systems" << endl; cout << " --joliet Like iso9660 but supports long filenames and CJK" << endl; cout << " characters, recommended for Windows compatibility" << endl; cout << " --rockridge Like iso9660 but preserves Linux permissions and" << endl; cout << " symbolic links, recommended for Linux-only use" << endl; cout << " --udf Modern format, supports files > 2GB and Unicode" << endl; cout << " filenames. Best for large files or mixed-OS use." << endl; cout << " Combine with --appendable for multi-session." << endl; cout << endl; cout << "Behavior:" << endl; cout << " --appendable After burning, leave disc open so you can add more" << endl; cout << " data in a later session (multi-session disc)" << endl; cout << " --verify Read back all data after burning to detect errors." << endl; cout << " Slower but ensures data integrity." << endl; cout << endl; cout << "Examples:" << endl; cout << " # Quick burn with defaults" << endl; cout << " dfm-burner burn /dev/sr0 ./my-folder" << endl; cout << endl; cout << " # Burn with a custom label" << endl; cout << " dfm-burner burn --volume-id=Photos_2026 /dev/sr0 ./photos" << endl; cout << endl; cout << " # Burn a 4GB file (needs UDF)" << endl; cout << " dfm-burner burn --udf /dev/sr0 ./large-video.mkv" << endl; cout << endl; cout << " # Multi-session: burn now, add more files later" << endl; cout << " dfm-burner burn --udf --appendable /dev/sr0 ./batch1" << endl; cout << endl; cout << " # Cross-platform: works on Windows, Linux, macOS" << endl; cout << " dfm-burner burn --joliet --rockridge --verify /dev/sr0 ./data" << endl; break; case BurnCommand::WriteISO: cout << "Usage: dfm-burner write-iso [--speed=] " << endl; cout << endl; cout << "Write an .iso image file to disc (1:1 copy, disc-at-once)." << endl; cout << endl; cout << "Arguments:" << endl; cout << " device Device path (e.g., /dev/sr0). Use 'ls /dev/sr*' to find it." << endl; cout << " iso_path Path to the .iso file to burn." << endl; cout << endl; cout << "Options:" << endl; cout << " --speed= Write speed. 0 = auto pick (default)" << endl; cout << endl; cout << "Examples:" << endl; cout << " dfm-burner write-iso /dev/sr0 ./ubuntu-22.04.iso" << endl; cout << " dfm-burner write-iso --speed=8 /dev/sr0 ./debian-live.iso" << endl; break; case BurnCommand::DumpISO: cout << "Usage: dfm-burner dump-iso " << endl; cout << endl; cout << "Read disc content and save it as an .iso image file (for backup or redistribution)." << endl; cout << endl; cout << "Arguments:" << endl; cout << " device Device path (e.g., /dev/sr0). Use 'ls /dev/sr*' to find it." << endl; cout << " output_path Where to save the .iso file." << endl; cout << endl; cout << "Examples:" << endl; cout << " dfm-burner dump-iso /dev/sr0 ./backup.iso" << endl; break; case BurnCommand::Erase: cout << "Usage: dfm-burner erase " << endl; cout << endl; cout << "Erase all data on a rewritable disc." << endl; cout << "Only works on rewritable media: CD-RW, DVD-RW, DVD+RW, BD-RE." << endl; cout << "CD-R and DVD+R are write-once and cannot be erased." << endl; cout << endl; cout << "Arguments:" << endl; cout << " device Device path (e.g., /dev/sr0). Use 'ls /dev/sr*' to find it." << endl; cout << endl; cout << "Examples:" << endl; cout << " dfm-burner erase /dev/sr0" << endl; break; case BurnCommand::Check: cout << "Usage: dfm-burner check [--json] " << endl; cout << endl; cout << "Read the entire disc to detect read errors and report quality statistics." << endl; cout << "Useful for verifying old backups or checking disc health." << endl; cout << endl; cout << "Arguments:" << endl; cout << " device Device path (e.g., /dev/sr0). Use 'ls /dev/sr*' to find it." << endl; cout << endl; cout << "Options:" << endl; cout << " --json, -j Output in JSON format (for scripting)" << endl; cout << endl; cout << "Examples:" << endl; cout << " dfm-burner check /dev/sr0" << endl; cout << " dfm-burner check --json /dev/sr0" << endl; break; case BurnCommand::ChecksumGen: cout << "Usage: dfm-burner checksum gen --output= " << endl; cout << endl; cout << "Generate an SM3 checksum manifest for files before burning." << endl; cout << "The manifest records the SM3 hash of every file, which can later be" << endl; cout << "used to verify disc integrity with 'dfm-burner checksum verify'." << endl; cout << endl; cout << "Arguments:" << endl; cout << " source_dir Directory containing files to be burned." << endl; cout << endl; cout << "Options:" << endl; cout << " --output= Where to save the manifest JSON (required)." << endl; cout << endl; cout << "Examples:" << endl; cout << " dfm-burner checksum gen --output=manifest.json ./my-data" << endl; break; case BurnCommand::ChecksumVerify: cout << "Usage: dfm-burner checksum verify " << endl; cout << endl; cout << "Verify burned disc integrity by comparing SM3 checksums." << endl; cout << "Extracts all files from the disc and compares their SM3 hashes" << endl; cout << "against the manifest generated by 'dfm-burner checksum gen'." << endl; cout << endl; cout << "Arguments:" << endl; cout << " device Device path (e.g., /dev/sr0). Disc must still be in drive." << endl; cout << " manifest.json Path to the manifest file created during 'gen'." << endl; cout << endl; cout << "Examples:" << endl; cout << " dfm-burner checksum verify /dev/sr0 manifest.json" << endl; break; case BurnCommand::PwOpen: case BurnCommand::PwClose: case BurnCommand::PwPut: case BurnCommand::PwMv: case BurnCommand::PwRm: cout << "Usage: dfm-burner pw [extra_args...]" << endl; cout << endl; cout << "Packet writing (UDF) lets you use a rewritable disc like a USB drive:" << endl; cout << "add, rename, or remove individual files without rewriting the whole disc." << endl; cout << endl; cout << "Arguments:" << endl; cout << " device Device path (e.g., /dev/sr0). Use 'ls /dev/sr*' to find it." << endl; cout << " working_path Local directory for temporary staging files." << endl; cout << " Must be on the same filesystem as the files you put." << endl; cout << endl; cout << "Actions:" << endl; cout << " open Open disc for packet writing (must be called first)" << endl; cout << " close Finish packet writing and finalize disc" << endl; cout << " put Copy a file from your computer onto the disc" << endl; cout << " mv Rename a file already on the disc" << endl; cout << " rm Delete a file from the disc" << endl; cout << endl; cout << "Workflow:" << endl; cout << " 1. dfm-burner pw open /dev/sr0 /tmp/pw-work" << endl; cout << " 2. dfm-burner pw put /dev/sr0 /tmp/pw-work /home/user/doc.txt" << endl; cout << " 3. dfm-burner pw put /dev/sr0 /tmp/pw-work /home/user/photo.jpg" << endl; cout << " 4. dfm-burner pw close /dev/sr0 /tmp/pw-work" << endl; break; default: printHelp(); break; } } // ── Subcommand parsers ───────────────────────────────────────── bool CliOptions::parseInfoArgs(const QStringList &args, BurnCliConfig &config) const { config.command = BurnCommand::Info; for (const auto &arg : args) { if (arg == "--help" || arg == "-h") { printCommandHelp(BurnCommand::Info); return false; } if (arg == "--json" || arg == "-j") { config.jsonOutput = true; } else if (!arg.startsWith('-') && config.device.isEmpty()) { config.device = arg; } else { cerr << "Error: Unknown argument '" << arg.toStdString() << "'" << endl; return false; } } if (config.device.isEmpty()) { cerr << "Error: Device path is required." << endl; cerr << "Run 'dfm-burner info --help' for usage." << endl; return false; } return true; } bool CliOptions::parseBurnArgs(const QStringList &args, BurnCliConfig &config) const { config.command = BurnCommand::Burn; bool hasFsOption = false; for (const auto &arg : args) { if (arg == "--help" || arg == "-h") { printCommandHelp(BurnCommand::Burn); return false; } if (arg.startsWith("--volume-id=")) { config.volumeId = arg.mid(12); if (config.volumeId.isEmpty()) { cerr << "Error: --volume-id requires a non-empty value." << endl; return false; } } else if (arg.startsWith("--speed=")) { bool ok = false; config.speed = arg.mid(8).toInt(&ok); if (!ok || config.speed < 0) { cerr << "Error: Invalid speed value '" << arg.mid(8).toStdString() << "'" << endl; return false; } } else if (arg == "--iso9660") { config.burnOptions |= BurnOption::kISO9660Only; hasFsOption = true; } else if (arg == "--joliet") { config.burnOptions |= BurnOption::kJolietSupport; hasFsOption = true; } else if (arg == "--rockridge") { config.burnOptions |= BurnOption::kRockRidgeSupport; hasFsOption = true; } else if (arg == "--udf") { config.burnOptions |= BurnOption::kUDF102Supported; hasFsOption = true; } else if (arg == "--appendable") { config.burnOptions |= BurnOption::kKeepAppendable; } else if (arg == "--verify") { config.burnOptions |= BurnOption::kVerifyDatas; } else if (!arg.startsWith('-')) { if (config.device.isEmpty()) config.device = arg; else config.stageFiles << arg; } else { cerr << "Error: Unknown option '" << arg.toStdString() << "'" << endl; return false; } } // Default filesystem is ISO9660 if (!hasFsOption) config.burnOptions |= BurnOption::kISO9660Only; if (config.device.isEmpty()) { cerr << "Error: Device path is required." << endl; cerr << "Run 'dfm-burner burn --help' for usage." << endl; return false; } if (config.stageFiles.isEmpty()) { cerr << "Error: At least one file or directory is required." << endl; cerr << "Run 'dfm-burner burn --help' for usage." << endl; return false; } // Validate stage files exist for (const auto &f : config.stageFiles) { if (!QFileInfo::exists(f)) { cerr << "Error: File not found: " << f.toStdString() << endl; return false; } } return true; } bool CliOptions::parseWriteIsoArgs(const QStringList &args, BurnCliConfig &config) const { config.command = BurnCommand::WriteISO; for (const auto &arg : args) { if (arg == "--help" || arg == "-h") { printCommandHelp(BurnCommand::WriteISO); return false; } if (arg.startsWith("--speed=")) { bool ok = false; config.speed = arg.mid(8).toInt(&ok); if (!ok || config.speed < 0) { cerr << "Error: Invalid speed value '" << arg.mid(8).toStdString() << "'" << endl; return false; } } else if (!arg.startsWith('-')) { if (config.device.isEmpty()) config.device = arg; else if (config.isoPath.isEmpty()) config.isoPath = arg; else { cerr << "Error: Unexpected argument '" << arg.toStdString() << "'" << endl; return false; } } else { cerr << "Error: Unknown option '" << arg.toStdString() << "'" << endl; return false; } } if (config.device.isEmpty() || config.isoPath.isEmpty()) { cerr << "Error: Device and ISO path are required." << endl; cerr << "Run 'dfm-burner write-iso --help' for usage." << endl; return false; } if (!QFileInfo::exists(config.isoPath)) { cerr << "Error: ISO file not found: " << config.isoPath.toStdString() << endl; return false; } return true; } bool CliOptions::parseDumpIsoArgs(const QStringList &args, BurnCliConfig &config) const { config.command = BurnCommand::DumpISO; for (const auto &arg : args) { if (arg == "--help" || arg == "-h") { printCommandHelp(BurnCommand::DumpISO); return false; } if (!arg.startsWith('-')) { if (config.device.isEmpty()) config.device = arg; else if (config.outputPath.isEmpty()) config.outputPath = arg; else { cerr << "Error: Unexpected argument '" << arg.toStdString() << "'" << endl; return false; } } else { cerr << "Error: Unknown option '" << arg.toStdString() << "'" << endl; return false; } } if (config.device.isEmpty() || config.outputPath.isEmpty()) { cerr << "Error: Device and output path are required." << endl; cerr << "Run 'dfm-burner dump-iso --help' for usage." << endl; return false; } return true; } bool CliOptions::parseEraseArgs(const QStringList &args, BurnCliConfig &config) const { config.command = BurnCommand::Erase; for (const auto &arg : args) { if (arg == "--help" || arg == "-h") { printCommandHelp(BurnCommand::Erase); return false; } if (!arg.startsWith('-') && config.device.isEmpty()) { config.device = arg; } else { cerr << "Error: Unexpected argument '" << arg.toStdString() << "'" << endl; return false; } } if (config.device.isEmpty()) { cerr << "Error: Device path is required." << endl; cerr << "Run 'dfm-burner erase --help' for usage." << endl; return false; } return true; } bool CliOptions::parseCheckArgs(const QStringList &args, BurnCliConfig &config) const { config.command = BurnCommand::Check; for (const auto &arg : args) { if (arg == "--help" || arg == "-h") { printCommandHelp(BurnCommand::Check); return false; } if (arg == "--json" || arg == "-j") { config.jsonOutput = true; } else if (!arg.startsWith('-') && config.device.isEmpty()) { config.device = arg; } else { cerr << "Error: Unknown argument '" << arg.toStdString() << "'" << endl; return false; } } if (config.device.isEmpty()) { cerr << "Error: Device path is required." << endl; cerr << "Run 'dfm-burner check --help' for usage." << endl; return false; } return true; } bool CliOptions::parseChecksumGenArgs(const QStringList &args, BurnCliConfig &config) const { config.command = BurnCommand::ChecksumGen; for (const auto &arg : args) { if (arg == "--help" || arg == "-h") { printCommandHelp(BurnCommand::ChecksumGen); return false; } if (arg.startsWith("--output=")) { config.manifestPath = arg.mid(9); if (config.manifestPath.isEmpty()) { cerr << "Error: --output requires a non-empty value." << endl; return false; } } else if (!arg.startsWith('-')) { if (config.stageFiles.isEmpty()) config.stageFiles << arg; else { cerr << "Error: Unexpected argument '" << arg.toStdString() << "'" << endl; return false; } } else { cerr << "Error: Unknown option '" << arg.toStdString() << "'" << endl; return false; } } if (config.manifestPath.isEmpty()) { cerr << "Error: --output= is required." << endl; cerr << "Run 'dfm-burner checksum gen --help' for usage." << endl; return false; } if (config.stageFiles.isEmpty()) { cerr << "Error: Source directory is required." << endl; cerr << "Run 'dfm-burner checksum gen --help' for usage." << endl; return false; } if (!QFileInfo::exists(config.stageFiles.first())) { cerr << "Error: Source not found: " << config.stageFiles.first().toStdString() << endl; return false; } return true; } bool CliOptions::parseChecksumVerifyArgs(const QStringList &args, BurnCliConfig &config) const { config.command = BurnCommand::ChecksumVerify; for (const auto &arg : args) { if (arg == "--help" || arg == "-h") { printCommandHelp(BurnCommand::ChecksumVerify); return false; } if (!arg.startsWith('-')) { if (config.device.isEmpty()) config.device = arg; else if (config.manifestPath.isEmpty()) config.manifestPath = arg; else { cerr << "Error: Unexpected argument '" << arg.toStdString() << "'" << endl; return false; } } else { cerr << "Error: Unknown option '" << arg.toStdString() << "'" << endl; return false; } } if (config.device.isEmpty()) { cerr << "Error: Device path is required." << endl; cerr << "Run 'dfm-burner checksum verify --help' for usage." << endl; return false; } if (config.manifestPath.isEmpty()) { cerr << "Error: Manifest path is required." << endl; cerr << "Run 'dfm-burner checksum verify --help' for usage." << endl; return false; } if (!QFileInfo::exists(config.manifestPath)) { cerr << "Error: Manifest not found: " << config.manifestPath.toStdString() << endl; return false; } return true; } bool CliOptions::parsePwArgs(const QString &pwAction, const QStringList &args, BurnCliConfig &config) const { // Map action to command if (pwAction == "--help" || pwAction == "-h") { printCommandHelp(BurnCommand::PwOpen); return false; } if (pwAction == "open") { config.command = BurnCommand::PwOpen; } else if (pwAction == "close") { config.command = BurnCommand::PwClose; } else if (pwAction == "put") { config.command = BurnCommand::PwPut; } else if (pwAction == "mv") { config.command = BurnCommand::PwMv; } else if (pwAction == "rm") { config.command = BurnCommand::PwRm; } else { cerr << "Error: Unknown pw action '" << pwAction.toStdString() << "'" << endl; cerr << "Valid actions: open, close, put, mv, rm" << endl; return false; } // Parse positional arguments for (const auto &arg : args) { if (arg.startsWith('-')) { cerr << "Error: Unknown option '" << arg.toStdString() << "'" << endl; return false; } if (config.device.isEmpty()) config.device = arg; else if (config.workingPath.isEmpty()) config.workingPath = arg; else if (config.command == BurnCommand::PwPut && config.pwFileName.isEmpty()) config.pwFileName = arg; else if (config.command == BurnCommand::PwRm && config.pwFileName.isEmpty()) config.pwFileName = arg; else if (config.command == BurnCommand::PwMv && config.pwSrcName.isEmpty()) config.pwSrcName = arg; else if (config.command == BurnCommand::PwMv && config.pwDestName.isEmpty()) config.pwDestName = arg; else { cerr << "Error: Unexpected argument '" << arg.toStdString() << "'" << endl; return false; } } // Validate required arguments if (config.device.isEmpty() || config.workingPath.isEmpty()) { cerr << "Error: Device and working path are required." << endl; cerr << "Run 'dfm-burner pw --help' for usage." << endl; return false; } if (config.command == BurnCommand::PwPut && config.pwFileName.isEmpty()) { cerr << "Error: File name is required for 'put' action." << endl; return false; } if (config.command == BurnCommand::PwRm && config.pwFileName.isEmpty()) { cerr << "Error: File name is required for 'rm' action." << endl; return false; } if (config.command == BurnCommand::PwMv && (config.pwSrcName.isEmpty() || config.pwDestName.isEmpty())) { cerr << "Error: Source and destination names are required for 'mv' action." << endl; return false; } return true; } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-client/cli_options.h000066400000000000000000000047561523477242100261110ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CLI_OPTIONS_H #define CLI_OPTIONS_H #include #include DFM_BURN_BEGIN_NS /** * @brief Supported burn subcommands */ enum class BurnCommand { None, Info, Burn, WriteISO, DumpISO, Erase, Check, ChecksumGen, ChecksumVerify, PwOpen, PwClose, PwPut, PwMv, PwRm }; /** * @brief Parsed CLI configuration * * Fields are populated based on the active subcommand; * unrelated fields are left at their defaults. */ struct BurnCliConfig { BurnCommand command = BurnCommand::None; QString device; bool jsonOutput = false; // burn options QStringList stageFiles; QString volumeId = "ISOIMAGE"; int speed = 0; BurnOptions burnOptions; // write-iso / dump-iso paths QString isoPath; QString outputPath; // packet writing QString workingPath; QString pwSrcName; QString pwDestName; QString pwFileName; // checksum QString manifestPath; }; /** * @brief Command-line option parser with subcommand routing * * Supports git-style subcommands: info, burn, write-iso, dump-iso, * erase, check, pw . */ class CliOptions { public: CliOptions() = default; ~CliOptions() = default; /** * @brief Parse command-line arguments into config * @return true on success, false on error (message already printed) */ bool parse(int argc, char *argv[], BurnCliConfig &config); void printHelp() const; private: void printCommandHelp(BurnCommand cmd) const; // ── Per-subcommand parsers ── bool parseInfoArgs(const QStringList &args, BurnCliConfig &config) const; bool parseBurnArgs(const QStringList &args, BurnCliConfig &config) const; bool parseWriteIsoArgs(const QStringList &args, BurnCliConfig &config) const; bool parseDumpIsoArgs(const QStringList &args, BurnCliConfig &config) const; bool parseEraseArgs(const QStringList &args, BurnCliConfig &config) const; bool parseCheckArgs(const QStringList &args, BurnCliConfig &config) const; bool parseChecksumGenArgs(const QStringList &args, BurnCliConfig &config) const; bool parseChecksumVerifyArgs(const QStringList &args, BurnCliConfig &config) const; bool parsePwArgs(const QString &pwAction, const QStringList &args, BurnCliConfig &config) const; }; DFM_BURN_END_NS #endif // CLI_OPTIONS_H linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-client/main.cpp000066400000000000000000000433441523477242100250420ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include "cli_options.h" #include "burn_utils.h" DFM_BURN_USE_NS using namespace std; // ── Helpers ──────────────────────────────────────────────────── /** * @brief Run an async operation that reports progress via jobStatusChanged. * * @param config Parsed CLI config (must have .device set) * @param app QCoreApplication reference (for event loop) * @param startOp Lambda that starts the operation; returns false on immediate failure * @param opName Human-readable operation name for progress display * @return Exit code (0 = success, 1 = failure) */ static int runAsyncJob(const BurnCliConfig &config, QCoreApplication &app, const function &startOp, const QString &opName) { DOpticalDiscManager manager(config.device); int exitCode = 1; QObject::connect(&manager, &DOpticalDiscManager::jobStatusChanged, [&exitCode, &app, &opName](JobStatus status, int progress, const QString &speed, const QStringList &message) { if (status == JobStatus::kRunning) { cout << "\r" << opName.toStdString() << "... " << progress << "%"; if (!speed.isEmpty()) cout << " (" << speed.toStdString() << ")"; cout << flush; } else if (status == JobStatus::kFinished) { cout << "\r" << opName.toStdString() << "... Done. " << endl; exitCode = 0; app.quit(); } else if (status == JobStatus::kFailed) { cerr << endl << "Operation failed: " << message.join(" ").toStdString() << endl; exitCode = 1; app.quit(); } else if (status == JobStatus::kStalled) { cout << "\r" << opName.toStdString() << "... Stalled (waiting for drive)." << flush; } }); if (!startOp(manager)) return 1; return app.exec(); } // ── Command: info (sync) ────────────────────────────────────── static int handleInfo(const BurnCliConfig &config) { QScopedPointer info(DOpticalDiscManager::createOpticalInfo(config.device)); if (!info) { cerr << "Error: Failed to get disc info for " << config.device.toStdString() << endl; return 1; } if (info->mediaType() == MediaType::kNoMedia) { cerr << "Error: No disc detected in " << config.device.toStdString() << endl; cerr << " Insert a disc and try again." << endl; return 1; } if (config.jsonOutput) { QJsonObject obj; obj["device"] = info->device(); obj["mediaType"] = mediaTypeName(info->mediaType()); obj["blank"] = info->blank(); obj["volumeName"] = info->volumeName(); obj["usedSize"] = static_cast(info->usedSize()); obj["availableSize"] = static_cast(info->availableSize()); obj["totalSize"] = static_cast(info->totalSize()); obj["dataBlocks"] = static_cast(info->dataBlocks()); QJsonArray speeds; for (const auto &s : info->writeSpeed()) speeds.append(s); obj["writeSpeeds"] = speeds; cout << QJsonDocument(obj).toJson(QJsonDocument::Indented).toStdString(); } else { cout << "Device: " << info->device().toStdString() << endl; cout << "Media Type: " << mediaTypeName(info->mediaType()).toStdString() << (isRewritable(info->mediaType()) ? " (rewritable)" : " (write-once)") << endl; cout << "Blank: " << (info->blank() ? "Yes" : "No") << endl; cout << "Volume Name: " << (info->volumeName().isEmpty() ? "(empty)" : info->volumeName().toStdString()) << endl; cout << "Used: " << formatSize(info->usedSize()).toStdString() << endl; cout << "Available: " << formatSize(info->availableSize()).toStdString() << endl; cout << "Total: " << formatSize(info->totalSize()).toStdString() << endl; cout << "Data Blocks: " << info->dataBlocks() << endl; QStringList speeds = info->writeSpeed(); cout << "Write Speeds: " << (speeds.isEmpty() ? "(none)" : speeds.join(", ").toStdString()) << endl; // Actionable hints cout << endl; if (info->blank()) { cout << ">> Disc is blank and ready to burn." << endl; cout << " Quick start: dfm-burner burn " << info->device().toStdString() << " ./your-folder" << endl; } else if (info->availableSize() > 0) { cout << ">> Disc has free space (" << formatSize(info->availableSize()).toStdString() << ")." << endl; cout << " To add data: dfm-burner burn --appendable " << info->device().toStdString() << " ./your-folder" << endl; cout << " To start fresh, erase first: dfm-burner erase " << info->device().toStdString() << endl; } else { cout << ">> Disc is full. To reuse, erase first: dfm-burner erase " << info->device().toStdString() << endl; } } return 0; } // ── Command: burn (async) ────────────────────────────────────── static int handleBurn(const BurnCliConfig &config, QCoreApplication &app) { auto startOp = [&config](DOpticalDiscManager &manager) -> bool { for (const auto &file : config.stageFiles) { if (!manager.setStageFile(file)) { cerr << "Error: Could not add '" << file.toStdString() << "' to burn queue." << endl; cerr << " " << manager.lastError().toStdString() << endl; return false; } } cout << "Device: " << config.device.toStdString() << endl; cout << "Volume: " << config.volumeId.toStdString() << endl; cout << "Mode: " << burnOptionsSummary(config.burnOptions).toStdString() << endl; cout << "Files: " << config.stageFiles.join(", ").toStdString() << endl; cout << endl; if (!manager.commit(config.burnOptions, config.speed, config.volumeId)) { cerr << "Error: Failed to start burn operation." << endl; cerr << " " << manager.lastError().toStdString() << endl; return false; } return true; }; return runAsyncJob(config, app, startOp, "Burning"); } // ── Command: write-iso (async) ───────────────────────────────── static int handleWriteISO(const BurnCliConfig &config, QCoreApplication &app) { QFileInfo isoInfo(config.isoPath); cout << "Device: " << config.device.toStdString() << endl; cout << "ISO: " << config.isoPath.toStdString() << endl; cout << "Size: " << formatSize(isoInfo.size()).toStdString() << endl; cout << endl; auto startOp = [&config](DOpticalDiscManager &manager) -> bool { if (!manager.writeISO(config.isoPath, config.speed)) { cerr << "Error: " << manager.lastError().toStdString() << endl; return false; } return true; }; return runAsyncJob(config, app, startOp, "Writing ISO"); } // ── Command: dump-iso (async) ────────────────────────────────── static int handleDumpISO(const BurnCliConfig &config, QCoreApplication &app) { cout << "Device: " << config.device.toStdString() << endl; cout << "Output: " << config.outputPath.toStdString() << endl; cout << endl; auto startOp = [&config](DOpticalDiscManager &manager) -> bool { if (!manager.dumpISO(config.outputPath)) { cerr << "Error: " << manager.lastError().toStdString() << endl; return false; } return true; }; return runAsyncJob(config, app, startOp, "Dumping"); } // ── Command: erase (async) ───────────────────────────────────── static int handleErase(const BurnCliConfig &config, QCoreApplication &app) { cout << "Device: " << config.device.toStdString() << endl; cout << endl; auto startOp = [](DOpticalDiscManager &manager) -> bool { if (!manager.erase()) { cerr << "Error: " << manager.lastError().toStdString() << endl; return false; } return true; }; return runAsyncJob(config, app, startOp, "Erasing"); } // ── Command: check (async, custom handler) ───────────────────── static int handleCheck(const BurnCliConfig &config, QCoreApplication &app) { DOpticalDiscManager manager(config.device); int exitCode = 1; double good = 0, slow = 0, bad = 0; QObject::connect(&manager, &DOpticalDiscManager::jobStatusChanged, [&exitCode, &app, &config, &good, &slow, &bad]( JobStatus status, int progress, const QString &speed, const QStringList &message) { if (status == JobStatus::kRunning) { cout << "\rChecking... " << progress << "%" << flush; } else if (status == JobStatus::kFinished) { cout << endl; bool passed = !(bad > (2 + 1e-6)); if (config.jsonOutput) { QJsonObject obj; obj["good"] = good; obj["slow"] = slow; obj["bad"] = bad; obj["passed"] = passed; cout << QJsonDocument(obj).toJson(QJsonDocument::Indented).toStdString(); } else { cout << fixed << setprecision(1); cout << "Quality:" << endl; cout << " Good: " << good << "%" << endl; cout << " Slow: " << slow << "%" << endl; cout << " Bad: " << bad << "%" << endl; cout << endl; cout << "Result: " << (passed ? "PASS" : "FAIL") << endl; } exitCode = passed ? 0 : 1; app.quit(); } else if (status == JobStatus::kFailed) { cerr << endl << "Check failed: " << message.join(" ").toStdString() << endl; exitCode = 1; app.quit(); } }); if (!manager.checkmedia(&good, &slow, &bad)) { cerr << "Error: " << manager.lastError().toStdString() << endl; return 1; } return app.exec(); } // ── Command: checksum gen (sync) ──────────────────────────────── static int handleChecksumGen(const BurnCliConfig &config) { DOpticalDiscManager manager(config.device); if (!manager.setStageFile(config.stageFiles.first())) { cerr << "Error: " << manager.lastError().toStdString() << endl; return 1; } if (!manager.generateChecksumManifest(config.manifestPath)) { cerr << "Error: " << manager.lastError().toStdString() << endl; return 1; } cout << "Manifest generated: " << config.manifestPath.toStdString() << endl; return 0; } // ── Command: checksum verify (async) ───────────────────────────── static int handleChecksumVerify(const BurnCliConfig &config, QCoreApplication &app) { DOpticalDiscManager manager(config.device); int exitCode = 1; QObject::connect(&manager, &DOpticalDiscManager::jobStatusChanged, [&exitCode, &app](JobStatus status, int progress, const QString &speed, const QStringList &message) { Q_UNUSED(speed); if (status == JobStatus::kRunning) { cout << "\rVerifying... " << progress << "%"; if (!message.isEmpty()) cout << " (" << message.first().toStdString() << ")"; cout << flush; } else if (status == JobStatus::kStalled) { cout << "\rVerifying... Stalled (waiting for drive)." << flush; } else if (status == JobStatus::kFinished) { cout << "\rVerifying... Done. " << endl; cout << "All checksums matched." << endl; exitCode = 0; app.quit(); } else if (status == JobStatus::kFailed) { cerr << endl << "Verification failed: " << message.join(" ").toStdString() << endl; exitCode = 1; app.quit(); } }); if (!manager.verifyChecksum(config.manifestPath)) { cerr << "Error: " << manager.lastError().toStdString() << endl; return 1; } return app.exec(); } // ── Packet Writing Commands (sync) ───────────────────────────── static int handlePwOpen(const BurnCliConfig &config) { DPacketWritingController controller(config.device, config.workingPath); if (!controller.open()) { cerr << "Error: Failed to open packet writing session." << endl; cerr << " " << controller.lastError().toStdString() << endl; return 1; } cout << "Packet writing session opened." << endl; cout << " Device: " << config.device.toStdString() << endl; cout << " Working: " << config.workingPath.toStdString() << endl; return 0; } static int handlePwClose(const BurnCliConfig &config) { DPacketWritingController controller(config.device, config.workingPath); controller.close(); cout << "Packet writing session closed." << endl; return 0; } static int handlePwPut(const BurnCliConfig &config) { DPacketWritingController controller(config.device, config.workingPath); if (!controller.put(config.pwFileName)) { cerr << "Error: Failed to add file." << endl; cerr << " " << controller.lastError().toStdString() << endl; return 1; } cout << "Added: " << config.pwFileName.toStdString() << endl; return 0; } static int handlePwMv(const BurnCliConfig &config) { DPacketWritingController controller(config.device, config.workingPath); if (!controller.mv(config.pwSrcName, config.pwDestName)) { cerr << "Error: Failed to rename." << endl; cerr << " " << controller.lastError().toStdString() << endl; return 1; } cout << "Renamed: " << config.pwSrcName.toStdString() << " -> " << config.pwDestName.toStdString() << endl; return 0; } static int handlePwRm(const BurnCliConfig &config) { DPacketWritingController controller(config.device, config.workingPath); if (!controller.rm(config.pwFileName)) { cerr << "Error: Failed to remove." << endl; cerr << " " << controller.lastError().toStdString() << endl; return 1; } cout << "Removed: " << config.pwFileName.toStdString() << endl; return 0; } // ── Entry point ──────────────────────────────────────────────── int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); app.setApplicationName("dfm-burner"); app.setApplicationVersion("1.0.0"); CliOptions cli; BurnCliConfig config; if (!cli.parse(argc, argv, config)) return 1; switch (config.command) { case BurnCommand::Info: return handleInfo(config); case BurnCommand::Burn: return handleBurn(config, app); case BurnCommand::WriteISO: return handleWriteISO(config, app); case BurnCommand::DumpISO: return handleDumpISO(config, app); case BurnCommand::Erase: return handleErase(config, app); case BurnCommand::Check: return handleCheck(config, app); case BurnCommand::ChecksumGen: return handleChecksumGen(config); case BurnCommand::ChecksumVerify: return handleChecksumVerify(config, app); case BurnCommand::PwOpen: return handlePwOpen(config); case BurnCommand::PwClose: return handlePwClose(config); case BurnCommand::PwPut: return handlePwPut(config); case BurnCommand::PwMv: return handlePwMv(config); case BurnCommand::PwRm: return handlePwRm(config); default: return 1; } } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/000077500000000000000000000000001523477242100226725ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/CMakeLists.txt000066400000000000000000000010651523477242100254340ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) set (VERSION "1.0.0" CACHE STRING "define project version") set(BASE_NAME dfm-burn) # public include file(GLOB_RECURSE PUBLIC_INCLUDES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/include/${BASE_NAME}/*") # udfclient FILE (GLOB_RECURSE UDFCLIENT_SRCS CONFIGURE_DEPENDS "../3rdparty/udfclient/*.c" "../3rdparty/udfclient/*.h" ) # src FILE (GLOB_RECURSE SRCS CONFIGURE_DEPENDS "./*.cpp" "./*.h" ) if (QT_VERSION_MAJOR MATCHES 6) include(dfm-burn-qt6.cmake) else() include(dfm-burn-qt5.cmake) endif() linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/dfm-burn-qt5.cmake000066400000000000000000000001431523477242100261130ustar00rootroot00000000000000set(BIN_NAME dfm-burn) set(QT_VERSION_MAJOR 5) set(DFM_VERSION_MAJOR "") include(dfm-burn.cmake) linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/dfm-burn-qt6.cmake000066400000000000000000000001431523477242100261140ustar00rootroot00000000000000set(BIN_NAME dfm6-burn) set(QT_VERSION_MAJOR 6) set(DFM_VERSION_MAJOR 6) include(dfm-burn.cmake) linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/dfm-burn.cmake000066400000000000000000000036641523477242100254170ustar00rootroot00000000000000# Setup the environment find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) find_package(PkgConfig REQUIRED) find_package(OpenSSL REQUIRED COMPONENTS Crypto) pkg_check_modules(isoburn REQUIRED libisoburn-1 IMPORTED_TARGET) # Build add_library(${BIN_NAME} SHARED ${PUBLIC_INCLUDES} ${SRCS} ${UDFCLIENT_SRCS} ) target_link_libraries(${BIN_NAME} Qt${QT_VERSION_MAJOR}::Core PkgConfig::isoburn OpenSSL::Crypto ) target_include_directories( ${BIN_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include/${BASE_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/.. ) if (NOT VERSION) set(VERSION "1.0.0") endif() set_target_properties( ${BIN_NAME} PROPERTIES VERSION ${VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) # for udfclient target_compile_definitions(${BIN_NAME} PRIVATE NEEDS_ISPRINT _DEFAULT_SOURCE _BSD_SOURCE _XOPEN_SOURCE=500 __USE_BSD _FILE_OFFSET_BITS=64 NO_INT_FMTIO NO_STRLCPY st_atimespec=st_atim st_ctimespec=st_ctim st_mtimespec=st_mtim NO_STAT_BIRTHTIME NO_DIRENT_NAMLEN SCSI USCSI_LINUX_SCSI ) # Install install(TARGETS ${BIN_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) # install headers install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/${BASE_NAME}/${BASE_NAME} DESTINATION include/${BIN_NAME} FILES_MATCHING PATTERN "*.h" ) # for pc file config set(PC_LIBS_PRIVATE Qt${QT_VERSION_MAJOR}Core) set(PC_REQ_PRIVATE) set(PC_REQ_PUBLIC) # config pkgconfig file configure_file(${PROJECT_SOURCE_DIR}/misc/${BASE_NAME}/${BASE_NAME}.pc.in ${BIN_NAME}.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BIN_NAME}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) # config cmake file configure_file(${PROJECT_SOURCE_DIR}/misc/${BASE_NAME}/${BASE_NAME}Config.cmake.in ${BIN_NAME}Config.cmake @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BIN_NAME}Config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${BIN_NAME}) linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/dopticaldiscinfo.cpp000066400000000000000000000073321523477242100267210ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include "private/dopticaldiscinfo_p.h" #include "private/scsicommandhelper.h" #include DFM_BURN_USE_NS DOpticalDiscInfo::DOpticalDiscInfo() : d_ptr(new DOpticalDiscInfoPrivate()) { } DOpticalDiscInfo::DOpticalDiscInfo(const QString &dev) : d_ptr(new DOpticalDiscInfoPrivate(dev)) { } DOpticalDiscInfo::~DOpticalDiscInfo() { } DOpticalDiscInfo::DOpticalDiscInfo(const DOpticalDiscInfo &info) : d_ptr(info.d_ptr) { } DOpticalDiscInfo &DOpticalDiscInfo::operator=(const DOpticalDiscInfo &info) { d_ptr = info.d_ptr; return *this; } bool DOpticalDiscInfo::blank() const { return d_ptr->formatted; } QString DOpticalDiscInfo::device() const { return d_ptr->devid; } QString DOpticalDiscInfo::volumeName() const { return d_ptr->volid; } quint64 DOpticalDiscInfo::usedSize() const { return d_ptr->data; } quint64 DOpticalDiscInfo::availableSize() const { return d_ptr->avail; } quint64 DOpticalDiscInfo::totalSize() const { return d_ptr->total; } quint64 DOpticalDiscInfo::dataBlocks() const { return d_ptr->datablocks; } dfmburn::MediaType DOpticalDiscInfo::mediaType() const { return d_ptr->media; } QStringList DOpticalDiscInfo::writeSpeed() const { return d_ptr->writespeed; } void DOpticalDiscInfoPrivate::initData() { if (!isoEngine->acquireDevice(devid)) { qWarning() << "[dfm-burn]: Init data failed, cannot acquire device"; devid = ""; return; } media = isoEngine->mediaTypeProperty(); isoEngine->mediaStorageProperty(&data, &avail, &datablocks); total = data + avail; // 如果是没有 iso 镜像树的 dvd-rw 光盘,则可能是 udf 文件系统,udf 文件系统无法通过 xorriso 查询容量 if (media == MediaType::kDVD_RW && !isoEngine->hasISOTree()) { auto capacity { acquireDVDRWCapacity() }; if (capacity != 0) { total = capacity; } } formatted = isoEngine->mediaFormattedProperty(); volid = isoEngine->mediaVolIdProperty(); writespeed = isoEngine->mediaSpeedProperty(); isoEngine->clearResult(); isoEngine->releaseDevice(); } quint64 DOpticalDiscInfoPrivate::acquireDVDRWCapacity() const { quint64 totalSize { 0 }; ScsiCommandHelper cmd { devid }; unsigned char formats[260] { 0 }; cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[8] = 12; cmd[9] = 0; if (!cmd.transport(ScsiCommandHelper::kREAD, formats, 12)) { qWarning() << "cannot read dvd-rw capacity"; return totalSize; } int len { formats[3] }; if (len & 7 || len < 16) { qWarning() << "allocation length isn't sane:" << len; return totalSize; } cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[7] = (4 + len) >> 8; // now with real length... cmd[8] = (4 + len) & 0xFF; cmd[9] = 0; if (!cmd.transport(ScsiCommandHelper::kREAD, formats, static_cast(4 + len))) { qWarning() << "cannot read format capacities"; return totalSize; } if (len != formats[3]) { qWarning() << "parameter length inconsistency"; return totalSize; } if (len != formats[3]) { qWarning() << "arameter length inconsistency"; return totalSize; } int i { 12 }; Q_ASSERT(i < len); quint64 blocksize { static_cast(formats[9] << 16 | formats[10] << 8 | formats[11]) }; quint64 capacity { static_cast( formats[i] << 24 | formats[i + 1] << 16 | formats[i + 2] << 8 | formats[i + 3]) }; totalSize = blocksize * capacity; return totalSize; } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/dopticaldiscmanager.cpp000066400000000000000000000373621523477242100274060ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "private/dopticaldiscmanager_p.h" #include "private/dxorrisoengine.h" #include "private/dudfburnengine.h" #include "private/dvdrwformatengine.h" #include "private/dsm3hash.h" #include #include #include #include #include #include #include #include #include DFM_BURN_USE_NS static const QString kExtractCacheSubDir = "discburn"; static QString extractCacheDir(const QString &dev) { QString safeDev = dev; safeDev.replace("/", "_"); // Remove leading underscore caused by the leading "/" in device path if (safeDev.startsWith("_")) safeDev.remove(0, 1); return QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/deepin/" + kExtractCacheSubDir + "/extract_" + safeDev; } // xorriso extracts files with read-only permissions (dr-xr-xr-x), // so we must chmod recursively before removal. static void cleanupExtractCache(QDir dir) { if (!dir.exists()) return; QDirIterator it(dir.absolutePath(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (it.hasNext()) { QString path = it.next(); QFile::setPermissions(path, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup | QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther); } dir.removeRecursively(); } DOpticalDiscManager::DOpticalDiscManager(const QString &dev, QObject *parent) : QObject(parent), dptr(new DOpticalDiscManagerPrivate) { dptr->curDev = dev; } DOpticalDiscManager::~DOpticalDiscManager() { } bool DOpticalDiscManager::setStageFile(const QString &diskPath, const QString &isoPath) { QUrl diskUrl { diskPath }; QUrl isoUrl { isoPath }; if (diskUrl.isEmpty() || !diskUrl.isValid()) { dptr->errorMsg = "Invalid disk path"; return false; } if (isoUrl.isEmpty() || !isoUrl.isValid()) { dptr->errorMsg = "Invalid iso path"; return false; } dptr->files.first = diskPath; dptr->files.second = isoPath; return true; } /*! * \brief DISOMaster::commit Burn all staged files to the disc. * \param opts burning options * \param speed desired writing speed in kilobytes per second * \param volId volume name of the disc * \return true on success, false on failure */ bool DOpticalDiscManager::commit(const BurnOptions &opts, int speed, const QString &volId) { bool ret { false }; if (opts.testFlag(BurnOption::kUDF102Supported)) { QScopedPointer udfEngine { new DUDFBurnEngine }; connect( udfEngine.data(), &DUDFBurnEngine::jobStatusChanged, this, [this, ptr = QPointer(udfEngine.data())](JobStatus status, int progress) { if (ptr) { if (status == JobStatus::kFailed) Q_EMIT jobStatusChanged(status, progress, {}, ptr->lastErrorMessage()); else Q_EMIT jobStatusChanged(status, progress, {}, {}); } }, Qt::DirectConnection); ret = udfEngine->doBurn(dptr->curDev, dptr->files, volId, opts); } else { QScopedPointer xorrisoEngine { new DXorrisoEngine }; connect( xorrisoEngine.data(), &DXorrisoEngine::jobStatusChanged, this, [this, ptr = QPointer(xorrisoEngine.data())](JobStatus status, int progress, QString speed) { if (ptr) Q_EMIT jobStatusChanged(status, progress, speed, ptr->takeInfoMessages()); }, Qt::DirectConnection); if (!xorrisoEngine->acquireDevice(dptr->curDev)) qWarning() << "[dfm-burn] Cannot acquire device"; using XJolietSupport = DXorrisoEngine::JolietSupport; using XRockRageSupport = DXorrisoEngine::RockRageSupport; using XKeepAppendable = DXorrisoEngine::KeepAppendable; XJolietSupport joliet = opts.testFlag(BurnOption::kJolietSupport) ? XJolietSupport::kTrue : XJolietSupport::kFalse; XRockRageSupport rockRage = opts.testFlag(BurnOption::kRockRidgeSupport) ? XRockRageSupport::kTrue : XRockRageSupport::kFalse; XKeepAppendable keepAppendable = opts.testFlag(BurnOption::kKeepAppendable) ? XKeepAppendable::kTrue : XKeepAppendable::kFalse; ret = xorrisoEngine->doBurn(dptr->files, speed, volId, joliet, rockRage, keepAppendable); xorrisoEngine->releaseDevice(); } return ret; } bool DOpticalDiscManager::erase() { bool ret { false }; // 查询介质类型 MediaType mediaType { MediaType::kNoMedia }; { QScopedPointer info { DOpticalDiscManager::createOpticalInfo(dptr->curDev) }; if (info) mediaType = info->mediaType(); } // DVD±RW 且 dvd+rw-format 可用时走新引擎 bool isDvdRw = (mediaType == MediaType::kDVD_PLUS_RW || mediaType == MediaType::kDVD_RW); QString dvdRwFormat = QStandardPaths::findExecutable("dvd+rw-format"); if (isDvdRw && !dvdRwFormat.isEmpty()) { QScopedPointer engine { new DVDRwFormatEngine }; connect( engine.data(), &DVDRwFormatEngine::jobStatusChanged, this, [this](JobStatus status, int progress) { Q_EMIT jobStatusChanged(status, progress, {}, {}); }, Qt::DirectConnection); ret = engine->doErase(dptr->curDev, mediaType); return ret; } // 降级:走 xorriso 原路径 if (isDvdRw) qWarning() << "[dfm-burn] dvd+rw-format not found, falling back to xorriso for DVD±RW erase"; QScopedPointer engine { new DXorrisoEngine }; connect( engine.data(), &DXorrisoEngine::jobStatusChanged, this, [this, ptr = QPointer(engine.data())](JobStatus status, int progress, QString speed) { if (ptr) Q_EMIT jobStatusChanged(status, progress, speed, ptr->takeInfoMessages()); }, Qt::DirectConnection); if (!engine->acquireDevice(dptr->curDev)) qWarning() << "[dfm-burn] Cannot acquire device"; ret = engine->doErase(); engine->releaseDevice(); return ret; } bool DOpticalDiscManager::checkmedia(double *qgood, double *qslow, double *qbad) { bool ret { false }; quint64 blocks { 0 }; { QScopedPointer info { DOpticalDiscManager::createOpticalInfo(dptr->curDev) }; if (!info) return ret; blocks = info->dataBlocks(); } QScopedPointer engine { new DXorrisoEngine }; connect( engine.data(), &DXorrisoEngine::jobStatusChanged, this, [this, ptr = QPointer(engine.data())](JobStatus status, int progress, QString speed) { if (ptr) Q_EMIT jobStatusChanged(status, progress, speed, ptr->takeInfoMessages()); }, Qt::DirectConnection); if (!engine->acquireDevice(dptr->curDev)) qWarning() << "[dfm-burn] Cannot acquire device"; ret = engine->doCheckmedia(blocks, qgood, qslow, qbad); engine->releaseDevice(); return ret; } bool DOpticalDiscManager::writeISO(const QString &isoPath, int speed) { bool ret { false }; QScopedPointer engine { new DXorrisoEngine }; connect( engine.data(), &DXorrisoEngine::jobStatusChanged, this, [this, ptr = QPointer(engine.data())](JobStatus status, int progress, QString speed) { if (ptr) Q_EMIT jobStatusChanged(status, progress, speed, ptr->takeInfoMessages()); }, Qt::DirectConnection); if (!engine->acquireDevice(dptr->curDev)) qWarning() << "[dfm-burn] Cannot acquire device"; if (QUrl(isoPath).isEmpty() || !QUrl(isoPath).isValid()) { dptr->errorMsg = QString("[dfm-burn]: Invalid path: %1 ").arg(isoPath); return ret; } ret = engine->doWriteISO(isoPath, speed); engine->releaseDevice(); return ret; } bool DOpticalDiscManager::dumpISO(const QString &isoPath) { bool ret { false }; quint64 blocks { 0 }; { QScopedPointer info { DOpticalDiscManager::createOpticalInfo(dptr->curDev) }; if (!info) return ret; blocks = info->dataBlocks(); } QScopedPointer engine { new DXorrisoEngine }; connect( engine.data(), &DXorrisoEngine::jobStatusChanged, this, [this, ptr = QPointer(engine.data())](JobStatus status, int progress, QString speed) { if (ptr) emit jobStatusChanged(status, progress, speed, ptr->takeInfoMessages()); }, Qt::DirectConnection); if (!engine->acquireDevice(dptr->curDev)) qWarning() << "[dfm-burn] Cannot acquire device"; if (QUrl(isoPath).isEmpty() || !QUrl(isoPath).isValid()) { dptr->errorMsg = QString("[dfm-burn]: Invalid path: %1 ").arg(isoPath); return ret; } ret = engine->doDumpISO(blocks, isoPath); engine->releaseDevice(); return ret; } bool DOpticalDiscManager::generateChecksumManifest(const QString &savePath) { QString srcPath = dptr->files.first; if (srcPath.isEmpty()) { dptr->errorMsg = "No staged file for checksum generation"; return false; } QFileInfo srcInfo(srcPath); if (!srcInfo.exists()) { dptr->errorMsg = QString("Source path does not exist: %1").arg(srcPath); return false; } QString isoBase = dptr->files.second; if (isoBase.isEmpty()) isoBase = "/"; // Normalize isoBase: ensure it ends with / if (!isoBase.endsWith("/")) isoBase += "/"; QJsonObject filesObj; if (srcInfo.isDir()) { QDirIterator it(srcPath, QDir::Files | QDir::Hidden, QDirIterator::Subdirectories); while (it.hasNext()) { QString filePath = it.next(); QString relPath = QDir(srcPath).relativeFilePath(filePath); QString isoRelPath = isoBase + relPath; QString hash = DSM3Hash::sm3File(filePath); if (hash.isEmpty()) { dptr->errorMsg = QString("Failed to compute SM3 for: %1").arg(filePath); return false; } filesObj[isoRelPath] = hash; } } else { QString hash = DSM3Hash::sm3File(srcPath); if (hash.isEmpty()) { dptr->errorMsg = QString("Failed to compute SM3 for: %1").arg(srcPath); return false; } filesObj[isoBase + srcInfo.fileName()] = hash; } if (filesObj.isEmpty()) { dptr->errorMsg = "No files found to checksum"; return false; } QJsonObject root; root["algorithm"] = "sm3"; root["device"] = dptr->curDev; root["files"] = filesObj; QFile outFile(savePath); if (!outFile.open(QIODevice::WriteOnly)) { dptr->errorMsg = QString("Cannot write manifest to: %1").arg(savePath); return false; } outFile.write(QJsonDocument(root).toJson(QJsonDocument::Indented)); return true; } bool DOpticalDiscManager::verifyChecksum(const QString &manifestPath) { QFile mf(manifestPath); if (!mf.open(QIODevice::ReadOnly)) { dptr->errorMsg = QString("Cannot read manifest: %1").arg(manifestPath); return false; } QJsonParseError parseErr; QJsonObject root = QJsonDocument::fromJson(mf.readAll(), &parseErr).object(); if (root.isEmpty()) { dptr->errorMsg = QString("Invalid manifest JSON: %1").arg(parseErr.errorString()); return false; } QJsonObject filesObj = root["files"].toObject(); if (filesObj.isEmpty()) { dptr->errorMsg = "Manifest contains no file entries"; return false; } // Prepare temp directory for per-file extraction QString cacheBase = extractCacheDir(dptr->curDev); QDir cacheDir(cacheBase); if (cacheDir.exists()) cleanupExtractCache(cacheDir); if (!cacheDir.mkpath(".")) { dptr->errorMsg = QString("Cannot create cache directory: %1").arg(cacheBase); return false; } // Acquire device and enable osirrox once for all per-file extractions QScopedPointer engine { new DXorrisoEngine }; connect( engine.data(), &DXorrisoEngine::jobStatusChanged, this, [this, ptr = QPointer(engine.data())](JobStatus status, int progress, QString speed) { if (ptr) Q_EMIT jobStatusChanged(status, progress, speed, ptr->takeInfoMessages()); }, Qt::DirectConnection); if (!engine->acquireDevice(dptr->curDev)) { dptr->errorMsg = "Cannot acquire device for verification"; cleanupExtractCache(cacheDir); return false; } if (!engine->doOsirroxOn()) { dptr->errorMsg = QString("Failed to enable osirrox: %1").arg(engine->takeInfoMessages().join(" ")); engine->releaseDevice(); cleanupExtractCache(cacheDir); return false; } // Extract and verify each file individually: // extract → compute SM3 → delete → next file // This avoids extracting the entire disc (multi-session friendly) // and minimizes disk usage (only one file at a time). QStringList isoPaths = filesObj.keys(); int total = isoPaths.size(); int checked = 0; for (const auto &isoPath : isoPaths) { QString localTmp = cacheBase + isoPath; // Ensure parent directory exists QDir().mkpath(QFileInfo(localTmp).absolutePath()); if (!engine->doExtract(localTmp, isoPath)) { dptr->errorMsg = QString("Failed to extract '%1' from disc: %2") .arg(isoPath, engine->takeInfoMessages().join(" ")); engine->releaseDevice(); cleanupExtractCache(cacheDir); return false; } QString expectedHash = filesObj[isoPath].toString(); QString actualHash = DSM3Hash::sm3File(localTmp); // Delete immediately to free disk space and avoid permission issues on cleanup QFile::remove(localTmp); ++checked; int pct = (total > 0) ? (100 * checked / total) : 0; Q_EMIT jobStatusChanged(JobStatus::kRunning, pct, {}, { isoPath }); if (actualHash.isEmpty()) { dptr->errorMsg = QString("Checksum mismatch: file not found on disc: %1").arg(isoPath); engine->releaseDevice(); cleanupExtractCache(cacheDir); return false; } if (actualHash != expectedHash) { dptr->errorMsg = QString("Checksum mismatch: %1 (expected %2, got %3)") .arg(isoPath, expectedHash, actualHash); engine->releaseDevice(); cleanupExtractCache(cacheDir); return false; } } engine->releaseDevice(); Q_EMIT jobStatusChanged(JobStatus::kFinished, 100, {}, {}); // Clean up cache directory structure cleanupExtractCache(cacheDir); return true; } QString DOpticalDiscManager::lastError() const { return dptr->errorMsg; } DOpticalDiscInfo *DOpticalDiscManager::createOpticalInfo(const QString &dev) { auto info = new DOpticalDiscInfo(dev); if (info && info->device().isEmpty()) { delete info; return nullptr; } return info; } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/dpacketwritingcontroller.cpp000066400000000000000000000266051523477242100305320ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include "private/dpacketwritingcontroller_p.h" extern "C" { #include "3rdparty/udfclient/udf.h" #include "3rdparty/udfclient/udf_bswap.h" } #include #include #include #include extern "C" { extern struct curdir { char *name; struct udf_mountpoint *mountpoint; /* foreign */ struct udf_node *udf_node; /* foreign */ struct hash_entry *udf_mountpoint_entry; /* `current' mountpoint entry */ } curdir; extern void udfclient_pwd(int args); extern void udfclient_lcd(int args, char *arg1); extern void udfclient_cd(int args, char *arg1); extern char *udfclient_realpath(char *cur_path, char *relpath, char **leaf); extern int udfclient_lookup_pathname(struct udf_node *cur_node, struct udf_node **res_node, char *restpath_given); extern int udfclient_getattr(struct udf_node *udf_node, struct stat *stat); extern int udfclient_readdir(struct udf_node *udf_node, struct uio *result_uio, int *eof_res); extern int udfclient_put_subtree(struct udf_node *parent_node, char *srcprefix, char *srcname, char *dstprefix, char *dstname, uint64_t *totalsize); extern int udfclient_rm_subtree(struct udf_node *parent_node, struct udf_node *dir_node, char *name, char *full_parent_name); } #define LS_SUBTREE_DIR_BUFFER_SIZE (16 * 1024) DFM_BURN_BEGIN_NS DPacketWritingControllerPrivate::DPacketWritingControllerPrivate() { char pwd[1024]; ::getcwd(pwd, 1024); oldLocalWoringPath = QString::fromLocal8Bit(pwd); } bool DPacketWritingControllerPrivate::initCurrentDir() { bzero(&curdir, sizeof(struct curdir)); curdir.mountpoint = NULL; curdir.name = strdup("/"); // disc cd const QString &rootPath { makeDiscRootPath() }; if (rootPath.isEmpty()) { qWarning() << "Make dsic root path failed!"; return false; } cd(rootPath); // local cd lcd(localWorkingPath); return true; } void DPacketWritingControllerPrivate::lcd(const QString &path) { QFileInfo info { path }; if (info.exists() && info.isDir()) udfclient_lcd(1, path.toLocal8Bit().data()); } void DPacketWritingControllerPrivate::cd(const QString &path) { udfclient_cd(1, path.toLocal8Bit().data()); } QString DPacketWritingControllerPrivate::makeDiscRootPath() { struct udf_node *udf_node; uint8_t *buffer; struct uio dir_uio; struct iovec dir_uiovec; struct dirent *dirent; struct stat stat; int eof; char *node_name, *leaf_name; int error; QString path; node_name = udfclient_realpath(curdir.name, "", &leaf_name); error = udfclient_lookup_pathname(NULL, &udf_node, node_name); if (error) { fprintf(stderr, "%s\n", strerror(error)); free(node_name); return path; } error = udfclient_getattr(udf_node, &stat); if (stat.st_mode & S_IFDIR) { /* start at the start of the directory */ dir_uio.uio_offset = 0; dir_uio.uio_iov = &dir_uiovec; dir_uio.uio_iovcnt = 1; buffer = (uint8_t *)calloc(1, LS_SUBTREE_DIR_BUFFER_SIZE); if (!buffer) return path; do { dir_uiovec.iov_base = buffer; dir_uiovec.iov_len = LS_SUBTREE_DIR_BUFFER_SIZE; dir_uio.uio_resid = LS_SUBTREE_DIR_BUFFER_SIZE; dir_uio.uio_rw = UIO_WRITE; error = udfclient_readdir(udf_node, &dir_uio, &eof); if (error) { fprintf(stderr, "error during readdir: %s\n", strerror(error)); break; } dirent = (struct dirent *)(buffer); path = QString::fromLocal8Bit(dirent->d_name); } while (0); free(buffer); } free(node_name); return path; } int64_t DPacketWritingControllerPrivate::getmtime() { struct timeval tp; gettimeofday(&tp, NULL); return 1000000 * tp.tv_sec + tp.tv_usec; } DPacketWritingController::DPacketWritingController(const QString &dev, const QString &workingPath, QObject *parent) : QObject(parent), dptr(new DPacketWritingControllerPrivate) { dptr->deviceName = dev; dptr->localWorkingPath = workingPath; } DPacketWritingController::~DPacketWritingController() { close(); } bool DPacketWritingController::open() { Q_ASSERT(!dptr->deviceName.isEmpty()); qInfo() << "Opening device:" << dptr->deviceName; QFileInfo local { dptr->localWorkingPath }; if (!local.exists() || !local.isDir()) { dptr->errorMsg = QString("Invalid local working directory: %1").arg(dptr->localWorkingPath); return false; } udf_init(); struct udf_discinfo *disc; int mnt_flags { UDF_MNT_RDONLY }; mnt_flags &= ~UDF_MNT_RDONLY; mnt_flags |= UDF_MNT_FORCE; int error = udf_mount_disc(dptr->deviceName.toLocal8Bit().data(), NULL, 0, mnt_flags, &disc); if (error) { fprintf(stderr, "Can't open my device; bailing out : %s\n", strerror(error)); dptr->errorMsg = QString::fromLocal8Bit(strerror(error)); return false; } if (!dptr->initCurrentDir()) { dptr->errorMsg = "Init dir faield"; return false; } dptr->deviceOpended = true; return true; } void DPacketWritingController::close() { if (!isOpen()) return; dptr->deviceOpended = false; struct udf_discinfo *disc, *next_disc; qInfo() << "Closing discs"; disc = SLIST_FIRST(&udf_discs_list); while (disc) { next_disc = SLIST_NEXT(disc, next_disc); udf_dismount_disc(disc); disc = next_disc; } free(udf_bufcache); udf_bufcache = NULL; qInfo() << "Restore local working path:" << dptr->oldLocalWoringPath; dptr->lcd(dptr->oldLocalWoringPath); udfclient_pwd(0); } bool DPacketWritingController::put(const QString &fileName) { Q_ASSERT(!fileName.isNull()); struct udf_node *curdir_node; uint64_t start, now, totalsize, avg_speed; int error { 0 }; error = udfclient_lookup_pathname(NULL, &curdir_node, curdir.name); if (error) { dptr->errorMsg = "Current directory not found"; return false; } qDebug() << "Attempting to copy: " << fileName; /* writeout file/dir tree and measure the time/speed */ totalsize = 0; start = dptr->getmtime(); error = udfclient_put_subtree(curdir_node, ".", fileName.toLocal8Bit().data(), ".", fileName.toLocal8Bit().data(), &totalsize); if (error) { dptr->errorMsg = QString::fromLocal8Bit(strerror(error)); return false; } now = dptr->getmtime(); if (now - start > 0) { avg_speed = (1000000 * totalsize) / (now - start); qDebug() << "A total of" << (uint32_t)(totalsize / 1024) << "kb transfered at an overal average of" << (uint32_t)(avg_speed / 1024) << "kb/sec"; } else { qDebug() << "Transfered" << (uint32_t)(totalsize / 1024) << "kb"; } return true; } /*! * \brief just rename now */ bool DPacketWritingController::mv(const QString &srcName, const QString &destName) { struct udf_node *rename_me, *present, *old_parent, *new_parent; char *rename_from_name, *rename_to_name, *old_parent_name, *new_parent_name; int error; char *from = strdup(srcName.toLocal8Bit().data()); char *from_bak = from; /* `from' gets substituted by its leaf name */ rename_from_name = udfclient_realpath(curdir.name, from, &from); error = udfclient_lookup_pathname(NULL, &rename_me, rename_from_name); if (error || !rename_me) { dptr->errorMsg = "Can't find file/dir to be renamed"; free(rename_from_name); free(from_bak); return false; } old_parent_name = udfclient_realpath(rename_from_name, "..", NULL); error = udfclient_lookup_pathname(NULL, &old_parent, old_parent_name); if (error || !old_parent) { dptr->errorMsg = "Can't determine rootdir of renamed file?"; free(rename_from_name); free(old_parent_name); free(from_bak); return false; } char *to = strdup(destName.toLocal8Bit().data()); char *to_bak = to; /* `to' gets substituted by its leaf name */ rename_to_name = udfclient_realpath(curdir.name, to, &to); udfclient_lookup_pathname(NULL, &present, rename_to_name); new_parent_name = udfclient_realpath(rename_to_name, "..", NULL); error = udfclient_lookup_pathname(NULL, &new_parent, new_parent_name); if (error || !new_parent) { dptr->errorMsg = "Can't determine rootdir of destination"; free(rename_from_name); free(rename_to_name); free(old_parent_name); free(new_parent_name); free(from_bak); free(to_bak); return false; } error = udf_rename(old_parent, rename_me, from, new_parent, present, to); if (error) dptr->errorMsg = QString("Can't move file or directory: %1").arg(strerror(error)); free(rename_from_name); free(rename_to_name); free(old_parent_name); free(new_parent_name); free(from_bak); free(to_bak); return error ? false : true; } bool DPacketWritingController::rm(const QString &fileName) { Q_ASSERT(!fileName.isNull()); struct udf_node *remove_node, *parent_node; struct stat stat; char *target_name, *leaf_name, *full_parent_name; int error, len; leaf_name = strdup(fileName.toLocal8Bit().data()); char *leaf_name_bak = leaf_name; target_name = udfclient_realpath(curdir.name, leaf_name, &leaf_name); error = udfclient_lookup_pathname(NULL, &remove_node, target_name); if (error || !remove_node) { printf("rm %s : %s\n", target_name, strerror(error)); dptr->errorMsg = QString("rm %1 : %2").arg(target_name).arg(strerror(error)); free(target_name); free(leaf_name_bak); return false; } full_parent_name = udfclient_realpath(target_name, "..", NULL); error = udfclient_lookup_pathname(NULL, &parent_node, full_parent_name); if (error || !parent_node) { dptr->errorMsg = QString("rm %1 : parent lookup failed : %2").arg(target_name).arg(strerror(error)); free(target_name); free(full_parent_name); free(leaf_name_bak); return false; } error = udfclient_getattr(remove_node, &stat); if (!error) { if (stat.st_mode & S_IFDIR) { len = strlen(target_name); if (target_name[len - 1] == '/') target_name[len - 1] = '\0'; error = udfclient_rm_subtree(parent_node, remove_node, leaf_name, target_name); } else { error = udf_remove_file(parent_node, remove_node, leaf_name); if (!error) qDebug() << "rm " << full_parent_name << "/" << leaf_name; } } if (error) { fprintf(stderr, "While removing file/dir : %s\n", strerror(error)); dptr->errorMsg = QString("rm failed: %1").arg(strerror(error)); } free(target_name); free(full_parent_name); free(leaf_name_bak); if (error) return false; return true; } QString DPacketWritingController::device() const { return dptr->deviceName; } QString DPacketWritingController::localWorkingDirectory() const { return dptr->localWorkingPath; } QString DPacketWritingController::lastError() const { return dptr->errorMsg; } bool DPacketWritingController::isOpen() const { return dptr->deviceOpended; } DFM_BURN_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/000077500000000000000000000000001523477242100243445ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dopticaldiscinfo_p.h000066400000000000000000000031621523477242100303540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef OPTICALDISCINFO_P_H #define OPTICALDISCINFO_P_H #include #include "dxorrisoengine.h" #include #include DFM_BURN_BEGIN_NS class DOpticalDiscInfoPrivate : public QSharedData { public: inline DOpticalDiscInfoPrivate() : QSharedData() { } inline explicit DOpticalDiscInfoPrivate(const QString &dev) : isoEngine(std::make_unique()), devid(dev) { initData(); } inline DOpticalDiscInfoPrivate(const DOpticalDiscInfoPrivate ©) : QSharedData(copy) { } inline ~DOpticalDiscInfoPrivate() {} void initData(); quint64 acquireDVDRWCapacity() const; std::unique_ptr const isoEngine; /** \brief True when the media in device is blank or false otherwise.*/ bool formatted {}; /** \brief Type of media currently in the device.*/ MediaType media; /** \brief Size of on-disc data in bytes.*/ quint64 data {}; /** \brief Available size in bytes.*/ quint64 avail {}; /** \brief Total size in bytes */ quint64 total {}; /** \brief Size of on-disc data in number of blocks.*/ quint64 datablocks {}; /** \brief List of write speeds supported, e.g. "10.0x".*/ QStringList writespeed {}; /** \brief Device identifier. Empty if the device property is invalid.*/ QString devid {}; /** \brief Volume name of the disc.*/ QString volid {}; }; DFM_BURN_END_NS #endif // OPTICALDISCINFO_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dopticaldiscmanager_p.h000066400000000000000000000007571523477242100310420ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef OPTICALDISCMANAGER_P_H #define OPTICALDISCMANAGER_P_H #include #include DFM_BURN_BEGIN_NS class DOpticalDiscManagerPrivate { public: QString errorMsg; QString curDev; QPair files; // first: local disk path, second: optical disk path }; DFM_BURN_END_NS #endif // OPTICALDISCMANAGER_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dpacketwritingcontroller_p.h000066400000000000000000000013061523477242100321570ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DPACKETWRITINGCONTROLLER_P_H #define DPACKETWRITINGCONTROLLER_P_H #include DFM_BURN_BEGIN_NS class DPacketWritingControllerPrivate { public: DPacketWritingControllerPrivate(); bool initCurrentDir(); void lcd(const QString &path); void cd(const QString &path); QString makeDiscRootPath(); int64_t getmtime(); public: bool deviceOpended { false }; QString errorMsg; QString deviceName; QString oldLocalWoringPath; QString localWorkingPath; }; DFM_BURN_END_NS #endif // DPACKETWRITINGCONTROLLER_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dsm3hash.cpp000066400000000000000000000030011523477242100265540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "dsm3hash.h" #include #include DFM_BURN_USE_NS static constexpr int kSM3HashLen = 32; // SM3 produces 256-bit (32-byte) hash static constexpr int kBufferSize = 8192; static QString toHex(const unsigned char *data, int len) { QString result; result.reserve(len * 2); static const char kHex[] = "0123456789abcdef"; for (int i = 0; i < len; ++i) { result.append(kHex[(data[i] >> 4) & 0x0F]); result.append(kHex[data[i] & 0x0F]); } return result; } QString DSM3Hash::sm3File(const QString &filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) return {}; EVP_MD_CTX *ctx = EVP_MD_CTX_new(); if (!ctx) return {}; const EVP_MD *md = EVP_sm3(); if (!md || EVP_DigestInit_ex(ctx, md, nullptr) != 1) { EVP_MD_CTX_free(ctx); return {}; } char buf[kBufferSize]; while (!file.atEnd()) { qint64 n = file.read(buf, kBufferSize); if (n <= 0) break; if (EVP_DigestUpdate(ctx, buf, static_cast(n)) != 1) { EVP_MD_CTX_free(ctx); return {}; } } unsigned char hash[kSM3HashLen]; unsigned int hashLen = 0; if (EVP_DigestFinal_ex(ctx, hash, &hashLen) != 1) { EVP_MD_CTX_free(ctx); return {}; } EVP_MD_CTX_free(ctx); return toHex(hash, hashLen); } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dsm3hash.h000066400000000000000000000010441523477242100262260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DSM3HASH_H #define DSM3HASH_H #include #include DFM_BURN_BEGIN_NS class DSM3Hash { public: /*! * \brief Compute SM3 hash of a file. * \param filePath Absolute path to the file. * \return Hex-encoded SM3 hash string (64 chars), or empty string on failure. */ static QString sm3File(const QString &filePath); }; DFM_BURN_END_NS #endif // DSM3HASH_H linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dudfburnengine.cpp000066400000000000000000000143231523477242100300520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "dudfburnengine.h" #include #include #include #include DFM_BURN_USE_NS namespace { typedef struct { long totalSize; long wroteSize; double progress; } ProgressInfo; // this is defined for callback register std::function udfProgressCbBinder; extern "C" void udProgressCbProxy(const ProgressInfo *info) { udfProgressCbBinder(info); } extern "C" { typedef void (*progress_cb)(const ProgressInfo *); typedef void (*uburn_init)(); typedef int (*uburn_do_burn)(const char *dev, const char *file, const char *lable); typedef int (*uburn_do_burn_ex)(const char *dev, const char *file, const char *lable, int finalize); typedef void (*uburn_regi_cb)(progress_cb cb); typedef char **(*uburn_get_errors)(int *); typedef void (*uburn_show_verbose)(); typedef void (*uburn_redirect_output)(int redir_stdout, int redir_stderr); static uburn_init ub_init = nullptr; static uburn_do_burn ub_do_burn = nullptr; static uburn_do_burn_ex ub_do_burn_ex = nullptr; static uburn_regi_cb ub_regi_cb = nullptr; static uburn_get_errors ub_get_errors = nullptr; static uburn_show_verbose ub_show_verbose = nullptr; static uburn_redirect_output ub_redirect_output = nullptr; } } // namespace DUDFBurnEngine::DUDFBurnEngine(QObject *parent) : QObject(parent) { lib.setFileName("udfburn"); if (lib.isLoaded()) return; libLoaded = lib.load(); if (!libLoaded) { qWarning() << "[dfm-burn] Cannot load lib: " << lib.fileName(); return; } qDebug() << lib.fileName(); ub_init = reinterpret_cast(lib.resolve("burn_init")); funcsLoaded &= (ub_init != nullptr); ub_do_burn = reinterpret_cast(lib.resolve("burn_burn_to_disc")); funcsLoaded &= (ub_do_burn != nullptr); ub_do_burn_ex = reinterpret_cast(lib.resolve("burn_burn_to_disc_ex")); // note: ub_do_burn_ex is optional, we fallback to ub_do_burn if not available ub_regi_cb = reinterpret_cast(lib.resolve("burn_register_progress_callback")); funcsLoaded &= (ub_regi_cb != nullptr); ub_show_verbose = reinterpret_cast(lib.resolve("burn_show_verbose_information")); funcsLoaded &= (ub_show_verbose != nullptr); ub_redirect_output = reinterpret_cast(lib.resolve("burn_redirect_output")); funcsLoaded &= (ub_redirect_output != nullptr); ub_get_errors = reinterpret_cast(lib.resolve("burn_get_last_errors")); funcsLoaded &= (ub_get_errors != nullptr); qInfo() << "[dfm-burn] udf load lib " << (libLoaded ? "success" : "failed"); qInfo() << "[dfm-burn] udf load func " << (funcsLoaded ? "success" : "failed"); } DUDFBurnEngine::~DUDFBurnEngine() { if (libLoaded) lib.unload(); } bool DUDFBurnEngine::doBurn(const QString &dev, const QPair files, QString volId, const BurnOptions &opts) { if (!canSafeUse()) return false; Q_EMIT jobStatusChanged(JobStatus::kStalled, 0); udfProgressCbBinder = [this](const ProgressInfo *info) { Q_EMIT jobStatusChanged(JobStatus::kRunning, static_cast(info->progress)); }; ub_init(); ub_regi_cb(udProgressCbProxy); ub_show_verbose(); ub_redirect_output(1, 0); int ret = 0; // Use new API if available and kKeepAppendable is set if (ub_do_burn_ex != nullptr && opts.testFlag(BurnOption::kKeepAppendable)) { // finalize=0 means keep appendable (multi-session) ret = ub_do_burn_ex(dev.toStdString().c_str(), files.first.toStdString().c_str(), volId.toStdString().c_str(), 0); qInfo() << "[dfm-burn] UDF: using burn_burn_to_disc_ex with finalize=0 (keep appendable)"; } else if (ub_do_burn_ex != nullptr) { // finalize=1 means finalize disc completely ret = ub_do_burn_ex(dev.toStdString().c_str(), files.first.toStdString().c_str(), volId.toStdString().c_str(), 1); qInfo() << "[dfm-burn] UDF: using burn_burn_to_disc_ex with finalize=1 (finalize disc)"; } else { // Fallback to old API (always finalize) ret = ub_do_burn(dev.toStdString().c_str(), files.first.toStdString().c_str(), volId.toStdString().c_str()); qInfo() << "[dfm-burn] UDF: using legacy burn_burn_to_disc (always finalize)"; } // burn failed if (ret != 0) { int err_count = 0; char **errors = ub_get_errors(&err_count); if (errors != nullptr && err_count > 0) { QStringList errMsg; for (int i = err_count - 1; i >= 0; i--) errMsg.append(errors[i]); message = errMsg; } message.append(readErrorsFromLog()); Q_EMIT jobStatusChanged(JobStatus::kFailed, 100); return false; } Q_EMIT jobStatusChanged(JobStatus::kFinished, 100); return true; } QStringList DUDFBurnEngine::lastErrorMessage() const { return message; } bool DUDFBurnEngine::canSafeUse() const { return libLoaded && funcsLoaded; } QStringList DUDFBurnEngine::readErrorsFromLog() const { auto &&homePaths { QStandardPaths::standardLocations(QStandardPaths::HomeLocation) }; if (homePaths.isEmpty()) return {}; auto &&logPath { homePaths.at(0) + "/.cache/deepin/discburn/uburn/" }; QDir logDir(logPath); if (!logDir.exists()) return {}; auto &&burns { logDir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot, QDir::SortFlag::Time) }; if (burns.count() == 0) return {}; auto &&lastBurn { burns.first() }; auto &&logFilePath { logPath + lastBurn + "/log" }; QFile logFile(logFilePath); if (!logFile.exists()) return {}; QStringList ret; logFile.open(QIODevice::ReadOnly | QIODevice::Text); while (!logFile.atEnd()) { QString &&line(logFile.readLine()); if (line.startsWith("Warning") || line.startsWith("Error")) { line.remove(QRegularExpression("/home/.*/.cache/deepin/discburn/_dev_sr[0-9]*/")); ret << line; } } logFile.close(); return ret; } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dudfburnengine.h000066400000000000000000000016061523477242100275170ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef UDFBURNENGINE_H #define UDFBURNENGINE_H #include #include #include DFM_BURN_BEGIN_NS class DUDFBurnEngine : public QObject { Q_OBJECT public: explicit DUDFBurnEngine(QObject *parent = nullptr); virtual ~DUDFBurnEngine() override; bool doBurn(const QString &dev, const QPair files, QString volId, const BurnOptions &opts); QStringList lastErrorMessage() const; Q_SIGNALS: void jobStatusChanged(JobStatus status, int progress); private: bool canSafeUse() const; QStringList readErrorsFromLog() const; private: QLibrary lib; bool libLoaded { false }; bool funcsLoaded { true }; QStringList message; }; DFM_BURN_END_NS #endif // UDFBURNENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp000066400000000000000000000132221523477242100305750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "dvdrwformatengine.h" #include #include #include #include #include DFM_BURN_USE_NS DVDRwFormatEngine::DVDRwFormatEngine(QObject *parent) : QObject(parent) { } DVDRwFormatEngine::~DVDRwFormatEngine() { } bool DVDRwFormatEngine::doErase(const QString &dev, MediaType type) { phase = 0; lastRawProgress = 0; // 1. 校验设备路径 QString cleanDev = QDir::cleanPath(dev); if (!cleanDev.startsWith("/dev/sr")) { qWarning() << "[dfm-burn] Invalid device path:" << dev; Q_EMIT jobStatusChanged(JobStatus::kFailed, -1); return false; } // 2. 查找 dvd+rw-format 二进制 QString binary = QStandardPaths::findExecutable("dvd+rw-format"); if (binary.isEmpty()) { qWarning() << "[dfm-burn] dvd+rw-format not found"; Q_EMIT jobStatusChanged(JobStatus::kFailed, -1); return false; } // 3. 根据介质类型选择命令参数 QStringList args; if (type == MediaType::kDVD_PLUS_RW) { args << "-force" << "-gui" << cleanDev; } else if (type == MediaType::kDVD_RW) { args << "-blank=full" << "-gui" << cleanDev; } else { qWarning() << "[dfm-burn] Unsupported media type for dvd+rw-format:" << static_cast(type); Q_EMIT jobStatusChanged(JobStatus::kFailed, -1); return false; } Q_EMIT jobStatusChanged(JobStatus::kRunning, 0); // dvd+rw-format 进度输出机制分析(源码 dvd+rw-format.cpp): // // 1. fork() 后子进程 setsid() 执行 SCSI 操作,父进程安装 SIGALRM // 定时轮询共享内存中的 progress 值(0~65536),用退格符覆盖式 // 输出百分比到 stderr。 // // 2. -gui 标志的 gui_action 赋值在 fork 之后的子进程中(727 行), // 父进程的 gui_action 始终为 NULL(129 行),所以 -gui 对父进程 // 的进度输出格式无影响——父进程始终用 \b 退格覆盖方式。 // // 3. DVD+RW -force 路径有 3 个 wait_for_unit(progress) 调用 // (FORMAT UNIT → STOP DE-ICING → CLOSE SESSION), // 每个 SCSI 操作的进度指示器独立从 0 开始, // 导致原始输出出现 3 个 0→100% 周期。 // DVD-RW -blank 路径只有 1 个 wait_for_unit,单周期。 // // 4. 进度数据通过 stderr 管道传输。QProcess::readyReadStandardOutput // 信号在 QEventLoop + MergedChannels 下可能不能及时触发(尤其在 // DVD-RW 单阶段输出稀疏时)。改用 waitForReadyRead 主动轮询 // 管道——其内部调用 processEvents 处理 Qt 信号,进度可实时传递。 int totalPhases = (type == MediaType::kDVD_PLUS_RW) ? 3 : 1; QProcess proc; proc.setProcessChannelMode(QProcess::MergedChannels); QByteArray buffer; proc.start(binary, args); if (!proc.waitForStarted()) { qWarning() << "[dfm-burn] Failed to start dvd+rw-format"; Q_EMIT jobStatusChanged(JobStatus::kFailed, -1); return false; } // 主动轮询管道,不依赖 readyReadStandardOutput 信号。 // waitForReadyRead 内部调用 processEvents,emit 的信号能被上层接收。 const int pollInterval { 200 }; // ms while (proc.state() == QProcess::Running) { proc.waitForReadyRead(pollInterval); if (proc.bytesAvailable() > 0) { buffer += proc.readAllStandardOutput(); parseProgress(buffer, totalPhases); if (buffer.size() > 1024) buffer = buffer.right(512); } } // 读取进程退出后管道中的剩余数据 if (proc.bytesAvailable() > 0) { buffer += proc.readAllStandardOutput(); parseProgress(buffer, totalPhases); } // 检查退出结果 if (proc.exitStatus() == QProcess::CrashExit) { qWarning() << "[dfm-burn] dvd+rw-format crashed"; Q_EMIT jobStatusChanged(JobStatus::kFailed, -1); return false; } int exitCode = proc.exitCode(); if (exitCode == 0) { Q_EMIT jobStatusChanged(JobStatus::kFinished, 100); return true; } else { qWarning() << "[dfm-burn] dvd+rw-format exited with code" << exitCode; Q_EMIT jobStatusChanged(JobStatus::kFailed, -1); return false; } } void DVDRwFormatEngine::parseProgress(const QByteArray &data, int totalPhases) { // dvd+rw-format 父进程用退格符覆盖式输出进度,格式如: // 0.0%\b\b\b\b0.1%\b|\b/\b\b0.2% ... // 也有部分行带换行符(子进程的诊断输出、父进程结束时的 \n)。 // 在整个缓冲区中搜索最后一个 XX.X% 模式即可获取当前进度。 static const QRegularExpression re("([0-9]+(?:\\.[0-9]+)?)%"); auto matches = re.globalMatch(QString::fromLocal8Bit(data)); QString lastPct; while (matches.hasNext()) lastPct = matches.next().captured(1); if (lastPct.isEmpty()) return; int rawProgress = static_cast(lastPct.toDouble()); // 检测相位切换:进度显著回退说明进入了下一个 SCSI 操作 if (rawProgress < lastRawProgress - 5) { phase++; if (phase >= totalPhases) phase = totalPhases - 1; } lastRawProgress = rawProgress; // 加权计算总体进度:(phase * 100 + rawProgress) / totalPhases int overall = (phase * 100 + rawProgress) / totalPhases; if (overall < 100) Q_EMIT jobStatusChanged(JobStatus::kRunning, overall); } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.h000066400000000000000000000013301523477242100302370ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DVDRWFORMATENGINE_H #define DVDRWFORMATENGINE_H #include #include DFM_BURN_BEGIN_NS class DVDRwFormatEngine : public QObject { Q_OBJECT public: explicit DVDRwFormatEngine(QObject *parent = nullptr); ~DVDRwFormatEngine() override; bool doErase(const QString &dev, MediaType type); Q_SIGNALS: void jobStatusChanged(JobStatus status, int progress); private: void parseProgress(const QByteArray &data, int totalPhases); private: int phase { 0 }; int lastRawProgress { 0 }; }; DFM_BURN_END_NS #endif // DVDRWFORMATENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dxorrisoengine.cpp000066400000000000000000000417111523477242100301130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "dxorrisoengine.h" #include #include #include #define FILTER_ISOBURN_WARN "warn_libisoburn" DFM_BURN_USE_NS static inline char *PCHAR(const char *c) { return const_cast(c); } static inline int XORRISO_OPT(struct XorrisO *x, std::function opt) { Xorriso_set_problem_status(x, PCHAR(""), 0); int r = opt(); return Xorriso_eval_problem_status(x, r, 0); } static inline bool JOBFAILED_IF(DXorrisoEngine *engine, int r, struct XorrisO *x) { if (r <= 0) { Xorriso_option_end(x, 1); Q_EMIT engine->jobStatusChanged(JobStatus::kFailed, -1, ""); return true; } return false; } static int xorrisoResultHandler(void *handle, char *text) { (static_cast(handle))->messageReceived(0, text); return 1; } static int xorrisoInfoHandler(void *handle, char *text) { // working around xorriso passing wrong handle to the callback if (strstr(text, "DEBUG : Concurrent message watcher")) { return 1; } (static_cast(handle))->messageReceived(1, text); return 1; } DXorrisoEngine::DXorrisoEngine(QObject *parent) : QObject(parent) { int r = Xorriso_new(&xorriso, PCHAR("xorriso"), 0); if (r <= 0) { xorriso = nullptr; return; } r = Xorriso_startup_libraries(xorriso, 0); if (r <= 0) { Xorriso_destroy(&xorriso, 0); return; } Xorriso_sieve_big(xorriso, 0); int idx[] = { 0 }; // 被 separators 分隔后列表中,要获取的元素序号 Xorriso_sieve_add_filter( xorriso, PCHAR(FILTER_ISOBURN_WARN), // 过滤器名, 后边根据这个名字筛选输出记录 2, // 监听 info channel PCHAR("libisoburn: WARNING : "), // 匹配前缀 PCHAR(" ."), // 分词符 1, // 保留分隔后列表中的多少项 idx, // 取第 1 号字段 5, // 如果有多行匹配,保留最后的n行 1 // 最后一个元素中保留整行剩余部分 ); Xorriso_start_msg_watcher(xorriso, xorrisoResultHandler, this, xorrisoInfoHandler, this, 0); } DXorrisoEngine::~DXorrisoEngine() { if (xorriso) { Xorriso_stop_msg_watcher(xorriso, 0); Xorriso_destroy(&xorriso, 0); } } /*! * \brief Acquire an optical drive. * \param dev The device identifier of the drive to be * acquired (e.g. "/dev/sr0") * * Unless otherwise stated, all methods below requires * a drive acquired. * XorrisoEngine will take exclusive control of the device * until it is released by calling releaseDevice(). * * \return true on success. If this function fails, it * is usually because the device is currently in use. */ bool DXorrisoEngine::acquireDevice(QString dev) { if (!dev.isEmpty()) { curDev = dev; int result = XORRISO_OPT(xorriso, [this, dev]() { return Xorriso_option_dev(xorriso, dev.toUtf8().data(), 3); }); if (result <= 0) { curDev = ""; return false; } return true; } return false; } /*! * \brief Release the drive currently held. */ void DXorrisoEngine::releaseDevice() { curDev = ""; Xorriso_option_end(xorriso, 0); } void DXorrisoEngine::clearResult() { Xorriso_sieve_clear_results(xorriso, 0); } MediaType DXorrisoEngine::mediaTypeProperty() const { MediaType devMedia { MediaType::kNoMedia }; if (curDev.isEmpty()) return devMedia; int ac, avail; char **av; Xorriso_sieve_get_result(xorriso, PCHAR("Media current:"), &ac, &av, &avail, 0); if (ac < 1) { Xorriso__dispose_words(&ac, &av); return devMedia; } QString mt = av[0]; const static QHash typemap = { { "CD-ROM", MediaType::kCD_ROM }, { "CD-R", MediaType::kCD_R }, { "CD-RW", MediaType::kCD_RW }, { "DVD-ROM", MediaType::kDVD_ROM }, { "DVD-R", MediaType::kDVD_R }, { "DVD-RW", MediaType::kDVD_RW }, { "DVD+R", MediaType::kDVD_PLUS_R }, { "DVD+R/DL", MediaType::kDVD_PLUS_R_DL }, { "DVD-R/DL", MediaType::kDVD_R_DL }, { "DVD-RAM", MediaType::kDVD_RAM }, { "DVD+RW", MediaType::kDVD_PLUS_RW }, { "BD-ROM", MediaType::kBD_ROM }, { "BD-R", MediaType::kBD_R }, { "BD-RE", MediaType::kBD_RE } }; mt = mt.left(mt.indexOf(' ')); if (typemap.find(mt) != typemap.end()) { devMedia = typemap[mt]; } else { devMedia = MediaType::kNoMedia; } Xorriso__dispose_words(&ac, &av); return devMedia; } void DXorrisoEngine::mediaStorageProperty(quint64 *usedSize, quint64 *availSize, quint64 *blocks) const { if (curDev.isEmpty()) return; int ac, avail; char **av; Xorriso_sieve_get_result(xorriso, PCHAR("Media summary:"), &ac, &av, &avail, 0); if (ac == 4) { const QString units = "kmg"; *blocks = static_cast(atoll(av[1])); *usedSize = static_cast(atof(av[2]) * (1 << ((units.indexOf(*(QString(av[2]).rbegin())) + 1) * 10))); *availSize = static_cast(atof(av[3]) * (1 << ((units.indexOf(*(QString(av[3]).rbegin())) + 1) * 10))); } Xorriso__dispose_words(&ac, &av); } bool DXorrisoEngine::mediaFormattedProperty() const { bool formatted { true }; if (curDev.isEmpty()) return formatted; int ac, avail; char **av; Xorriso_sieve_get_result(xorriso, PCHAR("Media status :"), &ac, &av, &avail, 0); if (ac == 1) formatted = QString(av[0]).contains("is blank"); Xorriso__dispose_words(&ac, &av); return formatted; } bool DXorrisoEngine::hasISOTree() const { if (curDev.isEmpty()) return false; int ac, avail; char **av; Xorriso_sieve_get_result(xorriso, PCHAR(FILTER_ISOBURN_WARN), &ac, &av, &avail, 0); QString msg = ac == 1 ? av[0] : ""; Xorriso__dispose_words(&ac, &av); if (msg == "No ISO 9660 image at LBA 0. Creating blank image.") return false; return true; } QString DXorrisoEngine::mediaVolIdProperty() const { QString volId; if (curDev.isEmpty()) return volId; int ac, avail; char **av; Xorriso_sieve_get_result(xorriso, PCHAR("Volume id :"), &ac, &av, &avail, 0); if (ac == 1) volId = QString(av[0]); Xorriso__dispose_words(&ac, &av); return volId; } QStringList DXorrisoEngine::mediaSpeedProperty() const { QStringList writeSpeed; if (curDev.isEmpty()) return writeSpeed; int r = XORRISO_OPT(xorriso, [this]() { return Xorriso_option_list_speeds(xorriso, 0); }); if (r < 0) return writeSpeed; int ac, avail; char **av; do { Xorriso_sieve_get_result(xorriso, PCHAR("Write speed :"), &ac, &av, &avail, 0); if (ac == 2) { writeSpeed.push_back(QString(av[0]) + '\t' + QString(av[1])); } Xorriso__dispose_words(&ac, &av); } while (avail > 0); return writeSpeed; } /*! * \brief Get a list of messages from xorriso. * * This will clear the internal message buffer. * * \return a list of messages from xorriso since the last command. */ QStringList DXorrisoEngine::takeInfoMessages() { QStringList ret = xorrisomsg; xorrisomsg.clear(); return ret; } bool DXorrisoEngine::doErase() { Q_EMIT jobStatusChanged(JobStatus::kRunning, 0, curspeed); xorrisomsg.clear(); XORRISO_OPT(xorriso, [this]() { return Xorriso_option_abort_on(xorriso, PCHAR("FAILURE"), 0); }); int r = 0; if (mediaTypeProperty() == MediaType::kDVD_PLUS_RW) { r = XORRISO_OPT(xorriso, [this]() { return Xorriso_option_blank(xorriso, PCHAR("full"), 1); }); } else { r = XORRISO_OPT(xorriso, [this]() { return Xorriso_option_blank(xorriso, PCHAR("as_needed"), 0); }); } if (JOBFAILED_IF(this, r, xorriso)) return false; return true; } bool DXorrisoEngine::doWriteISO(const QString &isoPath, int speed) { Q_EMIT jobStatusChanged(JobStatus::kStalled, 0, curspeed); xorrisomsg.clear(); QString spd = QString::number(speed) + "k"; if (speed == 0) spd = "0"; char **av = new char *[6]; av[0] = strdup("cdrecord"); av[1] = strdup("-v"); av[2] = strdup((QString("dev=") + curDev).toUtf8().data()); av[3] = strdup("blank=as_needed"); av[4] = strdup((QString("speed=") + spd).toUtf8().data()); av[5] = strdup(isoPath.toUtf8().data()); int r = XORRISO_OPT(xorriso, [this, av]() { int dummy { 0 }; return Xorriso_option_as(xorriso, 6, av, &dummy, 1); }); for (int i = 0; i < 6; ++i) free(av[i]); delete[] av; if (JOBFAILED_IF(this, r, xorriso)) return false; return true; } bool DXorrisoEngine::doDumpISO(quint64 dataBlocks, const QString &isoPath) { curDatablocks = dataBlocks; if (dataBlocks == 0) return false; Q_EMIT jobStatusChanged(JobStatus::kStalled, 0, curspeed); xorrisomsg.clear(); char **av = new char *[2]; av[0] = strdup((QString("use=outdev")).toUtf8().data()); av[1] = strdup((QString("data_to=") + isoPath).toUtf8().data()); int r = XORRISO_OPT(xorriso, [this, av]() { int dummy { 0 }; return Xorriso_option_check_media(xorriso, 2, av, &dummy, 0); }); for (int i = 0; i < 2; ++i) free(av[i]); delete[] av; if (JOBFAILED_IF(this, r, xorriso)) return false; return true; } /*! * \brief Perform a data integration check for the disc. * \param dataBlocks is current device's blocks * \param qgood if not null, will be set to the portion of sectors that can be read fast. * \param qslow if not null, will be set to the portion of sectors that can still be read, but slowly. * \param qbad if not null, will be set to the portion of sectors that are corrupt. * \return true on success, false on failure (if for some reason the disc could not be checked) * * The values returned should add up to 1 (or very close to 1). */ bool DXorrisoEngine::doCheckmedia(quint64 dataBlocks, double *qgood, double *qslow, double *qbad) { curDatablocks = dataBlocks; if (dataBlocks == 0) return false; Q_EMIT jobStatusChanged(JobStatus::kRunning, 0, curspeed); int r = XORRISO_OPT(xorriso, [this]() { int dummy { 0 }; return Xorriso_option_check_media(xorriso, 0, nullptr, &dummy, 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; quint64 ngood = 0; quint64 nslow = 0; quint64 nbad = 0; int ac, avail; char **av; do { Xorriso_sieve_get_result(xorriso, PCHAR("Media region :"), &ac, &av, &avail, 0); if (ac == 3) { quint64 szblk = static_cast(QString(av[1]).toLongLong()); if (av[2][0] == '-') { nbad += szblk; } else if (av[2][0] == '0') { ngood += szblk; } else { if (QString(av[2]).contains("slow")) { nslow += szblk; } else { ngood += szblk; } } } Xorriso__dispose_words(&ac, &av); } while (avail > 0); if (qgood) { *qgood = 1. * ngood / dataBlocks; } if (qslow) { *qslow = 1. * nslow / dataBlocks; } if (qbad) { *qbad = 1. * nbad / dataBlocks; } Xorriso_sieve_clear_results(xorriso, 0); Q_EMIT jobStatusChanged(JobStatus::kFinished, 0, curspeed); return true; } bool DXorrisoEngine::doBurn(const QPair files, int speed, QString volId, DXorrisoEngine::JolietSupport joliet, DXorrisoEngine::RockRageSupport rockRage, DXorrisoEngine::KeepAppendable appendable) { if (files.first.isEmpty()) return false; Q_EMIT jobStatusChanged(JobStatus::kStalled, 0, curspeed); xorrisomsg.clear(); QString spd = QString::number(speed) + "k"; if (speed == 0) spd = "0"; // speed int r = XORRISO_OPT(xorriso, [this, spd]() { return Xorriso_option_speed(xorriso, spd.toUtf8().data(), 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; // volid r = XORRISO_OPT(xorriso, [this, volId]() { return Xorriso_option_volid(xorriso, volId.toUtf8().data(), 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; // overwrite r = XORRISO_OPT(xorriso, [this]() { return Xorriso_option_overwrite(xorriso, PCHAR("off"), 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; // joliet r = XORRISO_OPT(xorriso, [this, joliet]() { return Xorriso_option_joliet(xorriso, PCHAR(joliet == JolietSupport::kTrue ? "on" : "off"), 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; // joliet extensions: enable long names and long paths when joliet is enabled if (joliet == JolietSupport::kTrue) { r = XORRISO_OPT(xorriso, [this]() { return Xorriso_option_compliance(xorriso, PCHAR("joliet_long_names:joliet_long_paths"), 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; } // rockridge r = XORRISO_OPT(xorriso, [this, rockRage]() { return Xorriso_option_rockridge(xorriso, PCHAR(rockRage == RockRageSupport::kTrue ? "on" : "off"), 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; // add files r = XORRISO_OPT(xorriso, [this, files]() { return Xorriso_option_map(xorriso, files.first.toUtf8().data(), files.second.toUtf8().data(), 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; // close r = XORRISO_OPT(xorriso, [this, appendable]() { return Xorriso_option_close(xorriso, PCHAR(appendable == KeepAppendable::kTrue ? "off" : "on"), 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; // commit r = XORRISO_OPT(xorriso, [this]() { return Xorriso_option_commit(xorriso, 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; return true; } bool DXorrisoEngine::doOsirroxOn() { int r = XORRISO_OPT(xorriso, [this]() { return Xorriso_option_osirrox(xorriso, PCHAR("on"), 0); }); return !JOBFAILED_IF(this, r, xorriso); } bool DXorrisoEngine::doExtract(const QString &diskPath, const QString &isoPath) { xorrisomsg.clear(); // NOTE: Despite the API parameter naming (disk_path, iso_path), xorriso actually // treats the first argument as the ISO source path and the second as disk destination. // This matches the CLI behavior: -extract int r = XORRISO_OPT(xorriso, [this, diskPath, isoPath]() { return Xorriso_option_extract(xorriso, PCHAR(isoPath.toUtf8().data()), PCHAR(diskPath.toUtf8().data()), 0); }); if (JOBFAILED_IF(this, r, xorriso)) return false; return true; } void DXorrisoEngine::messageReceived(int type, char *text) { Q_UNUSED(type); QString msg(text); msg = msg.trimmed(); fprintf(stderr, "msg from xorriso (%s) : %s\n", type ? " info " : "result", msg.toStdString().c_str()); xorrisomsg.push_back(msg); // closing session if (msg.contains("UPDATE : Closing track/session.")) { Q_EMIT jobStatusChanged(JobStatus::kStalled, 1, curspeed); return; } // stalled if (msg.contains("UPDATE : Thank you for being patient.")) { Q_EMIT jobStatusChanged(JobStatus::kStalled, 0, curspeed); return; } // cdrecord / blanking QRegularExpression r("([0-9.]*)%\\s*(fifo|done)"); QRegularExpressionMatch m = r.match(msg); if (m.hasMatch()) { double percentage = m.captured(1).toDouble(); Q_EMIT jobStatusChanged(JobStatus::kRunning, static_cast(percentage), curspeed); } // current speed r = QRegularExpression("([0-9]*\\.[0-9]x)[bBcCdD.]"); m = r.match(msg); if (m.hasMatch()) { curspeed = m.captured(1); } else { curspeed.clear(); } // commit r = QRegularExpression("([0-9]*)\\s*of\\s*([0-9]*) MB written"); m = r.match(msg); if (m.hasMatch()) { double percentage = 100. * m.captured(1).toDouble() / m.captured(2).toDouble(); Q_EMIT jobStatusChanged(JobStatus::kRunning, static_cast(percentage), curspeed); } // check media r = QRegularExpression("([0-9]*) blocks read in ([0-9]*) seconds , ([0-9.]*)x"); m = r.match(msg); if (m.hasMatch() && curDatablocks != 0) { double percentage = 100. * m.captured(1).toDouble() / curDatablocks; Q_EMIT jobStatusChanged(JobStatus::kRunning, static_cast(percentage), curspeed); } // operation complete if (msg.contains("Blanking done") || msg.contains(QRegularExpression("Writing to .* completed successfully."))) { Q_EMIT jobStatusChanged(JobStatus::kFinished, 0, curspeed); } } linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/dxorrisoengine.h000066400000000000000000000041651523477242100275620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef XORRISOENGINE_H #define XORRISOENGINE_H #include #include #include DFM_BURN_BEGIN_NS class DXorrisoEngine : public QObject { Q_OBJECT public: enum class JolietSupport { kFalse, kTrue }; enum class RockRageSupport { kFalse, kTrue }; enum class KeepAppendable { kFalse, kTrue }; public: explicit DXorrisoEngine(QObject *parent = nullptr); virtual ~DXorrisoEngine() override; bool acquireDevice(QString dev); void releaseDevice(); void clearResult(); MediaType mediaTypeProperty() const; void mediaStorageProperty(quint64 *usedSize, quint64 *availSize, quint64 *blocks) const; bool mediaFormattedProperty() const; bool hasISOTree() const; QString mediaVolIdProperty() const; QStringList mediaSpeedProperty() const; QStringList takeInfoMessages(); bool doErase(); bool doWriteISO(const QString &isoPath, int speed); bool doDumpISO(quint64 dataBlocks, const QString &isoPath); bool doCheckmedia(quint64 dataBlocks, double *qgood, double *qslow, double *qbad); bool doBurn(const QPair files, int speed, QString volId, JolietSupport joliet, RockRageSupport rockRage, KeepAppendable appendable); bool doExtract(const QString &diskPath, const QString &isoPath); bool doOsirroxOn(); public Q_SLOTS: void messageReceived(int type, char *text); Q_SIGNALS: /** * \brief Indicates a change of current job status. * * \param status Current job status. * \param progress Job progress in percentage if status is running. * Type of stalled work if status is stalled (1 = closing session, 0 = others). */ void jobStatusChanged(JobStatus status, int progress, QString speed); private: XorrisO *xorriso; QString curDev; QStringList xorrisomsg; QString curspeed; quint64 curDatablocks; }; DFM_BURN_END_NS #endif // XORRISOENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/scsicommandhelper.cpp000066400000000000000000000037131523477242100305540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "scsicommandhelper.h" #include #include #include #include #include DFM_BURN_BEGIN_NS #define CHECK_CONDITION 0x01 #define ERRCODE(s) ((((s)[2] & 0x0F) << 16) | ((s)[12] << 8) | ((s)[13])) static const int Dir_xlate[4] = { // should have been defined // private in USE_SG_IO scope, // but it appears to be too 0, // implementation-dependent... SG_DXFER_TO_DEV, // 1,CGC_DATA_WRITE SG_DXFER_FROM_DEV, // 2,CGC_DATA_READ SG_DXFER_NONE }; // 3,CGC_DATA_NONE ScsiCommandHelper::ScsiCommandHelper(const QString &dev) { fd = ::open(dev.toLocal8Bit().data(), O_RDWR | O_NONBLOCK); } ScsiCommandHelper::~ScsiCommandHelper() { if (fd > -1) close(fd); } unsigned char &ScsiCommandHelper::operator[](size_t i) { if (i == 0) { memset(&cgc, 0, sizeof(cgc)), memset(&_sense, 0, sizeof(_sense)); cgc.quiet = 1; cgc.sense = &_sense.s; memset(&sg_io, 0, sizeof(sg_io)); sg_io.interface_id = 'S'; sg_io.mx_sb_len = sizeof(_sense); sg_io.cmdp = cgc.cmd; sg_io.sbp = _sense.u; sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO; } sg_io.cmd_len = i + 1; return cgc.cmd[i]; } bool ScsiCommandHelper::transport(ScsiCommandHelper::Direction dir, void *buf, size_t sz) { int errorCode { 0 }; sg_io.dxferp = buf; sg_io.dxfer_len = sz; sg_io.dxfer_direction = Dir_xlate[dir]; if (ioctl(fd, SG_IO, &sg_io)) return -1; if ((sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK) { errno = EIO; errorCode = -1; if (sg_io.masked_status & CHECK_CONDITION) { errorCode = ERRCODE(_sense.u); if (errorCode == 0) errorCode = -1; } } return errorCode == 0; } DFM_BURN_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-burn/dfm-burn-lib/private/scsicommandhelper.h000066400000000000000000000021201523477242100302100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SCSICOMMANDHELPER_H #define SCSICOMMANDHELPER_H #include #include #include #include /*! * \brief copy from dvd+rw-tools * HomePage: http://fy.chalmers.se/~appro/linux/DVD+RW/ */ #define CGC_DATA_WRITE 1 #define CGC_DATA_READ 2 #define CGC_DATA_NONE 3 DFM_BURN_BEGIN_NS class ScsiCommandHelper { public: typedef enum { kNONE = CGC_DATA_NONE, // 3 kREAD = CGC_DATA_READ, // 2 kWRITE = CGC_DATA_WRITE // 1 } Direction; ScsiCommandHelper(const QString &dev); ~ScsiCommandHelper(); unsigned char &operator[](size_t i); bool transport(Direction dir = kNONE, void *buf = nullptr, size_t sz = 0); private: struct cdrom_generic_command cgc; union { struct request_sense s; unsigned char u[18]; } _sense; struct sg_io_hdr sg_io; int fd { -1 }; }; DFM_BURN_END_NS #endif // SCSICOMMANDHELPER_H linuxdeepin-util-dfm-a4307d3/src/dfm-io/000077500000000000000000000000001523477242100200555ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-io/CMakeLists.txt000066400000000000000000000004571523477242100226230ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) #SET(CMAKE_BUILD_TYPE "Debug") SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb") SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") add_subdirectory("dfm-io") add_subdirectory("tools") linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/000077500000000000000000000000001523477242100212305ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/CMakeLists.txt000066400000000000000000000010121523477242100237620ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) set (VERSION "1.0.0" CACHE STRING "define project version") set(BASE_NAME dfm-io) # gio signals conflicts with qt signals add_definitions(-DQT_NO_KEYWORDS) # public include file(GLOB_RECURSE IO_PUBLIC_INCLUDES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/include/${BASE_NAME}/*") # src FILE (GLOB_RECURSE IO_SRCS CONFIGURE_DEPENDS "./*.c" "./*.cpp" "./*.h" ) if (QT_VERSION_MAJOR MATCHES 6) include(dfm-io-qt6.cmake) else() include(dfm-io-qt5.cmake) endif() linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/denumerator.cpp000066400000000000000000000664651523477242100243020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "private/denumerator_p.h" #include "utils/dlocalhelper.h" #include #include #include #include #include #include #include #include #include #include #define FILE_DEFAULT_ATTRIBUTES "standard::*,etag::*,id::*,access::*,mountable::*,time::*,unix::*,dos::*,\ owner::*,thumbnail::*,preview::*,filesystem::*,gvfs::*,selinux::*,trash::*,recent::*,metadata::*" USING_IO_NAMESPACE /************************************************ * DEnumeratorPrivate ***********************************************/ DEnumeratorPrivate::DEnumeratorPrivate(DEnumerator *q) : q(q) { queryAttributes = FILE_DEFAULT_ATTRIBUTES; } DEnumeratorPrivate::~DEnumeratorPrivate() { clean(); if (cancellable) { g_object_unref(cancellable); cancellable = nullptr; } if (fts) { fts_close(fts); fts = nullptr; } } bool DEnumeratorPrivate::init(const QUrl &url) { QPointer me = this; const bool needTimeOut = q->timeout() != 0; if (!needTimeOut) { return createEnumerator(url, me); } else { mutex.lock(); bool succ = false; QtConcurrent::run([this, me, url, &succ]() { succ = createEnumerator(url, me); }); bool wait = waitCondition.wait(&mutex, q->timeout()); mutex.unlock(); if (!wait) qWarning() << "createEnumeratorInThread failed, url: " << url << " error: " << error.errorMsg(); return succ && wait; } } bool DEnumeratorPrivate::init() { const QUrl &uri = q->uri(); bool ret = init(uri); inited = true; return ret; } void DEnumeratorPrivate::clean() { if (!stackEnumerator.isEmpty()) { while (true) { GFileEnumerator *enumerator = stackEnumerator.pop(); g_object_unref(enumerator); if (stackEnumerator.isEmpty()) break; } } } bool DEnumeratorPrivate::createEnumerator(const QUrl &url, QPointer me) { const QString &uriPath = url.toString(); g_autoptr(GFile) gfile = g_file_new_for_uri(uriPath.toLocal8Bit().data()); g_autoptr(GError) gerror = nullptr; checkAndResetCancel(); GFileEnumerator *genumerator = g_file_enumerate_children(gfile, queryAttributes.toStdString().c_str(), enumLinks ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, &gerror); if (!me) { // Clean up the enumerator if it was created but the object is no longer valid if (genumerator) { g_object_unref(genumerator); } error.setCode(DFMIOErrorCode(DFM_IO_ERROR_NOT_FOUND)); return false; } bool ret = true; if (!genumerator || gerror) { // Clean up the enumerator if it was created but the object is no longer valid if (genumerator) { g_object_unref(genumerator); } if (gerror) setErrorFromGError(gerror); ret = false; qWarning() << "create enumerator failed, url: " << uriPath << " error: " << error.errorMsg() << gerror->message; } else { stackEnumerator.push_back(genumerator); } waitCondition.wakeAll(); return ret; } void DEnumeratorPrivate::checkAndResetCancel() { if (cancellable) { if (!g_cancellable_is_cancelled(cancellable)) g_cancellable_cancel(cancellable); g_cancellable_reset(cancellable); return; } cancellable = g_cancellable_new(); } void DEnumeratorPrivate::setErrorFromGError(GError *gerror) { if (!gerror) return; error.setCode(DFMIOErrorCode(gerror->code)); if (error.code() == DFMIOErrorCode::DFM_IO_ERROR_FAILED) error.setMessage(gerror->message); } bool DEnumeratorPrivate::checkFilter() { if (dirFilters.testFlag(DEnumerator::DirFilter::kNoFilter)) return true; if (!dfileInfoNext) return false; // 1. 首先处理特殊目录 "." 和 ".." const QString &fileName = dfileInfoNext->attribute(DFileInfo::AttributeID::kStandardName).toString(); if (!shouldShowDotAndDotDot(fileName)) return false; // 2. 处理目录和文件的过滤 if (!checkEntryTypeFilter()) return false; // 3. 处理权限过滤 if (!checkPermissionFilter()) return false; // 4. 处理符号链接过滤 if (!checkSymlinkFilter()) return false; // 5. 处理隐藏文件过滤 if (!checkHiddenFilter()) return false; // 6. 处理文件名过滤器 if (!checkNameFilter(fileName)) return false; return true; } bool DEnumeratorPrivate::shouldShowDotAndDotDot(const QString &fileName) { const bool isDot = (fileName == "."); const bool isDotDot = (fileName == ".."); if (isDot && (dirFilters.testFlag(DEnumerator::DirFilter::kNoDot) || dirFilters.testFlag(DEnumerator::DirFilter::kNoDotAndDotDot))) return false; if (isDotDot && (dirFilters.testFlag(DEnumerator::DirFilter::kNoDotDot) || dirFilters.testFlag(DEnumerator::DirFilter::kNoDotAndDotDot))) return false; return true; } bool DEnumeratorPrivate::checkEntryTypeFilter() { const bool isDir = dfileInfoNext->attribute(DFileInfo::AttributeID::kStandardIsDir).toBool(); const bool isSymlink = dfileInfoNext->attribute(DFileInfo::AttributeID::kStandardIsSymlink).toBool(); // kAllDirs: 显示所有目录,包括符号链接指向的目录 if (dirFilters.testFlag(DEnumerator::DirFilter::kAllDirs)) { if (isDir) return true; if (isSymlink) { // 对于符号链接,需要检查其目标 const QString targetPath = dfileInfoNext->attribute( DFileInfo::AttributeID::kStandardSymlinkTarget).toString(); if (!targetPath.isEmpty()) { // 如果是相对路径,需要转换为绝对路径 QString absoluteTargetPath = targetPath; if (QDir::isRelativePath(targetPath)) { const QString parentPath = dfileInfoNext->attribute( DFileInfo::AttributeID::kStandardParentPath).toString(); absoluteTargetPath = QDir(parentPath).absoluteFilePath(targetPath); } QUrl targetUrl = QUrl::fromLocalFile(absoluteTargetPath); QSharedPointer targetInfo = DLocalHelper::createFileInfoByUri(targetUrl); if (targetInfo) { return targetInfo->attribute(DFileInfo::AttributeID::kStandardIsDir).toBool(); } } } return false; } // 如果没有指定 kAllDirs,则根据 kDirs 和 kFiles 过滤 const bool wantDirs = dirFilters.testFlag(DEnumerator::DirFilter::kDirs); const bool wantFiles = dirFilters.testFlag(DEnumerator::DirFilter::kFiles); const bool isFile = dfileInfoNext->attribute(DFileInfo::AttributeID::kStandardIsFile).toBool(); // 如果既不要目录也不要文件,返回 false if (!wantDirs && !wantFiles) return false; // 如果只要目录 if (wantDirs && !wantFiles) return isDir; // 如果只要文件 if (!wantDirs && wantFiles) return isFile; // 如果都要 return true; } bool DEnumeratorPrivate::checkPermissionFilter() { if (!dirFilters.testFlag(DEnumerator::DirFilter::kReadable) && !dirFilters.testFlag(DEnumerator::DirFilter::kWritable) && !dirFilters.testFlag(DEnumerator::DirFilter::kExecutable)) return true; const bool readable = dfileInfoNext->attribute(DFileInfo::AttributeID::kAccessCanRead).toBool(); const bool writable = dfileInfoNext->attribute(DFileInfo::AttributeID::kAccessCanWrite).toBool(); const bool executable = dfileInfoNext->attribute(DFileInfo::AttributeID::kAccessCanExecute).toBool(); if (dirFilters.testFlag(DEnumerator::DirFilter::kReadable) && !readable) return false; if (dirFilters.testFlag(DEnumerator::DirFilter::kWritable) && !writable) return false; if (dirFilters.testFlag(DEnumerator::DirFilter::kExecutable) && !executable) return false; return true; } bool DEnumeratorPrivate::checkSymlinkFilter() { if (!dirFilters.testFlag(DEnumerator::DirFilter::kNoSymLinks)) return true; return !dfileInfoNext->attribute(DFileInfo::AttributeID::kStandardIsSymlink).toBool(); } bool DEnumeratorPrivate::checkHiddenFilter() { if (dirFilters.testFlag(DEnumerator::DirFilter::kHidden)) return true; const QString &parentPath = dfileInfoNext->attribute(DFileInfo::AttributeID::kStandardParentPath).toString(); const QUrl &urlHidden = QUrl::fromLocalFile(parentPath + "/.hidden"); QSet hideList; if (hideListMap.count(urlHidden) > 0) { hideList = hideListMap.value(urlHidden); } else { hideList = DLocalHelper::hideListFromUrl(urlHidden); hideListMap.insert(urlHidden, hideList); } return !DLocalHelper::fileIsHidden(dfileInfoNext.data(), hideList, false); } bool DEnumeratorPrivate::checkNameFilter(const QString &fileName) { if (nameFilters.isEmpty()) return true; const bool caseSensitive = dirFilters.testFlag(DEnumerator::DirFilter::kCaseSensitive); return !nameFilters.contains(fileName, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); } bool DEnumeratorPrivate::openDirByfts() { char *paths[2] = { nullptr, nullptr }; paths[0] = filePath(uri); if (!paths[0]) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); return false; } int (*compare)(const FTSENT **, const FTSENT **); compare = nullptr; if (sortRoleFlag == DEnumerator::SortRoleCompareFlag::kSortRoleCompareFileName) { compare = DLocalHelper::compareByName; } else if (sortRoleFlag == DEnumerator::SortRoleCompareFlag::kSortRoleCompareFileSize) { compare = DLocalHelper::compareBySize; } else if (sortRoleFlag == DEnumerator::SortRoleCompareFlag::kSortRoleCompareFileLastModified) { compare = DLocalHelper::compareByLastModifed; } else if (sortRoleFlag == DEnumerator::SortRoleCompareFlag::kSortRoleCompareFileLastRead) { compare = DLocalHelper::compareByLastRead; } fts = fts_open(paths, FTS_COMFOLLOW, compare); if (paths[0]) free(paths[0]); if (nullptr == fts) { qWarning() << "fts_open open error : " << QString::fromLocal8Bit(strerror(errno)); error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FTS_OPEN); return false; } return true; } void DEnumeratorPrivate::insertSortFileInfoList(QList> &fileList, QList> &dirList, FTSENT *ent, FTS *fts, const QSet &hideList) { auto sortInfo = DLocalHelper::createSortFileInfo(ent, hideList); if (sortInfo->isDir && !sortInfo->isSymLink) fts_set(fts, ent, FTS_SKIP); if (sortInfo->isDir && !isMixDirAndFile) { if (sortOrder == Qt::DescendingOrder) dirList.push_front(sortInfo); else dirList.push_back(sortInfo); return; } if (sortOrder == Qt::DescendingOrder) fileList.push_front(sortInfo); else fileList.push_back(sortInfo); } void DEnumeratorPrivate::enumUriAsyncOvered(GList *files) { asyncOvered = !files; if (!files) { asyncIteratorOver(); return; } GList *l; for (l = files; l != nullptr; l = l->next) { asyncInfos.append(static_cast(l->data)); } g_list_free(files); } void DEnumeratorPrivate::startAsyncIterator() { qInfo() << "start Async Iterator,uri = " << uri; asyncStoped = false; g_autoptr(GFile) gfile = DLocalHelper::createGFile(uri); checkAndResetCancel(); EnumUriData *userData = new EnumUriData(); userData->pointer = sharedFromThis(); g_file_enumerate_children_async(gfile, queryAttributes.toStdString().c_str(), G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, cancellable, enumUriAsyncCallBack, userData); } bool DEnumeratorPrivate::hasNext() { if (!asyncOvered) return false; while (!asyncInfos.isEmpty()) { auto gfileInfo = asyncInfos.takeFirst(); if (!gfileInfo) continue; nextUrl = buildUrl(uri, g_file_info_get_name(gfileInfo)); dfileInfoNext = DLocalHelper::createFileInfoByUri(nextUrl, g_file_info_dup(gfileInfo), queryAttributes.toStdString().c_str(), enumLinks ? DFileInfo::FileQueryInfoFlags::kTypeNone : DFileInfo::FileQueryInfoFlags::kTypeNoFollowSymlinks); g_object_unref(gfileInfo); if (checkFilter()) return true; } return false; } QList> DEnumeratorPrivate::fileInfoList() { if (asyncOvered) return QList>(); for (auto gfileInfo : asyncInfos) { if (!gfileInfo) continue; auto url = buildUrl(uri, g_file_info_get_name(gfileInfo)); infoList.append(DLocalHelper::createFileInfoByUri(url, g_file_info_dup(gfileInfo), queryAttributes.toStdString().c_str(), enumLinks ? DFileInfo::FileQueryInfoFlags::kTypeNone : DFileInfo::FileQueryInfoFlags::kTypeNoFollowSymlinks)); g_object_unref(gfileInfo); } return infoList; } void DEnumeratorPrivate::setQueryAttributes(const QString &attributes) { queryAttributes = attributes; } char *DEnumeratorPrivate::filePath(const QUrl &url) { if (url.userInfo().startsWith("originPath::")) return strdup(url.userInfo().replace("originPath::", "").toLatin1().constData()); QString path = url.path(); if (path != "/" && path.endsWith("/")) path = path.left(path.length() - 1); return strdup(path.toUtf8().constData()); } QUrl DEnumeratorPrivate::buildUrl(const QUrl &url, const char *fileName) { // 防御空指针,避免std::string或QByteArray构造时崩溃 if (!fileName) { return QUrl(); } QByteArray fileNameBa(fileName); // 路径遍历检查仅对本地文件系统 (file:/// 或无 scheme) 生效 // gio 的 trash:/// 后端对非用户主目录挂载点的回收站文件,使用反斜杠分隔的扁平路径 // 作为 GFileInfo 的 standard::name(例如 "\media\user\dev\.Trash-1000\files\x"), // 这是合法的 trash 文件名而非恶意路径,故不应对其做路径遍历拦截。 const QString scheme = url.scheme(); if (scheme.isEmpty() || scheme == QLatin1String("file")) { if (fileNameBa.contains("../") || fileNameBa.contains("..\\") || fileNameBa.startsWith("..")) { return QUrl(); } } QByteArray path; QString urlPath = url.path(); if (urlPath == "/" || urlPath.isEmpty()) { path = QByteArray("/") + fileNameBa; } else { QByteArray dirPath = urlPath.toUtf8(); if (!dirPath.endsWith('/')) { dirPath.append('/'); } // 使用QByteArray进行底层字节数组拼接,避免QString剥离BOM (efbbbf) path = dirPath + fileNameBa; } // 保留原始 URL 的 scheme 和 host,而不是假定为本地文件 QUrl nextUrl = url; nextUrl.setPath(path); if (url.userInfo().startsWith("originPath::")) { nextUrl.setUserInfo(url.userInfo() + QString::fromLatin1("/") + QString::fromLatin1(fileName)); } else if (DFMUtils::isInvalidCodecByPath(fileName)) { auto org = url.path() == "/" ? QString::fromLatin1("/") + QString::fromLatin1(fileName) : QString::fromLatin1(url.path().toUtf8()) + QString::fromLatin1("/") + QString::fromLatin1(fileName); nextUrl.setUserInfo(QString::fromLatin1("originPath::") + org); } return nextUrl; } void DEnumeratorPrivate::enumUriAsyncCallBack(GObject *sourceObject, GAsyncResult *res, gpointer userData) { EnumUriData *data = static_cast(userData); if (!data || !data->pointer || data->pointer->asyncStoped) { qInfo() << "user data error " << data; delete data; // 修复内存泄漏:在错误情况下删除 data return; } GFileEnumerator *enumerator; GError *error { nullptr }; enumerator = g_file_enumerate_children_finish(G_FILE(sourceObject), res, &error); if (error) { qInfo() << "enumerator url : " << data->pointer->uri << ". error msg : " << error->message; data->pointer->setErrorFromGError(error); } if (enumerator == nullptr || error) { data->pointer->enumUriAsyncOvered(nullptr); delete data; // 修复内存泄漏:在错误情况下删除 data } else { data->enumerator = enumerator; data->pointer->checkAndResetCancel(); g_file_enumerator_next_files_async(enumerator, 1000, G_PRIORITY_DEFAULT, data->pointer->cancellable, moreFilesCallback, data); } if (error) g_error_free(error); return; } void DEnumeratorPrivate::moreFilesCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { Q_UNUSED(sourceObject); EnumUriData *data = static_cast(userData); if (!data || !data->pointer || data->pointer->asyncStoped) { qInfo() << "user data error " << data; delete data; // 修复内存泄漏:在错误情况下删除 data return; } GError *error; GList *files; error = nullptr; GFileEnumerator *enumerator = data->enumerator; files = g_file_enumerator_next_files_finish(enumerator, res, &error); if (error) data->pointer->setErrorFromGError(error); data->pointer->enumUriAsyncOvered(files); if (files && !error) { data->pointer->checkAndResetCancel(); g_file_enumerator_next_files_async(enumerator, 100, G_PRIORITY_DEFAULT, data->pointer->cancellable, moreFilesCallback, data); } else { if (!g_file_enumerator_is_closed(data->enumerator)) { g_file_enumerator_close_async(data->enumerator, 0, nullptr, nullptr, nullptr); } g_object_unref(data->enumerator); data->enumerator = nullptr; delete data; // 修复内存泄漏:在异步操作结束时删除 data } if (error) g_error_free(error); } /************************************************ * DEnumerator ***********************************************/ DEnumerator::DEnumerator(const QUrl &uri) : d(new DEnumeratorPrivate(this)) { d->uri = uri; } DEnumerator::DEnumerator(const QUrl &uri, const QStringList &nameFilters, DirFilters filters, IteratorFlags flags) : d(new DEnumeratorPrivate(this)) { d->uri = uri; d->nameFilters = nameFilters; d->dirFilters = filters; d->iteratorFlags = flags; d->enumSubDir = d->iteratorFlags & DEnumerator::IteratorFlag::kSubdirectories; d->enumLinks = d->iteratorFlags & DEnumerator::IteratorFlag::kFollowSymlinks; } DEnumerator::~DEnumerator() { } QUrl DEnumerator::uri() const { return d->uri; } void DEnumerator::setNameFilters(const QStringList &filters) { d->nameFilters = filters; } QStringList DEnumerator::nameFilters() const { return d->nameFilters; } void DEnumerator::setDirFilters(DirFilters filters) { d->dirFilters = filters; } DEnumerator::DirFilters DEnumerator::dirFilters() const { return d->dirFilters; } void DEnumerator::setIteratorFlags(IteratorFlags flags) { d->iteratorFlags = flags; } DEnumerator::IteratorFlags DEnumerator::iteratorFlags() const { return d->iteratorFlags; } void DEnumerator::setTimeout(ulong timeout) { d->enumTimeout = timeout; } ulong DEnumerator::timeout() const { return d->enumTimeout; } void DEnumerator::setSortRole(SortRoleCompareFlag role) { d->sortRoleFlag = role; } DEnumerator::SortRoleCompareFlag DEnumerator::sortRole() const { return d->sortRoleFlag; } void DEnumerator::setSortOrder(Qt::SortOrder order) { d->sortOrder = order; } Qt::SortOrder DEnumerator::sortOrder() const { return d->sortOrder; } void DEnumerator::setSortMixed(bool mix) { d->isMixDirAndFile = mix; } bool DEnumerator::isSortMixed() const { return d->isMixDirAndFile; } void DEnumerator::setQueryAttributes(const QString &attributes) { return d->setQueryAttributes(attributes); } QString DEnumerator::queryAttributes() const { return d->queryAttributes; } bool DEnumerator::cancel() { if (d->cancellable && !g_cancellable_is_cancelled(d->cancellable)) g_cancellable_cancel(d->cancellable); d->ftsCanceled = true; d->asyncStoped = true; return true; } bool DEnumerator::hasNext() const { if (d->async) return d->hasNext(); if (!d->inited) d->init(); while (!d->stackEnumerator.isEmpty()) { GFileEnumerator *enumerator = d->stackEnumerator.top(); g_autoptr(GError) gerror = nullptr; d->checkAndResetCancel(); // Use g_file_enumerator_next_file() which returns a GFileInfo* for the next // entry (or NULL). This avoids ambiguous iterate() behavior across backends. GFileInfo *nextInfo = g_file_enumerator_next_file(enumerator, d->cancellable, &gerror); if (nextInfo) { d->nextUrl = d->buildUrl(d->uri, g_file_info_get_name(nextInfo)); d->dfileInfoNext = DLocalHelper::createFileInfoByUri(d->nextUrl, g_file_info_dup(nextInfo), FILE_DEFAULT_ATTRIBUTES, d->enumLinks ? DFileInfo::FileQueryInfoFlags::kTypeNone : DFileInfo::FileQueryInfoFlags::kTypeNoFollowSymlinks); g_object_unref(nextInfo); // 如果是目录且需要遍历子目录 if (d->enumSubDir && d->dfileInfoNext && d->dfileInfoNext->attribute(DFileInfo::AttributeID::kStandardIsDir).toBool()) { bool showDir = true; if (d->dfileInfoNext->attribute(DFileInfo::AttributeID::kStandardIsSymlink).toBool()) { showDir = d->enumLinks; } if (showDir) { d->init(d->nextUrl); } } if (!d->checkFilter()) continue; return true; } // nextInfo == NULL: either finished or an error occurred if (gerror) { d->setErrorFromGError(gerror); d->nextUrl = QUrl(); d->dfileInfoNext.reset(); return true; } // 当前枚举器已完成,弹出并继续下一个 GFileEnumerator *enumeratorPop = d->stackEnumerator.pop(); g_object_unref(enumeratorPop); } return false; } QUrl DEnumerator::next() const { return d->nextUrl; } QSharedPointer DEnumerator::fileInfo() const { return d->dfileInfoNext; } quint64 DEnumerator::fileCount() { if (!d->inited) d->init(); quint64 count = 0; while (hasNext()) ++count; return count; } QList> DEnumerator::fileInfoList() { if (d->async) return d->fileInfoList(); g_autoptr(GFileEnumerator) enumerator = nullptr; g_autoptr(GError) gerror = nullptr; g_autoptr(GFile) gfile = DLocalHelper::createGFile(d->uri); d->checkAndResetCancel(); enumerator = g_file_enumerate_children(gfile, FILE_DEFAULT_ATTRIBUTES, d->enumLinks ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, d->cancellable, &gerror); if (nullptr == enumerator) { if (gerror) { d->setErrorFromGError(gerror); } return d->infoList; } GFile *gfileIn = nullptr; GFileInfo *gfileInfoIn = nullptr; d->checkAndResetCancel(); while (g_file_enumerator_iterate(enumerator, &gfileInfoIn, &gfileIn, d->cancellable, &gerror)) { if (!gfileInfoIn) break; g_autofree gchar *uri = g_file_get_uri(gfileIn); const QUrl &url = QUrl(QString::fromLocal8Bit(uri)); QSharedPointer info = DLocalHelper::createFileInfoByUri(url); if (info) d->infoList.append(info); if (gerror) { d->setErrorFromGError(gerror); gerror = nullptr; } } if (gerror) d->setErrorFromGError(gerror); return d->infoList; } QList> DEnumerator::sortFileInfoList() { if (!d->fts) d->openDirByfts(); if (!d->fts) return {}; QList> listFile; QList> listDir; QSet hideList; QUrl urlHidden = d->buildUrl(d->uri, ".hidden"); hideList = DLocalHelper::hideListFromUrl(urlHidden); char *dirPath = d->filePath(d->uri); if (!dirPath) { qWarning() << "Failed to get file path for uri:" << d->uri; return {}; } while (1) { FTSENT *ent = fts_read(d->fts); if (ent == nullptr) { break; } if (d->ftsCanceled) break; unsigned short flag = ent->fts_info; if (strcmp(ent->fts_path, dirPath) == 0 || flag == FTS_DP) continue; d->insertSortFileInfoList(listFile, listDir, ent, d->fts, hideList); } fts_close(d->fts); d->fts = nullptr; // Clean up allocated memory free(dirPath); if (d->isMixDirAndFile) return listFile; listDir.append(listFile); return listDir; } DFMIOError DEnumerator::lastError() const { return d->error; } DEnumeratorFuture *DEnumerator::asyncIterator() { d->async = true; DEnumeratorFuture *future = new DEnumeratorFuture(sharedFromThis()); QObject::connect(d.data(), &DEnumeratorPrivate::asyncIteratorOver, future, &DEnumeratorFuture::onAsyncIteratorOver); return future; } void DEnumerator::startAsyncIterator() { d->startAsyncIterator(); } bool DEnumerator::isAsyncOver() const { return d->asyncOvered; } bool DEnumerator::initEnumerator(const bool oneByone) { if (d->async) return true; if (oneByone) { if (d->inited) return true; return d->init(); } if (d->fts) return true; return d->openDirByfts(); } linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/denumeratorfuture.cpp000066400000000000000000000022031523477242100255110ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include USING_IO_NAMESPACE DEnumeratorFuture::DEnumeratorFuture(const QSharedPointer &enumerator, QObject *parent) : QObject(parent), enumerator(enumerator) { } DEnumeratorFuture::~DEnumeratorFuture() { } void DEnumeratorFuture::startAsyncIterator() { enumerator->startAsyncIterator(); } bool DEnumeratorFuture::isFinished() { return enumerator->isAsyncOver(); } DFMIOError DEnumeratorFuture::lastError() const { return enumerator->lastError(); } qint64 DEnumeratorFuture::fileCount() { if (enumerator->isAsyncOver()) return 0; auto infos = enumerator->fileInfoList(); QList children; for (const auto &info : infos) { auto url = DFMUtils::bindUrlTransform(info->uri()); if (!children.contains(url)) children.append(url); } return children.count(); } void DEnumeratorFuture::onAsyncIteratorOver() { Q_EMIT asyncIteratorOver(); } linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/dfile.cpp000066400000000000000000001146421523477242100230270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "private/dfile_p.h" #include "utils/dlocalhelper.h" #include #include #include #include #include #include USING_IO_NAMESPACE /************************************************ * DFilePrivate ***********************************************/ DFilePrivate::DFilePrivate(DFile *q) : q(q) { } DFilePrivate::~DFilePrivate() { // 确保 cancellable 资源被正确清理 if (cancellable) { g_object_unref(cancellable); cancellable = nullptr; } // 清理其他 GObject 资源 if (iStream) { g_object_unref(iStream); iStream = nullptr; } if (oStream) { g_object_unref(oStream); oStream = nullptr; } if (ioStream) { g_object_unref(ioStream); ioStream = nullptr; } } void DFilePrivate::setError(DFMIOError error) { this->error = error; } void DFilePrivate::setErrorFromGError(GError *gerror) { if (!gerror) return; error.setCode(DFMIOErrorCode(gerror->code)); if (error.code() == DFMIOErrorCode::DFM_IO_ERROR_FAILED) error.setMessage(gerror->message); } void DFilePrivate::checkAndResetCancel() { if (cancellable) { if (!g_cancellable_is_cancelled(cancellable)) g_cancellable_cancel(cancellable); g_cancellable_reset(cancellable); return; } cancellable = g_cancellable_new(); } GInputStream *DFilePrivate::inputStream() { if (iStream) return iStream; if (ioStream) { GInputStream *inputStream = g_io_stream_get_input_stream(ioStream); if (inputStream) return inputStream; } return nullptr; } GOutputStream *DFilePrivate::outputStream() { if (oStream) return oStream; if (ioStream) { GOutputStream *outputStream = g_io_stream_get_output_stream(ioStream); if (outputStream) return outputStream; } return nullptr; } DFile::Permissions DFilePrivate::permissionsFromGFileInfo(GFileInfo *gfileinfo) { DFile::Permissions retValue = DFile::Permission::kNoPermission; if (!gfileinfo) return retValue; const std::string &attributeKey = DLocalHelper::attributeStringById(DFileInfo::AttributeID::kUnixMode); const quint32 &stMode = g_file_info_get_attribute_uint32(gfileinfo, attributeKey.c_str()); if (!stMode) return retValue; if ((stMode & S_IXUSR) == S_IXUSR) { retValue |= DFile::Permission::kExeOwner; retValue |= DFile::Permission::kExeUser; } if ((stMode & S_IWUSR) == S_IWUSR) { retValue |= DFile::Permission::kWriteOwner; retValue |= DFile::Permission::kWriteUser; } if ((stMode & S_IRUSR) == S_IRUSR) { retValue |= DFile::Permission::kReadOwner; retValue |= DFile::Permission::kReadUser; } if ((stMode & S_IXGRP) == S_IXGRP) retValue |= DFile::Permission::kExeGroup; if ((stMode & S_IWGRP) == S_IWGRP) retValue |= DFile::Permission::kWriteGroup; if ((stMode & S_IRGRP) == S_IRGRP) retValue |= DFile::Permission::kReadGroup; if ((stMode & S_IXOTH) == S_IXOTH) retValue |= DFile::Permission::kExeOther; if ((stMode & S_IWOTH) == S_IWOTH) retValue |= DFile::Permission::kWriteOther; if ((stMode & S_IROTH) == S_IROTH) retValue |= DFile::Permission::kReadOther; return retValue; } bool DFilePrivate::checkOpenFlags(DFile::OpenFlags *modeIn) { DFile::OpenFlags &mode = *modeIn; if (mode & DFile::OpenFlag::kNewOnly) { if (q->exists()) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FLAG_ERROR); return false; } } if (mode & DFile::OpenFlag::kExistingOnly) { if (!q->exists()) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FLAG_ERROR); return false; } } if ((mode & DFile::OpenFlag::kNewOnly) && (mode & DFile::OpenFlag::kExistingOnly)) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FLAG_ERROR); return false; } // WriteOnly implies Truncate when ReadOnly, Append, and NewOnly are not set. if ((mode & DFile::OpenFlag::kWriteOnly) && !(mode & (DFile::OpenFlag::kReadOnly | DFile::OpenFlag::kAppend | DFile::OpenFlag::kNewOnly))) mode |= DFile::OpenFlag::kTruncate; if (mode & (DFile::OpenFlag::kAppend | DFile::OpenFlag::kNewOnly)) mode |= DFile::OpenFlag::kWriteOnly; if ((mode & (DFile::OpenFlag::kReadOnly | DFile::OpenFlag::kWriteOnly)) == 0) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FLAG_ERROR); return false; } if ((mode & DFile::OpenFlag::kExistingOnly) && !(mode & (DFile::OpenFlag::kReadOnly | DFile::OpenFlag::kWriteOnly))) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FLAG_ERROR); return false; } return true; } quint32 DFilePrivate::buildPermissions(DFile::Permissions permission) { quint32 stMode = 0000; if (permission.testFlag(DFile::Permission::kExeOwner) | permission.testFlag(DFile::Permission::kExeUser)) stMode |= S_IXUSR; if (permission.testFlag(DFile::Permission::kWriteOwner) | permission.testFlag(DFile::Permission::kWriteUser)) stMode |= S_IWUSR; if (permission.testFlag(DFile::Permission::kReadOwner) | permission.testFlag(DFile::Permission::kReadUser)) stMode |= S_IRUSR; if (permission.testFlag(DFile::Permission::kExeGroup)) stMode |= S_IXGRP; if (permission.testFlag(DFile::Permission::kWriteGroup)) stMode |= S_IWGRP; if (permission.testFlag(DFile::Permission::kReadGroup)) stMode |= S_IRGRP; if (permission.testFlag(DFile::Permission::kExeOther)) stMode |= S_IXOTH; if (permission.testFlag(DFile::Permission::kWriteOther)) stMode |= S_IWOTH; if (permission.testFlag(DFile::Permission::kReadOther)) stMode |= S_IROTH; return stMode; } bool DFilePrivate::doOpen(DFile::OpenFlags mode) { if (q->isOpen()) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return false; } // check mode if (!checkOpenFlags(&mode)) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return false; } const QUrl &&uri = q->uri(); g_autoptr(GFile) gfile = DLocalHelper::createGFile(uri); g_autoptr(GError) gerror = nullptr; checkAndResetCancel(); if (mode & DFile::OpenFlag::kReadOnly && !(mode & DFile::OpenFlag::kWriteOnly)) { if (!q->exists()) { return false; } iStream = (GInputStream *)g_file_read(gfile, cancellable, &gerror); if (gerror) setErrorFromGError(gerror); if (!iStream) { return false; } return true; } else if (mode & DFile::OpenFlag::kWriteOnly && !(mode & DFile::OpenFlag::kReadOnly)) { if (mode & DFile::OpenFlag::kNewOnly) { oStream = (GOutputStream *)g_file_create(gfile, G_FILE_CREATE_NONE, cancellable, &gerror); if (gerror) setErrorFromGError(gerror); if (!oStream) { return false; } } else if (mode & DFile::OpenFlag::kAppend) { oStream = (GOutputStream *)g_file_append_to(gfile, G_FILE_CREATE_NONE, cancellable, &gerror); if (gerror) setErrorFromGError(gerror); if (!oStream) { return false; } } else { oStream = (GOutputStream *)g_file_replace(gfile, nullptr, false, G_FILE_CREATE_NONE, cancellable, &gerror); if (gerror) setErrorFromGError(gerror); if (!oStream) { return false; } } return true; } else if (mode & DFile::OpenFlag::kReadOnly && mode & DFile::OpenFlag::kWriteOnly) { if (mode & DFile::OpenFlag::kNewOnly) { ioStream = (GIOStream *)g_file_create_readwrite(gfile, G_FILE_CREATE_NONE, cancellable, &gerror); if (gerror) setErrorFromGError(gerror); if (!ioStream) { return false; } } else if (mode & DFile::OpenFlag::kExistingOnly) { ioStream = (GIOStream *)g_file_open_readwrite(gfile, cancellable, &gerror); if (gerror) setErrorFromGError(gerror); if (!ioStream) { return false; } } else { ioStream = (GIOStream *)g_file_replace_readwrite(gfile, nullptr, false, G_FILE_CREATE_NONE, cancellable, &gerror); if (gerror) setErrorFromGError(gerror); if (!ioStream) { return false; } } return true; } else { ioStream = (GIOStream *)g_file_replace_readwrite(gfile, nullptr, false, G_FILE_CREATE_NONE, cancellable, &gerror); if (gerror) setErrorFromGError(gerror); if (!ioStream) { return false; } return true; } } bool DFilePrivate::doClose() { if (iStream) { if (!g_input_stream_is_closed(iStream)) g_input_stream_close(iStream, nullptr, nullptr); g_object_unref(iStream); iStream = nullptr; } if (oStream) { if (!g_output_stream_is_closed(oStream)) g_output_stream_close(oStream, nullptr, nullptr); g_object_unref(oStream); oStream = nullptr; } if (ioStream) { if (!g_io_stream_is_closed(ioStream)) g_io_stream_close(ioStream, nullptr, nullptr); g_object_unref(ioStream); ioStream = nullptr; } if (cancellable) { g_object_unref(cancellable); cancellable = nullptr; } return true; } QByteArray DFilePrivate::doReadAll() { GInputStream *inputStream = this->inputStream(); if (!inputStream) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return QByteArray(); } QByteArray dataRet; const gsize size = 8192; GError *gerror = nullptr; while (true) { gsize bytesRead; char data[size]; memset(data, 0, size); checkAndResetCancel(); gboolean read = g_input_stream_read_all(inputStream, data, size, &bytesRead, cancellable, &gerror); if (!read || gerror) { if (gerror) { setErrorFromGError(gerror); g_error_free(gerror); } break; } if (bytesRead == 0) break; dataRet.append(data, bytesRead); } return dataRet; } qint64 DFilePrivate::doWrite(const char *data, qint64 maxSize) { GOutputStream *outputStream = this->outputStream(); if (!outputStream) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return -1; } g_autoptr(GError) gerror = nullptr; checkAndResetCancel(); gssize write = g_output_stream_write(outputStream, data, static_cast(maxSize), cancellable, &gerror); if (gerror) { setErrorFromGError(gerror); return -1; } return write; } qint64 DFilePrivate::doWrite(const char *data) { GOutputStream *outputStream = this->outputStream(); if (!outputStream) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return -1; } gsize bytes_write; g_autoptr(GError) gerror = nullptr; checkAndResetCancel(); gssize write = g_output_stream_write_all(outputStream, data, strlen(data), &bytes_write, cancellable, &gerror); if (gerror) { setErrorFromGError(gerror); return -1; } return write; } qint64 DFilePrivate::doWrite(const QByteArray &data) { return doWrite(data.data(), data.length()); } void DFilePrivate::readAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { ReadAsyncOp *data = static_cast(userData); GInputStream *stream = (GInputStream *)(sourceObject); g_autoptr(GError) gerror = nullptr; gssize size = g_input_stream_read_finish(stream, res, &gerror); if (data->callback) data->callback(size, data->userData); data->callback = nullptr; data->userData = nullptr; g_free(data); } void DFilePrivate::readQAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { ReadQAsyncOp *data = static_cast(userData); GInputStream *stream = (GInputStream *)(sourceObject); g_autoptr(GError) gerror = nullptr; gssize size = g_input_stream_read_finish(stream, res, &gerror); QByteArray dataRet = size >= 0 ? QByteArray(data->data) : QByteArray(); if (data->callback) data->callback(dataRet, data->userData); data->callback = nullptr; data->userData = nullptr; data->data = nullptr; g_free(data); } void DFilePrivate::readAllAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { ReadAllAsyncOp *data = static_cast(userData); GInputStream *stream = (GInputStream *)(sourceObject); g_autoptr(GError) gerror = nullptr; gsize size = 0; bool succ = g_input_stream_read_all_finish(stream, res, &size, &gerror); if (!succ || gerror) { if (data->callback) data->callback(QByteArray(), data->userData); } if (size == 0) { if (data->callback) { if (data->me) data->callback(data->me->readAllAsyncRet, data->userData); } } if (data->me) { data->me->readAllAsyncRet.append(data->data); data->me->q->readAllAsync(data->ioPriority, data->callback, data->userData); } data->callback = nullptr; data->userData = nullptr; data->data = nullptr; data->ioPriority = 0; data->me = nullptr; g_free(data); } void DFilePrivate::writeAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { WriteAsyncOp *data = static_cast(userData); GOutputStream *stream = (GOutputStream *)(sourceObject); g_autoptr(GError) gerror = nullptr; gssize size = g_output_stream_write_finish(stream, res, &gerror); if (data->callback) data->callback(size, data->userData); data->callback = nullptr; data->userData = nullptr; g_free(data); } void DFilePrivate::permissionsAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { NormalFutureAsyncOp *data = static_cast(userData); if (!data) return; QPointer me = data->me; if (!me) return; DFileFuture *future = data->future; g_autoptr(GFile) gfile = G_FILE(sourceObject); g_autoptr(GError) gerror = nullptr; g_autoptr(GFileInfo) gfileinfo = g_file_query_info_finish(gfile, res, &gerror); if (gerror) { me->setErrorFromGError(gerror); me = nullptr; future = nullptr; g_free(data); return; } auto permissions = data->me->permissionsFromGFileInfo(gfileinfo); future->infoPermissions(permissions); future->finished(); me = nullptr; future = nullptr; g_free(data); } void DFilePrivate::existsAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { NormalFutureAsyncOp *data = static_cast(userData); if (!data) return; QPointer me = data->me; if (!me) return; DFileFuture *future = data->future; g_autoptr(GFile) gfile = G_FILE(sourceObject); g_autoptr(GError) gerror = nullptr; g_autoptr(GFileInfo) gfileinfo = g_file_query_info_finish(gfile, res, &gerror); if (gerror) { me->setErrorFromGError(gerror); me = nullptr; future = nullptr; g_free(data); return; } const std::string &attributeKey = DLocalHelper::attributeStringById(DFileInfo::AttributeID::kStandardType); const quint32 exists = g_file_info_get_attribute_uint32(gfileinfo, attributeKey.c_str()); future->infoExists(exists != G_FILE_TYPE_UNKNOWN); future->finished(); me = nullptr; future = nullptr; g_free(data); } void DFilePrivate::sizeAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { NormalFutureAsyncOp *data = static_cast(userData); if (!data) return; QPointer me = data->me; if (!me) return; DFileFuture *future = data->future; g_autoptr(GFile) gfile = G_FILE(sourceObject); g_autoptr(GError) gerror = nullptr; g_autoptr(GFileInfo) gfileinfo = g_file_query_info_finish(gfile, res, &gerror); if (gerror) { me->setErrorFromGError(gerror); me = nullptr; future = nullptr; g_free(data); return; } const std::string &attributeKey = DLocalHelper::attributeStringById(DFileInfo::AttributeID::kStandardSize); const quint64 size = g_file_info_get_attribute_uint64(gfileinfo, attributeKey.c_str()); future->infoSize(size); future->finished(); me = nullptr; future = nullptr; g_free(data); } void DFilePrivate::flushAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { NormalFutureAsyncOp *data = static_cast(userData); QPointer me = data->me; DFileFuture *future = data->future; g_autoptr(GOutputStream) stream = G_OUTPUT_STREAM(sourceObject); g_autoptr(GError) gerror = nullptr; g_output_stream_flush_finish(stream, res, &gerror); if (gerror) { me->setErrorFromGError(gerror); me = nullptr; future = nullptr; g_free(data); return; } future->finished(); me = nullptr; future = nullptr; g_free(data); } void DFilePrivate::writeAsyncFutureCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { NormalFutureAsyncOp *data = static_cast(userData); QPointer me = data->me; DFileFuture *future = data->future; GOutputStream *stream = (GOutputStream *)(sourceObject); g_autoptr(GError) gerror = nullptr; gssize size = g_output_stream_write_finish(stream, res, &gerror); if (gerror) { me->setErrorFromGError(gerror); me = nullptr; future = nullptr; g_free(data); return; } future->writeAsyncSize(size); future->finished(); me = nullptr; future = nullptr; g_free(data); } void DFilePrivate::readAsyncFutureCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { ReadAllAsyncFutureOp *data = static_cast(userData); GInputStream *stream = (GInputStream *)(sourceObject); QPointer me = data->me; DFileFuture *future = data->future; g_autoptr(GError) gerror = nullptr; gsize size = 0; bool succ = g_input_stream_read_all_finish(stream, res, &size, &gerror); if (!succ || gerror) { future->setError(DFMIOErrorCode(gerror->code)); me->setErrorFromGError(gerror); } future->readData(data->data); future->finished(); data->future = nullptr; data->me = nullptr; g_free(data); } /************************************************ * DFile ***********************************************/ DFile::DFile(const QUrl &uri) : d(new DFilePrivate(this)) { d->uri = uri; } DFile::DFile(const QString &path) : d(new DFilePrivate(this)) { d->uri = QUrl::fromLocalFile(path); } DFile::~DFile() { close(); } QUrl DFile::uri() const { return d->uri; } bool DFile::isOpen() const { return d->isOpen; } qint64 DFile::size() const { g_autoptr(GFile) gfile = DLocalHelper::createGFile(d->uri); g_autoptr(GError) gerror = nullptr; d->checkAndResetCancel(); g_autoptr(GFileInfo) fileInfo = g_file_query_info(gfile, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, d->cancellable, &gerror); if (gerror) d->setErrorFromGError(gerror); if (fileInfo) { goffset size = g_file_info_get_size(fileInfo); return qint64(size); } return -1; } bool DFile::exists() const { g_autoptr(GFile) gfile = DLocalHelper::createGFile(d->uri); d->checkAndResetCancel(); return g_file_query_file_type(gfile, G_FILE_QUERY_INFO_NONE, d->cancellable) != G_FILE_TYPE_UNKNOWN; } qint64 DFile::pos() const { GInputStream *inputStream = d->inputStream(); if (inputStream) { // seems g_seekable_can_seek only support local file, survey after. todo lanxs gboolean canSeek = G_IS_SEEKABLE(inputStream) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; if (!canSeek) { return -1; } GSeekable *seekable = G_SEEKABLE(inputStream); if (!seekable) { return -2; } goffset pos = g_seekable_tell(seekable); return qint64(pos); } GOutputStream *outputStream = d->outputStream(); if (outputStream){ // seems g_seekable_can_seek only support local file, survey after. todo lanxs gboolean canSeek = G_IS_SEEKABLE(outputStream) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; if (!canSeek) { return -3; } GSeekable *seekable = G_SEEKABLE(outputStream); if (!seekable) { return -4; } goffset pos = g_seekable_tell(seekable); return qint64(pos); } d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return -5; } DFile::Permissions DFile::permissions() const { DFile::Permissions retValue = DFile::Permission::kNoPermission; g_autoptr(GFile) gfile = DLocalHelper::createGFile(d->uri); g_autoptr(GError) gerror = nullptr; d->checkAndResetCancel(); const std::string &attributeKey = DLocalHelper::attributeStringById(DFileInfo::AttributeID::kUnixMode); if (attributeKey.empty()) return retValue; g_autoptr(GFileInfo) fileInfo = g_file_query_info(gfile, attributeKey.c_str(), G_FILE_QUERY_INFO_NONE, d->cancellable, &gerror); if (gerror) d->setErrorFromGError(gerror); if (!fileInfo) return retValue; return d->permissionsFromGFileInfo(fileInfo); } DFMIOError DFile::lastError() const { return d->error; } bool DFile::open(DFile::OpenFlags mode) { d->isOpen = d->doOpen(mode); return d->isOpen; } bool DFile::close() { if (d->isOpen) { if (d->doClose()) d->isOpen = false; else return false; } return true; } bool DFile::cancel() { if (d->cancellable && !g_cancellable_is_cancelled(d->cancellable)) g_cancellable_cancel(d->cancellable); return true; } bool DFile::seek(qint64 pos, DFile::SeekType type) const { GInputStream *inputStream = d->inputStream(); if (inputStream) { // seems g_seekable_can_seek only support local file, survey after. todo lanxs gboolean canSeek = G_IS_SEEKABLE(inputStream) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; if (!canSeek) { return false; } GSeekable *seekable = G_SEEKABLE(inputStream); if (!seekable) { return false; } bool ret = false; GError *gerror = nullptr; GSeekType gtype = G_SEEK_CUR; switch (type) { case DFile::SeekType::kBegin: gtype = G_SEEK_SET; break; case DFile::SeekType::kEnd: gtype = G_SEEK_END; break; default: break; } d->checkAndResetCancel(); ret = g_seekable_seek(seekable, pos, gtype, d->cancellable, &gerror); if (gerror) { qCritical() << " seek err code = " << gerror->code << " , seek err msg = " << gerror->message; d->setErrorFromGError(gerror); g_error_free(gerror); } return ret; } GOutputStream *out = d->outputStream(); if (out) { // seems g_seekable_can_seek only support local file, survey after. todo lanxs gboolean canSeek = G_IS_SEEKABLE(out) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; if (!canSeek) { return false; } GSeekable *seekable = G_SEEKABLE(out); if (!seekable) { return false; } bool ret = false; GError *gerror = nullptr; GSeekType gtype = G_SEEK_CUR; switch (type) { case DFile::SeekType::kBegin: gtype = G_SEEK_SET; break; case DFile::SeekType::kEnd: gtype = G_SEEK_END; break; default: break; } d->checkAndResetCancel(); ret = g_seekable_seek(seekable, pos, gtype, d->cancellable, &gerror); if (gerror) { qCritical() << " seek err code = " << gerror->code << " , seek err msg = " << gerror->message; d->setErrorFromGError(gerror); g_error_free(gerror); } return ret; } d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return false; } bool DFile::flush() { GOutputStream *outputStream = d->outputStream(); if (!outputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return false; } g_autoptr(GError) gerror = nullptr; d->checkAndResetCancel(); gboolean ret = g_output_stream_flush(outputStream, d->cancellable, &gerror); if (gerror) d->setErrorFromGError(gerror); return ret; } bool DFile::setPermissions(Permissions permission) { quint32 stMode = d->buildPermissions(permission); g_autoptr(GFile) gfile = DLocalHelper::createGFile(d->uri); g_autoptr(GError) gerror = nullptr; d->checkAndResetCancel(); const std::string &attributeKey = DLocalHelper::attributeStringById(DFileInfo::AttributeID::kUnixMode); bool succ = g_file_set_attribute_uint32(gfile, attributeKey.c_str(), stMode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, d->cancellable, &gerror); if (gerror) d->setErrorFromGError(gerror); return succ; } qint64 DFile::read(char *data, qint64 maxSize) { GInputStream *inputStream = d->inputStream(); if (!inputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return -1; } g_autoptr(GError) gerror = nullptr; d->checkAndResetCancel(); gssize read = g_input_stream_read(inputStream, data, static_cast(maxSize), d->cancellable, &gerror); if (gerror) { d->setErrorFromGError(gerror); return -1; } return read; } QByteArray DFile::read(qint64 maxSize) { GInputStream *inputStream = d->inputStream(); if (!inputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return QByteArray(); } char data[maxSize + 1]; memset(&data, 0, maxSize + 1); g_autoptr(GError) gerror = nullptr; d->checkAndResetCancel(); g_input_stream_read(inputStream, data, static_cast(maxSize), d->cancellable, &gerror); if (gerror) { d->setErrorFromGError(gerror); return QByteArray(); } return QByteArray(data); } QByteArray DFile::readAll() { bool innerOpen { false }; if (!d->isOpen) { if (!open(DFMIO::DFile::OpenFlag::kReadOnly)) return QByteArray(); innerOpen = true; } const auto &bytes { d->doReadAll() }; if (innerOpen) close(); return bytes; } qint64 DFile::write(const char *data, qint64 len) { if (!d->isOpen) { d->setError(DFMIOError(DFM_IO_ERROR_OPEN_FAILED)); return -1; } return d->doWrite(data, len); } qint64 DFile::write(const char *data) { if (!d->isOpen) { d->setError(DFMIOError(DFM_IO_ERROR_OPEN_FAILED)); return -1; } return d->doWrite(data); } qint64 DFile::write(const QByteArray &byteArray) { if (!d->isOpen) { d->setError(DFMIOError(DFM_IO_ERROR_OPEN_FAILED)); return -1; } return d->doWrite(byteArray); } void DFile::readAsync(char *data, qint64 maxSize, int ioPriority, DFile::ReadCallbackFunc func, void *userData) { GInputStream *inputStream = d->inputStream(); if (!inputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); if (func) func(-1, userData); return; } DFilePrivate::ReadAsyncOp *dataOp = g_new0(DFilePrivate::ReadAsyncOp, 1); dataOp->callback = func; dataOp->userData = userData; d->checkAndResetCancel(); g_input_stream_read_async(inputStream, data, static_cast(maxSize), ioPriority, d->cancellable, DFilePrivate::readAsyncCallback, dataOp); } void DFile::readQAsync(qint64 maxSize, int ioPriority, DFile::ReadQCallbackFunc func, void *userData) { GInputStream *inputStream = d->inputStream(); if (!inputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); if (func) func(QByteArray(), userData); return; } char data[maxSize + 1]; memset(&data, 0, maxSize + 1); DFilePrivate::ReadQAsyncOp *dataOp = g_new0(DFilePrivate::ReadQAsyncOp, 1); dataOp->callback = func; dataOp->userData = userData; dataOp->data = data; d->checkAndResetCancel(); g_input_stream_read_async(inputStream, data, static_cast(maxSize), ioPriority, d->cancellable, DFilePrivate::readQAsyncCallback, dataOp); } void DFile::readAllAsync(int ioPriority, DFile::ReadAllCallbackFunc func, void *userData) { GInputStream *inputStream = d->inputStream(); if (!inputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); if (func) func(QByteArray(), userData); return; } const gsize size = 8192; char data[size + 1]; memset(data, 0, size + 1); DFilePrivate::ReadAllAsyncOp *dataOp = g_new0(DFilePrivate::ReadAllAsyncOp, 1); dataOp->callback = func; dataOp->userData = userData; dataOp->data = data; dataOp->ioPriority = ioPriority; dataOp->me = d.data(); d->checkAndResetCancel(); g_input_stream_read_all_async(inputStream, data, size, ioPriority, d->cancellable, DFilePrivate::readAllAsyncCallback, dataOp); } void DFile::writeAsync(const char *data, qint64 maxSize, int ioPriority, DFile::WriteCallbackFunc func, void *userData) { GOutputStream *outputStream = d->outputStream(); if (!outputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); if (func) func(-1, userData); return; } DFilePrivate::WriteAsyncOp *dataOp = g_new0(DFilePrivate::WriteAsyncOp, 1); dataOp->callback = func; dataOp->userData = userData; d->checkAndResetCancel(); g_output_stream_write_async(outputStream, data, static_cast(maxSize), ioPriority, d->cancellable, DFilePrivate::writeAsyncCallback, dataOp); } void DFile::writeAllAsync(const char *data, int ioPriority, DFile::WriteAllCallbackFunc func, void *userData) { writeAsync(data, strlen(data), ioPriority, func, userData); } void DFile::writeQAsync(const QByteArray &byteArray, int ioPriority, DFile::WriteQCallbackFunc func, void *userData) { writeAsync(byteArray.data(), byteArray.length(), ioPriority, func, userData); } DFileFuture *DFile::openAsync(OpenFlags mode, int ioPriority, QObject *parent) { Q_UNUSED(ioPriority); DFileFuture *future = new DFileFuture(parent); QPointer me = d.data(); QtConcurrent::run([&]() { this->open(mode); if (!me) return; future->finished(); }); return future; } DFileFuture *DFile::closeAsync(int ioPriority, QObject *parent) { Q_UNUSED(ioPriority); DFileFuture *future = new DFileFuture(parent); QPointer me = d.data(); QtConcurrent::run([&]() { this->close(); if (!me) return; future->finished(); }); return future; } DFileFuture *DFile::readAsync(quint64 maxSize, int ioPriority, QObject *parent) { DFileFuture *future = new DFileFuture(parent); GInputStream *inputStream = d->inputStream(); if (!inputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return future; } QByteArray data; DFilePrivate::ReadAllAsyncFutureOp *dataOp = g_new0(DFilePrivate::ReadAllAsyncFutureOp, 1); dataOp->me = d.data(); dataOp->future = future; dataOp->data = data; d->checkAndResetCancel(); g_input_stream_read_all_async(inputStream, &data, maxSize, ioPriority, d->cancellable, DFilePrivate::readAsyncFutureCallback, dataOp); return future; } DFileFuture *DFile::readAllAsync(int ioPriority, QObject *parent) { return readAsync(G_MAXSSIZE, ioPriority, parent); } DFileFuture *DFile::writeAsync(const QByteArray &data, qint64 len, int ioPriority, QObject *parent) { DFileFuture *future = new DFileFuture(parent); GOutputStream *outputStream = d->outputStream(); if (!outputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return future; } DFilePrivate::NormalFutureAsyncOp *dataOp = g_new0(DFilePrivate::NormalFutureAsyncOp, 1); dataOp->me = d.data(); dataOp->future = future; d->checkAndResetCancel(); g_output_stream_write_async(outputStream, data, static_cast(len), ioPriority, d->cancellable, DFilePrivate::writeAsyncFutureCallback, dataOp); return future; } DFileFuture *DFile::writeAsync(const QByteArray &data, int ioPriority, QObject *parent) { return writeAsync(data, strlen(data), ioPriority, parent); } DFileFuture *DFile::flushAsync(int ioPriority, QObject *parent) { DFileFuture *future = new DFileFuture(parent); GOutputStream *outputStream = d->outputStream(); if (!outputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); return future; } DFilePrivate::NormalFutureAsyncOp *data = g_new0(DFilePrivate::NormalFutureAsyncOp, 1); data->me = d.data(); data->future = future; d->checkAndResetCancel(); g_output_stream_flush_async(outputStream, ioPriority, d->cancellable, d->flushAsyncCallback, data); return future; } DFileFuture *DFile::sizeAsync(int ioPriority, QObject *parent) { DFileFuture *future = new DFileFuture(parent); DFilePrivate::NormalFutureAsyncOp *data = g_new0(DFilePrivate::NormalFutureAsyncOp, 1); data->me = d.data(); data->future = future; g_autoptr(GFile) gfile = DLocalHelper::createGFile(d->uri); d->checkAndResetCancel(); const std::string &attributeKey = DLocalHelper::attributeStringById(DFileInfo::AttributeID::kStandardSize); g_file_query_info_async(gfile, attributeKey.c_str(), G_FILE_QUERY_INFO_NONE, ioPriority, d->cancellable, DFilePrivate::sizeAsyncCallback, data); return future; } DFileFuture *DFile::existsAsync(int ioPriority, QObject *parent) { DFileFuture *future = new DFileFuture(parent); DFilePrivate::NormalFutureAsyncOp *data = g_new0(DFilePrivate::NormalFutureAsyncOp, 1); data->me = d.data(); data->future = future; g_autoptr(GFile) gfile = DLocalHelper::createGFile(d->uri); d->checkAndResetCancel(); const std::string &attributeKey = DLocalHelper::attributeStringById(DFileInfo::AttributeID::kStandardType); g_file_query_info_async(gfile, attributeKey.c_str(), G_FILE_QUERY_INFO_NONE, ioPriority, d->cancellable, d->existsAsyncCallback, data); return future; } DFileFuture *DFile::permissionsAsync(int ioPriority, QObject *parent) { DFileFuture *future = new DFileFuture(parent); DFilePrivate::NormalFutureAsyncOp *data = g_new0(DFilePrivate::NormalFutureAsyncOp, 1); data->me = d.data(); data->future = future; g_autoptr(GFile) gfile = DLocalHelper::createGFile(d->uri); d->checkAndResetCancel(); const std::string &attributeKey = DLocalHelper::attributeStringById(DFileInfo::AttributeID::kUnixMode); g_file_query_info_async(gfile, attributeKey.c_str(), G_FILE_QUERY_INFO_NONE, ioPriority, d->cancellable, d->permissionsAsyncCallback, data); return future; } DFileFuture *DFile::setPermissionsAsync(Permissions permission, int ioPriority, QObject *parent) { Q_UNUSED(ioPriority) DFileFuture *future = new DFileFuture(parent); quint32 stMode = d->buildPermissions(permission); g_autoptr(GFile) gfile = DLocalHelper::createGFile(d->uri); d->checkAndResetCancel(); g_autoptr(GError) gerror = nullptr; const std::string &attributeKey = DLocalHelper::attributeStringById(DFileInfo::AttributeID::kUnixMode); QPointer me = d.data(); QtConcurrent::run([&]() { g_file_set_attribute_uint32(gfile, attributeKey.c_str(), stMode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, d->cancellable, &gerror); if (!me) return; if (gerror) d->setErrorFromGError(gerror); future->finished(); }); return future; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/dfilefuture.cpp000066400000000000000000000015711523477242100242560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "private/dfilefuture_p.h" #include BEGIN_IO_NAMESPACE DFuturePrivate::DFuturePrivate(DFileFuture *qq) : q(qq) { } DFuturePrivate::~DFuturePrivate() { } DFileFuture::DFileFuture(QObject *parent) : QObject(parent) { } DFileFuture::~DFileFuture() { } bool DFileFuture::cancel() { // TODO: impl me! return false; } bool DFileFuture::isFinished() const { // TODO: impl me! return false; } bool DFileFuture::hasError() const { return d->error.code() != DFM_IO_ERROR_NONE; } int DFileFuture::errorCode() { return d->error.code(); } QString DFileFuture::errorMessage() { return d->error.errorMsg(); } void DFileFuture::setError(DFMIOError error) { d->error = error; } END_IO_NAMESPACE linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/dfileinfo.cpp000066400000000000000000001013301523477242100236710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "private/dfileinfo_p.h" #include "utils/dmediainfo.h" #include "utils/dlocalhelper.h" #include #include #include #include #include #include #include #include USING_IO_NAMESPACE /************************************************ * DFileInfoPrivate ***********************************************/ typedef struct { DFileInfo::AttributeAsyncCallback callback; gpointer user_data; DFileInfo::AttributeID id; QPointer me; } QueryFileInfoFromAttributeOp; static void queryFileInfoFromAttributeCallback(bool ok, void *userData) { QueryFileInfoFromAttributeOp *dataOp = static_cast(userData); if (!dataOp) return; if (dataOp->callback) { if (ok) { bool success = false; const QVariant &value = dataOp->me->q->attribute(dataOp->id, &success); dataOp->callback(success, dataOp->user_data, value); } else { dataOp->callback(false, dataOp->user_data, QVariant()); } } dataOp->callback = nullptr; dataOp->user_data = nullptr; dataOp->me = nullptr; g_free(dataOp); } DFileInfoPrivate::DFileInfoPrivate(DFileInfo *qq) : q(qq) { attributesRealizationSelf.push_back(DFileInfo::AttributeID::kStandardIsHidden); attributesRealizationSelf.push_back(DFileInfo::AttributeID::kOriginalUri); attributesRealizationSelf.push_back(DFileInfo::AttributeID::kTimeCreated); attributesRealizationSelf.push_back(DFileInfo::AttributeID::kTimeCreatedUsec); attributesRealizationSelf.push_back(DFileInfo::AttributeID::kTimeModified); attributesRealizationSelf.push_back(DFileInfo::AttributeID::kTimeModifiedUsec); attributesRealizationSelf.push_back(DFileInfo::AttributeID::kTimeAccess); attributesRealizationSelf.push_back(DFileInfo::AttributeID::kTimeAccessUsec); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardName); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardDisplayName); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardEditName); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardCopyName); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardSuffix); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardCompleteSuffix); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardFilePath); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardParentPath); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardBaseName); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardFileName); attributesNoBlockIO.push_back(DFileInfo::AttributeID::kStandardCompleteBaseName); } DFileInfoPrivate::DFileInfoPrivate(const DFileInfoPrivate &other) { q = other.q; } DFileInfoPrivate &DFileInfoPrivate::operator=(const DFileInfoPrivate &other) { q = other.q; return *this; } DFileInfoPrivate::~DFileInfoPrivate() { if (gfileinfo) { g_object_unref(gfileinfo); gfileinfo = nullptr; } if (gfile) { g_object_unref(gfile); gfile = nullptr; } if (gcancellable) { g_object_unref(gcancellable); gcancellable = nullptr; } } void DFileInfoPrivate::initNormal() { if (this->gfile) return; const QUrl &url = q->uri(); this->gfile = DLocalHelper::createGFile(url); } void DFileInfoPrivate::attributeExtend(DFileInfo::MediaType type, QList ids, DFileInfo::AttributeExtendFuncCallback callback) { if (ids.contains(DFileInfo::AttributeExtendID::kExtendMediaDuration) || ids.contains(DFileInfo::AttributeExtendID::kExtendMediaWidth) || ids.contains(DFileInfo::AttributeExtendID::kExtendMediaHeight)) { const QString &filePath = q->attribute(DFileInfo::AttributeID::kStandardFilePath, nullptr).toString(); if (!filePath.isEmpty()) { mediaType = type; extendIDs = ids; attributeExtendFuncCallback = callback; this->mediaInfo.reset(new DMediaInfo(filePath)); this->mediaInfo->startReadInfo(std::bind(&DFileInfoPrivate::attributeExtendCallback, this)); } else { if (callback) callback(false, {}); } } } DFileFuture *DFileInfoPrivate::attributeExtend(DFileInfo::MediaType type, QList ids, int ioPriority, QObject *parent) { Q_UNUSED(ioPriority); if (ids.contains(DFileInfo::AttributeExtendID::kExtendMediaDuration) || ids.contains(DFileInfo::AttributeExtendID::kExtendMediaWidth) || ids.contains(DFileInfo::AttributeExtendID::kExtendMediaHeight)) { DFileFuture *future = new DFileFuture(parent); const QString &filePath = q->attribute(DFileInfo::AttributeID::kStandardFilePath, nullptr).toString(); if (!filePath.isEmpty()) { mediaType = type; extendIDs = ids; this->future = future; this->mediaInfo.reset(new DMediaInfo(filePath)); this->mediaInfo->startReadInfo(std::bind(&DFileInfoPrivate::attributeExtendCallback, this)); return future; } else { return nullptr; } } else { return nullptr; } } bool DFileInfoPrivate::cancelAttributeExtend() { if (this->mediaInfo) this->mediaInfo->stopReadInfo(); return true; } bool DFileInfoPrivate::cancelAttributes() { if (gcancellable) g_cancellable_cancel(gcancellable); return cancelAttributeExtend(); } void DFileInfoPrivate::attributeExtendCallback() { if (this->mediaInfo) { QMap map; if (extendIDs.contains(DFileInfo::AttributeExtendID::kExtendMediaDuration)) { QString duration = mediaInfo->value("Duration", mediaType); if (duration.isEmpty()) { duration = mediaInfo->value("Duration", DFileInfo::MediaType::kGeneral); } map.insert(DFileInfo::AttributeExtendID::kExtendMediaDuration, duration); } if (extendIDs.contains(DFileInfo::AttributeExtendID::kExtendMediaWidth)) { const QString &width = mediaInfo->value("Width", mediaType); map.insert(DFileInfo::AttributeExtendID::kExtendMediaWidth, width); } if (extendIDs.contains(DFileInfo::AttributeExtendID::kExtendMediaHeight)) { const QString &height = mediaInfo->value("Height", mediaType); map.insert(DFileInfo::AttributeExtendID::kExtendMediaHeight, height); } if (attributeExtendFuncCallback) attributeExtendFuncCallback(true, map); if (this->future) Q_EMIT this->future->infoMedia(uri, map); } } void DFileInfoPrivate::setErrorFromGError(GError *gerror) { if (!gerror) return; if (g_error_matches(gerror, G_IO_ERROR, G_IO_ERROR_FAILED) && QString(gerror->message).contains(strerror(EHOSTDOWN))) { error.setCode(DFMIOErrorCode::DFM_IO_ERROR_HOST_IS_DOWN); error.setMessage(gerror->message); return; } error.setCode(DFMIOErrorCode(gerror->code)); if (error.code() == DFMIOErrorCode::DFM_IO_ERROR_FAILED) error.setMessage(gerror->message); } bool DFileInfoPrivate::queryInfoSync() { if (isQuquerying) return false; isQuquerying = true; if (!infoReseted && this->gfileinfo) { initFinished = true; isQuquerying = false; return true; } g_autoptr(GError) gerror = nullptr; checkAndResetCancel(); GFileInfo *fileinfo = g_file_query_info(gfile, attributes, GFileQueryInfoFlags(flag), gcancellable, &gerror); if (gerror) setErrorFromGError(gerror); if (!fileinfo) { isQuquerying = false; return false; } if (this->gfileinfo) { g_object_unref(this->gfileinfo); this->gfileinfo = nullptr; } this->gfileinfo = fileinfo; initFinished = true; isQuquerying = false; return true; } void DFileInfoPrivate::queryInfoAsync(int ioPriority, DFileInfo::InitQuerierAsyncCallback func, void *userData) { if (!infoReseted && this->gfileinfo) { initFinished = true; if (func) func(true, userData); return; } const char *attributes = q->queryAttributes(); const DFileInfo::FileQueryInfoFlags flag = q->queryInfoFlag(); QueryInfoAsyncOp *dataOp = g_new0(QueryInfoAsyncOp, 1); dataOp->callback = func; dataOp->userData = userData; dataOp->me = this; checkAndResetCancel(); g_file_query_info_async(this->gfile, attributes, GFileQueryInfoFlags(flag), ioPriority, gcancellable, queryInfoAsyncCallback, dataOp); } bool DFileInfoPrivate::ensureStatxCached() const { if (statxCached) return statxValid; statxCached = true; const QUrl &url = q->uri(); if (!url.isLocalFile()) return false; unsigned mask = STATX_BASIC_STATS | STATX_BTIME; int ret = statx(AT_FDCWD, url.path().toStdString().data(), AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, mask, &statxBuf); statxValid = (ret == 0); return statxValid; } QVariant DFileInfoPrivate::attributesBySelf(DFileInfo::AttributeID id) { QVariant retValue; switch (id) { case DFileInfo::AttributeID::kStandardIsHidden: { retValue = DLocalHelper::fileIsHidden(q, {}); break; } case DFileInfo::AttributeID::kTimeCreated: { const std::string &key = DLocalHelper::attributeStringById(id); if (key.empty()) return QVariant(); uint64_t ret = g_file_info_get_attribute_uint64(gfileinfo, key.c_str()); if (ret == 0) { if (!ensureStatxCached()) return QVariant(); return quint64(statxBuf.stx_btime.tv_sec); } return qulonglong(ret); } case DFileInfo::AttributeID::kTimeCreatedUsec: { const std::string &key = DLocalHelper::attributeStringById(id); if (key.empty()) return QVariant(); uint32_t ret = g_file_info_get_attribute_uint32(gfileinfo, key.c_str()); if (ret == 0) { if (!ensureStatxCached()) return QVariant(); return statxBuf.stx_btime.tv_nsec / 1000000; } return QVariant(ret); } case DFileInfo::AttributeID::kTimeModified: { const std::string &key = DLocalHelper::attributeStringById(id); if (key.empty()) return QVariant(); uint64_t ret = g_file_info_get_attribute_uint64(gfileinfo, key.c_str()); if (ret == 0) { if (!ensureStatxCached()) return QVariant(); return quint64(statxBuf.stx_mtime.tv_sec); } return qulonglong(ret); } case DFileInfo::AttributeID::kTimeModifiedUsec: { const std::string &key = DLocalHelper::attributeStringById(id); if (key.empty()) return QVariant(); uint32_t ret = g_file_info_get_attribute_uint32(gfileinfo, key.c_str()); if (ret == 0) { if (!ensureStatxCached()) return QVariant(); return statxBuf.stx_mtime.tv_nsec / 1000000; } return QVariant(ret); } case DFileInfo::AttributeID::kTimeAccess: { const std::string &key = DLocalHelper::attributeStringById(id); if (key.empty()) return QVariant(); uint64_t ret = g_file_info_get_attribute_uint64(gfileinfo, key.c_str()); if (ret == 0) { if (!ensureStatxCached()) return QVariant(); return quint64(statxBuf.stx_atime.tv_sec); } return qulonglong(ret); } case DFileInfo::AttributeID::kTimeAccessUsec: { const std::string &key = DLocalHelper::attributeStringById(id); if (key.empty()) return QVariant(); uint32_t ret = g_file_info_get_attribute_uint32(gfileinfo, key.c_str()); if (ret == 0) { if (!ensureStatxCached()) return QVariant(); return statxBuf.stx_atime.tv_nsec / 1000000; } return QVariant(ret); } case DFileInfo::AttributeID::kOriginalUri: if (gfile) { gchar *uri_str = g_file_get_uri(gfile); QUrl url = QUrl(QString::fromUtf8(uri_str)); g_free(uri_str); return url; } return uri; default: return retValue; } return retValue; } QVariant DFileInfoPrivate::attributesFromUrl(DFileInfo::AttributeID id) { if (!attributesNoBlockIO.contains(id)) return QVariant(); QVariant retValue; switch (id) { case DFileInfo::AttributeID::kStandardName: case DFileInfo::AttributeID::kStandardDisplayName: case DFileInfo::AttributeID::kStandardEditName: case DFileInfo::AttributeID::kStandardCopyName: case DFileInfo::AttributeID::kStandardFileName: { g_autofree gchar *name = g_path_get_basename(q->uri().toString().toStdString().c_str()); if (name != nullptr) return QString::fromLocal8Bit(name); return ""; } case DFileInfo::AttributeID::kStandardSuffix: { // path const QString &fullName = attributesFromUrl(DFileInfo::AttributeID::kStandardName).toString(); int pos2 = fullName.lastIndexOf("."); if (pos2 == -1) return ""; else return fullName.mid(pos2 + 1); } case DFileInfo::AttributeID::kStandardCompleteSuffix: { const QString &fullName = attributesFromUrl(DFileInfo::AttributeID::kStandardName).toString(); int pos2 = fullName.indexOf("."); if (pos2 == -1) return ""; else return fullName.mid(pos2 + 1); } case DFileInfo::AttributeID::kStandardFilePath: { g_autofree gchar *name = g_path_get_dirname(q->uri().path().toStdString().c_str()); if (name != nullptr) return QString::fromLocal8Bit(name); return ""; } case DFileInfo::AttributeID::kStandardParentPath: { g_autoptr(GFile) file = g_file_new_for_path(q->uri().path().toStdString().c_str()); g_autoptr(GFile) fileParent = g_file_get_parent(file); // no blocking I/O g_autofree gchar *gpath = g_file_get_path(fileParent); // no blocking I/O if (gpath != nullptr) return QString::fromLocal8Bit(gpath); return ""; } case DFileInfo::AttributeID::kStandardBaseName: { const QString &fullName = attributesFromUrl(DFileInfo::AttributeID::kStandardName).toString(); int pos2 = fullName.indexOf("."); if (pos2 == -1) return fullName; else return fullName.left(pos2); } case DFileInfo::AttributeID::kStandardCompleteBaseName: { const QString &fullName = attributesFromUrl(DFileInfo::AttributeID::kStandardName).toString(); int pos2 = fullName.lastIndexOf("."); if (pos2 == -1) return fullName; else return fullName.left(pos2); } case DFileInfo::AttributeID::kOriginalUri: { if (gfile) return QUrl(g_file_get_uri(gfile)); return uri; } default: break; } return retValue; } void DFileInfoPrivate::checkAndResetCancel() { if (gcancellable) { if (!g_cancellable_is_cancelled(gcancellable)) g_cancellable_cancel(gcancellable); g_cancellable_reset(gcancellable); return; } gcancellable = g_cancellable_new(); } DFileFuture *DFileInfoPrivate::initQuerierAsync(int ioPriority, QObject *parent) const { const char *attributes = q->queryAttributes(); const DFileInfo::FileQueryInfoFlags flag = q->queryInfoFlag(); DFileFuture *future = new DFileFuture(parent); QueryInfoAsyncOp2 *dataOp = g_new0(QueryInfoAsyncOp2, 1); dataOp->future = future; dataOp->me = const_cast(this); const_cast(this)->checkAndResetCancel(); g_file_query_info_async(this->gfile, attributes, GFileQueryInfoFlags(flag), ioPriority, gcancellable, queryInfoAsyncCallback2, dataOp); return future; } QFuture DFileInfoPrivate::refreshAsync() { if (refreshing) return futureRefresh; refreshing = true; if (futureRefresh.isRunning()) return futureRefresh; stoped = false; futureRefresh = QtConcurrent::run([=]() { if (stoped) { refreshing = false; return; } if (gfile) { g_object_unref(gfile); gfile = nullptr; } initNormal(); if (stoped) { refreshing = false; return; } queryInfoSync(); if (stoped) { refreshing = false; return; } cacheAttributes(); fileExists = exists(); refreshing = false; }); return futureRefresh; } void DFileInfoPrivate::cacheAttributes() { QMap tmp; for (const auto &[id, key] : DLocalHelper::attributeInfoMapFunc()) { tmp.insert(id, q->attribute(id)); } tmp.insert(DFileInfo::AttributeID::kAccessPermissions, QVariant::fromValue(permissions())); cacheing = true; caches = tmp; cacheing = false; } DFile::Permissions DFileInfoPrivate::permissions() const { DFile::Permissions retValue = DFile::Permission::kNoPermission; if (!initFinished) { bool succ = const_cast(this)->queryInfoSync(); if (!succ) return retValue; } const QVariant &value = q->attribute(DFileInfo::AttributeID::kUnixMode); if (!value.isValid()) return retValue; const uint32_t stMode = value.toUInt(); if ((stMode & S_IXUSR) == S_IXUSR) { retValue |= DFile::Permission::kExeOwner; retValue |= DFile::Permission::kExeUser; } if ((stMode & S_IWUSR) == S_IWUSR) { retValue |= DFile::Permission::kWriteOwner; retValue |= DFile::Permission::kWriteUser; } if ((stMode & S_IRUSR) == S_IRUSR) { retValue |= DFile::Permission::kReadOwner; retValue |= DFile::Permission::kReadUser; } if ((stMode & S_IXGRP) == S_IXGRP) retValue |= DFile::Permission::kExeGroup; if ((stMode & S_IWGRP) == S_IWGRP) retValue |= DFile::Permission::kWriteGroup; if ((stMode & S_IRGRP) == S_IRGRP) retValue |= DFile::Permission::kReadGroup; if ((stMode & S_IXOTH) == S_IXOTH) retValue |= DFile::Permission::kExeOther; if ((stMode & S_IWOTH) == S_IWOTH) retValue |= DFile::Permission::kWriteOther; if ((stMode & S_IROTH) == S_IROTH) retValue |= DFile::Permission::kReadOther; return retValue; } bool DFileInfoPrivate::exists() const { if (!gfileinfo) return false; return g_file_info_get_file_type(gfileinfo) != G_FILE_TYPE_UNKNOWN; } void DFileInfoPrivate::queryInfoAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { QueryInfoAsyncOp *data = static_cast(userData); if (!data) return; GFile *file = G_FILE(sourceObject); if (!file) { if (data->callback) data->callback(false, data->userData); freeQueryInfoAsyncOp(data); return; } g_autoptr(GError) gerror = nullptr; GFileInfo *fileinfo = g_file_query_info_finish(file, res, &gerror); if (gerror) { if (data->me) data->me->setErrorFromGError(gerror); if (data->callback) data->callback(false, data->userData); freeQueryInfoAsyncOp(data); return; } if (data->me) { data->me->gfileinfo = fileinfo; data->me->initFinished = true; } if (data->callback) data->callback(fileinfo ? true : false, data->userData); freeQueryInfoAsyncOp(data); } void DFileInfoPrivate::queryInfoAsyncCallback2(GObject *sourceObject, GAsyncResult *res, gpointer userData) { QueryInfoAsyncOp2 *data = static_cast(userData); if (!data) return; DFileFuture *future = data->future; if (!future) { freeQueryInfoAsyncOp2(data); return; } GFile *file = G_FILE(sourceObject); if (!file) { freeQueryInfoAsyncOp2(data); return; } g_autoptr(GError) gerror = nullptr; GFileInfo *fileinfo = g_file_query_info_finish(file, res, &gerror); if (gerror) { data->me->setErrorFromGError(gerror); freeQueryInfoAsyncOp2(data); return; } if (data->me) { data->me->gfileinfo = fileinfo; data->me->initFinished = true; future->finished(); } freeQueryInfoAsyncOp2(data); } void DFileInfoPrivate::freeQueryInfoAsyncOp(DFileInfoPrivate::QueryInfoAsyncOp *op) { op->callback = nullptr; op->userData = nullptr; op->me = nullptr; g_free(op); } void DFileInfoPrivate::freeQueryInfoAsyncOp2(DFileInfoPrivate::QueryInfoAsyncOp2 *op) { op->me = nullptr; g_free(op); } /************************************************ * DFileInfo ***********************************************/ DFileInfo::DFileInfo(const QUrl &uri, const char *attributes, const FileQueryInfoFlags flag) : d(new DFileInfoPrivate(this)) { d->uri = uri; d->attributes = strdup(attributes); d->flag = flag; d->initNormal(); } DFileInfo::DFileInfo(const QUrl &uri, void *fileInfo, const char *attributes, const DFileInfo::FileQueryInfoFlags flag) : DFileInfo(uri, attributes, flag) { d->gfileinfo = static_cast(fileInfo); } DFileInfo::DFileInfo(const DFileInfo &info) : d(info.d) { } DFileInfo &DFileInfo::operator=(const DFileInfo &info) { d = info.d; return *this; } DFileInfo::~DFileInfo() { free(d->attributes); } bool DFileInfo::initQuerier() { return d->queryInfoSync(); } QVariant DFileInfo::attribute(DFileInfo::AttributeID id, bool *success) const { if (!d->initFinished) { bool succ = const_cast(d.data())->queryInfoSync(); if (!succ) { if (!d->attributesNoBlockIO.contains(id)) { if (id == DFileInfo::AttributeID::kStandardIsHidden) { const auto &fileName = d->uri.fileName(); return fileName.startsWith('.'); } return QVariant(); } else { return const_cast(d.data())->attributesFromUrl(id); } } } QVariant retValue; if (id > DFileInfo::AttributeID::kCustomStart) { const QString &path = d->uri.path(); retValue = DLocalHelper::customAttributeFromPathAndInfo(path, d->gfileinfo, id); } else { if (d->gfileinfo) { DFMIOErrorCode errorCode(DFM_IO_ERROR_NONE); if (!d->attributesRealizationSelf.contains(id)) { QMutexLocker lk(&d->mutex); retValue = DLocalHelper::attributeFromGFileInfo(d->gfileinfo, id, errorCode); if (errorCode != DFM_IO_ERROR_NONE) const_cast(d.data())->error.setCode(errorCode); } else { retValue = const_cast(d.data())->attributesBySelf(id); } } } if (success) *success = retValue.isValid(); if (!retValue.isValid()) retValue = std::get<1>(DLocalHelper::attributeInfoMapFunc().at(id)); return retValue; } void DFileInfo::initQuerierAsync(int ioPriority, DFileInfo::InitQuerierAsyncCallback func, void *userData) { if (!d->infoReseted && d->gfileinfo) { d->initFinished = true; if (func) func(true, userData); return; } const char *attributes = queryAttributes(); const DFileInfo::FileQueryInfoFlags flag = queryInfoFlag(); DFileInfoPrivate::QueryInfoAsyncOp *dataOp = g_new0(DFileInfoPrivate::QueryInfoAsyncOp, 1); dataOp->callback = func; dataOp->userData = userData; dataOp->me = d.data(); g_file_query_info_async(d->gfile, attributes, GFileQueryInfoFlags(flag), ioPriority, nullptr, DFileInfoPrivate::queryInfoAsyncCallback, dataOp); } void DFileInfo::attributeAsync(DFileInfo::AttributeID id, bool *success, int ioPriority, DFileInfo::AttributeAsyncCallback func, void *userData) { if (!d->initFinished) { // query async QueryFileInfoFromAttributeOp *dataOp = g_new0(QueryFileInfoFromAttributeOp, 1); dataOp->callback = func; dataOp->user_data = userData; dataOp->id = id; dataOp->me = d.data(); d->queryInfoAsync(ioPriority, queryFileInfoFromAttributeCallback, dataOp); return; } const QVariant &value = attribute(id, success); if (func) func(success, userData, value); } DFileFuture *DFileInfo::initQuerierAsync(int ioPriority, QObject *parent) { const char *attributes = queryAttributes(); const DFileInfo::FileQueryInfoFlags flag = queryInfoFlag(); DFileFuture *future = new DFileFuture(parent); DFileInfoPrivate::QueryInfoAsyncOp2 *dataOp = g_new0(DFileInfoPrivate::QueryInfoAsyncOp2, 1); dataOp->future = future; dataOp->me = d.data(); d->checkAndResetCancel(); g_file_query_info_async(d->gfile, attributes, GFileQueryInfoFlags(flag), ioPriority, d->gcancellable, DFileInfoPrivate::queryInfoAsyncCallback2, dataOp); return future; } DFileFuture *DFileInfo::attributeAsync(DFileInfo::AttributeID id, int ioPriority, QObject *parent) const { DFileFuture *futureRet = new DFileFuture(parent); if (!d->initFinished) { DFileFuture *future = d->initQuerierAsync(ioPriority, nullptr); QObject::connect(future, &DFileFuture::finished, d.data(), [=]() { if (!future->hasError()) { futureRet->infoAttribute(id, attribute(id)); futureRet->finished(); } future->deleteLater(); }); } QTimer::singleShot(0, [=]() { futureRet->infoAttribute(id, attribute(id)); futureRet->finished(); }); return futureRet; } DFileFuture *DFileInfo::attributeAsync(const QByteArray &key, const DFileInfo::DFileAttributeType type, int ioPriority, QObject *parent) const { DFileFuture *futureRet = new DFileFuture(parent); if (!d->initFinished) { DFileFuture *future = d->initQuerierAsync(ioPriority, nullptr); QObject::connect(future, &DFileFuture::finished, d.data(), [=]() { if (!future->hasError()) { futureRet->infoAttribute(key, customAttribute(key, type)); futureRet->finished(); } future->deleteLater(); }); } QTimer::singleShot(0, [=]() { futureRet->infoAttribute(key, customAttribute(key, type)); futureRet->finished(); }); return futureRet; } DFileFuture *DFileInfo::existsAsync(int ioPriority, QObject *parent) const { DFileFuture *futureRet = new DFileFuture(parent); if (!d->initFinished) { DFileFuture *future = d->initQuerierAsync(ioPriority, nullptr); QObject::connect(future, &DFileFuture::finished, d.data(), [=]() { if (!future->hasError()) { const bool exists = this->exists(); futureRet->infoExists(exists); futureRet->finished(); } future->deleteLater(); }); } QTimer::singleShot(0, [=]() { const bool exists = this->exists(); futureRet->infoExists(exists); futureRet->finished(); }); return futureRet; } DFileFuture *DFileInfo::refreshAsync(int ioPriority, QObject *parent) { DFileFuture *future = this->initQuerierAsync(ioPriority, parent); QObject::connect(future, &DFileFuture::finished, d.data(), [=]() { future->finished(); }); return future; } DFileFuture *DFileInfo::permissionsAsync(int ioPriority, QObject *parent) { DFileFuture *future = this->initQuerierAsync(ioPriority, parent); QObject::connect(future, &DFileFuture::finished, d.data(), [=]() { future->infoPermissions(this->permissions()); future->finished(); }); return future; } QFuture DFileInfo::refreshAsync() { return d->refreshAsync(); } bool DFileInfo::hasAttribute(DFileInfo::AttributeID id) const { if (!d->initFinished) { bool succ = const_cast(d.data())->queryInfoSync(); if (!succ) return false; } if (d->gfileinfo) { const std::string &key = DLocalHelper::attributeStringById(id); if (key.empty()) return false; return g_file_info_has_attribute(d->gfileinfo, key.c_str()); } return false; } bool DFileInfo::exists() const { if (!d->cacheing && !d->caches.isEmpty()) return d->fileExists; return d->exists(); } bool DFileInfo::refresh() { d->infoReseted = true; bool ret = d->queryInfoSync(); d->infoReseted = false; return ret; } DFile::Permissions DFileInfo::permissions() const { if (!d->cacheing && !d->caches.isEmpty()) return d->caches.value(AttributeID::kAccessPermissions).value(); return d->permissions(); } bool DFileInfo::setCustomAttribute(const char *key, const DFileInfo::DFileAttributeType type, const void *value, const DFileInfo::FileQueryInfoFlags flag) { if (d->gfile) { g_autoptr(GError) gerror = nullptr; bool ret = g_file_set_attribute(d->gfile, key, GFileAttributeType(type), (gpointer)(value), GFileQueryInfoFlags(flag), nullptr, &gerror); if (gerror) d->setErrorFromGError(gerror); return ret; } return false; } QVariant DFileInfo::customAttribute(const char *key, const DFileInfo::DFileAttributeType type) const { if (!d->initFinished) { bool succ = const_cast(d.data())->queryInfoSync(); if (!succ) return QVariant(); } if (!d->gfileinfo) return QVariant(); switch (type) { case DFileInfo::DFileAttributeType::kTypeString: { const char *ret = g_file_info_get_attribute_string(d->gfileinfo, key); return QVariant(ret); } case DFileInfo::DFileAttributeType::kTypeByteString: { const char *ret = g_file_info_get_attribute_byte_string(d->gfileinfo, key); return QVariant(ret); } case DFileInfo::DFileAttributeType::kTypeBool: { bool ret = g_file_info_get_attribute_boolean(d->gfileinfo, key); return QVariant(ret); } case DFileInfo::DFileAttributeType::kTypeUInt32: { uint32_t ret = g_file_info_get_attribute_uint32(d->gfileinfo, key); return QVariant(ret); } case DFileInfo::DFileAttributeType::kTypeInt32: { int32_t ret = g_file_info_get_attribute_int32(d->gfileinfo, key); return QVariant(ret); } case DFileInfo::DFileAttributeType::kTypeUInt64: { uint64_t ret = g_file_info_get_attribute_uint64(d->gfileinfo, key); return QVariant(qulonglong(ret)); } case DFileInfo::DFileAttributeType::kTypeInt64: { int64_t ret = g_file_info_get_attribute_int64(d->gfileinfo, key); return QVariant(qulonglong(ret)); } case DFileInfo::DFileAttributeType::kTypeStringV: { char **ret = g_file_info_get_attribute_stringv(d->gfileinfo, key); QStringList retValue; for (int i = 0; ret && ret[i]; ++i) { retValue.append(QString::fromLocal8Bit(ret[i])); } return retValue; } default: return QVariant(); } } DFMIOError DFileInfo::lastError() const { return d->error; } void DFileInfo::attributeExtend(DFileInfo::MediaType type, QList ids, DFileInfo::AttributeExtendFuncCallback callback) { d->attributeExtend(type, ids, callback); } DFileFuture *DFileInfo::attributeExtend(DFileInfo::MediaType type, QList ids, int ioPriority, QObject *parent) { return d->attributeExtend(type, ids, ioPriority, parent); } bool DFileInfo::cancelAttributeExtend() { return d->cancelAttributeExtend(); } bool DFileInfo::cancelAttributes() { d->stoped = true; if (d->gcancellable) g_cancellable_cancel(d->gcancellable); cancelAttributeExtend(); return true; } QUrl DFileInfo::uri() const { return d->uri; } char *DFileInfo::queryAttributes() const { return d->attributes; } DFileInfo::FileQueryInfoFlags DFileInfo::queryInfoFlag() const { return d->flag; } QString DFileInfo::dump() const { QString ret; for (const auto &[id, key] : DLocalHelper::attributeInfoMapFunc()) { const QVariant &&value = attribute(id); if (value.isValid()) { ret.append(std::get<0>(DLocalHelper::attributeInfoMapFunc().at(id)).c_str()); ret.append(":"); ret.append(value.toString()); ret.append("\n"); } } return ret; } bool DFileInfo::queryAttributeFinished() const { return d->initFinished; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/dfm-io-qt5.cmake000066400000000000000000000001371523477242100241150ustar00rootroot00000000000000set(BIN_NAME dfm-io) set(QT_VERSION_MAJOR 5) set(DFM_VERSION_MAJOR "") include(dfm-io.cmake) linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/dfm-io-qt6.cmake000066400000000000000000000001371523477242100241160ustar00rootroot00000000000000set(BIN_NAME dfm6-io) set(QT_VERSION_MAJOR 6) set(DFM_VERSION_MAJOR 6) include(dfm-io.cmake) linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/dfm-io.cmake000066400000000000000000000033051523477242100234060ustar00rootroot00000000000000# Setup the environment find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Concurrent REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(GLIB glib-2.0 gobject-2.0 gio-2.0) pkg_check_modules(mediainfoVal REQUIRED libmediainfo IMPORTED_TARGET) list(APPEND mediainfos ${mediainfoVal_LDFLAGS}) # Build add_library(${BIN_NAME} SHARED ${IO_PUBLIC_INCLUDES} ${IO_SRCS} ) target_link_libraries(${BIN_NAME} Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Concurrent PkgConfig::mediainfoVal ${mediainfos} ${GLIB_LIBRARIES} ) target_include_directories(${BIN_NAME} PUBLIC ${GLIB_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/include/${BASE_NAME} ) if (NOT VERSION) set(VERSION "1.0.0") endif() set_target_properties( ${BIN_NAME} PROPERTIES VERSION ${VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) include(GNUInstallDirs) # install lib install(TARGETS ${BIN_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) # install headers install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/${BASE_NAME}/${BASE_NAME} DESTINATION include/${BIN_NAME} FILES_MATCHING PATTERN "*.h" ) # for pc file config set(PC_LIBS_PRIVATE Qt${QT_VERSION_MAJOR}Core) set(PC_REQ_PRIVATE) set(PC_REQ_PUBLIC) # config pkgconfig file configure_file(${PROJECT_SOURCE_DIR}/misc/${BASE_NAME}/${BASE_NAME}.pc.in ${BIN_NAME}.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BIN_NAME}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) # config cmake file configure_file(${PROJECT_SOURCE_DIR}/misc/${BASE_NAME}/${BASE_NAME}Config.cmake.in ${BIN_NAME}Config.cmake @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BIN_NAME}Config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${BIN_NAME}) linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/dfmio_utils.cpp000066400000000000000000000261331523477242100242570ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "utils/dlocalhelper.h" #include #include #include #include #include #include #include #include #include USING_IO_NAMESPACE bool DFMUtils::fileUnmountable(const QString &path) { if (path.isEmpty()) return false; g_autoptr(GFile) gfile = g_file_new_for_path(path.toStdString().c_str()); g_autoptr(GMount) gmount = g_file_find_enclosing_mount(gfile, nullptr, nullptr); if (gmount) { return g_mount_can_unmount(gmount); } return false; } QString DFMUtils::devicePathFromUrl(const QUrl &url) { if (!url.isValid()) return QString(); g_autoptr(GFile) gfile = DLocalHelper::createGFile(url); g_autoptr(GError) gerror = nullptr; g_autoptr(GMount) gmount = g_file_find_enclosing_mount(gfile, nullptr, &gerror); if (gmount) { g_autoptr(GFile) rootFile = g_mount_get_root(gmount); g_autofree gchar *uri = g_file_get_uri(rootFile); return QString::fromLocal8Bit(uri); } else { g_autofree gchar *path = g_file_get_path(gfile); g_autoptr(GUnixMountEntry) mount = g_unix_mount_for(path, nullptr); if (mount) { const gchar *devicePath = g_unix_mount_get_device_path(mount); return QString::fromLocal8Bit(devicePath); } } return QString(); } QString DFMUtils::deviceNameFromUrl(const QUrl &url) { if (!url.isValid()) return QString(); g_autoptr(GFile) gfile = DLocalHelper::createGFile(url); g_autoptr(GUnixMountEntry) mount = g_unix_mount_for(g_file_peek_path(gfile), nullptr); if (mount) return QString::fromLocal8Bit(g_unix_mount_get_device_path(mount)); return QString(); } QString DFMUtils::fsTypeFromUrl(const QUrl &url) { if (!url.isValid()) return QString(); g_autoptr(GFile) gfile = DLocalHelper::createGFile(url); g_autofree char *path = g_file_get_path(gfile); if (!path) return QString(); g_autoptr(GUnixMountEntry) mount = g_unix_mount_for(path, nullptr); if (mount) return QString::fromLocal8Bit(g_unix_mount_get_fs_type(mount)); return QString(); } QString DFMUtils::mountPathFromUrl(const QUrl &url) { if (!url.isValid()) return QString(); g_autoptr(GFile) gfile = DLocalHelper::createGFile(url); g_autofree char *path = g_file_get_path(gfile); if (!path) return QString(); g_autoptr(GUnixMountEntry) mount = g_unix_mount_for(path, nullptr); if (mount) return QString::fromLocal8Bit(g_unix_mount_get_mount_path(mount)); return QString(); } QUrl DFMUtils::directParentUrl(const QUrl &url, const bool localFirst /*= true*/) { if (!url.isValid()) return QUrl(); auto localPathUrl = [=](GFile *gfile) { g_autofree gchar *path = g_file_get_path(gfile); if (path) return QUrl::fromLocalFile(QString::fromLocal8Bit(path)); return QUrl(); }; g_autoptr(GFile) file = DLocalHelper::createGFile(url); g_autoptr(GFile) fileParent = g_file_get_parent(file); if (fileParent) { if (localFirst) { const QUrl urlParent = localPathUrl(fileParent); if (urlParent.isValid()) return urlParent; } g_autofree gchar *uri = g_file_get_uri(fileParent); if (uri) return QUrl(QString::fromLocal8Bit(uri)); return localPathUrl(fileParent); } return QUrl(); } bool DFMUtils::fileIsRemovable(const QUrl &url) { if (!url.isValid()) return false; g_autoptr(GFile) gfile = DLocalHelper::createGFile(url); g_autoptr(GMount) gmount = g_file_find_enclosing_mount(gfile, nullptr, nullptr); if (gmount) { g_autoptr(GDrive) gdrive = g_mount_get_drive(gmount); if (gdrive) return g_drive_is_removable(gdrive); else return g_mount_can_unmount(gmount); // when gdrive is nullptr, check unmountable } return false; } QSet DFMUtils::hideListFromUrl(const QUrl &url) { return DLocalHelper::hideListFromUrl(url); } QString DFMUtils::buildFilePath(const char *segment, ...) { va_list args; va_start(args, segment); g_autofree gchar *str = g_build_filename_valist(segment, &args); va_end(args); return QString::fromLocal8Bit(str); } QStringList DFMUtils::systemDataDirs() { QStringList lst; const char *const *cresult = g_get_system_data_dirs(); if (!cresult) return {}; for (const gchar *const *iter = cresult; *iter != nullptr; ++iter) { lst.append(QString::fromLocal8Bit(*iter)); } return lst; } QString DFMUtils::userSpecialDir(DGlibUserDirectory userDirectory) { const gchar *str = g_get_user_special_dir(static_cast(userDirectory)); return QString::fromLocal8Bit(str); } QString DFMUtils::userDataDir() { const gchar *dir = g_get_user_data_dir(); return QString::fromLocal8Bit(dir); } QString DFMUtils::bindPathTransform(const QString &path, bool toDevice) { if (!path.startsWith("/") || path == "/") return path; const QMap &table = fstabBindInfo(); if (table.isEmpty()) return path; QString bindPath(path); if (toDevice) { for (const auto &mntPoint : table.values()) { if (path.startsWith(mntPoint)) { bindPath.replace(mntPoint, table.key(mntPoint)); break; } } } else { for (const auto &device : table.keys()) { if (path.startsWith(device)) { bindPath.replace(device, table[device]); break; } } } return bindPath; } int DFMUtils::dirFfileCount(const QUrl &url) { if (!url.isValid()) return 0; DFMIO::DEnumerator enumerator(url); return int(enumerator.fileCount()); } QUrl DFMUtils::bindUrlTransform(const QUrl &url) { auto tmp = url; if (!url.path().contains("\\")) { tmp.setPath(bindPathTransform(url.path(), false)); return tmp; } auto path = BackslashPathToNormal(url.path()); path = bindPathTransform(path, false); path = normalPathToBackslash(path); tmp.setPath(path); return tmp; } QString DFMUtils::BackslashPathToNormal(const QString &trash) { if (!trash.contains("\\")) return trash; QString normal = trash; normal = normal.replace("\\", "/"); normal = normal.replace("//", "/"); return normal; } QString DFMUtils::normalPathToBackslash(const QString &normal) { QString trash = normal; trash = trash.replace("/", "\\"); trash.push_front("/"); return trash; } DEnumeratorFuture *DFMUtils::asyncTrashCount() { QSharedPointer enumerator(new DEnumerator(QUrl("trash:///"))); return enumerator->asyncIterator(); } int DFMUtils::syncTrashCount() { DEnumerator enumerator(QUrl("trash:///")); QList children; while (enumerator.hasNext()) { auto url = DFMUtils::bindUrlTransform(enumerator.next()); if (!children.contains(url)) children.append(url); } return children.length(); } // 传入的url不能是链接文件,如果是链接文件就是链接文件所在磁盘的数据 qint64 DFMUtils::deviceBytesFree(const QUrl &url) { if (!url.isValid()) return 0; auto path = url.path(); g_autoptr(GFile) file = g_file_new_for_path(QFile::encodeName(path).constData()); GError *error = nullptr; g_autoptr(GFileInfo) gioInfo = g_file_query_filesystem_info(file, "filesystem::*", nullptr, &error); if (error || !gioInfo) { if (error) g_error_free(error); error = nullptr; return std::numeric_limits::max(); } quint64 bytesTotal = 0; if (g_file_info_has_attribute(gioInfo, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE)) { bytesTotal = g_file_info_get_attribute_uint64(gioInfo, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); } else { qInfo() << "file do not support G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, returns max of qint64"; return std::numeric_limits::max(); } if (g_file_info_has_attribute(gioInfo, G_FILE_ATTRIBUTE_FILESYSTEM_USED)) { quint64 used = g_file_info_get_attribute_uint64(gioInfo, G_FILE_ATTRIBUTE_FILESYSTEM_USED); return static_cast(bytesTotal - used); } else { qInfo() << "file do not support G_FILE_ATTRIBUTE_FILESYSTEM_USED, returns max of qint64"; return std::numeric_limits::max(); } } bool dfmio::DFMUtils::supportTrash(const QUrl &url) { if (!url.isValid()) return false; auto path = url.path(); GStatBuf file_stat, home_stat; if (g_stat (path.toStdString().data(), &file_stat) != 0) return false; const char *homedir = g_get_home_dir (); g_stat (homedir, &home_stat); // 和当前用的主目录在同一挂载点 if (file_stat.st_dev == home_stat.st_dev) return true; g_autoptr(GFile) gfile = DLocalHelper::createGFile(url); g_autofree char *path1 = g_file_get_path(gfile); if (!path1) return false; g_autoptr(GUnixMountEntry) mount = g_unix_mount_for(path1, nullptr); if (mount == nullptr || g_unix_mount_is_system_internal (mount)) return false; return true; } bool DFMUtils::isGvfsFile(const QUrl &url) { if (!url.isValid()) return false; const QString &path = url.toLocalFile(); static const QString *gvfsMatch = new QString{ "(^/run/user/\\d+/gvfs/|^/root/.gvfs/|^/media/[\\s\\S]*/smbmounts)" }; // TODO(xust) /media/$USER/smbmounts might be changed in the future. QRegularExpression re { *gvfsMatch }; QRegularExpressionMatch match { re.match(path) }; return match.hasMatch(); } bool DFMUtils::isInvalidCodecByPath(const char *path) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QTextCodec::ConverterState stat; auto codec = QTextCodec::codecForLocale(); auto str = codec->toUnicode(path, static_cast(strlen(path)), &stat); return stat.invalidChars; #else QByteArray pathA(path); return !pathA.isValidUtf8(); #endif } QMap DFMUtils::fstabBindInfo() { static QMutex mutex; static QMap table; struct stat statInfo; int result = stat("/etc/fstab", &statInfo); QMutexLocker locker(&mutex); if (0 == result) { static quint32 lastModify = 0; if (lastModify != statInfo.st_mtime) { lastModify = static_cast(statInfo.st_mtime); table.clear(); struct fstab *fs; setfsent(); while ((fs = getfsent()) != nullptr) { QString mntops(fs->fs_mntops); if (mntops.contains("bind")) table.insert(fs->fs_spec, fs->fs_file); } endfsent(); } } return table; } bool DFMUtils::compareFileName(const QString &str1, const QString &str2) { return DLocalHelper::compareByStringEx(str1, str2); } linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/doperator.cpp000066400000000000000000000415021523477242100237350ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "private/doperator_p.h" #include "utils/dlocalhelper.h" #include #include #include #include #include USING_IO_NAMESPACE /************************************************ * DOperatorPrivate ***********************************************/ DOperatorPrivate::DOperatorPrivate(DOperator *q) : q(q) { } DOperatorPrivate::~DOperatorPrivate() { } void DOperatorPrivate::setErrorFromGError(GError *gerror) { if (!gerror) return; error.setCode(DFMIOErrorCode(gerror->code)); if (error.code() == DFMIOErrorCode::DFM_IO_ERROR_FAILED) { QString strErr(gerror->message); if (strErr.contains(':')) strErr = strErr.left(strErr.indexOf(":")) + strErr.mid(strErr.lastIndexOf(":")); error.setMessage(strErr); } } void DOperatorPrivate::setErrorFromErrno(int errnoValue) { DFMIOErrorCode errorCode; switch (errnoValue) { case EACCES: case EPERM: errorCode = DFM_IO_ERROR_PERMISSION_DENIED; break; case ENOENT: errorCode = DFM_IO_ERROR_NOT_FOUND; break; case EEXIST: case ENOTEMPTY: errorCode = DFM_IO_ERROR_EXISTS; break; case EISDIR: errorCode = DFM_IO_ERROR_IS_DIRECTORY; break; case ENOTDIR: errorCode = DFM_IO_ERROR_NOT_DIRECTORY; break; case EROFS: errorCode = DFM_IO_ERROR_READ_ONLY; break; case ENOSPC: errorCode = DFM_IO_ERROR_NO_SPACE; break; case ENAMETOOLONG: errorCode = DFM_IO_ERROR_FILENAME_TOO_LONG; break; case EINVAL: errorCode = DFM_IO_ERROR_INVALID_ARGUMENT; break; case EBUSY: errorCode = DFM_IO_ERROR_BUSY; break; case EXDEV: // Cross-device rename not supported by g_rename errorCode = DFM_IO_ERROR_NOT_SUPPORTED; break; default: errorCode = DFM_IO_ERROR_FAILED; break; } error.setCode(errorCode); } GFile *DOperatorPrivate::makeGFile(const QUrl &url) { return DLocalHelper::createGFile(url); } void DOperatorPrivate::checkAndResetCancel() { if (gcancellable) { if (!g_cancellable_is_cancelled(gcancellable)) g_cancellable_cancel(gcancellable); g_cancellable_reset(gcancellable); return; } gcancellable = g_cancellable_new(); } void DOperatorPrivate::renameCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { OperateFileOp *data = static_cast(userData); GFile *gfile = G_FILE(sourceObject); g_autoptr(GError) gerror = nullptr; auto dataUser = data->userData; GFile *gfileRet = g_file_set_display_name_finish(gfile, res, &gerror); g_object_unref(gfileRet); if (data->callback) data->callback(!gerror, dataUser); data->callback = nullptr; data->userData = nullptr; g_free(data); } void DOperatorPrivate::copyCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { OperateFileOp *data = static_cast(userData); GFile *gfile = G_FILE(sourceObject); g_autoptr(GError) gerror = nullptr; auto dataUser = data->userData; gboolean succ = g_file_copy_finish(gfile, res, &gerror); if (data->callback) data->callback(succ, dataUser); data->callback = nullptr; data->userData = nullptr; g_free(data); } void DOperatorPrivate::trashCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { OperateFileOp *data = static_cast(userData); GFile *gfile = G_FILE(sourceObject); auto dataUser = data->userData; g_autoptr(GError) gerror = nullptr; bool succ = g_file_trash_finish(gfile, res, &gerror); if (data->callback) data->callback(succ, dataUser); data->callback = nullptr; data->userData = nullptr; g_free(data); } void DOperatorPrivate::deleteCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { OperateFileOp *data = static_cast(userData); GFile *gfile = G_FILE(sourceObject); auto dataUser = data->userData; g_autoptr(GError) gerror = nullptr; bool succ = g_file_delete_finish(gfile, res, &gerror); if (data->callback) data->callback(succ, dataUser); data->callback = nullptr; data->userData = nullptr; g_free(data); } void DOperatorPrivate::touchCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { OperateFileOp *data = static_cast(userData); auto dataUser = data->userData; GFile *gfile = G_FILE(sourceObject); g_autoptr(GError) gerror = nullptr; g_autoptr(GFileOutputStream) stream = g_file_create_finish(gfile, res, &gerror); if (data->callback) data->callback(!stream, dataUser); data->callback = nullptr; data->userData = nullptr; g_free(data); } void DOperatorPrivate::makeDirCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { OperateFileOp *data = static_cast(userData); GFile *gfile = G_FILE(sourceObject); auto dataUser = data->userData; g_autoptr(GError) gerror = nullptr; bool succ = g_file_make_directory_finish(gfile, res, &gerror); if (data->callback) data->callback(succ, dataUser); data->callback = nullptr; data->userData = nullptr; g_free(data); } /************************************************ * DOperator ***********************************************/ DOperator::DOperator(const QUrl &uri) : d(new DOperatorPrivate(this)) { d->uri = uri; } DOperator::~DOperator() { if (d->gcancellable) { if (!g_cancellable_is_cancelled(d->gcancellable)) g_cancellable_cancel(d->gcancellable); g_object_unref(d->gcancellable); d->gcancellable = nullptr; } } QUrl DOperator::uri() const { return d->uri; } bool DOperator::renameFile(const QString &newName) { const QUrl &url = uri(); GError *gerror = nullptr; // name must deep copy, otherwise name freed and crash gchar *name = g_strdup(newName.toLocal8Bit().data()); GFile *gfile = d->makeGFile(url); GFile *gfile_ret = g_file_set_display_name(gfile, name, nullptr, &gerror); g_object_unref(gfile); g_free(name); if (!gfile_ret) { d->setErrorFromGError(gerror); g_error_free(gerror); return false; } if (gerror) g_error_free(gerror); g_object_unref(gfile_ret); return true; } bool DOperator::renameFile(const QUrl &toUrl) { const QUrl &fromUrl = uri(); const std::string &fromStr = fromUrl.toLocalFile().toStdString(); const std::string &toStr = toUrl.toLocalFile().toStdString(); if (fromStr.empty() || toStr.empty()) { d->error.setCode(DFM_IO_ERROR_INVALID_FILENAME); return false; } const bool ret = g_rename(fromStr.c_str(), toStr.c_str()) == 0; // set error info based on errno if (!ret) d->setErrorFromErrno(errno); return ret; } bool DOperator::copyFile(const QUrl &destUri, dfmio::DFile::CopyFlags flag, DOperator::ProgressCallbackFunc func, void *progressCallbackData) { GError *gerror = nullptr; const QUrl &urlFrom = uri(); GFile *gfile_from = d->makeGFile(urlFrom); GFile *gfile_to = d->makeGFile(destUri); GFile *gfileTarget = nullptr; if (DLocalHelper::checkGFileType(gfile_to, G_FILE_TYPE_DIRECTORY)) { char *basename = g_file_get_basename(gfile_from); gfileTarget = g_file_get_child(gfile_to, basename); g_free(basename); } else { gfileTarget = d->makeGFile(destUri); } g_object_unref(gfile_to); d->checkAndResetCancel(); bool ret = g_file_copy(gfile_from, gfileTarget, GFileCopyFlags(static_cast(flag)), d->gcancellable, func, progressCallbackData, &gerror); if (gerror) { d->setErrorFromGError(gerror); g_error_free(gerror); } g_object_unref(gfile_from); g_object_unref(gfileTarget); return ret; } bool DOperator::moveFile(const QUrl &destUri, dfmio::DFile::CopyFlags flag, DOperator::ProgressCallbackFunc func, void *progressCallbackData) { g_autoptr(GError) gerror = nullptr; const QUrl &from = uri(); g_autoptr(GFile) gfile_from = d->makeGFile(from); g_autoptr(GFile) gfile_to = d->makeGFile(destUri); bool ret = g_file_move(gfile_from, gfile_to, GFileCopyFlags(static_cast(flag)), nullptr, func, progressCallbackData, &gerror); if (gerror) d->setErrorFromGError(gerror); return ret; } void DOperator::renameFileAsync(const QString &newName, int ioPriority, DOperator::FileOperateCallbackFunc func, void *userData) { const QUrl &url = uri(); // name must deep copy, otherwise name freed and crash g_autofree gchar *gname = g_strdup(newName.toLocal8Bit().data()); g_autoptr(GFile) gfile = d->makeGFile(url); DOperatorPrivate::OperateFileOp *data = g_new0(DOperatorPrivate::OperateFileOp, 1); data->callback = func; data->userData = userData; g_file_set_display_name_async(gfile, gname, ioPriority, nullptr, DOperatorPrivate::renameCallback, data); } void DOperator::copyFileAsync(const QUrl &destUri, DFile::CopyFlags flag, DOperator::ProgressCallbackFunc progressfunc, void *progressCallbackData, int ioPriority, DOperator::FileOperateCallbackFunc operatefunc, void *userData) { const QUrl &urlFrom = uri(); g_autoptr(GFile) gfile_from = d->makeGFile(urlFrom); g_autoptr(GFile) gfile_to = d->makeGFile(destUri); g_autoptr(GFile) gfileTarget = nullptr; if (DLocalHelper::checkGFileType(gfile_to, G_FILE_TYPE_DIRECTORY)) { g_autofree char *basename = g_file_get_basename(gfile_from); gfileTarget = g_file_get_child(gfile_to, basename); } else { gfileTarget = d->makeGFile(destUri); } DOperatorPrivate::OperateFileOp *data = g_new0(DOperatorPrivate::OperateFileOp, 1); data->callback = operatefunc; data->userData = userData; g_file_copy_async(gfile_from, gfileTarget, GFileCopyFlags(static_cast(flag)), ioPriority, nullptr, progressfunc, progressCallbackData, DOperatorPrivate::copyCallback, data); } void DOperator::moveFileAsync(const QUrl &destUri, dfmio::DFile::CopyFlags flag, DOperator::ProgressCallbackFunc progressFunc, void *progressCallbackData, int ioPriority, DOperator::FileOperateCallbackFunc operatefunc, void *userData) { // TODO: // since 2.72, but current gio version is 2.58 // g_file_move_async(gfile_from, gfile_to, GFileCopyFlags(flag), ioPriority, nullptr, progressFunc, progressData, nullptr, userData); Q_UNUSED(ioPriority) Q_UNUSED(operatefunc) Q_UNUSED(userData) bool ret = moveFile(destUri, flag, progressFunc, progressCallbackData); if (operatefunc) operatefunc(ret, userData); } QString DOperator::trashFile() { g_autoptr(GError) gerror = nullptr; const QUrl &uri = this->uri(); g_autoptr(GFile) gfile = d->makeGFile(uri); QString targetTrashTime = QString::number(QDateTime::currentSecsSinceEpoch()) + "-"; bool ret = g_file_trash(gfile, nullptr, &gerror); targetTrashTime.append(QString::number(QDateTime::currentSecsSinceEpoch())); if (ret) return targetTrashTime; if (gerror) d->setErrorFromGError(gerror); return QString(); } bool DOperator::deleteFile() { g_autoptr(GError) gerror = nullptr; const QUrl &uri = this->uri(); g_autoptr(GFile) gfile = d->makeGFile(uri); bool ret = g_file_delete(gfile, nullptr, &gerror); if (gerror) d->setErrorFromGError(gerror); return ret; } bool DOperator::restoreFile(DOperator::ProgressCallbackFunc func, void *progressCallbackData) { GError *gerror = nullptr; const QUrl &uri = this->uri(); GFile *file = d->makeGFile(uri); GFileInfo *gfileinfo = g_file_query_info(file, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH, G_FILE_QUERY_INFO_NONE, nullptr, &gerror); g_object_unref(file); if (!gfileinfo) { if (gerror) { d->setErrorFromGError(gerror); g_error_free(gerror); } return false; } const char *srcPath = g_file_info_get_attribute_byte_string(gfileinfo, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH); if (srcPath == nullptr) { g_object_unref(gfileinfo); return false; } QUrl urlDest; urlDest.setPath(QString::fromLocal8Bit(srcPath)); urlDest.setScheme(QString("file")); bool ret = moveFile(urlDest, DFile::CopyFlag::kNone, func, progressCallbackData); g_object_unref(gfileinfo); return ret; } void DOperator::trashFileAsync(int ioPriority, DOperator::FileOperateCallbackFunc operatefunc, void *userData) { const QUrl &uri = this->uri(); g_autoptr(GFile) gfile = d->makeGFile(uri); DOperatorPrivate::OperateFileOp *data = g_new0(DOperatorPrivate::OperateFileOp, 1); data->callback = operatefunc; data->userData = userData; g_file_trash_async(gfile, ioPriority, nullptr, DOperatorPrivate::trashCallback, data); } void DOperator::deleteFileAsync(int ioPriority, DOperator::FileOperateCallbackFunc operatefunc, void *userData) { const QUrl &uri = this->uri(); g_autoptr(GFile) gfile = d->makeGFile(uri); DOperatorPrivate::OperateFileOp *data = g_new0(DOperatorPrivate::OperateFileOp, 1); data->callback = operatefunc; data->userData = userData; g_file_delete_async(gfile, ioPriority, nullptr, DOperatorPrivate::deleteCallback, data); } void DOperator::restoreFileAsync(DOperator::ProgressCallbackFunc func, void *progressCallbackData, int ioPriority, DOperator::FileOperateCallbackFunc operatefunc, void *userData) { // TODO: impl me! Q_UNUSED(ioPriority) Q_UNUSED(operatefunc) Q_UNUSED(userData) restoreFile(func, progressCallbackData); } bool DOperator::touchFile() { g_autoptr(GError) gerror = nullptr; const QUrl &uri = this->uri(); g_autoptr(GFile) gfile = d->makeGFile(uri); // if file exist, return failed g_autoptr(GFileOutputStream) stream = g_file_create(gfile, GFileCreateFlags::G_FILE_CREATE_REPLACE_DESTINATION, nullptr, &gerror); if (gerror) d->setErrorFromGError(gerror); return stream != nullptr; } bool DOperator::makeDirectory() { // only create direct path g_autoptr(GError) gerror = nullptr; const QUrl &uri = this->uri(); g_autoptr(GFile) gfile = d->makeGFile(uri); bool ret = g_file_make_directory(gfile, nullptr, &gerror); if (gerror) d->setErrorFromGError(gerror); return ret; } bool DOperator::createLink(const QUrl &link) { g_autoptr(GError) gerror = nullptr; g_autoptr(GFile) gfile = d->makeGFile(link); const QUrl &uri = this->uri(); const QString &linkValue = uri.toLocalFile(); bool ret = g_file_make_symbolic_link(gfile, linkValue.toLocal8Bit().data(), nullptr, &gerror); if (!ret) d->setErrorFromGError(gerror); return ret; } void DOperator::touchFileAsync(int ioPriority, DOperator::FileOperateCallbackFunc operatefunc, void *userData) { const QUrl &uri = this->uri(); g_autoptr(GFile) gfile = d->makeGFile(uri); DOperatorPrivate::OperateFileOp *data = g_new0(DOperatorPrivate::OperateFileOp, 1); data->callback = operatefunc; data->userData = userData; g_file_create_async(gfile, GFileCreateFlags::G_FILE_CREATE_REPLACE_DESTINATION, ioPriority, nullptr, DOperatorPrivate::touchCallback, data); } void DOperator::makeDirectoryAsync(int ioPriority, DOperator::FileOperateCallbackFunc operatefunc, void *userData) { // only create direct path const QUrl &uri = this->uri(); g_autoptr(GFile) gfile = d->makeGFile(uri); DOperatorPrivate::OperateFileOp *data = g_new0(DOperatorPrivate::OperateFileOp, 1); data->callback = operatefunc; data->userData = userData; g_file_make_directory_async(gfile, ioPriority, nullptr, DOperatorPrivate::makeDirCallback, data); } void DOperator::createLinkAsync(const QUrl &link, int ioPriority, DOperator::FileOperateCallbackFunc operatefunc, void *userData) { // TODO: imple me! Q_UNUSED(ioPriority) Q_UNUSED(operatefunc) Q_UNUSED(userData) createLink(link); } bool DOperator::setFileInfo(const DFileInfo &fileInfo) { const QUrl &uri = this->uri(); g_autoptr(GFile) gfile = d->makeGFile(uri); bool ret = true; for (const auto &[key, value] : DLocalHelper::attributeInfoMapFunc()) { g_autoptr(GError) gerror = nullptr; bool succ = DLocalHelper::setAttributeByGFile(gfile, key, fileInfo.attribute(key, nullptr), &gerror); if (!succ) ret = false; if (gerror) d->setErrorFromGError(gerror); } return ret; } bool DOperator::cancel() { if (d->gcancellable && !g_cancellable_is_cancelled(d->gcancellable)) g_cancellable_cancel(d->gcancellable); return true; } DFMIOError DOperator::lastError() const { return d->error; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/dwatcher.cpp000066400000000000000000000124111523477242100235340ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include "utils/dlocalhelper.h" #include "private/dwatcher_p.h" #include USING_IO_NAMESPACE /************************************************ * DWatcherPrivate ***********************************************/ DWatcherPrivate::DWatcherPrivate(DWatcher *q) : q(q) { } DWatcherPrivate::~DWatcherPrivate() { } GFileMonitor *DWatcherPrivate::createMonitor(GFile *gfile, DWatcher::WatchType type) { if (!gfile) { error.setCode(DFMIOErrorCode(DFM_IO_ERROR_NOT_FOUND)); return nullptr; } g_autoptr(GError) gerror = nullptr; g_autoptr(GCancellable) cancel = g_cancellable_new(); GFileMonitorFlags flags = GFileMonitorFlags(G_FILE_MONITOR_WATCH_MOUNTS | G_FILE_MONITOR_WATCH_MOVES); if (type == DWatcher::WatchType::kAuto) { gmonitor = g_file_monitor(gfile, flags, cancel, &gerror); } else if (type == DWatcher::WatchType::kDir) { gmonitor = g_file_monitor_directory(gfile, flags, cancel, &gerror); } else { gmonitor = g_file_monitor_file(gfile, flags, cancel, &gerror); } if (!gmonitor) { setErrorFromGError(gerror); return nullptr; } return gmonitor; } void DWatcherPrivate::setErrorFromGError(GError *gerror) { if (!gerror) return error.setCode(DFMIOErrorCode(DFM_IO_ERROR_FAILED)); error.setCode(DFMIOErrorCode(gerror->code)); if (error.code() == DFMIOErrorCode::DFM_IO_ERROR_FAILED) error.setMessage(gerror->message); } void DWatcherPrivate::watchCallback(GFileMonitor *monitor, GFile *child, GFile *other, GFileMonitorEvent eventType, gpointer userData) { Q_UNUSED(monitor); DWatcher *watcher = static_cast(userData); if (nullptr == watcher) { return; } QUrl childUrl = getUrl(child); QUrl otherUrl = getUrl(other); switch (eventType) { case G_FILE_MONITOR_EVENT_CHANGED: watcher->fileChanged(childUrl); break; case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: break; case G_FILE_MONITOR_EVENT_DELETED: watcher->fileDeleted(childUrl); break; case G_FILE_MONITOR_EVENT_CREATED: watcher->fileAdded(childUrl); break; case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: watcher->fileChanged(childUrl); break; case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: break; case G_FILE_MONITOR_EVENT_UNMOUNTED: watcher->fileDeleted(childUrl); break; case G_FILE_MONITOR_EVENT_MOVED_IN: watcher->fileAdded(childUrl); break; case G_FILE_MONITOR_EVENT_MOVED_OUT: watcher->fileDeleted(childUrl); break; case G_FILE_MONITOR_EVENT_RENAMED: watcher->fileRenamed(childUrl, otherUrl); break; //case G_FILE_MONITOR_EVENT_MOVED: default: g_assert_not_reached(); break; } } QUrl DWatcherPrivate::getUrl(GFile *file) { if (!file) return QUrl(); g_autofree gchar *path = g_file_get_path(file); QString strPath(path); // bug-204961 监视根目录时,监视的是"//"路径。所以监视上来的文件创建就是"//path"。 // 使用QUrl::fromLocalFile转换"//path",转换出来的url就是"file:///0.0.0/path"。 // 修改在路径前对"//"进行处理。处理完了在做后面的判断,如果路径错误重新使用uri. strPath.replace("//", "/"); if (!strPath.isEmpty()) { return QUrl::fromLocalFile(strPath); } else { g_autofree gchar *uri = g_file_get_uri(file); return QUrl::fromUserInput(uri); } } DWatcher::DWatcher(const QUrl &uri, QObject *parent) : QObject(parent), d(new DWatcherPrivate(this)) { d->uri = uri; } /************************************************ * DWatcher ***********************************************/ DWatcher::~DWatcher() { stop(); } QUrl DWatcher::uri() const { return d->uri; } void DWatcher::setTimeRate(int msec) { d->timeRate = msec; } int DWatcher::timeRate() const { return d->timeRate; } void DWatcher::setWatchType(DWatcher::WatchType type) { d->type = type; } DWatcher::WatchType DWatcher::watchType() const { return d->type; } bool DWatcher::running() const { return d->gmonitor != nullptr; } bool DWatcher::start(int timeRate) { // stop first stop(); const QUrl &uri = this->uri(); QString url = uri.url(); if (uri.scheme() == "file" && uri.path() == "/") url.append("/"); d->gfile = DLocalHelper::createGFile(url); d->gmonitor = d->createMonitor(d->gfile, d->type); if (!d->gmonitor) { g_object_unref(d->gfile); d->gfile = nullptr; return false; } g_file_monitor_set_rate_limit(d->gmonitor, timeRate); g_signal_connect(d->gmonitor, "changed", G_CALLBACK(&DWatcherPrivate::watchCallback), this); return true; } bool DWatcher::stop() { if (d->gmonitor) { g_file_monitor_cancel(d->gmonitor); g_object_unref(d->gmonitor); d->gmonitor = nullptr; } if (d->gfile) { g_object_unref(d->gfile); d->gfile = nullptr; } return true; } DFMIOError DWatcher::lastError() const { return d->error; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/private/000077500000000000000000000000001523477242100227025ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/private/denumerator_p.h000066400000000000000000000067231523477242100257270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DENUMERATOR_P_H #define DENUMERATOR_P_H #include #include #include #include #include #include #include #include #include #include #include #include BEGIN_IO_NAMESPACE class DEnumerator; class DFileInfo; class DEnumeratorPrivate : public QObject, public QEnableSharedFromThis { Q_OBJECT public: struct EnumUriData { QSharedPointer pointer { nullptr }; GFileEnumerator *enumerator { nullptr }; }; public: explicit DEnumeratorPrivate(DEnumerator *q); ~DEnumeratorPrivate(); bool init(const QUrl &url); bool init(); void clean(); bool createEnumerator(const QUrl &url, QPointer me); void checkAndResetCancel(); void setErrorFromGError(GError *gerror); bool checkFilter(); bool openDirByfts(); void insertSortFileInfoList(QList> &fileList, QList> &dirList, FTSENT *ent, FTS *fts, const QSet &hideList); void enumUriAsyncOvered(GList *files); void startAsyncIterator(); bool hasNext(); QList> fileInfoList(); void setQueryAttributes(const QString &attributes); char *filePath(const QUrl &url); QUrl buildUrl(const QUrl &url, const char *fileName); static void enumUriAsyncCallBack(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void moreFilesCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); Q_SIGNALS: void asyncIteratorOver(); public: DEnumerator *q { nullptr }; QMutex mutex; QWaitCondition waitCondition; DFMIOError error; GCancellable *cancellable { nullptr }; QStack stackEnumerator; QSharedPointer dfileInfoNext { nullptr }; QMap> hideListMap; QList> infoList; QList asyncInfos; QString queryAttributes; QStringList nameFilters; DEnumerator::DirFilters dirFilters { DEnumerator::DirFilter::kNoFilter }; DEnumerator::IteratorFlags iteratorFlags { DEnumerator::IteratorFlag::kNoIteratorFlags }; bool isMixDirAndFile { false }; Qt::SortOrder sortOrder { Qt::AscendingOrder }; DEnumerator::SortRoleCompareFlag sortRoleFlag { DEnumerator::SortRoleCompareFlag::kSortRoleCompareDefault }; QUrl uri; QUrl nextUrl; ulong enumTimeout { 0 }; bool ftsCanceled { false }; std::atomic_bool inited { false }; FTS *fts { nullptr }; bool enumSubDir { false }; bool enumLinks { false }; std::atomic_bool async { false }; std::atomic_bool asyncStoped { false }; std::atomic_bool asyncOvered { false }; private: bool shouldShowDotAndDotDot(const QString &fileName); bool checkEntryTypeFilter(); bool checkPermissionFilter(); bool checkSymlinkFilter(); bool checkHiddenFilter(); bool checkNameFilter(const QString &fileName); }; END_IO_NAMESPACE #endif // DENUMERATOR_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/private/dfile_p.h000066400000000000000000000061021523477242100244540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFILE_P_H #define DFILE_P_H #include #include #include BEGIN_IO_NAMESPACE class DFile; class DFilePrivate : public QObject { public: typedef struct { DFile::ReadCallbackFunc callback; gpointer userData; } ReadAsyncOp; typedef struct { DFile::ReadQCallbackFunc callback; char *data; gpointer userData; } ReadQAsyncOp; typedef struct { char *data; int ioPriority; DFile::ReadAllCallbackFunc callback; gpointer userData; QPointer me; } ReadAllAsyncOp; typedef struct { DFile::WriteCallbackFunc callback; gpointer userData; } WriteAsyncOp; typedef struct { DFileFuture *future = nullptr; QPointer me; } NormalFutureAsyncOp; typedef struct { QByteArray data; DFileFuture *future = nullptr; QPointer me; } ReadAllAsyncFutureOp; public: explicit DFilePrivate(DFile *q); ~DFilePrivate(); void setError(DFMIOError error); void setErrorFromGError(GError *gerror); void checkAndResetCancel(); GInputStream *inputStream(); GOutputStream *outputStream(); DFile::Permissions permissionsFromGFileInfo(GFileInfo *gfileinfo); bool checkOpenFlags(DFile::OpenFlags *modeIn); quint32 buildPermissions(DFile::Permissions permission); bool doOpen(DFile::OpenFlags mode); bool doClose(); QByteArray doReadAll(); qint64 doWrite(const char *data, qint64 maxSize); qint64 doWrite(const char *data); qint64 doWrite(const QByteArray &data); static void readAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void readQAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void readAllAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void writeAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void permissionsAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void existsAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void sizeAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void flushAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void writeAsyncFutureCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void readAsyncFutureCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); public: DFile *q { nullptr }; GIOStream *ioStream { nullptr }; GInputStream *iStream { nullptr }; GOutputStream *oStream { nullptr }; GCancellable *cancellable { nullptr }; DFMIOError error; QByteArray readAllAsyncRet; QUrl uri; bool isOpen { false }; }; END_IO_NAMESPACE #endif // DFILE_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/private/dfilefuture_p.h000066400000000000000000000007641523477242100257170ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFILEFUTURE_P_H #define DFILEFUTURE_P_H #include #include #include BEGIN_IO_NAMESPACE class DFileFuture; class DFuturePrivate { public: explicit DFuturePrivate(DFileFuture *qq); ~DFuturePrivate(); DFileFuture *q = nullptr; DFMIOError error; }; END_IO_NAMESPACE #endif // DFILEFUTURE_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/private/dfileinfo_p.h000066400000000000000000000075101523477242100253340ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DFILEINFO_P_H #define DFILEINFO_P_H #include "utils/dmediainfo.h" #include #include #include #include #include #include #include #include #include #include BEGIN_IO_NAMESPACE class DFileInfoPrivate : public QObject, public QSharedData { public: typedef struct { DFileInfo::InitQuerierAsyncCallback callback; gpointer userData; QPointer me; } QueryInfoAsyncOp; typedef struct { QPointer me; DFileFuture *future = nullptr; } QueryInfoAsyncOp2; explicit DFileInfoPrivate(DFileInfo *qq); DFileInfoPrivate(const DFileInfoPrivate &other); DFileInfoPrivate &operator=(const DFileInfoPrivate &other); virtual ~DFileInfoPrivate() override; void initNormal(); void attributeExtend(DFileInfo::MediaType type, QList ids, DFileInfo::AttributeExtendFuncCallback callback = nullptr); [[nodiscard]] DFileFuture *attributeExtend(DFileInfo::MediaType type, QList ids, int ioPriority, QObject *parent = nullptr); bool cancelAttributeExtend(); bool cancelAttributes(); void attributeExtendCallback(); void setErrorFromGError(GError *gerror); bool queryInfoSync(); void queryInfoAsync(int ioPriority = 0, DFileInfo::InitQuerierAsyncCallback func = nullptr, void *userData = nullptr); QVariant attributesBySelf(DFileInfo::AttributeID id); QVariant attributesFromUrl(DFileInfo::AttributeID id); void checkAndResetCancel(); [[nodiscard]] bool ensureStatxCached() const; [[nodiscard]] DFileFuture *initQuerierAsync(int ioPriority, QObject *parent = nullptr) const; [[nodiscard]] QFuture refreshAsync(); void cacheAttributes(); DFile::Permissions permissions() const; bool exists() const; static void queryInfoAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void queryInfoAsyncCallback2(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void freeQueryInfoAsyncOp(QueryInfoAsyncOp *op); static void freeQueryInfoAsyncOp2(QueryInfoAsyncOp2 *op); public: DFileInfo *q { nullptr }; QUrl uri = QUrl(); char *attributes { nullptr }; DFileInfo::FileQueryInfoFlags flag = DFileInfo::FileQueryInfoFlags::kTypeNone; QSharedPointer mediaInfo { nullptr }; QList extendIDs; QPointer future; // 使用 QPointer 自动追踪对象生命周期,防止访问已删除的对象 DFileInfo::MediaType mediaType = DFileInfo::MediaType::kGeneral; DFileInfo::AttributeExtendFuncCallback attributeExtendFuncCallback { nullptr }; QList attributesRealizationSelf; QList attributesNoBlockIO; GFile *gfile { nullptr }; GFileInfo *gfileinfo { nullptr }; std::atomic_bool initFinished { false }; std::atomic_bool infoReseted { false }; std::atomic_bool isQuquerying { false }; GCancellable *gcancellable { nullptr }; QFuture futureRefresh; std::atomic_bool stoped { false }; std::atomic_bool fileExists { false }; QMap caches; // statx 缓存:6 个时间属性 case 共享一次 statx 调用,避免重复系统调用 mutable struct statx statxBuf {}; mutable bool statxCached { false }; mutable bool statxValid { false }; std::atomic_bool cacheing { false }; std::atomic_bool refreshing { false }; QMutex mutex; DFMIOError error; }; END_IO_NAMESPACE #endif // DFILEINFO_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/private/doperator_p.h000066400000000000000000000025301523477242100253710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DOPERATOR_P_H #define DOPERATOR_P_H #include #include BEGIN_IO_NAMESPACE class DOperatorPrivate { public: typedef struct { DOperator::FileOperateCallbackFunc callback; gpointer userData; } OperateFileOp; explicit DOperatorPrivate(DOperator *q); virtual ~DOperatorPrivate(); void setErrorFromGError(GError *gerror); void setErrorFromErrno(int errnoValue); GFile *makeGFile(const QUrl &url); void checkAndResetCancel(); static void renameCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void copyCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void trashCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void deleteCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void touchCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void makeDirCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); public: DOperator *q { nullptr }; QUrl uri; GCancellable *gcancellable { nullptr }; DFMIOError error; }; END_IO_NAMESPACE #endif // DOPERATOR_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/private/dwatcher_p.h000066400000000000000000000020171523477242100251730ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DWATCHER_P_H #define DWATCHER_P_H #include #include #include #include #include BEGIN_IO_NAMESPACE class DWatcher; class DWatcherPrivate { public: explicit DWatcherPrivate(DWatcher *q); virtual ~DWatcherPrivate(); GFileMonitor *createMonitor(GFile *gfile, DWatcher::WatchType type); void setErrorFromGError(GError *gerror); static void watchCallback(GFileMonitor *monitor, GFile *child, GFile *other, GFileMonitorEvent eventType, gpointer userData); static QUrl getUrl(GFile *file); public: DWatcher *q { nullptr }; GFileMonitor *gmonitor { nullptr }; GFile *gfile { nullptr }; int timeRate { 200 }; DWatcher::WatchType type = DWatcher::WatchType::kAuto; QUrl uri; DFMIOError error; }; END_IO_NAMESPACE #endif // DWATCHER_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/trashhelper.cpp000066400000000000000000000072711523477242100242640ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include BEGIN_IO_NAMESPACE TrashHelper::TrashHelper() { } TrashHelper::~TrashHelper() { } void TrashHelper::setDeleteInfos(const QMap> &infos) { deleteInfos = infos; } bool TrashHelper::getTrashUrls(QList *trashUrls, QString *errorMsg) { if (!trashUrls) { if (errorMsg) *errorMsg = "trash Urls list is nullptr!"; qWarning() << "trash Urls list is nullptr!"; return false; } GFileEnumerator *enumerator { nullptr }; GFile *trash { nullptr }; GError *error { nullptr }; trash = g_file_new_for_uri("trash:///"); if (!trash) { // pause and emit error msg if (errorMsg) *errorMsg = "fialed to create trash file!"; qWarning() << "fialed to create trash file!"; return false; } enumerator = g_file_enumerate_children(trash, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_TRASH_DELETION_DATE "," G_FILE_ATTRIBUTE_TRASH_ORIG_PATH, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, &error); // 错误处理 if (!enumerator) { // pause and emit error msg qWarning() << "fialed to create trash iterator!"; if (errorMsg) *errorMsg = error ? error->message : "fialed to create trash iterator!"; if (error) g_error_free(error); g_object_unref(trash); return false; } GFileInfo *info { nullptr }; GFile *item { nullptr }; glong trash_time; const char *origpath; GFile *origfile { nullptr }; trashUrls->clear(); while ((info = g_file_enumerator_next_file(enumerator, nullptr, &error)) != nullptr) { /* Retrieve the original file uri */ origpath = g_file_info_get_attribute_byte_string(info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH); origfile = g_file_new_for_path(origpath); if (!origfile) { g_object_unref(info); continue; } gchar *origfile_uri = g_file_get_uri(origfile); auto deleteinfo = deleteInfos.value(QUrl::fromPercentEncoding(origfile_uri)); g_free(origfile_uri); if (!deleteinfo) { g_object_unref(origfile); g_object_unref(info); continue; } GDateTime *date; trash_time = 0; date = g_file_info_get_deletion_date(info); if (date) { trash_time = g_date_time_to_unix(date); g_date_time_unref(date); } if (deleteinfo->startTime <= trash_time && trash_time <= deleteinfo->endTime) { /* File in the trash */ item = g_file_get_child(trash, g_file_info_get_name(info)); if (!item) { g_object_unref(origfile); g_object_unref(info); continue; } gchar *item_uri = g_file_get_uri(item); trashUrls->append(QUrl(item_uri)); g_free(item_uri); g_object_unref(item); } g_object_unref(origfile); g_object_unref(info); if (trashUrls->size() >= deleteInfos.size()) break; } g_file_enumerator_close(enumerator, nullptr, nullptr); g_object_unref(enumerator); g_object_unref(trash); if (error) { if (errorMsg) *errorMsg = error->message; return false; } return true; } END_IO_NAMESPACE linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/utils/000077500000000000000000000000001523477242100223705ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/utils/dlocalhelper.cpp000066400000000000000000002010061523477242100255310ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "dlocalhelper.h" #include #include #include #include #include #include #include #include USING_IO_NAMESPACE namespace LocalFunc { static bool isFile(GFileInfo *fileInfo) { if (!fileInfo) return false; return g_file_info_get_file_type(fileInfo) == G_FILE_TYPE_REGULAR; } static bool isDir(GFileInfo *fileInfo) { if (!fileInfo) return false; return g_file_info_get_file_type(fileInfo) == G_FILE_TYPE_DIRECTORY; } static bool isRoot(const QString &path) { return path == "/"; } static QString fileName(GFileInfo *fileInfo) { if (!fileInfo) return QString(); const char *name = g_file_info_get_name(fileInfo); return QString::fromLocal8Bit(name); } static QString baseName(GFileInfo *fileInfo) { const QString &fullName = fileName(fileInfo); if (isDir(fileInfo)) return fullName; int pos2 = fullName.indexOf("."); if (pos2 == -1) return fullName; else return fullName.left(pos2); } static QString completeBaseName(GFileInfo *fileInfo) { const QString &fullName = fileName(fileInfo); if (isDir(fileInfo)) return fullName; int pos2 = fullName.lastIndexOf("."); if (pos2 == -1) return fullName; else return fullName.left(pos2); } static QString suffix(GFileInfo *fileInfo) { // path if (isDir(fileInfo)) return ""; const QString &fullName = fileName(fileInfo); int pos2 = fullName.lastIndexOf("."); if (pos2 == -1) return ""; else return fullName.mid(pos2 + 1); } static QString completeSuffix(GFileInfo *fileInfo) { if (isDir(fileInfo)) return ""; const QString &fullName = fileName(fileInfo); int pos2 = fullName.indexOf("."); if (pos2 == -1) return ""; else return fullName.mid(pos2 + 1); } static QString filePath(const QString &path) { g_autoptr(GFile) file = g_file_new_for_path(path.toStdString().c_str()); g_autofree gchar *gpath = g_file_get_path(file); // no blocking I/O if (gpath != nullptr) return QString::fromLocal8Bit(gpath); return ""; } static QString parentPath(const QString &path) { g_autoptr(GFile) file = g_file_new_for_path(path.toStdString().c_str()); g_autoptr(GFile) fileParent = g_file_get_parent(file); // no blocking I/O g_autofree gchar *gpath = g_file_get_path(fileParent); // no blocking I/O if (gpath != nullptr) return QString::fromLocal8Bit(gpath); return ""; } } // LocalFunc DLocalHelper::AttributeInfoMap &DLocalHelper::attributeInfoMapFunc() { static AttributeInfoMap kAttributeInfoMap { { DFileInfo::AttributeID::kStandardType, std::make_tuple("standard::type", 0) }, // G_FILE_ATTRIBUTE_STANDARD_TYPE { DFileInfo::AttributeID::kStandardIsHidden, std::make_tuple("standard::is-hidden", false) }, // G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN { DFileInfo::AttributeID::kStandardIsBackup, std::make_tuple("standard::is-backup", false) }, // G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP { DFileInfo::AttributeID::kStandardIsSymlink, std::make_tuple("standard::is-symlink", false) }, // G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK { DFileInfo::AttributeID::kStandardIsVirtual, std::make_tuple("standard::is-virtual", false) }, // G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL { DFileInfo::AttributeID::kStandardIsVolatile, std::make_tuple("standard::is-volatile", false) }, // G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE { DFileInfo::AttributeID::kStandardName, std::make_tuple("standard::name", "") }, // G_FILE_ATTRIBUTE_STANDARD_NAME { DFileInfo::AttributeID::kStandardDisplayName, std::make_tuple("standard::display-name", "") }, // G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME { DFileInfo::AttributeID::kStandardEditName, std::make_tuple("standard::edit-name", "") }, // G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME { DFileInfo::AttributeID::kStandardCopyName, std::make_tuple("standard::copy-name", "") }, // G_FILE_ATTRIBUTE_STANDARD_COPY_NAME { DFileInfo::AttributeID::kStandardIcon, std::make_tuple("standard::icon", 0) }, // G_FILE_ATTRIBUTE_STANDARD_ICON { DFileInfo::AttributeID::kStandardSymbolicIcon, std::make_tuple("standard::symbolic-icon", 0) }, // G_FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON { DFileInfo::AttributeID::kStandardContentType, std::make_tuple("standard::content-type", "") }, // G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE { DFileInfo::AttributeID::kStandardFastContentType, std::make_tuple("standard::fast-content-type", "") }, // G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE { DFileInfo::AttributeID::kStandardSize, std::make_tuple("standard::size", 0) }, // G_FILE_ATTRIBUTE_STANDARD_SIZE { DFileInfo::AttributeID::kStandardAllocatedSize, std::make_tuple("standard::allocated-size", 0) }, // G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE { DFileInfo::AttributeID::kStandardSymlinkTarget, std::make_tuple("standard::symlink-target", "") }, // G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET { DFileInfo::AttributeID::kStandardTargetUri, std::make_tuple("standard::target-uri", "") }, // G_FILE_ATTRIBUTE_STANDARD_TARGET_URI { DFileInfo::AttributeID::kStandardSortOrder, std::make_tuple("standard::sort-order", 0) }, // G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER { DFileInfo::AttributeID::kStandardDescription, std::make_tuple("standard::description", "") }, // G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION { DFileInfo::AttributeID::kEtagValue, std::make_tuple("etag::value", "") }, // G_FILE_ATTRIBUTE_ETAG_VALUE { DFileInfo::AttributeID::kIdFile, std::make_tuple("id::file", "") }, // G_FILE_ATTRIBUTE_ID_FILE { DFileInfo::AttributeID::kIdFilesystem, std::make_tuple("id::filesystem", "") }, // G_FILE_ATTRIBUTE_ID_FILESYSTEM { DFileInfo::AttributeID::kAccessCanRead, std::make_tuple("access::can-read", true) }, // G_FILE_ATTRIBUTE_ACCESS_CAN_READ { DFileInfo::AttributeID::kAccessCanWrite, std::make_tuple("access::can-write", true) }, // G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE { DFileInfo::AttributeID::kAccessCanExecute, std::make_tuple("access::can-execute", true) }, // G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE { DFileInfo::AttributeID::kAccessCanDelete, std::make_tuple("access::can-delete", true) }, // G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE { DFileInfo::AttributeID::kAccessCanTrash, std::make_tuple("access::can-trash", false) }, // G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH { DFileInfo::AttributeID::kAccessCanRename, std::make_tuple("access::can-rename", true) }, // G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME { DFileInfo::AttributeID::kMountableCanMount, std::make_tuple("mountable::can-mount", false) }, // G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT { DFileInfo::AttributeID::kMountableCanUnmount, std::make_tuple("mountable::can-unmount", false) }, // G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT { DFileInfo::AttributeID::kMountableCanEject, std::make_tuple("mountable::can-eject", false) }, // G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT { DFileInfo::AttributeID::kMountableUnixDevice, std::make_tuple("mountable::unix-device", 0) }, // G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE { DFileInfo::AttributeID::kMountableUnixDeviceFile, std::make_tuple("mountable::unix-device-file", "") }, // G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE { DFileInfo::AttributeID::kMountableHalUdi, std::make_tuple("mountable::hal-udi", "") }, // G_FILE_ATTRIBUTE_MOUNTABLE_HAL_UDI { DFileInfo::AttributeID::kMountableCanPoll, std::make_tuple("mountable::can-poll", false) }, // G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL { DFileInfo::AttributeID::kMountableIsMediaCheckAutomatic, std::make_tuple("mountable::is-media-check-automatic", false) }, // G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC { DFileInfo::AttributeID::kMountableCanStart, std::make_tuple("mountable::can-start", false) }, // G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START { DFileInfo::AttributeID::kMountableCanStartDegraded, std::make_tuple("mountable::can-start-degraded", false) }, // G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START_DEGRADED { DFileInfo::AttributeID::kMountableCanStop, std::make_tuple("mountable::can-stop", false) }, // G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP { DFileInfo::AttributeID::kMountableStartStopType, std::make_tuple("mountable::start-stop-type", 0) }, // G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE { DFileInfo::AttributeID::kTimeModified, std::make_tuple("time::modified", 0) }, // G_FILE_ATTRIBUTE_TIME_MODIFIED { DFileInfo::AttributeID::kTimeModifiedUsec, std::make_tuple("time::modified-usec", 0) }, // G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC { DFileInfo::AttributeID::kTimeAccess, std::make_tuple("time::access", 0) }, // G_FILE_ATTRIBUTE_TIME_ACCESS { DFileInfo::AttributeID::kTimeAccessUsec, std::make_tuple("time::access-usec", 0) }, // G_FILE_ATTRIBUTE_TIME_ACCESS_USEC { DFileInfo::AttributeID::kTimeChanged, std::make_tuple("time::changed", 0) }, // G_FILE_ATTRIBUTE_TIME_CHANGED { DFileInfo::AttributeID::kTimeChangedUsec, std::make_tuple("time::changed-usec", 0) }, // G_FILE_ATTRIBUTE_TIME_CHANGED_USEC { DFileInfo::AttributeID::kTimeCreated, std::make_tuple("time::created", 0) }, // G_FILE_ATTRIBUTE_TIME_CREATED { DFileInfo::AttributeID::kTimeCreatedUsec, std::make_tuple("time::created-usec", 0) }, // G_FILE_ATTRIBUTE_TIME_CREATED_USEC { DFileInfo::AttributeID::kUnixDevice, std::make_tuple("unix::device", 0) }, // G_FILE_ATTRIBUTE_UNIX_DEVICE { DFileInfo::AttributeID::kUnixInode, std::make_tuple("unix::inode", 0) }, // G_FILE_ATTRIBUTE_UNIX_INODE { DFileInfo::AttributeID::kUnixMode, std::make_tuple("unix::mode", 0) }, // G_FILE_ATTRIBUTE_UNIX_MODE { DFileInfo::AttributeID::kUnixNlink, std::make_tuple("unix::nlink", 0) }, // G_FILE_ATTRIBUTE_UNIX_NLINK { DFileInfo::AttributeID::kUnixUID, std::make_tuple("unix::uid", 0) }, // G_FILE_ATTRIBUTE_UNIX_UID { DFileInfo::AttributeID::kUnixGID, std::make_tuple("unix::gid", 0) }, // G_FILE_ATTRIBUTE_UNIX_GID { DFileInfo::AttributeID::kUnixRdev, std::make_tuple("unix::rdev", 0) }, // G_FILE_ATTRIBUTE_UNIX_RDEV { DFileInfo::AttributeID::kUnixBlockSize, std::make_tuple("unix::block-size", 0) }, // G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE { DFileInfo::AttributeID::kUnixBlocks, std::make_tuple("unix::blocks", 0) }, // G_FILE_ATTRIBUTE_UNIX_BLOCKS { DFileInfo::AttributeID::kUnixIsMountPoint, std::make_tuple("unix::is-mountpoint", false) }, // G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT { DFileInfo::AttributeID::kDosIsArchive, std::make_tuple("dos::is-archive", false) }, // G_FILE_ATTRIBUTE_DOS_IS_ARCHIVE { DFileInfo::AttributeID::kDosIsSystem, std::make_tuple("dos::is-system", false) }, // G_FILE_ATTRIBUTE_DOS_IS_SYSTEM { DFileInfo::AttributeID::kOwnerUser, std::make_tuple("owner::user", "") }, // G_FILE_ATTRIBUTE_OWNER_USER { DFileInfo::AttributeID::kOwnerUserReal, std::make_tuple("owner::user-real", "") }, // G_FILE_ATTRIBUTE_OWNER_USER_REAL { DFileInfo::AttributeID::kOwnerGroup, std::make_tuple("owner::group", "") }, // G_FILE_ATTRIBUTE_OWNER_GROUP { DFileInfo::AttributeID::kThumbnailPath, std::make_tuple("thumbnail::path", "") }, // G_FILE_ATTRIBUTE_THUMBNAIL_PATH { DFileInfo::AttributeID::kThumbnailFailed, std::make_tuple("thumbnail::failed", false) }, // G_FILE_ATTRIBUTE_THUMBNAILING_FAILED { DFileInfo::AttributeID::kThumbnailIsValid, std::make_tuple("thumbnail::is-valid", false) }, // G_FILE_ATTRIBUTE_THUMBNAIL_IS_VALID { DFileInfo::AttributeID::kPreviewIcon, std::make_tuple("preview::icon", 0) }, // G_FILE_ATTRIBUTE_PREVIEW_ICON { DFileInfo::AttributeID::kFileSystemSize, std::make_tuple("filesystem::size", 0) }, // G_FILE_ATTRIBUTE_FILESYSTEM_SIZE { DFileInfo::AttributeID::kFileSystemFree, std::make_tuple("filesystem::free", 0) }, // G_FILE_ATTRIBUTE_FILESYSTEM_FREE { DFileInfo::AttributeID::kFileSystemUsed, std::make_tuple("filesystem::used", 0) }, // G_FILE_ATTRIBUTE_FILESYSTEM_USED { DFileInfo::AttributeID::kFileSystemType, std::make_tuple("filesystem::type", "") }, // G_FILE_ATTRIBUTE_FILESYSTEM_TYPE { DFileInfo::AttributeID::kFileSystemReadOnly, std::make_tuple("filesystem::readonly", false) }, // G_FILE_ATTRIBUTE_FILESYSTEM_READONLY { DFileInfo::AttributeID::kFileSystemUsePreview, std::make_tuple("filesystem::use-preview", 0) }, // G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW { DFileInfo::AttributeID::kFileSystemRemote, std::make_tuple("filesystem::remote", false) }, // G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE { DFileInfo::AttributeID::kGvfsBackend, std::make_tuple("gvfs::backend", "") }, // G_FILE_ATTRIBUTE_GVFS_BACKEND { DFileInfo::AttributeID::kSelinuxContext, std::make_tuple("selinux::context", "") }, // G_FILE_ATTRIBUTE_SELINUX_CONTEXT { DFileInfo::AttributeID::kTrashItemCount, std::make_tuple("trash::item-count", 0) }, // G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT { DFileInfo::AttributeID::kTrashDeletionDate, std::make_tuple("trash::deletion-date", "") }, // G_FILE_ATTRIBUTE_TRASH_DELETION_DATE { DFileInfo::AttributeID::kTrashOrigPath, std::make_tuple("trash::orig-path", "") }, // G_FILE_ATTRIBUTE_TRASH_ORIG_PATH { DFileInfo::AttributeID::kRecentModified, std::make_tuple("recent::modified", 0) }, // G_FILE_ATTRIBUTE_RECENT_MODIFIED { DFileInfo::AttributeID::kCustomStart, std::make_tuple("custom-start", 0) }, { DFileInfo::AttributeID::kStandardIsFile, std::make_tuple("standard::is-file", false) }, { DFileInfo::AttributeID::kStandardIsDir, std::make_tuple("standard::is-dir", false) }, { DFileInfo::AttributeID::kStandardIsRoot, std::make_tuple("standard::is-root", false) }, { DFileInfo::AttributeID::kStandardSuffix, std::make_tuple("standard::suffix", "") }, { DFileInfo::AttributeID::kStandardCompleteSuffix, std::make_tuple("standard::complete-suffix", "") }, { DFileInfo::AttributeID::kStandardFilePath, std::make_tuple("standard::file-path", "") }, { DFileInfo::AttributeID::kStandardParentPath, std::make_tuple("standard::parent-path", "") }, { DFileInfo::AttributeID::kStandardBaseName, std::make_tuple("standard::base-name", "") }, { DFileInfo::AttributeID::kStandardFileName, std::make_tuple("standard::file-name", "") }, { DFileInfo::AttributeID::kStandardCompleteBaseName, std::make_tuple("standard::complete-base-name", "") }, }; return kAttributeInfoMap; } QSharedPointer DLocalHelper::createFileInfoByUri(const QUrl &uri, const char *attributes /*= "*"*/, const DFMIO::DFileInfo::FileQueryInfoFlags flag /*= DFMIO::DFileInfo::FileQueryInfoFlags::TypeNone*/) { return QSharedPointer(new DFileInfo(uri, attributes, flag)); } QSharedPointer DLocalHelper::createFileInfoByUri(const QUrl &uri, GFileInfo *gfileInfo, const char *attributes, const DFileInfo::FileQueryInfoFlags flag) { return QSharedPointer(new DFileInfo(uri, gfileInfo, attributes, flag)); } QVariant DLocalHelper::attributeFromGFileInfo(GFileInfo *gfileinfo, DFileInfo::AttributeID id, DFMIOErrorCode &errorcode) { if (!gfileinfo) return QVariant(); // check custom attribute if (id > DFileInfo::AttributeID::kCustomStart) { return QVariant(); } switch (id) { // uint32_t case DFileInfo::AttributeID::kStandardType: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_TYPE, errorcode); case DFileInfo::AttributeID::kMountableUnixDevice: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE, errorcode); case DFileInfo::AttributeID::kMountableStartStopType: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE, errorcode); case DFileInfo::AttributeID::kTimeModifiedUsec: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, errorcode); case DFileInfo::AttributeID::kTimeAccessUsec: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, errorcode); case DFileInfo::AttributeID::kTimeChangedUsec: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, errorcode); case DFileInfo::AttributeID::kUnixDevice: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_UNIX_DEVICE, errorcode); case DFileInfo::AttributeID::kUnixMode: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_UNIX_MODE, errorcode); case DFileInfo::AttributeID::kUnixNlink: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_UNIX_NLINK, errorcode); case DFileInfo::AttributeID::kUnixUID: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_UNIX_UID, errorcode); case DFileInfo::AttributeID::kUnixGID: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_UNIX_GID, errorcode); case DFileInfo::AttributeID::kUnixRdev: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_UNIX_RDEV, errorcode); case DFileInfo::AttributeID::kUnixBlockSize: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE, errorcode); case DFileInfo::AttributeID::kFileSystemUsePreview: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, errorcode); case DFileInfo::AttributeID::kTrashItemCount: return getGFileInfoUint32(gfileinfo, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, errorcode); // int32_t case DFileInfo::AttributeID::kStandardSortOrder: return getGFileInfoInt32(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER, errorcode); // uint64_t case DFileInfo::AttributeID::kStandardSize: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_SIZE, errorcode); case DFileInfo::AttributeID::kStandardAllocatedSize: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE, errorcode); case DFileInfo::AttributeID::kTimeModified: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED, errorcode); case DFileInfo::AttributeID::kTimeAccess: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_TIME_ACCESS, errorcode); case DFileInfo::AttributeID::kTimeChanged: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_TIME_CHANGED, errorcode); case DFileInfo::AttributeID::kUnixInode: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_UNIX_INODE, errorcode); case DFileInfo::AttributeID::kUnixBlocks: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_UNIX_BLOCKS, errorcode); case DFileInfo::AttributeID::kFileSystemSize: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, errorcode); case DFileInfo::AttributeID::kFileSystemFree: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, errorcode); case DFileInfo::AttributeID::kFileSystemUsed: return getGFileInfoUint64(gfileinfo, G_FILE_ATTRIBUTE_FILESYSTEM_USED, errorcode); // Int64_t case DFileInfo::AttributeID::kRecentModified: return getGFileInfoInt64(gfileinfo, G_FILE_ATTRIBUTE_RECENT_MODIFIED, errorcode); // bool case DFileInfo::AttributeID::kStandardIsHidden: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, errorcode); case DFileInfo::AttributeID::kStandardIsBackup: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP, errorcode); case DFileInfo::AttributeID::kStandardIsSymlink: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK, errorcode); case DFileInfo::AttributeID::kStandardIsVirtual: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, errorcode); case DFileInfo::AttributeID::kStandardIsVolatile: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE, errorcode); case DFileInfo::AttributeID::kAccessCanRead: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, errorcode); case DFileInfo::AttributeID::kAccessCanWrite: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, errorcode); case DFileInfo::AttributeID::kAccessCanExecute: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, errorcode); case DFileInfo::AttributeID::kAccessCanDelete: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, errorcode); case DFileInfo::AttributeID::kAccessCanTrash: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, errorcode); case DFileInfo::AttributeID::kAccessCanRename: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, errorcode); case DFileInfo::AttributeID::kMountableCanMount: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, errorcode); case DFileInfo::AttributeID::kMountableCanUnmount: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, errorcode); case DFileInfo::AttributeID::kMountableCanEject: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT, errorcode); case DFileInfo::AttributeID::kMountableCanPoll: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL, errorcode); case DFileInfo::AttributeID::kMountableIsMediaCheckAutomatic: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC, errorcode); case DFileInfo::AttributeID::kMountableCanStart: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START, errorcode); case DFileInfo::AttributeID::kMountableCanStartDegraded: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START_DEGRADED, errorcode); case DFileInfo::AttributeID::kMountableCanStop: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP, errorcode); case DFileInfo::AttributeID::kUnixIsMountPoint: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT, errorcode); case DFileInfo::AttributeID::kDosIsArchive: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_DOS_IS_ARCHIVE, errorcode); case DFileInfo::AttributeID::kDosIsSystem: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_DOS_IS_SYSTEM, errorcode); case DFileInfo::AttributeID::kFileSystemReadOnly: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, errorcode); case DFileInfo::AttributeID::kFileSystemRemote: return getGFileInfoBool(gfileinfo, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, errorcode); // byte string case DFileInfo::AttributeID::kStandardName: return getGFileInfoByteString(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_NAME, errorcode); case DFileInfo::AttributeID::kStandardSymlinkTarget: return getGFileInfoByteString(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, errorcode); case DFileInfo::AttributeID::kTrashOrigPath: return getGFileInfoByteString(gfileinfo, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH, errorcode); case DFileInfo::AttributeID::kThumbnailPath: return getGFileInfoByteString(gfileinfo, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, errorcode); // string case DFileInfo::AttributeID::kStandardDisplayName: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, errorcode); case DFileInfo::AttributeID::kStandardEditName: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME, errorcode); case DFileInfo::AttributeID::kStandardCopyName: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME, errorcode); case DFileInfo::AttributeID::kStandardContentType: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, errorcode); case DFileInfo::AttributeID::kStandardFastContentType: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, errorcode); case DFileInfo::AttributeID::kStandardTargetUri: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, errorcode); case DFileInfo::AttributeID::kStandardDescription: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION, errorcode); case DFileInfo::AttributeID::kEtagValue: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_ETAG_VALUE, errorcode); case DFileInfo::AttributeID::kIdFile: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_ID_FILE, errorcode); case DFileInfo::AttributeID::kIdFilesystem: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_ID_FILESYSTEM, errorcode); case DFileInfo::AttributeID::kMountableUnixDeviceFile: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE, errorcode); case DFileInfo::AttributeID::kMountableHalUdi: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_MOUNTABLE_HAL_UDI, errorcode); case DFileInfo::AttributeID::kOwnerUser: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_OWNER_USER, errorcode); case DFileInfo::AttributeID::kOwnerUserReal: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_OWNER_USER_REAL, errorcode); case DFileInfo::AttributeID::kOwnerGroup: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_OWNER_GROUP, errorcode); case DFileInfo::AttributeID::kFileSystemType: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, errorcode); case DFileInfo::AttributeID::kGvfsBackend: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_GVFS_BACKEND, errorcode); case DFileInfo::AttributeID::kSelinuxContext: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_SELINUX_CONTEXT, errorcode); case DFileInfo::AttributeID::kTrashDeletionDate: return getGFileInfoString(gfileinfo, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE, errorcode); // object case DFileInfo::AttributeID::kStandardIcon: return getGFileInfoIcon(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_ICON, errorcode); case DFileInfo::AttributeID::kPreviewIcon: return getGFileInfoIcon(gfileinfo, G_FILE_ATTRIBUTE_PREVIEW_ICON, errorcode); case DFileInfo::AttributeID::kStandardSymbolicIcon: return getGFileInfoIcon(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON, errorcode); default: return QVariant(); } } QVariant DLocalHelper::customAttributeFromPathAndInfo(const QString &path, GFileInfo *fileInfo, DFileInfo::AttributeID id) { if (id < DFileInfo::AttributeID::kCustomStart) return QVariant(); switch (id) { case DFileInfo::AttributeID::kStandardIsFile: { return LocalFunc::isFile(fileInfo); } case DFileInfo::AttributeID::kStandardIsDir: { return LocalFunc::isDir(fileInfo); } case DFileInfo::AttributeID::kStandardIsRoot: { return LocalFunc::isRoot(path); } case DFileInfo::AttributeID::kStandardSuffix: { return LocalFunc::suffix(fileInfo); } case DFileInfo::AttributeID::kStandardCompleteSuffix: { return LocalFunc::completeSuffix(fileInfo); } case DFileInfo::AttributeID::kStandardFilePath: { return LocalFunc::filePath(path); } case DFileInfo::AttributeID::kStandardParentPath: { return LocalFunc::parentPath(path); } case DFileInfo::AttributeID::kStandardBaseName: { return LocalFunc::baseName(fileInfo); } case DFileInfo::AttributeID::kStandardFileName: { return LocalFunc::fileName(fileInfo); } case DFileInfo::AttributeID::kStandardCompleteBaseName: { return LocalFunc::completeBaseName(fileInfo); } default: return QVariant(); } } QVariant DLocalHelper::customAttributeFromPath(const QString &path, DFileInfo::AttributeID id) { Q_UNUSED(path) Q_UNUSED(id) return QVariant(); } bool DLocalHelper::setAttributeByGFile(GFile *gfile, DFileInfo::AttributeID id, const QVariant &value, GError **gerror) { if (!gfile) { return false; } switch (id) { // uint32_t case DFileInfo::AttributeID::kStandardType: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_STANDARD_TYPE, value, gerror); case DFileInfo::AttributeID::kMountableUnixDevice: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE, value, gerror); case DFileInfo::AttributeID::kMountableStartStopType: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE, value, gerror); case DFileInfo::AttributeID::kTimeModifiedUsec: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, value, gerror); case DFileInfo::AttributeID::kTimeAccessUsec: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, value, gerror); case DFileInfo::AttributeID::kTimeChangedUsec: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, value, gerror); case DFileInfo::AttributeID::kTimeCreatedUsec: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_TIME_CREATED_USEC, value, gerror); case DFileInfo::AttributeID::kUnixDevice: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_UNIX_DEVICE, value, gerror); case DFileInfo::AttributeID::kUnixMode: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_UNIX_MODE, value, gerror); case DFileInfo::AttributeID::kUnixNlink: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_UNIX_NLINK, value, gerror); case DFileInfo::AttributeID::kUnixUID: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_UNIX_UID, value, gerror); case DFileInfo::AttributeID::kUnixGID: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_UNIX_GID, value, gerror); case DFileInfo::AttributeID::kUnixRdev: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_UNIX_RDEV, value, gerror); case DFileInfo::AttributeID::kUnixBlockSize: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE, value, gerror); case DFileInfo::AttributeID::kFileSystemUsePreview: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, value, gerror); case DFileInfo::AttributeID::kTrashItemCount: return setGFileInfoUint32(gfile, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, value, gerror); // int32_t case DFileInfo::AttributeID::kStandardSortOrder: return setGFileInfoInt32(gfile, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER, value, gerror); // uint64_t case DFileInfo::AttributeID::kStandardSize: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_STANDARD_SIZE, value, gerror); case DFileInfo::AttributeID::kStandardAllocatedSize: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE, value, gerror); case DFileInfo::AttributeID::kTimeModified: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_TIME_MODIFIED, value, gerror); case DFileInfo::AttributeID::kTimeAccess: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_TIME_ACCESS, value, gerror); case DFileInfo::AttributeID::kTimeChanged: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_TIME_CHANGED, value, gerror); case DFileInfo::AttributeID::kTimeCreated: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_TIME_CREATED, value, gerror); case DFileInfo::AttributeID::kUnixInode: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_UNIX_INODE, value, gerror); case DFileInfo::AttributeID::kUnixBlocks: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_UNIX_BLOCKS, value, gerror); case DFileInfo::AttributeID::kFileSystemSize: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, value, gerror); case DFileInfo::AttributeID::kFileSystemFree: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, value, gerror); case DFileInfo::AttributeID::kFileSystemUsed: return setGFileInfoUint64(gfile, G_FILE_ATTRIBUTE_FILESYSTEM_USED, value, gerror); // int64_t case DFileInfo::AttributeID::kRecentModified: return setGFileInfoInt64(gfile, G_FILE_ATTRIBUTE_RECENT_MODIFIED, value, gerror); // bool case DFileInfo::AttributeID::kStandardIsHidden: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, value, gerror); case DFileInfo::AttributeID::kStandardIsBackup: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP, value, gerror); case DFileInfo::AttributeID::kStandardIsSymlink: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK, value, gerror); case DFileInfo::AttributeID::kStandardIsVirtual: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, value, gerror); case DFileInfo::AttributeID::kStandardIsVolatile: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE, value, gerror); case DFileInfo::AttributeID::kAccessCanRead: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, value, gerror); case DFileInfo::AttributeID::kAccessCanWrite: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, value, gerror); case DFileInfo::AttributeID::kAccessCanExecute: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, value, gerror); case DFileInfo::AttributeID::kAccessCanDelete: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, value, gerror); case DFileInfo::AttributeID::kAccessCanTrash: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, value, gerror); case DFileInfo::AttributeID::kAccessCanRename: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, value, gerror); case DFileInfo::AttributeID::kMountableCanMount: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, value, gerror); case DFileInfo::AttributeID::kMountableCanUnmount: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, value, gerror); case DFileInfo::AttributeID::kMountableCanEject: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT, value, gerror); case DFileInfo::AttributeID::kMountableCanPoll: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL, value, gerror); case DFileInfo::AttributeID::kMountableIsMediaCheckAutomatic: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC, value, gerror); case DFileInfo::AttributeID::kMountableCanStart: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START, value, gerror); case DFileInfo::AttributeID::kMountableCanStartDegraded: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START_DEGRADED, value, gerror); case DFileInfo::AttributeID::kMountableCanStop: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP, value, gerror); case DFileInfo::AttributeID::kUnixIsMountPoint: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT, value, gerror); case DFileInfo::AttributeID::kDosIsArchive: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_DOS_IS_ARCHIVE, value, gerror); case DFileInfo::AttributeID::kDosIsSystem: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_DOS_IS_SYSTEM, value, gerror); case DFileInfo::AttributeID::kFileSystemReadOnly: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, value, gerror); case DFileInfo::AttributeID::kFileSystemRemote: return setGFileInfoBool(gfile, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, value, gerror); // byte string case DFileInfo::AttributeID::kStandardName: return setGFileInfoByteString(gfile, G_FILE_ATTRIBUTE_STANDARD_NAME, value, gerror); case DFileInfo::AttributeID::kStandardSymlinkTarget: return setGFileInfoByteString(gfile, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, value, gerror); case DFileInfo::AttributeID::kThumbnailPath: return setGFileInfoByteString(gfile, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, value, gerror); // string case DFileInfo::AttributeID::kStandardDisplayName: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, value, gerror); case DFileInfo::AttributeID::kStandardEditName: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME, value, gerror); case DFileInfo::AttributeID::kStandardCopyName: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME, value, gerror); case DFileInfo::AttributeID::kStandardContentType: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, value, gerror); case DFileInfo::AttributeID::kStandardFastContentType: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, value, gerror); case DFileInfo::AttributeID::kStandardTargetUri: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, value, gerror); case DFileInfo::AttributeID::kStandardDescription: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION, value, gerror); case DFileInfo::AttributeID::kEtagValue: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_ETAG_VALUE, value, gerror); case DFileInfo::AttributeID::kIdFile: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_ID_FILE, value, gerror); case DFileInfo::AttributeID::kIdFilesystem: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_ID_FILESYSTEM, value, gerror); case DFileInfo::AttributeID::kMountableUnixDeviceFile: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE, value, gerror); case DFileInfo::AttributeID::kMountableHalUdi: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_MOUNTABLE_HAL_UDI, value, gerror); case DFileInfo::AttributeID::kOwnerUser: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_OWNER_USER, value, gerror); case DFileInfo::AttributeID::kOwnerUserReal: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_OWNER_USER_REAL, value, gerror); case DFileInfo::AttributeID::kOwnerGroup: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_OWNER_GROUP, value, gerror); case DFileInfo::AttributeID::kFileSystemType: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, value, gerror); case DFileInfo::AttributeID::kGvfsBackend: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_GVFS_BACKEND, value, gerror); case DFileInfo::AttributeID::kSelinuxContext: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_SELINUX_CONTEXT, value, gerror); case DFileInfo::AttributeID::kTrashDeletionDate: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE, value, gerror); case DFileInfo::AttributeID::kTrashOrigPath: return setGFileInfoString(gfile, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH, value, gerror); // object case DFileInfo::AttributeID::kStandardIcon: case DFileInfo::AttributeID::kStandardSymbolicIcon: case DFileInfo::AttributeID::kPreviewIcon: { // g_file_info_set_attribute_object(gfileinfo, key, value.object()); // TODO(lanxs) return true; } default: return true; } } bool DLocalHelper::setAttributeByGFileInfo(GFileInfo *gfileinfo, DFileInfo::AttributeID id, const QVariant &value) { // discard Q_UNUSED(gfileinfo) Q_UNUSED(id) Q_UNUSED(value) return false; } std::string DLocalHelper::attributeStringById(DFileInfo::AttributeID id) { if (DLocalHelper::attributeInfoMapFunc().count(id) > 0) { const std::string &value = std::get<0>(DLocalHelper::attributeInfoMapFunc().at(id)); return value; } return ""; } QSet DLocalHelper::hideListFromUrl(const QUrl &url) { g_autofree char *contents = nullptr; g_autoptr(GError) error = nullptr; gsize len = 0; g_autoptr(GFile) hiddenFile = createGFile(url); const bool succ = g_file_load_contents(hiddenFile, nullptr, &contents, &len, nullptr, &error); if (succ) { if (contents && len > 0) { QString dataStr(contents); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QStringList strs { dataStr.split('\n', Qt::SkipEmptyParts) }; return QSet { strs.begin(), strs.end() }; #else const QStringList strs { dataStr.split('\n', QString::SkipEmptyParts) }; return QSet::fromList(strs); #endif } } return {}; } bool DLocalHelper::fileIsHidden(const DFileInfo *dfileinfo, const QSet &hideList, const bool needRead) { if (!dfileinfo) return false; // if file not exist DFileInfo::AttributeID::kStandardFileName 返回有错误 bug-200989 const QString &fileName = dfileinfo->uri().fileName(); if (fileName.startsWith(".")) { return true; } else { if (hideList.isEmpty() && needRead) { const QString &hiddenPath = dfileinfo->attribute(DFileInfo::AttributeID::kStandardParentPath, nullptr).toString() + "/.hidden"; const QSet &hideList = DLocalHelper::hideListFromUrl(QUrl::fromLocalFile(hiddenPath)); if (hideList.contains(fileName)) return true; } else { return hideList.contains(fileName); } } return false; } bool DLocalHelper::checkGFileType(GFile *file, GFileType type) { if (!file) return false; g_autoptr(GFileInfo) gfileinfo = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, nullptr, nullptr); if (!gfileinfo) return false; return g_file_info_get_file_type(gfileinfo) == type; } // fix 多线程排序时,该处的全局变量在compareByString函数中可能导致软件崩溃 // QCollator sortCollator; class DCollator : public QCollator { public: DCollator() : QCollator() { setNumericMode(true); setCaseSensitivity(Qt::CaseInsensitive); } }; bool DLocalHelper::isNumOrChar(const QChar ch) { QChar normalized; if (isFullWidthChar(ch, normalized)) return isNumOrChar(normalized); int chValue = ch.unicode(); return (chValue >= 48 && chValue <= 57) || (chValue >= 65 && chValue <= 90) || (chValue >= 97 && chValue <= 122); } bool DLocalHelper::isNumber(const QChar ch) { QChar number; if (isFullWidthChar(ch, number)) { return (number.unicode() >= '0' && number.unicode() <= '9'); } return ch.isDigit(); } bool DLocalHelper::isSymbol(const QChar ch) { // 如果是高代理项,不应该单独判断 if (ch.isHighSurrogate() || ch.isLowSurrogate()) return false; QChar normalized; if (isFullWidthChar(ch, normalized)) { return isSymbol(normalized); } return ch.script() != QChar::Script_Han && !isNumOrChar(ch); } bool DLocalHelper::isFullWidthChar(const QChar ch, QChar &normalized) { // 全角字符的 Unicode 范围 ushort unicode = ch.unicode(); // 处理全角数字 (0xFF10-0xFF19) if (unicode >= 0xFF10 && unicode <= 0xFF19) { normalized = QChar(unicode - 0xFF10 + '0'); return true; } // 处理全角大写字母 (0xFF21-0xFF3A) if (unicode >= 0xFF21 && unicode <= 0xFF3A) { normalized = QChar(unicode - 0xFF21 + 'A'); return true; } // 处理全角小写字母 (0xFF41-0xFF5A) if (unicode >= 0xFF41 && unicode <= 0xFF5A) { normalized = QChar(unicode - 0xFF41 + 'a'); return true; } // 处理全角标点符号 static const QHash punctuationMap { { 0xFF01, '!' }, // ! { 0xFF08, '(' }, // ( { 0xFF09, ')' }, // ) { 0xFF0C, ',' }, // , { 0xFF1A, ':' }, // : { 0xFF1B, ';' }, // ; { 0xFF1F, '?' }, // ? { 0xFF3B, '[' }, // [ { 0xFF3D, ']' }, // ] { 0xFF5B, '{' }, // { { 0xFF5D, '}' }, // } { 0xFF0E, '.' }, // . { 0xFF0F, '/' }, // / { 0xFF3F, '_' }, // _ { 0xFF0D, '-' }, // - { 0xFF1D, '=' }, // = { 0xFF06, '&' }, // & { 0xFF5C, '|' }, // | { 0xFF1C, '<' }, // < { 0xFF1E, '>' }, // > { 0xFF02, '"' }, // " { 0xFF07, '\'' }, // ' { 0xFF0B, '+' }, // + { 0xFF03, '#' }, // # { 0xFF04, '$' }, // $ { 0xFF05, '%' }, // % { 0xFF20, '@' }, // @ { 0xFF0A, '*' }, // * { 0xFF3C, '\\' }, // \ { 0xFF5E, '~' } // ~ }; auto it = punctuationMap.find(unicode); if (it != punctuationMap.end()) { normalized = it.value(); return true; } return false; } QString DLocalHelper::numberStr(const QString &str, int pos) { QString tmp; int total = str.length(); // 从当前'pos'开始向前扫描 while (pos < total) { const QChar &ch = str.at(pos); if (!isNumber(ch)) { break; // 遇到非数字字符,停止提取 } QChar number; // 将全角数字统一转换为半角数字后添加 if (isFullWidthChar(ch, number)) { tmp += number; } else { tmp += ch; } pos++; } return tmp; } // The first is smaller than the second and returns true bool DLocalHelper::compareByStringEx(const QString &str1, const QString &str2) { thread_local static DCollator sortCollator; enum CharType { NumberType = 0, // 数字 LetterType = 1, // 字母 (非汉字) HanType = 2, // 汉字 SymbolType = 3 // 符号 }; auto getCharType = [](uint unicode) -> CharType { // 使用静态 QChar 函数,它们可以安全地处理32位Unicode码点 if (QChar::isDigit(unicode)) return NumberType; // isNumber() 辅助函数可以处理全角数字,但 QChar::isDigit 更高效 // 我们的 isNumber 仍需在 numberStr 中使用 QChar::Script script = QChar::script(unicode); if (script == QChar::Script_Han) return HanType; if (QChar::isLetter(unicode)) return LetterType; return SymbolType; }; auto compareUnified = [&](const QString &s1, const QString &s2) -> int { QString::const_iterator it1 = s1.constBegin(); QString::const_iterator it2 = s2.constBegin(); while (it1 != s1.constEnd() && it2 != s2.constEnd()) { int len1 = 1; uint unicode1 = it1->unicode(); if (it1->isHighSurrogate() && (it1 + 1) != s1.constEnd()) { unicode1 = QChar::surrogateToUcs4(*it1, *(it1 + 1)); len1 = 2; } int len2 = 1; uint unicode2 = it2->unicode(); if (it2->isHighSurrogate() && (it2 + 1) != s2.constEnd()) { unicode2 = QChar::surrogateToUcs4(*it2, *(it2 + 1)); len2 = 2; } CharType type1 = getCharType(unicode1); CharType type2 = getCharType(unicode2); if (type1 != type2) { return (type1 < type2) ? -1 : 1; } switch (type1) { // ============================ FIX START ============================ case NumberType: { // 提取从当前位置开始的整个数字块 QString numPart1 = numberStr(s1, it1 - s1.constBegin()); QString numPart2 = numberStr(s2, it2 - s2.constBegin()); // 规则1: 使用QCollator按数值大小比较 int numCompareResult = sortCollator.compare(numPart1, numPart2); if (numCompareResult != 0) { return numCompareResult; } // 规则2: 数值相同,比较原始长度 (前导零多的排前面) // numberStr返回的是规范化后的半角字符串,其长度可能与原始长度不同。 // 我们需要计算原始字符串中数字块的真实长度。 int rawLen1 = 0; for (auto temp_it = it1; temp_it != s1.constEnd() && isNumber(*temp_it); ++temp_it) rawLen1++; int rawLen2 = 0; for (auto temp_it = it2; temp_it != s2.constEnd() && isNumber(*temp_it); ++temp_it) rawLen2++; if (rawLen1 != rawLen2) { // 原始字符串更长的排在前面 return (rawLen1 > rawLen2) ? -1 : 1; } // 数值和原始长度都相同,跳过这个块继续比较 it1 += rawLen1; it2 += rawLen2; continue; } // ============================= FIX END ============================= case LetterType: case HanType: { // 使用 char32_t* overload to avoid deprecation warning. QString charStr1 = QString::fromUcs4(reinterpret_cast(&unicode1), 1); QString charStr2 = QString::fromUcs4(reinterpret_cast(&unicode2), 1); int result = sortCollator.compare(charStr1, charStr2); if (result != 0) return result; // 规则3平局决胜: a A b B if (QChar::isLetter(unicode1) && QChar::isLetter(unicode2)) { if (QChar::toLower(unicode1) == QChar::toLower(unicode2)) { // 如果小写形式相同,小写字母排在前面 if (QChar::isLower(unicode1) != QChar::isLower(unicode2)) { return QChar::isLower(unicode1) ? -1 : 1; } } } break; } case SymbolType: { if (unicode1 != unicode2) { return (unicode1 < unicode2) ? -1 : 1; } break; } } it1 += len1; it2 += len2; } if (it1 == s1.constEnd() && it2 != s2.constEnd()) return -1; if (it1 != s1.constEnd() && it2 == s2.constEnd()) return 1; return 0; }; return compareUnified(str1, str2) < 0; } bool DLocalHelper::compareByString(const QString &str1, const QString &str2) { // 处理文件名称为 新建文件a 新建文件夹 排序错误的问题 // 按名称排序 // 1、按名称排序规则为:数字→字母→汉字→其它; // 2、其中数字由小到大排列,字母由a~z、 A~Z排列(例如:a A b B),汉字:按拼音首字母由a~z排列; // 3、如果首字母相同看第二位字母,以此类推; // 4、其他:特殊字符和乱码排在后面; return compareByStringEx(str1, str2); } int DLocalHelper::compareByName(const FTSENT **left, const FTSENT **right) { QString str1 = QString((*left)->fts_name), str2 = QString((*right)->fts_name); auto tt = compareByString(str1, str2); return tt ? -1 : 1; } int DLocalHelper::compareBySize(const FTSENT **left, const FTSENT **right) { auto leftSize = fileSizeByEnt(left); auto rightSize = fileSizeByEnt(right); if (leftSize == rightSize) return compareByName(left, right); return leftSize > rightSize; } int DLocalHelper::compareByLastModifed(const FTSENT **left, const FTSENT **right) { if ((*left)->fts_statp->st_mtim.tv_sec == (*right)->fts_statp->st_mtim.tv_sec) { if ((*left)->fts_statp->st_mtim.tv_nsec > (*right)->fts_statp->st_mtim.tv_nsec) return compareByName(left, right); return (*left)->fts_statp->st_mtim.tv_nsec > (*right)->fts_statp->st_mtim.tv_nsec; } return (*left)->fts_statp->st_mtim.tv_sec > (*right)->fts_statp->st_mtim.tv_sec; } int DLocalHelper::compareByLastRead(const FTSENT **left, const FTSENT **right) { if ((*left)->fts_statp->st_atim.tv_sec == (*right)->fts_statp->st_atim.tv_sec) { if ((*left)->fts_statp->st_atim.tv_nsec > (*right)->fts_statp->st_atim.tv_nsec) return compareByName(left, right); return (*left)->fts_statp->st_atim.tv_nsec > (*right)->fts_statp->st_atim.tv_nsec; } return (*left)->fts_statp->st_atim.tv_sec > (*right)->fts_statp->st_atim.tv_sec; } QSharedPointer DLocalHelper::createSortFileInfo(const FTSENT *ent, const QSet hidList) { auto sortPointer = QSharedPointer(new DEnumerator::SortFileInfo); auto name = QString(ent->fts_name); sortPointer->filesize = ent->fts_statp->st_size; sortPointer->isSymLink = S_ISLNK(ent->fts_statp->st_mode); sortPointer->isDir = S_ISDIR(ent->fts_statp->st_mode); if (sortPointer->isSymLink) { QString symlinkTagetPath = resolveSymlink(QUrl::fromLocalFile(ent->fts_path)); if (!symlinkTagetPath.isEmpty()) sortPointer->symlinkUrl = QUrl::fromLocalFile(symlinkTagetPath); } if (sortPointer->symlinkUrl.isValid() && !DFMUtils::isGvfsFile(sortPointer->symlinkUrl)) { struct stat st; if (stat(sortPointer->symlinkUrl.path().toUtf8().data(), &st) == 0) { sortPointer->filesize = st.st_size; sortPointer->isDir = S_ISDIR(st.st_mode); } } sortPointer->isFile = !sortPointer->isDir; sortPointer->isHide = name.startsWith(".") ? true : hidList.contains(name); sortPointer->isReadable = ent->fts_statp->st_mode & S_IREAD; sortPointer->isWriteable = ent->fts_statp->st_mode & S_IWRITE; sortPointer->isExecutable = ent->fts_statp->st_mode & S_IEXEC; sortPointer->url = QUrl::fromLocalFile(ent->fts_path); if (DFMUtils::isInvalidCodecByPath(ent->fts_path)) sortPointer->url.setUserInfo("originPath::" + QString::fromLatin1(ent->fts_path)); sortPointer->inode = ent->fts_statp->st_ino; sortPointer->gid = ent->fts_statp->st_gid; sortPointer->uid = ent->fts_statp->st_uid; sortPointer->lastRead = ent->fts_statp->st_atim.tv_sec; sortPointer->lastReadNs = ent->fts_statp->st_atim.tv_nsec; sortPointer->lastModifed = ent->fts_statp->st_mtim.tv_sec; sortPointer->lastModifedNs = ent->fts_statp->st_mtim.tv_nsec; sortPointer->create = ent->fts_statp->st_ctim.tv_sec; sortPointer->createNs = ent->fts_statp->st_ctim.tv_nsec; return sortPointer; } GFile *DLocalHelper::createGFile(const QUrl &uri) { QString path = uri.userInfo().isEmpty() || !uri.userInfo().startsWith("originPath::") ? QString() : uri.userInfo().replace("originPath::", ""); GFile *gfile = path.isEmpty() ? g_file_new_for_uri(uri.toString().toLocal8Bit().data()) : g_file_new_for_path(path.toLatin1().data()); return gfile; } QVariant DLocalHelper::getGFileInfoIcon(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode) { assert(key != nullptr); if (!g_file_info_has_attribute(gfileinfo, key)) { errorcode = DFM_IO_ERROR_INFO_NO_ATTRIBUTE; return QVariant(); } GObject *icon = g_file_info_get_attribute_object(gfileinfo, key); if (!icon) return QVariant(); QList ret; auto names = g_themed_icon_get_names(G_THEMED_ICON(icon)); for (int j = 0; names && names[j] != nullptr; ++j) { if (strcmp(names[j], "folder") == 0) ret.prepend(QString::fromLocal8Bit(names[j])); else ret.append(QString::fromLocal8Bit(names[j])); } return QVariant(ret); } QVariant DLocalHelper::getGFileInfoString(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode) { assert(key != nullptr); if (g_file_info_has_attribute(gfileinfo, key)) return g_file_info_get_attribute_string(gfileinfo, key); errorcode = DFM_IO_ERROR_INFO_NO_ATTRIBUTE; return QVariant(); } QVariant DLocalHelper::getGFileInfoByteString(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode) { assert(key != nullptr); if (g_file_info_has_attribute(gfileinfo, key)) return g_file_info_get_attribute_byte_string(gfileinfo, key); errorcode = DFM_IO_ERROR_INFO_NO_ATTRIBUTE; return QVariant(); } QVariant DLocalHelper::getGFileInfoBool(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode) { assert(key != nullptr); if (g_file_info_has_attribute(gfileinfo, key)) return g_file_info_get_attribute_boolean(gfileinfo, key); errorcode = DFM_IO_ERROR_INFO_NO_ATTRIBUTE; return QVariant(); } QVariant DLocalHelper::getGFileInfoUint32(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode) { assert(key != nullptr); if (g_file_info_has_attribute(gfileinfo, key)) return g_file_info_get_attribute_uint32(gfileinfo, key); errorcode = DFM_IO_ERROR_INFO_NO_ATTRIBUTE; return QVariant(); } QVariant DLocalHelper::getGFileInfoInt32(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode) { assert(key != nullptr); if (g_file_info_has_attribute(gfileinfo, key)) return g_file_info_get_attribute_int32(gfileinfo, key); errorcode = DFM_IO_ERROR_INFO_NO_ATTRIBUTE; return QVariant(); } QVariant DLocalHelper::getGFileInfoUint64(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode) { assert(key != nullptr); if (g_file_info_has_attribute(gfileinfo, key)) return qulonglong(g_file_info_get_attribute_uint64(gfileinfo, key)); errorcode = DFM_IO_ERROR_INFO_NO_ATTRIBUTE; return QVariant(); } QVariant DLocalHelper::getGFileInfoInt64(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode) { assert(key != nullptr); if (g_file_info_has_attribute(gfileinfo, key)) return qlonglong(g_file_info_get_attribute_int64(gfileinfo, key)); errorcode = DFM_IO_ERROR_INFO_NO_ATTRIBUTE; return QVariant(); } bool DLocalHelper::setGFileInfoString(GFile *gfile, const char *key, const QVariant &value, GError **gerror) { assert(key != nullptr); g_file_set_attribute_string(gfile, key, value.toString().toLocal8Bit().data(), G_FILE_QUERY_INFO_NONE, nullptr, gerror); if (gerror) { g_autofree gchar *url = g_file_get_uri(gfile); qWarning() << "file set attribute failed, url: " << url << " msg: " << (*gerror)->message; return false; } return true; } bool DLocalHelper::setGFileInfoByteString(GFile *gfile, const char *key, const QVariant &value, GError **gerror) { assert(key != nullptr); g_file_set_attribute_byte_string(gfile, key, value.toString().toLocal8Bit().data(), G_FILE_QUERY_INFO_NONE, nullptr, gerror); if (gerror) { g_autofree gchar *url = g_file_get_uri(gfile); qWarning() << "file set attribute failed, url: " << url << " msg: " << (*gerror)->message; return false; } return true; } bool DLocalHelper::setGFileInfoBool(GFile *gfile, const char *key, const QVariant &value, GError **gerror) { assert(key != nullptr); gboolean b = value.toBool(); gpointer gpValue = &b; g_file_set_attribute(gfile, key, G_FILE_ATTRIBUTE_TYPE_BOOLEAN, gpValue, G_FILE_QUERY_INFO_NONE, nullptr, gerror); if (gerror) { g_autofree gchar *url = g_file_get_uri(gfile); qWarning() << "file set attribute failed, url: " << url << " msg: " << (*gerror)->message; return false; } return true; } bool DLocalHelper::setGFileInfoUint32(GFile *gfile, const char *key, const QVariant &value, GError **gerror) { assert(key != nullptr); g_file_set_attribute_uint32(gfile, key, value.toUInt(), G_FILE_QUERY_INFO_NONE, nullptr, gerror); if (gerror) { g_autofree gchar *url = g_file_get_uri(gfile); qWarning() << "file set attribute failed, url: " << url << " msg: " << (*gerror)->message; return false; } return true; } bool DLocalHelper::setGFileInfoInt32(GFile *gfile, const char *key, const QVariant &value, GError **gerror) { assert(key != nullptr); g_file_set_attribute_int32(gfile, key, value.toInt(), G_FILE_QUERY_INFO_NONE, nullptr, gerror); if (gerror) { g_autofree gchar *url = g_file_get_uri(gfile); qWarning() << "file set attribute failed, url: " << url << " msg: " << (*gerror)->message; return false; } return true; } bool DLocalHelper::setGFileInfoUint64(GFile *gfile, const char *key, const QVariant &value, GError **gerror) { assert(key != nullptr); g_file_set_attribute_uint64(gfile, key, value.toULongLong(), G_FILE_QUERY_INFO_NONE, nullptr, gerror); if (gerror) { g_autofree gchar *url = g_file_get_uri(gfile); qWarning() << "file set attribute failed, url: " << url << " msg: " << (*gerror)->message; return false; } return true; } bool DLocalHelper::setGFileInfoInt64(GFile *gfile, const char *key, const QVariant &value, GError **gerror) { assert(key != nullptr); g_file_set_attribute_int64(gfile, key, value.toLongLong(), G_FILE_QUERY_INFO_NONE, nullptr, gerror); if (gerror) { g_autofree gchar *url = g_file_get_uri(gfile); qWarning() << "file set attribute failed, url: " << url << " msg: " << (*gerror)->message; return false; } return true; } QString DLocalHelper::symlinkTarget(const QUrl &url) { char buffer[4096] { 0 }; auto size = readlink(url.path().toStdString().c_str(), buffer, sizeof(buffer)); if (size > 0) return QString::fromUtf8(buffer, static_cast(size)); return QString(); } QString DLocalHelper::resolveSymlink(const QUrl &url) { QSet visited; QString target = DLocalHelper::symlinkTarget(url); while (!target.isEmpty()) { if (visited.contains(target)) return QString(); // Cycle detected: return empty visited.insert(target); QUrl newUrl = QUrl::fromLocalFile(target); QString nextTarget = DLocalHelper::symlinkTarget(newUrl); if (nextTarget.isEmpty()) break; target = nextTarget; } return target; } qint64 DLocalHelper::fileSizeByEnt(const FTSENT **ent) { if (!S_ISLNK((*ent)->fts_statp->st_mode)) return S_ISDIR((*ent)->fts_statp->st_mode) ? -1 : (*ent)->fts_statp->st_size; // 这里很奇怪 fts_name="tt 快捷方式" fts_path="/home/uos/Desktop" 所以重新拼接"/home/ut005319@uos/Desktop/tt 快捷方式" auto filePath = QString((*ent)->fts_path) + QDir::separator() + (*ent)->fts_name; QString linkTag = resolveSymlink(QUrl::fromLocalFile(filePath)); struct stat st; qint64 size = (*ent)->fts_statp->st_size; if (linkTag.isEmpty() || DFMUtils::isGvfsFile(QUrl::fromLocalFile(linkTag)) || stat(linkTag.toUtf8().data(), &st) != 0) size = (*ent)->fts_statp->st_size; else { size = S_ISDIR(st.st_mode) ? -1 : st.st_size; } return size; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/utils/dlocalhelper.h000066400000000000000000000123431523477242100252020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DGIOHELPER_H #define DGIOHELPER_H #include #include #include #include #include #include BEGIN_IO_NAMESPACE template std::function bind_field(C *c, Ret (C::*m)(Ts...)) { return [=](auto &&...args) { return (c->*m)(std::forward(args)...); }; } template std::function bind_field(const C *c, Ret (C::*m)(Ts...) const) { return [=](auto &&...args) { return (c->*m)(std::forward(args)...); }; } template std::function bind_field(Ret (*m)(Ts...)) { return [=](auto &&...args) { return (*m)(std::forward(args)...); }; } class DLocalHelper { public: using AttributeInfoMap = std::unordered_map>; static AttributeInfoMap &attributeInfoMapFunc(); static QSharedPointer createFileInfoByUri(const QUrl &uri, const char *attributes = "*", const DFMIO::DFileInfo::FileQueryInfoFlags flag = DFMIO::DFileInfo::FileQueryInfoFlags::kTypeNone); static QSharedPointer createFileInfoByUri(const QUrl &uri, GFileInfo *gfileInfo, const char *attributes = "*", const DFMIO::DFileInfo::FileQueryInfoFlags flag = DFMIO::DFileInfo::FileQueryInfoFlags::kTypeNone); static QVariant attributeFromGFileInfo(GFileInfo *gfileinfo, DFileInfo::AttributeID id, DFMIOErrorCode &errorcode); static QVariant customAttributeFromPath(const QString &path, DFileInfo::AttributeID id); static QVariant customAttributeFromPathAndInfo(const QString &path, GFileInfo *fileInfo, DFileInfo::AttributeID id); static bool setAttributeByGFile(GFile *gfile, DFileInfo::AttributeID id, const QVariant &value, GError **error); static bool setAttributeByGFileInfo(GFileInfo *gfileinfo, DFileInfo::AttributeID id, const QVariant &value); static std::string attributeStringById(DFileInfo::AttributeID id); static QSet hideListFromUrl(const QUrl &url); static bool fileIsHidden(const DFileInfo *dfileinfo, const QSet &hideList, const bool needRead = true); // tools static bool checkGFileType(GFile *file, GFileType type); static bool isNumOrChar(const QChar ch); static bool isNumber(const QChar ch); static bool isSymbol(const QChar ch); static bool isFullWidthChar(const QChar ch, QChar &normalized); static bool compareByStringEx(const QString &str1, const QString &str2); static QString numberStr(const QString &str, int pos); static bool compareByString(const QString &str1, const QString &str2); static int compareByName(const FTSENT **left, const FTSENT **right); static int compareBySize(const FTSENT **left, const FTSENT **right); static int compareByLastModifed(const FTSENT **left, const FTSENT **right); static int compareByLastRead(const FTSENT **left, const FTSENT **right); static QSharedPointer createSortFileInfo(const FTSENT *ent, const QSet hidList); static GFile* createGFile(const QUrl &uri); private: static QVariant getGFileInfoIcon(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode); static QVariant getGFileInfoString(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode); static QVariant getGFileInfoByteString(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode); static QVariant getGFileInfoBool(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode); static QVariant getGFileInfoUint32(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode); static QVariant getGFileInfoInt32(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode); static QVariant getGFileInfoUint64(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode); static QVariant getGFileInfoInt64(GFileInfo *gfileinfo, const char *key, DFMIOErrorCode &errorcode); static bool setGFileInfoString(GFile *gfile, const char *key, const QVariant &value, GError **gerror); static bool setGFileInfoByteString(GFile *gfile, const char *key, const QVariant &value, GError **gerror); static bool setGFileInfoBool(GFile *gfile, const char *key, const QVariant &value, GError **gerror); static bool setGFileInfoUint32(GFile *gfile, const char *key, const QVariant &value, GError **gerror); static bool setGFileInfoInt32(GFile *gfile, const char *key, const QVariant &value, GError **gerror); static bool setGFileInfoUint64(GFile *gfile, const char *key, const QVariant &value, GError **gerror); static bool setGFileInfoInt64(GFile *gfile, const char *key, const QVariant &value, GError **gerror); static QString symlinkTarget(const QUrl &url); static QString resolveSymlink(const QUrl &url); static qint64 fileSizeByEnt(const FTSENT **ent); }; END_IO_NAMESPACE #endif // DGIOHELPER_H linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/utils/dmediainfo.cpp000066400000000000000000000112751523477242100252010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "dmediainfo.h" #include #include #include #include #include #include #include #include static constexpr size_t kMediaInfoStateFinished { 10000 }; // read finished and no error Q_GLOBAL_STATIC(QQueue>, queueDestoryMediaInfo) BEGIN_IO_NAMESPACE class DMediaInfoPrivate : public QObject { public: DMediaInfoPrivate(DMediaInfo *qq, const QString &fileName) : q(qq) { this->fileName = fileName; isStopState.store(false); mediaInfo.reset(new MediaInfoLib::MediaInfo()); } ~DMediaInfoPrivate() { if (mediaInfo) { // 由于当远程文件夹下存在大量图片文件时,析构mediainfo对象耗时会很长,造成文管卡 // 所以将对象添加到队列中,开启线程去释放对象 static QMutex lock; static std::atomic isRunning { false }; { QMutexLocker locker(&lock); queueDestoryMediaInfo->enqueue(mediaInfo); } bool expected = false; // 使用 compare_exchange_strong 确保只有一个线程能启动清理工作 if (isRunning.compare_exchange_strong(expected, true)) { std::thread thread( []() { while (!queueDestoryMediaInfo->isEmpty()) { QMutexLocker locker(&lock); queueDestoryMediaInfo->dequeue(); } isRunning.store(false); }); thread.detach(); } } } /** * @brief bug-35165, 将构造时读取media信息的方式改为独立的方法 * 以免造成构造对象时直接卡住 */ void start() { if (isStopState.load()) return; mediaInfo->Option(__T("Thread")); mediaInfo->Option(__T("Width"), __T("Text")); mediaInfo->Option(__T("Height"), __T("Text")); mediaInfo->Option(__T("Duration"), __T("Text")); mediaInfo->Open(fileName.toStdWString()); QPointer me = this; std::thread thread([me]() { auto startTime = std::chrono::steady_clock::now(); const int kTimeoutMs = 3000; // 3秒超时 while (1) { if (!me) break; if (me->isStopState.load()) break; auto currentTime = std::chrono::steady_clock::now(); auto elapsed = std::chrono::duration_cast(currentTime - startTime).count(); // 达到完成状态或超时 if (me->mediaInfo->State_Get() == kMediaInfoStateFinished || elapsed > kTimeoutMs) { // 使用 QMetaObject::invokeMethod 切换到主线程执行 callback // 避免从后台线程直接调用 Qt 对象方法导致的崩溃 QMetaObject::invokeMethod( me.data(), [me]() { if (me && me->callback) { me->callback(); } }, Qt::QueuedConnection); break; } std::chrono::milliseconds dura(200); std::this_thread::sleep_for(dura); } }); thread.detach(); } QString value(const QString &key, MediaInfoLib::stream_t type) { QString info = QString::fromStdWString(mediaInfo->Get(type, 0, key.toStdWString())); return info; } public: QString fileName; QSharedPointer mediaInfo { nullptr }; DMediaInfo *q { nullptr }; DMediaInfo::FinishedCallback callback = nullptr; std::atomic_bool isStopState; }; END_IO_NAMESPACE USING_IO_NAMESPACE DMediaInfo::DMediaInfo(const QString &fileName) : d(new DMediaInfoPrivate(this, fileName)) { } DMediaInfo::~DMediaInfo() { } QString DMediaInfo::value(const QString &key, DFileInfo::MediaType meidiaType /* = General*/) { return d->value(key, static_cast(meidiaType)); } void DMediaInfo::startReadInfo(FinishedCallback callback) { d->isStopState.store(false); d->callback = callback; d->start(); } void DMediaInfo::stopReadInfo() { d->isStopState.store(true); if (d->mediaInfo) d->mediaInfo->Close(); } linuxdeepin-util-dfm-a4307d3/src/dfm-io/dfm-io/utils/dmediainfo.h000066400000000000000000000014571523477242100246470ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DMEDIAINFO_H #define DMEDIAINFO_H #include #include #include #include #include #include BEGIN_IO_NAMESPACE class DMediaInfoPrivate; class DMediaInfo : public QObject { public: using FinishedCallback = std::function; explicit DMediaInfo(const QString &fileName); ~DMediaInfo(); QString value(const QString &key, DFileInfo::MediaType meidiaType = DFileInfo::MediaType::kGeneral); void startReadInfo(FinishedCallback callback); void stopReadInfo(); private: QScopedPointer d; }; END_IO_NAMESPACE #endif // DMEDIAINFO_H linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/000077500000000000000000000000001523477242100212155ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/CMakeLists.txt000066400000000000000000000052171523477242100237620ustar00rootroot00000000000000add_definitions(-DQT_NO_KEYWORDS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall") # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -fsanitize=address") find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) if (QT_VERSION_MAJOR EQUAL "5") if (CMAKE_BUILD_TYPE STREQUAL "Debug") set (CMAKE_VERBOSE_MAKEFILE ON) message("debug type open sanitize check") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,address,leak -fno-omit-frame-pointer") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined,address,leak -fno-omit-frame-pointer") set(CMAKE_L_FLAGS "${CMAKE_L_FLAGS} -fsanitize=undefined,address,leak -fno-omit-frame-pointer") #set(CMAKE_CXX_FLAGS "-fsanitize=thread") endif() endif() # include include_directories( ${PROJECT_SOURCE_DIR}/../dfm-io/inlcude ) add_executable(dfm-list dfm-list.cpp) target_link_libraries(dfm-list dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-sort-list dfm-sort-list.cpp) target_link_libraries(dfm-sort-list dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-info dfm-info.cpp) target_link_libraries(dfm-info dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-copy dfm-copy.cpp) target_link_libraries(dfm-copy dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-copy-gio dfm-copy-gio.cpp) target_link_libraries(dfm-copy-gio dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-copy3 dfm-copy3.cpp) target_link_libraries(dfm-copy3 dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-copy-system dfm-copy-system.cpp) target_link_libraries(dfm-copy-system dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-cat dfm-cat.cpp) target_link_libraries(dfm-cat dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-save dfm-save.cpp) target_link_libraries(dfm-save dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-rename dfm-rename.cpp) target_link_libraries(dfm-rename dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-move dfm-move.cpp) target_link_libraries(dfm-move dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-trash dfm-trash.cpp) target_link_libraries(dfm-trash dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-delete dfm-delete.cpp) target_link_libraries(dfm-delete dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-restore dfm-restore.cpp) target_link_libraries(dfm-restore dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-touch dfm-touch.cpp) target_link_libraries(dfm-touch dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-mkdir dfm-mkdir.cpp) target_link_libraries(dfm-mkdir dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-link dfm-link.cpp) target_link_libraries(dfm-link dfm${DFM_VERSION_MAJOR}-io) add_executable(dfm-watcher dfm-watcher.cpp) target_link_libraries(dfm-watcher dfm${DFM_VERSION_MAJOR}-io) linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-cat.cpp000066400000000000000000000027631523477242100232440ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-cat: %s\n", msg); } static QSharedPointer make_stream(const QUrl &url, DFile::OpenFlag flag) { QSharedPointer file { new DFile(url) }; if (!file) { err_msg("get device file failed."); return nullptr; } if (!file->open(flag)) { return nullptr; } return file; } #define BLOCK (4 * 4096) static void cat(const QUrl &url) { QSharedPointer stream = make_stream(url, DFile::OpenFlag::kReadOnly); if (!stream) { err_msg("make stream failed."); return; } char buff[BLOCK]; int read = 0; while ((read = stream->read(buff, BLOCK)) > 0) { if (write(STDOUT_FILENO, buff, read) != read) { err_msg("write failed."); break; } } } static void usage() { err_msg("usage: dfm-cat uri."); } // display file content. int main(int argc, char *argv[]) { if (argc != 2) { usage(); return 1; } const char *uri = argv[1]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; cat(url); return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-copy-gio.cpp000066400000000000000000000045471523477242100242250ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "../dfm-io/utils/dlocalhelper.h" // for test #include #include #include #include #include #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-copy: %s\n", msg); } static void copy(const QString &sourcePath, const QString &destPath) { GFile *gfileSource = DLocalHelper::createGFile(sourcePath); GFile *gfileDest = DLocalHelper::createGFile(destPath); GFile *gfileTarget = nullptr; if (DLocalHelper::checkGFileType(gfileDest, G_FILE_TYPE_DIRECTORY)) { char *basename = g_file_get_basename(gfileSource); gfileTarget = g_file_get_child(gfileDest, basename); g_free(basename); } else { gfileTarget = DLocalHelper::createGFile(destPath); } //预先读取 { char *gpath = g_file_get_path(gfileSource); int fromfd = ::open(gpath, O_RDONLY); if (-1 != fromfd) { GError *gerror = nullptr; GFileInfo *gfileinfo = g_file_query_info(gfileSource, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, nullptr, &gerror); if (gfileinfo) { goffset size = g_file_info_get_size(gfileinfo); readahead(fromfd, 0, static_cast(size)); g_object_unref(gfileinfo); } if (gerror) g_error_free(gerror); close(fromfd); } g_free(gpath); } GError *gerror = nullptr; g_file_copy(gfileSource, gfileTarget, G_FILE_COPY_OVERWRITE, nullptr, nullptr, nullptr, &gerror); g_object_unref(gfileSource); g_object_unref(gfileDest); g_object_unref(gfileTarget); if (gerror) g_error_free(gerror); } static void usage() { err_msg("usage: dfm-copy src_uri dst_uri."); } // copy src to dst with uri names. int main(int argc, char *argv[]) { if (argc != 3) { usage(); return 1; } const char *uri_src = argv[1]; const char *uri_dst = argv[2]; QElapsedTimer timer; timer.start(); copy(uri_src, uri_dst); auto time = timer.elapsed(); qInfo() << "gio g_file_copy call elapsed time: (ms)" << time; return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-copy-system.cpp000066400000000000000000000030421523477242100247600ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include qint64 readData(int m_fileFd, char *data, qint64 maxlen) { return ::read(m_fileFd, data, static_cast(maxlen)); } qint64 writeData(int m_fileFd, const char *data, qint64 maxlen) { return ::write(m_fileFd, data, static_cast(maxlen)); } static void err_msg(const char *msg) { fprintf(stderr, "dfm-copy: %s\n", msg); } static void copy(const QString &url_src, const QString &url_dst) { const int block = 128 * 1024; char buff[block]; int read = 0; int m_fileFd = ::open(url_src.toLocal8Bit().data(), O_RDONLY, 0755); int m_fileFd2 = ::open(url_dst.toLocal8Bit().data(), O_CREAT | O_WRONLY, 0755); while ((read = readData(m_fileFd, buff, block)) > 0) { if (writeData(m_fileFd2, buff, read) != read) { err_msg("write failed."); break; } } } static void usage() { err_msg("usage: dfm-copy src_uri dst_uri."); } // copy src to dst with uri names. int main(int argc, char *argv[]) { if (argc != 3) { usage(); return 1; } const char *uri_src = argv[1]; const char *uri_dst = argv[2]; QElapsedTimer timer; timer.start(); copy(uri_src, uri_dst); auto time = timer.elapsed(); qInfo() << "system call elapsed time: (ms)" << time; return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-copy.cpp000066400000000000000000000043021523477242100234360ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-copy: %s\n", msg); } static QSharedPointer make_stream(const QUrl &url, DFile::OpenFlag flag) { QSharedPointer file { new DFile(url) }; if (!file) { err_msg("get device file failed."); return nullptr; } if (!file->open(flag)) { return nullptr; } return file; } static void copy(const QUrl &url_src, const QUrl &url_dst) { const int block = 128 * 1024; char buff[block]; qint64 read = 0; QSharedPointer stream_src = make_stream(url_src, DFile::OpenFlag::kReadOnly); QSharedPointer stream_dst = make_stream(url_dst, DFile::OpenFlag::kWriteOnly); if (!stream_src || !stream_dst) { return; } while ((read = stream_src->read(buff, block)) > 0) { qint64 sizeall = 0, sizeWrite = 0, sizeRead = read; char *surplusData = buff; do { sizeWrite = stream_dst->write(surplusData, sizeRead); sizeall += sizeWrite; surplusData += sizeWrite; sizeRead -= sizeWrite; if (sizeWrite < 0 || (sizeWrite == 0 && sizeRead > 0)){ err_msg("write failed."); return; } } while (sizeall < read); } } static void usage() { err_msg("usage: dfm-copy src_uri dst_uri."); } // copy src to dst with uri names. int main(int argc, char *argv[]) { if (argc != 3) { usage(); return 1; } const char *uri_src = argv[1]; const char *uri_dst = argv[2]; QUrl url_src(QUrl::fromLocalFile(QString::fromLocal8Bit(uri_src))); QUrl url_dst(QUrl::fromLocalFile(QString::fromLocal8Bit(uri_dst))); if (!url_src.isValid() || !url_dst.isValid()) return 1; QElapsedTimer timer; timer.start(); copy(url_src, url_dst); auto time = timer.elapsed(); qInfo() << "dfm-io call elapsed time: (ms)" << time; return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-copy3.cpp000066400000000000000000000075121523477242100235270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "../dfm-io/utils/dlocalhelper.h" // for test #include #include #include #include #include #include #include USING_IO_NAMESPACE qint64 readData(GFileInputStream *input_stream, char *data, qint64 maxlen) { GError *gerror = nullptr; qint64 size = g_input_stream_read((GInputStream *)input_stream, data, static_cast(maxlen), nullptr, &gerror); if (gerror) { g_error_free(gerror); return -1; } return size; } qint64 writeData(GFileOutputStream *output_stream, const char *data, qint64 len) { GError *gerror = nullptr; qint64 size = g_output_stream_write((GOutputStream *)output_stream, data, static_cast(len), nullptr, &gerror); if (gerror) { g_error_free(gerror); return -1; } return size; } static void err_msg(const char *msg) { fprintf(stderr, "dfm-copy: %s\n", msg); } static void copy(const QString &url_src, const QString &url_dst) { const int block = 128 * 1024; char buff[block]; int read = 0; GError *gerror = nullptr; GFile *gfileSource = DLocalHelper::createGFile(url_src); GFile *gfileDest = DLocalHelper::createGFile(url_dst); GFile *gfileTarget = nullptr; if (DLocalHelper::checkGFileType(gfileDest, G_FILE_TYPE_DIRECTORY)) { char *basename = g_file_get_basename(gfileSource); gfileTarget = g_file_get_child(gfileDest, basename); g_free(basename); } else { gfileTarget = DLocalHelper::createGFile(url_dst); } GFileInputStream *inputStream = g_file_read(gfileSource, nullptr, &gerror); if (gerror) { g_error_free(gerror); gerror = nullptr; } GFileOutputStream *outputStream = g_file_replace(gfileTarget, nullptr, false, G_FILE_CREATE_NONE, nullptr, &gerror); if (gerror) { g_error_free(gerror); gerror = nullptr; } //预先读取 { char *gpath = g_file_get_path(gfileSource); int fromfd = ::open(gpath, O_RDONLY); if (-1 != fromfd) { GError *gerror = nullptr; GFileInfo *gfileinfo = g_file_query_info(gfileSource, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, nullptr, &gerror); if (gfileinfo) { goffset size = g_file_info_get_size(gfileinfo); readahead(fromfd, 0, static_cast(size)); g_object_unref(gfileinfo); } if (gerror) { g_error_free(gerror); gerror = nullptr; } close(fromfd); } g_free(gpath); } while ((read = readData(inputStream, buff, block)) > 0) { if (writeData(outputStream, buff, read) != read) { err_msg("write failed."); break; } } g_object_unref(inputStream); g_object_unref(outputStream); g_object_unref(gfileSource); g_object_unref(gfileDest); g_object_unref(gfileTarget); } static void usage() { err_msg("usage: dfm-copy src_uri dst_uri."); } // copy src to dst with uri names. int main(int argc, char *argv[]) { if (argc != 3) { usage(); return 1; } const char *uri_src = argv[1]; const char *uri_dst = argv[2]; QElapsedTimer timer; timer.start(); copy(uri_src, uri_dst); auto time = timer.elapsed(); qInfo() << "gio stream call elapsed time: (ms)" << time; return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-delete.cpp000066400000000000000000000017711523477242100237350ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-delete: %s\n", msg); } static void usage() { err_msg("usage: dfm-delete uri."); } static bool delete_file(const QUrl &url) { QSharedPointer op { new DOperator(url) }; if (!op) { err_msg("operator create failed."); return false; } bool success = op->deleteFile(); if (!success) { err_msg("delete file failed."); return false; } return true; } // delete file with uri. int main(int argc, char *argv[]) { if (argc != 2) { usage(); return 1; } const char *uri = argv[1]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; if (!delete_file(url)) { return 1; } return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-info.cpp000066400000000000000000000024211523477242100234170ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-info: %s\n", msg); } static void display(const QUrl &url) { QSharedPointer info { new DFileInfo(url) }; if (!info) { err_msg("create file info failed."); return; } printf("%s\n", info->dump().toStdString().c_str()); } static void displayAsync(const QUrl &url) { QSharedPointer info { new DFileInfo(url) }; if (!info) { err_msg("create file info failed."); return; } auto future = info->refreshAsync(); future.waitForFinished(); printf("%s\n", info->dump().toStdString().c_str()); } static void usage() { err_msg("usage: dfm-info uri."); } // get file info with uri. int main(int argc, char *argv[]) { if (argc != 2) { usage(); return 1; } const char *uri = argv[1]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; display(url); displayAsync(url); return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-link.cpp000066400000000000000000000022371523477242100234260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-link: %s\n", msg); } static void usage() { err_msg("usage: dfm-link link src-path."); } static bool link_file(const QUrl &url, const QUrl &urlto) { QSharedPointer op { new DOperator(url) }; if (!op) { err_msg("operator create failed."); return false; } bool success = op->createLink(urlto); if (!success) { err_msg("create file link failed."); return false; } return true; } // link uri to dst-uri. int main(int argc, char *argv[]) { if (argc != 3) { usage(); return 1; } const char *uri = argv[1]; const char *urito = argv[2]; QUrl urlSource(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); QUrl urlTarget(QUrl::fromLocalFile(QString::fromLocal8Bit(urito))); if (!urlSource.isValid()) return -1; if (!link_file(urlSource, urlTarget)) { return 1; } return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-list.cpp000066400000000000000000000067351523477242100234530ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include USING_IO_NAMESPACE void printFileInfo(const QSharedPointer &info) { QString type; if (info->attribute(DFileInfo::AttributeID::kStandardIsDir).toBool()) type = "DIR "; else if (info->attribute(DFileInfo::AttributeID::kStandardIsSymlink).toBool()) type = "LINK"; else type = "FILE"; QString perms; perms += info->attribute(DFileInfo::AttributeID::kAccessCanRead).toBool() ? "r" : "-"; perms += info->attribute(DFileInfo::AttributeID::kAccessCanWrite).toBool() ? "w" : "-"; perms += info->attribute(DFileInfo::AttributeID::kAccessCanExecute).toBool() ? "x" : "-"; qInfo() << QString("%1 %2 %3").arg(type, 4).arg(perms, 3) << info->attribute(DFileInfo::AttributeID::kStandardName).toString(); } void testFilter(const QUrl &url, DEnumerator::DirFilters filter, const QString &desc) { qInfo() << "\n=== Testing" << desc << "==="; QSharedPointer enumerator(new DEnumerator(url, {}, filter, {})); if (!enumerator) { qWarning() << "Failed to create enumerator"; return; } int count = 0; while (enumerator->hasNext()) { enumerator->next(); auto info = enumerator->fileInfo(); if (info) { printFileInfo(info); count++; } } qInfo() << "Total:" << count << "items\n"; } int main(int argc, char *argv[]) { if (argc != 2) { qWarning() << "Usage:" << argv[0] << ""; return 1; } QCoreApplication a(argc, argv); QUrl url = QUrl::fromLocalFile(QString::fromLocal8Bit(argv[1])); if (!url.isValid()) { qWarning() << "Invalid URL"; return 1; } // 测试所有基本过滤器 testFilter(url, DEnumerator::DirFilter::kNoFilter, "No Filter"); testFilter(url, DEnumerator::DirFilter::kDirs, "Directories Only"); testFilter(url, DEnumerator::DirFilter::kFiles, "Files Only"); testFilter(url, DEnumerator::DirFilter::kAllDirs, "All Directories (including . and ..)"); // 测试权限过滤器 testFilter(url, DEnumerator::DirFilter::kAllEntries | DEnumerator::DirFilter::kReadable, "Readable"); testFilter(url, DEnumerator::DirFilter::kAllEntries | DEnumerator::DirFilter::kWritable, "Writable"); testFilter(url, DEnumerator::DirFilter::kAllEntries | DEnumerator::DirFilter::kExecutable, "Executable"); // 测试特殊过滤器 testFilter(url, DEnumerator::DirFilter::kAllEntries | DEnumerator::DirFilter::kNoSymLinks, "No Symlinks"); // 测试组合过滤器 testFilter(url, DEnumerator::DirFilter::kAllDirs | DEnumerator::DirFilter::kHidden, "All Dirs + Hidden"); testFilter(url, DEnumerator::DirFilter::kFiles | DEnumerator::DirFilter::kReadable | DEnumerator::DirFilter::kWritable, "Files + Readable + Writable"); testFilter(url, DEnumerator::DirFilter::kAllEntries | DEnumerator::DirFilter::kNoDotAndDotDot, "All Entries but no . and .."); testFilter(url, DEnumerator::DirFilter::kDirs | DEnumerator::DirFilter::kNoSymLinks, "Directories without symlinks"); return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-mkdir.cpp000066400000000000000000000017721523477242100236020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-mkdir: %s\n", msg); } static void usage() { err_msg("usage: dfm-mkdir dir-uri."); } static bool mkdir(const QUrl &url) { QSharedPointer op { new DOperator(url) }; if (!op) { err_msg("operator create failed."); return false; } bool success = op->makeDirectory(); if (!success) { err_msg("create directory failed."); return false; } return true; } // make directory with uri. int main(int argc, char *argv[]) { if (argc != 2) { usage(); return 1; } const char *uri = argv[1]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; if (!mkdir(url)) { return 1; } return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-move.cpp000066400000000000000000000023211523477242100234310ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-move: %s\n", msg); } static void usage() { err_msg("usage: dfm-move uri new-path."); } static bool move_file(const QUrl &url_src, const QUrl &url_dst) { QSharedPointer op { new DOperator(url_src) }; if (!op) { err_msg("operator create failed."); return false; } bool success = op->moveFile(url_dst, DFile::CopyFlag::kNone); if (!success) { err_msg("move file failed."); return false; } return true; } // move uri to new-path. int main(int argc, char *argv[]) { if (argc != 3) { usage(); return 1; } const char *uri_src = argv[1]; const char *uri_dst = argv[2]; QUrl url_src(QUrl::fromLocalFile(QString::fromLocal8Bit(uri_src))); QUrl url_dst(QUrl::fromLocalFile(QString::fromLocal8Bit(uri_dst))); if (!url_src.isValid() || !url_dst.isValid()) return 1; if (!move_file(url_src, url_dst)) { return 1; } return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-rename.cpp000066400000000000000000000023001523477242100237270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-rename: %s\n", msg); } static void usage() { err_msg("usage: dfm-rename uri new-name."); } static bool rename_file(const QUrl &url, const QString &new_name) { QSharedPointer op { new DOperator(url) }; if (!op) { err_msg("operator create failed."); return false; } bool success = op->renameFile(new_name); if (!success) { err_msg("rename file failed."); return false; } return true; } // rename uri with new-name. int main(int argc, char *argv[]) { if (argc != 3) { usage(); return 1; } const char *uri = argv[1]; const char *new_name = argv[2]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; const QString &qsName = QString::fromLocal8Bit(new_name); if (qsName.isEmpty()) return 1; if (!rename_file(url, qsName)) { return 1; } return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-restore.cpp000066400000000000000000000015771523477242100241620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-restore: %s\n", msg); } static void usage() { err_msg("usage: dfm-restore trash-file-uri."); } static void restore(const QUrl &url) { QSharedPointer op { new DOperator(url) }; if (!op) { err_msg("operator create failed."); return; } if (!op->restoreFile()) { err_msg("restore file failed."); return; } } // restore file from trash. int main(int argc, char *argv[]) { if (argc != 2) { usage(); return 1; } const char *uri = argv[1]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); restore(url); return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-save.cpp000066400000000000000000000026261523477242100234310ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-save: %s\n", msg); } static QSharedPointer make_stream(const QUrl &url, DFile::OpenFlag flag) { QSharedPointer file { new DFile(url) }; if (!file) { err_msg("get device file failed."); return nullptr; } if (!file->open(flag)) { return nullptr; } return file; } #define BLOCK 4 * 4096 static void save(const QUrl &url) { QSharedPointer stream = make_stream(url, DFile::OpenFlag::kWriteOnly); if (!stream) { err_msg("make stream failed."); return; } char buff[BLOCK]; int bytes = 0; while ((bytes = read(STDIN_FILENO, buff, BLOCK)) > 0) { if (stream->write(buff, bytes) != bytes) err_msg("write failed."); } } static void usage() { err_msg("usage: dfm-save uri."); } // copy src to dst with uri names. int main(int argc, char *argv[]) { if (argc != 2) { usage(); return 1; } const char *uri = argv[1]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; save(url); return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-sort-list.cpp000066400000000000000000000031661523477242100244330ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include USING_IO_NAMESPACE // show detail static bool lflag = false; static void err_msg(const char *msg) { fprintf(stderr, "dfm-list: %s\n", msg); } static void enum_uri(const QUrl &url) { QSharedPointer enumerator { new DEnumerator(url) }; if (!enumerator) { err_msg("create enumerator failed."); return; } enumerator->setSortRole(DEnumerator::SortRoleCompareFlag::kSortRoleCompareFileLastRead); enumerator->setSortMixed(true); enumerator->setSortOrder(Qt::DescendingOrder); auto list = enumerator->sortFileInfoList(); for (auto sortInfo : list) { qInfo() << sortInfo->url; } } void usage() { err_msg("usage: dfm-list [-l] uri."); } // list all children in a directory. int main(int argc, char *argv[]) { if (argc != 2 && argc != 3) { usage(); return 1; } char *uri = nullptr; if (argc == 3) { if (strcmp(argv[1], "-l") == 0) { lflag = true; uri = argv[2]; } else if (strcmp(argv[2], "-l") == 0) { lflag = true; uri = argv[1]; } else { usage(); return 1; } } else { // argc == 2 uri = argv[1]; } QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; enum_uri(url); return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-touch.cpp000066400000000000000000000017761523477242100236220ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-touch: %s\n", msg); } static void usage() { err_msg("usage: dfm-touch uri."); } static bool touch_file(const QUrl &url) { QSharedPointer op { new DOperator(url) }; if (!op) { err_msg("operator create failed."); return false; } bool success = op->touchFile(); if (!success) { err_msg("touch file failed."); return false; } return true; } // ensure file with uri is exists. int main(int argc, char *argv[]) { if (argc != 2) { usage(); return 1; } const char *uri = argv[1]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; if (!touch_file(url)) { return 1; } return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-trash.cpp000066400000000000000000000020041523477242100236020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-trash: %s\n", msg); } static void usage() { err_msg("usage: dfm-trash uri."); } static bool trash_file(const QUrl &url) { QSharedPointer op { new DOperator(url) }; if (!op) { err_msg("operator create failed."); return false; } QString targetTrash = op->trashFile(); if (targetTrash.isEmpty()) { err_msg("trash file failed."); return false; } return true; } // move file to trash. int main(int argc, char *argv[]) { if (argc != 2) { usage(); return 1; } const char *uri = argv[1]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; if (!trash_file(url)) { return 1; } return 0; } linuxdeepin-util-dfm-a4307d3/src/dfm-io/tools/dfm-watcher.cpp000066400000000000000000000040351523477242100241240ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include USING_IO_NAMESPACE static void err_msg(const char *msg) { fprintf(stderr, "dfm-watcher: %s\n", msg); } static void usage() { err_msg("usage: dfm-watcher uri."); } static bool watcher_dir(const QUrl &url, QSharedPointer &watcher) { watcher.reset(new DWatcher(url)); if (!watcher) { err_msg("watcher create failed."); return false; } QObject::connect(watcher.get(), &DWatcher::fileChanged, [](const QUrl &url) { qDebug() << "file changed: " << url; printf("file changed:%s.\n", url.url().toLocal8Bit().data()); }); QObject::connect(watcher.get(), &DWatcher::fileDeleted, [](const QUrl &url) { qDebug() << "file deleted: " << url; printf("file deleted:%s.\n", url.url().toLocal8Bit().data()); }); QObject::connect(watcher.get(), &DWatcher::fileAdded, [](const QUrl &url) { qDebug() << "file added: " << url; printf("file added:%s.\n", url.url().toLocal8Bit().data()); }); QObject::connect(watcher.get(), &DWatcher::fileRenamed, [](const QUrl &fromUri, const QUrl &toUri) { qDebug() << "file move: " << fromUri << "to" << toUri; printf("file move: %s to %s.\n", fromUri.url().toLocal8Bit().data(), toUri.url().toLocal8Bit().data()); }); if (!watcher->start()) { err_msg("start watcher failed."); return false; } return true; } // watch directory. int main(int argc, char *argv[]) { if (argc != 2) { usage(); return 1; } QCoreApplication app(argc, argv); const char *uri = argv[1]; QUrl url(QUrl::fromLocalFile(QString::fromLocal8Bit(uri))); if (!url.isValid()) return 1; QSharedPointer watcher = nullptr; if (!watcher_dir(url, watcher)) { return 1; } return app.exec(); } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/000077500000000000000000000000001523477242100206105ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-mount/CMakeLists.txt000066400000000000000000000016661523477242100233610ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) set(BASE_NAME dfm-mount) # signals and slots keyword is disable, use Q_SIGNALS and Q_SLOTS instead add_compile_definitions(QT_NO_SIGNALS_SLOTS_KEYWORDS) # gio signals conflicts with qt signals add_definitions(-DQT_NO_KEYWORDS) # public include file(GLOB_RECURSE PUBLIC_INCLUDES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/include/${BASE_NAME}/*") # src FILE (GLOB_RECURSE SRCS CONFIGURE_DEPENDS "./*.cpp" "./*.h" ) if (QT_VERSION_MAJOR MATCHES 6) include(dfm-mount-qt6.cmake) else() include(dfm-mount-qt5.cmake) endif() #set(CMAKE_CXX_FLAGS "-fsanitize=undefined,address,leak -fno-omit-frame-pointer") #set(CMAKE_C_FLAGS "-fsanitize=undefined,address,leak -fno-omit-frame-pointer") #set(CMAKE_L_FLAGS "-fsanitize=undefined,address,leak -fno-omit-frame-pointer") #set(CMAKE_CXX_FLAGS "-fsanitize=thread") #set(CMAKE_C_FLAGS "-fsanitize=thread") #set(CMAKE_L_FLAGS "-fsanitize=thread") linuxdeepin-util-dfm-a4307d3/src/dfm-mount/dfm-mount-qt5.cmake000066400000000000000000000001451523477242100242270ustar00rootroot00000000000000set(BIN_NAME dfm-mount) set(QT_VERSION_MAJOR 5) set(DFM_VERSION_MAJOR "") include(dfm-mount.cmake) linuxdeepin-util-dfm-a4307d3/src/dfm-mount/dfm-mount-qt6.cmake000066400000000000000000000001451523477242100242300ustar00rootroot00000000000000set(BIN_NAME dfm6-mount) set(QT_VERSION_MAJOR 6) set(DFM_VERSION_MAJOR 6) include(dfm-mount.cmake) linuxdeepin-util-dfm-a4307d3/src/dfm-mount/dfm-mount.cmake000066400000000000000000000044341523477242100235250ustar00rootroot00000000000000 find_package(PkgConfig REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Concurrent REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS DBus REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED) pkg_check_modules(udisks2 REQUIRED udisks2 IMPORTED_TARGET) pkg_check_modules(glib REQUIRED glib-2.0 IMPORTED_TARGET) pkg_check_modules(gio REQUIRED gio-unix-2.0 IMPORTED_TARGET) pkg_check_modules(secret REQUIRED libsecret-1 IMPORTED_TARGET) pkg_check_modules(mount REQUIRED mount IMPORTED_TARGET) # Build add_library(${BIN_NAME} SHARED ${PUBLIC_INCLUDES} ${SRCS} ) if (NOT VERSION) set(VERSION "1.0.0") endif() set_target_properties( ${BIN_NAME} PROPERTIES VERSION ${VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) target_link_libraries(${BIN_NAME} Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Concurrent Qt${QT_VERSION_MAJOR}::DBus Qt${QT_VERSION_MAJOR}::Widgets PkgConfig::udisks2 PkgConfig::glib PkgConfig::gio PkgConfig::secret PkgConfig::mount ) target_include_directories( ${BIN_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include/${BASE_NAME} ) include(GNUInstallDirs) # Install lib install(TARGETS ${BIN_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) # install headers install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/${BASE_NAME}/${BASE_NAME} DESTINATION include/${BIN_NAME} FILES_MATCHING PATTERN "*.h" ) # for pc file config set(PC_LIBS_PRIVATE Qt${QT_VERSION_MAJOR}Core) set(PC_REQ_PRIVATE) set(PC_REQ_PUBLIC) # config pkgconfig file configure_file(${PROJECT_SOURCE_DIR}/misc/${BASE_NAME}/${BASE_NAME}.pc.in ${BIN_NAME}.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BIN_NAME}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) # config cmake file configure_file(${PROJECT_SOURCE_DIR}/misc/${BASE_NAME}/${BASE_NAME}Config.cmake.in ${BIN_NAME}Config.cmake @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BIN_NAME}Config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${BIN_NAME}) # Build example set(EXAMPLE_SRCS example/main.cpp) add_executable(${BIN_NAME}_example ${EXAMPLE_SRCS}) target_link_libraries(${BIN_NAME}_example ${BIN_NAME}) target_link_libraries(${BIN_NAME}_example Qt${QT_VERSION_MAJOR}::Widgets) linuxdeepin-util-dfm-a4307d3/src/dfm-mount/example/000077500000000000000000000000001523477242100222435ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-mount/example/main.cpp000066400000000000000000000005701523477242100236750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "dfm-mount/dmount.h" using namespace dfmmount; int main(int argc, char **argv) { QApplication app(argc, argv); DDeviceManager::instance()->startMonitorWatch(); return app.exec(); } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/000077500000000000000000000000001523477242100213565ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/base/000077500000000000000000000000001523477242100222705ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/base/ddevice.cpp000066400000000000000000000122051523477242100243770ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "private/ddevice_p.h" #include DFM_MOUNT_USE_NS DFMMOUNT::DDevicePrivate::DDevicePrivate(DDevice *qq) : q(qq) { } DDevicePrivate::~DDevicePrivate() { } DDevice::DDevice(DDevicePrivate *dd, QObject *parent) : QObject(parent), d(dd) { } DDevice::~DDevice() { } QString DDevice::path() const { Q_ASSERT_X(d->path, __PRETTY_FUNCTION__, "not register"); return d->path(); } QString DDevice::mount(const QVariantMap &opts) { Q_ASSERT_X(d->mount, __PRETTY_FUNCTION__, "not register"); return d->mount(opts); } void DDevice::mountAsync(const QVariantMap &opts, DeviceOperateCallbackWithMessage cb) { Q_ASSERT_X(d->mountAsync, __PRETTY_FUNCTION__, "not register"); return d->mountAsync(opts, cb); } bool DDevice::unmount(const QVariantMap &opts) { Q_ASSERT_X(d->unmount, __PRETTY_FUNCTION__, "not register"); return d->unmount(opts); } void DDevice::unmountAsync(const QVariantMap &opts, DeviceOperateCallback cb) { Q_ASSERT_X(d->unmountAsync, __PRETTY_FUNCTION__, "not register"); return d->unmountAsync(opts, cb); } bool DDevice::rename(const QString &newName, const QVariantMap &opts) { Q_ASSERT_X(d->rename, __PRETTY_FUNCTION__, "not register"); return d->rename(newName, opts); } void DDevice::renameAsync(const QString &newName, const QVariantMap &opts, DeviceOperateCallback cb) { Q_ASSERT_X(d->renameAsync, __PRETTY_FUNCTION__, "not register"); return d->renameAsync(newName, opts, cb); } QString DDevice::mountPoint() const { Q_ASSERT_X(d->mountPoint, __PRETTY_FUNCTION__, "not register"); return d->mountPoint(); } QString DDevice::fileSystem() const { Q_ASSERT_X(d->fileSystem, __PRETTY_FUNCTION__, "not register"); return d->fileSystem(); } qint64 DDevice::sizeTotal() const { Q_ASSERT_X(d->sizeTotal, __PRETTY_FUNCTION__, "not register"); return d->sizeTotal(); } qint64 DDevice::sizeFree() const { Q_ASSERT_X(d->sizeFree, __PRETTY_FUNCTION__, "not register"); return d->sizeFree(); } qint64 DDevice::sizeUsage() const { Q_ASSERT_X(d->sizeUsage, __PRETTY_FUNCTION__, "not register"); return d->sizeUsage(); } DeviceType DDevice::deviceType() const { Q_ASSERT_X(d->deviceType, __PRETTY_FUNCTION__, "not register"); return d->deviceType(); } QVariant DDevice::getProperty(Property item) const { Q_ASSERT_X(d->getProperty, __PRETTY_FUNCTION__, "not register"); return d->getProperty(item); } QString DDevice::displayName() const { Q_ASSERT_X(d->displayName, __PRETTY_FUNCTION__, "not register"); return d->displayName(); } OperationErrorInfo DDevice::lastError() const { return d->lastError; } void DDevice::registerPath(const DDevice::PathFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->path = func; } void DDevice::registerMount(const DDevice::MountFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->mount = func; } void DDevice::registerMountAsync(const DDevice::MountAsyncFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->mountAsync = func; } void DDevice::registerUnmount(const DDevice::UnmountFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->unmount = func; } void DDevice::registerUnmountAsync(const DDevice::UnmountAsyncFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->unmountAsync = func; } void DDevice::registerRename(const DDevice::RenameFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->rename = func; } void DDevice::registerRenameAsync(const DDevice::RenameAsyncFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->renameAsync = func; } void DDevice::registerMountPoint(const DDevice::MountPointFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->mountPoint = func; } void DDevice::registerFileSystem(const DDevice::FileSystemFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->fileSystem = func; } void DDevice::registerSizeTotal(const DDevice::SizeTotalFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->sizeTotal = func; } void DDevice::registerSizeUsage(const DDevice::SizeUsageFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->sizeUsage = func; } void DDevice::registerSizeFree(const DDevice::SizeFreeFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->sizeFree = func; } void DDevice::registerDeviceType(const DDevice::DeviceTypeFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->deviceType = func; } void DDevice::registerGetProperty(const DDevice::GetPropertyFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->getProperty = func; } void DDevice::registerDisplayName(const DDevice::DisplayNameFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->displayName = func; } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/base/ddevicemonitor.cpp000066400000000000000000000046131523477242100260130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "private/ddevicemonitor_p.h" #include DFM_MOUNT_USE_NS DFMMOUNT::DDeviceMonitorPrivate::DDeviceMonitorPrivate(DDeviceMonitor *qq) : q(qq) { } DDeviceMonitorPrivate::~DDeviceMonitorPrivate() { } DDeviceMonitor::DDeviceMonitor(DDeviceMonitorPrivate *dd, QObject *parent) : QObject(parent), d(dd) { } DDeviceMonitor::~DDeviceMonitor() { } bool DDeviceMonitor::startMonitor() { Q_ASSERT_X(d->start, __PRETTY_FUNCTION__, "not register"); d->monitorStatus = MonitorStatus::kMonitoring; return d->start(); } bool DDeviceMonitor::stopMonitor() { Q_ASSERT_X(d->stop, __PRETTY_FUNCTION__, "not register"); d->monitorStatus = MonitorStatus::kIdle; return d->stop(); } MonitorStatus DDeviceMonitor::status() const { return d->monitorStatus; } DeviceType DDeviceMonitor::monitorObjectType() const { Q_ASSERT_X(d->monitorObjectType, __PRETTY_FUNCTION__, "not register"); return d->monitorObjectType(); } QStringList DDeviceMonitor::getDevices() const { Q_ASSERT_X(d->monitorObjectType, __PRETTY_FUNCTION__, "not register"); return d->getDevices(); } QSharedPointer DDeviceMonitor::createDeviceById(const QString &id) const { Q_ASSERT_X(d->monitorObjectType, __PRETTY_FUNCTION__, "not register"); return d->createDeviceById(id); } void DDeviceMonitor::registerStartMonitor(const DDeviceMonitor::StartMonitorFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->start = func; } void DDeviceMonitor::registerStopMonitor(const DDeviceMonitor::StopMonitorFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->stop = func; } void DDeviceMonitor::registerMonitorObjectType(const DDeviceMonitor::MonitorObjectTypeFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->monitorObjectType = func; } void DDeviceMonitor::registerGetDevices(const DDeviceMonitor::GetDevicesFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->getDevices = func; } void DDeviceMonitor::registerCreateDeviceById(const DDeviceMonitor::CreateDeviceByIdFunc &func) { Q_ASSERT_X(func, __PRETTY_FUNCTION__, "not register"); d->createDeviceById = func; } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/base/dmountutils.cpp000066400000000000000000001376221523477242100253760ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include extern "C" { #include } #include #include DFM_MOUNT_USE_NS // use G_VARIANT_TYPE_* gets alot compile warning #define GVAR_TYPE_BOOL reinterpret_cast("b") #define GVAR_TYPE_BYTE reinterpret_cast("y") #define GVAR_TYPE_INT16 reinterpret_cast("n") #define GVAR_TYPE_UINT16 reinterpret_cast("q") #define GVAR_TYPE_INT32 reinterpret_cast("i") #define GVAR_TYPE_UINT32 reinterpret_cast("u") #define GVAR_TYPE_INT64 reinterpret_cast("x") #define GVAR_TYPE_UINT64 reinterpret_cast("t") #define GVAR_TYPE_DOUBLE reinterpret_cast("d") #define GVAR_TYPE_STRING reinterpret_cast("s") #define GVAR_TYPE_STRING_ARR reinterpret_cast("as") #define GVAR_TYPE_OBJECTPATH reinterpret_cast("o") #define GVAR_TYPE_OBJECTPATH_ARR reinterpret_cast("ao") #define GVAR_TYPE_VARIANT reinterpret_cast("v") #define GVAR_TYPE_BYTESTRING reinterpret_cast("ay") #define GVAR_TYPE_BYTESTRING_ARR reinterpret_cast("aay") #define GVAR_TYPE_VARDICT reinterpret_cast("a{sv}") #define GVAR_TYPE_ARR reinterpret_cast("a*") QVariant Utils::castFromGVariant(GVariant *val) { auto isType = [val](const GVariantType *type) { return g_variant_is_of_type(val, type); }; if (!val) return QVariant(); if (isType(GVAR_TYPE_BOOL)) return QVariant(static_cast(g_variant_get_boolean(val))); if (isType(GVAR_TYPE_BYTE)) return QVariant(static_cast(g_variant_get_byte(val))); if (isType(GVAR_TYPE_INT16)) return QVariant(static_cast(g_variant_get_int16(val))); if (isType(GVAR_TYPE_INT32)) return QVariant(static_cast(g_variant_get_int32(val))); if (isType(GVAR_TYPE_INT64)) return QVariant(static_cast(g_variant_get_int64(val))); if (isType(GVAR_TYPE_UINT16)) return QVariant(static_cast(g_variant_get_uint16(val))); if (isType(GVAR_TYPE_UINT32)) return QVariant(static_cast(g_variant_get_uint32(val))); if (isType(GVAR_TYPE_UINT64)) return QVariant(static_cast(g_variant_get_uint64(val))); if (isType(GVAR_TYPE_DOUBLE)) return QVariant(static_cast(g_variant_get_double(val))); if (isType(GVAR_TYPE_STRING) || isType(GVAR_TYPE_OBJECTPATH)) return QString(g_variant_get_string(val, nullptr)); if (isType(GVAR_TYPE_VARIANT)) return castFromGVariant(val); if (isType(GVAR_TYPE_STRING_ARR)) { QStringList ret; const char **strv = g_variant_get_strv(val, nullptr); int next = 0; while (strv && strv[next]) { ret << QString(strv[next]); next++; } g_free(strv); return ret; } if (isType(GVAR_TYPE_OBJECTPATH_ARR)) { QStringList ret; const char **strv = g_variant_get_objv(val, nullptr); int next = 0; while (strv && strv[next]) { ret << QString(strv[next]); next++; } g_free(strv); return ret; } if (isType(GVAR_TYPE_BYTESTRING)) { const char *byteString = g_variant_get_bytestring(val); return QString(byteString); } if (isType(GVAR_TYPE_BYTESTRING_ARR)) { QStringList ret; const char **bstrv = g_variant_get_bytestring_array(val, nullptr); int next = 0; while (bstrv && bstrv[next]) { ret << QString(bstrv[next]); next++; } g_free(bstrv); return ret; } if (isType(GVAR_TYPE_VARDICT)) { QVariantMap ret; GVariantIter *iter = nullptr; g_variant_get(val, "a{sv}", &iter); char *key = nullptr; GVariant *item = nullptr; while (iter && g_variant_iter_loop(iter, "{&sv}", &key, &item)) ret.insert(QString(key), castFromGVariant(item)); g_variant_iter_free(iter); return ret; } if (isType(GVAR_TYPE_ARR)) { QList lst; GVariantIter *iter = nullptr; g_variant_get(val, "av", &iter); GVariant *item = nullptr; while (iter && g_variant_iter_loop(iter, "v", &item)) lst << castFromGVariant(item); g_variant_iter_free(iter); return lst; } qDebug() << g_variant_classify(val) << "cannot be parsed"; return QVariant(); } GVariant *Utils::castFromQVariant(const QVariant &val) { switch (val.type()) { case QVariant::Bool: return g_variant_new("b", val.toBool()); case QVariant::Int: return g_variant_new("i", val.toInt()); case QVariant::UInt: return g_variant_new("u", val.toUInt()); case QVariant::LongLong: return g_variant_new("x", val.toLongLong()); case QVariant::ULongLong: return g_variant_new("t", val.toULongLong()); case QVariant::Double: return g_variant_new("d", val.toDouble()); case QVariant::Char: return g_variant_new("y", val.toChar().toLatin1()); case QVariant::String: { std::string str = val.toString().toStdString(); const char *cstr = str.c_str(); return g_variant_new("s", cstr); } case QVariant::StringList: return castFromQStringList(val.toStringList()); case QVariant::ByteArray: return g_variant_new_bytestring(val.toByteArray().data()); case QVariant::Map: return castFromQVariantMap(val.toMap()); case QVariant::List: return castFromList(val.toList()); default: return nullptr; } } GVariant *Utils::castFromQVariantMap(const QVariantMap &val) { GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); if (!builder) { qWarning() << "cannot allocate a gvariantbuilder"; return nullptr; } for (auto iter = val.cbegin(); iter != val.cend(); iter += 1) { GVariant *item = castFromQVariant(iter.value()); std::string key = iter.key().toStdString(); const char *ckey = key.c_str(); if (item) g_variant_builder_add(builder, "{sv}", ckey, item); } GVariant *ret = g_variant_builder_end(builder); g_variant_builder_unref(builder); return ret; } GVariant *Utils::castFromQStringList(const QStringList &val) { GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE("as")); if (!builder) { qWarning() << "cannot allocate a gvariantbuilder"; return nullptr; } for (const auto &s : val) { std::string str = s.toStdString(); const char *cstr = str.c_str(); g_variant_builder_add(builder, "s", cstr); } GVariant *ret = g_variant_builder_end(builder); g_variant_builder_unref(builder); return ret; } GVariant *Utils::castFromList(const QList &val) { GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE("av")); if (!builder) { qWarning() << "cannot allocate a gvariantbuilder"; return nullptr; } for (auto valItem : val) { GVariant *item = castFromQVariant(valItem); if (item) g_variant_builder_add(builder, "v", item); } GVariant *ret = g_variant_builder_end(builder); g_variant_builder_unref(builder); return ret; } Property Utils::getPropertyByName(const QString &name, const QString &iface) { #ifdef p # undef p #endif #define p(k, v) std::pair(k, v) const static QMap datas = { p("UserspaceMountOptions", Property::kBlockUserspaceMountOptions), p("Configuration", Property::kBlockConfiguration), p("CryptoBackingDevice", Property::kBlockCryptoBackingDevice), p("Device", Property::kBlockDevice), p("Drive", Property::kBlockDrive), p("IdLabel", Property::kBlockIDLabel), p("IdType", Property::kBlockIDType), p("IdUsage", Property::kBlockIDUsage), p("IdUUID", Property::kBlockIDUUID), p("IdVersion", Property::kBlockIDVersion), p("DeviceNumber", Property::kBlockDeviceNumber), p("PreferredDevice", Property::kBlockPreferredDevice), p("Id", Property::kBlockID), p("Size", Property::kBlockSize), p("ReadOnly", Property::kBlockReadOnly), p("Symlinks", Property::kBlockSymlinks), p("HintPartitionable", Property::kBlockHintPartitionable), p("HintSystem", Property::kBlockHintSystem), p("HintIgnore", Property::kBlockHintIgnore), p("HintAuto", Property::kBlockHintAuto), p("HintName", Property::kBlockHintName), p("HintIconName", Property::kBlockHintIconName), p("HintSymbolicIconName", Property::kBlockHintSymbolicIconName), p("MdRaid", Property::kBlockMdRaid), p("MdRaidMember", Property::kBlockMdRaidMember), p("ConnectionBus", Property::kDriveConnectionBus), p("Removable", Property::kDriveRemovable), p("Ejectable", Property::kDriveEjectable), p("Seat", Property::kDriveSeat), p("Media", Property::kDriveMedia), p("MediaCompatibility", Property::kDriveMediaCompatibility), p("MediaRemovable", Property::kDriveMediaRemovable), p("MediaAvailable", Property::kDriveMediaAvailable), p("MediaChangeDetected", Property::kDriveMediaChangeDetected), p("TimeDetected", Property::kDriveTimeDetected), p("TimeMediaDetected", Property::kDriveTimeMediaDetected), p("Size", Property::kDriveSize), p("Optical", Property::kDriveOptical), p("OpticalBlank", Property::kDriveOpticalBlank), p("OpticalNumTracks", Property::kDriveOpticalNumTracks), p("OpticalNumAudioTracks", Property::kDriveOpticalNumAudioTracks), p("OpticalNumDataTracks", Property::kDriveOpticalNumDataTracks), p("OpticalNumSessions", Property::kDriveOpticalNumSessions), p("Model", Property::kDriveModel), p("Revision", Property::kDriveRevision), p("RotationRate", Property::kDriveRotationRate), p("Serial", Property::kDriveSerial), p("Vender", Property::kDriveVender), p("WWN", Property::kDriveWWN), p("SortKey", Property::kDriveSortKey), p("Configuration", Property::kDriveConfiguration), p("ID", Property::kDriveID), p("CanPowerOff", Property::kDriveCanPowerOff), p("SiblingID", Property::kDriveSiblingID), p("MountPoints", Property::kFileSystemMountPoint), p("Number", Property::kPartitionNumber), p("Type", Property::kPartitionType), p("Offset", Property::kPartitionOffset), p("Size", Property::kPartitionSize), p("Flags", Property::kPartitionFlags), p("Name", Property::kPartitionName), p("UUID", Property::kPartitionUUID), p("Table", Property::kPartitionTable), p("IsContainer", Property::kPartitionIsContainer), p("IsContained", Property::kPartitionIsContained), p("ChildConfiguration", Property::kEncryptedChildConfiguration), p("CleartextDevice", Property::kEncryptedCleartextDevice), p("HintEncryptionType", Property::kEncryptedHintEncryptionType), p("MetadataSize", Property::kEncryptedMetadataSize), }; // there are 3 same-name property in udisks, so distinguish it by InterfaceName if (name == "Size") { if (iface.endsWith("Block")) return Property::kBlockSize; else if (iface.endsWith("Drive")) return Property::kDriveSize; else if (iface.endsWith("Partition")) return Property::kPartitionSize; } return datas.value(name, Property::kNotInit); } PartitionType Utils::getPartitionTypeByGuid(const QString &guid) { #ifdef p # undef p #endif #define p(k, v) std::pair(k, v) static const QMap datas = { p("00000000-0000-0000-0000-000000000000", PartitionType::kGptUnusedEntryNA), p("024DEE41-33E7-11D3-9D69-0008C781F39F", PartitionType::kGptMBRPartitionSchemeNA), p("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", PartitionType::kGptEFISystemPartitionNA), p("21686148-6449-6E6F-744E-656564454649", PartitionType::kGptBIOSBootPartitionNA), p("D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", PartitionType::kGptIntelFastFlashPartitionNA), p("F4019732-066E-4E12-8273-346C5641494F", PartitionType::kGptSonyBootPartitionNA), p("BFBFAFE7-A34F-448A-9A5B-6213EB736C22", PartitionType::kGptLenovoBootPartitionNA), p("E3C9E316-0B5C-4DB8-817D-F92DF00215AE", PartitionType::kGptMicrosoftReservedPartitionWin), p("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", PartitionType::kGptBasicDataPartitionWin), p("5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", PartitionType::kGptLogicalDiskManagerMetaDataPartitionWin), p("AF9B60A0-1431-4F62-BC68-3311714A69AD", PartitionType::kGptLogicalDiskManagerDataPartitionWin), p("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", PartitionType::kGptWindowsRecoveryEnvironmentWin), p("37AFFC90-EF7D-4E96-91C3-2D7AE055B174", PartitionType::kGptIBMGeneralParallelFileSystemPartitionWin), p("E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", PartitionType::kGptStorageSpacesPartitionWin), p("558D43C5-A1AC-43C0-AAC8-D1472B2923D1", PartitionType::kGptStorageReplicaPartitionWin), p("75894C1E-3AEB-11D3-B7C1-7B03A0000000", PartitionType::kGptDataPartitionHPUX), p("E2A1E728-32E3-11D6-A682-7B03A0000000", PartitionType::kGptServicePartitionHPUX), p("0FC63DAF-8483-4772-8E79-3D69D8477DE4", PartitionType::kGptLinuxFilesystemDataLinux), p("A19D880F-05FC-4D3B-A006-743F0F84911E", PartitionType::kGptRAIDPartitionLinux), p("44479540-F297-41B2-9AF7-D131D5F0458A", PartitionType::kGptRootPartitionX86Linux), p("4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", PartitionType::kGptRootPartitionX8664Linux), p("69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", PartitionType::kGptRootPartitionArm32Linux), p("B921B045-1DF0-41C3-AF44-4C6F280D3FAE", PartitionType::kGptRootPartitionArm64Linux), p("BC13C2FF-59E6-4262-A352-B275FD6F7172", PartitionType::kGptBootPartitionLinux), p("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", PartitionType::kGptSwapPartitionLinux), p("E6D6D379-F507-44C2-A23C-238F2A3DF928", PartitionType::kGptLogicalVolumeManagerPartitionLinux), p("933AC7E1-2EB4-4F13-B844-0E14E2AEF915", PartitionType::kGptHomePartitionLinux), p("3B8F8425-20E0-4F3B-907F-1A25A76F98E8", PartitionType::kGptServerDataPartitionLinux), p("7FFEC5C9-2D00-49B7-8941-3EA10A5586B7", PartitionType::kGptPlainDMCryptPartitionLinux), p("CA7D7CCB-63ED-4C53-861C-1742536059CC", PartitionType::kGptLUKSPartitionLinux), p("8DA63339-0007-60C0-C436-083AC8230908", PartitionType::kGptReservedLinux), p("83BD6B9D-7F41-11DC-BE0B-001560B84F0F", PartitionType::kGptBootPartitionFreeBSD), p("516E7CB4-6ECF-11D6-8FF8-00022D09712B", PartitionType::kGptBSDDisklabelPartitionFreeBSD), p("516E7CB5-6ECF-11D6-8FF8-00022D09712B", PartitionType::kGptSwapPartitionFreeBSD), p("516E7CB6-6ECF-11D6-8FF8-00022D09712B", PartitionType::kGptUnixFileSystemPartitionFreeBSD), p("516E7CB8-6ECF-11D6-8FF8-00022D09712B", PartitionType::kGptVinumVolumeManagerPartitionFreeBSD), p("516E7CBA-6ECF-11D6-8FF8-00022D09712B", PartitionType::kGptZFSPartitionFreeBSD), p("74BA7DD9-A689-11E1-BD04-00E081286ACF", PartitionType::kGptNandfsPartitionFreeBSD), p("48465300-0000-11AA-AA11-00306543ECAC", PartitionType::kGptHierarchialFileSystemPlusPartitionMacOS), p("7C3457EF-0000-11AA-AA11-00306543ECAC", PartitionType::kGptAppleAPFSContainerMacOS), p("55465300-0000-11AA-AA11-00306543ECAC", PartitionType::kGptAppleUFSContainerMacOS), p("6A898CC3-1DD2-11B2-99A6-080020736631", PartitionType::kGptZFSMacOS), p("52414944-0000-11AA-AA11-00306543ECAC", PartitionType::kGptAppleRAIDPartitionMacOS), p("52414944-5F4F-11AA-AA11-00306543ECAC", PartitionType::kGptAppleRAIDPartitionOfflineMacOS), p("426F6F74-0000-11AA-AA11-00306543ECAC", PartitionType::kGptAppleBootPartitionMacOS), p("4C616265-6C00-11AA-AA11-00306543ECAC", PartitionType::kGptAppleLabelMacOS), p("5265636F-7665-11AA-AA11-00306543ECAC", PartitionType::kGptAppleTVRecoveryPartitionMacOS), p("53746F72-6167-11AA-AA11-00306543ECAC", PartitionType::kGptAppleCoreStorageContainerMacOS), p("69646961-6700-11AA-AA11-00306543ECAC", PartitionType::kGptAppleAPFSPrebootPartitionMacOS), p("52637672-7900-11AA-AA11-00306543ECAC", PartitionType::kGptAppleAPFSRecoveryPartitionMacOS), p("6A82CB45-1DD2-11B2-99A6-080020736631", PartitionType::kGptBootPartitionSolaris), p("6A85CF4D-1DD2-11B2-99A6-080020736631", PartitionType::kGptRootPartitionSolaris), p("6A87C46F-1DD2-11B2-99A6-080020736631", PartitionType::kGptSwapPartitionSolaris), p("6A8B642B-1DD2-11B2-99A6-080020736631", PartitionType::kGptBackupPartitionSolaris), p("6A898CC3-1DD2-11B2-99A6-080020736631", PartitionType::kGptUsrPartitionSolaris), p("6A8EF2E9-1DD2-11B2-99A6-080020736631", PartitionType::kGptVarPartitionSolaris), p("6A90BA39-1DD2-11B2-99A6-080020736631", PartitionType::kGptHomePartitionSolaris), p("6A9283A5-1DD2-11B2-99A6-080020736631", PartitionType::kGptAlternateSectorSolaris), p("6A945A3B-1DD2-11B2-99A6-080020736631", PartitionType::kGptReservedPartitionSolaris), p("6A9630D1-1DD2-11B2-99A6-080020736631", PartitionType::kGptReservedPartitionSolaris), p("6A980767-1DD2-11B2-99A6-080020736631", PartitionType::kGptReservedPartitionSolaris), p("6A96237F-1DD2-11B2-99A6-080020736631", PartitionType::kGptReservedPartitionSolaris), p("6A8D2AC7-1DD2-11B2-99A6-080020736631", PartitionType::kGptReservedPartitionSolaris), p("49F48D32-B10E-11DC-B99B-0019D1879648", PartitionType::kGptSwapPartitionNetBSD), p("49F48D5A-B10E-11DC-B99B-0019D1879648", PartitionType::kGptFFSPartitionNetBSD), p("49F48D82-B10E-11DC-B99B-0019D1879648", PartitionType::kGptLFSPartitionNetBSD), p("49F48DAA-B10E-11DC-B99B-0019D1879648", PartitionType::kGptRAIDPartitionNetBSD), p("2DB519C4-B10F-11DC-B99B-0019D1879648", PartitionType::kGptConcatenatedPartitionNetBSD), p("2DB519EC-B10F-11DC-B99B-0019D1879648", PartitionType::kGptEncryptedPartitionNetBSD), p("FE3A2A5D-4F32-41A7-B725-ACCC3285A309", PartitionType::kGptKernelChromeOS), p("3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", PartitionType::kGptRootfsChromeOS), p("CAB6E88E-ABF3-4102-A07A-D4BB9BE3C1D3", PartitionType::kGptFirmwareChromeOS), p("2E0A753D-9E48-43B0-8337-B15192CB1B5E", PartitionType::kGptFutureUseChromeOS), p("09845860-705F-4BB5-B16C-8A8A099CAF52", PartitionType::kGptMiniOSChromeOS), p("3F0F8318-F146-4E6B-8222-C28C8F02E0D5", PartitionType::kGptHibernateChromeOS), p("5DFBF5F4-2848-4BAC-AA5E-0D9A20B745A6", PartitionType::kGptUsrPartitionCoreOS), p("3884DD41-8582-4404-B9A8-E9B84F2DF50E", PartitionType::kGptResizableRootfsCoreOS), p("C95DC21A-DF0E-4340-8D7B-26CBFA9A03E0", PartitionType::kGptOEMCustomizationsCoreOS), p("BE9067B9-EA49-4F15-B4F6-F36F8C9E1818", PartitionType::kGptRootFilesystemOnRAIDCoreOS), p("42465331-3BA3-10F1-802A-4861696B7521", PartitionType::kGptHaikuBFSHaiku), p("85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", PartitionType::kGptBootPartitionMidnightBSD), p("85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", PartitionType::kGptDataPartitionMidnightBSD), p("85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", PartitionType::kGptSwapPartitionMidnightBSD), p("0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7", PartitionType::kGptUnixFileSystemPartitionMidnightBSD), p("85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", PartitionType::kGptVinumVolumemanagerPartitionMidnightBSD), p("85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", PartitionType::kGptZFSPartitionMidnightBSD), p("45B0969E-9B03-4F30-B4C6-B4B80CEFF106", PartitionType::kGptJournalCeph), p("45B0969E-9B03-4F30-B4C6-5EC00CEFF106", PartitionType::kGptDmCryptJournalCeph), p("4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D", PartitionType::kGptOSDCeph), p("4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D", PartitionType::kGptDmCryptOSDCeph), p("89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE", PartitionType::kGptDiskinCreationCeph), p("89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE", PartitionType::kGptDmCryptDiskinCreationCeph), p("CAFECAFE-9B03-4F30-B4C6-B4B80CEFF106", PartitionType::kGptBlockCeph), p("30CD0809-C2B2-499C-8879-2D6B78529876", PartitionType::kGptBlockDBCeph), p("5CE17FCE-4087-4169-B7FF-056CC58473F9", PartitionType::kGptBlockWriteAheadlogCeph), p("FB3AABF9-D25F-47CC-BF5E-721D1816496B", PartitionType::kGptLockboxForDmCryptKeysCeph), p("4FBD7E29-8AE0-4982-BF9D-5A8D867AF560", PartitionType::kGptMultipathOSDCeph), p("45B0969E-8AE0-4982-BF9D-5A8D867AF560", PartitionType::kGptMultipathJournalCeph), p("CAFECAFE-8AE0-4982-BF9D-5A8D867AF560", PartitionType::kGptMultipathBlockCeph), p("7F4A666A-16F3-47A2-8445-152EF4D03F6C", PartitionType::kGptMultipathBlockCeph), p("EC6D6385-E346-45DC-BE91-DA2A7C8B3261", PartitionType::kGptMultipathBlockDBCeph), p("01B41E1B-002A-453C-9F17-88793989FF8F", PartitionType::kGptMultipathblockwriteAheadogCeph), p("CAFECAFE-9B03-4F30-B4C6-5EC00CEFF106", PartitionType::kGptDmCryptBlockCeph), p("93B0052D-02D9-4D8A-A43B-33A3EE4DFBC3", PartitionType::kGptDmCryptBlockDBCeph), p("306E8683-4FE2-4330-B7C0-00A917C16966", PartitionType::kGptDmCryptBlockWriteAheadlogCeph), p("45B0969E-9B03-4F30-B4C6-35865CEFF106", PartitionType::kGptDmCryptLUKSjournalCeph), p("CAFECAFE-9B03-4F30-B4C6-35865CEFF106", PartitionType::kGptDmCryptLUKSBlockCeph), p("166418DA-C469-4022-ADF4-B30AFD37F176", PartitionType::kGptDmCryptLUKSBlockDBCeph), p("86A32090-3647-40B9-BBBD-38D8C573AA86", PartitionType::kGptDmCryptLUKSBlockwriteAheadlogCeph), p("4FBD7E29-9D25-41B8-AFD0-35865CEFF05D", PartitionType::kGptDmCryptLUKSOSDCeph), p("824CC7A0-36A8-11E3-890A-952519AD3F61", PartitionType::kGptDataPartitionOpenBSD), p("CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1", PartitionType::kGptPowerSafeFilesystemQNX), p("C91818F9-8025-47AF-89D2-F030D7000C2C", PartitionType::kGptPlan9PartitionPlan9), p("9D275380-40AD-11DB-BF97-000C2911D1B8", PartitionType::kGptVmkCoreVMwareESX), p("AA31E02A-400F-11DB-9590-000C2911D1B8", PartitionType::kGptVMFSFilesystemPartitionVMwareESX), p("9198EFFC-31C0-11DB-8F78-000C2911D1B8", PartitionType::kGptVMWareReservedVMwareESX), p("2568845D-2332-4675-BC39-8FA5A4748D15", PartitionType::kGptBootloaderAndroidIA), p("114EAFFE-1552-4022-B26E-9B053604CF84", PartitionType::kGptBootloader2AndroidIA), p("49A4D17F-93A3-45C1-A0DE-F50B2EBE2599", PartitionType::kGptBootAndroidIA), p("4177C722-9E92-4AAB-8644-43502BFD5506", PartitionType::kGptRecoveryAndroidIA), p("EF32A33B-A409-486C-9141-9FFB711F6266", PartitionType::kGptMiscAndroidIA), p("20AC26BE-20B7-11E3-84C5-6CFDB94711E9", PartitionType::kGptMetadataAndroidIA), p("38F428E6-D326-425D-9140-6E0EA133647C", PartitionType::kGptSystemAndroidIA), p("A893EF21-E428-470A-9E55-0668FD91A2D9", PartitionType::kGptCacheAndroidIA), p("DC76DDA9-5AC1-491C-AF42-A82591580C0D", PartitionType::kGptDataAndroidIA), p("EBC597D0-2053-4B15-8B64-E0AAC75F4DB1", PartitionType::kGptPersistentAndroidIA), p("C5A0AEEC-13EA-11E5-A1B1-001E67CA0C3C", PartitionType::kGptVendorAndroidIA), p("BD59408B-4514-490D-BF12-9878D963F378", PartitionType::kGptConfigAndroidIA), p("8F68CC74-C5E5-48DA-BE91-A0C8C15E9C80", PartitionType::kGptFactoryAndroidIA), p("9FDAA6EF-4B3F-40D2-BA8D-BFF16BFB887B", PartitionType::kGptFactoryAltAndroidIA), p("767941D0-2085-11E3-AD3B-6CFDB94711E9", PartitionType::kGptFastbootOrTertiaryAndroidIA), p("AC6D7924-EB71-4DF8-B48D-E267B27148FF", PartitionType::kGptOEMAndroidIA), p("19A710A2-B3CA-11E4-B026-10604B889DCF", PartitionType::kGptAndroidMetaAndroid6Arm), p("193D1EA4-B3CA-11E4-B075-10604B889DCF", PartitionType::kGptAndroidEXTAndroid6Arm), p("7412F7D5-A156-4B13-81DC-867174929325", PartitionType::kGptBootONIE), p("D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149", PartitionType::kGptConfigONIE), p("9E1A2D38-C612-4316-AA26-8B49521E5A8B", PartitionType::kGptPRePBootPowerPC), p("BC13C2FF-59E6-4262-A352-B275FD6F7172", PartitionType::kGptSharedBootloaderConfigurationFreedesktop), p("734E5AFE-F61A-11E6-BC64-92361F002671", PartitionType::kGptBasicDataPartitionAtariTOS), p("8C8F8EFF-AC95-4770-814A-21994F2DBC8F", PartitionType::kGptEncryptedDataPartitionVeraCrypt), p("90B6FF38-B98F-4358-A21F-48F35B4A8AD3", PartitionType::kGptArcaOSType1OS2), p("7C5222BD-8F5D-4087-9C00-BF9843C7B58C", PartitionType::kGptSPDKBlockDeviceSPDK), p("4778ED65-BF42-45FA-9C5B-287A1DC4AAB1", PartitionType::kGptBareBoxStateBareboxBootloader), p("3DE21764-95BD-54BD-A5C3-4ABE786F38A8", PartitionType::kGptUBootEnvironmentUBootBootloader), p("B6FA30DA-92D2-4A9A-96F1-871EC6486200", PartitionType::kGptStatusSoftRAID), p("2E313465-19B9-463F-8126-8A7993773801", PartitionType::kGptScratchSoftRAID), p("FA709C7E-65B1-4593-BFD5-E71D61DE9B02", PartitionType::kGptVolumeSoftRAID), p("BBBA6DF5-F46F-4A89-8F59-8765B2727503", PartitionType::kGptCacheSoftRAID), p("FE8A2634-5E2E-46BA-99E3-3A192091A350", PartitionType::kGptBootloaderFuchsiaStandard), p("D9FD4535-106C-4CEC-8D37-DFC020CA87CB", PartitionType::kGptDurablemutableencryptedsystemdataFuchsiaStandard), p("A409E16B-78AA-4ACC-995C-302352621A41", PartitionType::kGptDurablemutablebootloaderdataFuchsiaStandard), p("F95D940E-CABA-4578-9B93-BB6C90F29D3E", PartitionType::kGptFactoryProvisionedreadOnlysystemdataFuchsiaStandard), p("10B8DBAA-D2BF-42A9-98C6-A7C5DB3701E7", PartitionType::kGptFactoryProvisionedreadOnlybootloaderdataFuchsiaStandard), p("49FD7CB8-DF15-4E73-B9D9-992070127F0F", PartitionType::kGptFuchsiaVolumeManagerFuchsiaStandard), p("421A8BFC-85D9-4D85-ACDA-B64EEC0133E9", PartitionType::kGptVerifiedbootmetadataFuchsiaStandard), p("9B37FFF6-2E58-466A-983A-F7926D0B04E0", PartitionType::kGptZirconbootimageFuchsiaStandard), p("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", PartitionType::kGptFuchsiaEspFuchsiaLegacy), p("606B000B-B7C7-4653-A7D5-B737332C899D", PartitionType::kGptFuchsiaSystemFuchsiaLegacy), p("08185F0C-892D-428A-A789-DBEEC8F55E6A", PartitionType::kGptFuchsiaDataFuchsiaLegacy), p("48435546-4953-2041-494E-5354414C4C52", PartitionType::kGptFuchsiaInstallFuchsiaLegacy), p("2967380E-134C-4CBB-B6DA-17E7CE1CA45D", PartitionType::kGptFuchsiaBlobFuchsiaLegacy), p("41D0E340-57E3-954E-8C1E-17ECAC44CFF5", PartitionType::kGptFuchsiaFvmFuchsiaLegacy), p("DE30CC86-1F4A-4A31-93C4-66F147D33E05", PartitionType::kGptZirconbootimageSlotAFuchsiaLegacy), p("23CC04DF-C278-4CE7-8471-897D1A4BCDF7", PartitionType::kGptZirconbootimageSlotBFuchsiaLegacy), p("A0E5CF57-2DEF-46BE-A80C-A2067C37CD49", PartitionType::kGptZirconbootimageSlotRFuchsiaLegacy), p("4E5E989E-4C86-11E8-A15B-480FCF35F8E6", PartitionType::kGptSysConfigFuchsiaLegacy), p("5A3A90BE-4C86-11E8-A15B-480FCF35F8E6", PartitionType::kGptFactoryConfigFuchsiaLegacy), p("5ECE94FE-4C86-11E8-A15B-480FCF35F8E6", PartitionType::kGptBootloaderFuchsiaLegacy), p("8B94D043-30BE-4871-9DFA-D69556E8C1F3", PartitionType::kGptGuidTestFuchsiaLegacy), p("A13B4D9A-EC5F-11E8-97D8-6C3BE52705BF", PartitionType::kGptVerifiedbootmetadataSlotAFuchsiaLegacy), p("A288ABF2-EC5F-11E8-97D8-6C3BE52705BF", PartitionType::kGptVerifiedbootmetadataSlotBFuchsiaLegacy), p("6A2460C3-CD11-4E8B-80A8-12CCE268ED0A", PartitionType::kGptVerifiedbootmetadataSlotRFuchsiaLegacy), p("1D75395D-F2C6-476B-A8B7-45CC1C97B476", PartitionType::kGptMiscFuchsiaLegacy), p("900B0FC5-90CD-4D4F-84F9-9F8ED579DB88", PartitionType::kGptEmmcBoot1FuchsiaLegacy), p("B2B2E8D1-7C10-4EBC-A2D0-4614568260AD", PartitionType::kGptEmmcBoot2FuchsiaLegacy), }; if (datas.contains(guid)) return datas.value(guid); return PartitionType::kPartitionTypeNotFound; } DeviceError Utils::castFromGError(const GError *const err) { if (!err) return DeviceError::kNoError; const char *errDomain = g_quark_to_string(err->domain); if (strcmp(errDomain, UDISKS_ERR_DOMAIN) == 0) { return static_cast(err->code + UDISKS_ERR_START); } else if (strcmp(errDomain, GIO_ERR_DOMAIN) == 0) { return static_cast(err->code + GIO_ERR_START); } else if (strcmp(errDomain, GDBUS_ERR_DOMAIN) == 0) { return static_cast(err->code + GDBUS_ERR_START); } else { qDebug() << "unhandled error: quark: " << g_quark_to_string(err->domain) << ", msg: " << err->message << ", code: " << err->code; return DeviceError::kUnhandledError; } } DeviceError Utils::castFromJobOperation(const QString &op) { #ifdef p # undef p #endif #define p(k, v) std::pair(k, DeviceError::v) const static QMap datas = { p("ata-smart-selftest", kUDisksBusySMARTSelfTesting), p("drive-eject", kUDisksBusyDriveEjecting), p("encrypted-unlock", kUDisksBusyEncryptedUnlocking), p("encrypted-lock", kUDisksBusyEncryptedLocking), p("encrypted-modify", kUDisksBusyEncryptedModifying), p("encrypted-resize", kUDisksBusyEncryptedResizing), p("swapspace-start", kUDisksBusySwapSpaceStarting), p("swapspace-stop", kUDisksBusySwapSpaceStoping), p("swapspace-modify", kUDisksBusySwpaSpaceModifying), p("filesystem-mount", kUDisksBusyFileSystemMounting), p("filesystem-unmount", kUDisksBusyFileSystemUnmounting), p("filesystem-modify", kUDisksBusyFileSystemModifying), p("filesystem-resize", kUDisksBusyFileSystemResizing), p("format-erase", kUDisksBusyFormatErasing), p("format-mkfs", kUDisksBusyFormatMkfsing), p("loop-setup", kUDisksBusyLoopSetuping), p("partition-modify", kUDisksBusyPartitionModifying), p("partition-delete", kUDisksBusyPartitionDeleting), p("partition-create", kUDisksBusyPartitionCreating), p("cleanup", kUDisksBusyCleanuping), p("ata-secure-erase", kUDisksBusyATASecureErasing), p("ata-enhanced-secure-erase", kUDisksBusyATAEnhancedSecureErasing), p("md-raid-stop", kUDisksBusyMdRaidStarting), p("md-raid-start", kUDisksBusyMdRaidStoping), p("md-raid-fault-device", kUDisksBusyMdRaidFaultingDevice), p("md-raid-remove-device", kUDisksBusyMdRaidRemovingDevice), p("md-raid-create", kUDisksBusyMdRaidCreating), }; return datas.value(op, DeviceError::kUnhandledError); } QString Utils::getNameByProperty(Property property) { #ifdef rp # undef rp #endif #define rp(v, k) std::pair(k, v) const static QMap datas = { rp("UserspaceMountOptions", Property::kBlockUserspaceMountOptions), rp("Configuration", Property::kBlockConfiguration), rp("CryptoBackingDevice", Property::kBlockCryptoBackingDevice), rp("Device", Property::kBlockDevice), rp("Drive", Property::kBlockDrive), rp("IdLabel", Property::kBlockIDLabel), rp("IdType", Property::kBlockIDType), rp("IdUsage", Property::kBlockIDUsage), rp("IdUUID", Property::kBlockIDUUID), rp("IdVersion", Property::kBlockIDVersion), rp("DeviceNumber", Property::kBlockDeviceNumber), rp("PreferredDevice", Property::kBlockPreferredDevice), rp("Id", Property::kBlockID), rp("Size", Property::kBlockSize), rp("ReadOnly", Property::kBlockReadOnly), rp("Symlinks", Property::kBlockSymlinks), rp("HintPartitionable", Property::kBlockHintPartitionable), rp("HintSystem", Property::kBlockHintSystem), rp("HintIgnore", Property::kBlockHintIgnore), rp("HintAuto", Property::kBlockHintAuto), rp("HintName", Property::kBlockHintName), rp("HintIconName", Property::kBlockHintIconName), rp("HintSymbolicIconName", Property::kBlockHintSymbolicIconName), rp("MdRaid", Property::kBlockMdRaid), rp("MdRaidMember", Property::kBlockMdRaidMember), rp("ConnectionBus", Property::kDriveConnectionBus), rp("Removable", Property::kDriveRemovable), rp("Ejectable", Property::kDriveEjectable), rp("Seat", Property::kDriveSeat), rp("Media", Property::kDriveMedia), rp("MediaCompatibility", Property::kDriveMediaCompatibility), rp("MediaRemovable", Property::kDriveMediaRemovable), rp("MediaAvailable", Property::kDriveMediaAvailable), rp("MediaChangeDetected", Property::kDriveMediaChangeDetected), rp("TimeDetected", Property::kDriveTimeDetected), rp("TimeMediaDetected", Property::kDriveTimeMediaDetected), rp("Size", Property::kDriveSize), rp("Optical", Property::kDriveOptical), rp("OpticalBlank", Property::kDriveOpticalBlank), rp("OpticalNumTracks", Property::kDriveOpticalNumTracks), rp("OpticalNumAudioTracks", Property::kDriveOpticalNumAudioTracks), rp("OpticalNumDataTracks", Property::kDriveOpticalNumDataTracks), rp("OpticalNumSessions", Property::kDriveOpticalNumSessions), rp("Model", Property::kDriveModel), rp("Revision", Property::kDriveRevision), rp("RotationRate", Property::kDriveRotationRate), rp("Serial", Property::kDriveSerial), rp("Vender", Property::kDriveVender), rp("WWN", Property::kDriveWWN), rp("SortKey", Property::kDriveSortKey), rp("Configuration", Property::kDriveConfiguration), rp("ID", Property::kDriveID), rp("CanPowerOff", Property::kDriveCanPowerOff), rp("SiblingID", Property::kDriveSiblingID), rp("MountPoints", Property::kFileSystemMountPoint), rp("Number", Property::kPartitionNumber), rp("Type", Property::kPartitionType), rp("Offset", Property::kPartitionOffset), rp("Size", Property::kPartitionSize), rp("Flags", Property::kPartitionFlags), rp("Name", Property::kPartitionName), rp("UUID", Property::kPartitionUUID), rp("Table", Property::kPartitionTable), rp("IsContainer", Property::kPartitionIsContainer), rp("IsContained", Property::kPartitionIsContained), rp("ChildConfiguration", Property::kEncryptedChildConfiguration), rp("CleartextDevice", Property::kEncryptedCleartextDevice), rp("HintEncryptionType", Property::kEncryptedHintEncryptionType), rp("MetadataSize", Property::kEncryptedMetadataSize), }; return datas.value(property, ""); } QString Utils::errorMessage(DeviceError err) { #ifdef p # undef p #endif #define p(k, v) std::pair(DeviceError::k, v) static const QMap errors { p(kNoError, "No error"), p(kUDisksErrorFailed, "Failed"), p(kUDisksErrorCancelled, "Cancelled"), p(kUDisksErrorAlreadyCancelled, "Already cancelled"), p(kUDisksErrorNotAuthorized, "Not authorized"), p(kUDisksErrorNotAuthorizedCanObtain, "Not authorized can obtain"), p(kUDisksErrorNotAuthorizedDismissed, "Not authorized dismissed"), p(kUDisksErrorAlreadyMounted, "Device is already mounted"), p(kUDisksErrorNotMounted, "Device is not mounted"), p(kUDisksErrorOptionNotPermitted, "Option is not permitted"), p(kUDisksErrorMountedByOtherUser, "Device is mounted by other user"), p(kUDisksErrorAlreadyUnmounting, "Device is already unmounted"), p(kUDisksErrorNotSupproted, "Not supported operation"), p(kUDisksErrorTimedOut, "Operation time out"), p(kUDisksErrorWouldWakeup, "Operation would wakeup"), p(kUDisksErrorDeviceBusy, "Device is busy"), p(kUDisksErrorScsiDaemonTransportFailed, "SCSI daemon transport failed"), p(kUDisksErrorScsiHostNotFound, "SCSI host not found"), p(kUDisksErrorScsiIDMB, "SCSI IDMB"), p(kUDisksErrorScsiLoginFailed, "SCSI login failed"), p(kUDisksErrorScsiLoginAuthFailed, "SCSI login auth failed"), p(kUDisksErrorScsiLoginFatal, "SCSI login fatal"), p(kUDisksErrorScsiLogoutFailed, "SCSI logout failed"), p(kUDisksErrorScsiNoFirmware, "SCSI no firmware found"), p(kUDisksErrorScsiNoObjectsFound, "SCSI no objects found"), p(kUDisksErrorScsiNotConnected, "SCSI not connected"), p(kUDisksErrorScsiTransportFailed, "SCSI transport failed"), p(kUDisksErrorScsiUnknownDiscoveryType, "SCSI unknown discovery type"), // http://storaged.org/doc/udisks2-api/latest/gdbus-org.freedesktop.UDisks2.Job.html#gdbus-property-org-freedesktop-UDisks2-Job.Operation p(kUDisksBusySMARTSelfTesting, "SMART self-test operation"), p(kUDisksBusyDriveEjecting, "Ejecting the medium from a drive"), p(kUDisksBusyEncryptedUnlocking, "Unlocking encrypted device"), p(kUDisksBusyEncryptedLocking, "Locking encrypted device"), p(kUDisksBusyEncryptedModifying, "Modifying encrypted device"), p(kUDisksBusyEncryptedResizing, "Resizing encrypted device"), p(kUDisksBusySwapSpaceStarting, "Starting swapspace"), p(kUDisksBusySwapSpaceStoping, "Stopping swapspace"), p(kUDisksBusySwpaSpaceModifying, "Modifying swapspace"), p(kUDisksBusyFileSystemUnmounting, "Unmounting a filesystem"), p(kUDisksBusyFileSystemMounting, "Mounting a filesystem"), p(kUDisksBusyFileSystemModifying, "Modifying a filesystem"), p(kUDisksBusyFileSystemResizing, "Resizing a filesystem"), p(kUDisksBusyFormatErasing, "Erasing a device"), p(kUDisksBusyFormatMkfsing, "Creating a filesystem"), p(kUDisksBusyLoopSetuping, "Setting up a loop device"), p(kUDisksBusyPartitionModifying, "Modifying a partition"), p(kUDisksBusyPartitionDeleting, "Deleting a partition"), p(kUDisksBusyPartitionCreating, "Creating a partition"), p(kUDisksBusyCleanuping, "Cleaning up devices that were removed without being properly unmounted or shut down"), p(kUDisksBusyATASecureErasing, "ATA Secure Erase"), p(kUDisksBusyATAEnhancedSecureErasing, "ATA Enhanced Secure Erase"), p(kUDisksBusyMdRaidStarting, "Stopping a RAID Array"), p(kUDisksBusyMdRaidStoping, "Starting a RAID Array"), p(kUDisksBusyMdRaidFaultingDevice, "Marking device in RAID Array as faulty"), p(kUDisksBusyMdRaidRemovingDevice, " Removing device from RAID Array"), p(kUDisksBusyMdRaidCreating, "Create a RAID Array"), p(kGIOErrorFailed, "GIOErrorFailed"), p(kGIOErrorNotFound, "GIOErrorNotFound"), p(kGIOErrorExists, "GIOErrorExists"), p(kGIOErrorIsDirectory, "GIOErrorIsDirectory"), p(kGIOErrorNotDirertory, "GIOErrorNotDirertory"), p(kGIOErrorNotEmpty, "GIOErrorNotEmpty"), p(kGIOErrorNotRegularFile, "GIOErrorNotRegularFile"), p(kGIOErrorNotSymbolicLink, "GIOErrorNotSymbolicLink"), p(kGIOErrorNotMountableFile, "GIOErrorNotMountableFile"), p(kGIOErrorFilenameTooLong, "GIOErrorFilenameTooLong"), p(kGIOErrorInvalidFilename, "GIOErrorInvalidFilename"), p(kGIOErrorTooManyLinks, "GIOErrorTooManyLinks"), p(kGIOErrorNoSpace, "GIOErrorNoSpace"), p(kGIOErrorInvalidArgument, "GIOErrorInvalidArgument"), p(kGIOErrorPermissionDenied, "GIOErrorPermissionDenied"), p(kGIOErrorNotSupported, "GIOErrorNotSupported"), p(kGIOErrorNotMounted, "GIOErrorNotMounted"), p(kGIOErrorAlreadyMounted, "GIOErrorAlreadyMounted"), p(kGIOErrorClosed, "GIOErrorClosed"), p(kGIOErrorCancelled, "GIOErrorCancelled"), p(kGIOErrorPending, "GIOErrorPending"), p(kGIOErrorReadONly, "GIOErrorReadONly"), p(kGIOErrorCantCreateBackup, "GIOErrorCantCreateBackup"), p(kGIOErrorWrongETAG, "GIOErrorWrongETAG"), p(kGIOErrorTimedOut, "GIOErrorTimedOut"), p(kGIOErrorWouldRecurse, "GIOErrorWouldRecurse"), p(kGIOErrorBusy, "GIOErrorBusy"), p(kGIOErrorWouldBlock, "GIOErrorWouldBlock"), p(kGIOErrorHostNotFound, "GIOErrorHostNotFound"), p(kGIOErrorWouldMerge, "GIOErrorWouldMerge"), p(kGIOErrorFailedHandled, "GIOErrorFailedHandled"), p(kGIOErrorTooManyOpenFiles, "GIOErrorTooManyOpenFiles"), p(kGIOErrorNotInitilized, "GIOErrorNotInitilized"), p(kGIOErrorAddressInUse, "GIOErrorAddressInUse"), p(kGIOErrorPartialInput, "GIOErrorPartialInput"), p(kGIOErrorInvalidData, "GIOErrorInvalidData"), p(kGIOErrorDBusError, "GIOErrorDBusError"), p(kGIOErrorHostUnreachable, "GIOErrorHostUnreachable"), p(kGIOErrorNetworkUnreachable, "GIOErrorNetworkUnreachable"), p(kGIOErrorConnectionRefused, "GIOErrorConnectionRefused"), p(kGIOErrorProxyFailed, "GIOErrorProxyFailed"), p(kGIOErrorProxyAuthFailed, "GIOErrorProxyAuthFailed"), p(kGIOErrorProxyNeedAuth, "GIOErrorProxyNeedAuth"), p(kGIOErrorProxyNotAllowed, "GIOErrorProxyNotAllowed"), p(kGIOErrorBrokenPipe, "GIOErrorBrokenPipe"), p(kGIOErrorConnectionClosed, "GIOErrorConnectionClosed"), p(kGIOErrorNotConnected, "GIOErrorNotConnected"), p(kGIOErrorMessageTooLarge, "GIOErrorMessageTooLarge"), p(kGDBusErrorFailed, "GDBusErrorFailed"), p(kGDBusErrorNoMemory, "GDBusErrorNoMemory"), p(kGDBusErrorServiceUnknown, "GDBusErrorServiceUnknown"), p(kGDBusErrorNameHasNoOwner, "GDBusErrorNameHasNoOwner"), p(kGDBusErrorNoReply, "GDBusErrorNoReply"), p(kGDBusErrorIOError, "GDBusErrorIOError"), p(kGDBusErrorBadAddress, "GDBusErrorBadAddress"), p(kGDBusErrorNotSupported, "GDBusErrorNotSupported"), p(kGDBusErrorLimitsExceeded, "GDBusErrorLimitsExceeded"), p(kGDBusErrorAccessDenied, "GDBusErrorAccessDenied"), p(kGDBusErrorAuthFailed, "GDBusErrorAuthFailed"), p(kGDBusErrorNoServer, "GDBusErrorNoServer"), p(kGDBusErrorTimeout, "GDBusErrorTimeout"), p(kGDBusErrorNoNetwork, "GDBusErrorNoNetwork"), p(kGDBusErrorAddressInUse, "GDBusErrorAddressInUse"), p(kGDBusErrorDisconnected, "GDBusErrorDisconnected"), p(kGDBusErrorInvalidArgs, "GDBusErrorInvalidArgs"), p(kGDBusErrorFileNotFound, "GDBusErrorFileNotFound"), p(kGDBusErrorFileExists, "GDBusErrorFileExists"), p(kGDBusErrorUnknownMethod, "GDBusErrorUnknownMethod"), p(kGDBusErrorTimedOut, "GDBusErrorTimedOut"), p(kGDBusErrorMatchRuleNotFound, "GDBusErrorMatchRuleNotFound"), p(kGDBusErrorMatchRuleInvalid, "GDBusErrorMatchRuleInvalid"), p(kGDBusErrorSpawnExecFailed, "GDBusErrorSpawnExecFailed"), p(kGDBusErrorSpawnForkFailed, "GDBusErrorSpawnForkFailed"), p(kGDBusErrorSpawnChildExited, "GDBusErrorSpawnChildExited"), p(kGDBusErrorSpawnChildSignaled, "GDBusErrorSpawnChildSignaled"), p(kGDBusErrorSpawnFailed, "GDBusErrorSpawnFailed"), p(kGDBusErrorSpawnSetupFailed, "GDBusErrorSpawnSetupFailed"), p(kGDBusErrorSpawnConfigInvalid, "GDBusErrorSpawnConfigInvalid"), p(kGDBusErrorSpawnServiceInvalid, "GDBusErrorSpawnServiceInvalid"), p(kGDBusErrorSpawnServiceNotFound, "GDBusErrorSpawnServiceNotFound"), p(kGDBusErrorSpawnPermissionsInvalid, "GDBusErrorSpawnPermissionsInvalid"), p(kGDBusErrorSpawnFileInvalid, "GDBusErrorSpawnFileInvalid"), p(kGDBusErrorSpawnNoMemory, "GDBusErrorSpawnNoMemory"), p(kGDBusErrorUnixProcessIdUnkown, "GDBusErrorUnixProcessIdUnkown"), p(kGDBusErrorInvalidSignature, "GDBusErrorInvalidSignature"), p(kGDBusErrorInvalidFileContent, "GDBusErrorInvalidFileContent"), p(kGDBusErrorSeLinuxSecurityContextUnknown, "GDBusErrorSeLinuxSecurityContextUnknown"), p(kGDBusErrorADTAuditDataUnknown, "GDBusErrorADTAuditDataUnknown"), p(kGDBusErrorObjectPathInUse, "GDBusErrorObjectPathInUse"), p(kGDBusErrorUnknownObject, "GDBusErrorUnknownObject"), p(kGDBusErrorUnknownInterface, "GDBusErrorUnknownInterface"), p(kGDBusErrorUnknownProperty, "GDBusErrorUnknownProperty"), p(kGDBusErrorPropertyReadOnly, "GDBusErrorPropertyReadOnly"), p(kUserErrorNotMountable, "Device is not mountable"), p(kUserErrorNotEjectable, "Device is not ejectable"), p(kUserErrorNoDriver, "Device do not have a drive"), p(kUserErrorNotEncryptable, "Device is not encryptable"), p(kUserErrorNoPartition, "Device do not have a partition"), p(kUserErrorNoBlock, "Device do not have a block"), p(kUserErrorNetworkWrongPasswd, "Wrong username or password"), p(kUserErrorNetworkAnonymousNotAllowed, "Anonymous login is not allowed"), p(kUserErrorTimedOut, "Operation timeout"), p(kUserErrorAlreadyMounted, "Device is already mounted"), p(kUserErrorNotMounted, "Device is not mounted"), p(kUserErrorFailed, "Operation failed"), p(kUnhandledError, "Unhandled error"), }; return errors.value(err); } QString Utils::errorMessage(MonitorError err) { static const QMap errors { std::pair(MonitorError::kNoError, "No error"), std::pair(MonitorError::kMonitorNotRegister, "Not registered monitor"), std::pair(MonitorError::kMonitorAlreadyRegistered, "Monitor is already registered"), }; return errors.value(err); } OperationErrorInfo Utils::genOperateErrorInfo(DeviceError err, const QString &errMsg) { OperationErrorInfo ret; ret.code = err; ret.message = errMsg; if (errMsg.isEmpty()) ret.message = errorMessage(err); return ret; } QString Utils::gcharToQString(char *tmp) { if (!tmp) return QString(); QString ret(tmp); g_free(tmp); tmp = nullptr; return ret; } QStringList Utils::gcharvToQStringList(char **tmp) { QStringList ret; int next = 0; while (tmp && tmp[next]) { ret << QString(tmp[next]); next += 1; } g_strfreev(tmp); tmp = nullptr; return ret; } QString Utils::currentUser() { auto userInfo = getpwuid(getuid()); if (userInfo) return userInfo->pw_name; return ""; } QVariant Utils::gvariantToQVariant(GVariant *value) { if (!value) return QVariant(); const GVariantType *type = g_variant_get_type(value); if (g_variant_type_equal(type, G_VARIANT_TYPE_STRING)) { return QString::fromUtf8(g_variant_get_string(value, nullptr)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTESTRING)) { return QByteArray(g_variant_get_bytestring(value)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_BOOLEAN)) { return bool(g_variant_get_boolean(value)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE)) { return quint8(g_variant_get_byte(value)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16)) { return qint16(g_variant_get_int16(value)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16)) { return quint16(g_variant_get_uint16(value)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32)) { return qint32(g_variant_get_int32(value)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32)) { return quint32(g_variant_get_uint32(value)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64)) { return qint64(g_variant_get_int64(value)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64)) { return quint64(g_variant_get_uint64(value)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_DOUBLE)) { return g_variant_get_double(value); } // Not all processed return QVariant(); } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/dblockdevice.cpp000066400000000000000000001316631523477242100245120ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "private/dblockdevice_p.h" #include #include #include #include extern "C" { #include } DFM_MOUNT_USE_NS inline void DBlockDevicePrivate::handleErrorAndRelease(CallbackProxy *proxy, bool result, GError *gerr, QString info) { OperationErrorInfo err; if (!result && gerr) { err.code = Utils::castFromGError(gerr); err.message = gerr->message; qInfo() << "error occured while operating device" << err.message; g_error_free(gerr); } if (proxy) { if (proxy->cb) { proxy->cb(result, err); } else if (proxy->cbWithInfo) { proxy->cbWithInfo(result, err, info); } delete proxy; } } void DBlockDevicePrivate::mountAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData) { UDisksFilesystem *fs = UDISKS_FILESYSTEM(sourceObj); Q_ASSERT_X(fs, __FUNCTION__, "fs is not valid"); CallbackProxy *proxy = static_cast(userData); GError *err = nullptr; g_autofree char *mountPoint = nullptr; bool result = udisks_filesystem_call_mount_finish(fs, &mountPoint, res, &err); if (mountPoint) result = true; handleErrorAndRelease(proxy, result, err, mountPoint); // ignore mount point, which will be notified by onPropertyChanged } void DBlockDevicePrivate::unmountAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData) { UDisksFilesystem *fs = UDISKS_FILESYSTEM(sourceObj); Q_ASSERT_X(fs, __FUNCTION__, "fs is not valid"); CallbackProxy *proxy = static_cast(userData); GError *err = nullptr; bool result = udisks_filesystem_call_unmount_finish(fs, res, &err); handleErrorAndRelease(proxy, result, err); } void DBlockDevicePrivate::renameAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData) { UDisksFilesystem *fs = UDISKS_FILESYSTEM(sourceObj); Q_ASSERT_X(fs, __FUNCTION__, "fs is not valid"); CallbackProxy *proxy = static_cast(userData); GError *err = nullptr; bool result = udisks_filesystem_call_set_label_finish(fs, res, &err); handleErrorAndRelease(proxy, result, err); } void DBlockDevicePrivate::ejectAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData) { UDisksDrive *drive = UDISKS_DRIVE(sourceObj); Q_ASSERT_X(drive, __FUNCTION__, "drive is not valid"); CallbackProxy *proxy = static_cast(userData); GError *err = nullptr; bool result = udisks_drive_call_eject_finish(drive, res, &err); handleErrorAndRelease(proxy, result, err); } void DBlockDevicePrivate::powerOffAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData) { UDisksDrive *drive = UDISKS_DRIVE(sourceObj); Q_ASSERT_X(drive, __FUNCTION__, "drive is not valid"); CallbackProxy *proxy = static_cast(userData); GError *err = nullptr; bool result = udisks_drive_call_power_off_finish(drive, res, &err); handleErrorAndRelease(proxy, result, err); } void DBlockDevicePrivate::lockAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData) { UDisksEncrypted *encrypted = UDISKS_ENCRYPTED(sourceObj); Q_ASSERT_X(encrypted, __FUNCTION__, "encrypted is not valid"); CallbackProxy *proxy = static_cast(userData); GError *err = nullptr; bool result = udisks_encrypted_call_lock_finish(encrypted, res, &err); handleErrorAndRelease(proxy, result, err); } void DBlockDevicePrivate::unlockAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData) { UDisksEncrypted *encrypted = UDISKS_ENCRYPTED(sourceObj); Q_ASSERT_X(encrypted, __FUNCTION__, "encrypted is not valid"); CallbackProxy *proxy = static_cast(userData); GError *err = nullptr; g_autofree char *clearTextDev = nullptr; bool result = udisks_encrypted_call_unlock_finish(encrypted, &clearTextDev, res, &err); handleErrorAndRelease(proxy, result, err, QString(clearTextDev)); } void DBlockDevicePrivate::rescanAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData) { UDisksBlock *block = UDISKS_BLOCK(sourceObj); Q_ASSERT_X(block, __FUNCTION__, "block is not valid"); CallbackProxy *proxy = static_cast(userData); GError *err = nullptr; bool result = udisks_block_call_rescan_finish(block, res, &err); handleErrorAndRelease(proxy, result, err); } int DBlockDevicePrivate::dedupMountPoint(struct libmnt_table *table, struct libmnt_fs *a, struct libmnt_fs *b) { if (mnt_fs_is_pseudofs(a) || mnt_fs_is_netfs(a) || mnt_fs_is_pseudofs(b) || mnt_fs_is_netfs(b)) return 1; return !mnt_fs_streq_srcpath(a, mnt_fs_get_srcpath(b)); } QString DBlockDevicePrivate::findFirstMountPoint(const QString &device) { if (device.isEmpty()) return ""; QString mountPoint; struct libmnt_table *table = mnt_new_table(); if (mnt_table_parse_mtab(table, NULL) < 0) { qWarning() << "cannot parse mtab!"; return ""; } mnt_table_uniq_fs(table, MNT_UNIQ_FORWARD, dedupMountPoint); struct libmnt_iter *iter = mnt_new_iter(MNT_ITER_FORWARD); struct libmnt_fs *fs = NULL; while (mnt_table_next_fs(table, iter, &fs) == 0) { const char *src = mnt_fs_get_source(fs); const char *target = mnt_fs_get_target(fs); if (strcmp(src, device.toStdString().c_str()) == 0) { mountPoint = target; break; } } mnt_free_iter(iter); mnt_free_table(table); return mountPoint; } UDisksObject_autoptr DBlockDevicePrivate::getUDisksObject() const { Q_ASSERT(client); Q_ASSERT(!blkObjPath.isEmpty()); std::string str = blkObjPath.toStdString(); UDisksObject_autoptr obj = udisks_client_get_object(client, str.c_str()); return obj; } UDisksBlock_autoptr DBlockDevicePrivate::getBlockHandler() const { UDisksObject_autoptr obj = getUDisksObject(); if (!obj) { qWarning() << "UDisksObject is not valid for" << blkObjPath; return nullptr; } UDisksBlock_autoptr ret = udisks_object_get_block(obj); return ret; } UDisksDrive_autoptr DBlockDevicePrivate::getDriveHandler() const { Q_ASSERT(client); UDisksBlock_autoptr blk = getBlockHandler(); if (!blk) { qWarning() << "UDisksBlock is not valid for" << blkObjPath; return nullptr; } UDisksDrive_autoptr ret = udisks_client_get_drive_for_block(client, blk); return ret; } UDisksLoop_autoptr DBlockDevicePrivate::getLoopHandler() const { Q_ASSERT(client); UDisksBlock_autoptr blk = getBlockHandler(); if (!blk) { qWarning() << "UDisksBlock is not valid for" << blkObjPath; return nullptr; } UDisksLoop_autoptr ret = udisks_client_get_loop_for_block(client, blk); return ret; } UDisksEncrypted_autoptr DBlockDevicePrivate::getEncryptedHandler() const { UDisksObject_autoptr obj = getUDisksObject(); if (!obj) { qWarning() << "UDisksObject is not valid for" << blkObjPath; return nullptr; } UDisksEncrypted_autoptr ret = udisks_object_get_encrypted(obj); return ret; } UDisksPartition_autoptr DBlockDevicePrivate::getPartitionHandler() const { UDisksObject_autoptr obj = getUDisksObject(); if (!obj) { qWarning() << "UDisksObject is not valid for" << blkObjPath; return nullptr; } UDisksPartition_autoptr ret = udisks_object_get_partition(obj); return ret; } UDisksPartitionTable_autoptr DBlockDevicePrivate::getPartitionTableHandler() const { UDisksObject_autoptr obj = getUDisksObject(); if (!obj) { qWarning() << "UDisksObject is not valid for" << blkObjPath; return nullptr; } UDisksPartitionTable_autoptr ret = udisks_object_get_partition_table(obj); return ret; } UDisksFilesystem_autoptr DBlockDevicePrivate::getFilesystemHandler() const { UDisksObject_autoptr obj = getUDisksObject(); if (!obj) { qWarning() << "UDisksObject is not valid for" << blkObjPath; return nullptr; } UDisksFilesystem_autoptr ret = udisks_object_get_filesystem(obj); return ret; } bool DBlockDevicePrivate::findJob(JobType type) { QString objPath = blkObjPath; if (type == kDriveJob) objPath = getBlockProperty(Property::kBlockDrive).toString(); if (objPath == "/") // loop devices' drive field is '/' return false; UDisksObject_autoptr obj = udisks_client_get_object(client, objPath.toStdString().c_str()); if (!obj) return false; struct UserData { DBlockDevicePrivate *d { nullptr }; QString objPath; bool hasJob = false; }; UserData data { this, blkObjPath, false }; GList_autoptr jobs = udisks_client_get_jobs_for_object(client, obj); g_list_foreach( jobs, [](void *item, void *that) { UDisksJob *job = static_cast(item); UserData *d = static_cast(that); if (!job || !d) return; QString op(udisks_job_get_operation(job)); qInfo() << "Working now..." << d->objPath << op; d->hasJob = true; d->d->lastError.code = Utils::castFromJobOperation(op); }, &data); return data.hasJob; } DBlockDevice::DBlockDevice(UDisksClient *cli, const QString &udisksObjPath, QObject *parent) : DDevice(new DBlockDevicePrivate(cli, udisksObjPath, this), parent) { auto dp = Utils::castClassFromTo(d.data()); if (!dp) { qCritical() << "private pointer not valid" << __PRETTY_FUNCTION__; abort(); } using namespace std; using namespace std::placeholders; registerPath(bind(&DBlockDevicePrivate::path, dp)); registerMount(bind(&DBlockDevicePrivate::mount, dp, _1)); registerMountAsync(bind(&DBlockDevicePrivate::mountAsync, dp, _1, _2)); registerUnmount(bind(&DBlockDevicePrivate::unmount, dp, _1)); registerUnmountAsync(bind(&DBlockDevicePrivate::unmountAsync, dp, _1, _2)); registerRename(bind(&DBlockDevicePrivate::rename, dp, _1, _2)); registerRenameAsync(bind(&DBlockDevicePrivate::renameAsync, dp, _1, _2, _3)); registerMountPoint(bind(&DBlockDevicePrivate::mountPoint, dp)); registerFileSystem(bind(&DBlockDevicePrivate::fileSystem, dp)); registerSizeTotal(bind(&DBlockDevicePrivate::sizeTotal, dp)); registerSizeUsage(bind(&DBlockDevicePrivate::sizeUsage, dp)); registerSizeFree(bind(&DBlockDevicePrivate::sizeFree, dp)); registerDeviceType(bind(&DBlockDevicePrivate::deviceType, dp)); registerGetProperty(bind(&DBlockDevicePrivate::getProperty, dp, _1)); registerDisplayName(bind(&DBlockDevicePrivate::displayName, dp)); } DBlockDevice::~DBlockDevice() { } bool DBlockDevice::eject(const QVariantMap &opts) { auto dp = Utils::castClassFromTo(d.data()); if (dp) { return dp->eject(opts); } else { qCritical() << "DP IS NULL: " << __PRETTY_FUNCTION__; return false; } } void DBlockDevice::ejectAsync(const QVariantMap &opts, DeviceOperateCallback cb) { auto dp = Utils::castClassFromTo(d.data()); if (dp) { return dp->ejectAsync(opts, cb); } else { qCritical() << "DP IS NULL: " << __PRETTY_FUNCTION__; } } bool DBlockDevice::powerOff(const QVariantMap &opts) { auto dp = Utils::castClassFromTo(d.data()); if (dp) { return dp->powerOff(opts); } else { qCritical() << "DP IS NULL: " << __PRETTY_FUNCTION__; return false; } } void DBlockDevice::powerOffAsync(const QVariantMap &opts, DeviceOperateCallback cb) { auto dp = Utils::castClassFromTo(d.data()); if (dp) { dp->powerOffAsync(opts, cb); } else { qCritical() << "DP IS NULL: " << __PRETTY_FUNCTION__; } } bool DBlockDevice::lock(const QVariantMap &opts) { auto dp = Utils::castClassFromTo(d.data()); if (dp) { return dp->lock(opts); } else { qCritical() << "DP IS NULL: " << __PRETTY_FUNCTION__; return false; } } void DBlockDevice::lockAsync(const QVariantMap &opts, DeviceOperateCallback cb) { auto dp = Utils::castClassFromTo(d.data()); if (dp) { dp->lockAsync(opts, cb); } else { qCritical() << "DP IS NULL: " << __PRETTY_FUNCTION__; } } bool DBlockDevice::unlock(const QString &passwd, QString &clearTextDev, const QVariantMap &opts) { auto dp = Utils::castClassFromTo(d.data()); if (dp) { return dp->unlock(passwd, clearTextDev, opts); } else { qCritical() << "DP IS NULL: " << __PRETTY_FUNCTION__; return false; } } void DBlockDevice::unlockAsync(const QString &passwd, const QVariantMap &opts, DeviceOperateCallbackWithMessage cb) { auto dp = Utils::castClassFromTo(d.data()); if (dp) { dp->unlockAsync(passwd, opts, cb); } else { qCritical() << "DP IS NULL: " << __PRETTY_FUNCTION__; } } bool DBlockDevice::rescan(const QVariantMap &opts) { auto dp = Utils::castClassFromTo(d.data()); if (dp) return dp->rescan(opts); else qCritical() << "DP IS NULL: " << __PRETTY_FUNCTION__; return false; } void DBlockDevice::rescanAsync(const QVariantMap &opts, DeviceOperateCallback cb) { auto dp = Utils::castClassFromTo(d.data()); if (dp) { dp->rescanAsync(opts, cb); } else { if (cb) cb(false, Utils::genOperateErrorInfo(DeviceError::kUserErrorFailed)); qWarning() << "private pointer is null!"; } } QStringList DBlockDevice::mountPoints() const { return getProperty(Property::kFileSystemMountPoint).toStringList(); } QString DBlockDevice::device() const { return getProperty(Property::kBlockDevice).toString(); } QString DBlockDevice::drive() const { return getProperty(Property::kBlockDrive).toString(); } QString DBlockDevice::idLabel() const { return getProperty(Property::kBlockIDLabel).toString(); } bool DBlockDevice::removable() const { return getProperty(Property::kDriveRemovable).toBool(); } bool DBlockDevice::optical() const { return getProperty(Property::kDriveOptical).toBool(); } bool DBlockDevice::opticalBlank() const { return getProperty(Property::kDriveOpticalBlank).toBool(); } QStringList DBlockDevice::mediaCompatibility() const { return getProperty(Property::kDriveMediaCompatibility).toStringList(); } bool DBlockDevice::canPowerOff() const { return getProperty(Property::kDriveCanPowerOff).toBool(); } bool DBlockDevice::ejectable() const { return getProperty(Property::kDriveEjectable).toBool(); } bool DBlockDevice::isEncrypted() const { auto dp = Utils::castClassFromTo(d.data()); return dp ? dp->getEncryptedHandler() != nullptr : false; } bool DBlockDevice::hasFileSystem() const { auto dp = Utils::castClassFromTo(d.data()); return dp ? dp->getFilesystemHandler() != nullptr : false; } bool DBlockDevice::hasPartitionTable() const { auto dp = Utils::castClassFromTo(d.data()); return dp ? dp->getPartitionTableHandler() != nullptr : false; } bool DBlockDevice::hasPartition() const { auto dp = Utils::castClassFromTo(d.data()); return dp ? dp->getPartitionHandler() != nullptr : false; } bool DBlockDevice::isLoopDevice() const { auto dp = Utils::castClassFromTo(d.data()); return dp ? dp->getLoopHandler() != nullptr : false; } bool DBlockDevice::hintIgnore() const { return getProperty(Property::kBlockHintIgnore).toBool(); } bool DBlockDevice::hintSystem() const { return getProperty(Property::kBlockHintSystem).toBool(); } PartitionType DBlockDevice::partitionEType() const { auto typestr = partitionType(); if (typestr.isEmpty()) return PartitionType::kPartitionTypeNotFound; bool ok = false; int type = typestr.toInt(&ok, 16); if (ok) { if (type >= static_cast(PartitionType::kMbrEmpty) && type <= static_cast(PartitionType::kMbrBBT)) return static_cast(type); else return PartitionType::kPartitionTypeNotFound; } else { return Utils::getPartitionTypeByGuid(typestr); } } QString DBlockDevice::partitionType() const { return getProperty(Property::kPartitionType).toString(); } bool DBlockDevice::hasBlock() const { auto dp = Utils::castClassFromTo(d.data()); return dp ? dp->getBlockHandler() != nullptr : false; } DBlockDevicePrivate::DBlockDevicePrivate(UDisksClient *cli, const QString &blkObjPath, DBlockDevice *qq) : DDevicePrivate(qq), blkObjPath(blkObjPath), client(cli) { } DBlockDevicePrivate::~DBlockDevicePrivate() { } QString DBlockDevicePrivate::path() const { return blkObjPath; } QString DBlockDevicePrivate::mount(const QVariantMap &opts) { warningIfNotInMain(); if (findJob(kBlockJob)) return ""; UDisksFilesystem_autoptr fs = getFilesystemHandler(); if (!fs) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); return ""; } QStringList mpts = getProperty(Property::kFileSystemMountPoint).toStringList(); if (!mpts.empty()) { lastError = Utils::genOperateErrorInfo(DeviceError::kUDisksErrorAlreadyMounted); return mpts.first(); } GError *err = nullptr; GVariant *gopts = Utils::castFromQVariantMap(opts); char *mountPoint = nullptr; bool mounted = udisks_filesystem_call_mount_sync(fs, gopts, &mountPoint, nullptr, &err); handleErrorAndRelease(err); QString ret; if (mounted && mountPoint) { ret = mountPoint; g_free(mountPoint); } return ret; } void DBlockDevicePrivate::mountAsync(const QVariantMap &opts, DeviceOperateCallbackWithMessage cb) { if (findJob(kBlockJob)) { if (cb) cb(false, lastError, ""); return; } CallbackProxy *proxy = cb ? new CallbackProxy(cb) : nullptr; UDisksFilesystem_autoptr fs = getFilesystemHandler(); if (!fs) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); if (proxy) { proxy->cbWithInfo(false, lastError, ""); delete proxy; } return; } QStringList mpts = getProperty(Property::kFileSystemMountPoint).toStringList(); if (!mpts.empty()) { lastError = Utils::genOperateErrorInfo(DeviceError::kUDisksErrorAlreadyMounted); if (proxy) { proxy->cbWithInfo(true, lastError, mpts.first()); // when it's already mounted, return true but report an error refer to `already mounted`. delete proxy; } return; } // mount device async GVariant *gopts = Utils::castFromQVariantMap(opts); udisks_filesystem_call_mount(fs, gopts, nullptr, mountAsyncCallback, proxy); } bool DBlockDevicePrivate::unmount(const QVariantMap &opts) { warningIfNotInMain(); if (findJob(kBlockJob)) return false; UDisksFilesystem_autoptr fs = getFilesystemHandler(); if (!fs) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); return true; // since device is not mountable, so we just return true here } QStringList mpts = getProperty(Property::kFileSystemMountPoint).toStringList(); if (mpts.empty()) { lastError = Utils::genOperateErrorInfo(DeviceError::kUDisksErrorNotMounted); return true; // since it's not mounted, then this invocation returns true } GVariant *gopts = Utils::castFromQVariantMap(opts); GError *err = nullptr; bool result = udisks_filesystem_call_unmount_sync(fs, gopts, nullptr, &err); if (result) return true; handleErrorAndRelease(err); return false; } void DBlockDevicePrivate::unmountAsync(const QVariantMap &opts, DeviceOperateCallback cb) { if (findJob(kBlockJob)) { if (cb) cb(false, lastError); return; } CallbackProxy *proxy = cb ? new CallbackProxy(cb) : nullptr; UDisksFilesystem_autoptr fs = getFilesystemHandler(); if (!fs) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); if (proxy) { proxy->cb(true, lastError); delete proxy; } return; } QStringList mpts = getProperty(Property::kFileSystemMountPoint).toStringList(); if (mpts.empty()) { lastError = Utils::genOperateErrorInfo(DeviceError::kUDisksErrorNotMounted); if (proxy) { proxy->cb(true, lastError); delete proxy; } return; } // start unmount device async // construct the options GVariant *gopts = Utils::castFromQVariantMap(opts); udisks_filesystem_call_unmount(fs, gopts, nullptr, unmountAsyncCallback, proxy); } bool DBlockDevicePrivate::rename(const QString &newName, const QVariantMap &opts) { warningIfNotInMain(); if (findJob(kBlockJob)) return false; UDisksFilesystem_autoptr fs = getFilesystemHandler(); if (!fs) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); return false; } QStringList mpts = getProperty(Property::kFileSystemMountPoint).toStringList(); if (!mpts.empty()) { lastError = Utils::genOperateErrorInfo(DeviceError::kUDisksErrorAlreadyMounted); return false; } // construct the options GVariant *gopts = Utils::castFromQVariantMap(opts); std::string name = newName.toStdString(); const char *label = name.data(); GError *err = nullptr; bool result = udisks_filesystem_call_set_label_sync(fs, label, gopts, nullptr, &err); if (result) return true; handleErrorAndRelease(err); return false; } void DBlockDevicePrivate::renameAsync(const QString &newName, const QVariantMap &opts, DeviceOperateCallback cb) { if (findJob(kBlockJob)) { if (cb) cb(false, lastError); return; } CallbackProxy *proxy = cb ? new CallbackProxy(cb) : nullptr; UDisksFilesystem_autoptr fs = getFilesystemHandler(); if (!fs) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); if (proxy) { proxy->cb(false, lastError); delete proxy; } return; } QStringList mpts = getProperty(Property::kFileSystemMountPoint).toStringList(); if (!mpts.empty()) { lastError = Utils::genOperateErrorInfo(DeviceError::kUDisksErrorAlreadyMounted); if (proxy) { proxy->cb(false, lastError); delete proxy; } return; } GVariant *gopts = Utils::castFromQVariantMap(opts); std::string name = newName.toStdString(); const char *label = name.data(); udisks_filesystem_call_set_label(fs, label, gopts, nullptr, renameAsyncCallback, proxy); } bool DBlockDevicePrivate::eject(const QVariantMap &opts) { warningIfNotInMain(); if (findJob(kBlockJob)) return false; UDisksDrive_autoptr drv = getDriveHandler(); if (!drv) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNoDriver); return false; } bool ejectable = q->getProperty(Property::kDriveEjectable).toBool(); if (!ejectable) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotEjectable); return false; } // construct the options GVariant *gopts = Utils::castFromQVariantMap(opts); GError *err = nullptr; bool result = udisks_drive_call_eject_sync(drv, gopts, nullptr, &err); if (result) return true; handleErrorAndRelease(err); return false; } void DBlockDevicePrivate::ejectAsync(const QVariantMap &opts, DeviceOperateCallback cb) { if (findJob(kBlockJob)) { if (cb) cb(false, lastError); return; } CallbackProxy *proxy = cb ? new CallbackProxy(cb) : nullptr; bool ejectable = q->getProperty(Property::kDriveEjectable).toBool(); if (!ejectable) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotEjectable); if (proxy) { proxy->cb(false, lastError); delete proxy; } return; } UDisksDrive_autoptr drv = getDriveHandler(); if (!drv) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNoDriver); if (proxy) { proxy->cb(false, lastError); delete proxy; } return; } GVariant *gopts = Utils::castFromQVariantMap(opts); udisks_drive_call_eject(drv, gopts, nullptr, ejectAsyncCallback, proxy); } bool DBlockDevicePrivate::powerOff(const QVariantMap &opts) { warningIfNotInMain(); if (findJob(kBlockJob) || findJob(kDriveJob)) return false; UDisksDrive_autoptr drv = getDriveHandler(); if (!drv) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNoDriver); return false; } // construct the options GVariant *gopts = Utils::castFromQVariantMap(opts); GError *err = nullptr; bool result = udisks_drive_call_power_off_sync(drv, gopts, nullptr, &err); if (result) return true; handleErrorAndRelease(err); return false; } void DBlockDevicePrivate::powerOffAsync(const QVariantMap &opts, DeviceOperateCallback cb) { if (findJob(kBlockJob) || findJob(kDriveJob)) { if (cb) cb(false, lastError); return; } CallbackProxy *proxy = cb ? new CallbackProxy(cb) : nullptr; UDisksDrive_autoptr drv = getDriveHandler(); if (!drv) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNoDriver); if (proxy) { proxy->cb(false, lastError); delete proxy; } return; } GVariant *gopts = Utils::castFromQVariantMap(opts); udisks_drive_call_power_off(drv, gopts, nullptr, powerOffAsyncCallback, proxy); } bool DBlockDevicePrivate::lock(const QVariantMap &opts) { warningIfNotInMain(); if (findJob(kBlockJob)) return false; UDisksEncrypted_autoptr encrypted = getEncryptedHandler(); if (!encrypted) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotEncryptable); return false; } GError *err = nullptr; GVariant *gopts = Utils::castFromQVariantMap(opts); bool result = udisks_encrypted_call_lock_sync(encrypted, gopts, nullptr, &err); if (result) return true; handleErrorAndRelease(err); return false; } void DBlockDevicePrivate::lockAsync(const QVariantMap &opts, DeviceOperateCallback cb) { if (findJob(kBlockJob)) { if (cb) cb(false, lastError); return; } CallbackProxy *proxy = cb ? new CallbackProxy(cb) : nullptr; UDisksEncrypted_autoptr encrypted = getEncryptedHandler(); if (!encrypted) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotEncryptable); if (proxy) { proxy->cb(false, lastError); delete proxy; } return; } GVariant *gopts = Utils::castFromQVariantMap(opts); udisks_encrypted_call_lock(encrypted, gopts, nullptr, lockAsyncCallback, proxy); } bool DBlockDevicePrivate::unlock(const QString &passwd, QString &clearTextDev, const QVariantMap &opts) { warningIfNotInMain(); if (findJob(kBlockJob)) return false; UDisksEncrypted_autoptr encrypted = getEncryptedHandler(); if (!encrypted) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotEncryptable); return false; } GError *err = nullptr; GVariant *gopts = Utils::castFromQVariantMap(opts); char *clearDev = nullptr; bool result = udisks_encrypted_call_unlock_sync(encrypted, passwd.toStdString().c_str(), gopts, &clearDev, nullptr, &err); if (result) { clearTextDev = QString(clearDev); g_free(clearDev); return true; } handleErrorAndRelease(err); return false; } void DBlockDevicePrivate::unlockAsync(const QString &passwd, const QVariantMap &opts, DeviceOperateCallbackWithMessage cb) { if (findJob(kBlockJob)) { if (cb) cb(false, lastError, ""); return; } CallbackProxy *proxy = cb ? new CallbackProxy(cb) : nullptr; UDisksEncrypted_autoptr encrypted = getEncryptedHandler(); if (!encrypted) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotEncryptable); if (proxy) { proxy->cbWithInfo(false, lastError, QString()); delete proxy; } return; } GVariant *gopts = Utils::castFromQVariantMap(opts); udisks_encrypted_call_unlock(encrypted, passwd.toStdString().c_str(), gopts, nullptr, unlockAsyncCallback, proxy); } bool DBlockDevicePrivate::rescan(const QVariantMap &opts) { warningIfNotInMain(); if (findJob(kBlockJob)) return false; UDisksBlock_autoptr blk = getBlockHandler(); GError_autoptr err = nullptr; if (blk) { bool ret = udisks_block_call_rescan_sync(blk, Utils::castFromQVariantMap(opts), nullptr, &err); if (err) { qWarning() << "error while rescaning: " << err->message; return false; } return ret; } return false; } void DBlockDevicePrivate::rescanAsync(const QVariantMap &opts, DeviceOperateCallback cb) { if (findJob(kBlockJob)) { if (cb) cb(false, lastError); return; } CallbackProxy *proxy = cb ? new CallbackProxy(cb) : nullptr; UDisksBlock_autoptr blk = getBlockHandler(); if (blk) { udisks_block_call_rescan(blk, Utils::castFromQVariantMap(opts), nullptr, rescanAsyncCallback, proxy); } else { if (cb) cb(false, Utils::genOperateErrorInfo(DeviceError::kUserErrorNoBlock)); qWarning() << "cannot get block handler"; } } inline void DBlockDevicePrivate::handleErrorAndRelease(GError *err) { if (err) { lastError.code = Utils::castFromGError(err); lastError.message = err->message; g_error_free(err); } } QString DBlockDevicePrivate::mountPoint() const { auto mpts = q->getProperty(Property::kFileSystemMountPoint).toStringList(); return mpts.isEmpty() ? QString() : mpts.first(); } QString DBlockDevicePrivate::fileSystem() const { return getProperty(Property::kBlockIDType).toString(); } qint64 DBlockDevicePrivate::sizeTotal() const { return q->getProperty(Property::kBlockSize).toLongLong(); } qint64 DBlockDevicePrivate::sizeUsage() const { return sizeTotal() - sizeFree(); } qint64 DBlockDevicePrivate::sizeFree() const { auto mpts = q->getProperty(Property::kFileSystemMountPoint).toStringList(); if (mpts.isEmpty()) { // lastError = MountError::NotMounted; qInfo() << __FUNCTION__ << "NOT MOUNTED: " << blkObjPath; return 0; } auto mpt = mpts.first(); struct statvfs fsInfo; int ok = statvfs(mpt.toStdString().c_str(), &fsInfo); if (ok == 0) { const quint64 blksize = quint64(fsInfo.f_bsize); return static_cast(fsInfo.f_bavail * blksize); } QStorageInfo info(mpt); return info.bytesAvailable(); } DeviceType DBlockDevicePrivate::deviceType() const { return DeviceType::kBlockDevice; } QVariant DBlockDevicePrivate::getProperty(Property name) const { if (name > Property::kBlockProperty && name < Property::kBlockPropertyEND) return getBlockProperty(name); else if (name > Property::kDriveProperty && name < Property::kDrivePropertyEND) return getDriveProperty(name); else if (name > Property::kFileSystemProperty && name < Property::kFileSystemPropertyEND) return getFileSystemProperty(name); else if (name > Property::kPartitionProperty && name < Property::kPartitionPropertyEND) return getPartitionProperty(name); else if (name > Property::kEncryptedProperty && name < Property::kEncryptedPropertyEnd) return getEncryptedProperty(name); // Q_ASSERT_X(0, __FUNCTION__, "the property is not supported for block device"); return QVariant(); } QString DBlockDevicePrivate::displayName() const { return getProperty(Property::kBlockIDLabel).toString(); } QVariant DBlockDevicePrivate::getBlockProperty(Property name) const { UDisksBlock_autoptr blk = getBlockHandler(); if (!blk) { // qDebug() << __FUNCTION__ << "NO BLOCK: " << blkObjPath; lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNoBlock); return QVariant(); } // make sure we can safely get the properties in cross-thread cases: so we use DUP rather than GET when DUP can be used. // but we shall release the objects by calling g_free for char * or g_strfreev for char ** funcs. switch (name) { case Property::kBlockConfiguration: { GVariant *config = udisks_block_dup_configuration(blk); // a(sa{sv}) if (!config) return QVariantMap(); QVariantMap result; GVariantIter iter; g_variant_iter_init(&iter, config); gchar *key; GVariant *value; while (g_variant_iter_next(&iter, "(s@a{sv})", &key, &value)) { // @ means the followed type should be handled as a variant QVariantMap valueMap; GVariantIter valueIter; g_variant_iter_init(&valueIter, value); gchar *valueKey; GVariant *valueValue; while (g_variant_iter_next(&valueIter, "{sv}", &valueKey, &valueValue)) { valueMap.insert(QString::fromUtf8(valueKey), Utils::gvariantToQVariant(valueValue)); g_free(valueKey); g_variant_unref(valueValue); } result.insert(QString::fromUtf8(key), valueMap); g_free(key); g_variant_unref(value); } g_variant_unref(config); return result; } case Property::kBlockUserspaceMountOptions: { char **opts = udisks_block_dup_userspace_mount_options(blk); return Utils::gcharvToQStringList(opts); } case Property::kBlockCryptoBackingDevice: { char *tmp = udisks_block_dup_crypto_backing_device(blk); return Utils::gcharToQString(tmp); } case Property::kBlockDevice: { char *tmp = udisks_block_dup_device(blk); return Utils::gcharToQString(tmp); } case Property::kBlockDrive: { char *tmp = udisks_block_dup_drive(blk); return Utils::gcharToQString(tmp); } case Property::kBlockIDLabel: { char *tmp = udisks_block_dup_id_label(blk); return Utils::gcharToQString(tmp); } case Property::kBlockIDType: { char *tmp = udisks_block_dup_id_type(blk); return Utils::gcharToQString(tmp); } case Property::kBlockIDUsage: { char *tmp = udisks_block_dup_id_usage(blk); QString ret = Utils::gcharToQString(tmp); return ret.toLongLong(); } case Property::kBlockIDUUID: { char *tmp = udisks_block_dup_id_uuid(blk); return Utils::gcharToQString(tmp); } case Property::kBlockIDVersion: { char *tmp = udisks_block_dup_id_version(blk); return Utils::gcharToQString(tmp); } case Property::kBlockDeviceNumber: return quint64(udisks_block_get_device_number(blk)); case Property::kBlockPreferredDevice: { char *tmp = udisks_block_dup_preferred_device(blk); return Utils::gcharToQString(tmp); } case Property::kBlockID: { char *tmp = udisks_block_dup_id(blk); return Utils::gcharToQString(tmp); } case Property::kBlockSize: return quint64(udisks_block_get_size(blk)); case Property::kBlockReadOnly: return bool(udisks_block_get_read_only(blk)); case Property::kBlockSymlinks: { char **ret = udisks_block_dup_symlinks(blk); return Utils::gcharvToQStringList(ret); } case Property::kBlockHintPartitionable: return bool(udisks_block_get_hint_partitionable(blk)); case Property::kBlockHintSystem: return bool(udisks_block_get_hint_system(blk)); case Property::kBlockHintIgnore: return bool(udisks_block_get_hint_ignore(blk)); case Property::kBlockHintAuto: return bool(udisks_block_get_hint_auto(blk)); case Property::kBlockHintName: { char *tmp = udisks_block_dup_hint_name(blk); return Utils::gcharToQString(tmp); } case Property::kBlockHintIconName: { char *tmp = udisks_block_dup_hint_icon_name(blk); return Utils::gcharToQString(tmp); } case Property::kBlockHintSymbolicIconName: { char *tmp = udisks_block_dup_hint_symbolic_icon_name(blk); return Utils::gcharToQString(tmp); } case Property::kBlockMdRaid: { char *tmp = udisks_block_dup_mdraid(blk); return Utils::gcharToQString(tmp); } case Property::kBlockMdRaidMember: { char *tmp = udisks_block_dup_mdraid_member(blk); return Utils::gcharToQString(tmp); } default: Q_ASSERT_X(0, __FUNCTION__, "not valid for block device"); return "not valid for block device"; } } QVariant DBlockDevicePrivate::getDriveProperty(Property name) const { UDisksDrive_autoptr drv = getDriveHandler(); if (!drv) { // qDebug() << __FUNCTION__ << "NO DRIVE: " << blkObjPath; lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNoDriver); return ""; } switch (name) { case Property::kDriveConnectionBus: { char *tmp = udisks_drive_dup_connection_bus(drv); return Utils::gcharToQString(tmp); } case Property::kDriveRemovable: return bool(udisks_drive_get_removable(drv)); case Property::kDriveEjectable: return bool(udisks_drive_get_ejectable(drv)); case Property::kDriveSeat: { char *tmp = udisks_drive_dup_seat(drv); return Utils::gcharToQString(tmp); } case Property::kDriveMedia: { char *tmp = udisks_drive_dup_media(drv); return Utils::gcharToQString(tmp); } case Property::kDriveMediaCompatibility: { char **ret = udisks_drive_dup_media_compatibility(drv); return Utils::gcharvToQStringList(ret); } case Property::kDriveMediaRemovable: return bool(udisks_drive_get_media_removable(drv)); case Property::kDriveMediaAvailable: return bool(udisks_drive_get_media_available(drv)); case Property::kDriveMediaChangeDetected: return bool(udisks_drive_get_media_change_detected(drv)); case Property::kDriveTimeDetected: return quint64(udisks_drive_get_time_detected(drv)); case Property::kDriveTimeMediaDetected: return quint64(udisks_drive_get_time_media_detected(drv)); case Property::kDriveSize: return quint64(udisks_drive_get_size(drv)); case Property::kDriveOptical: return bool(udisks_drive_get_optical(drv)); case Property::kDriveOpticalBlank: return bool(udisks_drive_get_optical_blank(drv)); case Property::kDriveOpticalNumTracks: return uint(udisks_drive_get_optical_num_tracks(drv)); case Property::kDriveOpticalNumAudioTracks: return uint(udisks_drive_get_optical_num_audio_tracks(drv)); case Property::kDriveOpticalNumDataTracks: return uint(udisks_drive_get_optical_num_data_tracks(drv)); case Property::kDriveOpticalNumSessions: return uint(udisks_drive_get_optical_num_sessions(drv)); case Property::kDriveModel: { char *tmp = udisks_drive_dup_model(drv); return Utils::gcharToQString(tmp); } case Property::kDriveRevision: { char *tmp = udisks_drive_dup_revision(drv); return Utils::gcharToQString(tmp); } case Property::kDriveRotationRate: return int(udisks_drive_get_rotation_rate(drv)); case Property::kDriveSerial: { char *tmp = udisks_drive_dup_serial(drv); return Utils::gcharToQString(tmp); } case Property::kDriveVender: { char *tmp = udisks_drive_dup_vendor(drv); return Utils::gcharToQString(tmp); } case Property::kDriveWWN: { char *tmp = udisks_drive_dup_wwn(drv); return Utils::gcharToQString(tmp); } case Property::kDriveSortKey: { char *tmp = udisks_drive_dup_sort_key(drv); return Utils::gcharToQString(tmp); } case Property::kDriveConfiguration: // return QVariant(udisks_drive_dup_configuration(drv)); return ""; case Property::kDriveID: { char *tmp = udisks_drive_dup_id(drv); return Utils::gcharToQString(tmp); } case Property::kDriveCanPowerOff: return bool(udisks_drive_get_can_power_off(drv)); case Property::kDriveSiblingID: { char *tmp = udisks_drive_dup_sibling_id(drv); return Utils::gcharToQString(tmp); } default: Q_ASSERT_X(0, __FUNCTION__, "not valid property for this device"); return ""; } } QVariant DBlockDevicePrivate::getFileSystemProperty(Property name) const { UDisksFilesystem_autoptr fs = getFilesystemHandler(); if (!fs) { // qDebug() << __FUNCTION__ << "NO FILESYSTEM: " << blkObjPath; lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); return QVariant(); } switch (name) { case Property::kFileSystemMountPoint: { char **ret = udisks_filesystem_dup_mount_points(fs); QStringList mpts = Utils::gcharvToQStringList(ret); if (mpts.count() > 1) { // make the first mountpoint stays on first position. QString dev = getBlockProperty(Property::kBlockDevice).toString(); auto firstMpt = findFirstMountPoint(dev); if (mpts.contains(firstMpt) && !firstMpt.isEmpty()) { mpts.removeAll(firstMpt); mpts.prepend(firstMpt); } } return mpts; } default: Q_ASSERT_X(0, __FUNCTION__, "the property is not supported for block device"); return ""; } } QVariant DBlockDevicePrivate::getPartitionProperty(Property name) const { UDisksPartition_autoptr partition = getPartitionHandler(); if (!partition) { // qDebug() << __FUNCTION__ << "NO PARTITION: " << blkObjPath; lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNoPartition); return QVariant(); } switch (name) { case Property::kPartitionNumber: return uint(udisks_partition_get_number(partition)); case Property::kPartitionType: { char *tmp = udisks_partition_dup_type_(partition); return Utils::gcharToQString(tmp); } case Property::kPartitionOffset: return quint64(udisks_partition_get_offset(partition)); case Property::kPartitionSize: return quint64(udisks_partition_get_size(partition)); case Property::kPartitionFlags: return quint64(udisks_partition_get_flags(partition)); case Property::kPartitionName: { char *tmp = udisks_partition_dup_name(partition); return Utils::gcharToQString(tmp); } case Property::kPartitionUUID: { char *tmp = udisks_partition_dup_uuid(partition); return Utils::gcharToQString(tmp); } case Property::kPartitionTable: { char *tmp = udisks_partition_dup_table(partition); return Utils::gcharToQString(tmp); } case Property::kPartitionIsContainer: return bool(udisks_partition_get_is_container(partition)); case Property::kPartitionIsContained: return bool(udisks_partition_get_is_contained(partition)); default: Q_ASSERT_X(0, __FUNCTION__, "the property is not supported for block device"); return ""; } } QVariant DBlockDevicePrivate::getEncryptedProperty(Property name) const { UDisksEncrypted_autoptr encrypted = getEncryptedHandler(); if (!encrypted) { // qDebug() << __FUNCTION__ << "NOT ENCRYPTED: " << blkObjPath; lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotEncryptable); return QVariant(); } switch (name) { case Property::kEncryptedChildConfiguration: return Utils::castFromGVariant(udisks_encrypted_get_child_configuration(encrypted)); case Property::kEncryptedCleartextDevice: return Utils::gcharToQString(udisks_encrypted_dup_cleartext_device(encrypted)); case Property::kEncryptedHintEncryptionType: return Utils::gcharToQString(udisks_encrypted_dup_hint_encryption_type(encrypted)); case Property::kEncryptedMetadataSize: return quint64(udisks_encrypted_get_metadata_size(encrypted)); default: Q_ASSERT_X(0, __FUNCTION__, "the property is not supported for block device"); return ""; } } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/dblockmonitor.cpp000066400000000000000000000405351523477242100247370ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include "private/dblockdevice_p.h" #include "private/dblockmonitor_p.h" #include #include #include #include #include #include DFM_MOUNT_USE_NS #define UDISKS_SERVICE_NAME "org.freedesktop.UDisks2" #define UDISKS_BLOCK_PATH_PREFIX "/org/freedesktop/UDisks2/block_devices/" #define UDISKS_DRIVE_PATH_PREFIX "/org/freedesktop/UDisks2/drives/" #define UDISKS_BLOCK_IFACE_FILESYSTEM "org.freedesktop.UDisks2.Filesystem" #define UDISKS_BLOCK_IFACE_PARTITIONTABLE "org.freedesktop.UDisks2.PartitionTable" QMap> DBlockMonitorPrivate::blksOfDrive = {}; DBlockMonitorPrivate::DBlockMonitorPrivate(DBlockMonitor *qq) : DDeviceMonitorPrivate(qq) { GError *err = nullptr; client = udisks_client_new_sync(nullptr, &err); if (err) { qCritical() << "init udisks client failed. " << err->message; g_error_free(err); } watcher = new QDBusServiceWatcher(UDISKS_SERVICE_NAME, QDBusConnection::systemBus(), QDBusServiceWatcher::WatchForOwnerChange); initDevices(); } DBlockMonitorPrivate::~DBlockMonitorPrivate() { qDebug() << "block monitor release..."; if (client) { g_object_unref(client); client = nullptr; } if (watcher) { watcher->deleteLater(); watcher = nullptr; } } bool DBlockMonitorPrivate::startMonitor() { if (!startDeviceMonitor()) return false; QObject::connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, watcher, [=](const QString &serviceName, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner) if (serviceName == UDISKS_SERVICE_NAME) { if (newOwner.isEmpty()) { qDebug() << "The udisks service has exited and stopped monitoring device signals."; stopDeviceMonitor(); } else { qDebug() << "The udisks service has started and begun monitoring device signals."; startDeviceMonitor(); } } }); qDebug() << "block monitor start"; return true; } bool DBlockMonitorPrivate::stopMonitor() { watcher->disconnect(); if (!stopDeviceMonitor()) return false; qDebug() << "block monitor stop"; return true; } bool DBlockMonitorPrivate::startDeviceMonitor() { if (!client) { qCritical() << "client is not valid"; return false; } // the mng is owned by client, do not free it manually GDBusObjectManager *dbusMng = udisks_client_get_object_manager(client); if (!dbusMng) { qCritical() << "start monitor block failed: cannot get dbus monitor"; return false; } auto handler = g_signal_connect(dbusMng, OBJECT_ADDED, G_CALLBACK(&DBlockMonitorPrivate::onObjectAdded), q); connections.insert(OBJECT_ADDED, handler); handler = g_signal_connect(dbusMng, OBJECT_REMOVED, G_CALLBACK(&DBlockMonitorPrivate::onObjectRemoved), q); connections.insert(OBJECT_REMOVED, handler); handler = g_signal_connect(dbusMng, PROPERTY_CHANGED, G_CALLBACK(&DBlockMonitorPrivate::onPropertyChanged), q); connections.insert(PROPERTY_CHANGED, handler); handler = g_signal_connect(dbusMng, INTERFACE_ADDED, G_CALLBACK(&DBlockMonitorPrivate::onInterfaceAdded), q); connections.insert(INTERFACE_ADDED, handler); handler = g_signal_connect(dbusMng, INTERFACE_REMOVED, G_CALLBACK(&DBlockMonitorPrivate::onInterfaceRemoved), q); connections.insert(INTERFACE_REMOVED, handler); return true; } bool DBlockMonitorPrivate::stopDeviceMonitor() { if (!client) { qDebug() << "client is not valid"; return false; } GDBusObjectManager *dbusMng = udisks_client_get_object_manager(client); for (auto iter = connections.cbegin(); iter != connections.cend(); iter++) g_signal_handler_disconnect(dbusMng, iter.value()); connections.clear(); return true; } DeviceType DBlockMonitorPrivate::monitorObjectType() const { return DeviceType::kBlockDevice; } QStringList DBlockMonitorPrivate::getDevices() { Q_ASSERT(client); UDisksManager *mng = udisks_client_get_manager(client); Q_ASSERT(mng); GVariant *gopts = Utils::castFromQVariantMap({}); char **results = nullptr; GError *err = nullptr; bool ret = udisks_manager_call_get_block_devices_sync(mng, gopts, &results, nullptr, &err); if (ret) { QStringList blks; int next = 0; while (results && results[next]) { blks << QString(results[next]); next++; } if (results) g_strfreev(results); return blks; } else { // TODO: ERROR HANDLE if (err) g_error_free(err); } return QStringList(); } QSharedPointer DBlockMonitorPrivate::createDeviceById(const QString &id) { auto blk = new DBlockDevice(client, id, nullptr); // for a block device, there must have a block node in dbus, otherwise treat it as a invalid object if (blk->hasBlock()) { QSharedPointer ret; ret.reset(blk); return ret; } else { delete blk; blk = nullptr; return QSharedPointer(nullptr); } } QStringList DBlockMonitorPrivate::resolveDevice(const QVariantMap &devspec, const QVariantMap &opts) { Q_ASSERT(client); char **results = nullptr; GError *err = nullptr; UDisksManager *mng = udisks_client_get_manager(client); GVariant *gDevSpec = Utils::castFromQVariantMap(devspec); GVariant *GOpts = Utils::castFromQVariantMap(opts); bool ok = udisks_manager_call_resolve_device_sync(mng, gDevSpec, GOpts, &results, nullptr, &err); if (ok) { QStringList ret; int next = 0; while (results && results[next]) { ret << QString(results[next]); next++; } if (results) g_strfreev(results); return ret; } else { // TODO: ERROR HANDLE if (err) g_error_free(err); } return {}; } QStringList DBlockMonitorPrivate::resolveDeviceNode(const QString &node, const QVariantMap &opts) { if (node.isEmpty()) return QStringList(); QVariantMap devSpec { { "path", node } }; return resolveDevice(devSpec, opts); } QStringList DBlockMonitorPrivate::resolveDeviceOfDrive(const QString &drvObjPath) { if (q->status() != MonitorStatus::kMonitoring) initDevices(); return blksOfDrive.value(drvObjPath).values(); } void DBlockMonitorPrivate::onObjectAdded(GDBusObjectManager *mng, GDBusObject *obj, gpointer userData) { Q_UNUSED(mng); DBlockMonitor *q = static_cast(userData); Q_ASSERT(q); UDisksObject *udisksObj = UDISKS_OBJECT(obj); if (!udisksObj) return; QString objPath = g_dbus_object_get_object_path(obj); UDisksDrive *drive = udisks_object_peek_drive(udisksObj); UDisksBlock *block = udisks_object_peek_block(udisksObj); UDisksFilesystem *fileSystem = udisks_object_peek_filesystem(udisksObj); UDisksPartition *partition = udisks_object_peek_partition(udisksObj); UDisksEncrypted *encrypted = udisks_object_peek_encrypted(udisksObj); if (drive) { qDebug() << "drive added: " << objPath; Q_EMIT q->driveAdded(objPath); blksOfDrive.insert(objPath, {}); } if (block) { qDebug() << "block added: " << objPath; Q_EMIT q->deviceAdded(objPath); QString drvPath(udisks_block_get_drive(block)); blksOfDrive[drvPath].insert(objPath); g_autofree char *encryptedShell = udisks_block_dup_crypto_backing_device(block); if (strcmp(encryptedShell, "/") != 0) { Q_EMIT q->blockUnlocked(encryptedShell, objPath); qDebug() << "unlocked: " << encryptedShell << "-->" << objPath; } } if (fileSystem) { qDebug() << "filesystem added: " << objPath << ", filesystem: " << fileSystem; Q_EMIT q->fileSystemAdded(objPath); } if (partition) { qDebug() << "partition added: " << objPath; } if (encrypted) { qDebug() << "encrypted added: " << objPath; } } void DBlockMonitorPrivate::onObjectRemoved(GDBusObjectManager *mng, GDBusObject *obj, gpointer userData) { Q_UNUSED(mng); DBlockMonitor *q = static_cast(userData); Q_ASSERT(q); UDisksObject *udisksObj = UDISKS_OBJECT(obj); if (!udisksObj) return; QString objPath = g_dbus_object_get_object_path(obj); UDisksDrive *drive = udisks_object_peek_drive(udisksObj); UDisksBlock *block = udisks_object_peek_block(udisksObj); UDisksFilesystem *fileSystem = udisks_object_peek_filesystem(udisksObj); UDisksPartition *partition = udisks_object_peek_partition(udisksObj); UDisksEncrypted *encrypted = udisks_object_peek_encrypted(udisksObj); if (drive) { qDebug() << "drive removed: " << objPath; Q_EMIT q->driveRemoved(objPath); blksOfDrive.remove(objPath); } if (block) { qDebug() << "block removed: " << objPath; Q_EMIT q->deviceRemoved(objPath); QString drvPath(udisks_block_get_drive(block)); if (blksOfDrive.contains(QString(drvPath))) blksOfDrive[drvPath].remove(objPath); g_autofree char *encryptedShell = udisks_block_dup_crypto_backing_device(block); if (strcmp(encryptedShell, "/") != 0) { Q_EMIT q->blockLocked(encryptedShell); qDebug() << "locked: " << objPath << "removed, " << encryptedShell << "locked"; } } if (fileSystem) { qDebug() << "filesystem removed: " << objPath; Q_EMIT q->fileSystemRemoved(objPath); } if (partition) { qDebug() << "partition removed: " << objPath; } if (encrypted) { qDebug() << "encrypted removed: " << objPath; } } void DBlockMonitorPrivate::onPropertyChanged(GDBusObjectManagerClient *mngClient, GDBusObjectProxy *dbusObjProxy, GDBusProxy *dbusProxy, GVariant *property, const gchar *const invalidProperty, gpointer userData) { Q_UNUSED(mngClient); Q_UNUSED(dbusObjProxy); Q_UNUSED(invalidProperty); DBlockMonitor *q = static_cast(userData); if (!q) return; // get the changed object path: "/org/freedesktop/UDisks2/block_devices/sdb1" QString objPath = dbusProxy ? QString(g_dbus_proxy_get_object_path(dbusProxy)) : QString(); QString ifaceName = dbusProxy ? QString(g_dbus_proxy_get_interface_name(dbusProxy)) : QString(); bool isBlockChanged = objPath.startsWith(UDISKS_BLOCK_PATH_PREFIX); bool isDriveChanged = objPath.startsWith(UDISKS_DRIVE_PATH_PREFIX); if (!isBlockChanged && !isDriveChanged) return; QMap changes; QVariant val = Utils::castFromGVariant(property); if (val.type() == QVariant::Map) { QVariantMap vmap = val.toMap(); for (auto iter = vmap.cbegin(); iter != vmap.cend(); ++iter) { auto key = iter.key(); auto val = iter.value(); Property type = Utils::getPropertyByName(key, ifaceName); if (type == Property::kNotInit) { qDebug() << "\tproperty: " << key << "has no mapped type, but value is" << val; continue; } changes.insert(type, val); } } else { qWarning() << "property is not dict" << val; return; } if (changes.isEmpty()) return; if (changes.contains(Property::kFileSystemMountPoint)) { auto mpts = changes.value(Property::kFileSystemMountPoint).toStringList(); // make a short delay to emit (un)mounted signal, in case that at the moment device handler is created, the property is not ready. QTimer::singleShot(100, q, [=] { Q_EMIT mpts.isEmpty() ? q->mountRemoved(objPath) : q->mountAdded(objPath, mpts.first()); }); } if (isBlockChanged) { Q_EMIT q->propertyChanged(objPath, changes); } else { QSet blks = blksOfDrive.value(objPath); for (const auto &blk : blks) Q_EMIT q->propertyChanged(blk, changes); } } void DBlockMonitorPrivate::onInterfaceAdded(GDBusObjectManager *mng, GDBusObject *obj, GDBusInterface *iface, gpointer userData) { DBlockMonitor *q = static_cast(userData); Q_ASSERT(q); QString objPath = g_dbus_object_get_object_path(obj); if (!objPath.startsWith(UDISKS_BLOCK_PATH_PREFIX)) return; auto info = g_dbus_interface_get_info(iface); if (strcmp(info->name, UDISKS_BLOCK_IFACE_FILESYSTEM) == 0) { qDebug() << "filesystem added: " << objPath; Q_EMIT q->fileSystemAdded(objPath); } /*else if (strcmp(info->name, UDISKS_BLOCK_IFACE_PARTITIONTABLE) == 0) { }*/ } void DBlockMonitorPrivate::onInterfaceRemoved(GDBusObjectManager *mng, GDBusObject *obj, GDBusInterface *iface, gpointer userData) { DBlockMonitor *q = static_cast(userData); Q_ASSERT(q); QString objPath = g_dbus_object_get_object_path(obj); if (!objPath.startsWith(UDISKS_BLOCK_PATH_PREFIX)) return; auto info = g_dbus_interface_get_info(iface); if (strcmp(info->name, UDISKS_BLOCK_IFACE_FILESYSTEM) == 0) { qDebug() << "filesystem removed: " << objPath; Q_EMIT q->fileSystemRemoved(objPath); } /*else if (strcmp(info->name, UDISKS_BLOCK_IFACE_PARTITIONTABLE) == 0) { }*/ } void DBlockMonitorPrivate::initDevices() { blksOfDrive.clear(); auto lst = getDevices(); for (const auto &blk : lst) { std::string str = blk.toStdString(); UDisksObject *obj = udisks_client_peek_object(client, str.c_str()); if (!obj) continue; auto blkObj = udisks_object_peek_block(obj); if (!blkObj) continue; const char *drive = udisks_block_get_drive(blkObj); if (drive) blksOfDrive[QString(drive)].insert(blk); } } DBlockMonitor::DBlockMonitor(QObject *parent) : DDeviceMonitor(new DBlockMonitorPrivate(this), parent) { auto dp = Utils::castClassFromTo(d.data()); if (!dp) { qCritical() << "private pointer not valid" << __PRETTY_FUNCTION__; abort(); } registerStartMonitor(std::bind(&DBlockMonitorPrivate::startMonitor, dp)); registerStopMonitor(std::bind(&DBlockMonitorPrivate::stopMonitor, dp)); registerMonitorObjectType(std::bind(&DBlockMonitorPrivate::monitorObjectType, dp)); registerGetDevices(std::bind(&DBlockMonitorPrivate::getDevices, dp)); registerCreateDeviceById(std::bind(&DBlockMonitorPrivate::createDeviceById, dp, std::placeholders::_1)); } DBlockMonitor::~DBlockMonitor() { auto dp = Utils::castClassFromTo(d.data()); if (dp) dp->stopMonitor(); } /*! * \brief DBlockMonitor::resolveDevice * \param devspec: currently support keys: path, label and uuid * \param opts: currently unused in udisks2 * \return the associated device object paths */ QStringList DBlockMonitor::resolveDevice(const QVariantMap &devspec, const QVariantMap &opts) { auto dp = Utils::castClassFromTo(d.data()); return dp ? dp->resolveDevice(devspec, opts) : QStringList(); } QStringList DBlockMonitor::resolveDeviceNode(const QString &node, const QVariantMap &opts) { auto dp = Utils::castClassFromTo(d.data()); return dp ? dp->resolveDeviceNode(node, opts) : QStringList(); } /*! * \brief DBlockMonitor::resolveDeviceFromDrive * \param drvObjPath: object path for drive * \return the associated block object paths */ QStringList DBlockMonitor::resolveDeviceFromDrive(const QString &drvObjPath) { auto dp = Utils::castClassFromTo(d.data()); return dp ? dp->resolveDeviceOfDrive(drvObjPath) : QStringList(); } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/ddevicemanager.cpp000066400000000000000000000113031523477242100250160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2019 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "private/ddevicemanager_p.h" #include DFM_MOUNT_USE_NS DDeviceManagerPrivate::DDeviceManagerPrivate(DDeviceManager *qq) : q(qq) { } QSharedPointer DDeviceManagerPrivate::getRegisteredMonitor(DeviceType type) const { if (type == DeviceType::kAllDevice) { qWarning() << "DeviceType::AllDevice is not a specific type."; return nullptr; } return monitors.value(type, nullptr); } bool DDeviceManagerPrivate::startMonitor() { bool res = true; for (const auto &monitor : monitors) { auto type = monitor->monitorObjectType(); res &= monitor->startMonitor(); if (res) qDebug() << type << "started..."; else qWarning() << type << "failed to start..."; } return res; } bool DDeviceManagerPrivate::stopMonitor() { bool res = true; for (const auto &monitor : monitors) { auto type = monitor->monitorObjectType(); res &= monitor->stopMonitor(); if (res) qDebug() << type << "stopped..."; else qWarning() << type << "failed to stop..."; } return res; } QMap DDeviceManagerPrivate::devices(DeviceType type) { auto getAllDev = [this] { QMap ret; for (const auto &monitor : monitors) { if (monitor) ret.insert(monitor->monitorObjectType(), monitor->getDevices()); else lastError = MonitorError::kMonitorNotRegister; } return ret; }; auto getDevsOfType = [this](DeviceType type) { QMap ret; auto monitor = monitors.value(type); if (monitor) { ret.insert(type, monitor->getDevices()); return ret; } lastError = MonitorError::kMonitorNotRegister; return ret; }; switch (type) { case DeviceType::kAllDevice: return getAllDev(); case DeviceType::kNetDevice: // by intentionally case DeviceType::kProtocolDevice: case DeviceType::kBlockDevice: return getDevsOfType(type); } return {}; } DDeviceManager::DDeviceManager(QObject *parent) : QObject(parent), d(new DDeviceManagerPrivate(this)) { registerMonitor(this); registerMonitor(this); } DDeviceManager::~DDeviceManager() { } DDeviceManager *DDeviceManager::instance() { static DDeviceManager mng; return &mng; } QSharedPointer DDeviceManager::getRegisteredMonitor(DeviceType type) const { return d->getRegisteredMonitor(type); } bool DDeviceManager::startMonitorWatch() { return d->startMonitor(); } bool DDeviceManager::stopMonitorWatch() { return d->stopMonitor(); } MonitorError DDeviceManager::lastError() const { return d->lastError; } QMap DDeviceManager::devices(DeviceType type) { return d->devices(type); } template bool DDeviceManager::registerMonitor(ConstructArgs &&... args) { return d->registerMonitor(std::forward(args)...); } template bool DDeviceManagerPrivate::registerMonitor(ConstructArgs &&... args) { QSharedPointer monitor(new DFMSubMonitor(std::forward(args)...)); if (!monitor) return false; auto type = monitor->monitorObjectType(); if (monitors.contains(type)) { lastError = MonitorError::kMonitorAlreadyRegistered; return false; } monitors.insert(type, monitor); QObject::connect(monitor.data(), &DDeviceMonitor::deviceAdded, q, [type, this](const QString &devId) { Q_EMIT q->deviceAdded(devId, type); }); QObject::connect(monitor.data(), &DDeviceMonitor::deviceRemoved, q, [type, this](const QString &devId) { Q_EMIT q->deviceRemoved(devId, type); }); QObject::connect(monitor.data(), &DDeviceMonitor::mountAdded, q, [type, this](const QString &devId, const QString &mpt) { Q_EMIT q->mounted(devId, mpt, type); }); QObject::connect(monitor.data(), &DDeviceMonitor::mountRemoved, q, [type, this](const QString &devId) { Q_EMIT q->unmounted(devId, type); }); QObject::connect(monitor.data(), &DDeviceMonitor::propertyChanged, q, [type, this](const QString &devId, const QMap &changes) { Q_EMIT q->propertyChanged(devId, changes, type); }); return true; } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/dfm-mount.pc.in000066400000000000000000000005251523477242100242170ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/include Name: dfm-mount Description: a lib for simply mount devices Version: @PROJECT_VERSION@ Requires: udisks2 gio-2.0 libsecret-1 Libs: -L${libdir} -ldfm-mount Cflags: -I${includedir} -I${includedir}/dfm-mount linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/dprotocoldevice.cpp000066400000000000000000000544541523477242100252630ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "private/dprotocoldevice_p.h" #include "private/dnetworkmounter.h" #include #include #include #include #include #include #include DFM_MOUNT_USE_NS namespace { struct CallbackProxyWithData { CallbackProxyWithData() = delete; explicit CallbackProxyWithData(DeviceOperateCallback cb) : caller(cb) {} explicit CallbackProxyWithData(DeviceOperateCallbackWithMessage cb) : caller(cb) {} CallbackProxy caller; QPointer data; DProtocolDevicePrivate *d { nullptr }; }; } DProtocolDevice::DProtocolDevice(const QString &id, GVolumeMonitor *monitor, QObject *parent) : DDevice(new DProtocolDevicePrivate(id, monitor, this), parent) { auto dp = Utils::castClassFromTo(d.data()); if (!dp) { qCritical() << "private pointer not valid" << __PRETTY_FUNCTION__; abort(); } using namespace std; using namespace std::placeholders; registerPath(bind(&DProtocolDevicePrivate::path, dp)); registerMount(bind(&DProtocolDevicePrivate::mount, dp, _1)); registerMountAsync(bind(&DProtocolDevicePrivate::mountAsync, dp, _1, _2)); registerUnmount(bind(&DProtocolDevicePrivate::unmount, dp, _1)); registerUnmountAsync(bind(&DProtocolDevicePrivate::unmountAsync, dp, _1, _2)); registerRename(bind(&DProtocolDevicePrivate::rename, dp, _1)); registerRenameAsync(bind(&DProtocolDevicePrivate::renameAsync, dp, _1, _2, _3)); registerFileSystem(bind(&DProtocolDevicePrivate::fileSystem, dp)); registerSizeTotal(bind(&DProtocolDevicePrivate::sizeTotal, dp)); registerSizeUsage(bind(&DProtocolDevicePrivate::sizeUsage, dp)); registerSizeFree(bind(&DProtocolDevicePrivate::sizeFree, dp)); registerDeviceType(bind(&DProtocolDevicePrivate::deviceType, dp)); registerDisplayName(bind(&DProtocolDevicePrivate::displayName, dp)); auto mountPointOfClass = static_cast(&DProtocolDevicePrivate::mountPoint); registerMountPoint(bind(mountPointOfClass, dp)); } void DProtocolDevice::mounted(const QString &id) { auto dp = Utils::castClassFromTo(d.data()); if (!dp || id != dp->deviceId) return; if (dp->mountHandler) { // release the old one, keep the handler is the newest. QMutexLocker locker(&dp->mutexForMount); g_object_unref(dp->mountHandler); dp->mountHandler = nullptr; } auto mnts = g_volume_monitor_get_mounts(dp->volumeMonitor); while (mnts) { auto mnt = reinterpret_cast(mnts->data); g_autoptr(GFile) mntRoot = g_mount_get_root(mnt); if (mntRoot) { g_autofree char *curi = g_file_get_uri(mntRoot); if (QString(curi) == id) { QMutexLocker locker(&dp->mutexForMount); dp->mountHandler = reinterpret_cast(g_object_ref(mnt)); break; } } mnts = mnts->next; } g_list_free_full(mnts, g_object_unref); } void DProtocolDevice::unmounted(const QString &id) { auto dp = Utils::castClassFromTo(d.data()); if (!dp || id != dp->deviceId) return; QMutexLocker locker(&dp->mutexForMount); g_object_unref(dp->mountHandler); dp->mountHandler = nullptr; } DProtocolDevice::~DProtocolDevice() { } QStringList DProtocolDevice::deviceIcons() const { auto dp = Utils::castClassFromTo(d.data()); if (!dp) return {}; if (!dp->deviceIcons.isEmpty()) return dp->deviceIcons; GIcon_autoptr icon { nullptr }; if (dp->volumeHandler) { icon = g_volume_get_icon(dp->volumeHandler); } else if (dp->mountHandler) { icon = g_mount_get_icon(dp->mountHandler); } else { return {}; } if (icon) { g_autofree char *cname = g_icon_to_string(icon); if (cname) { // iconName: . GThemedIcon drive-removable-media drive-removable drive drive-removable-media-symbolic drive-removable-symbolic drive-symbolic QString iconNames(cname); iconNames.remove(". GThemedIcon"); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) auto iconLst = iconNames.split(" ", Qt::SkipEmptyParts); #else auto iconLst = iconNames.split(" ", QString::SkipEmptyParts); #endif dp->deviceIcons = iconLst; return iconLst; } } return {}; } /*! * \brief DProtocolDevice::mountNetworkDevice * \param address remote link address like 'smb://1.2.3.4/sharefolder/ * \param getPassInfo an passwd-asking dialog should be exec in this function, and return a struct object which contains the passwd * \param mountResult when mount finished, this function will be invoked. */ void DProtocolDevice::mountNetworkDevice(const QString &address, GetMountPassInfo getPassInfo, GetUserChoice getUserChoice, DeviceOperateCallbackWithMessage mountResult, int secs) { DNetworkMounter::mountNetworkDev(address, getPassInfo, getUserChoice, mountResult, secs); } bool DProtocolDevice::isMountByDaemon(const QString &address) { return DNetworkMounter::isMountByDae(address); } void DProtocolDevice::setOperatorTimeout(int msecs) { auto dp = Utils::castClassFromTo(d.data()); if (dp) dp->timeout = msecs; } DProtocolDevicePrivate::DProtocolDevicePrivate(const QString &id, GVolumeMonitor *monitor, DProtocolDevice *qq) : DDevicePrivate(qq), deviceId(id), volumeMonitor(monitor) { auto vols = g_volume_monitor_get_volumes(monitor); auto vols_head = vols; // 保存链表头指针 while (vols) { auto vol = reinterpret_cast(vols->data); g_autoptr(GFile) volRoot = g_volume_get_activation_root(vol); if (volRoot) { g_autofree char *curi = g_file_get_uri(volRoot); if (QString(curi) == id) { this->volumeHandler = reinterpret_cast(g_object_ref(vol)); break; } } vols = vols->next; } g_list_free_full(vols_head, g_object_unref); auto mnts = g_volume_monitor_get_mounts(monitor); auto mnts_head = mnts; // 保存链表头指针 while (mnts) { auto mnt = reinterpret_cast(mnts->data); g_autoptr(GFile) mntRoot = g_mount_get_root(mnt); if (mntRoot) { g_autofree char *curi = g_file_get_uri(mntRoot); if (QString(curi) == id) { this->mountHandler = reinterpret_cast(g_object_ref(mnt)); break; } } mnts = mnts->next; } g_list_free_full(mnts_head, g_object_unref); } DProtocolDevicePrivate::~DProtocolDevicePrivate() { if (mountHandler) g_object_unref(mountHandler); if (volumeHandler) g_object_unref(volumeHandler); } QString DProtocolDevicePrivate::path() const { return deviceId; } QString DProtocolDevicePrivate::mount(const QVariantMap &opts) { if (mountHandler) { // qInfo() << "mutexForMount prelock" << __FUNCTION__; QMutexLocker locker(&mutexForMount); // qInfo() << "mutexForMount locked" << __FUNCTION__; lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorAlreadyMounted); return mountPoint(mountHandler); } if (volumeHandler) { GMountOperation *operation { nullptr }; if (opts.contains(ParamMountOperation)) operation = reinterpret_cast((opts.value(ParamMountOperation).value())); // qInfo() << "mutexForVolume prelocked" << __FUNCTION__; // QMutexLocker locker(&mutexForVolume); // qInfo() << "mutexForVolume locked" << __FUNCTION__; if (!g_volume_can_mount(volumeHandler)) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); return ""; } g_autoptr(GCancellable) cancellable = g_cancellable_new(); QScopedPointer blocker(new ASyncToSyncHelper(timeout)); g_volume_mount(volumeHandler, G_MOUNT_MOUNT_NONE, operation, cancellable, mountWithBlocker, blocker.data()); auto ret = blocker->exec(); if (ret == ASyncToSyncHelper::NoError) { auto mpt = blocker->result().toString(); g_autoptr(GMount) mnt = g_volume_get_mount(volumeHandler); // setMount(mnt); return mpt; } else if (ret == ASyncToSyncHelper::Timeout) { if (cancellable) g_cancellable_cancel(cancellable); lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorTimedOut); } } lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); return ""; } void DProtocolDevicePrivate::mountAsync(const QVariantMap &opts, DeviceOperateCallbackWithMessage cb) { if (mountHandler) { // qInfo() << "mutexForMount prelock" << __FUNCTION__; QMutexLocker locker(&mutexForMount); // qInfo() << "mutexForMount locked" << __FUNCTION__; lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorAlreadyMounted); if (cb) cb(true, lastError, mountPoint(mountHandler)); return; } if (volumeHandler) { if (!g_volume_can_mount(volumeHandler)) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); if (cb) cb(false, lastError, ""); return; } if (!opts.contains(ParamCancellable)) qWarning() << "Cancellable is not defined, may cause problem"; GCancellable *cancellable { nullptr }; GMountOperation *operation { nullptr }; if (opts.contains(ParamCancellable)) cancellable = reinterpret_cast((opts.value(ParamCancellable).value())); if (opts.contains(ParamMountOperation)) operation = reinterpret_cast((opts.value(ParamMountOperation).value())); CallbackProxyWithData *proxy = new CallbackProxyWithData(cb); proxy->data = qobject_cast(q); proxy->d = this; g_volume_mount(volumeHandler, G_MOUNT_MOUNT_NONE, operation, cancellable, mountWithCallback, proxy); } } bool DProtocolDevicePrivate::unmount(const QVariantMap &opts) { if (!mountHandler) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMounted); return true; } else { QString mpt = mountPoint(mountHandler); if (mpt.contains(QRegularExpression("^(?:/run/media|/media)/.*")) && DNetworkMounter::isDaemonMountEnable()) { return DNetworkMounter::unmountNetworkDev(mpt); } else { GMountOperation *operation { nullptr }; if (opts.contains(ParamMountOperation)) operation = reinterpret_cast((opts.value(ParamMountOperation).value())); GMountUnmountFlags flag; if (opts.contains(ParamForce) && opts.value(ParamForce).toBool()) flag = GMountUnmountFlags::G_MOUNT_UNMOUNT_FORCE; else flag = GMountUnmountFlags::G_MOUNT_UNMOUNT_NONE; g_autoptr(GCancellable) cancellable = g_cancellable_new(); QScopedPointer blocker(new ASyncToSyncHelper(timeout)); g_mount_unmount_with_operation(mountHandler, flag, operation, cancellable, unmountWithBlocker, blocker.data()); auto ret = blocker->exec(); if (ret == ASyncToSyncHelper::NoError) { return true; } else if (ret == ASyncToSyncHelper::Timeout) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorTimedOut); g_cancellable_cancel(cancellable); return false; } } } return false; } void DProtocolDevicePrivate::unmountAsync(const QVariantMap &opts, DeviceOperateCallback cb) { if (!mountHandler) { lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMounted); if (cb) cb(true, lastError); return; } else { QString mpt = mountPoint(mountHandler); if (mpt.contains(QRegularExpression("^(?:/run/media|/media)/.*")) && DNetworkMounter::isDaemonMountEnable()) { DNetworkMounter::unmountNetworkDevAsync(mpt, cb); } else { GCancellable *cancellable { nullptr }; GMountOperation *operation { nullptr }; if (opts.contains(ParamCancellable)) cancellable = reinterpret_cast((opts.value(ParamCancellable).value())); if (opts.contains(ParamMountOperation)) operation = reinterpret_cast((opts.value(ParamMountOperation).value())); GMountUnmountFlags flag; if (opts.contains(ParamForce) && opts.value(ParamForce).toBool()) flag = GMountUnmountFlags::G_MOUNT_UNMOUNT_FORCE; else flag = GMountUnmountFlags::G_MOUNT_UNMOUNT_NONE; // qInfo() << "mutexForMount prelock" << __FUNCTION__; // QMutexLocker locker(&mutexForMount); // qInfo() << "mutexForMount locked" << __FUNCTION__; CallbackProxyWithData *proxy = new CallbackProxyWithData(cb); proxy->data = qobject_cast(q); proxy->d = this; g_mount_unmount_with_operation(mountHandler, flag, operation, cancellable, unmountWithCallback, proxy); } } } bool DProtocolDevicePrivate::rename(const QString &newName) { Q_UNUSED(newName); qWarning() << "not supported operation" << __FUNCTION__; return false; } void DProtocolDevicePrivate::renameAsync(const QString &newName, const QVariantMap &opts, DeviceOperateCallback cb) { Q_UNUSED(newName); Q_UNUSED(opts); Q_UNUSED(cb); qWarning() << "not supported operation" << __FUNCTION__; } QString DProtocolDevicePrivate::mountPoint() const { // qInfo() << "mutexForMount prelock" << __FUNCTION__; QMutexLocker locker(&mutexForMount); // qInfo() << "mutexForMount locked" << __FUNCTION__; if (mountHandler) return mountPoint(mountHandler); return QString(); } QString DProtocolDevicePrivate::fileSystem() const { return getAttr(Type).toString(); } long DProtocolDevicePrivate::sizeTotal() const { return getAttr(Total).value(); } long DProtocolDevicePrivate::sizeUsage() const { return sizeTotal() - sizeFree(); } long DProtocolDevicePrivate::sizeFree() const { return getAttr(Free).value(); } DeviceType DProtocolDevicePrivate::deviceType() const { return DeviceType::kProtocolDevice; } QString DProtocolDevicePrivate::displayName() const { if (volumeHandler) { g_autofree char *displayName = g_volume_get_name(volumeHandler); QString name(displayName); return name; } if (mountHandler) { // qInfo() << "mutexForMount prelock" << __FUNCTION__; QMutexLocker mntLocker(&mutexForMount); // qInfo() << "mutexForMount locked" << __FUNCTION__; g_autofree char *displayName = g_mount_get_name(mountHandler); QString name(displayName); return name; } lastError = Utils::genOperateErrorInfo(DeviceError::kUserErrorNotMountable); return ""; } bool DProtocolDevicePrivate::eject() { qWarning() << "not supported operation" << __FUNCTION__; return false; } bool DProtocolDevicePrivate::powerOff() { qWarning() << "not supported operation" << __FUNCTION__; return false; } QString DProtocolDevicePrivate::mountPoint(GMount *mount) { QString mpt; g_autoptr(GFile) mntDefLocation = g_mount_get_default_location(mount); if (mntDefLocation) { g_autofree char *mntPath = g_file_get_path(mntDefLocation); mpt = QString(mntPath); } return mpt; } QVariant DProtocolDevicePrivate::getAttr(DProtocolDevicePrivate::FsAttr type) const { if (fsAttrs.contains(QString::number(type))) return fsAttrs.value(QString::number(type)); if (mountPoint().isEmpty() || deviceId.startsWith("afc://")) // gvfs cannot handle the afc files. return QVariant(); g_autoptr(GFile) mntFile = g_file_new_for_uri(deviceId.toStdString().data()); if (!mntFile) { lastError = Utils::genOperateErrorInfo(DeviceError::kUnhandledError); qDebug() << "cannot create file handler for " << deviceId; return QVariant(); } GError *err = nullptr; QElapsedTimer t; t.start(); do { g_autoptr(GFileInfo) sysInfo = g_file_query_filesystem_info(mntFile, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE "," G_FILE_ATTRIBUTE_FILESYSTEM_FREE "," G_FILE_ATTRIBUTE_FILESYSTEM_USED "," G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, nullptr, &err); if (err) { int errCode = err->code; QString errMsg = err->message; lastError.code = Utils::castFromGError(err); lastError.message = errMsg; g_error_free(err); err = nullptr; qDebug() << "query filesystem info failed" << deviceId << errMsg; if (errCode == G_IO_ERROR_NOT_MOUNTED || errCode == G_IO_ERROR_PERMISSION_DENIED) return QVariant(); QThread::msleep(50); qApp->processEvents(); continue; } quint64 total = g_file_info_get_attribute_uint64(sysInfo, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); quint64 free = g_file_info_get_attribute_uint64(sysInfo, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); quint64 used = g_file_info_get_attribute_uint64(sysInfo, G_FILE_ATTRIBUTE_FILESYSTEM_USED); g_autofree char *fs_cstr = g_file_info_get_attribute_as_string(sysInfo, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE); QString fs = QString::fromUtf8(fs_cstr ? fs_cstr : ""); fsAttrs.insert(QString::number(FsAttr::Free), free); fsAttrs.insert(QString::number(FsAttr::Total), total); fsAttrs.insert(QString::number(FsAttr::Usage), used); fsAttrs.insert(QString::number(FsAttr::Type), fs); if (type == FsAttr::Free) return free; if (type == FsAttr::Total) return total; if (type == FsAttr::Usage) return used; if (type == FsAttr::Type) return fs; } while (t.elapsed() < 5000); qDebug() << "info not obtained after timeout" << deviceId; fsAttrs.insert(QString::number(FsAttr::Free), QVariant()); fsAttrs.insert(QString::number(FsAttr::Total), QVariant()); fsAttrs.insert(QString::number(FsAttr::Usage), QVariant()); fsAttrs.insert(QString::number(FsAttr::Type), QVariant()); return QVariant(); } static bool mountDone(GObject *sourceObj, GAsyncResult *res, OperationErrorInfo &derr) { auto vol = reinterpret_cast(sourceObj); GError *err { nullptr }; bool ret = g_volume_mount_finish(vol, res, &err); if (err) { derr.code = Utils::castFromGError(err); derr.message = err->message; qDebug() << "mount failed" << err->message; g_error_free(err); } return ret; } void DProtocolDevicePrivate::mountWithBlocker(GObject *sourceObj, GAsyncResult *res, gpointer blocker) { OperationErrorInfo err; bool ret = mountDone(sourceObj, res, err); auto helper = static_cast(blocker); if (helper) { int code = ret ? ASyncToSyncHelper::NoError : ASyncToSyncHelper::Failed; if (ret) { auto volume = reinterpret_cast(sourceObj); if (volume) { GMount_autoptr mount = g_volume_get_mount(volume); if (mount) helper->setResult(mountPoint(mount)); } } helper->exit(code); } } void DProtocolDevicePrivate::mountWithCallback(GObject *sourceObj, GAsyncResult *res, gpointer cbProxy) { OperationErrorInfo err; auto &&ret = mountDone(sourceObj, res, err); auto proxy = static_cast(cbProxy); if (proxy) { auto volume = reinterpret_cast(sourceObj); if (volume) { GMount_autoptr mount = g_volume_get_mount(volume); if (proxy->caller.cbWithInfo) proxy->caller.cbWithInfo(ret, err, mountPoint(mount)); } delete proxy; } } void DProtocolDevicePrivate::unmountWithBlocker(GObject *sourceObj, GAsyncResult *res, gpointer blocker) { auto mnt = reinterpret_cast(sourceObj); GError *err { nullptr }; bool ret = g_mount_unmount_with_operation_finish(mnt, res, &err); if (err) { // TODO qDebug() << err->message; g_error_free(err); } auto helper = static_cast(blocker); if (helper) { auto code = ret ? ASyncToSyncHelper::NoError : ASyncToSyncHelper::Failed; helper->setResult(ret); helper->exit(code); } } void DProtocolDevicePrivate::unmountWithCallback(GObject *sourceObj, GAsyncResult *res, gpointer cbProxy) { auto mnt = reinterpret_cast(sourceObj); GError *err { nullptr }; OperationErrorInfo derr; bool ret = g_mount_unmount_with_operation_finish(mnt, res, &err); if (err) { derr.code = Utils::castFromGError(err); derr.message = err->message; g_error_free(err); } auto proxy = static_cast(cbProxy); if (proxy) { if (proxy->data) { proxy->d->mountHandler = nullptr; } if (proxy->caller.cb) { proxy->caller.cb(ret, derr); } delete proxy; } } ASyncToSyncHelper::ASyncToSyncHelper(int timeout) { blocker = new QEventLoop(); timer.reset(new QTimer()); timer->setInterval(timeout); timer->setSingleShot(true); QObject::connect(timer.data(), &QTimer::timeout, blocker, [this] { blocker->exit(Timeout); }); } ASyncToSyncHelper::~ASyncToSyncHelper() { if (blocker) { blocker->exit(); delete blocker; blocker = nullptr; } timer->stop(); } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/lib/dprotocolmonitor.cpp000066400000000000000000000324771523477242100255140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "private/dprotocolmonitor_p.h" #include "private/dprotocoldevice_p.h" #include #include #include #include #include extern "C" { #include #include } DFM_MOUNT_USE_NS DProtocolMonitor::DProtocolMonitor(QObject *parent) : DDeviceMonitor(new DProtocolMonitorPrivate(this), parent) { auto dp = Utils::castClassFromTo(d.data()); if (!dp) { qCritical() << "private pointer not valid" << __PRETTY_FUNCTION__; abort(); } registerStartMonitor(std::bind(&DProtocolMonitorPrivate::startMonitor, dp)); registerStopMonitor(std::bind(&DProtocolMonitorPrivate::stopMonitor, dp)); registerMonitorObjectType(std::bind(&DProtocolMonitorPrivate::monitorObjectType, dp)); registerGetDevices(std::bind(&DProtocolMonitorPrivate::getDevices, dp)); registerCreateDeviceById(std::bind(&DProtocolMonitorPrivate::createDevice, dp, std::placeholders::_1)); } DProtocolMonitor::~DProtocolMonitor() { } DProtocolMonitorPrivate::DProtocolMonitorPrivate(DProtocolMonitor *qq) : DDeviceMonitorPrivate(qq) { // gvolumemonitor must be initialized in main thread if (QThread::currentThread() != qApp->thread()) { qCritical() << "not allow to init protocol monitor in non-main thread"; abort(); } gVolMonitor = g_volume_monitor_get(); if (!gVolMonitor) { qCritical() << "cannot allocate volume monitor"; abort(); } initDeviceList(); } DProtocolMonitorPrivate::~DProtocolMonitorPrivate() { if (gVolMonitor) g_object_unref(gVolMonitor); gVolMonitor = nullptr; } bool DProtocolMonitorPrivate::startMonitor() { if (!gVolMonitor) { qCritical() << "monitor is not valid"; abort(); } ulong handler = 0; handler = g_signal_connect(gVolMonitor, MOUNT_ADDED, G_CALLBACK(&DProtocolMonitorPrivate::onMountAdded), this); connections.insert(MOUNT_ADDED, handler); // handler = g_signal_connect(gVolMonitor, MOUNT_CHANGED, G_CALLBACK(&DProtocolMonitorPrivate::onMountChanged), this); // connections.insert(MOUNT_CHANGED, handler); handler = g_signal_connect(gVolMonitor, MOUNT_REMOVED, G_CALLBACK(&DProtocolMonitorPrivate::onMountRemoved), this); connections.insert(MOUNT_REMOVED, handler); handler = g_signal_connect(gVolMonitor, VOLUME_ADDED, G_CALLBACK(&DProtocolMonitorPrivate::onVolumeAdded), this); connections.insert(VOLUME_ADDED, handler); // handler = g_signal_connect(gVolMonitor, VOLUME_CHANGED, G_CALLBACK(&DProtocolMonitorPrivate::onVolumeChanged), this); // connections.insert(VOLUME_CHANGED, handler); handler = g_signal_connect(gVolMonitor, VOLUME_REMOVED, G_CALLBACK(&DProtocolMonitorPrivate::onVolumeRemoved), this); connections.insert(VOLUME_REMOVED, handler); qDebug() << "protocol monitor start"; return true; } bool DProtocolMonitorPrivate::stopMonitor() { for (auto iter = connections.cbegin(); iter != connections.cend(); iter++) g_signal_handler_disconnect(gVolMonitor, iter.value()); connections.clear(); qDebug() << "protocol monitor stop"; return true; } DeviceType DProtocolMonitorPrivate::monitorObjectType() const { return DeviceType::kProtocolDevice; } QStringList DProtocolMonitorPrivate::getDevices() { return cachedDevices.values(); } QSharedPointer DProtocolMonitorPrivate::createDevice(const QString &id) { auto dev = new DProtocolDevice(id, gVolMonitor, nullptr); // for updating the mounthandler of device, if the device exists. QObject::connect(q, &DProtocolMonitor::mountAdded, dev, &DProtocolDevice::mounted); QObject::connect(q, &DProtocolMonitor::mountRemoved, dev, &DProtocolDevice::unmounted); QSharedPointer ret; ret.reset(dev); return ret; } QSharedPointer DProtocolMonitorPrivate::createDeviceByMount() { // TODO return nullptr; } void DProtocolMonitorPrivate::initDeviceList() { /* all blocks have a GDrive object, but we need to handle CDDA which exactly has a GDrive * GDrive <--> UDisks::Drive * GVolume <--> UDisks::Block which to show on user interface, a 'partiton' entry. * GMount <--> UDisks::FileSystem which means a mountable, always have a filesystem. * it's not precise though * * ----GDrive--------GVolume--------GMount-------DeviceType * yes yes opt block devices * no yes opt phones (mpt/ptp/gphoto, etc.) * no no opt virtual devices (smb/ftp/sftp, etc.) * ----------------------------------------------------------------------------------- * opt means the Object is not existed anytime, only if it was mounted, then GMount object generated. * so, we need to gather both phones and virtual devices and ignore block devices which will be gathered in DBlockMonitor. * */ // first, get all volumes which do not have a GDrive. GList *vols = g_volume_monitor_get_volumes(gVolMonitor); auto iterVol = [](gpointer pVol, gpointer userData) { auto d = static_cast(userData); Q_ASSERT(d); GVolume *vol = static_cast(pVol); if (!vol) return; g_autoptr(GDrive) drv = g_volume_get_drive(vol); if (drv) // ignore blocks return; g_autoptr(GFile) activationRoot = g_volume_get_activation_root(vol); if (activationRoot) { g_autofree char *curi = g_file_get_uri(activationRoot); d->cachedDevices.insert(curi); } else { // g_autofree char *volId = g_volume_get_identifier(vol, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); // qWarning() << "protocol: cannot get the root of " << volId; } }; g_list_foreach(vols, static_cast(iterVol), this); g_list_free_full(vols, g_object_unref); // second, find those orphan mounts GList *mnts = g_volume_monitor_get_mounts(gVolMonitor); auto iterMnt = [](gpointer pMnt, gpointer userData) { auto d = static_cast(userData); Q_ASSERT(d); GMount *mnt = static_cast(pMnt); if (!mnt) return; g_autoptr(GVolume) vol = g_mount_get_volume(mnt); if (vol) // only find orphan mounts return; // TODO: cannot bind an iphone AFC mount with its volume, it does not emit a mountAdded signal when monitor start. // nemo/nautilus... show volume and mount seperated. seems like an upstream issue or designed so. g_autoptr(GFile) root = g_mount_get_root(mnt); if (root) { g_autofree char *curi = g_file_get_uri(root); auto mpt = DProtocolDevicePrivate::mountPoint(mnt); if (!isNativeMount(mpt) && !isMountByOther(mpt)) d->cachedDevices.insert(curi); } else { // g_autofree char *cname = g_mount_get_name(mnt); // qWarning() << "protocol: cannot get the root of " << cname; } }; g_list_foreach(mnts, static_cast(iterMnt), this); g_list_free_full(mnts, g_object_unref); } void DProtocolMonitorPrivate::onMountAdded(GVolumeMonitor *monitor, GMount *mount, gpointer userData) { Q_UNUSED(monitor); auto mpt = DProtocolDevicePrivate::mountPoint(mount); if (isNativeMount(mpt) || hasDrive(mount) || isMountByOther(mpt)) return; auto d = static_cast(userData); Q_ASSERT(d); g_autoptr(GFile) mntRoot = g_mount_get_root(mount); if (mntRoot) { g_autofree char *curi = g_file_get_uri(mntRoot); d->cachedDevices.insert(curi); Q_EMIT d->q->mountAdded(curi, mpt); } else { // qWarning() << "protocol: cannot get the root of " << mpt; } } void DProtocolMonitorPrivate::onMountChanged(GVolumeMonitor *monitor, GMount *mount, gpointer userData) { Q_UNUSED(monitor); if (hasDrive(mount)) // don't handle real block devices return; auto d = static_cast(userData); Q_ASSERT(d); qDebug() << __FUNCTION__; } void DProtocolMonitorPrivate::onMountRemoved(GVolumeMonitor *monitor, GMount *mount, gpointer userData) { Q_UNUSED(monitor); if (hasDrive(mount)) // don't handle real block devices return; auto d = static_cast(userData); Q_ASSERT(d); auto mpt = DProtocolDevicePrivate::mountPoint(mount); // if (!mpt.isEmpty() && isNativeMount(mpt)) // don't report block device unmounted. // return; g_autoptr(GFile) mntRoot = g_mount_get_root(mount); if (mntRoot) { g_autofree char *curi = g_file_get_uri(mntRoot); if (isOrphanMount(mount)) d->cachedDevices.remove(curi); Q_EMIT d->q->mountRemoved(curi); } else { // qWarning() << "protocol: cannot get the root of " << mpt; } } void DProtocolMonitorPrivate::onVolumeAdded(GVolumeMonitor *monitor, GVolume *volume, gpointer userData) { Q_UNUSED(monitor); if (hasDrive(volume)) // don't handle real block devices return; auto d = static_cast(userData); Q_ASSERT(d); g_autoptr(GFile) activationRoot = g_volume_get_activation_root(volume); if (activationRoot) { g_autofree char *curi = g_file_get_uri(activationRoot); d->cachedDevices.insert(curi); Q_EMIT d->q->deviceAdded(curi); } else { // qWarning() << "protocol: cannot get the root of " << volume; } } void DProtocolMonitorPrivate::onVolumeChanged(GVolumeMonitor *monitor, GVolume *volume, gpointer userData) { Q_UNUSED(monitor); if (hasDrive(volume)) // don't handle real block devices return; auto d = static_cast(userData); Q_ASSERT(d); qDebug() << __FUNCTION__; } void DProtocolMonitorPrivate::onVolumeRemoved(GVolumeMonitor *monitor, GVolume *volume, gpointer userData) { Q_UNUSED(monitor); if (hasDrive(volume)) // don't handle real block devices return; auto d = static_cast(userData); Q_ASSERT(d); g_autoptr(GFile) activationRoot = g_volume_get_activation_root(volume); if (activationRoot) { g_autofree char *curi = g_file_get_uri(activationRoot); d->cachedDevices.remove(curi); Q_EMIT d->q->deviceRemoved(curi); } else { // qWarning() << "protocol: cannot get the root of " << volume; } } bool DProtocolMonitorPrivate::hasDrive(GMount *mount) { if (!mount) return false; g_autoptr(GDrive) drv = g_mount_get_drive(mount); return drv ? true : false; } bool DProtocolMonitorPrivate::hasDrive(GVolume *volume) { if (!volume) return false; g_autoptr(GDrive) drv = g_volume_get_drive(volume); return drv ? true : false; } static GUnixMountEntry* find_unix_mount_for(const gchar *path) { if (!path) return NULL; GUnixMountEntry *found_mount = NULL; g_autofree gchar *current_path = g_strdup(path); while (TRUE) { found_mount = g_unix_mount_at(current_path, NULL); if (found_mount) break; if (g_strcmp0(current_path, "/") == 0) break; gchar *parent_path = g_path_get_dirname(current_path); g_free(current_path); current_path = parent_path; } return found_mount; } bool DProtocolMonitorPrivate::isNativeMount(const QString &mpt) { if (mpt.isEmpty()) return false; std::string s = mpt.toStdString(); g_autoptr(GUnixMountEntry) entry = find_unix_mount_for(s.data()); if (entry) { QString devPath = g_unix_mount_get_device_path(entry); if (devPath.startsWith("/dev/")) return true; } return false; } bool DProtocolMonitorPrivate::isMountByOther(const QString &mpt) { QRegularExpression re("^/(?:run/)?media/(.*)/smbmounts"); auto match = re.match(mpt); if (!match.hasMatch()) // mount which not mouted at preseted path regards as a normal mount return false; auto user = match.captured(1); return user != Utils::currentUser(); } bool DProtocolMonitorPrivate::isOrphanMount(GMount *mount) { g_autoptr(GFile) mntRoot = g_mount_get_root(mount); g_autofree char *curi = g_file_get_uri(mntRoot); QString uri(curi); if (uri.startsWith("smb") || uri.startsWith("ftp") || uri.startsWith("sftp")) return true; bool isOrphan = true; g_autoptr(GVolumeMonitor) monitor = g_volume_monitor_get(); GList *vols = g_volume_monitor_get_volumes(monitor); while (vols) { auto *vol = reinterpret_cast(vols->data); g_autoptr(GFile) volRoot = g_volume_get_activation_root(vol); if (volRoot) { g_autofree char *volUri = g_file_get_uri(volRoot); if (g_strcmp0(curi, volUri) == 0) { isOrphan = false; break; } } vols = vols->next; } g_list_free_full(vols, g_object_unref); return isOrphan; } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/000077500000000000000000000000001523477242100222625ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/dblockdevice_p.h000066400000000000000000000102411523477242100253660ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DBLOCKDEVICE_P_H #define DBLOCKDEVICE_P_H #include #include #include #include "private/ddevice_p.h" extern "C" { #include } DFM_MOUNT_BEGIN_NS class DBlockDevicePrivate final : public DDevicePrivate { friend class DBlockDevice; public: DBlockDevicePrivate(UDisksClient *cli, const QString &blkObjPath, DBlockDevice *qq); ~DBlockDevicePrivate(); QString path() const DMNT_OVERRIDE; QString mount(const QVariantMap &opts) DMNT_OVERRIDE; void mountAsync(const QVariantMap &opts, DeviceOperateCallbackWithMessage cb) DMNT_OVERRIDE; bool unmount(const QVariantMap &opts) DMNT_OVERRIDE; void unmountAsync(const QVariantMap &opts, DeviceOperateCallback cb) DMNT_OVERRIDE; bool rename(const QString &newName, const QVariantMap &opts) DMNT_OVERRIDE; void renameAsync(const QString &newName, const QVariantMap &opts, DeviceOperateCallback cb) DMNT_OVERRIDE; QString mountPoint() const DMNT_OVERRIDE; QString fileSystem() const DMNT_OVERRIDE; qint64 sizeTotal() const DMNT_OVERRIDE; qint64 sizeUsage() const DMNT_OVERRIDE; qint64 sizeFree() const DMNT_OVERRIDE; DeviceType deviceType() const DMNT_OVERRIDE; QVariant getProperty(Property name) const DMNT_OVERRIDE; QString displayName() const DMNT_OVERRIDE; QVariant getBlockProperty(Property name) const; QVariant getDriveProperty(Property name) const; QVariant getFileSystemProperty(Property name) const; QVariant getPartitionProperty(Property name) const; QVariant getEncryptedProperty(Property name) const; bool eject(const QVariantMap &opts); void ejectAsync(const QVariantMap &opts, DeviceOperateCallback cb); bool powerOff(const QVariantMap &opts); void powerOffAsync(const QVariantMap &opts, DeviceOperateCallback cb); bool lock(const QVariantMap &opts); void lockAsync(const QVariantMap &opts, DeviceOperateCallback cb); bool unlock(const QString &passwd, QString &clearTextDev, const QVariantMap &opts); void unlockAsync(const QString &passwd, const QVariantMap &opts, DeviceOperateCallbackWithMessage cb); bool rescan(const QVariantMap &opts); void rescanAsync(const QVariantMap &opts, DeviceOperateCallback cb); private: // error report void handleErrorAndRelease(GError *err); static void handleErrorAndRelease(CallbackProxy *proxy, bool result, GError *gerr, QString info = QString()); // async callbacks static void mountAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData); static void unmountAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData); static void renameAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData); static void ejectAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData); static void powerOffAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData); static void lockAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData); static void unlockAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData); static void rescanAsyncCallback(GObject *sourceObj, GAsyncResult *res, gpointer userData); static int dedupMountPoint(struct libmnt_table *table, struct libmnt_fs *a, struct libmnt_fs *b); static QString findFirstMountPoint(const QString &device); UDisksObject_autoptr getUDisksObject() const; UDisksBlock_autoptr getBlockHandler() const; UDisksDrive_autoptr getDriveHandler() const; UDisksLoop_autoptr getLoopHandler() const; UDisksEncrypted_autoptr getEncryptedHandler() const; UDisksPartition_autoptr getPartitionHandler() const; UDisksPartitionTable_autoptr getPartitionTableHandler() const; UDisksFilesystem_autoptr getFilesystemHandler() const; enum JobType { kBlockJob, kDriveJob }; bool findJob(JobType type); QString blkObjPath; // path of block device object UDisksClient *client { nullptr }; }; DFM_MOUNT_END_NS #endif // DBLOCKDEVICE_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/dblockmonitor_p.h000066400000000000000000000042531523477242100256240ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DBLOCKMONITOR_P_H #define DBLOCKMONITOR_P_H #include #include "private/ddevicemonitor_p.h" #include #include #include extern "C" { #include } DFM_MOUNT_BEGIN_NS #define OBJECT_ADDED "object-added" #define OBJECT_REMOVED "object-removed" #define INTERFACE_ADDED "interface-added" #define INTERFACE_REMOVED "interface-removed" #define PROPERTY_CHANGED "interface-proxy-properties-changed" class DBlockDevice; class DBlockMonitorPrivate final : public DDeviceMonitorPrivate { public: DBlockMonitorPrivate(DBlockMonitor *qq); ~DBlockMonitorPrivate(); bool startMonitor() DMNT_OVERRIDE; bool stopMonitor() DMNT_OVERRIDE; bool startDeviceMonitor(); bool stopDeviceMonitor(); DeviceType monitorObjectType() const DMNT_OVERRIDE; QStringList getDevices() DMNT_OVERRIDE; QSharedPointer createDeviceById(const QString &id) DMNT_OVERRIDE; QStringList resolveDevice(const QVariantMap &devspec, const QVariantMap &opts); QStringList resolveDeviceNode(const QString &node, const QVariantMap &opts); QStringList resolveDeviceOfDrive(const QString &drvObjPath); private: static void onObjectAdded(GDBusObjectManager *mng, GDBusObject *obj, gpointer userData); static void onObjectRemoved(GDBusObjectManager *mng, GDBusObject *obj, gpointer userData); static void onPropertyChanged(GDBusObjectManagerClient *mngClient, GDBusObjectProxy *objProxy, GDBusProxy *dbusProxy, GVariant *property, const gchar *const invalidProperty, gpointer userData); static void onInterfaceAdded(GDBusObjectManager *mng, GDBusObject *obj, GDBusInterface *iface, gpointer userData); static void onInterfaceRemoved(GDBusObjectManager *mng, GDBusObject *obj, GDBusInterface *iface, gpointer userData); void initDevices(); public: UDisksClient *client = nullptr; QDBusServiceWatcher *watcher = nullptr; static QMap> blksOfDrive; }; DFM_MOUNT_END_NS #endif // DBLOCKMONITOR_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/ddevice_p.h000066400000000000000000000030401523477242100243520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DDEVICEPRIVATE_H #define DDEVICEPRIVATE_H #include #include DFM_MOUNT_BEGIN_NS class CallbackProxy { public: CallbackProxy(DeviceOperateCallback cb) : cb(cb) { } CallbackProxy(DeviceOperateCallbackWithMessage cb) : cbWithInfo(cb) { } DeviceOperateCallback cb { nullptr }; DeviceOperateCallbackWithMessage cbWithInfo { nullptr }; }; class DDevicePrivate { public: DDevicePrivate(DDevice *qq); virtual ~DDevicePrivate(); DDevice::PathFunc path { nullptr }; DDevice::MountFunc mount { nullptr }; DDevice::MountAsyncFunc mountAsync { nullptr }; DDevice::UnmountFunc unmount { nullptr }; DDevice::UnmountAsyncFunc unmountAsync { nullptr }; DDevice::RenameFunc rename { nullptr }; DDevice::RenameAsyncFunc renameAsync { nullptr }; DDevice::MountPointFunc mountPoint { nullptr }; DDevice::FileSystemFunc fileSystem { nullptr }; DDevice::SizeTotalFunc sizeTotal { nullptr }; DDevice::SizeUsageFunc sizeUsage { nullptr }; DDevice::SizeFreeFunc sizeFree { nullptr }; DDevice::DeviceTypeFunc deviceType { nullptr }; DDevice::GetPropertyFunc getProperty { nullptr }; DDevice::DisplayNameFunc displayName { nullptr }; mutable OperationErrorInfo lastError { DeviceError::kNoError, "" }; protected: DDevice *q = nullptr; }; DFM_MOUNT_END_NS #endif // DDEVICEPRIVATE_H linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/ddevicemanager_p.h000066400000000000000000000017111523477242100257100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2019 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DDEVICEMANAGERPRIVATE_H #define DDEVICEMANAGERPRIVATE_H #include #include #include #include #include DFM_MOUNT_BEGIN_NS class DDeviceManagerPrivate final { public: DDeviceManagerPrivate(DDeviceManager *qq); template bool registerMonitor(ConstructArgs &&... args); QSharedPointer getRegisteredMonitor(DeviceType type) const; bool startMonitor(); bool stopMonitor(); QMap devices(DeviceType type); QMap> monitors; MonitorError lastError; DDeviceManager *q = nullptr; }; DFM_MOUNT_END_NS #endif // DDEVICEMANAGERPRIVATE_H linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/ddevicemonitor_p.h000066400000000000000000000020451523477242100257660ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DMONITORPRIVATE_H #define DMONITORPRIVATE_H #include #include #include #include using namespace std; DFM_MOUNT_BEGIN_NS class DDeviceMonitorPrivate { public: DDeviceMonitorPrivate(DDeviceMonitor *qq); virtual ~DDeviceMonitorPrivate(); DDeviceMonitor::StartMonitorFunc start = nullptr; DDeviceMonitor::StopMonitorFunc stop = nullptr; DDeviceMonitor::MonitorObjectTypeFunc monitorObjectType = nullptr; DDeviceMonitor::GetDevicesFunc getDevices = nullptr; DDeviceMonitor::CreateDeviceByIdFunc createDeviceById = nullptr; DDeviceMonitor *q = nullptr; // for saving gsignals connections, key: singal_name, value: the handler returned by g_signal_connect QMap connections; MonitorStatus monitorStatus = MonitorStatus::kIdle; }; DFM_MOUNT_END_NS #endif // DMONITORPRIVATE_H linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/dnetworkmounter.cpp000066400000000000000000000652771523477242100262560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include "dnetworkmounter.h" #include #include #include #include #include #include #include #include #include #include #include DFM_MOUNT_USE_NS #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) static constexpr char kDaemonService[] { "org.deepin.Filemanager.MountControl" }; static constexpr char kDaemonPath[] { "/org/deepin/Filemanager" }; static constexpr char kMountControlPath[] { "/org/deepin/Filemanager/MountControl" }; static constexpr char kMountControlIFace[] { "org.deepin.Filemanager.MountControl" }; #else static constexpr char kDaemonService[] { "com.deepin.filemanager.daemon" }; static constexpr char kDaemonPath[] { "/com/deepin/filemanager/daemon" }; static constexpr char kMountControlPath[] { "/com/deepin/filemanager/daemon/MountControl" }; static constexpr char kMountControlIFace[] { "com.deepin.filemanager.daemon.MountControl" }; #endif static constexpr char kDaemonIntro[] { "org.freedesktop.DBus.Introspectable" }; static constexpr char kDaemonIntroMethod[] { "Introspect" }; static constexpr char kMountControlMount[] { "Mount" }; static constexpr char kMountControlUnmount[] { "Unmount" }; static constexpr char kSchemaUser[] { "user" }; static constexpr char kSchemaProtocol[] { "protocol" }; static constexpr char kSchemaDomain[] { "domain" }; static constexpr char kSchemaServer[] { "server" }; static constexpr char kLoginUser[] { "user" }; static constexpr char kLoginDomain[] { "domain" }; static constexpr char kLoginPasswd[] { "passwd" }; static constexpr char kLoginTimeout[] { "timeout" }; static constexpr char kMountFsType[] { "fsType" }; static constexpr char kSmbConfigPath[] { "/etc/samba/smb.conf" }; static constexpr char kDaemonMountRetKeyMpt[] { "mountPoint" }; static constexpr char kDaemonMountRetKeyErrno[] { "errno" }; struct AskPasswdHelper { GetMountPassInfo callback { nullptr }; bool callOnceFlag { false }; bool anonymous { false }; DeviceError err { DeviceError::kNoError }; }; struct AskQuestionHelper { GetUserChoice callback { nullptr }; DeviceError err { DeviceError::kNoError }; }; struct FinalizeHelper { AskPasswdHelper *askPasswd { nullptr }; AskQuestionHelper *askQuestion { nullptr }; DeviceOperateCallbackWithMessage resultCallback; QVariant customData; }; bool DNetworkMounter::isDaemonMountEnable() { auto systemBusIFace = QDBusConnection::systemBus().interface(); if (!systemBusIFace) return false; if (!systemBusIFace->isServiceRegistered(kDaemonService)) return false; // check if MountControl interface exists QDBusInterface daemonIntroIface(kDaemonService, kDaemonPath, kDaemonIntro, QDBusConnection::systemBus()); QDBusReply reply = daemonIntroIface.call(kDaemonIntroMethod); if (reply.value().contains(R"()")) { // check if "SupportedFileSystems" method exists QDBusInterface introIface(kDaemonService, kMountControlPath, kDaemonIntro, QDBusConnection::systemBus()); QDBusReply ifaceDesc = introIface.call(kDaemonIntroMethod); if (!ifaceDesc.value().contains(R"()")) return true; QDBusInterface mountIface(kDaemonService, kMountControlPath, kMountControlIFace, QDBusConnection::systemBus()); QDBusReply supported = mountIface.call("SupportedFileSystems"); return supported.value().contains("cifs"); } return false; } QList DNetworkMounter::loginPasswd(const QString &address) { QUrl u(address); QString protocol = u.scheme(); QString host = u.host(); GHashTable_autoptr query = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); g_hash_table_insert(query, strdup(kSchemaServer), strdup(host.toStdString().c_str())); g_hash_table_insert(query, strdup(kSchemaProtocol), strdup(protocol.toStdString().c_str())); QList passwds; GError_autoptr err { nullptr }; GList_autoptr items = secret_service_search_sync(nullptr, smbSchema(), query, SECRET_SEARCH_ALL, nullptr, &err); // 在使用完query后立即释放 if (query) { g_hash_table_unref(query); query = nullptr; } while (items) { auto item = static_cast(items->data); GHashTable *itemAttrs = secret_item_get_attributes(item); QVariantMap attr; g_hash_table_foreach( itemAttrs, [](gpointer k, gpointer v, gpointer vm) { auto info = static_cast(vm); if (!info) return; info->insert(static_cast(k), QString(static_cast(v))); qInfo() << "found saved login info:" << *info; }, &attr); if (attr.contains(kSchemaDomain) && attr.contains(kSchemaProtocol) && attr.contains(kSchemaServer) && attr.contains(kSchemaUser)) passwds.append(attr); else qInfo() << "got invalid saved keyring, ignore." << attr; // 手动释放 GHashTable 以避免内存泄漏 if (itemAttrs) g_hash_table_unref(itemAttrs); items = items->next; } for (auto &passwd : passwds) { std::string server = passwd.value(kSchemaServer).toString().toStdString(); std::string protocol = passwd.value(kSchemaProtocol).toString().toStdString(); std::string user = passwd.value(kSchemaUser).toString().toStdString(); std::string domain = passwd.value(kSchemaDomain).toString().toStdString(); GError_autoptr err { nullptr }; g_autofree char *pwd = secret_password_lookup_sync( smbSchema(), nullptr, &err, kSchemaServer, server.c_str(), kSchemaProtocol, protocol.c_str(), kSchemaUser, user.c_str(), kSchemaDomain, domain.c_str(), nullptr); if (err) qDebug() << "query password failed: " << passwd << err->message; else { passwd.insert(kLoginPasswd, QString(pwd)); } } return passwds; } void DNetworkMounter::savePasswd(const QString &address, const MountPassInfo &info) { QUrl u(address); QString protocol = u.scheme(); QString server = u.host(); const char *collection = info.savePasswd == NetworkMountPasswdSaveMode::kSaveBeforeLogout ? SECRET_COLLECTION_SESSION : SECRET_COLLECTION_DEFAULT; if (protocol == "smb") { GError_autoptr err { nullptr }; QString title = QString("%1@%2") .arg(info.userName) .arg(server); // username@host, just like the way gvfs do. secret_password_store_sync(smbSchema(), collection, title.toStdString().c_str(), info.passwd.toStdString().c_str(), nullptr, &err, kSchemaDomain, info.domain.toStdString().c_str(), kSchemaProtocol, protocol.toStdString().c_str(), kSchemaServer, server.toStdString().c_str(), kSchemaUser, info.userName.toStdString().c_str(), nullptr); if (err) qWarning() << "save passwd failed: " << err->message; } } SecretSchema *DNetworkMounter::smbSchema() { static SecretSchema sche; sche.name = "org.gnome.keyring.NetworkPassword"; sche.flags = SECRET_SCHEMA_NONE; sche.attributes[0] = { kSchemaUser, SECRET_SCHEMA_ATTRIBUTE_STRING }; sche.attributes[1] = { kSchemaDomain, SECRET_SCHEMA_ATTRIBUTE_STRING }; sche.attributes[2] = { kSchemaServer, SECRET_SCHEMA_ATTRIBUTE_STRING }; sche.attributes[3] = { kSchemaProtocol, SECRET_SCHEMA_ATTRIBUTE_STRING }; return &sche; } void DNetworkMounter::mountNetworkDev(const QString &address, GetMountPassInfo getPassInfo, GetUserChoice getUserChoice, DeviceOperateCallbackWithMessage mountResult, int secs) { if (isMountByDae(address)) mountByDaemon(address, getPassInfo, mountResult, secs); else mountByGvfs(address, getPassInfo, getUserChoice, mountResult, secs); } bool DNetworkMounter::isMountByDae(const QString &address) { QUrl u(address); // don't mount samba's root by Daemon return u.scheme() == "smb" && !u.path().remove("/").isEmpty() && isDaemonMountEnable(); } bool DNetworkMounter::unmountNetworkDev(const QString &mpt) { QDBusInterface mntCtrl(kDaemonService, kMountControlPath, kMountControlIFace, QDBusConnection::systemBus()); QVariantMap opts { { kMountFsType, "cifs" } }; QDBusReply ret = mntCtrl.call(kMountControlUnmount, mpt, opts); auto result = ret.value(); return result.value("result", false).toBool(); } void DNetworkMounter::unmountNetworkDevAsync(const QString &mpt, DeviceOperateCallback cb) { struct UnmountResult { bool success; int errnoFromDaemon; QString errMsgFromDaemon; }; QFutureWatcher *watcher { new QFutureWatcher() }; QObject::connect(watcher, &QFutureWatcher::finished, [cb, watcher] { UnmountResult result = watcher->result(); watcher->deleteLater(); if (cb) { if (result.errnoFromDaemon == -8) { // service_mountcontrol::MountErrorCode::kAuthenticationFailed cb(result.success, Utils::genOperateErrorInfo(DeviceError::kUserErrorAuthenticationFailed, result.errMsgFromDaemon)); } else if (result.errnoFromDaemon == static_cast(DeviceError::kGDBusErrorNoReply)) { cb(result.success, Utils::genOperateErrorInfo(DeviceError::kUDisksErrorNotAuthorizedDismissed, result.errMsgFromDaemon)); } else { cb(result.success, Utils::genOperateErrorInfo(result.success ? DeviceError::kNoError : DeviceError::kUserError, result.errMsgFromDaemon)); } } }); watcher->setFuture(QtConcurrent::run([mpt]() -> UnmountResult { int errnoFromDaemon = 0; QString errMsgFromDaemon; bool success = unmountNetworkDevAsyncDetailed(mpt, &errnoFromDaemon, &errMsgFromDaemon); return { success, errnoFromDaemon, errMsgFromDaemon }; })); } bool DNetworkMounter::unmountNetworkDevAsyncDetailed(const QString &mpt, int *errCode, QString *errMsg) { QDBusInterface mntCtrl(kDaemonService, kMountControlPath, kMountControlIFace, QDBusConnection::systemBus()); QVariantMap opts { { kMountFsType, "cifs" } }; QDBusReply ret = mntCtrl.call(kMountControlUnmount, mpt, opts); if (!ret.isValid()) { const auto &e = ret.error(); const QString name = e.name(); if (e.type() == QDBusError::NoReply || name == QStringLiteral("org.freedesktop.DBus.Error.NoReply")) { if (errCode) *errCode = static_cast(DFMMOUNT::DeviceError::kGDBusErrorNoReply); if (errMsg) *errMsg = QStringLiteral("Authorization pending (NoReply)"); return false; } if (errCode) *errCode = EINVAL; if (errMsg) *errMsg = "DBus call failed"; return false; } auto result = ret.value(); if (errCode) *errCode = result.value("errno", 0).toInt(); if (errMsg) *errMsg = result.value("errMsg", "").toString(); return result.value("result", false).toBool(); } /*! * \brief DNetworkMounter::mountByDaemon, mount network device (smb/ftp/webdav) by * dde-file-manager-daemon. \param address \param getPassInfo \param mountResult \param msecs */ void DNetworkMounter::mountByDaemon(const QString &address, GetMountPassInfo getPassInfo, DeviceOperateCallbackWithMessage mountResult, int secs) { auto requestLoginInfo = [address, getPassInfo] { if (getPassInfo) { QSettings setting(kSmbConfigPath, QSettings::IniFormat); return getPassInfo(QObject::tr("need authorization to access %1").arg(address), Utils::currentUser(), setting.value("global/workgroup", "WORKGROUP").toString()); } return MountPassInfo(); }; auto checkThread = [] { if (QThread::currentThread() != qApp->thread()) qWarning() << "invoking callback in non-main-thread!!!"; }; QString mpt; QString addr(QUrl::fromPercentEncoding(address.toLower().toLocal8Bit())); if (isMounted(addr, mpt)) { if (mountResult) mountResult(false, Utils::genOperateErrorInfo(DeviceError::kGIOErrorAlreadyMounted), mpt); return; } auto logins = loginPasswd(addr); MountPassInfo loginInfo; if (logins.isEmpty()) { loginInfo = requestLoginInfo(); if (loginInfo.cancelled && mountResult) { checkThread(); mountResult(false, Utils::genOperateErrorInfo(DeviceError::kUserErrorUserCancelled), ""); return; } } QFutureWatcher *watcher { new QFutureWatcher() }; QObject::connect(watcher, &QFutureWatcher::finished, [=] { auto mntRet = watcher->result(); watcher->deleteLater(); if (mntRet.requestLoginInfo) { auto loginInfo = requestLoginInfo(); loginInfo.timeout = secs; if (loginInfo.cancelled && mountResult) { checkThread(); mountResult(false, Utils::genOperateErrorInfo(DeviceError::kUserErrorUserCancelled), ""); return; } doLastMount(addr, loginInfo, mountResult); } else { if (mountResult) { checkThread(); mountResult(mntRet.ok, Utils::genOperateErrorInfo(mntRet.err), mntRet.mpt); } } }); loginInfo.timeout = secs; auto fu = QtConcurrent::run([=] { if (logins.isEmpty()) // try mount with user's input (loginInfo) return mountWithUserInput(addr, loginInfo); else return mountWithSavedInfos(addr, logins, loginInfo.timeout); }); watcher->setFuture(fu); } void DNetworkMounter::mountByGvfs(const QString &address, GetMountPassInfo getPassInfo, GetUserChoice getUserChoice, DeviceOperateCallbackWithMessage mountResult, int secs) { QUrl url(address); QString pureAddr = url.toString(); pureAddr.remove("?" + url.query()); QString mountAddr = address; if (address.startsWith("ftp") && secs > 0 && !address.contains("socket_timeout=")) { mountAddr += (url.query().isEmpty() ? QString("?socket_timeout=%1").arg(secs) // address = ftp://1.2.3.4 : QString("&socket_timeout=%1").arg(secs)); // address = ftp://1.2.3.4?charset=utf8 } qInfo() << "protocol: the mountAddress is: " << mountAddr << "and pureAddress is: " << pureAddr; GFile_autoptr file = g_file_new_for_uri(mountAddr.toStdString().c_str()); if (!file) { qWarning() << "protocol: cannot generate location for" << mountAddr; return; } AskPasswdHelper *passwdHelper = new AskPasswdHelper(); passwdHelper->callback = getPassInfo; passwdHelper->callOnceFlag = false; // make sure the signal will not emit continuously when validate failed. AskQuestionHelper *questionHelper = new AskQuestionHelper(); questionHelper->callback = getUserChoice; GMountOperation_autoptr op = g_mount_operation_new(); g_signal_connect(op, "ask_question", G_CALLBACK(DNetworkMounter::mountByGvfsAskQuestion), questionHelper); g_signal_connect(op, "ask_password", G_CALLBACK(DNetworkMounter::mountByGvfsAskPasswd), passwdHelper); FinalizeHelper *finalizeHelper = new FinalizeHelper; finalizeHelper->askPasswd = passwdHelper; finalizeHelper->askQuestion = questionHelper; finalizeHelper->resultCallback = mountResult; finalizeHelper->customData = pureAddr; GCancellable_autoptr cancellable = nullptr; /*g_cancellable_new(); if (msecs > 0) { QTimer::singleShot(msecs, [cancellable] { if (cancellable) g_cancellable_cancel(cancellable); }); }*/ g_file_mount_enclosing_volume(file, G_MOUNT_MOUNT_NONE, op, cancellable, &DNetworkMounter::mountByGvfsCallback, finalizeHelper); } void DNetworkMounter::mountByGvfsAskQuestion(GMountOperation *self, const char *message, const char **choices, gpointer userData) { auto helper = reinterpret_cast(userData); if (!helper || !helper->callback) { if (helper) helper->err = DeviceError::kUserErrorFailed; g_mount_operation_reply(self, G_MOUNT_OPERATION_ABORTED); return; } QString sMsg(message); QStringList lstChoices; while (*choices) lstChoices << QString::asprintf("%s", *choices++); int choice = helper->callback(sMsg, lstChoices); if (choice < 0 || choice >= lstChoices.count()) { g_mount_operation_reply(self, G_MOUNT_OPERATION_ABORTED); return; } g_mount_operation_set_choice(self, choice); g_mount_operation_reply(self, G_MOUNT_OPERATION_HANDLED); } void DNetworkMounter::mountByGvfsAskPasswd(GMountOperation *self, gchar *message, gchar *defaultUser, gchar *defaultDomain, GAskPasswordFlags flags, gpointer userData) { auto helper = reinterpret_cast(userData); if (!helper || !helper->callback) { if (helper) helper->err = DeviceError::kUserErrorFailed; g_mount_operation_reply(self, G_MOUNT_OPERATION_ABORTED); return; } if (!helper->callOnceFlag) { helper->callOnceFlag = true; } else { if (helper->anonymous) helper->err = DeviceError::kUserErrorNetworkAnonymousNotAllowed; else helper->err = DeviceError::kUserErrorNetworkWrongPasswd; g_mount_operation_reply(self, G_MOUNT_OPERATION_ABORTED); return; } auto mountInfo = helper->callback(message, defaultUser, defaultDomain); if (mountInfo.cancelled) { g_mount_operation_reply(self, G_MOUNT_OPERATION_ABORTED); helper->err = DeviceError::kUserErrorUserCancelled; return; } if (mountInfo.anonymous) { // the flags seem to always be 31(0b11111) if (!(flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED)) { helper->err = DeviceError::kUserErrorNetworkAnonymousNotAllowed; g_mount_operation_reply(self, G_MOUNT_OPERATION_ABORTED); return; } helper->anonymous = true; g_mount_operation_set_anonymous(self, true); } else { if (flags & G_ASK_PASSWORD_NEED_DOMAIN) g_mount_operation_set_domain(self, mountInfo.domain.toStdString().c_str()); if (flags & G_ASK_PASSWORD_NEED_USERNAME) g_mount_operation_set_username(self, mountInfo.userName.toStdString().c_str()); if (flags & G_ASK_PASSWORD_NEED_PASSWORD) g_mount_operation_set_password(self, mountInfo.passwd.toStdString().c_str()); g_mount_operation_set_password_save(self, GPasswordSave(mountInfo.savePasswd)); } g_mount_operation_reply(self, G_MOUNT_OPERATION_HANDLED); } void DNetworkMounter::mountByGvfsCallback(GObject *srcObj, GAsyncResult *res, gpointer userData) { auto finalize = reinterpret_cast(userData); if (!finalize) return; OperationErrorInfo derr = Utils::genOperateErrorInfo(finalize->askPasswd->err); auto file = reinterpret_cast(srcObj); GError_autoptr err = nullptr; bool ok = g_file_mount_enclosing_volume_finish(file, res, &err); if (!ok && derr.code == DeviceError::kNoError && err) { derr.code = Utils::castFromGError(err); derr.message = err->message; } GFile_autoptr srcFile { nullptr }; if (finalize->customData.isValid()) srcFile = g_file_new_for_uri(finalize->customData.toString().toStdString().c_str()); if (srcFile) file = srcFile; g_autofree char *mntPath = g_file_get_path(file); GError_autoptr mountErr = nullptr; GMount_autoptr mount = g_file_find_enclosing_mount(file, nullptr, &mountErr); if (mount) { GFile_autoptr defLocation = g_mount_get_default_location(mount); if (defLocation) { if (mntPath) g_free(mntPath); mntPath = g_file_get_path(defLocation); } } if (finalize->resultCallback) finalize->resultCallback(ok, derr, mntPath); delete finalize->askPasswd; delete finalize->askQuestion; delete finalize; } static QVariant preparePasswd(const QString &passwd) { if (passwd.isEmpty()) { qDebug() << "Created empty QVariant for empty passwd"; return QVariant(""); } int fd = memfd_create("DBusFD", MFD_CLOEXEC); if (fd < 0) { qCritical() << "Failed to create memfd for data transfer"; return QVariant(""); } QByteArray byteData = passwd.toUtf8(); ssize_t written = ::write(fd, byteData.constData(), byteData.size()); if (written < 0 || static_cast(byteData.size()) != written) { qCritical() << "Failed to write data to memfd"; ::close(fd); return QVariant(""); } if (lseek(fd, 0, SEEK_SET) < 0) { qCritical() << "Failed to seek memfd to beginning"; ::close(fd); return QVariant(""); } QDBusUnixFileDescriptor dbusFd; dbusFd.giveFileDescriptor(fd); return QVariant::fromValue(dbusFd); } DNetworkMounter::MountRet DNetworkMounter::mountWithUserInput(const QString &address, const MountPassInfo info) { QVariantMap param { { kLoginUser, info.userName }, { kLoginDomain, info.domain }, { kLoginPasswd, preparePasswd(info.passwd) }, { kLoginTimeout, info.timeout }, { kMountFsType, "cifs" } }; QDBusInterface mntCtrl(kDaemonService, kMountControlPath, kMountControlIFace, QDBusConnection::systemBus()); QDBusReply ret = mntCtrl.call(kMountControlMount, address, param); auto mntRet = ret.value(); QString mpt = mntRet.value(kDaemonMountRetKeyMpt).toString(); int errNum = mntRet.value(kDaemonMountRetKeyErrno).toInt(); bool ok = !mpt.isEmpty(); DeviceError err = (info.anonymous && errNum == EACCES) ? DeviceError::kUserErrorNetworkAnonymousNotAllowed : static_cast(errNum); if (ok) { err = DeviceError::kNoError; if (!info.anonymous && info.savePasswd != NetworkMountPasswdSaveMode::kNeverSavePasswd) { savePasswd(address, info); } } return { ok, err, mpt }; } DNetworkMounter::MountRet DNetworkMounter::mountWithSavedInfos(const QString &address, const QList &infos, int secs) { QDBusInterface mntCtrl(kDaemonService, kMountControlPath, kMountControlIFace, QDBusConnection::systemBus()); for (const auto &login : infos) { QVariantMap param { { kLoginUser, login.value(kSchemaUser, "") }, { kLoginDomain, login.value(kSchemaDomain, "") }, { kLoginPasswd, preparePasswd(login.value(kLoginPasswd, "").toString()) }, { kLoginTimeout, secs }, { kMountFsType, "cifs" } }; QDBusReply ret = mntCtrl.call(kMountControlMount, address, param); auto mntRet = ret.value(); QString mpt = mntRet.value(kDaemonMountRetKeyMpt).toString(); if (!mpt.isEmpty()) return { true, DeviceError::kNoError, mpt }; } // when all saved login data is tried, get info from user MountRet ret { .requestLoginInfo = true }; return ret; } void DNetworkMounter::doLastMount(const QString &address, const MountPassInfo info, DeviceOperateCallbackWithMessage cb) { QFutureWatcher *watcher { new QFutureWatcher() }; QObject::connect(watcher, &QFutureWatcher::finished, [cb, watcher] { auto mntRet = watcher->result(); watcher->deleteLater(); if (cb) { if (QThread::currentThread() != qApp->thread()) qWarning() << "invoking callback in non-main-thread!!!"; cb(mntRet.ok, Utils::genOperateErrorInfo(mntRet.err), mntRet.mpt); } }); watcher->setFuture(QtConcurrent::run([=] { return mountWithUserInput(address, info); })); } bool DNetworkMounter::isMounted(const QString &address, QString &mpt) { class Helper { public: Helper() { tab = mnt_new_table(); } ~Helper() { mnt_free_table(tab); } libmnt_table *tab { nullptr }; }; Helper d; auto tab = d.tab; int ret = mnt_table_parse_mtab(tab, nullptr); qDebug() << "parse mtab: " << ret; QString stdAddr(address); stdAddr.remove("smb:"); std::string aPath = stdAddr.toStdString(); auto fs = mnt_table_find_source(tab, aPath.c_str(), MNT_ITER_BACKWARD); if (!fs) fs = mnt_table_find_target(tab, aPath.c_str(), MNT_ITER_BACKWARD); qDebug() << "find mount: " << fs << aPath.c_str(); if (fs) { mpt = mnt_fs_get_target(fs); qDebug() << "find mounted at: " << mpt << address; QRegularExpression reg("^/(?:run/)?media/(.*)/smbmounts/"); QRegularExpressionMatch match = reg.match(mpt); if (match.hasMatch()) { auto user = match.captured(1); qDebug() << "the mounted mount is mounted by " << user << address; auto currUser = Utils::currentUser(); if (currUser == user) { return true; } else { return false; // if not mounted by current user, treat it as not mounted. } } else { return false; // if not mounted at preseted mountpoint, treat it as not mounted. } } else { return false; } } linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/dnetworkmounter.h000066400000000000000000000043261523477242100257070ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DNETWORKMOUNTER_H #define DNETWORKMOUNTER_H #include #include #include #include extern "C" { #include } DFM_MOUNT_BEGIN_NS class DNetworkMounter { public: static bool isDaemonMountEnable(); static bool isMountByDae(const QString &address); static void mountNetworkDev(const QString &, GetMountPassInfo, GetUserChoice, DeviceOperateCallbackWithMessage, int); static bool unmountNetworkDev(const QString &); static void unmountNetworkDevAsync(const QString &, DeviceOperateCallback); static bool unmountNetworkDevAsyncDetailed(const QString &mpt, int *errCode, QString *errMsg); private: static QList loginPasswd(const QString &address); static void savePasswd(const QString &address, const MountPassInfo &info); static SecretSchema *smbSchema(); static void mountByDaemon(const QString &, GetMountPassInfo, DeviceOperateCallbackWithMessage, int); static void mountByGvfs(const QString &, GetMountPassInfo, GetUserChoice, DeviceOperateCallbackWithMessage, int); static void mountByGvfsAskQuestion(GMountOperation *self, const char *message, const char **choices, gpointer userData); static void mountByGvfsAskPasswd(GMountOperation *self, gchar *message, gchar *defaultUser, gchar *defaultDomain, GAskPasswordFlags flags, gpointer userData); static void mountByGvfsCallback(GObject *srcObj, GAsyncResult *res, gpointer userData); struct MountRet { bool ok { false }; DeviceError err { DeviceError::kNoError }; QString mpt {}; bool requestLoginInfo { false }; }; static MountRet mountWithUserInput(const QString &address, const MountPassInfo info); static MountRet mountWithSavedInfos(const QString &address, const QList &infos, int secs = 0); static void doLastMount(const QString &address, const MountPassInfo info, DeviceOperateCallbackWithMessage cb); static bool isMounted(const QString &address, QString &mpt); }; DFM_MOUNT_END_NS #endif // DNETWORKMOUNTER_H linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/dprotocoldevice_p.h000066400000000000000000000054571523477242100261520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DPROTOCOLDEVICE_P_H #define DPROTOCOLDEVICE_P_H #include #include #include "private/ddevice_p.h" #include #include #include #include extern "C" { #include } DFM_MOUNT_BEGIN_NS class ASyncToSyncHelper { Q_DISABLE_COPY(ASyncToSyncHelper) public: enum { NoError, Failed, Timeout, }; explicit ASyncToSyncHelper(int timeout); ~ASyncToSyncHelper(); inline QVariant result() { return ret; } inline void setResult(QVariant result) { ret = result; } inline int exec() { timer->start(); return blocker->exec(); } inline void exit(int code) { blocker->exit(code); } inline void setTimeout(int msec) { timer->setInterval(msec); } private: QVariant ret; QEventLoop *blocker { nullptr }; QScopedPointer timer { nullptr }; }; class DProtocolDevicePrivate final : public DDevicePrivate { friend class DProtocolDevice; public: DProtocolDevicePrivate(const QString &id, GVolumeMonitor *monitor, DProtocolDevice *qq); ~DProtocolDevicePrivate(); QString path() const; QString mount(const QVariantMap &opts); void mountAsync(const QVariantMap &opts, DeviceOperateCallbackWithMessage cb); bool unmount(const QVariantMap &opts); void unmountAsync(const QVariantMap &opts, DeviceOperateCallback cb); bool rename(const QString &newName); void renameAsync(const QString &newName, const QVariantMap &opts, DeviceOperateCallback cb); QString mountPoint() const; QString fileSystem() const; long sizeTotal() const; long sizeUsage() const; long sizeFree() const; DeviceType deviceType() const; QString displayName() const; bool eject(); bool powerOff(); static QString mountPoint(GMount *mnt); enum FsAttr { Total, Usage, Free, Type }; QVariant getAttr(FsAttr type) const; private: static void mountWithBlocker(GObject *sourceObj, GAsyncResult *res, gpointer blocker); static void mountWithCallback(GObject *sourceObj, GAsyncResult *res, gpointer cbProxy); static void unmountWithBlocker(GObject *sourceObj, GAsyncResult *res, gpointer blocker); static void unmountWithCallback(GObject *sourceObj, GAsyncResult *res, gpointer cbProxy); private: QString deviceId; QStringList deviceIcons; mutable QMutex mutexForMount; GMount *mountHandler { nullptr }; GVolume *volumeHandler { nullptr }; GVolumeMonitor *volumeMonitor { nullptr }; mutable QVariantMap fsAttrs; int timeout { 25000 }; }; DFM_MOUNT_END_NS #endif // DPROTOCOLDEVICE_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-mount/private/dprotocolmonitor_p.h000066400000000000000000000044511523477242100263730ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DPROTOCOLMONITOR_P_H #define DPROTOCOLMONITOR_P_H #include #include "private/ddevicemonitor_p.h" #include #include typedef struct _GVolumeMonitor GVolumeMonitor; typedef struct _GDrive GDrive; typedef struct _GMount GMount; typedef struct _GVolume GVolume; typedef void *gpointer; DFM_MOUNT_BEGIN_NS #define DRIVE_CHANGED "drive-changed" #define DRIVE_CONNECTED "drive-connected" #define DRIVE_DISCONNED "drive-disconnected" #define MOUNT_ADDED "mount-added" #define MOUNT_CHANGED "mount-changed" #define MOUNT_PRE_UNMOUNT "mount-pre-unmount" #define MOUNT_REMOVED "mount-removed" #define VOLUME_ADDED "volume-added" #define VOLUME_CHANGED "volume-changed" #define VOLUME_REMOVED "volume-removed" class DProtocolMonitorPrivate final : public DDeviceMonitorPrivate { public: DProtocolMonitorPrivate(DProtocolMonitor *qq); ~DProtocolMonitorPrivate(); bool startMonitor() DMNT_OVERRIDE; bool stopMonitor() DMNT_OVERRIDE; DeviceType monitorObjectType() const DMNT_OVERRIDE; QStringList getDevices() DMNT_OVERRIDE; QSharedPointer createDevice(const QString &id) DMNT_OVERRIDE; QSharedPointer createDeviceByMount(); private: void initDeviceList(); static void onMountAdded(GVolumeMonitor *gVolMonitor, GMount *mount, gpointer userData); static void onMountChanged(GVolumeMonitor *gVolMonitor, GMount *mount, gpointer userData); static void onMountRemoved(GVolumeMonitor *gVolMonitor, GMount *mount, gpointer userData); static void onVolumeAdded(GVolumeMonitor *gVolMonitor, GVolume *volume, gpointer userData); static void onVolumeChanged(GVolumeMonitor *gVolMonitor, GVolume *volume, gpointer userData); static void onVolumeRemoved(GVolumeMonitor *gVolMonitor, GVolume *volume, gpointer userData); static bool hasDrive(GMount *mount); static bool hasDrive(GVolume *volume); static bool isNativeMount(const QString &mpt); static bool isMountByOther(const QString &mpt); static bool isOrphanMount(GMount *mount); QSet cachedDevices; public: GVolumeMonitor *gVolMonitor { nullptr }; }; DFM_MOUNT_END_NS #endif // DFMProtocolMONITOR_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/000077500000000000000000000000001523477242100207135ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/CMakeLists.txt000066400000000000000000000005041523477242100234520ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) #SET(CMAKE_BUILD_TYPE "Debug") SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb") SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") add_subdirectory("dfm-search-lib") add_subdirectory("dfm-search-client") linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/000077500000000000000000000000001523477242100242005ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/CMakeLists.txt000066400000000000000000000016251523477242100267440ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) project(dfm-searcher) set(SRCS main.cpp cli_options.cpp cli_options.h preview_command.cpp preview_command.h preview_output_utils.cpp preview_output_utils.h time_parser.cpp time_parser.h size_parser.cpp size_parser.h output/output_formatter.h output/text_output.cpp output/text_output.h output/json_output.cpp output/json_output.h ) find_package(Qt${QT_VERSION_MAJOR}Core REQUIRED) add_executable(${PROJECT_NAME} ${SRCS}) if(NOT DEFINED VERSION) set(VERSION "0.0.0") endif() target_compile_definitions(${PROJECT_NAME} PRIVATE VERSION="${VERSION}") include_directories( ${PROJECT_SOURCE_DIR}/../dfm-search-lib/include ) target_link_libraries( ${PROJECT_NAME} Qt${QT_VERSION_MAJOR}::Core dfm${DFM_VERSION_MAJOR}-search ) install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/README.md000066400000000000000000000102321523477242100254550ustar00rootroot00000000000000# File Search Utilities A comprehensive file search utility for Deepin File Manager that provides high-performance search capabilities using Lucene++ indexing. ## Features - **Filename search**: Search files by their names with various search methods: - Simple search - Wildcard search (using * and ?) - Boolean search (multiple keywords with AND/OR operators) - Pinyin search (for Chinese characters) - File type filtering - File extension filtering - Combined search (keyword + type/extension/pinyin) - **Content search**: Search within file contents with features like: - Full-text search - Result highlighting - Preview of matching content - **OCR search**: Search text extracted from images via OCR: - Search text in image files (PNG, JPG, etc.) - Result highlighting - Preview of matching OCR text ## Search Methods - **Indexed search**: Fast search using pre-built Lucene indexes - **Realtime search**: On-the-fly search that doesn't require indexing ## Usage ### Command Line Interface ```bash dfm6-search-client [options] ``` ### Options - `--type=`: Search type (default: filename) - `--method=`: Search method (default: indexed) - `--query=`: Query type (default: simple) - `--case-sensitive`: Enable case sensitivity - `--include-hidden`: Include hidden files - `--pinyin`: Enable pinyin search (for filename search) - `--file-types=`: Filter by file types, comma separated - `--file-extensions=`: Filter by file extensions, comma separated - `--max-results=`: Maximum number of results (0 for unlimited, default: 0) - `--max-preview=`: Max content preview length (for content/ocr search) ### Examples #### Basic filename search ```bash dfm6-search-client "document" /home/user ``` #### Content search ```bash dfm6-search-client --type=content "hello world" /home/user/Documents ``` #### Realtime search ```bash dfm6-search-client --method=realtime "report" /home/user ``` #### Case-sensitive search ```bash dfm6-search-client --case-sensitive "README" /home/user ``` #### File type filtering ```bash dfm6-search-client --file-types=doc,pic "" /home/user ``` #### File extension filtering ```bash dfm6-search-client --file-extensions=txt,pdf "" /home/user ``` #### Combining file types and extensions ```bash dfm6-search-client --file-types=doc --file-extensions=docx,odt "report" /home/user ``` #### Boolean search ```bash dfm6-search-client --query=boolean "meeting,notes,2023" /home/user/Documents ``` #### Combined search examples ```bash # Combine keywords with file types dfm6-search-client --file-types="dir,doc" --query=boolean "dde,file" / # Combine keywords with pinyin dfm6-search-client --pinyin --query=boolean "wendang,xinjian" / # Combine keywords with file types and pinyin dfm6-search-client --pinyin --file-types="doc,pic" --query=boolean "wen,dang" / # Combine keywords with file extensions dfm6-search-client --file-extensions="txt,pdf" --query=boolean "report,data" / ``` ## Supported File Types - `app`: Application files - `archive`: Archive files - `audio`: Audio files - `doc`: Document files - `pic`: Picture files - `video`: Video files - `dir`: Directories/folders - `other`: Other file types ## File Extension Support The `file-extensions` option allows filtering by specific file extensions (without the dot): - Example: `txt`, `pdf`, `docx`, etc. ## Implementation Details The file search functionality uses Lucene++ for indexing and searching, providing: - High-performance search capabilities - Support for complex queries - Support for Chinese characters through pinyin - Multiple filter options (file types, extensions) The implementation follows SOLID principles with a clean separation of concerns: - Strategy pattern for different search methods - Dependency injection for flexible configuration - Single Responsibility Principle with specialized classes - Open/Closed Principle through extensible interfaces ## Development To extend or modify this utility: 1. Clone the repository 2. Build using CMake 3. Run tests to ensure functionality 4. Implement new features following the established patterns ## License GPL-3.0-or-later linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/cli_options.cpp000066400000000000000000000713111523477242100272310ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "cli_options.h" #include "time_parser.h" #include "size_parser.h" #include #include #include #include #include #include #include using namespace dfmsearch; using namespace std; CliOptions::CliOptions() : m_typeOption(QStringList() << "type", "Search type (filename, content or ocr)", "type", "filename"), m_methodOption(QStringList() << "method", "Search method (indexed or realtime)", "method", "indexed"), m_queryOption(QStringList() << "query", "Query type (simple, boolean or wildcard)", "query", "simple"), m_caseSensitiveOption(QStringList() << "case-sensitive", "Enable case sensitivity"), m_includeHiddenOption(QStringList() << "include-hidden", "Include hidden files"), m_pinyinOption(QStringList() << "pinyin", "Enable pinyin search (for filename search)"), m_pinyinAcronymOption(QStringList() << "pinyin-acronym", "Enable pinyin acronym search (for filename search)"), m_fileTypesOption(QStringList() << "file-types", "Filter by file types, comma separated", "types"), m_fileExtensionsOption(QStringList() << "file-extensions", "Filter by file extensions, comma separated", "extensions"), m_excludeOption(QStringList() << "exclude", "Exclude directories from search, comma separated", "paths"), m_maxResultsOption(QStringList() << "max-results", "Maximum number of results (0 for unlimited)", "number", "0"), m_maxPreviewOption(QStringList() << "max-preview", "Max content preview length", "length", "200"), m_offsetOption(QStringList() << "offset", "Content offset: start reading from the n-th character (preview only)", "n", "0"), m_filenameOption(QStringList() << "filename", "Search by filename in content/ocr index", "keyword"), m_wildcardOption(QStringList() << "wildcard", "Enable wildcard search with * and ? patterns"), m_jsonOption(QStringList() << "json" << "j", "Output results in JSON format"), m_verboseOption(QStringList() << "verbose" << "v", "Enable verbose output with detailed result information"), m_semanticOption(QStringList() << "semantic" << "s", "Enable semantic natural language search"), m_timeFieldOption(QStringList() << "time-field", "Time field to filter (birth or modify)", "field", "modify"), m_timeLastOption(QStringList() << "time-last", "Rolling time window (e.g., 3d, 2h, 30m)", "duration"), m_timeTodayOption(QStringList() << "time-today", "Filter files from today"), m_timeYesterdayOption(QStringList() << "time-yesterday", "Filter files from yesterday"), m_timeThisWeekOption(QStringList() << "time-this-week", "Filter files from this week"), m_timeLastWeekOption(QStringList() << "time-last-week", "Filter files from last week"), m_timeThisMonthOption(QStringList() << "time-this-month", "Filter files from this month"), m_timeLastMonthOption(QStringList() << "time-last-month", "Filter files from last month"), m_timeThisYearOption(QStringList() << "time-this-year", "Filter files from this year"), m_timeLastYearOption(QStringList() << "time-last-year", "Filter files from last year"), m_timeRangeOption(QStringList() << "time-range", "Custom time range (start,end)", "range"), m_sizeMinOption(QStringList() << "size-min", "Minimum file size (e.g., 1K, 10M, 1G, 512)", "size"), m_sizeMaxOption(QStringList() << "size-max", "Maximum file size (e.g., 1K, 10M, 1G, 512)", "size"), m_versionOption(QStringList() << "version", "Show version information and exit"), m_featuresOption(QStringList() << "features", "List supported features and exit") { setupOptions(); } QStringList CliOptions::supportedFeatures() { // 静态特性列表(编译时确定,26 项) // Search types return { "filename", "content", "ocr", "recent", "semantic", // Search methods "indexed", "realtime", // Query types "simple", "boolean", "wildcard", // Functional features "preview", "pinyin", "pinyin-acronym", "case-sensitive", "include-hidden", "file-types", "file-extensions", "exclude", "time-filter", "size-filter", "max-results", "max-preview", "offset", "filename-in-content", "json-output", "verbose" }; } void CliOptions::setupOptions() { m_parser.setApplicationDescription("DFM Search Client"); m_parser.addHelpOption(); m_parser.addOption(m_versionOption); m_parser.addOption(m_featuresOption); // Basic options m_parser.addOption(m_typeOption); m_parser.addOption(m_methodOption); m_parser.addOption(m_queryOption); m_parser.addOption(m_caseSensitiveOption); m_parser.addOption(m_includeHiddenOption); m_parser.addOption(m_pinyinOption); m_parser.addOption(m_pinyinAcronymOption); m_parser.addOption(m_fileTypesOption); m_parser.addOption(m_fileExtensionsOption); m_parser.addOption(m_excludeOption); m_parser.addOption(m_maxResultsOption); m_parser.addOption(m_maxPreviewOption); m_parser.addOption(m_offsetOption); m_parser.addOption(m_filenameOption); m_parser.addOption(m_wildcardOption); m_parser.addOption(m_jsonOption); m_parser.addOption(m_verboseOption); m_parser.addOption(m_semanticOption); // Time range filtering options m_parser.addOption(m_timeFieldOption); m_parser.addOption(m_timeLastOption); m_parser.addOption(m_timeTodayOption); m_parser.addOption(m_timeYesterdayOption); m_parser.addOption(m_timeThisWeekOption); m_parser.addOption(m_timeLastWeekOption); m_parser.addOption(m_timeThisMonthOption); m_parser.addOption(m_timeLastMonthOption); m_parser.addOption(m_timeThisYearOption); m_parser.addOption(m_timeLastYearOption); m_parser.addOption(m_timeRangeOption); // File size range filtering options m_parser.addOption(m_sizeMinOption); m_parser.addOption(m_sizeMaxOption); // Positional arguments m_parser.addPositionalArgument("keyword", "Search keyword"); m_parser.addPositionalArgument("search_path", "Path to search in"); } void CliOptions::printHelp() const { std::cout << "Usage: dfm-searcher [options] [search_path]" << std::endl; std::cout << std::endl; std::cout << "Semantic Search:" << std::endl; std::cout << " --semantic, -s Enable semantic natural language search" << std::endl; std::cout << " Example: dfm-searcher -s \"recent 3 days images\" /home/user" << std::endl; std::cout << std::endl; std::cout << "Search Types:" << std::endl; std::cout << " --type= Search type (default: filename)" << std::endl; std::cout << " filename: Search by file name" << std::endl; std::cout << " content: Search by file content" << std::endl; std::cout << " ocr: Search by OCR text in images" << std::endl; std::cout << std::endl; std::cout << "Search Options:" << std::endl; std::cout << " --method= Search method (default: indexed)" << std::endl; std::cout << " --query= Query type (default: simple)" << std::endl; std::cout << " boolean: Separate keywords with | for OR, & or , for AND" << std::endl; std::cout << " --wildcard Enable wildcard search with * and ? patterns" << std::endl; std::cout << " --case-sensitive Enable case sensitivity" << std::endl; std::cout << " --include-hidden Include hidden files" << std::endl; std::cout << " --pinyin Enable pinyin search (for filename search)" << std::endl; std::cout << " --pinyin-acronym Enable pinyin acronym search (for filename search)" << std::endl; std::cout << " --file-types= Filter by file types, comma separated" << std::endl; std::cout << " --file-extensions= Filter by file extensions, comma separated" << std::endl; std::cout << " --exclude= Exclude directories from search, comma separated" << std::endl; std::cout << " --max-results= Maximum number of results (0 for unlimited)" << std::endl; std::cout << " --max-preview= Max content preview length (for content/ocr search)" << std::endl; std::cout << " --offset= Content offset: start reading from the n-th character (preview only, default 0)" << std::endl; std::cout << " --filename= Search by filename in content/ocr index" << std::endl; std::cout << std::endl; std::cout << "Time Range Filter Options:" << std::endl; std::cout << " --time-field= Time field to filter (birth=creation, modify=modification)" << std::endl; std::cout << " --time-last= Rolling time window: N units ago to now" << std::endl; std::cout << " Units: m=minutes, h=hours, d=days, w=weeks, M=months, y=years" << std::endl; std::cout << " Examples: --time-last=3d (last 3 days), --time-last=2h (last 2 hours)" << std::endl; std::cout << " --time-today Filter files from today" << std::endl; std::cout << " --time-yesterday Filter files from yesterday" << std::endl; std::cout << " --time-this-week Filter files from this week" << std::endl; std::cout << " --time-last-week Filter files from last week" << std::endl; std::cout << " --time-this-month Filter files from this month" << std::endl; std::cout << " --time-last-month Filter files from last month" << std::endl; std::cout << " --time-this-year Filter files from this year" << std::endl; std::cout << " --time-last-year Filter files from last year" << std::endl; std::cout << " --time-range=, Custom time range (format: YYYY-MM-DD or \"YYYY-MM-DD HH:MM\")" << std::endl; std::cout << " Example: --time-range=\"2025-01-01,2025-12-31\"" << std::endl; std::cout << std::endl; std::cout << "File Size Range Filter Options:" << std::endl; std::cout << " --size-min= Minimum file size (e.g., 1K, 10M, 1G, 512)" << std::endl; std::cout << " Units: K=KB, M=MB, G=GB, T=TB (default: bytes)" << std::endl; std::cout << " --size-max= Maximum file size (e.g., 1K, 10M, 1G, 512)" << std::endl; std::cout << " Units: K=KB, M=MB, G=GB, T=TB (default: bytes)" << std::endl; std::cout << " Example: --size-min=1M --size-max=100M" << std::endl; std::cout << std::endl; std::cout << "Output Options:" << std::endl; std::cout << " --json, -j Output results in JSON format" << std::endl; std::cout << " --verbose, -v Enable verbose output with detailed result information" << std::endl; std::cout << " --features List supported features and exit" << std::endl; std::cout << " --help Display this help" << std::endl; std::cout << " --version Show version information and exit" << std::endl; std::cout << std::endl; std::cout << "Examples:" << std::endl; std::cout << " # Basic filename search" << std::endl; std::cout << " dfm-searcher \"document\" /home/user" << std::endl; std::cout << std::endl; std::cout << " # Content search" << std::endl; std::cout << " dfm-searcher --type=content \"hello world\" /home/user/Documents" << std::endl; std::cout << std::endl; std::cout << " # OCR search in images" << std::endl; std::cout << " dfm-searcher --type=ocr \"screenshot\" /home/user/Pictures" << std::endl; std::cout << std::endl; std::cout << " # Realtime search with time filter" << std::endl; std::cout << " dfm-searcher --method=realtime --time-last=7d \"report\" /home/user" << std::endl; std::cout << std::endl; std::cout << " # Semantic search: find recent images" << std::endl; std::cout << " dfm-searcher --semantic \"recent 3 days images\" /home/user" << std::endl; std::cout << std::endl; std::cout << " # Semantic search with JSON output" << std::endl; std::cout << " dfm-searcher -s -j \"content contains meeting notes\" /home/user" << std::endl; std::cout << std::endl; std::cout << " # Filename search with file size filter (1MB to 100MB)" << std::endl; std::cout << " dfm-searcher --size-min=1M --size-max=100M \"video\" /home/user" << std::endl; std::cout << std::endl; std::cout << "Content Preview (on-demand):" << std::endl; std::cout << " dfm-searcher preview [] [path2 ...] [--type=] [--offset=] [--max-preview=] [-j]" << std::endl; std::cout << " Fetch content snippets for specific files without running a full search." << std::endl; std::cout << " With keyword: returns snippet from the keyword match position (search starts at --offset)." << std::endl; std::cout << " Without keyword: returns exact content starting from --offset (full content if --max-preview is omitted)." << std::endl; std::cout << " Search type is auto-detected by file extension; use --type to force a specific index." << std::endl; std::cout << std::endl; std::cout << " # Fetch highlighted snippet for a single file" << std::endl; std::cout << " dfm-searcher preview \"hello\" /home/user/doc.txt" << std::endl; std::cout << std::endl; std::cout << " # Fetch full content (no keyword)" << std::endl; std::cout << " dfm-searcher preview /home/user/doc.txt" << std::endl; std::cout << std::endl; std::cout << " # Read content from offset 100, 50 chars max" << std::endl; std::cout << " dfm-searcher preview /home/user/doc.txt --offset=100 --max-preview=50 -j" << std::endl; std::cout << std::endl; std::cout << " # Search keyword from offset 150" << std::endl; std::cout << " dfm-searcher preview \"keyword\" /home/user/doc.txt --offset=150 -j" << std::endl; std::cout << std::endl; std::cout << " # Batch fetch highlighted snippets with JSON output" << std::endl; std::cout << " dfm-searcher preview \"screenshot\" img1.png img2.png -j" << std::endl; std::cout << std::endl; std::cout << " # Force a specific search type" << std::endl; std::cout << " dfm-searcher preview --type=ocr \"screenshot\" photo.png" << std::endl; } bool CliOptions::parse(QCoreApplication &app, SearchCliConfig &config) { // Pre-scan for "preview" subcommand const QStringList rawArgs = app.arguments(); // Intercept -h/--help to show custom help (includes preview subcommand docs) if (rawArgs.contains("-h") || rawArgs.contains("--help")) { printHelp(); return false; } if (rawArgs.size() >= 2 && rawArgs.at(1) == "preview") { config.subcommand = "preview"; } m_parser.process(app); if (m_parser.isSet(m_versionOption)) { QString name = QCoreApplication::applicationName().isEmpty() ? QStringLiteral("dfm-searcher") : QCoreApplication::applicationName(); std::cout << name.toStdString() << " " << QCoreApplication::applicationVersion().toStdString() << std::endl; std::exit(0); } if (m_parser.isSet(m_featuresOption)) { // 静态特性列表(编译时确定) static const QStringList kFeatures = supportedFeatures(); if (m_parser.isSet(m_jsonOption)) { // JSON output QJsonObject root; root["type"] = QStringLiteral("features"); QJsonArray featuresArray; for (const QString &feature : kFeatures) { featuresArray.append(feature); } root["features"] = featuresArray; root["count"] = kFeatures.size(); QJsonDocument doc(root); std::cout << doc.toJson(QJsonDocument::Indented).toStdString() << std::endl; } else { // Space-separated text output std::cout << kFeatures.join(' ').toStdString() << std::endl; } std::exit(0); } QStringList positionalArgs = m_parser.positionalArguments(); // For preview subcommand, the positional args are: [] [path2 ...] // "preview" itself is consumed as first positional by QCommandLineParser if (config.subcommand == "preview") { // Skip "preview" keyword from positional args (it was parsed as the first positional) QStringList args = positionalArgs; if (!args.isEmpty() && args.first() == "preview") { args.removeFirst(); } if (args.isEmpty()) { std::cerr << "Error: preview requires at least one " << std::endl; return false; } // If first arg is an existing file, treat all args as paths (no-keyword mode). // Otherwise first arg is the keyword and the rest are paths. if (QFileInfo::exists(args.first())) { config.keyword.clear(); config.searchPath = args.join(','); } else { config.keyword = args.first(); config.searchPath = args.mid(1).join(','); if (config.searchPath.isEmpty()) { std::cerr << "Error: preview with keyword requires at least one " << std::endl; return false; } } // Search type: default Semantic (auto-detect by extension), optional override QString typeStr = m_parser.value(m_typeOption); if (typeStr == "content") { config.searchType = SearchType::Content; } else if (typeStr == "ocr") { config.searchType = SearchType::Ocr; } else { config.searchType = SearchType::Semantic; } config.jsonOutput = m_parser.isSet(m_jsonOption); if (m_parser.isSet(m_maxPreviewOption)) { bool ok; int previewLength = m_parser.value(m_maxPreviewOption).toInt(&ok); if (ok && previewLength > 0) { config.maxPreviewLength = previewLength; } config.maxPreviewSet = true; } if (m_parser.isSet(m_offsetOption)) { bool ok; int offsetVal = m_parser.value(m_offsetOption).toInt(&ok); if (ok && offsetVal >= 0) { config.offset = offsetVal; } } return true; } if (positionalArgs.isEmpty()) { printHelp(); return false; } // Semantic mode: only keyword is required, search path is optional config.semanticMode = m_parser.isSet(m_semanticOption); config.keyword = positionalArgs.at(0); if (positionalArgs.size() >= 2) { config.searchPath = positionalArgs.at(1); } // Validate search path (not required in semantic mode) if (!config.searchPath.isEmpty()) { QFileInfo pathInfo(config.searchPath); if (!pathInfo.exists() || !pathInfo.isDir()) { std::cerr << "Error: Search path does not exist or is not a directory" << std::endl; return false; } } else if (!config.semanticMode) { std::cerr << "Error: Search path is required" << std::endl; printHelp(); return false; } // Auto-enable includeHidden when search path contains hidden directory components. // User who explicitly specifies a hidden path (e.g. ~/.local/share/Trash) // expects results without needing --include-hidden. config.includeHidden = m_parser.isSet(m_includeHiddenOption); if (!config.includeHidden && !config.searchPath.isEmpty() && Global::isHiddenPathOrInHiddenDir(config.searchPath)) { config.includeHidden = true; } // In semantic mode, skip type/method/query parsing if (config.semanticMode) { config.jsonOutput = m_parser.isSet(m_jsonOption); config.verbose = m_parser.isSet(m_verboseOption); if (m_parser.isSet(m_maxPreviewOption)) { bool ok; int previewLength = m_parser.value(m_maxPreviewOption).toInt(&ok); if (ok && previewLength > 0) { config.maxPreviewLength = previewLength; } } if (m_parser.isSet(m_maxResultsOption)) { bool ok; int maxResults = m_parser.value(m_maxResultsOption).toInt(&ok); if (ok && maxResults >= 0) { config.maxResults = maxResults; } } if (m_parser.isSet(m_excludeOption)) { #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) config.excludedPaths = m_parser.value(m_excludeOption).split(',', Qt::SkipEmptyParts); #else config.excludedPaths = m_parser.value(m_excludeOption).split(',', QString::SkipEmptyParts); #endif } return true; } // Parse search type (non-semantic mode only) QString typeStr = m_parser.value(m_typeOption); if (typeStr == "content") { config.searchType = SearchType::Content; } else if (typeStr == "ocr") { config.searchType = SearchType::Ocr; } else if (typeStr != "filename") { std::cerr << "Error: Invalid search type. Use 'filename', 'content', or 'ocr'" << std::endl; return false; } // 解析搜索方法 QString methodStr = m_parser.value(m_methodOption); if (methodStr == "realtime") { config.searchMethod = SearchMethod::Realtime; } else if (methodStr != "indexed") { std::cerr << "Error: Invalid search method. Use 'indexed' or 'realtime'" << std::endl; return false; } // 解析查询类型 QString queryStr = m_parser.value(m_queryOption); if (queryStr == "boolean") { config.queryType = SearchQuery::Type::Boolean; } else if (queryStr == "wildcard" || m_parser.isSet(m_wildcardOption)) { config.queryType = SearchQuery::Type::Wildcard; } else if (queryStr != "simple") { std::cerr << "Error: Invalid query type. Use 'simple', 'boolean', or 'wildcard'" << std::endl; return false; } // 解析开关选项 config.caseSensitive = m_parser.isSet(m_caseSensitiveOption); config.pinyinEnabled = m_parser.isSet(m_pinyinOption); config.pinyinAcronymEnabled = m_parser.isSet(m_pinyinAcronymOption); config.jsonOutput = m_parser.isSet(m_jsonOption); config.verbose = m_parser.isSet(m_verboseOption); // 解析过滤选项 if (m_parser.isSet(m_fileTypesOption)) { #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) config.fileTypes = m_parser.value(m_fileTypesOption).split(',', Qt::SkipEmptyParts); #else config.fileTypes = m_parser.value(m_fileTypesOption).split(',', QString::SkipEmptyParts); #endif } if (m_parser.isSet(m_fileExtensionsOption)) { #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) config.fileExtensions = m_parser.value(m_fileExtensionsOption).split(',', Qt::SkipEmptyParts); #else config.fileExtensions = m_parser.value(m_fileExtensionsOption).split(',', QString::SkipEmptyParts); #endif } if (m_parser.isSet(m_excludeOption)) { #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) config.excludedPaths = m_parser.value(m_excludeOption).split(',', Qt::SkipEmptyParts); #else config.excludedPaths = m_parser.value(m_excludeOption).split(',', QString::SkipEmptyParts); #endif } // 解析文件名搜索选项(仅对 content/ocr 搜索有效) if (m_parser.isSet(m_filenameOption)) { config.filenameKeyword = m_parser.value(m_filenameOption); } // 解析数值选项 if (m_parser.isSet(m_maxResultsOption)) { bool ok; int maxResults = m_parser.value(m_maxResultsOption).toInt(&ok); if (ok && maxResults >= 0) { config.maxResults = maxResults; } } if (m_parser.isSet(m_maxPreviewOption)) { bool ok; int previewLength = m_parser.value(m_maxPreviewOption).toInt(&ok); if (ok && previewLength > 0) { config.maxPreviewLength = previewLength; } } // 解析时间范围选项 if (!parseTimeOptions(config)) { return false; } // 解析文件大小范围选项 if (m_parser.isSet(m_sizeMinOption)) { qint64 minBytes = 0; if (SizeParser::parseSize(m_parser.value(m_sizeMinOption), minBytes) && minBytes > 0) { config.sizeFilter.setMin(minBytes); config.hasSizeFilter = true; } else { std::cerr << "Error: Invalid --size-min format. Use format like '1K', '10M', '1G', or '512'" << std::endl; return false; } } if (m_parser.isSet(m_sizeMaxOption)) { qint64 maxBytes = 0; if (SizeParser::parseSize(m_parser.value(m_sizeMaxOption), maxBytes) && maxBytes > 0) { config.sizeFilter.setMax(maxBytes); config.hasSizeFilter = true; } else { std::cerr << "Error: Invalid --size-max format. Use format like '1K', '10M', '1G', or '512'" << std::endl; return false; } } // Validate option compatibility with search type if (config.searchType == SearchType::FileName) { if (m_parser.isSet(m_filenameOption)) { std::cerr << "Error: --filename is only valid for content/ocr search, not filename search" << std::endl; return false; } } else { if (m_parser.isSet(m_pinyinOption)) { std::cerr << "Error: --pinyin is only valid for filename search" << std::endl; return false; } if (m_parser.isSet(m_pinyinAcronymOption)) { std::cerr << "Error: --pinyin-acronym is only valid for filename search" << std::endl; return false; } if (m_parser.isSet(m_fileTypesOption)) { std::cerr << "Error: --file-types is only valid for filename search" << std::endl; return false; } } return true; } bool CliOptions::parseTimeOptions(SearchCliConfig &config) { // 设置时间字段 QString timeFieldStr = m_parser.value(m_timeFieldOption); if (timeFieldStr == "birth") { config.timeFilter.setTimeField(DFMSEARCH::TimeField::BirthTime); } else if (timeFieldStr == "modify") { config.timeFilter.setTimeField(DFMSEARCH::TimeField::ModifyTime); } else { std::cerr << "Error: Invalid time field. Use 'birth' or 'modify'" << std::endl; return false; } // 处理时间范围选项(只能激活一个) if (m_parser.isSet(m_timeLastOption)) { QString lastArg = m_parser.value(m_timeLastOption); int value; DFMSEARCH::TimeUnit unit; if (TimeParser::parseTimeLast(lastArg, value, unit)) { config.timeFilter.setLast(value, unit); config.hasTimeFilter = true; } else { std::cerr << "Error: Invalid --time-last format. Use format like '3d', '2h', '30m'" << std::endl; return false; } } else if (m_parser.isSet(m_timeTodayOption)) { config.timeFilter.setToday(); config.hasTimeFilter = true; } else if (m_parser.isSet(m_timeYesterdayOption)) { config.timeFilter.setYesterday(); config.hasTimeFilter = true; } else if (m_parser.isSet(m_timeThisWeekOption)) { config.timeFilter.setThisWeek(); config.hasTimeFilter = true; } else if (m_parser.isSet(m_timeLastWeekOption)) { config.timeFilter.setLastWeek(); config.hasTimeFilter = true; } else if (m_parser.isSet(m_timeThisMonthOption)) { config.timeFilter.setThisMonth(); config.hasTimeFilter = true; } else if (m_parser.isSet(m_timeLastMonthOption)) { config.timeFilter.setLastMonth(); config.hasTimeFilter = true; } else if (m_parser.isSet(m_timeThisYearOption)) { config.timeFilter.setThisYear(); config.hasTimeFilter = true; } else if (m_parser.isSet(m_timeLastYearOption)) { config.timeFilter.setLastYear(); config.hasTimeFilter = true; } else if (m_parser.isSet(m_timeRangeOption)) { QString rangeArg = m_parser.value(m_timeRangeOption); QDateTime start, end; if (TimeParser::parseTimeRange(rangeArg, start, end)) { config.timeFilter.setRange(start, end); config.hasTimeFilter = true; } else { std::cerr << "Error: Invalid --time-range format. Use format 'YYYY-MM-DD,YYYY-MM-DD' or 'YYYY-MM-DD HH:MM,YYYY-MM-DD HH:MM'" << std::endl; return false; } } return true; } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/cli_options.h000066400000000000000000000105201523477242100266710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CLI_OPTIONS_H #define CLI_OPTIONS_H #include #include #include #include #include #include #include namespace dfmsearch { /** * @brief 命令行搜索参数配置 * * 封装所有命令行解析结果,遵循单一职责原则 */ struct SearchCliConfig { // Subcommand: if non-empty, the first positional arg is treated as a subcommand QString subcommand; // "" (search), "preview" // Basic parameters QString keyword; QString searchPath; SearchType searchType = SearchType::FileName; SearchMethod searchMethod = SearchMethod::Indexed; SearchQuery::Type queryType = SearchQuery::Type::Simple; // 开关选项 bool caseSensitive = false; bool includeHidden = false; bool pinyinEnabled = false; bool pinyinAcronymEnabled = false; bool wildcardEnabled = false; bool jsonOutput = false; bool verbose = false; // 详细输出模式 // 过滤选项 QStringList fileTypes; QStringList fileExtensions; QStringList excludedPaths; // 排除的搜索路径 int maxResults = 0; // 0 表示不限制 int maxPreviewLength = 200; int offset = 0; // 内容偏移值,preview 子命令使用 bool maxPreviewSet = false; // --max-preview 是否被显式设置(preview 子命令使用) // 文件名搜索选项 QString filenameKeyword; // Semantic mode bool semanticMode = false; // Time range filtering bool hasTimeFilter = false; DFMSEARCH::TimeRangeFilter timeFilter; // File size range filtering bool hasSizeFilter = false; DFMSEARCH::SizeRangeFilter sizeFilter; }; /** * @brief 命令行选项管理器 * * 负责定义和解析命令行参数,遵循单一职责原则 */ class CliOptions { public: CliOptions(); ~CliOptions() = default; /** * @brief 解析命令行参数 * @param app QCoreApplication实例 * @param config 输出的配置结构 * @return 解析成功返回true,失败返回false */ bool parse(QCoreApplication &app, SearchCliConfig &config); /** * @brief 打印帮助信息 */ void printHelp() const; /** * @brief 返回 --features 输出的静态特性列表(编译时确定,26 项) * * 提取为静态方法以便单元测试验证列表完整性,无需启动 CLI 进程。 */ static QStringList supportedFeatures(); private: void setupOptions(); bool validateConfig(SearchCliConfig &config); bool parseTimeOptions(SearchCliConfig &config); private: QCommandLineParser m_parser; // 基本选项 QCommandLineOption m_typeOption; QCommandLineOption m_methodOption; QCommandLineOption m_queryOption; QCommandLineOption m_caseSensitiveOption; QCommandLineOption m_includeHiddenOption; QCommandLineOption m_pinyinOption; QCommandLineOption m_pinyinAcronymOption; QCommandLineOption m_fileTypesOption; QCommandLineOption m_fileExtensionsOption; QCommandLineOption m_excludeOption; QCommandLineOption m_maxResultsOption; QCommandLineOption m_maxPreviewOption; QCommandLineOption m_offsetOption; QCommandLineOption m_filenameOption; QCommandLineOption m_wildcardOption; QCommandLineOption m_jsonOption; QCommandLineOption m_verboseOption; QCommandLineOption m_semanticOption; // Time range filtering options QCommandLineOption m_timeFieldOption; QCommandLineOption m_timeLastOption; QCommandLineOption m_timeTodayOption; QCommandLineOption m_timeYesterdayOption; QCommandLineOption m_timeThisWeekOption; QCommandLineOption m_timeLastWeekOption; QCommandLineOption m_timeThisMonthOption; QCommandLineOption m_timeLastMonthOption; QCommandLineOption m_timeThisYearOption; QCommandLineOption m_timeLastYearOption; QCommandLineOption m_timeRangeOption; // File size range filtering options QCommandLineOption m_sizeMinOption; QCommandLineOption m_sizeMaxOption; QCommandLineOption m_versionOption; QCommandLineOption m_featuresOption; }; } // namespace dfmsearch #endif // CLI_OPTIONS_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/main.cpp000066400000000000000000000266511523477242100256420ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include "cli_options.h" #include "preview_command.h" #include "output/text_output.h" #include "output/json_output.h" #include #ifndef VERSION #define VERSION "unknown" #endif using namespace dfmsearch; /** * @brief 配置搜索引擎选项 */ static void configureSearchOptions(SearchOptions &options, const SearchCliConfig &config) { options.setSearchMethod(config.searchMethod); options.setCaseSensitive(config.caseSensitive); options.setIncludeHidden(config.includeHidden); options.setSearchPath(config.searchPath); options.setMaxResults(config.maxResults); options.setDetailedResultsEnabled(config.verbose); // 使用 verbose 选项控制详细输出 if (!config.excludedPaths.isEmpty()) { options.setSearchExcludedPaths(config.excludedPaths); } if (config.searchMethod == SearchMethod::Realtime) { options.setResultFoundEnabled(true); } // 配置类型特定选项 if (config.searchType == SearchType::FileName) { FileNameOptionsAPI fileNameOptions(options); fileNameOptions.setPinyinEnabled(config.pinyinEnabled); fileNameOptions.setPinyinAcronymEnabled(config.pinyinAcronymEnabled); if (!config.fileTypes.isEmpty()) { fileNameOptions.setFileTypes(config.fileTypes); } if (!config.fileExtensions.isEmpty()) { fileNameOptions.setFileExtensions(config.fileExtensions); } } else if (config.searchType == SearchType::Content) { ContentOptionsAPI contentOptions(options); contentOptions.setMaxPreviewLength(config.maxPreviewLength); contentOptions.setFullTextRetrievalEnabled(config.verbose); contentOptions.setSearchResultHighlightEnabled(config.verbose); contentOptions.setFilenameContentMixedAndSearchEnabled(true); if (!config.filenameKeyword.isEmpty()) { contentOptions.setFilenameKeyword(config.filenameKeyword); } if (!config.fileExtensions.isEmpty()) { contentOptions.setFileExtensions(config.fileExtensions); } } else if (config.searchType == SearchType::Ocr) { OcrTextOptionsAPI ocrTextOptions(options); ocrTextOptions.setMaxPreviewLength(config.maxPreviewLength); ocrTextOptions.setFullTextRetrievalEnabled(config.verbose); ocrTextOptions.setSearchResultHighlightEnabled(config.verbose); ocrTextOptions.setFilenameOcrContentMixedAndSearchEnabled(true); if (!config.filenameKeyword.isEmpty()) { ocrTextOptions.setFilenameKeyword(config.filenameKeyword); } if (!config.fileExtensions.isEmpty()) { ocrTextOptions.setFileExtensions(config.fileExtensions); } } // 应用时间范围过滤 if (config.hasTimeFilter) { options.setTimeRangeFilter(config.timeFilter); } // 应用文件大小范围过滤 if (config.hasSizeFilter) { options.setSizeRangeFilter(config.sizeFilter); } } /** * @brief 创建输出格式化器 */ static OutputFormatter *createOutputFormatter(const SearchCliConfig &config, QObject *parent) { if (config.jsonOutput) { // JSON 输出:实时搜索使用流式,索引搜索使用完整输出 bool streaming = (config.searchMethod == SearchMethod::Realtime); return new JsonOutput(streaming, parent); } return new TextOutput(parent); } /** * @brief 连接搜索引擎信号到输出格式化器 */ static void connectSignals(SearchEngine *engine, OutputFormatter *formatter, const SearchCliConfig &config, QCoreApplication &app) { // 连接 finished 信号以退出应用 QObject::connect(formatter, &OutputFormatter::finished, &app, &QCoreApplication::quit); // 搜索开始 QObject::connect(engine, &SearchEngine::searchStarted, [formatter]() { formatter->outputSearchStarted(); }); // 结果到达 QObject::connect(engine, &SearchEngine::resultsFound, [formatter](const SearchResultList &results) { for (const auto &result : results) { formatter->outputResult(result); } }); // 搜索完成 QObject::connect(engine, &SearchEngine::searchFinished, [formatter](const QList &results) { formatter->outputSearchFinished(results); }); // 搜索取消 QObject::connect(engine, &SearchEngine::searchCancelled, [formatter]() { formatter->outputSearchCancelled(); }); // 错误处理 QObject::connect(engine, &SearchEngine::errorOccurred, [formatter](const DFMSEARCH::SearchError &error) { formatter->outputError(error); }); } /** * @brief 创建搜索查询 */ static SearchQuery createSearchQuery(const SearchCliConfig &config) { if (config.queryType == SearchQuery::Type::Simple) { return SearchFactory::createQuery(config.keyword, SearchQuery::Type::Simple); } else if (config.queryType == SearchQuery::Type::Wildcard) { return SearchFactory::createQuery(config.keyword, SearchQuery::Type::Wildcard); } else { // Boolean 查询:根据分隔符确定 AND/OR 逻辑 // '|' 分隔 → OR,'&' 分隔 → AND,',' 分隔 → AND(向后兼容) QChar separator = ','; SearchQuery::BooleanOperator op = SearchQuery::BooleanOperator::AND; if (config.keyword.contains('|')) { separator = '|'; op = SearchQuery::BooleanOperator::OR; } else if (config.keyword.contains('&')) { separator = '&'; } #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) QStringList keywords = config.keyword.split(separator, Qt::SkipEmptyParts); #else QStringList keywords = config.keyword.split(separator, QString::SkipEmptyParts); #endif SearchQuery query = SearchFactory::createQuery(keywords, SearchQuery::Type::Boolean); query.setBooleanOperator(op); return query; } } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); app.setApplicationVersion(QString::fromLatin1(VERSION)); // Parse CLI arguments CliOptions cliOptions; SearchCliConfig config; if (!cliOptions.parse(app, config)) { return 1; } // Preview subcommand: fetch content preview on demand if (config.subcommand == "preview") { DFMSEARCH::ContentRetriever retriever; const PreviewCommandResult previewResult = runPreviewCommand(config, retriever); if (!previewResult.stderrText.isEmpty()) { std::cerr << previewResult.stderrText.toStdString(); } if (!previewResult.stdoutText.isEmpty()) { std::cout << previewResult.stdoutText.toStdString(); } return previewResult.exitCode; } // Semantic search mode if (config.semanticMode) { auto *semanticSearcher = new DFMSEARCH::SemanticSearcher(&app); semanticSearcher->setDetailedResultsEnabled(config.verbose); if (config.maxResults > 0) { semanticSearcher->setMaxResults(config.maxResults); } if (!config.excludedPaths.isEmpty()) { semanticSearcher->setSearchExcludedPaths(config.excludedPaths); } OutputFormatter *formatter = createOutputFormatter(config, &app); // 为语义搜索构建 formatter 需要的 options SearchOptions formatterOptions; formatterOptions.setDetailedResultsEnabled(config.verbose); if (config.hasTimeFilter) formatterOptions.setTimeRangeFilter(config.timeFilter); if (config.hasSizeFilter) formatterOptions.setSizeRangeFilter(config.sizeFilter); JsonOutput *jsonOutput = qobject_cast(formatter); if (jsonOutput) { jsonOutput->setSearchOptions(formatterOptions); } TextOutput *textOutput = qobject_cast(formatter); if (textOutput) { textOutput->setSearchOptions(formatterOptions); textOutput->setVerbose(config.verbose); } formatter->setSearchContext(config.keyword, config.searchPath, SearchType::Semantic, SearchMethod::Indexed); QObject::connect(formatter, &OutputFormatter::finished, &app, &QCoreApplication::quit); QObject::connect(semanticSearcher, &DFMSEARCH::SemanticSearcher::intentParsed, [formatter](const DFMSEARCH::ParsedIntent &intent) { if (auto *jsonOut = qobject_cast(formatter)) { jsonOut->setParsedIntent(intent); } }); QObject::connect(semanticSearcher, &DFMSEARCH::SemanticSearcher::searchStarted, [formatter]() { formatter->outputSearchStarted(); }); QObject::connect(semanticSearcher, &DFMSEARCH::SemanticSearcher::searchFinished, [formatter](const SearchResultList &results) { formatter->outputSearchFinished(results); }); QObject::connect(semanticSearcher, &DFMSEARCH::SemanticSearcher::searchCancelled, [formatter]() { formatter->outputSearchCancelled(); }); QObject::connect(semanticSearcher, &DFMSEARCH::SemanticSearcher::errorOccurred, [formatter](const DFMSEARCH::SearchError &error) { formatter->outputError(error); }); QStringList semanticDirs; if (!config.searchPath.isEmpty()) { semanticDirs = QStringList { config.searchPath }; } semanticSearcher->search(config.keyword, semanticDirs); return app.exec(); } // Create search engine (non-semantic mode) SearchEngine *engine = SearchFactory::createEngine(config.searchType, &app); if (!engine) { qCritical() << "Error: Failed to create search engine"; return 1; } // 配置搜索选项 SearchOptions options; configureSearchOptions(options, config); engine->setSearchOptions(options); // 创建输出格式化器 OutputFormatter *formatter = createOutputFormatter(config, &app); // 设置输出格式化器上下文 formatter->setSearchContext(config.keyword, config.searchPath, config.searchType, config.searchMethod); // 为文本输出设置额外信息 TextOutput *textOutput = qobject_cast(formatter); if (textOutput) { textOutput->setSearchOptions(options); textOutput->setTimeFilterInfo(config.hasTimeFilter, config.timeFilter); textOutput->setVerbose(config.verbose); if (!config.fileExtensions.isEmpty()) { textOutput->setFileExtensionsFilter(config.fileExtensions.join(',')); } } // 为JSON输出设置额外信息 JsonOutput *jsonOutput = qobject_cast(formatter); if (jsonOutput) { jsonOutput->setSearchOptions(options); } // 连接信号 connectSignals(engine, formatter, config, app); // 创建并执行搜索查询 SearchQuery query = createSearchQuery(config); engine->search(query); return app.exec(); } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/output/000077500000000000000000000000001523477242100255405ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/output/json_output.cpp000066400000000000000000000465641523477242100306540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "json_output.h" #include #include #include #include #include #include using namespace dfmsearch; using namespace std; namespace { QString plainContentMatch(const SearchResult &result) { if (result.hasCustomAttribute("plainContentMatch")) { return result.customAttribute("plainContentMatch").toString(); } QString content = result.customAttribute("highlightedContent").toString(); static const QRegularExpression kHtmlTagPattern(QStringLiteral("<[^>]+>")); return content.remove(kHtmlTagPattern); } int snippetOffset(const SearchResult &result) { return result.hasCustomAttribute("snippetOffset") ? result.customAttribute("snippetOffset").toInt() : -1; } } // namespace void JsonOutput::setSearchContext(const QString &keyword, const QString &searchPath, SearchType searchType, SearchMethod searchMethod) { m_keyword = keyword; m_searchPath = searchPath; m_searchType = searchType; m_searchMethod = searchMethod; } QJsonObject JsonOutput::intentToJson(const DFMSEARCH::ParsedIntent &intent) { QJsonObject obj; // timeConstraint if (intent.timeConstraint().isValid()) { QJsonObject time; QString kindStr; switch (intent.timeConstraint().kind()) { case DFMSEARCH::TimeConstraintKind::Preset: kindStr = "preset"; { QString presetStr; switch (intent.timeConstraint().preset()) { case DFMSEARCH::TimePreset::Today: presetStr = "today"; break; case DFMSEARCH::TimePreset::Yesterday: presetStr = "yesterday"; break; case DFMSEARCH::TimePreset::DayBeforeYesterday: presetStr = "dayBeforeYesterday"; break; case DFMSEARCH::TimePreset::ThisWeek: presetStr = "thisWeek"; break; case DFMSEARCH::TimePreset::LastWeek: presetStr = "lastWeek"; break; case DFMSEARCH::TimePreset::ThisMonth: presetStr = "thisMonth"; break; case DFMSEARCH::TimePreset::LastMonth: presetStr = "lastMonth"; break; case DFMSEARCH::TimePreset::ThisYear: presetStr = "thisYear"; break; case DFMSEARCH::TimePreset::LastYear: presetStr = "lastYear"; break; } time["preset"] = presetStr; } break; case DFMSEARCH::TimeConstraintKind::Relative: kindStr = "relative"; time["value"] = intent.timeConstraint().relativeValue(); { QString unitStr; switch (intent.timeConstraint().relativeUnit()) { case DFMSEARCH::TimeUnit::Minutes: unitStr = "minutes"; break; case DFMSEARCH::TimeUnit::Hours: unitStr = "hours"; break; case DFMSEARCH::TimeUnit::Days: unitStr = "days"; break; case DFMSEARCH::TimeUnit::Weeks: unitStr = "weeks"; break; case DFMSEARCH::TimeUnit::Months: unitStr = "months"; break; case DFMSEARCH::TimeUnit::Years: unitStr = "years"; break; } time["unit"] = unitStr; } break; case DFMSEARCH::TimeConstraintKind::Custom: kindStr = "custom"; if (intent.timeConstraint().customStart().isValid()) time["start"] = intent.timeConstraint().customStart().toString(Qt::ISODate); if (intent.timeConstraint().customEnd().isValid()) time["end"] = intent.timeConstraint().customEnd().toString(Qt::ISODate); break; case DFMSEARCH::TimeConstraintKind::None: break; } time["kind"] = kindStr; if (intent.timeConstraint().timeField() != DFMSEARCH::TimeField::Unspecified) { QString fieldStr; switch (intent.timeConstraint().timeField()) { case DFMSEARCH::TimeField::ModifyTime: fieldStr = "modifyTime"; break; case DFMSEARCH::TimeField::BirthTime: fieldStr = "birthTime"; break; case DFMSEARCH::TimeField::Both: fieldStr = "both"; break; default: fieldStr = "unspecified"; break; } time["timeField"] = fieldStr; } obj["timeConstraint"] = time; } // sizeConstraint if (intent.sizeConstraint().isValid()) { QJsonObject size; if (intent.sizeConstraint().minSize() > 0) size["minBytes"] = intent.sizeConstraint().minSize(); if (intent.sizeConstraint().maxSize() > 0) size["maxBytes"] = intent.sizeConstraint().maxSize(); size["includeLower"] = intent.sizeConstraint().includeLower(); size["includeUpper"] = intent.sizeConstraint().includeUpper(); obj["sizeConstraint"] = size; } // fileExtensions if (!intent.fileExtensions().isEmpty()) { obj["fileExtensions"] = QJsonArray::fromStringList(intent.fileExtensions()); } // searchDirectories (NLP-parsed, before caller override is applied) if (!intent.searchDirectories().isEmpty()) { obj["searchDirectories"] = QJsonArray::fromStringList(intent.searchDirectories()); } // keywords if (!intent.keywords().isEmpty()) { obj["keywords"] = QJsonArray::fromStringList(intent.keywords()); } // includeHidden obj["includeHidden"] = intent.includeHidden(); // consumedSpans if (!intent.consumedSpans().isEmpty()) { QJsonArray spans; for (const auto &span : intent.consumedSpans()) { if (span.isValid()) { QJsonObject s; s["start"] = span.start(); s["end"] = span.end(); s["ruleId"] = span.ruleId(); spans.append(s); } } if (!spans.isEmpty()) { obj["consumedSpans"] = spans; } } return obj; } QJsonValue JsonOutput::resultToJson(const SearchResult &result) { // 如果不启用详细结果,只返回路径 if (!m_options.detailedResultsEnabled()) { return result.path(); } if (m_searchType == SearchType::FileName) { // 文件名搜索:返回详细对象 QJsonObject obj; obj["path"] = result.path(); FileNameResultAPI resultAPI(const_cast(result)); obj["isDirectory"] = resultAPI.isDirectory(); if (!resultAPI.isDirectory()) { obj["fileType"] = resultAPI.fileType(); obj["size"] = resultAPI.size(); // 文件大小数值(字节) qint64 fileSizeBytes = resultAPI.fileSizeBytes(); if (fileSizeBytes > 0) { obj["sizeBytes"] = fileSizeBytes; } } QString filename = resultAPI.filename(); if (!filename.isEmpty()) { obj["filename"] = filename; } QString ext = resultAPI.fileExtension(); if (!ext.isEmpty()) { obj["extension"] = ext; } obj["isHidden"] = resultAPI.isHidden(); // 修改时间(包含时间戳和时间字符串) qint64 modifyTs = resultAPI.modifyTimestamp(); if (modifyTs > 0) { QJsonObject modifyTimeObj; modifyTimeObj["timestamp"] = modifyTs; modifyTimeObj["formatted"] = resultAPI.modifyTimeString(); obj["modifyTime"] = modifyTimeObj; } // 创建时间(包含时间戳和时间字符串) qint64 birthTs = resultAPI.birthTimestamp(); if (birthTs > 0) { QJsonObject birthTimeObj; birthTimeObj["timestamp"] = birthTs; birthTimeObj["formatted"] = resultAPI.birthTimeString(); obj["birthTime"] = birthTimeObj; } return obj; } else if (m_searchType == SearchType::Content) { // 内容搜索:返回包含路径和内容匹配的对象 QJsonObject obj; obj["path"] = result.path(); ContentResultAPI resultAPI(const_cast(result)); obj["contentMatch"] = plainContentMatch(result); obj["snippetOffset"] = snippetOffset(result); int charCount = resultAPI.charCount(); if (charCount > 0) { obj["charCount"] = charCount; } QString filename = resultAPI.filename(); if (!filename.isEmpty()) { obj["filename"] = filename; } obj["isHidden"] = resultAPI.isHidden(); // 修改时间 qint64 modifyTs = resultAPI.modifyTimestamp(); if (modifyTs > 0) { QJsonObject modifyTimeObj; modifyTimeObj["timestamp"] = modifyTs; modifyTimeObj["formatted"] = resultAPI.modifyTimeString(); obj["modifyTime"] = modifyTimeObj; } // 创建时间 qint64 birthTs = resultAPI.birthTimestamp(); if (birthTs > 0) { QJsonObject birthTimeObj; birthTimeObj["timestamp"] = birthTs; birthTimeObj["formatted"] = resultAPI.birthTimeString(); obj["birthTime"] = birthTimeObj; } // 文件大小 qint64 sizeBytes = resultAPI.fileSizeBytes(); if (sizeBytes > 0) { obj["sizeBytes"] = sizeBytes; } return obj; } else if (m_searchType == SearchType::Ocr) { // OCR 搜索:返回详细对象 QJsonObject obj; obj["path"] = result.path(); OcrTextResultAPI resultAPI(const_cast(result)); obj["contentMatch"] = plainContentMatch(result); obj["snippetOffset"] = snippetOffset(result); int charCount = resultAPI.charCount(); if (charCount > 0) { obj["charCount"] = charCount; } QString ocrContent = resultAPI.ocrContent(); if (!ocrContent.isEmpty()) { obj["ocrContent"] = ocrContent; } QString filename = resultAPI.filename(); if (!filename.isEmpty()) { obj["filename"] = filename; } obj["isHidden"] = resultAPI.isHidden(); // 修改时间 qint64 modifyTs = resultAPI.modifyTimestamp(); if (modifyTs > 0) { QJsonObject modifyTimeObj; modifyTimeObj["timestamp"] = modifyTs; modifyTimeObj["formatted"] = resultAPI.modifyTimeString(); obj["modifyTime"] = modifyTimeObj; } // 创建时间 qint64 birthTs = resultAPI.birthTimestamp(); if (birthTs > 0) { QJsonObject birthTimeObj; birthTimeObj["timestamp"] = birthTs; birthTimeObj["formatted"] = resultAPI.birthTimeString(); obj["birthTime"] = birthTimeObj; } // 文件校验和 QString checksum = resultAPI.checksum(); if (!checksum.isEmpty()) { obj["checksum"] = checksum; } // 文件大小 qint64 sizeBytes = resultAPI.fileSizeBytes(); if (sizeBytes > 0) { obj["sizeBytes"] = sizeBytes; } return obj; } else if (m_searchType == SearchType::Semantic) { // 语义搜索:结果来自多个子引擎,通用输出所有 customAttributes QJsonObject obj; obj["path"] = result.path(); const QVariantMap attrs = result.customAttributes(); for (auto it = attrs.cbegin(); it != attrs.cend(); ++it) { obj[it.key()] = QJsonValue::fromVariant(it.value()); } return obj; } return result.path(); } void JsonOutput::printJsonLine(const QJsonObject &obj) { QJsonDocument doc(obj); std::cout << doc.toJson(QJsonDocument::Compact).constData() << std::endl; } void JsonOutput::outputSearchStarted() { m_startTime = QDateTime::currentDateTime(); m_collectedResults = QJsonArray(); // 清空收集的结果 if (m_streaming) { outputStreamingStart(); } // 非流式模式:开始时不输出 } void JsonOutput::outputStreamingStart() { QJsonObject startObj; startObj["type"] = "search_started"; QString searchTypeStr; switch (m_searchType) { case SearchType::FileName: searchTypeStr = "filename"; break; case SearchType::Content: searchTypeStr = "content"; break; case SearchType::Ocr: searchTypeStr = "ocr"; break; case SearchType::Semantic: searchTypeStr = "semantic"; break; default: searchTypeStr = "unknown"; } QJsonObject searchInfo; searchInfo["keyword"] = m_keyword; searchInfo["searchPath"] = m_searchPath; searchInfo["searchType"] = searchTypeStr; searchInfo["searchMethod"] = (m_searchMethod == SearchMethod::Indexed ? "indexed" : "realtime"); searchInfo["caseSensitive"] = m_options.caseSensitive(); searchInfo["includeHidden"] = m_options.includeHidden(); // 添加时间范围过滤信息 if (m_options.hasTimeRangeFilter()) { DFMSEARCH::TimeRangeFilter timeFilter = m_options.timeRangeFilter(); QJsonObject timeFilterInfo; timeFilterInfo["field"] = (timeFilter.timeField() == DFMSEARCH::TimeField::BirthTime) ? "birth" : "modify"; auto [start, end] = timeFilter.resolveTimeRange(); timeFilterInfo["startTime"] = start.toString(Qt::ISODate); timeFilterInfo["endTime"] = end.toString(Qt::ISODate); searchInfo["timeRangeFilter"] = timeFilterInfo; } // 添加文件大小范围过滤信息 if (m_options.hasSizeRangeFilter()) { DFMSEARCH::SizeRangeFilter sizeFilter = m_options.sizeRangeFilter(); QJsonObject sizeFilterInfo; if (sizeFilter.minSize() > 0) { sizeFilterInfo["minBytes"] = sizeFilter.minSize(); } if (sizeFilter.maxSize() > 0) { sizeFilterInfo["maxBytes"] = sizeFilter.maxSize(); } sizeFilterInfo["includeLower"] = sizeFilter.includeLower(); sizeFilterInfo["includeUpper"] = sizeFilter.includeUpper(); searchInfo["sizeRangeFilter"] = sizeFilterInfo; } // 语义搜索:附加 ParsedIntent if (m_searchType == SearchType::Semantic && m_parsedIntent.has_value()) { searchInfo["intent"] = intentToJson(*m_parsedIntent); } startObj["search"] = searchInfo; startObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); printJsonLine(startObj); } void JsonOutput::outputResult(const SearchResult &result) { if (m_streaming) { outputStreamingResult(result); } else { // 非流式模式:收集结果 m_collectedResults.append(resultToJson(result)); } } void JsonOutput::outputStreamingResult(const SearchResult &result) { QJsonObject resultObj; resultObj["type"] = "result"; resultObj["data"] = resultToJson(result); printJsonLine(resultObj); } void JsonOutput::outputSearchFinished(const QList &results) { if (m_streaming) { outputStreamingFinish(results); } else { outputCompleteResult(results); } emit finished(); } void JsonOutput::outputStreamingFinish(const QList &results) { QJsonObject endObj; endObj["type"] = "search_finished"; QJsonObject status; status["state"] = "success"; status["totalResults"] = results.size(); endObj["status"] = status; QJsonObject timestamps; timestamps["finished"] = QDateTime::currentDateTime().toString(Qt::ISODate); timestamps["duration"] = m_startTime.msecsTo(QDateTime::currentDateTime()); endObj["timestamps"] = timestamps; printJsonLine(endObj); } void JsonOutput::outputCompleteResult(const QList &results) { QJsonObject root; // 搜索信息 QJsonObject searchInfo; searchInfo["keyword"] = m_keyword; searchInfo["searchPath"] = m_searchPath; QString searchTypeStr; switch (m_searchType) { case SearchType::FileName: searchTypeStr = "filename"; break; case SearchType::Content: searchTypeStr = "content"; break; case SearchType::Ocr: searchTypeStr = "ocr"; break; case SearchType::Semantic: searchTypeStr = "semantic"; break; default: searchTypeStr = "unknown"; } searchInfo["searchType"] = searchTypeStr; searchInfo["searchMethod"] = (m_searchMethod == SearchMethod::Indexed ? "indexed" : "realtime"); searchInfo["caseSensitive"] = m_options.caseSensitive(); searchInfo["includeHidden"] = m_options.includeHidden(); // 添加时间范围过滤信息 if (m_options.hasTimeRangeFilter()) { DFMSEARCH::TimeRangeFilter timeFilter = m_options.timeRangeFilter(); QJsonObject timeFilterInfo; timeFilterInfo["field"] = (timeFilter.timeField() == DFMSEARCH::TimeField::BirthTime) ? "birth" : "modify"; auto [start, end] = timeFilter.resolveTimeRange(); timeFilterInfo["startTime"] = start.toString(Qt::ISODate); timeFilterInfo["endTime"] = end.toString(Qt::ISODate); searchInfo["timeRangeFilter"] = timeFilterInfo; } // 添加文件大小范围过滤信息 if (m_options.hasSizeRangeFilter()) { DFMSEARCH::SizeRangeFilter sizeFilter = m_options.sizeRangeFilter(); QJsonObject sizeFilterInfo; if (sizeFilter.minSize() > 0) { sizeFilterInfo["minBytes"] = sizeFilter.minSize(); } if (sizeFilter.maxSize() > 0) { sizeFilterInfo["maxBytes"] = sizeFilter.maxSize(); } sizeFilterInfo["includeLower"] = sizeFilter.includeLower(); sizeFilterInfo["includeUpper"] = sizeFilter.includeUpper(); searchInfo["sizeRangeFilter"] = sizeFilterInfo; } // 语义搜索:附加 ParsedIntent if (m_searchType == SearchType::Semantic && m_parsedIntent.has_value()) { searchInfo["intent"] = intentToJson(*m_parsedIntent); } root["search"] = searchInfo; // 时间戳 QDateTime endTime = QDateTime::currentDateTime(); qint64 duration = m_startTime.msecsTo(endTime); QJsonObject timestamps; timestamps["started"] = m_startTime.toString(Qt::ISODate); timestamps["finished"] = endTime.toString(Qt::ISODate); timestamps["duration"] = duration; root["timestamps"] = timestamps; // 状态 QJsonObject status; status["state"] = "success"; status["totalResults"] = m_collectedResults.isEmpty() ? results.size() : m_collectedResults.size(); root["status"] = status; // 结果数组 if (m_collectedResults.isEmpty() && !results.isEmpty()) { // 如果结果没有被 resultsFound 收集,则在这里转换 for (const auto &result : results) { m_collectedResults.append(resultToJson(result)); } } root["results"] = m_collectedResults; // 输出完整 JSON QJsonDocument doc(root); std::cout << doc.toJson(QJsonDocument::Indented).constData() << std::endl; } void JsonOutput::outputSearchCancelled() { QJsonObject cancelObj; cancelObj["type"] = "search_cancelled"; cancelObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); printJsonLine(cancelObj); emit finished(); } void JsonOutput::outputError(const DFMSEARCH::SearchError &error) { QJsonObject errorObj; errorObj["type"] = "error"; errorObj["name"] = error.name(); errorObj["message"] = error.message(); errorObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); printJsonLine(errorObj); } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/output/json_output.h000066400000000000000000000050441523477242100303050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef JSON_OUTPUT_H #define JSON_OUTPUT_H #include "output_formatter.h" #include #include #include #include #include namespace dfmsearch { /** * @brief JSON格式输出器 * * 支持流式和非流式两种JSON输出模式 */ class JsonOutput : public OutputFormatter { Q_OBJECT public: explicit JsonOutput(bool streaming = false, QObject *parent = nullptr) : OutputFormatter(parent), m_streaming(streaming) { } void setSearchContext(const QString &keyword, const QString &searchPath, SearchType searchType, SearchMethod searchMethod) override; void outputSearchStarted() override; void outputResult(const SearchResult &result) override; void outputSearchFinished(const QList &results) override; void outputSearchCancelled() override; void outputError(const DFMSEARCH::SearchError &error) override; /** * @brief 设置搜索选项 */ void setSearchOptions(const SearchOptions &options) { m_options = options; } /** * @brief 设置语义搜索解析的 ParsedIntent * * 在 searchType 为 Semantic 时,intent 信息会序列化到 JSON 输出中 */ void setParsedIntent(const DFMSEARCH::ParsedIntent &intent) { m_parsedIntent = intent; } /** * @brief 设置是否启用详细输出模式 * @param verbose true 启用详细输出 */ void setVerbose(bool verbose) { m_verbose = verbose; } private: QJsonValue resultToJson(const SearchResult &result); void printJsonLine(const QJsonObject &obj); // Intent 序列化辅助 QJsonObject intentToJson(const DFMSEARCH::ParsedIntent &intent); // 流式输出方法 void outputStreamingStart(); void outputStreamingResult(const SearchResult &result); void outputStreamingFinish(const QList &results); // 非流式输出方法 void outputCompleteResult(const QList &results); private: QString m_keyword; QString m_searchPath; SearchType m_searchType = SearchType::FileName; SearchMethod m_searchMethod = SearchMethod::Indexed; SearchOptions m_options; QDateTime m_startTime; bool m_streaming; bool m_verbose = false; QJsonArray m_collectedResults; std::optional m_parsedIntent; }; } // namespace dfmsearch #endif // JSON_OUTPUT_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/output/output_formatter.h000066400000000000000000000034401523477242100313350ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef OUTPUT_FORMATTER_H #define OUTPUT_FORMATTER_H #include #include #include #include #include namespace dfmsearch { /** * @brief 搜索结果输出格式化器基类 * * 定义输出格式化的抽象接口,遵循依赖倒置原则 (DIP) */ class OutputFormatter : public QObject { Q_OBJECT public: explicit OutputFormatter(QObject *parent = nullptr) : QObject(parent) { } virtual ~OutputFormatter() = default; /** * @brief 设置搜索上下文信息 */ virtual void setSearchContext(const QString &keyword, const QString &searchPath, SearchType searchType, SearchMethod searchMethod) = 0; /** * @brief 输出搜索开始 */ virtual void outputSearchStarted() = 0; /** * @brief 输出单个搜索结果 */ virtual void outputResult(const SearchResult &result) = 0; /** * @brief 输出搜索结束 */ virtual void outputSearchFinished(const QList &results) = 0; /** * @brief 输出搜索取消 */ virtual void outputSearchCancelled() = 0; /** * @brief 设置是否启用详细输出模式 * @param verbose true 启用详细输出, false 只禁用简易输出 */ virtual void setVerbose(bool verbose) = 0; /** * @brief 输出错误 */ virtual void outputError(const DFMSEARCH::SearchError &error) = 0; Q_SIGNALS: /** * @brief 输出完成信号(用于退出应用) */ void finished(); }; } // namespace dfmsearch #endif // OUTPUT_FORMATTER_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/output/text_output.cpp000066400000000000000000000236011523477242100306520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "text_output.h" #include #include #include #include #include using namespace dfmsearch; using namespace std; namespace { QString plainContentMatch(const SearchResult &result) { if (result.hasCustomAttribute("plainContentMatch")) { return result.customAttribute("plainContentMatch").toString(); } QString content = result.customAttribute("highlightedContent").toString(); static const QRegularExpression kHtmlTagPattern(QStringLiteral("<[^>]+>")); return content.remove(kHtmlTagPattern); } int snippetOffset(const SearchResult &result) { return result.hasCustomAttribute("snippetOffset") ? result.customAttribute("snippetOffset").toInt() : -1; } } // namespace void TextOutput::setSearchContext(const QString &keyword, const QString &searchPath, SearchType searchType, SearchMethod searchMethod) { m_keyword = keyword; m_searchPath = searchPath; m_searchType = searchType; m_searchMethod = searchMethod; } void TextOutput::setTimeFilterInfo(bool hasFilter, const DFMSEARCH::TimeRangeFilter &filter) { m_hasTimeFilter = hasFilter; m_timeFilter = filter; } void TextOutput::outputSearchStarted() { std::cout << "Search started..." << std::endl; std::cout << "Searching for: " << m_keyword.toStdString() << std::endl; std::cout << "In path: " << m_searchPath.toStdString() << std::endl; QString typeStr = "Filename"; if (m_searchType == SearchType::Content) typeStr = "Content"; else if (m_searchType == SearchType::Ocr) typeStr = "Ocr"; else if (m_searchType == SearchType::Semantic) typeStr = "Semantic"; std::cout << "Search type: " << typeStr.toStdString() << std::endl; std::cout << "Search method: " << (m_searchMethod == SearchMethod::Indexed ? "Indexed" : "Realtime") << std::endl; // 打印文件扩展名过滤 if (!m_fileExtensionsFilter.isEmpty()) { std::cout << "File extensions filter: " << m_fileExtensionsFilter.toStdString() << std::endl; } // 打印时间范围过滤 if (m_hasTimeFilter) { std::cout << "Time range filter: "; std::cout << (m_timeFilter.timeField() == DFMSEARCH::TimeField::BirthTime ? "birth_time" : "modify_time"); auto [start, end] = m_timeFilter.resolveTimeRange(); std::cout << " from " << start.toString("yyyy-MM-dd HH:mm:ss").toStdString() << " to " << end.toString("yyyy-MM-dd HH:mm:ss").toStdString(); std::cout << std::endl; } // 打印文件大小范围过滤 if (m_options.hasSizeRangeFilter()) { DFMSEARCH::SizeRangeFilter sizeFilter = m_options.sizeRangeFilter(); std::cout << "Size range filter: "; if (sizeFilter.minSize() > 0) { std::cout << "min=" << sizeFilter.minSize() << " bytes"; } if (sizeFilter.maxSize() > 0) { if (sizeFilter.minSize() > 0) std::cout << ", "; std::cout << "max=" << sizeFilter.maxSize() << " bytes"; } std::cout << std::endl; } } void TextOutput::printSearchResult(const SearchResult &result) { // 非详细模式下只输出路径 if (!m_verbose) { std::cout << result.path().toStdString() << std::endl; return; } // 详细模式下输出所有信息 std::cout << "Found: " << result.path().toStdString() << std::endl; if (m_searchType == SearchType::FileName) { FileNameResultAPI resultAPI(const_cast(result)); if (resultAPI.isDirectory()) { std::cout << " Type: Directory" << std::endl; } else { std::cout << " Type: " << resultAPI.fileType().toStdString() << std::endl; qint64 fileSizeBytes = resultAPI.fileSizeBytes(); if (fileSizeBytes > 0) { std::cout << " Size: " << fileSizeBytes << " bytes" << std::endl; } else { std::cout << " Size: " << resultAPI.size().toStdString() << " bytes" << std::endl; } } // 文件名和扩展名 QString filename = resultAPI.filename(); if (!filename.isEmpty()) { std::cout << " Filename: " << filename.toStdString() << std::endl; } QString ext = resultAPI.fileExtension(); if (!ext.isEmpty()) { std::cout << " Extension: " << ext.toStdString() << std::endl; } // 隐藏状态 std::cout << " Hidden: " << (resultAPI.isHidden() ? "Yes" : "No") << std::endl; // 修改时间(同时输出时间戳和时间字符串) qint64 modifyTs = resultAPI.modifyTimestamp(); if (modifyTs > 0) { std::cout << " Modified: " << resultAPI.modifyTimeString().toStdString() << " (timestamp: " << modifyTs << ")" << std::endl; } // 创建时间(同时输出时间戳和时间字符串) qint64 birthTs = resultAPI.birthTimestamp(); if (birthTs > 0) { std::cout << " Created: " << resultAPI.birthTimeString().toStdString() << " (timestamp: " << birthTs << ")" << std::endl; } } else if (m_searchType == SearchType::Content) { ContentResultAPI resultAPI(const_cast(result)); std::cout << " Content match: " << plainContentMatch(result).toStdString() << std::endl; std::cout << " Snippet offset: " << snippetOffset(result) << std::endl; if (resultAPI.charCount() > 0) { std::cout << " Char count: " << resultAPI.charCount() << std::endl; } // 文件名 QString filename = resultAPI.filename(); if (!filename.isEmpty()) { std::cout << " Filename: " << filename.toStdString() << std::endl; } // 隐藏状态 std::cout << " Hidden: " << (resultAPI.isHidden() ? "Yes" : "No") << std::endl; // 修改时间 qint64 modifyTs = resultAPI.modifyTimestamp(); if (modifyTs > 0) { std::cout << " Modified: " << resultAPI.modifyTimeString().toStdString() << " (timestamp: " << modifyTs << ")" << std::endl; } // 创建时间 qint64 birthTs = resultAPI.birthTimestamp(); if (birthTs > 0) { std::cout << " Created: " << resultAPI.birthTimeString().toStdString() << " (timestamp: " << birthTs << ")" << std::endl; } // 文件大小 qint64 sizeBytes = resultAPI.fileSizeBytes(); if (sizeBytes > 0) { std::cout << " Size: " << sizeBytes << " bytes" << std::endl; } } else if (m_searchType == SearchType::Ocr) { OcrTextResultAPI resultAPI(const_cast(result)); std::cout << " Content match: " << plainContentMatch(result).toStdString() << std::endl; std::cout << " Snippet offset: " << snippetOffset(result) << std::endl; if (resultAPI.charCount() > 0) { std::cout << " Char count: " << resultAPI.charCount() << std::endl; } QString ocrContent = resultAPI.ocrContent(); if (!ocrContent.isEmpty()) { std::cout << " OCR content: " << ocrContent.toStdString() << std::endl; } // 文件名 QString filename = resultAPI.filename(); if (!filename.isEmpty()) { std::cout << " Filename: " << filename.toStdString() << std::endl; } // 隐藏状态 std::cout << " Hidden: " << (resultAPI.isHidden() ? "Yes" : "No") << std::endl; // 修改时间 qint64 modifyTs = resultAPI.modifyTimestamp(); if (modifyTs > 0) { std::cout << " Modified: " << resultAPI.modifyTimeString().toStdString() << " (timestamp: " << modifyTs << ")" << std::endl; } // 创建时间 qint64 birthTs = resultAPI.birthTimestamp(); if (birthTs > 0) { std::cout << " Created: " << resultAPI.birthTimeString().toStdString() << " (timestamp: " << birthTs << ")" << std::endl; } // 文件校验和 QString checksum = resultAPI.checksum(); if (!checksum.isEmpty()) { std::cout << " Checksum: " << checksum.toStdString() << std::endl; } // 文件大小 qint64 sizeBytes = resultAPI.fileSizeBytes(); if (sizeBytes > 0) { std::cout << " Size: " << sizeBytes << " bytes" << std::endl; } } else if (m_searchType == SearchType::Semantic) { // 语义搜索:通用输出所有 customAttributes const QVariantMap attrs = result.customAttributes(); for (auto it = attrs.cbegin(); it != attrs.cend(); ++it) { std::cout << " " << it.key().toStdString() << ": " << it.value().toString().toStdString() << std::endl; } } std::cout << std::endl; } void TextOutput::outputResult(const SearchResult &result) { printSearchResult(result); } void TextOutput::outputSearchFinished(const QList &results) { std::cout << "Search finished. Total results: " << results.size() << std::endl; // 如果禁用了 resultFoundEnabled,则在这里输出结果 if (!m_options.resultFoundEnabled()) { for (const auto &result : results) { printSearchResult(result); } } emit finished(); } void TextOutput::outputSearchCancelled() { std::cout << "Search cancelled" << std::endl; emit finished(); } void TextOutput::outputError(const DFMSEARCH::SearchError &error) { std::cerr << "[Error]: " << error.code() << "[Name]: " << error.name().toStdString() << "[Message]:" << error.message().toStdString() << std::endl; } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/output/text_output.h000066400000000000000000000040401523477242100303130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TEXT_OUTPUT_H #define TEXT_OUTPUT_H #include "output_formatter.h" #include namespace dfmsearch { /** * @brief 文本格式输出器 * * 以人类可读的文本格式输出搜索结果 */ class TextOutput : public OutputFormatter { Q_OBJECT public: explicit TextOutput(QObject *parent = nullptr) : OutputFormatter(parent) { } void setSearchContext(const QString &keyword, const QString &searchPath, SearchType searchType, SearchMethod searchMethod) override; void outputSearchStarted() override; void outputResult(const SearchResult &result) override; void outputSearchFinished(const QList &results) override; void outputSearchCancelled() override; void outputError(const DFMSEARCH::SearchError &error) override; /** * @brief 设置文件扩展名过滤(用于输出提示) */ void setFileExtensionsFilter(const QString &extensions) { m_fileExtensionsFilter = extensions; } /** * @brief 设置时间过滤信息 */ void setTimeFilterInfo(bool hasFilter, const DFMSEARCH::TimeRangeFilter &filter); /** * @brief 设置搜索选项 */ void setSearchOptions(const SearchOptions &options) { m_options = options; } /** * @brief 设置是否启用详细输出模式 * @param verbose true 启用详细输出, false 只输出路径 */ void setVerbose(bool verbose) { m_verbose = verbose; } private: void printSearchResult(const SearchResult &result); private: QString m_keyword; QString m_searchPath; SearchType m_searchType = SearchType::FileName; SearchMethod m_searchMethod = SearchMethod::Indexed; SearchOptions m_options; QString m_fileExtensionsFilter; bool m_hasTimeFilter = false; DFMSEARCH::TimeRangeFilter m_timeFilter; bool m_verbose = false; }; } // namespace dfmsearch #endif // TEXT_OUTPUT_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/preview_command.cpp000066400000000000000000000110031523477242100300560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "preview_command.h" #include #include #include #include namespace dfmsearch { namespace { struct PreviewEntry { QString path; DFMSEARCH::PreviewStatus status = DFMSEARCH::PreviewStatus::Success; QString content; int charCount = 0; int keywordOffset = -1; }; QString formatPreviewNotIndexedError(const QStringList &paths) { QString text; QTextStream stream(&text); stream << "Error: preview requires indexed content for:\n"; for (const QString &path : paths) { stream << " " << path << "\n"; } return text; } PreviewEntry toPreviewEntry(const QString &path, const DFMSEARCH::PreviewResult &result) { PreviewEntry entry; entry.path = path; entry.status = result.status(); entry.content = result.content(); entry.charCount = result.charCount(); entry.keywordOffset = result.keywordOffset(); return entry; } QList fetchPreviewEntries(const QStringList &paths, const SearchCliConfig &config, DFMSEARCH::ContentRetriever &retriever, const DFMSEARCH::PreviewOptions &options, QStringList *notIndexedPaths) { QList entries; entries.reserve(paths.size()); for (const QString &path : paths) { DFMSEARCH::PreviewResult result = retriever.fetchPreview(path, config.searchType, options); if (result.status() == DFMSEARCH::PreviewStatus::NotIndexed) { notIndexedPaths->append(path); } entries.append(toPreviewEntry(path, result)); } return entries; } QString buildPreviewJson(const QList &entries, const SearchCliConfig &config, const DFMSEARCH::PreviewOptions &options) { QJsonObject root; root["type"] = "preview"; root["searchType"] = (config.searchType == SearchType::Content) ? "content" : (config.searchType == SearchType::Ocr) ? "ocr" : "semantic"; root["keyword"] = config.keyword; root["offset"] = config.offset; root["maxLength"] = options.maxLength(); QJsonArray results; for (const PreviewEntry &entry : entries) { QJsonObject item; item["path"] = entry.path; item["content"] = entry.content; item["charCount"] = entry.charCount; item["keywordOffset"] = entry.keywordOffset; results.append(item); } root["totalResults"] = results.size(); root["results"] = results; return QString::fromUtf8(QJsonDocument(root).toJson(QJsonDocument::Indented)); } QString buildPreviewText(const QList &entries) { QString text; QTextStream out(&text); for (const PreviewEntry &entry : entries) { out << entry.path << "\n"; if (!entry.content.isEmpty()) { out << " " << entry.content << "\n"; } else { out << " (no content)\n"; } out << " Char count: " << entry.charCount << "\n"; out << Qt::endl; } return text; } } // namespace PreviewCommandResult runPreviewCommand(const SearchCliConfig &config, DFMSEARCH::ContentRetriever &retriever) { DFMSEARCH::PreviewOptions options; options.setOffset(config.offset); if (config.keyword.isEmpty() && !config.maxPreviewSet) { options.setMaxLength(0); } else { options.setMaxLength(config.maxPreviewLength); } options.setKeyword(config.keyword); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) const QStringList paths = config.searchPath.split(',', Qt::SkipEmptyParts); #else const QStringList paths = config.searchPath.split(',', QString::SkipEmptyParts); #endif QStringList notIndexedPaths; const QList entries = fetchPreviewEntries(paths, config, retriever, options, ¬IndexedPaths); PreviewCommandResult commandResult; if (!notIndexedPaths.isEmpty()) { commandResult.exitCode = 1; commandResult.stderrText = formatPreviewNotIndexedError(notIndexedPaths); return commandResult; } if (config.jsonOutput) { commandResult.stdoutText = buildPreviewJson(entries, config, options); } else { commandResult.stdoutText = buildPreviewText(entries); } return commandResult; } } // namespace dfmsearch linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/preview_command.h000066400000000000000000000011261523477242100275300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef PREVIEW_COMMAND_H #define PREVIEW_COMMAND_H #include "cli_options.h" #include #include namespace dfmsearch { struct PreviewCommandResult { int exitCode = 0; QString stdoutText; QString stderrText; }; PreviewCommandResult runPreviewCommand(const SearchCliConfig &config, DFMSEARCH::ContentRetriever &retriever); } // namespace dfmsearch #endif // PREVIEW_COMMAND_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/preview_output_utils.cpp000066400000000000000000000015371523477242100312330ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "preview_output_utils.h" namespace dfmsearch { QJsonObject previewResultToJson(const QString &path, const DFMSEARCH::PreviewResult &result) { QJsonObject item; item["path"] = path; item["content"] = result.content(); item["charCount"] = result.charCount(); item["keywordOffset"] = result.keywordOffset(); return item; } void writePreviewResultText(QTextStream &out, const QString &path, const DFMSEARCH::PreviewResult &result) { out << path << "\n"; if (!result.content().isEmpty()) { out << " " << result.content() << "\n"; } else { out << " (no content)\n"; } out << " Char count: " << result.charCount() << "\n"; out << Qt::endl; } } // namespace dfmsearch linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/preview_output_utils.h000066400000000000000000000010621523477242100306710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef PREVIEW_OUTPUT_UTILS_H #define PREVIEW_OUTPUT_UTILS_H #include #include #include namespace dfmsearch { QJsonObject previewResultToJson(const QString &path, const DFMSEARCH::PreviewResult &result); void writePreviewResultText(QTextStream &out, const QString &path, const DFMSEARCH::PreviewResult &result); } // namespace dfmsearch #endif // PREVIEW_OUTPUT_UTILS_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/size_parser.cpp000066400000000000000000000027301523477242100272340ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "size_parser.h" namespace dfmsearch { bool SizeParser::parseSize(const QString &arg, qint64 &bytes) { if (arg.isEmpty()) { return false; } QString trimmed = arg.trimmed(); if (trimmed.isEmpty()) { return false; } // 提取数字部分和单位后缀 QString numStr; QString suffix; for (int i = 0; i < trimmed.length(); ++i) { QChar c = trimmed[i]; if (c.isDigit() || c == '.') { numStr += c; } else { suffix = trimmed.mid(i).trimmed().toUpper(); break; } } if (numStr.isEmpty()) { return false; } bool ok = false; double value = numStr.toDouble(&ok); if (!ok || value < 0) { return false; } // 根据后缀计算字节数 qint64 multiplier = 1; if (suffix == "K" || suffix == "KB") { multiplier = 1024LL; } else if (suffix == "M" || suffix == "MB") { multiplier = 1024LL * 1024; } else if (suffix == "G" || suffix == "GB") { multiplier = 1024LL * 1024 * 1024; } else if (suffix == "T" || suffix == "TB") { multiplier = 1024LL * 1024 * 1024 * 1024; } else if (!suffix.isEmpty()) { // 未知后缀 return false; } bytes = static_cast(value * multiplier); return true; } } // namespace dfmsearch linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/size_parser.h000066400000000000000000000017041523477242100267010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SIZE_PARSER_H #define SIZE_PARSER_H #include namespace dfmsearch { /** * @brief 文件大小参数解析工具类 * * 支持解析人类可读的文件大小字符串,如 "1K", "10M", "1G", "512" * 不带后缀的纯数字视为字节数。 */ class SizeParser { public: /** * @brief 解析文件大小字符串 * @param arg 输入字符串(如 "1K", "10M", "1G", "512") * @param bytes 输出字节数 * @return 解析成功返回true * * 支持的后缀(不区分大小写): * - K/KB: 千字节 (1024) * - M/MB: 兆字节 (1024^2) * - G/GB: 吉字节 (1024^3) * - T/TB: 太字节 (1024^4) * - 无后缀: 纯字节数 */ static bool parseSize(const QString &arg, qint64 &bytes); }; } // namespace dfmsearch #endif // SIZE_PARSER_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/time_parser.cpp000066400000000000000000000041051523477242100272160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "time_parser.h" #include #include using namespace dfmsearch; bool TimeParser::parseTimeLast(const QString &arg, int &value, DFMSEARCH::TimeUnit &unit) { if (arg.isEmpty()) return false; // 提取数字和单位 QRegularExpression re("^(\\d+)([mhdwMy])$"); QRegularExpressionMatch match = re.match(arg); if (!match.hasMatch()) return false; bool ok; value = match.captured(1).toInt(&ok); if (!ok || value < 0) return false; QString unitChar = match.captured(2); if (unitChar == "m") { unit = DFMSEARCH::TimeUnit::Minutes; } else if (unitChar == "h") { unit = DFMSEARCH::TimeUnit::Hours; } else if (unitChar == "d") { unit = DFMSEARCH::TimeUnit::Days; } else if (unitChar == "w") { unit = DFMSEARCH::TimeUnit::Weeks; } else if (unitChar == "M") { unit = DFMSEARCH::TimeUnit::Months; } else if (unitChar == "y") { unit = DFMSEARCH::TimeUnit::Years; } else { return false; } return true; } bool TimeParser::parseTimeRange(const QString &arg, QDateTime &start, QDateTime &end) { if (arg.isEmpty()) return false; QStringList parts = arg.split(','); if (parts.size() != 2) return false; QString startStr = parts[0].trimmed(); QString endStr = parts[1].trimmed(); // 优先尝试带时间的格式,再尝试不带时间的 start = QDateTime::fromString(startStr, "yyyy-MM-dd HH:mm"); if (!start.isValid()) { start = QDateTime::fromString(startStr, "yyyy-MM-dd"); if (start.isValid()) { start.setTime(QTime(0, 0, 0)); } } end = QDateTime::fromString(endStr, "yyyy-MM-dd HH:mm"); if (!end.isValid()) { end = QDateTime::fromString(endStr, "yyyy-MM-dd"); if (end.isValid()) { end.setTime(QTime(23, 59, 59)); } } return start.isValid() && end.isValid(); } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-client/time_parser.h000066400000000000000000000021211523477242100266570ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TIME_PARSER_H #define TIME_PARSER_H #include #include #include namespace dfmsearch { /** * @brief 时间范围解析工具类 * * 提供时间参数解析功能,遵循单一职责原则 */ class TimeParser { public: /** * @brief 解析 --time-last 参数(如 "3d", "2h", "30m") * @param arg 输入字符串 * @param value 输出数值 * @param unit 输出时间单位 * @return 解析成功返回true */ static bool parseTimeLast(const QString &arg, int &value, DFMSEARCH::TimeUnit &unit); /** * @brief 解析 --time-range 参数(如 "2025-01-01,2025-12-31") * @param arg 输入字符串 * @param start 输出开始时间 * @param end 输出结束时间 * @return 解析成功返回true */ static bool parseTimeRange(const QString &arg, QDateTime &start, QDateTime &end); }; } // namespace dfmsearch #endif // TIME_PARSER_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/000077500000000000000000000000001523477242100234705ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/CMakeLists.txt000066400000000000000000000007021523477242100262270ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) set (VERSION "1.0.0" CACHE STRING "define project version") set(BASE_NAME dfm-search) # public include file(GLOB_RECURSE PUBLIC_INCLUDES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/include/${BASE_NAME}/*") # src FILE (GLOB_RECURSE SRCS CONFIGURE_DEPENDS "./*.cpp" "./*.h" ) # Qt6 if (QT_VERSION_MAJOR MATCHES 6) include(dfm-search-qt6.cmake) else() include(dfm-search-qt5.cmake) endif() linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/contentsearch/000077500000000000000000000000001523477242100263305ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/contentsearch/contentsearchapi.cpp000066400000000000000000000020301523477242100323610ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include DFM_SEARCH_BEGIN_NS ContentOptionsAPI::ContentOptionsAPI(SearchOptions &options) : TextSearchOptionsAPI(options) { // init default if (!m_options.hasCustomOption("maxPreviewLength")) setMaxPreviewLength(200); if (!m_options.hasCustomOption("searchResultHighlight")) setSearchResultHighlightEnabled(false); if (!m_options.hasCustomOption("fullTextRetrieval")) setFullTextRetrievalEnabled(true); } void ContentOptionsAPI::setFilenameContentMixedAndSearchEnabled(bool enabled) { m_options.setCustomOption("filenameContentMixedAndSearchEnabled", enabled); } bool ContentOptionsAPI::isFilenameContentMixedAndSearchEnabled() const { return m_options.customOption("filenameContentMixedAndSearchEnabled").toBool(); } ContentResultAPI::ContentResultAPI(SearchResult &result) : TextSearchResultAPI(result) { } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/contentsearch/contentsearchengine.cpp000066400000000000000000000037141523477242100330670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "contentsearchengine.h" #include "contentstrategies/indexedstrategy.h" DFM_SEARCH_BEGIN_NS DCORE_USE_NAMESPACE ContentSearchEngine::ContentSearchEngine(QObject *parent) : GenericSearchEngine(parent) { } ContentSearchEngine::~ContentSearchEngine() = default; void ContentSearchEngine::setupStrategyFactory() { // 设置内容搜索策略工厂 auto factory = std::make_unique(); m_worker->setStrategyFactory(std::move(factory)); } SearchError ContentSearchEngine::validateSearchConditions() { // 先执行基类验证 auto result = GenericSearchEngine::validateSearchConditions(); if (result.isError()) { return result; } // 内容搜索特定验证 ContentOptionsAPI api(m_options); if (m_options.method() != SearchMethod::Indexed) { return SearchError(SearchErrorCode::InvalidSerchMethod); } // 检查是否为不支持的 Wildcard 查询类型 if (m_currentQuery.type() == SearchQuery::Type::Wildcard) { return SearchError(ContentSearchErrorCode::WildcardNotSupported); } if (m_currentQuery.type() == SearchQuery::Type::Simple && m_currentQuery.keyword().toUtf8().size() < Global::kMinContentSearchKeywordLength && api.filenameKeyword().isEmpty()) { return SearchError(ContentSearchErrorCode::KeywordTooShort); } return result; } std::unique_ptr ContentSearchStrategyFactory::createStrategy( SearchType searchType, const SearchOptions &options) { // 确保搜索类型正确 if (searchType != SearchType::Content) { return nullptr; } // 根据搜索方法创建对应的策略 if (options.method() == SearchMethod::Indexed) { return std::make_unique(options); } return nullptr; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/contentsearch/contentsearchengine.h000066400000000000000000000021241523477242100325260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CONTENT_SEARCH_ENGINE_H #define CONTENT_SEARCH_ENGINE_H #include "core/genericsearchengine.h" DFM_SEARCH_BEGIN_NS /** * @brief 内容搜索引擎 * * 实现基于文件内容的搜索功能 */ class ContentSearchEngine : public GenericSearchEngine { Q_OBJECT public: explicit ContentSearchEngine(QObject *parent = nullptr); ~ContentSearchEngine() override; // 实现搜索类型 SearchType searchType() const override { return SearchType::Content; } protected: // 设置策略工厂 void setupStrategyFactory() override; // 重写验证方法以添加特定验证 SearchError validateSearchConditions() override; }; /** * @brief 内容搜索策略工厂 */ class ContentSearchStrategyFactory : public SearchStrategyFactory { public: std::unique_ptr createStrategy( SearchType searchType, const SearchOptions &options) override; }; DFM_SEARCH_END_NS #endif // CONTENT_SEARCH_ENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies/000077500000000000000000000000001523477242100320755ustar00rootroot00000000000000basestrategy.h000066400000000000000000000014041523477242100346630ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CONTENT_BASE_STRATEGY_H #define CONTENT_BASE_STRATEGY_H #include "core/searchstrategy/basesearchstrategy.h" #include DFM_SEARCH_BEGIN_NS /** * @brief 内容搜索策略基类 */ class ContentBaseStrategy : public BaseSearchStrategy { Q_OBJECT public: explicit ContentBaseStrategy(const SearchOptions &options, QObject *parent = nullptr) : BaseSearchStrategy(options, parent) { } SearchType searchType() const override { return SearchType::Content; } protected: // 公共工具方法 int getMaxPreviewLength(); }; DFM_SEARCH_END_NS #endif // CONTENT_BASE_STRATEGY_H indexedstrategy.cpp000066400000000000000000000644201523477242100357330ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "indexedstrategy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "utils/cancellablecollector.h" #include "utils/contenthighlighter.h" #include "utils/lucenequeryutils.h" #include "utils/lucene_cancellation_compat.h" #include "utils/timerangeutils.h" using namespace Lucene; DFM_SEARCH_BEGIN_NS ContentIndexedStrategy::ContentIndexedStrategy(const SearchOptions &options, QObject *parent) : ContentBaseStrategy(options, parent) { initializeIndexing(); } ContentIndexedStrategy::~ContentIndexedStrategy() = default; void ContentIndexedStrategy::initializeIndexing() { // 获取索引目录 m_indexDir = Global::contentIndexDirectory(); // 检查索引目录是否存在 if (!QDir(m_indexDir).exists()) { qWarning() << "Content index directory does not exist:" << m_indexDir; } } void ContentIndexedStrategy::search(const SearchQuery &query) { m_results.clear(); try { // 执行内容索引搜索 performContentSearch(query); } catch (const std::exception &e) { qWarning() << "Content Index Search Exception:" << e.what(); emit errorOccurred(SearchError(ContentSearchErrorCode::ContentIndexException)); } } Lucene::QueryPtr ContentIndexedStrategy::buildLuceneQuery(const SearchQuery &query) { try { m_keywords.clear(); ContentOptionsAPI optAPI(m_options); // Use the member m_options bool mixedAndEnabled = optAPI.isFilenameContentMixedAndSearchEnabled(); Lucene::QueryPtr mainQuery; if (query.type() == SearchQuery::Type::Simple) { mainQuery = buildSimpleContentsQuery(query); } else if (query.type() == SearchQuery::Type::Boolean) { if (query.subQueries().isEmpty()) { // For an empty boolean query, match nothing. mainQuery = newLucene(); } else { // Determine which logic path to take for boolean queries if (mixedAndEnabled && query.booleanOperator() == SearchQuery::BooleanOperator::AND) { // New "advanced" AND logic for contents/filename mainQuery = buildAdvancedAndQuery(query); } else { // "Standard" contents-only logic for: // 1. OR queries (regardless of mixedAndEnabled value). // 2. AND queries when mixedAndEnabled is false. mainQuery = buildStandardBooleanContentsQuery(query); } } } else { qWarning() << "Unknown SearchQuery type encountered."; mainQuery = newLucene(); // Should not happen } // Add filename keyword query (before filters, so it replaces empty content query correctly) QString filenameKw = optAPI.filenameKeyword(); if (!filenameKw.isEmpty()) { Lucene::QueryPtr filenameQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::Content::kFilename), filenameKw); if (filenameQuery) { // Check if content keywords are effectively empty bool noContentKeywords = (query.type() == SearchQuery::Type::Simple) ? query.keyword().isEmpty() : (query.subQueries().isEmpty() || std::all_of(query.subQueries().cbegin(), query.subQueries().cend(), [](const auto &sq) { return sq.keyword().isEmpty(); })); if (noContentKeywords) { // Filename-only search: replace empty content query with filename query mainQuery = filenameQuery; } else if (mainQuery) { // Both content and filename: AND combination BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(filenameQuery, BooleanClause::MUST); mainQuery = finalQuery; } m_keywords.append(filenameKw); } } // Add path prefix query optimization QStringList searchPathsList = m_options.searchPaths(); if (mainQuery) { QueryPtr pathPrefixQuery = LuceneQueryUtils::buildMultiPathPrefixQuery( searchPathsList, QString::fromWCharArray(LuceneFieldNames::Content::kAncestorPaths)); if (pathPrefixQuery) { BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(pathPrefixQuery, BooleanClause::MUST); qInfo() << "Using multi-path prefix query for content search optimization:" << searchPathsList; mainQuery = finalQuery; } } // Add excluded paths filter (pushed down to query layer to avoid per-doc filtering) if (mainQuery) { const QStringList &excludedPaths = m_options.searchExcludedPaths(); if (!excludedPaths.isEmpty()) { QueryPtr excludedQuery = LuceneQueryUtils::buildMultiPathPrefixQuery( excludedPaths, QString::fromWCharArray(LuceneFieldNames::Content::kAncestorPaths)); if (excludedQuery) { BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(excludedQuery, BooleanClause::MUST_NOT); mainQuery = finalQuery; } } } // Add hidden file filter (pushed down to query layer) if (mainQuery && !m_options.includeHidden()) { QueryPtr hiddenQuery = Lucene::newLucene( Lucene::newLucene( LuceneFieldNames::Content::kIsHidden, L"Y")); BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(hiddenQuery, BooleanClause::MUST_NOT); mainQuery = finalQuery; } // When hiddenOnly is true, only return hidden files (MUST kIsHidden:Y) if (mainQuery && m_options.hiddenOnly()) { QueryPtr hiddenOnlyQuery = Lucene::newLucene( Lucene::newLucene( LuceneFieldNames::Content::kIsHidden, L"Y")); BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(hiddenOnlyQuery, BooleanClause::MUST); mainQuery = finalQuery; } // Add time range filter query if (m_options.hasTimeRangeFilter()) { TimeRangeFilter filter = m_options.timeRangeFilter(); QueryPtr timeQuery = TimeRangeUtils::buildTimeRangeFilterQuery( filter, LuceneFieldNames::Content::kBirthTime, LuceneFieldNames::Content::kModifyTime); if (timeQuery) { if (mainQuery) { BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(timeQuery, BooleanClause::MUST); mainQuery = finalQuery; } else { // Time filter alone is a valid query mainQuery = timeQuery; } } } // Add file size range filter query if (m_options.hasSizeRangeFilter()) { SizeRangeFilter sizeFilter = m_options.sizeRangeFilter(); QueryPtr sizeQuery = TimeRangeUtils::buildNumericRangeQuery( LuceneFieldNames::Content::kFileSize, sizeFilter.minSize(), sizeFilter.maxSize(), sizeFilter.includeLower(), sizeFilter.includeUpper()); if (sizeQuery) { if (mainQuery) { BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(sizeQuery, BooleanClause::MUST); mainQuery = finalQuery; } else { // Size filter alone is a valid query mainQuery = sizeQuery; } } } // Add file extension filter query (pushed down to index query layer) { const QStringList fileExtensions = optAPI.fileExtensions(); if (!fileExtensions.isEmpty() && mainQuery) { Lucene::BooleanQueryPtr extQuery = newLucene(); for (const QString &ext : fileExtensions) { extQuery->add( Lucene::newLucene( Lucene::newLucene( LuceneFieldNames::Content::kFileExt, ext.toStdWString())), Lucene::BooleanClause::SHOULD); } extQuery->setMinimumNumberShouldMatch(1); BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(extQuery, BooleanClause::MUST); mainQuery = finalQuery; } } return mainQuery; } catch (const Lucene::LuceneException &e) { qWarning() << "Error building Lucene query:" << QString::fromStdWString(e.getError()); return nullptr; } catch (const std::exception &e) { qWarning() << "Standard exception building Lucene query:" << e.what(); return nullptr; } } QueryPtr ContentIndexedStrategy::buildAdvancedAndQuery(const SearchQuery &query) { // This method implements the new "mixed" AND logic. Lucene::BooleanQueryPtr overallQuery = newLucene(); Lucene::BooleanQueryPtr mainAndClausesQuery = newLucene(); Lucene::BooleanQueryPtr allContentsQuery = newLucene(); Lucene::BooleanQueryPtr allFilenamesQuery = newLucene(); bool hasValidKeywords = false; for (const auto &subQuery : query.subQueries()) { m_keywords.append(subQuery.keyword()); if (subQuery.keyword().isEmpty()) { continue; // Skip empty keywords } hasValidKeywords = true; Lucene::QueryPtr contentsTermQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::Content::kContents), subQuery.keyword()); Lucene::QueryPtr filenameTermQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::Content::kFilename), subQuery.keyword()); // Build (contents:keyword OR filename:keyword) Lucene::BooleanQueryPtr combinedTermQuery = newLucene(); combinedTermQuery->add(contentsTermQuery, Lucene::BooleanClause::SHOULD); combinedTermQuery->add(filenameTermQuery, Lucene::BooleanClause::SHOULD); mainAndClausesQuery->add(combinedTermQuery, Lucene::BooleanClause::MUST); allContentsQuery->add(contentsTermQuery, Lucene::BooleanClause::MUST); allFilenamesQuery->add(filenameTermQuery, Lucene::BooleanClause::MUST); } if (!hasValidKeywords) { // All subQuery keywords were empty qWarning() << "No valid keywords found in advanced AND query"; return newLucene(); // Matches nothing } // New logic: Include results that match content OR exclude pure filename-only matches // Final query: ( (c:k1 OR f:k1) AND ... ) AND NOT (f:k1 AND f:k2 ... AND NOT (c:k1 AND c:k2 ...)) // This means: exclude documents that match all keywords in filename but don't match all keywords in content Lucene::BooleanQueryPtr pureFilenameQuery = newLucene(); pureFilenameQuery->add(allFilenamesQuery, Lucene::BooleanClause::MUST); pureFilenameQuery->add(allContentsQuery, Lucene::BooleanClause::MUST_NOT); overallQuery->add(mainAndClausesQuery, Lucene::BooleanClause::MUST); overallQuery->add(pureFilenameQuery, Lucene::BooleanClause::MUST_NOT); return overallQuery; } QueryPtr ContentIndexedStrategy::buildStandardBooleanContentsQuery(const SearchQuery &query) { // This method implements the "original" boolean logic, searching only "contents". Lucene::BooleanQueryPtr booleanQuery = newLucene(); for (const auto &subQuery : query.subQueries()) { m_keywords.append(subQuery.keyword()); if (subQuery.keyword().isEmpty()) { continue; // Skip empty keywords } Lucene::QueryPtr termQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::Content::kContents), subQuery.keyword()); booleanQuery->add(termQuery, query.booleanOperator() == SearchQuery::BooleanOperator::AND ? Lucene::BooleanClause::MUST : Lucene::BooleanClause::SHOULD); } return booleanQuery; } QueryPtr ContentIndexedStrategy::buildSimpleContentsQuery(const SearchQuery &query) { m_keywords.append(query.keyword()); if (query.keyword().isEmpty()) { return newLucene(); // Match nothing for empty keyword } return LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::Content::kContents), query.keyword()); } void ContentIndexedStrategy::processSearchResults(const Lucene::IndexSearcherPtr &searcher, const Lucene::Collection &scoreDocs) { // Measure the time taken to process search results QElapsedTimer resultTimer; resultTimer.start(); auto docsSize = scoreDocs.size(); ContentOptionsAPI optAPI(m_options); bool enableHTML = optAPI.isSearchResultHighlightEnabled(); int previewLen = optAPI.maxPreviewLength() > 0 ? optAPI.maxPreviewLength() : 50; bool enableRetrieval = optAPI.isFullTextRetrievalEnabled(); bool detailedResults = m_options.detailedResultsEnabled(); // Build field selector to avoid loading the large 'contents' field when not needed. // The contents field stores full document text and loading it for every result // (even when only path is needed) causes significant disk I/O overhead. Lucene::Collection fieldsToLoad = Lucene::Collection::newInstance(); if (enableRetrieval) { fieldsToLoad.add(LuceneFieldNames::Content::kContents); } fieldsToLoad.add(LuceneFieldNames::Content::kPath); if (Q_UNLIKELY(detailedResults)) { fieldsToLoad.add(LuceneFieldNames::Content::kFilename); fieldsToLoad.add(LuceneFieldNames::Content::kIsHidden); fieldsToLoad.add(LuceneFieldNames::Content::kModifyTime); fieldsToLoad.add(LuceneFieldNames::Content::kBirthTime); fieldsToLoad.add(LuceneFieldNames::Content::kFileSize); } Lucene::FieldSelectorPtr fieldSelector = newLucene(fieldsToLoad); // Pre-allocate to avoid reallocation during append m_results.reserve(m_results.size() + static_cast(docsSize)); for (int32_t i = 0; i < docsSize; ++i) { if (m_cancelledRef && m_cancelledRef->load()) { qInfo() << "Content search cancelled"; break; } try { Lucene::ScoreDocPtr scoreDoc = scoreDocs[i]; if (!scoreDoc || scoreDoc->doc < 0) { qWarning() << "Invalid ScoreDoc at index" << i; continue; } Lucene::DocumentPtr doc; try { doc = searcher->doc(scoreDoc->doc, fieldSelector); if (!doc) { qWarning() << "Failed to retrieve document at index:" << scoreDoc->doc; continue; } } catch (const Lucene::LuceneException &e) { qWarning() << "Exception while retrieving document:" << QString::fromStdWString(e.getError()); continue; } catch (const std::exception &e) { qWarning() << "Standard exception while retrieving document:" << e.what(); continue; } // Path filtering, hidden file exclusion — handled at query layer Lucene::String pathField = doc->get(LuceneFieldNames::Content::kPath); if (pathField.empty()) { qWarning() << "Document missing path field at index:" << scoreDoc->doc; continue; } SearchResult result(QString::fromStdWString(pathField)); ContentResultAPI resultApi(result); result.setCustomAttribute("plainContentMatch", QString()); result.setCustomAttribute("snippetOffset", -1); if (enableRetrieval) { try { Lucene::String contentField = doc->get(LuceneFieldNames::Content::kContents); if (!contentField.empty()) { const QString content = QString::fromStdWString(contentField); // Fix: keep a dedicated plain-text snippet for CLI verbose output // instead of reusing HTML-oriented highlightedContent. const ContentHighlighter::PlainSnippetResult plainSnippet = ContentHighlighter::plainSnippet( m_keywords, content, previewLen, previewLen); const QString highlightedContent = ContentHighlighter::customHighlight(m_keywords, content, previewLen, enableHTML); resultApi.setHighlightedContent(highlightedContent); resultApi.setCharCount(content.size()); result.setCustomAttribute("plainContentMatch", plainSnippet.content); result.setCustomAttribute("snippetOffset", plainSnippet.snippetOffset); } } catch (const Lucene::LuceneException &e) { qWarning() << "Exception retrieving content field:" << QString::fromStdWString(e.getError()); } catch (const std::exception &e) { qWarning() << "Standard exception retrieving content field:" << e.what(); } } // 设置详细结果(如果启用) if (Q_UNLIKELY(detailedResults)) { // 文件名 Lucene::String filenameField = doc->get(LuceneFieldNames::Content::kFilename); if (!filenameField.empty()) { resultApi.setFilename(QString::fromStdWString(filenameField)); } // 隐藏状态 Lucene::String hiddenField = doc->get(LuceneFieldNames::Content::kIsHidden); if (!hiddenField.empty()) { resultApi.setIsHidden(QString::fromStdWString(hiddenField).toLower() == "y"); } // 修改时间 Lucene::String modifyTimeField = doc->get(LuceneFieldNames::Content::kModifyTime); if (!modifyTimeField.empty()) { bool ok = false; qint64 timestamp = QString::fromStdWString(modifyTimeField).toLongLong(&ok); if (ok && timestamp > 0) { resultApi.setModifyTimestamp(timestamp); } } // 创建时间 Lucene::String birthTimeField = doc->get(LuceneFieldNames::Content::kBirthTime); if (!birthTimeField.empty()) { bool ok = false; qint64 timestamp = QString::fromStdWString(birthTimeField).toLongLong(&ok); if (ok && timestamp > 0) { resultApi.setBirthTimestamp(timestamp); } } // 文件大小 Lucene::String fileSizeField = doc->get(LuceneFieldNames::Content::kFileSize); if (!fileSizeField.empty()) { bool ok = false; qint64 fileSize = QString::fromStdWString(fileSizeField).toLongLong(&ok); if (ok && fileSize > 0) { resultApi.setFileSizeBytes(fileSize); } } } // 添加到结果集合 m_results.append(std::move(result)); // 实时发送结果 if (Q_UNLIKELY(m_options.resultFoundEnabled())) emit resultFound(m_results.last()); } catch (const Lucene::LuceneException &e) { qWarning() << "Error processing result:" << QString::fromStdWString(e.getError()); continue; } catch (const std::exception &e) { qWarning() << "Standard exception:" << e.what(); continue; } catch (...) { qWarning() << "Unknown exception during result processing"; continue; } } qInfo() << "Content result processing time:" << resultTimer.elapsed() << "ms"; emit searchFinished(m_results); } void ContentIndexedStrategy::performContentSearch(const SearchQuery &query) { // 防御性空指针拦截:正常流程 m_cancelledRef 由引擎注入,非空; // 若注入失败(setCancelledFlag 拒绝空指针),此处拦截避免底层 // SearchCancellationGuard / CancellableCollector 解引用 nullptr 导致 SIGSEGV。 if (!m_cancelledRef) { qWarning("ContentIndexedStrategy::performContentSearch: cancellation flag is null, aborting search"); emit errorOccurred(SearchError(ContentSearchErrorCode::ContentIndexException)); emit searchFinished(m_results); return; } // RAII 守护类:自动管理取消标志的生命周期 // 构造时设置标志,析构时自动清理(即使发生异常) SearchCancellationGuard guard(m_cancelledRef); try { // 获取索引目录 FSDirectoryPtr directory = FSDirectory::open(m_indexDir.toStdWString()); if (!directory) { qWarning() << "Failed to open index directory:" << m_indexDir; emit errorOccurred(SearchError(ContentSearchErrorCode::ContentIndexNotFound)); return; } // 获取索引读取器 IndexReaderPtr reader = IndexReader::open(directory, true); if (!reader || reader->numDocs() == 0) { qWarning() << "Index is empty or cannot be opened"; emit errorOccurred(SearchError(ContentSearchErrorCode::ContentIndexNotFound)); return; } // 创建搜索器 IndexSearcherPtr searcher = newLucene(reader); // 构建查询 m_currentQuery = buildLuceneQuery(query); if (!m_currentQuery) { qWarning() << "Failed to build Lucene query"; emit errorOccurred(SearchError(ContentSearchErrorCode::ContentIndexException)); return; } // 执行搜索 // Measure the time taken to execute the search QElapsedTimer searchTimer; searchTimer.start(); int32_t maxResults = m_options.maxResults() > 0 ? m_options.maxResults() : reader->numDocs(); // 使用自定义 CancellableCollector 实现可中断搜索 Collection scoreDocs; try { // 创建可取消的收集器 boost::shared_ptr collector = newLucene(m_cancelledRef, maxResults); // 执行搜索,使用自定义收集器 qInfo() << "Content search execution start:" << query.keyword(); searcher->search(m_currentQuery, collector); // 获取收集到的文档 scoreDocs = collector->getScoreDocs(); qInfo() << "Content search execution time:" << searchTimer.elapsed() << "ms" << "Total hits:" << collector->getTotalHits() << "Collected:" << scoreDocs.size() << "Keyword:" << query.keyword() << "Cancelled" << (m_cancelledRef ? m_cancelledRef->load() : false); } catch (const SearchCancelledException &e) { qInfo() << "Content search cancelled during execution"; emit searchFinished(m_results); return; } catch (const RuntimeException &e) { #if LUCENE_HAS_SEARCH_CANCELLATION // 仅在支持 SearchCancellation 时检查取消异常 // 检查是否是在 ExactPhraseScorer::phraseFreq() 中抛出的取消异常 QString errorMsg = QString::fromStdWString(e.getError()); if (errorMsg.contains("cancelled", Qt::CaseInsensitive)) { qInfo() << "Content search cancelled in phraseFreq():" << errorMsg; emit searchFinished(m_results); return; } #endif // 其他运行时异常,继续抛出 throw; } // 处理搜索结果 processSearchResults(searcher, scoreDocs); } catch (const LuceneException &e) { qWarning() << "Lucene search exception:" << QString::fromStdWString(e.getError()); emit errorOccurred(SearchError(ContentSearchErrorCode::ContentIndexException)); } catch (const std::exception &e) { qWarning() << "Standard exception:" << e.what(); emit errorOccurred(SearchError(ContentSearchErrorCode::ContentIndexException)); } } void ContentIndexedStrategy::cancel() { if (m_cancelledRef) m_cancelledRef->store(true); } DFM_SEARCH_END_NS indexedstrategy.h000066400000000000000000000035441523477242100354000ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CONTENT_INDEXED_STRATEGY_H #define CONTENT_INDEXED_STRATEGY_H #include "basestrategy.h" #include #include #include #include // 前向声明 class ContentSearcher; DFM_SEARCH_BEGIN_NS /** * @brief 内容索引搜索策略 */ class ContentIndexedStrategy : public ContentBaseStrategy { Q_OBJECT public: explicit ContentIndexedStrategy(const SearchOptions &options, QObject *parent = nullptr); ~ContentIndexedStrategy() override; void search(const SearchQuery &query) override; void cancel() override; private: // 初始化索引 void initializeIndexing(); // 执行内容搜索 void performContentSearch(const SearchQuery &query); // Build Lucene query Lucene::QueryPtr buildLuceneQuery(const SearchQuery &query); // Helper for simple queries (original logic for "contents" field) Lucene::QueryPtr buildSimpleContentsQuery(const SearchQuery &query); // Helper for "standard" boolean logic (original logic for "contents" field, handles AND/OR) Lucene::QueryPtr buildStandardBooleanContentsQuery(const SearchQuery &query); // Helper for "advanced" mixed AND logic (searches "contents" and "filename") Lucene::QueryPtr buildAdvancedAndQuery(const SearchQuery &query); // Operator is implicitly AND // Process search results void processSearchResults(const Lucene::IndexSearcherPtr &searcher, const Lucene::Collection &scoreDocs); QString m_indexDir; Lucene::QueryPtr m_currentQuery; // 存储当前查询 QStringList m_keywords; }; DFM_SEARCH_END_NS #endif // CONTENT_INDEXED_STRATEGY_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/000077500000000000000000000000001523477242100244205ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/abstractsearchengine.cpp000066400000000000000000000013741523477242100313100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "abstractsearchengine.h" DFM_SEARCH_BEGIN_NS AbstractSearchEngine::AbstractSearchEngine(QObject *parent) : QObject(parent), m_status(SearchStatus::Ready), m_cancelled(false) { qRegisterMetaType(); qRegisterMetaType(); } AbstractSearchEngine::~AbstractSearchEngine() { } void AbstractSearchEngine::init() { } void AbstractSearchEngine::setStatus(SearchStatus status) { m_status.store(status); emit statusChanged(status); } void AbstractSearchEngine::reportError(const SearchError &error) { emit errorOccurred(error); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/abstractsearchengine.h000066400000000000000000000075221523477242100307560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef ABSTRACTSEARCHENGINE_H #define ABSTRACTSEARCHENGINE_H #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief The AbstractSearchEngine class provides the base interface for all search engines * * This abstract class defines the common interface that all search engine implementations * must provide. It includes methods for search operations, status management, and error handling. */ class AbstractSearchEngine : public QObject { Q_OBJECT public: /** * @brief Constructor * @param parent The parent QObject */ explicit AbstractSearchEngine(QObject *parent = nullptr); /** * @brief Virtual destructor */ virtual ~AbstractSearchEngine(); /** * @brief Initialize the search engine */ virtual void init(); /** * @brief Get the search type * @return The SearchType of this engine */ virtual SearchType searchType() const = 0; /** * @brief Get the current search options * @return The current SearchOptions */ virtual SearchOptions searchOptions() const = 0; /** * @brief Set the search options * @param options The new SearchOptions */ virtual void setSearchOptions(const SearchOptions &options) = 0; /** * @brief Get the current search status * @return The current SearchStatus */ virtual SearchStatus status() const = 0; /** * @brief Perform an asynchronous search * @param query The search query to execute */ virtual void search(const SearchQuery &query) = 0; /** * @brief Perform an asynchronous search with callback * @param query The search query to execute * @param callback The callback function to process results */ virtual void searchWithCallback(const SearchQuery &query, SearchEngine::ResultCallback callback) = 0; /** * @brief Perform a synchronous search * @param query The search query to execute * @return A SearchResultExpected containing the results or an error */ virtual SearchResultExpected searchSync(const SearchQuery &query) = 0; /** * @brief Cancel the current search operation */ virtual void cancel() = 0; Q_SIGNALS: /** * @brief Emitted when a search operation starts */ void searchStarted(); /** * @brief Emitted when a new search result is found * @param results The found search results */ void resultsFound(const DFMSEARCH::SearchResultList &results); /** * @brief Emitted when the search status changes * @param status The new search status */ void statusChanged(SearchStatus status); /** * @brief Emitted when a search operation completes * @param results The list of all search results */ void searchFinished(const DFMSEARCH::SearchResultList &results); /** * @brief Emitted when a search operation is cancelled */ void searchCancelled(); /** * @brief Emitted when an error occurs during search * @param error The SearchError that occurred */ void errorOccurred(const DFMSEARCH::SearchError &error); protected: /** * @brief Set the current search status * @param status The new search status */ void setStatus(SearchStatus status); /** * @brief Report a search error * @param error The SearchError to report */ void reportError(const SearchError &error); std::atomic m_status; ///< Current search status std::atomic m_cancelled; ///< Search cancellation flag }; DFM_SEARCH_END_NS #endif // ABSTRACTSEARCHENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/genericsearchengine.cpp000066400000000000000000000202431523477242100311150ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "genericsearchengine.h" #include #include #include #include DFM_SEARCH_BEGIN_NS DCORE_USE_NAMESPACE constexpr int kDefaultBatchTime = 1000; // 1000ms GenericSearchEngine::GenericSearchEngine(QObject *parent) : AbstractSearchEngine(parent), m_worker(nullptr) { // 设置初始状态 m_status.store(SearchStatus::Ready); // 设置批处理定时器 m_batchTimer.setInterval(kDefaultBatchTime); connect(&m_batchTimer, &QTimer::timeout, this, [this]() { if (!m_batchResults.isEmpty() && m_status.load() == SearchStatus::Searching) { emit resultsFound(m_batchResults); m_batchResults.clear(); } }); } GenericSearchEngine::~GenericSearchEngine() { // 停止工作线程 m_workerThread.quit(); m_workerThread.wait(); // 停止批处理定时器 m_batchTimer.stop(); } void GenericSearchEngine::init() { // 创建工作对象 m_worker = new SearchWorker(); m_worker->moveToThread(&m_workerThread); // 连接线程生命周期信号 connect(&m_workerThread, &QThread::finished, m_worker, &QObject::deleteLater); // 连接控制信号(主线程 -> 工作线程) connect(this, &GenericSearchEngine::requestSearch, m_worker, &SearchWorker::doSearch); // 连接结果信号(工作线程 -> 主线程) connect(m_worker, &SearchWorker::resultFound, this, &GenericSearchEngine::handleSearchResult); connect(m_worker, &SearchWorker::searchFinished, this, &GenericSearchEngine::handleSearchFinished); connect(m_worker, &SearchWorker::errorOccurred, this, &GenericSearchEngine::handleErrorOccurred); // 设置策略工厂 setupStrategyFactory(); // 将引擎级取消标志注入 worker,使策略能即时读取取消状态 m_worker->setEngineCancelledFlag(&m_cancelled); // 启动工作线程 m_workerThread.start(); } SearchOptions GenericSearchEngine::searchOptions() const { return m_options; } void GenericSearchEngine::setSearchOptions(const SearchOptions &options) { m_options = options; // 更新批处理定时器间隔 m_batchTimer.setInterval(m_options.batchTime()); } SearchStatus GenericSearchEngine::status() const { return m_status.load(); } void GenericSearchEngine::search(const SearchQuery &query) { if (m_status.load() == SearchStatus::Searching) return; m_cancelled.store(false); setStatus(SearchStatus::Searching); emit searchStarted(); // 清空结果列表 m_results.clear(); // 清空批处理结果并启动定时器 m_batchResults.clear(); m_batchTimer.setInterval(m_options.batchTime()); m_batchTimer.start(); // 保存当前查询 m_currentQuery = query; // 验证搜索条件 auto validationResult = validateSearchConditions(); if (validationResult.isError()) { reportError(validationResult); setStatus(SearchStatus::Error); return; } // 发射信号请求工作线程执行搜索 emit requestSearch(query, m_options, searchType()); } void GenericSearchEngine::searchWithCallback(const SearchQuery &query, SearchEngine::ResultCallback callback) { m_callback = callback; searchSync(query); } SearchResultExpected GenericSearchEngine::searchSync(const SearchQuery &query) { // 保存当前查询 m_currentQuery = query; // 验证搜索条件 auto validationResult = validateSearchConditions(); if (validationResult.isError()) { return DUnexpected(validationResult); } // 执行同步搜索 return doSyncSearch(query); } void GenericSearchEngine::cancel() { // 设置取消标志,工作线程通过注入的 flag 即时响应 m_cancelled.store(true); // 停止批处理定时器 m_batchTimer.stop(); if (m_status.load() != SearchStatus::Ready && m_status.load() != SearchStatus::Finished) { setStatus(SearchStatus::Cancelled); emit searchCancelled(); } } void GenericSearchEngine::handleSearchResult(const DFMSEARCH::SearchResult &result) { // 存储结果到全局结果列表 m_results.append(result); // 如果设置了回调,立即调用回调,如果回调返回true则终止搜索 if (m_callback) { if (m_callback(result)) { // 回调返回 true,取消搜索 cancel(); return; } } // 将结果添加到批处理队列 m_batchResults.append(result); } void GenericSearchEngine::handleSearchFinished(const DFMSEARCH::SearchResultList &results) { // 停止批处理定时器 m_batchTimer.stop(); // 发送剩余的批处理结果 if (!m_batchResults.isEmpty()) { emit resultsFound(m_batchResults); m_batchResults.clear(); } // 确保所有结果都已添加到m_results if (m_results.size() != results.size()) { m_results = results; } // 设置状态为完成 setStatus(SearchStatus::Finished); // 如果没有通过 handleSearchResult 中断搜索,在这里执行最终检查 // 注意:通常在 handleSearchResult 已处理大部分情况,此处仅作为备用 // 发送完成信号 emit searchFinished(m_results); } void GenericSearchEngine::handleErrorOccurred(const DFMSEARCH::SearchError &error) { // 停止批处理定时器 m_batchTimer.stop(); // 保存错误 m_lastError = error; // 设置状态 setStatus(SearchStatus::Error); // 转发错误信号 reportError(error); } SearchResultExpected GenericSearchEngine::doSyncSearch(const SearchQuery &query) { // 重置取消标志,避免上次搜索的取消状态残留(与异步 search() 一致) m_cancelled.store(false); // 重置同步搜索状态 m_results.clear(); m_lastError = SearchError(SearchErrorCode::Success); // 创建事件循环 QEventLoop eventLoop; QTimer timeoutTimer; timeoutTimer.setSingleShot(true); timeoutTimer.setInterval(m_options.syncSearchTimeout() * 1000); // 超时 // 连接信号到事件循环 connect(this, &GenericSearchEngine::searchFinished, &eventLoop, &QEventLoop::quit); connect(this, &GenericSearchEngine::errorOccurred, &eventLoop, &QEventLoop::quit); connect(&timeoutTimer, &QTimer::timeout, &eventLoop, &QEventLoop::quit); // 发射信号启动异步搜索 emit requestSearch(query, m_options, searchType()); // 启动超时计时器 timeoutTimer.start(); // 进入事件循环等待 eventLoop.exec(); // 检查是否超时 if (!timeoutTimer.isActive()) { // 超时:设置取消标志通知工作线程停止 m_cancelled.store(true); return DUnexpected { SearchError(SearchErrorCode::SearchTimeout) }; } // 检查是否被取消 if (m_cancelled.load()) { return m_results; } // 检查是否有错误 if (m_lastError.isError()) { return DUnexpected { m_lastError }; } return m_results; } SearchError GenericSearchEngine::validateSearchConditions() { if (m_currentQuery.type() == SearchQuery::Type::Simple || m_currentQuery.type() == SearchQuery::Type::Wildcard) { if (m_options.searchPath().isEmpty()) { return SearchError(SearchErrorCode::PathIsEmpty); } // 检查目录是否存在且是否为目录 QFileInfo pathInfo(m_options.searchPath()); if (!pathInfo.exists() || !pathInfo.isDir()) { return SearchError(SearchErrorCode::PathNotFound); } // 检查读权限 if (!pathInfo.isReadable()) { return SearchError(SearchErrorCode::PermissionDenied); } } else if (m_currentQuery.type() == SearchQuery::Type::Boolean) { if (m_currentQuery.subQueries().isEmpty()) return SearchError(SearchErrorCode::InvalidBoolean); } // 返回空结果列表表示条件有效 return SearchError(SearchErrorCode::Success); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/genericsearchengine.h000066400000000000000000000107621523477242100305670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef GENERICSEARCHENGINE_H #define GENERICSEARCHENGINE_H #include #include #include #include "abstractsearchengine.h" #include "searchstrategy/searchworker.h" DFM_SEARCH_BEGIN_NS /** * @brief The GenericSearchEngine class provides a base implementation for all search engines * * This class implements the common functionality required by all search engines, * including thread management, result handling, and error reporting. It serves as * a base class for specific search engine implementations. */ class GenericSearchEngine : public AbstractSearchEngine { Q_OBJECT public: /** * @brief Constructor * @param parent The parent QObject */ explicit GenericSearchEngine(QObject *parent = nullptr); /** * @brief Virtual destructor */ virtual ~GenericSearchEngine(); /** * @brief Initialize the search engine and its worker thread */ virtual void init() override; /** * @brief Get the current search options * @return The current SearchOptions */ SearchOptions searchOptions() const override; /** * @brief Set the search options * @param options The new SearchOptions */ void setSearchOptions(const SearchOptions &options) override; /** * @brief Get the current search status * @return The current SearchStatus */ SearchStatus status() const override; /** * @brief Perform an asynchronous search * @param query The search query to execute */ void search(const SearchQuery &query) override; /** * @brief Perform an asynchronous search with callback * @param query The search query to execute * @param callback The callback function to process results */ void searchWithCallback(const SearchQuery &query, SearchEngine::ResultCallback callback) override; /** * @brief Perform a synchronous search * @param query The search query to execute * @return A SearchResultExpected containing the results or an error */ SearchResultExpected searchSync(const SearchQuery &query) override; /** * @brief Cancel the current search operation */ void cancel() override; Q_SIGNALS: /** * @brief Internal signal to request worker thread to execute search */ void requestSearch(const DFMSEARCH::SearchQuery &query, const DFMSEARCH::SearchOptions &options, DFMSEARCH::SearchType searchType); protected: /** * @brief Set up the strategy factory for this search engine * * This pure virtual method must be implemented by derived classes * to provide the appropriate search strategy factory. */ virtual void setupStrategyFactory() = 0; /** * @brief Execute a synchronous search operation * @param query The search query to execute * @return A SearchResultExpected containing the results or an error */ SearchResultExpected doSyncSearch(const SearchQuery &query); /** * @brief Validate search conditions * @return A SearchError indicating any validation errors */ virtual SearchError validateSearchConditions(); private Q_SLOTS: /** * @brief Handle a new search result * @param result The found search result */ void handleSearchResult(const DFMSEARCH::SearchResult &result); /** * @brief Handle search completion * @param results The list of all search results */ void handleSearchFinished(const DFMSEARCH::SearchResultList &results); /** * @brief Handle search errors * @param error The SearchError that occurred */ void handleErrorOccurred(const DFMSEARCH::SearchError &error); protected: SearchOptions m_options; ///< Current search options SearchQuery m_currentQuery; ///< Current search query SearchEngine::ResultCallback m_callback; ///< Current result callback SearchResultList m_results; ///< List of search results QThread m_workerThread; ///< Worker thread for search operations SearchWorker *m_worker; ///< Search worker object SearchError m_lastError; ///< Last occurred error // Result batching members QTimer m_batchTimer; ///< Timer for batch sending results SearchResultList m_batchResults; ///< Cached results waiting to be sent }; DFM_SEARCH_END_NS #endif // GENERICSEARCHENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchengine.cpp000066400000000000000000000072131523477242100275620ustar00rootroot00000000000000 // SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "contentsearch/contentsearchengine.h" #include "filenamesearch/filenamesearchengine.h" #include "ocrtextsearch/ocrtextsearchengine.h" #include "recentsearch/recentsearchengine.h" DFM_SEARCH_BEGIN_NS SearchEngine *SearchEngine::create(SearchType type, QObject *parent) { return SearchFactory::createEngine(type, parent); } SearchEngine::SearchEngine(QObject *parent) : QObject(parent), d_ptr(nullptr) { // 默认创建文件名搜索引擎 setSearchType(SearchType::FileName); } SearchEngine::SearchEngine(SearchType type, QObject *parent) : QObject(parent), d_ptr(nullptr) { setSearchType(type); } SearchEngine::~SearchEngine() = default; SearchType SearchEngine::searchType() const { if (d_ptr) { return d_ptr->searchType(); } return SearchType::FileName; // 默认 } void SearchEngine::setSearchType(SearchType type) { // 如果类型相同且引擎已存在,则不重新创建 if (d_ptr && d_ptr->searchType() == type) { return; } // 根据类型创建相应的引擎 switch (type) { case SearchType::FileName: d_ptr = std::make_unique(); break; case SearchType::Content: d_ptr = std::make_unique(); break; case SearchType::Ocr: d_ptr = std::make_unique(); break; case SearchType::Recent: d_ptr = std::make_unique(); break; default: qWarning("Unsupported search type: %d", static_cast(type)); return; } d_ptr->init(); // 连接信号 connect(d_ptr.get(), &AbstractSearchEngine::searchStarted, this, &SearchEngine::searchStarted); connect(d_ptr.get(), &AbstractSearchEngine::resultsFound, this, &SearchEngine::resultsFound); connect(d_ptr.get(), &AbstractSearchEngine::statusChanged, this, &SearchEngine::statusChanged); connect(d_ptr.get(), &AbstractSearchEngine::searchFinished, this, &SearchEngine::searchFinished); connect(d_ptr.get(), &AbstractSearchEngine::searchCancelled, this, &SearchEngine::searchCancelled); connect(d_ptr.get(), &AbstractSearchEngine::errorOccurred, this, &SearchEngine::errorOccurred); } SearchOptions SearchEngine::searchOptions() const { if (d_ptr) { return d_ptr->searchOptions(); } return SearchOptions(); } void SearchEngine::setSearchOptions(const SearchOptions &options) { if (d_ptr) { d_ptr->setSearchOptions(options); } } SearchStatus SearchEngine::status() const { if (d_ptr) { return d_ptr->status(); } return SearchStatus::Error; } void SearchEngine::search(const SearchQuery &query) { if (d_ptr) { d_ptr->search(query); } } void SearchEngine::searchWithCallback(const SearchQuery &query, ResultCallback callback) { if (d_ptr) { // 自动开启 resultFoundEnabled 以确保回调能正常工作 SearchOptions options = d_ptr->searchOptions(); options.setResultFoundEnabled(true); d_ptr->setSearchOptions(options); d_ptr->searchWithCallback(query, callback); } } SearchResultExpected SearchEngine::searchSync(const SearchQuery &query) { if (d_ptr) { return d_ptr->searchSync(query); } return QList(); } void SearchEngine::cancel() { if (d_ptr) { d_ptr->cancel(); } } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searcherror.cpp000066400000000000000000000136741523477242100274560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "dfm-search/searcherror.h" DFM_SEARCH_BEGIN_NS // 实现基础错误分类的消息 std::string SearchErrorCategory::message(int ev) const { switch (static_cast(ev)) { case SearchErrorCode::Success: return "Success: The operation completed successfully."; case SearchErrorCode::InvalidQuery: return "Invalid search query: The provided search query is not valid. Please check the syntax and try again."; case SearchErrorCode::PathIsEmpty: return "Path is empty: The search path provided is empty. Please specify a valid path."; case SearchErrorCode::PathNotFound: return "Path not found: The specified search path does not exist. Please verify the path."; case SearchErrorCode::SearchTimeout: return "Search timeout: The search operation took too long and has timed out. Please try again with a more specific query."; case SearchErrorCode::InternalError: return "Internal error: An unexpected error occurred during the search operation. Please check the logs for more details."; case SearchErrorCode::InvalidBoolean: return "Invalid boolean: The boolean operation specified in the query is not valid. Please check the query syntax."; case SearchErrorCode::InvalidSerchMethod: return "Invalid search method"; // ... 其他错误消息 default: return "Unknown error: An unknown error occurred. Please contact support."; } } QString SearchErrorCategory::qMessage(int ev) const { return QString::fromStdString(message(ev)); } // 实现文件名搜索错误分类的消息 std::string FileNameSearchErrorCategory::message(int ev) const { switch (static_cast(ev)) { case FileNameSearchErrorCode::KeywordIsEmpty: return "Keyword is empty: The search keyword cannot be empty. Please provide a valid keyword."; case FileNameSearchErrorCode::KeywordTooLong: return "Keyword too long: The search keyword exceeds the maximum allowed length. Please shorten the keyword."; case FileNameSearchErrorCode::InvalidPinyinFormat: return "Invalid Pinyin format: The provided Pinyin format is not valid. Please check the format and try again."; case FileNameSearchErrorCode::InvalidFileTypes: return "Invalid file types: One or more specified file types are not valid. Please check the file types and try again."; case FileNameSearchErrorCode::FileNameIndexNotFound: return "File name index not found: The search index for file names could not be found. Please ensure the index is created."; case FileNameSearchErrorCode::FileNameIndexException: return "File name index exception: An error occurred while accessing the file name index. Please check the index integrity."; // ... 其他错误消息 default: return "Unknown filename search error: An unknown error occurred related to filename search. Please contact support."; } } std::string ContentSearchErrorCategory::message(int ev) const { switch (static_cast(ev)) { case ContentSearchErrorCode::KeywordTooShort: return "Keyword too short: The search keyword is too short to perform a search. Please provide a longer keyword."; case ContentSearchErrorCode::WildcardNotSupported: return "Wildcard not supported: Wildcard search is not supported for content search. Please use simple or boolean query instead."; case ContentSearchErrorCode::ContentIndexNotFound: return "Content index not found: The content index could not be found. Please ensure the index is created."; case ContentSearchErrorCode::ContentIndexException: return "Content index exception: An error occurred while accessing the content index. Please check the index integrity."; // ... 其他错误消息 default: return "Unknown content search error: An unknown error occurred related to content search. Please contact support."; } } std::string OcrTextSearchErrorCategory::message(int ev) const { switch (static_cast(ev)) { case OcrTextSearchErrorCode::KeywordTooShort: return "Keyword too short: The search keyword is too short to perform an OCR text search. Please provide a longer keyword."; case OcrTextSearchErrorCode::WildcardNotSupported: return "Wildcard not supported: Wildcard search is not supported for OCR text search. Please use simple or boolean query instead."; case OcrTextSearchErrorCode::OcrTextIndexNotFound: return "OCR text index not found: The OCR text index could not be found. Please ensure the index is created."; case OcrTextSearchErrorCode::OcrTextIndexException: return "OCR text index exception: An error occurred while accessing the OCR text index. Please check the index integrity."; default: return "Unknown OCR text search error: An unknown error occurred related to OCR text search. Please contact support."; } } // ... 实现其他错误分类的消息方法 ... // 获取错误分类单例 const SearchErrorCategory &search_category() { static SearchErrorCategory c; return c; } const FileNameSearchErrorCategory &filename_search_category() { static FileNameSearchErrorCategory c; return c; } const ContentSearchErrorCategory &content_search_category() { static ContentSearchErrorCategory c; return c; } const OcrTextSearchErrorCategory &ocrtext_search_category() { static OcrTextSearchErrorCategory c; return c; } // ... 实现其他错误分类单例 ... QString SearchError::message() const { if (auto cat = dynamic_cast(&m_code.category())) { return cat->qMessage(m_code.value()); } return QString::fromStdString(m_code.message()); } QString SearchError::name() const { return QString::fromLocal8Bit(m_code.category().name()); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchfactory.cpp000066400000000000000000000030511523477242100277600ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include DFM_SEARCH_BEGIN_NS SearchEngine *SearchFactory::createEngine(SearchType type, QObject *parent) { SearchEngine *engine = nullptr; switch (type) { case SearchType::FileName: engine = new SearchEngine(type, parent); break; case SearchType::Content: engine = new SearchEngine(type, parent); break; case SearchType::Ocr: engine = new SearchEngine(type, parent); break; case SearchType::Recent: engine = new SearchEngine(type, parent); break; case SearchType::Semantic: case SearchType::Custom: // TODO: Created by application based on provider break; } return engine; } SearchQuery SearchFactory::createQuery(const QString &keyword, SearchQuery::Type type) { switch (type) { case SearchQuery::Type::Simple: return SearchQuery::createSimpleQuery(keyword); case SearchQuery::Type::Boolean: // NOTE: space return SearchQuery::createBooleanQuery(keyword.split(" ")); case SearchQuery::Type::Wildcard: return SearchQuery(keyword, SearchQuery::Type::Wildcard); } return SearchQuery(keyword); } SearchQuery SearchFactory::createQuery(const QStringList &keywords, SearchQuery::Type type) { if (type == SearchQuery::Type::Boolean) return SearchQuery::createBooleanQuery(keywords); return SearchQuery(); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchoptions.cpp000066400000000000000000000115111523477242100300040ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "searchoptionsdata.h" DFM_SEARCH_BEGIN_NS SearchOptionsData::SearchOptionsData() : method(SearchMethod::Indexed), caseSensitive(false), searchPath(QDir::homePath()), includeHidden(false), hiddenOnly(false), maxResults(-1), resultFoundEnabled(false), detailedResultsEnabled(false) { } ///////////// SearchOptions::SearchOptions() : d(std::make_unique()) { } SearchOptions::SearchOptions(const SearchOptions &other) : d(std::make_unique(*other.d)) { } SearchOptions::SearchOptions(SearchOptions &&other) noexcept : d(std::move(other.d)) { } SearchOptions::~SearchOptions() = default; SearchOptions &SearchOptions::operator=(const SearchOptions &other) { if (this != &other) { d = std::make_unique(*other.d); } return *this; } SearchOptions &SearchOptions::operator=(SearchOptions &&other) noexcept { if (this != &other) { d = std::move(other.d); } return *this; } SearchMethod SearchOptions::method() const { return d->method; } void SearchOptions::setSearchMethod(SearchMethod method) { d->method = method; } bool SearchOptions::caseSensitive() const { return d->caseSensitive; } void SearchOptions::setCaseSensitive(bool sensitive) { d->caseSensitive = sensitive; } QString SearchOptions::searchPath() const { if (!d->searchPathsList.isEmpty()) { return d->searchPathsList.first(); } return d->searchPath; } void SearchOptions::setSearchPath(const QString &path) { d->searchPath = path; d->searchPathsList.clear(); } QStringList SearchOptions::searchPaths() const { if (!d->searchPathsList.isEmpty()) { return d->searchPathsList; } if (!d->searchPath.isEmpty()) { return QStringList { d->searchPath }; } return {}; } void SearchOptions::setSearchPaths(const QStringList &paths) { d->searchPathsList = paths; if (!paths.isEmpty()) { d->searchPath = paths.first(); } } QStringList SearchOptions::searchExcludedPaths() const { return d->searchExcludedPaths; } void SearchOptions::setSearchExcludedPaths(const QStringList &excludedPaths) { d->searchExcludedPaths = excludedPaths; } void SearchOptions::setIncludeHidden(bool include) { d->includeHidden = include; } bool SearchOptions::includeHidden() const { return d->includeHidden; } void SearchOptions::setHiddenOnly(bool hiddenOnly) { d->hiddenOnly = hiddenOnly; } bool SearchOptions::hiddenOnly() const { return d->hiddenOnly; } int SearchOptions::maxResults() const { return d->maxResults; } void SearchOptions::setMaxResults(int count) { d->maxResults = count; } void SearchOptions::setCustomOption(const QString &key, const QVariant &value) { d->customOptions[key] = value; } QVariant SearchOptions::customOption(const QString &key) const { return d->customOptions.value(key); } bool SearchOptions::hasCustomOption(const QString &key) const { return d->customOptions.contains(key); } void SearchOptions::setResultFoundEnabled(bool enable) { d->resultFoundEnabled = enable; } bool SearchOptions::resultFoundEnabled() const { return d->resultFoundEnabled; } void SearchOptions::setDetailedResultsEnabled(bool enable) { d->detailedResultsEnabled = enable; } bool SearchOptions::detailedResultsEnabled() const { return d->detailedResultsEnabled; } void SearchOptions::setSyncSearchTimeout(int seconds) { d->syncSearchTimeoutSecs = seconds; } int SearchOptions::syncSearchTimeout() const { return d->syncSearchTimeoutSecs; } void SearchOptions::setBatchTime(int milliseconds) { // 限制批处理时间范围在 50ms 到 5000ms 之间 if (milliseconds < 50) { milliseconds = 50; } else if (milliseconds > 5000) { milliseconds = 5000; } d->batchTimeMs = milliseconds; } int SearchOptions::batchTime() const { return d->batchTimeMs; } void SearchOptions::setTimeRangeFilter(const TimeRangeFilter &filter) { d->timeRangeFilter = filter; } TimeRangeFilter SearchOptions::timeRangeFilter() const { return d->timeRangeFilter; } bool SearchOptions::hasTimeRangeFilter() const { return d->timeRangeFilter.isValid(); } void SearchOptions::clearTimeRangeFilter() { d->timeRangeFilter.clear(); } void SearchOptions::setSizeRangeFilter(const SizeRangeFilter &filter) { d->sizeRangeFilter = filter; } SizeRangeFilter SearchOptions::sizeRangeFilter() const { return d->sizeRangeFilter; } bool SearchOptions::hasSizeRangeFilter() const { return d->sizeRangeFilter.isValid(); } void SearchOptions::clearSizeRangeFilter() { d->sizeRangeFilter.clear(); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchoptionsdata.h000066400000000000000000000034421523477242100303070ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEARCHOPTIONSDATA_H #define SEARCHOPTIONSDATA_H #include #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief The SearchOptionsData class provides the private implementation for SearchOptions * * This class implements the PIMPL pattern for SearchOptions, containing all the * actual data members and implementation details. */ class SearchOptionsData { public: /** * @brief Default constructor */ SearchOptionsData(); // Public data fields SearchMethod method; ///< The search method to use bool caseSensitive; ///< Whether search is case sensitive QString searchPath; ///< The path to search in QStringList searchPathsList; ///< Multiple search paths QStringList searchExcludedPaths; ///< excluded search paths. bool includeHidden; ///< Whether to include hidden files bool hiddenOnly; ///< Whether to search hidden files only (exclude non-hidden) int maxResults; ///< Maximum number of results to return QVariantHash customOptions; ///< Custom search options bool resultFoundEnabled; ///< Whether to enable result found notifications bool detailedResultsEnabled; ///< Whether to include detailed information in search results int syncSearchTimeoutSecs { 60 }; int batchTimeMs { 1000 }; ///< Batch processing time interval in milliseconds TimeRangeFilter timeRangeFilter; ///< Time range filter for search SizeRangeFilter sizeRangeFilter; ///< File size range filter for search }; DFM_SEARCH_END_NS #endif // SEARCHOPTIONSDATA_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchquery.cpp000066400000000000000000000055671523477242100274740ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include DFM_SEARCH_BEGIN_NS class SearchQueryData { public: SearchQueryData() : type(SearchQuery::Type::Simple), booleanOp(SearchQuery::BooleanOperator::AND) { } SearchQueryData(const QString &keyword, SearchQuery::Type type = SearchQuery::Type::Simple) : keyword(keyword), type(type), booleanOp(SearchQuery::BooleanOperator::AND) { } SearchQueryData(const SearchQueryData &other) : keyword(other.keyword), type(other.type), booleanOp(other.booleanOp), subQueries(other.subQueries) { } QString keyword; SearchQuery::Type type; SearchQuery::BooleanOperator booleanOp; QList subQueries; }; SearchQuery::SearchQuery() : d(std::make_unique()) { } SearchQuery::SearchQuery(const QString &keyword) : d(std::make_unique(keyword)) { } SearchQuery::SearchQuery(const QString &keyword, Type type) : d(std::make_unique(keyword, type)) { } SearchQuery::SearchQuery(const SearchQuery &other) : d(std::make_unique(*other.d)) { } SearchQuery::SearchQuery(SearchQuery &&other) noexcept : d(std::move(other.d)) { } SearchQuery::~SearchQuery() = default; SearchQuery &SearchQuery::operator=(const SearchQuery &other) { if (this != &other) { d = std::make_unique(*other.d); } return *this; } SearchQuery &SearchQuery::operator=(SearchQuery &&other) noexcept { if (this != &other) { d = std::move(other.d); } return *this; } QString SearchQuery::keyword() const { return d->keyword; } void SearchQuery::setKeyword(const QString &keyword) { d->keyword = keyword; } SearchQuery::Type SearchQuery::type() const { return d->type; } void SearchQuery::setType(Type type) { d->type = type; } SearchQuery::BooleanOperator SearchQuery::booleanOperator() const { return d->booleanOp; } void SearchQuery::setBooleanOperator(BooleanOperator op) { d->booleanOp = op; } void SearchQuery::addSubQuery(const SearchQuery &query) { d->subQueries.append(query); } QList SearchQuery::subQueries() const { return d->subQueries; } void SearchQuery::clearSubQueries() { d->subQueries.clear(); } SearchQuery SearchQuery::createSimpleQuery(const QString &keyword) { return SearchQuery(keyword, Type::Simple); } SearchQuery SearchQuery::createBooleanQuery(const QStringList &keywords, BooleanOperator op) { SearchQuery query; query.setType(Type::Boolean); query.setBooleanOperator(op); for (const QString &keyword : keywords) { query.addSubQuery(SearchQuery(keyword, Type::Simple)); } return query; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchresult.cpp000066400000000000000000000043231523477242100276320ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include "searchresultdata.h" DFM_SEARCH_BEGIN_NS ///////// SearchResultData::SearchResultData() { } SearchResultData::SearchResultData(const QString &path) : path(path) { } SearchResultData::SearchResultData(const SearchResultData &other) : path(other.path), customAttributes(other.customAttributes) { } SearchResultData::SearchResultData(SearchResultData &&other) noexcept : path(std::move(other.path)), customAttributes(std::move(other.customAttributes)) { } SearchResultData &SearchResultData::operator=(SearchResultData &&other) noexcept { if (this != &other) { path = std::move(other.path); customAttributes = std::move(other.customAttributes); } return *this; } ///////// SearchResult::SearchResult() : d(std::make_unique()) { } SearchResult::SearchResult(const QString &path) : d(std::make_unique(path)) { } SearchResult::SearchResult(const SearchResult &other) : d(std::make_unique(*other.d)) { } SearchResult::SearchResult(SearchResult &&other) noexcept : d(std::move(other.d)) { } SearchResult::~SearchResult() = default; SearchResult &SearchResult::operator=(const SearchResult &other) { if (this != &other) { d = std::make_unique(*other.d); } return *this; } SearchResult &SearchResult::operator=(SearchResult &&other) noexcept { if (this != &other) { d = std::move(other.d); } return *this; } QString SearchResult::path() const { return d->path; } void SearchResult::setPath(const QString &path) { d->path = path; } void SearchResult::setCustomAttribute(const QString &key, const QVariant &value) { d->customAttributes[key] = value; } QVariant SearchResult::customAttribute(const QString &key) const { return d->customAttributes.value(key); } bool SearchResult::hasCustomAttribute(const QString &key) const { return d->customAttributes.contains(key); } QVariantMap SearchResult::customAttributes() const { return d->customAttributes; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchresultdata.h000066400000000000000000000032021523477242100301240ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEARCH_RESULT_DATA_H #define SEARCH_RESULT_DATA_H #include #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief The SearchResultData class provides the base private implementation for SearchResult * * This class implements the PIMPL pattern for SearchResult, containing only the * data fields that are common to all types of search results. */ class SearchResultData { public: /** * @brief Default constructor */ SearchResultData(); /** * @brief Constructor with path * @param path The file path */ SearchResultData(const QString &path); /** * @brief Copy constructor * @param other The SearchResultData object to copy from */ SearchResultData(const SearchResultData &other); /** * @brief Default destructor */ ~SearchResultData() = default; /** * @brief Move constructor * @param other The SearchResultData object to move from */ SearchResultData(SearchResultData &&other) noexcept; /** * @brief Move assignment operator * @param other The SearchResultData object to move from * @return Reference to this object */ SearchResultData &operator=(SearchResultData &&other) noexcept; // Public data fields QString path; ///< The file path QVariantMap customAttributes; ///< Custom attributes associated with the result }; DFM_SEARCH_END_NS #endif // SEARCH_RESULT_DATA_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchstrategy/000077500000000000000000000000001523477242100274505ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchstrategy/basesearchstrategy.h000066400000000000000000000050341523477242100335060ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef BASESEARCHSTRATEGY_H #define BASESEARCHSTRATEGY_H #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief 基础搜索策略接口 * * 为所有类型的搜索提供统一接口 */ class BaseSearchStrategy : public QObject { Q_OBJECT public: explicit BaseSearchStrategy(const SearchOptions &options, QObject *parent = nullptr) : QObject(parent), m_options(options) { } virtual ~BaseSearchStrategy() = default; /** * @brief 设置搜索选项 */ virtual void setSearchOptions(const SearchOptions &options) { m_options = options; } /** * @brief 获取搜索选项 */ virtual SearchOptions searchOptions() const { return m_options; } /** * @brief 获取搜索类型 */ virtual SearchType searchType() const = 0; /** * @brief 执行搜索 * @param query 搜索查询 */ virtual void search(const SearchQuery &query) = 0; /** * @brief 取消搜索 */ virtual void cancel() = 0; /** * @brief 注入引擎级取消标志指针(必须注入,否则取消无效) * * 策略只读取引擎的 atomic flag:主线程调 GenericSearchEngine::cancel() * 设 true,工作线程即时响应。flag 必须非空,由 SearchWorker::doSearch * 创建策略后立即注入。 * * 注意:不使用 Q_ASSERT,因为它在 Release 构建中会被移除。 * 若传入空指针,保持 m_cancelledRef 为 nullptr 并打印警告。 */ void setCancelledFlag(std::atomic *flag) { if (!flag) { qWarning("BaseSearchStrategy::setCancelledFlag: flag is null, cancellation will be ignored"); return; } m_cancelledRef = flag; } Q_SIGNALS: /** * @brief 找到搜索结果信号 */ void resultFound(const DFMSEARCH::SearchResult &result); /** * @brief 搜索完成信号 */ void searchFinished(const DFMSEARCH::SearchResultList &results); /** * @brief 搜索出错信号 */ void errorOccurred(const DFMSEARCH::SearchError &error); protected: SearchOptions m_options; SearchResultList m_results; std::atomic *m_cancelledRef { nullptr }; }; DFM_SEARCH_END_NS #endif // BASESEARCHSTRATEGY_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.cpp000066400000000000000000000037351523477242100326630ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "searchworker.h" DFM_SEARCH_BEGIN_NS SearchWorker::SearchWorker(QObject *parent) : QObject(parent) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } SearchWorker::~SearchWorker() = default; void SearchWorker::setStrategyFactory(std::unique_ptr factory) { m_strategyFactory = std::move(factory); } void SearchWorker::doSearch(const SearchQuery &query, const SearchOptions &options, SearchType searchType) { if (!m_strategyFactory) { emit errorOccurred(SearchError(SearchErrorCode::InternalError)); return; } // 断开之前的连接 if (m_strategy) { disconnect(m_strategy.get(), nullptr, this, nullptr); } // 创建策略 m_strategy = m_strategyFactory->createStrategy(searchType, options); if (!m_strategy) { emit errorOccurred(SearchError(SearchErrorCode::InternalError)); return; } // 注入引擎级取消标志,使策略能即时响应取消 if (!m_engineCancelled) { qWarning("SearchWorker::doSearch: m_engineCancelled is null, aborting search"); emit errorOccurred(SearchError(SearchErrorCode::InternalError)); return; } m_strategy->setCancelledFlag(m_engineCancelled); // 连接信号 connect(m_strategy.get(), &BaseSearchStrategy::resultFound, this, &SearchWorker::resultFound); connect(m_strategy.get(), &BaseSearchStrategy::searchFinished, this, &SearchWorker::searchFinished); connect(m_strategy.get(), &BaseSearchStrategy::errorOccurred, this, &SearchWorker::errorOccurred); // 执行搜索 m_strategy->search(query); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.h000066400000000000000000000035511523477242100323240ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CORE_SEARCHWORKER_H #define CORE_SEARCHWORKER_H #include #include #include #include #include #include #include "basesearchstrategy.h" DFM_SEARCH_BEGIN_NS class SearchStrategyFactory; // 前向声明 /** * @brief 通用搜索工作线程 */ class SearchWorker : public QObject { Q_OBJECT public: explicit SearchWorker(QObject *parent = nullptr); ~SearchWorker(); void setStrategyFactory(std::unique_ptr factory); void setEngineCancelledFlag(std::atomic *flag) { m_engineCancelled = flag; } public Q_SLOTS: /** * @brief 执行搜索操作 */ void doSearch(const DFMSEARCH::SearchQuery &query, const DFMSEARCH::SearchOptions &options, DFMSEARCH::SearchType searchType); Q_SIGNALS: /** * @brief 搜索结果信号 */ void resultFound(const DFMSEARCH::SearchResult &result); /** * @brief 搜索完成信号 */ void searchFinished(const DFMSEARCH::SearchResultList &results); /** * @brief 搜索错误信号 */ void errorOccurred(const DFMSEARCH::SearchError &error); private: std::unique_ptr m_strategyFactory; std::unique_ptr m_strategy; std::atomic *m_engineCancelled { nullptr }; }; /** * @brief 搜索策略工厂接口 */ class SearchStrategyFactory { public: virtual ~SearchStrategyFactory() = default; virtual std::unique_ptr createStrategy( SearchType searchType, const SearchOptions &options) = 0; }; DFM_SEARCH_END_NS #endif // CORE_SEARCHWORKER_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/sizerangefilter.cpp000066400000000000000000000047001523477242100303220ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include DFM_SEARCH_BEGIN_NS class SizeRangeFilterData { public: SizeRangeFilterData() : minSize(0), maxSize(0), includeLower(true), includeUpper(true) { } SizeRangeFilterData(const SizeRangeFilterData &other) : minSize(other.minSize), maxSize(other.maxSize), includeLower(other.includeLower), includeUpper(other.includeUpper) { } qint64 minSize; qint64 maxSize; bool includeLower; bool includeUpper; }; SizeRangeFilter::SizeRangeFilter() : d(std::make_unique()) { } SizeRangeFilter::SizeRangeFilter(const SizeRangeFilter &other) : d(std::make_unique(*other.d)) { } SizeRangeFilter::SizeRangeFilter(SizeRangeFilter &&other) noexcept : d(std::move(other.d)) { } SizeRangeFilter::~SizeRangeFilter() = default; SizeRangeFilter &SizeRangeFilter::operator=(const SizeRangeFilter &other) { if (this != &other) { d = std::make_unique(*other.d); } return *this; } SizeRangeFilter &SizeRangeFilter::operator=(SizeRangeFilter &&other) noexcept { if (this != &other) { d = std::move(other.d); } return *this; } SizeRangeFilter &SizeRangeFilter::setMin(qint64 minSize) { d->minSize = minSize; return *this; } SizeRangeFilter &SizeRangeFilter::setMax(qint64 maxSize) { d->maxSize = maxSize; return *this; } SizeRangeFilter &SizeRangeFilter::setRange(qint64 minSize, qint64 maxSize) { d->minSize = minSize; d->maxSize = maxSize; return *this; } qint64 SizeRangeFilter::minSize() const { return d->minSize; } qint64 SizeRangeFilter::maxSize() const { return d->maxSize; } SizeRangeFilter &SizeRangeFilter::setIncludeLower(bool include) { d->includeLower = include; return *this; } SizeRangeFilter &SizeRangeFilter::setIncludeUpper(bool include) { d->includeUpper = include; return *this; } bool SizeRangeFilter::includeLower() const { return d->includeLower; } bool SizeRangeFilter::includeUpper() const { return d->includeUpper; } SizeRangeFilter &SizeRangeFilter::clear() { d->minSize = 0; d->maxSize = 0; d->includeLower = true; d->includeUpper = true; return *this; } bool SizeRangeFilter::isValid() const { return d->minSize > 0 || d->maxSize > 0; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/timerangefilter.cpp000066400000000000000000000243241523477242100303120ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include DFM_SEARCH_BEGIN_NS /** * @brief Internal enum for range mode */ enum class RangeMode { Invalid, // No range set Relative, // Relative time (setLast) - rolling range from N units ago to now FixedUnit, // Fixed unit range (yesterday, last week, etc.) - complete unit Custom // Custom start/end }; class TimeRangeFilterData { public: TimeRangeFilterData() : field(TimeField::ModifyTime), mode(RangeMode::Invalid), relativeValue(0), relativeUnit(TimeUnit::Days), includeLower(true), includeUpper(false) { } TimeRangeFilterData(const TimeRangeFilterData &other) : field(other.field), mode(other.mode), relativeValue(other.relativeValue), relativeUnit(other.relativeUnit), startTime(other.startTime), endTime(other.endTime), includeLower(other.includeLower), includeUpper(other.includeUpper) { } TimeField field; RangeMode mode; // For relative/fixed mode int relativeValue; TimeUnit relativeUnit; // For custom mode QDateTime startTime; QDateTime endTime; bool includeLower; bool includeUpper; }; TimeRangeFilter::TimeRangeFilter() : d(std::make_unique()) { } TimeRangeFilter::TimeRangeFilter(const TimeRangeFilter &other) : d(std::make_unique(*other.d)) { } TimeRangeFilter::TimeRangeFilter(TimeRangeFilter &&other) noexcept : d(std::move(other.d)) { } TimeRangeFilter::~TimeRangeFilter() = default; TimeRangeFilter &TimeRangeFilter::operator=(const TimeRangeFilter &other) { if (this != &other) { d = std::make_unique(*other.d); } return *this; } TimeRangeFilter &TimeRangeFilter::operator=(TimeRangeFilter &&other) noexcept { if (this != &other) { d = std::move(other.d); } return *this; } TimeRangeFilter &TimeRangeFilter::setTimeField(TimeField field) { d->field = field; return *this; } TimeField TimeRangeFilter::timeField() const { return d->field; } TimeRangeFilter &TimeRangeFilter::setLast(int value, TimeUnit unit) { d->mode = RangeMode::Relative; // Rolling range from N units ago to now d->relativeValue = value; d->relativeUnit = unit; d->startTime = QDateTime(); d->endTime = QDateTime(); return *this; } TimeRangeFilter &TimeRangeFilter::setToday() { d->mode = RangeMode::FixedUnit; // Complete unit (today) d->relativeValue = 0; d->relativeUnit = TimeUnit::Days; return *this; } TimeRangeFilter &TimeRangeFilter::setYesterday() { d->mode = RangeMode::FixedUnit; // Complete unit (yesterday) d->relativeValue = 1; d->relativeUnit = TimeUnit::Days; return *this; } TimeRangeFilter &TimeRangeFilter::setThisWeek() { d->mode = RangeMode::FixedUnit; d->relativeValue = 0; d->relativeUnit = TimeUnit::Weeks; return *this; } TimeRangeFilter &TimeRangeFilter::setLastWeek() { d->mode = RangeMode::FixedUnit; d->relativeValue = 1; d->relativeUnit = TimeUnit::Weeks; return *this; } TimeRangeFilter &TimeRangeFilter::setThisMonth() { d->mode = RangeMode::FixedUnit; d->relativeValue = 0; d->relativeUnit = TimeUnit::Months; return *this; } TimeRangeFilter &TimeRangeFilter::setLastMonth() { d->mode = RangeMode::FixedUnit; d->relativeValue = 1; d->relativeUnit = TimeUnit::Months; return *this; } TimeRangeFilter &TimeRangeFilter::setThisYear() { d->mode = RangeMode::FixedUnit; d->relativeValue = 0; d->relativeUnit = TimeUnit::Years; return *this; } TimeRangeFilter &TimeRangeFilter::setLastYear() { d->mode = RangeMode::FixedUnit; d->relativeValue = 1; d->relativeUnit = TimeUnit::Years; return *this; } TimeRangeFilter &TimeRangeFilter::setRange(const QDateTime &start, const QDateTime &end) { d->mode = RangeMode::Custom; d->startTime = start; d->endTime = end; d->relativeValue = 0; d->relativeUnit = TimeUnit::Days; return *this; } QDateTime TimeRangeFilter::startTime() const { return d->startTime; } QDateTime TimeRangeFilter::endTime() const { return d->endTime; } TimeRangeFilter &TimeRangeFilter::setIncludeLower(bool include) { d->includeLower = include; return *this; } TimeRangeFilter &TimeRangeFilter::setIncludeUpper(bool include) { d->includeUpper = include; return *this; } bool TimeRangeFilter::includeLower() const { return d->includeLower; } bool TimeRangeFilter::includeUpper() const { return d->includeUpper; } TimeRangeFilter &TimeRangeFilter::clear() { d->mode = RangeMode::Invalid; d->startTime = QDateTime(); d->endTime = QDateTime(); d->relativeValue = 0; d->relativeUnit = TimeUnit::Days; d->includeLower = true; d->includeUpper = false; return *this; } bool TimeRangeFilter::isValid() const { return d->mode != RangeMode::Invalid; } QPair TimeRangeFilter::resolveTimeRange() const { if (d->mode == RangeMode::Custom) { return qMakePair(d->startTime, d->endTime); } if (d->mode == RangeMode::Relative) { return resolveRelativeTimeRange(d->relativeValue, d->relativeUnit); } if (d->mode == RangeMode::FixedUnit) { return resolveFixedUnitTimeRange(d->relativeValue, d->relativeUnit); } return qMakePair(QDateTime(), QDateTime()); } QPair TimeRangeFilter::resolveRelativeTimeRange(int value, TimeUnit unit) { // Relative mode: from N units ago to now (rolling window) QDateTime now = QDateTime::currentDateTime(); QDateTime start; QDateTime end = now; switch (unit) { case TimeUnit::Minutes: // "Last N minutes": from N minutes ago to now start = now.addSecs(-value * 60); break; case TimeUnit::Hours: // "Last N hours": from N hours ago to now start = now.addSecs(-value * 3600); break; case TimeUnit::Days: // "Last N days": from N days ago (00:00:00) to now start = QDateTime(now.date().addDays(-value), QTime(0, 0, 0)); break; case TimeUnit::Weeks: // "Last N weeks": from N weeks ago (Monday 00:00:00) to now { int daysToMonday = now.date().dayOfWeek() - 1; QDate thisMonday = now.date().addDays(-daysToMonday); QDate startMonday = thisMonday.addDays(-value * 7); start = QDateTime(startMonday, QTime(0, 0, 0)); } break; case TimeUnit::Months: // "Last N months": from N months ago (1st day 00:00:00) to now { QDate firstOfThisMonth(now.date().year(), now.date().month(), 1); QDate startMonth = firstOfThisMonth.addMonths(-value); start = QDateTime(startMonth, QTime(0, 0, 0)); } break; case TimeUnit::Years: // "Last N years": from N years ago (Jan 1st 00:00:00) to now { QDate startYear(now.date().year() - value, 1, 1); start = QDateTime(startYear, QTime(0, 0, 0)); } break; } return qMakePair(start, end); } QPair TimeRangeFilter::resolveFixedUnitTimeRange(int value, TimeUnit unit) { // Fixed unit mode: complete unit range (today, yesterday, this week, etc.) QDateTime now = QDateTime::currentDateTime(); QDateTime start; QDateTime end; switch (unit) { case TimeUnit::Minutes: case TimeUnit::Hours: // For minutes/hours, fixed unit doesn't make much sense, treat as relative return resolveRelativeTimeRange(value, unit); case TimeUnit::Days: if (value == 0) { // Today: from 00:00:00 today to 00:00:00 tomorrow start = QDateTime(now.date(), QTime(0, 0, 0)); end = QDateTime(now.date().addDays(1), QTime(0, 0, 0)); } else { // Yesterday/N days ago: complete day from 00:00:00 to 00:00:00 next day start = QDateTime(now.date().addDays(-value), QTime(0, 0, 0)); end = QDateTime(now.date().addDays(-value + 1), QTime(0, 0, 0)); } break; case TimeUnit::Weeks: if (value == 0) { // This week: from Monday 00:00:00 to next Monday 00:00:00 int daysToMonday = now.date().dayOfWeek() - 1; QDate monday = now.date().addDays(-daysToMonday); start = QDateTime(monday, QTime(0, 0, 0)); end = QDateTime(monday.addDays(7), QTime(0, 0, 0)); } else { // Last week/N weeks ago: complete week int daysToMonday = now.date().dayOfWeek() - 1; QDate thisMonday = now.date().addDays(-daysToMonday); QDate startMonday = thisMonday.addDays(-value * 7); start = QDateTime(startMonday, QTime(0, 0, 0)); end = QDateTime(startMonday.addDays(7), QTime(0, 0, 0)); } break; case TimeUnit::Months: if (value == 0) { // This month: from 1st day 00:00:00 to 1st day of next month QDate firstOfMonth(now.date().year(), now.date().month(), 1); start = QDateTime(firstOfMonth, QTime(0, 0, 0)); end = QDateTime(firstOfMonth.addMonths(1), QTime(0, 0, 0)); } else { // Last month/N months ago: complete month QDate firstOfThisMonth(now.date().year(), now.date().month(), 1); QDate startMonth = firstOfThisMonth.addMonths(-value); start = QDateTime(startMonth, QTime(0, 0, 0)); end = QDateTime(startMonth.addMonths(1), QTime(0, 0, 0)); } break; case TimeUnit::Years: if (value == 0) { // This year: from Jan 1st 00:00:00 to Jan 1st of next year QDate firstOfYear(now.date().year(), 1, 1); start = QDateTime(firstOfYear, QTime(0, 0, 0)); end = QDateTime(QDate(now.date().year() + 1, 1, 1), QTime(0, 0, 0)); } else { // Last year/N years ago: complete year QDate startYear(now.date().year() - value, 1, 1); start = QDateTime(startYear, QTime(0, 0, 0)); end = QDateTime(QDate(now.date().year() - value + 1, 1, 1), QTime(0, 0, 0)); } break; } return qMakePair(start, end); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/core/timeresultapi.cpp000066400000000000000000000024051523477242100300140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include DFM_SEARCH_BEGIN_NS TimeResultAPI::TimeResultAPI(SearchResult &result) : m_result(result) { } void TimeResultAPI::setModifyTimestamp(qint64 timestamp) { m_result.setCustomAttribute("modifyTimestamp", timestamp); } qint64 TimeResultAPI::modifyTimestamp() const { return m_result.customAttribute("modifyTimestamp").toLongLong(); } QString TimeResultAPI::modifyTimeString() const { qint64 ts = modifyTimestamp(); return ts > 0 ? formatTimestamp(ts) : QString(); } void TimeResultAPI::setBirthTimestamp(qint64 timestamp) { m_result.setCustomAttribute("birthTimestamp", timestamp); } qint64 TimeResultAPI::birthTimestamp() const { return m_result.customAttribute("birthTimestamp").toLongLong(); } QString TimeResultAPI::birthTimeString() const { qint64 ts = birthTimestamp(); return ts > 0 ? formatTimestamp(ts) : QString(); } QString TimeResultAPI::formatTimestamp(qint64 timestamp) { if (timestamp <= 0) { return QString(); } return QDateTime::fromSecsSinceEpoch(timestamp).toString("yyyy-MM-dd HH:mm:ss"); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/dfm-search-qt5.cmake000066400000000000000000000001471523477242100272140ustar00rootroot00000000000000set(BIN_NAME dfm-search) set(QT_VERSION_MAJOR 5) set(DFM_VERSION_MAJOR "") include(dfm-search.cmake) linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/dfm-search-qt6.cmake000066400000000000000000000001471523477242100272150ustar00rootroot00000000000000set(BIN_NAME dfm6-search) set(QT_VERSION_MAJOR 6) set(DFM_VERSION_MAJOR 6) include(dfm-search.cmake) linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/dfm-search.cmake000066400000000000000000000061161523477242100265070ustar00rootroot00000000000000# Setup the environment find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core DBus REQUIRED) find_package(Dtk${DFM_VERSION_MAJOR} COMPONENTS Core REQUIRED) find_package(Threads REQUIRED) find_package(Boost REQUIRED) if (Boost_VERSION_STRING VERSION_LESS "1.89.0") message(STATUS "Boost < 1.89 → using Boost::system") find_package(Boost REQUIRED COMPONENTS system) endif() find_package(PkgConfig REQUIRED) pkg_check_modules(Lucene REQUIRED IMPORTED_TARGET liblucene++ liblucene++-contrib) # Build add_library(${BIN_NAME} SHARED ${PUBLIC_INCLUDES} ${SRCS} ) target_compile_definitions(${BIN_NAME} PRIVATE CMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}" ) target_link_libraries(${BIN_NAME} PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::DBus Dtk${DFM_VERSION_MAJOR}::Core PkgConfig::Lucene Threads::Threads ) if (Boost_VERSION_STRING VERSION_LESS "1.89.0") target_link_libraries(${BIN_NAME} PUBLIC Boost::system) endif() target_include_directories( ${BIN_NAME} PUBLIC $ $ $ $ $ ) if (NOT VERSION) set(VERSION "1.0.0") endif() if (NOT PROJECT_VERSION_MAJOR) set(PROJECT_VERSION_MAJOR 1) endif() set_target_properties( ${BIN_NAME} PROPERTIES VERSION ${VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} EXPORT_NAME ${BIN_NAME} ) # Install with export install(TARGETS ${BIN_NAME} EXPORT ${BIN_NAME}Targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION include/${BIN_NAME} ) # Install the export install(EXPORT ${BIN_NAME}Targets FILE ${BIN_NAME}Targets.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${BIN_NAME} ) # install headers install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/${BASE_NAME}/ DESTINATION include/${BIN_NAME} FILES_MATCHING PATTERN "*.h" ) # install semantic search rules (locale subdirectories preserved) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/semantic/rules/ DESTINATION share/deepin/dfm-search/semantic/rules ) # for pc file config - update to include all dependencies set(PC_LIBS_PRIVATE Qt${QT_VERSION_MAJOR}Core dtk${DFM_VERSION_MAJOR}core) set(PC_REQ_PRIVATE liblucene++ liblucene++-contrib) set(PC_REQ_PUBLIC Qt${QT_VERSION_MAJOR}Core Qt${QT_VERSION_MAJOR}DBus dtk${DFM_VERSION_MAJOR}core) # config pkgconfig file configure_file(${PROJECT_SOURCE_DIR}/misc/${BASE_NAME}/${BASE_NAME}.pc.in ${BIN_NAME}.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BIN_NAME}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) # config cmake file configure_file(${PROJECT_SOURCE_DIR}/misc/${BASE_NAME}/${BASE_NAME}Config.cmake.in ${BIN_NAME}Config.cmake @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BIN_NAME}Config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${BIN_NAME}) linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/000077500000000000000000000000001523477242100264365ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/filenamesearchapi.cpp000066400000000000000000000103661523477242100326100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include DFM_SEARCH_BEGIN_NS FileNameOptionsAPI::FileNameOptionsAPI(SearchOptions &options) : m_options(options) { } void FileNameOptionsAPI::setPinyinEnabled(bool enabled) { m_options.setCustomOption("pinyinEnabled", enabled); } bool FileNameOptionsAPI::pinyinEnabled() const { return m_options.customOption("pinyinEnabled").toBool(); } void FileNameOptionsAPI::setPinyinAcronymEnabled(bool enabled) { m_options.setCustomOption("pinyinAcronymEnabled", enabled); } bool FileNameOptionsAPI::pinyinAcronymEnabled() const { return m_options.customOption("pinyinAcronymEnabled").toBool(); } void FileNameOptionsAPI::setFileTypes(const QStringList &types) { m_options.setCustomOption("fileTypes", types); } QStringList FileNameOptionsAPI::fileTypes() const { return m_options.customOption("fileTypes").toStringList(); } void FileNameOptionsAPI::setFileExtensions(const QStringList &extensions) { m_options.setCustomOption("fileExtensions", extensions); } QStringList FileNameOptionsAPI::fileExtensions() const { return m_options.customOption("fileExtensions").toStringList(); } ////////// FileNameResultAPI::FileNameResultAPI(SearchResult &result) : m_result(result) { } QString FileNameResultAPI::size() const { return m_result.customAttribute("size").toString(); } void FileNameResultAPI::setSize(const QString &size) { m_result.setCustomAttribute("size", size); } QString FileNameResultAPI::modifiedTime() const { return m_result.customAttribute("modifiedTime").toString(); } void FileNameResultAPI::setModifiedTime(const QString &time) { m_result.setCustomAttribute("modifiedTime", time); } bool FileNameResultAPI::isDirectory() const { return m_result.customAttribute("isDirectory").toBool(); } void FileNameResultAPI::setIsDirectory(bool isDir) { m_result.setCustomAttribute("isDirectory", isDir); } QString FileNameResultAPI::fileType() const { return m_result.customAttribute("fileType").toString(); } void FileNameResultAPI::setFileType(const QString &type) const { m_result.setCustomAttribute("fileType", type); } // ==================== Extended Attributes ==================== QString FileNameResultAPI::filename() const { return m_result.customAttribute("filename").toString(); } void FileNameResultAPI::setFilename(const QString &name) { m_result.setCustomAttribute("filename", name); } QString FileNameResultAPI::fileExtension() const { return m_result.customAttribute("fileExtension").toString(); } void FileNameResultAPI::setFileExtension(const QString &ext) { m_result.setCustomAttribute("fileExtension", ext); } bool FileNameResultAPI::isHidden() const { return m_result.customAttribute("isHidden").toBool(); } void FileNameResultAPI::setIsHidden(bool hidden) { m_result.setCustomAttribute("isHidden", hidden); } // ==================== Modification Time ==================== void FileNameResultAPI::setModifyTimestamp(qint64 timestamp) { m_result.setCustomAttribute("modifyTimestamp", timestamp); } qint64 FileNameResultAPI::modifyTimestamp() const { return m_result.customAttribute("modifyTimestamp").toLongLong(); } QString FileNameResultAPI::modifyTimeString() const { qint64 ts = modifyTimestamp(); return ts > 0 ? TimeResultAPI::formatTimestamp(ts) : QString(); } // ==================== Birth/Creation Time ==================== void FileNameResultAPI::setBirthTimestamp(qint64 timestamp) { m_result.setCustomAttribute("birthTimestamp", timestamp); } qint64 FileNameResultAPI::birthTimestamp() const { return m_result.customAttribute("birthTimestamp").toLongLong(); } QString FileNameResultAPI::birthTimeString() const { qint64 ts = birthTimestamp(); return ts > 0 ? TimeResultAPI::formatTimestamp(ts) : QString(); } // ==================== File Size (Numeric) ==================== void FileNameResultAPI::setFileSizeBytes(qint64 bytes) { m_result.setCustomAttribute("fileSizeBytes", bytes); } qint64 FileNameResultAPI::fileSizeBytes() const { return m_result.customAttribute("fileSizeBytes").toLongLong(); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/filenamesearchengine.cpp000066400000000000000000000062731523477242100333060ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "filenamesearchengine.h" #include #include #include #include #include #include "filenamestrategies/realtimestrategy.h" #include "filenamestrategies/indexedstrategy.h" #include "utils/searchutility.h" DFM_SEARCH_BEGIN_NS DCORE_USE_NAMESPACE FileNameSearchEngine::FileNameSearchEngine(QObject *parent) : GenericSearchEngine(parent) { } FileNameSearchEngine::~FileNameSearchEngine() = default; void FileNameSearchEngine::setupStrategyFactory() { // 设置文件名搜索策略工厂 auto factory = std::make_unique(); m_worker->setStrategyFactory(std::move(factory)); } SearchError FileNameSearchEngine::validateSearchConditions() { // 先执行基类验证 auto result = GenericSearchEngine::validateSearchConditions(); if (result.isError()) { return result; } FileNameOptionsAPI api(m_options); const auto &fileTypes = api.fileTypes(); const auto &fileExts = api.fileExtensions(); // Validate each file type in fileTypes against deepinAnythingFileTypes using std algorithms if (!fileTypes.isEmpty()) { auto invalidType = std::find_if(fileTypes.begin(), fileTypes.end(), [](const QString &type) { return !SearchUtility::deepinAnythingFileTypes().contains(type.trimmed().toLower()); }); if (invalidType != fileTypes.end()) { return SearchError(FileNameSearchErrorCode::InvalidFileTypes); } } // 文件名搜索特定验证 if (m_currentQuery.type() == SearchQuery::Type::Simple || m_currentQuery.type() == SearchQuery::Type::Wildcard) { // 允许对类型/后缀/大小范围/时间范围/隐藏文件进行搜索,获取满足条件的所有文件 // hiddenOnly=true 时(如"下载的隐藏文件"),无需关键字,按 kIsHidden:Y 过滤即可 if (m_currentQuery.keyword().isEmpty() && fileTypes.isEmpty() && fileExts.isEmpty() && !m_options.hasSizeRangeFilter() && !m_options.hasTimeRangeFilter() && !m_options.hiddenOnly()) { return SearchError(FileNameSearchErrorCode::KeywordIsEmpty); } // pinyin (wildcard类型不支持拼音搜索) if (m_currentQuery.type() == SearchQuery::Type::Simple && api.pinyinEnabled() && !Global::isPinyinSequence(m_currentQuery.keyword())) { qWarning() << SearchError(FileNameSearchErrorCode::InvalidPinyinFormat).message() << "key: " << m_currentQuery.keyword(); } } return result; } std::unique_ptr FileNameSearchStrategyFactory::createStrategy( SearchType searchType, const SearchOptions &options) { // 确保搜索类型正确 if (searchType != SearchType::FileName) { return nullptr; } // 根据搜索方法创建对应的策略 if (options.method() == SearchMethod::Indexed) { return std::make_unique(options); } else { return std::make_unique(options); } } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/filenamesearchengine.h000066400000000000000000000020641523477242100327450ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef FILENAME_SEARCH_ENGINE_H #define FILENAME_SEARCH_ENGINE_H #include "core/genericsearchengine.h" DFM_SEARCH_BEGIN_NS /** * @brief 文件名搜索引擎 */ class FileNameSearchEngine : public GenericSearchEngine { Q_OBJECT public: explicit FileNameSearchEngine(QObject *parent = nullptr); ~FileNameSearchEngine() override; // 实现搜索类型 SearchType searchType() const override { return SearchType::FileName; } protected: // 设置策略工厂 void setupStrategyFactory() override; // 重写验证方法以添加特定验证 SearchError validateSearchConditions() override; }; /** * @brief 文件名搜索策略工厂 */ class FileNameSearchStrategyFactory : public SearchStrategyFactory { public: std::unique_ptr createStrategy( SearchType searchType, const SearchOptions &options) override; }; DFM_SEARCH_END_NS #endif // FILENAME_SEARCH_ENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/000077500000000000000000000000001523477242100323115ustar00rootroot00000000000000basestrategy.h000066400000000000000000000013111523477242100350740ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef FILENAME_BASE_STRATEGY_H #define FILENAME_BASE_STRATEGY_H #include "core/searchstrategy/basesearchstrategy.h" #include DFM_SEARCH_BEGIN_NS /** * @brief 文件名搜索策略基类 */ class FileNameBaseStrategy : public BaseSearchStrategy { Q_OBJECT public: explicit FileNameBaseStrategy(const SearchOptions &options, QObject *parent = nullptr) : BaseSearchStrategy(options, parent) { } SearchType searchType() const override { return SearchType::FileName; } }; DFM_SEARCH_END_NS #endif // FILENAME_BASE_STRATEGY_H indexedstrategy.cpp000066400000000000000000000671741523477242100361600ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "indexedstrategy.h" #include #include #include #include #include #include #include #include #include #include #include "utils/cancellablecollector.h" #include "utils/searchutility.h" #include "utils/lucenequeryutils.h" #include "utils/timerangeutils.h" DFM_SEARCH_BEGIN_NS //-------------------------------------------------------------------- // QueryBuilder 实现 //-------------------------------------------------------------------- QueryBuilder::QueryBuilder() { } Lucene::QueryPtr QueryBuilder::buildTypeQuery(const QStringList &types) const { if (types.isEmpty()) { return nullptr; } BooleanQueryPtr typeQuery = newLucene(); for (const QString &type : types) { QString cleanType = type.trimmed().toLower(); if (!cleanType.isEmpty()) { QueryPtr termQuery = newLucene( newLucene(LuceneFieldNames::FileName::kFileType, StringUtils::toUnicode(cleanType.toStdString()))); typeQuery->add(termQuery, BooleanClause::SHOULD); } } return typeQuery; } Lucene::QueryPtr QueryBuilder::buildExtQuery(const QStringList &extensions) const { if (extensions.isEmpty()) { return nullptr; } BooleanQueryPtr extQuery = newLucene(); for (const QString &ext : extensions) { QString cleanExt = ext.trimmed().toLower(); if (!cleanExt.isEmpty()) { QueryPtr termQuery = newLucene( newLucene(LuceneFieldNames::FileName::kFileExt, StringUtils::toUnicode(cleanExt.toStdString()))); extQuery->add(termQuery, BooleanClause::SHOULD); } } return extQuery; } Lucene::QueryPtr QueryBuilder::buildPinyinQuery(const QStringList &pinyins, SearchQuery::BooleanOperator op) const { if (pinyins.isEmpty()) { return nullptr; } BooleanQueryPtr pinyinQuery = newLucene(); for (const QString &pinyin : pinyins) { QString cleanPinyin = pinyin.trimmed(); if (!cleanPinyin.isEmpty() && Global::isPinyinSequence(cleanPinyin)) { QueryPtr termQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::FileName::kPinyin), cleanPinyin); if (termQuery) { pinyinQuery->add(termQuery, op == SearchQuery::BooleanOperator::AND ? BooleanClause::MUST : BooleanClause::SHOULD); } } } return pinyinQuery; } Lucene::QueryPtr QueryBuilder::buildPinyinAcronymQuery(const QStringList &acronyms, SearchQuery::BooleanOperator op) const { if (acronyms.isEmpty()) { return nullptr; } BooleanQueryPtr acronymQuery = newLucene(); for (const QString &acronym : acronyms) { QString cleanAcronym = acronym.trimmed(); if (!cleanAcronym.isEmpty()) { QueryPtr termQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::FileName::kPinyinAcronym), cleanAcronym); if (termQuery) { acronymQuery->add(termQuery, op == SearchQuery::BooleanOperator::AND ? BooleanClause::MUST : BooleanClause::SHOULD); } } } return acronymQuery; } Lucene::QueryPtr QueryBuilder::buildSimpleQuery(const QString &keyword, bool caseSensitive) const { return LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::FileName::kFileName), keyword, caseSensitive); } Lucene::QueryPtr QueryBuilder::buildWildcardQuery(const QString &keyword, bool caseSensitive) const { if (keyword.isEmpty()) { return nullptr; } // 对于通配符查询,使用 file_name_lower 字段(非分词)而非 file_name(分词) QString processedKeyword = caseSensitive ? keyword : keyword.toLower(); // 直接构建WildcardQuery,不使用QueryParser避免分词干扰 return newLucene( newLucene(LuceneFieldNames::FileName::kFileNameLower, StringUtils::toUnicode(processedKeyword.toStdString()))); } //-------------------------------------------------------------------- // IndexManager 实现 //-------------------------------------------------------------------- IndexManager::IndexManager() { } FSDirectoryPtr IndexManager::getIndexDirectory(const QString &indexPath) const { if (m_cachedDirectory && m_cachedIndexPath == indexPath) { return m_cachedDirectory; } m_cachedIndexPath = indexPath; try { m_cachedDirectory = FSDirectory::open(StringUtils::toUnicode(indexPath.toStdString())); return m_cachedDirectory; } catch (const LuceneException &e) { qWarning() << "Failed to open index directory:" << QString::fromStdWString(e.getError()); return nullptr; } } IndexReaderPtr IndexManager::getIndexReader(FSDirectoryPtr directory) const { if (!directory) { return nullptr; } if (m_cachedReader && m_cachedDirectory == directory) { return m_cachedReader; } try { if (IndexReader::indexExists(directory)) { m_cachedReader = IndexReader::open(directory, true); return m_cachedReader; } } catch (const LuceneException &e) { qWarning() << "Failed to open index reader:" << QString::fromStdWString(e.getError()); } return nullptr; } SearcherPtr IndexManager::getSearcher(IndexReaderPtr reader) const { if (!reader) { return nullptr; } if (m_cachedSearcher && m_cachedReader == reader) { return m_cachedSearcher; } try { m_cachedSearcher = newLucene(reader); return m_cachedSearcher; } catch (const LuceneException &e) { qWarning() << "Failed to create searcher:" << QString::fromStdWString(e.getError()); return nullptr; } } //-------------------------------------------------------------------- // FileNameIndexedStrategy 实现 //-------------------------------------------------------------------- FileNameIndexedStrategy::FileNameIndexedStrategy(const SearchOptions &options, QObject *parent) : FileNameBaseStrategy(options, parent) { m_queryBuilder = std::make_unique(); m_indexManager = std::make_unique(); initializeIndexing(); } FileNameIndexedStrategy::~FileNameIndexedStrategy() = default; void FileNameIndexedStrategy::initializeIndexing() { m_indexDir = Global::fileNameIndexDirectory(); if (!QFileInfo::exists(m_indexDir)) { qWarning() << "Index directory does not exist:" << m_indexDir; } } void FileNameIndexedStrategy::search(const SearchQuery &query) { m_results.clear(); if (!QFileInfo::exists(m_indexDir)) { emit errorOccurred(SearchError(SearchErrorCode::InternalError)); emit searchFinished(m_results); return; } // 获取文件类型设置 FileNameOptionsAPI optionsApi(const_cast(m_options)); // 执行搜索 try { performIndexSearch(query, optionsApi); } catch (const LuceneException &e) { qWarning() << "Lucene search exception:" << QString::fromStdWString(e.getError()); emit errorOccurred(SearchError(SearchErrorCode::InternalError)); } catch (const std::exception &e) { qWarning() << "Standard exception:" << e.what(); emit errorOccurred(SearchError(SearchErrorCode::InternalError)); } emit searchFinished(m_results); } void FileNameIndexedStrategy::performIndexSearch(const SearchQuery &query, const FileNameOptionsAPI &api) { bool caseSensitive = m_options.caseSensitive(); QStringList fileTypes = api.fileTypes(); QStringList fileExtensions = api.fileExtensions(); bool pinyinEnabled = api.pinyinEnabled(); bool pinyinAcronymEnabled = api.pinyinAcronymEnabled(); // 1. 确定搜索类型 SearchType searchType = determineSearchType(query, pinyinEnabled, pinyinAcronymEnabled, fileTypes, fileExtensions); // 2. 构建查询 IndexQuery indexQuery = buildIndexQuery(query, searchType, caseSensitive, pinyinEnabled, pinyinAcronymEnabled, fileTypes, fileExtensions); // 3. 执行查询并处理结果 executeIndexQuery(indexQuery); } FileNameIndexedStrategy::SearchType FileNameIndexedStrategy::determineSearchType( const SearchQuery &query, bool pinyinEnabled, bool pinyinAcronymEnabled, const QStringList &fileTypes, const QStringList &fileExtensions) const { QString keyword = query.keyword(); bool hasKeyword = !keyword.isEmpty(); bool hasFileTypes = !fileTypes.isEmpty(); bool hasFileExts = !fileExtensions.isEmpty(); bool isBoolean = (query.type() == SearchQuery::Type::Boolean); // 检查是否需要组合搜索(关键词 + 文件类型/后缀) bool combinedWithTypes = (hasKeyword || isBoolean) && (hasFileTypes || hasFileExts); if (combinedWithTypes) { return SearchType::Combined; } // 无关键词时,文件类型和后缀组合搜索 if (!hasKeyword && (hasFileTypes || hasFileExts)) { return SearchType::Combined; } // 通配符查询类型(显式指定) if (query.type() == SearchQuery::Type::Wildcard) { return SearchType::Wildcard; } // 布尔查询 if (isBoolean) { return SearchType::Boolean; } if ((pinyinEnabled || pinyinAcronymEnabled) && !isBoolean) { // 如果同时启用了拼音和拼音首字母 if (pinyinEnabled && pinyinAcronymEnabled) { // 检查关键词是否为有效拼音序列 if (hasKeyword && Global::isPinyinSequence(keyword)) { return SearchType::Pinyin; } else if (hasKeyword && Global::isPinyinAcronymSequence(keyword)) { // 不是有效拼音序列,但是有效的拼音首字母,fallback到拼音首字母搜索 return SearchType::PinyinAcronym; } else { // 既不是拼音也不是有效的拼音首字母,使用简单搜索 return SearchType::Simple; } } // 只启用拼音搜索 else if (pinyinEnabled) { return SearchType::Pinyin; } // 只启用拼音首字母搜索 else if (pinyinAcronymEnabled) { return SearchType::PinyinAcronym; } } // 默认简单搜索 return SearchType::Simple; } FileNameIndexedStrategy::IndexQuery FileNameIndexedStrategy::buildIndexQuery( const SearchQuery &query, SearchType searchType, bool caseSensitive, bool pinyinEnabled, bool pinyinAcronymEnabled, const QStringList &fileTypes, const QStringList &fileExtensions) { IndexQuery result; result.type = searchType; result.caseSensitive = caseSensitive; result.fileTypes = fileTypes; result.fileExtensions = fileExtensions; result.usePinyin = pinyinEnabled; // 设置拼音搜索标志 result.usePinyinAcronym = pinyinAcronymEnabled; // 设置拼音首字母搜索标志 switch (searchType) { case SearchType::Simple: result.terms.append(query.keyword()); break; case SearchType::Wildcard: result.terms.append(query.keyword()); break; case SearchType::Boolean: result.terms = SearchUtility::extractBooleanKeywords(query); result.booleanOp = query.type() == SearchQuery::Type::Boolean ? query.booleanOperator() : SearchQuery::BooleanOperator::AND; break; case SearchType::Pinyin: result.terms.append(query.keyword()); break; case SearchType::PinyinAcronym: result.terms.append(query.keyword()); break; case SearchType::Combined: // 只有当有关键词时才设置 terms if (query.type() == SearchQuery::Type::Boolean) { result.terms = SearchUtility::extractBooleanKeywords(query); result.booleanOp = query.booleanOperator(); } else if (!query.keyword().isEmpty()) { result.terms = QStringList { query.keyword() }; } result.combineWithFileType = !fileTypes.isEmpty(); result.combineWithFileExt = !fileExtensions.isEmpty(); break; } return result; } void FileNameIndexedStrategy::executeIndexQuery(const IndexQuery &query) { // 获取索引目录 FSDirectoryPtr directory = m_indexManager->getIndexDirectory(m_indexDir); if (!directory) { emit errorOccurred(SearchError(SearchErrorCode::InternalError)); return; } if (!IndexReader::indexExists(directory)) { qWarning() << "Index does not exist:" << m_indexDir; emit errorOccurred(SearchError(FileNameSearchErrorCode::FileNameIndexNotFound)); return; } // 获取索引读取器 IndexReaderPtr reader = m_indexManager->getIndexReader(directory); if (!reader || reader->numDocs() == 0) { qWarning() << "Index is empty"; emit errorOccurred(SearchError(SearchErrorCode::InternalError)); return; } // 获取搜索器 SearcherPtr searcher = m_indexManager->getSearcher(reader); if (!searcher) { emit errorOccurred(SearchError(SearchErrorCode::InternalError)); return; } // 构建查询 QueryPtr luceneQuery; try { luceneQuery = buildLuceneQuery(query); if (!luceneQuery) { emit errorOccurred(SearchError(SearchErrorCode::InvalidQuery)); return; } } catch (const LuceneException &e) { qWarning() << "Error building query:" << QString::fromStdWString(e.getError()); emit errorOccurred(SearchError(SearchErrorCode::InvalidQuery)); return; } // Measure the time taken to execute the search QElapsedTimer searchTimer; searchTimer.start(); // 执行搜索 int32_t maxResults = m_options.maxResults() > 0 ? m_options.maxResults() : reader->numDocs(); // 使用自定义 CancellableCollector 实现可中断搜索 Collection scoreDocs; try { // 创建可取消的收集器 boost::shared_ptr collector = newLucene(m_cancelledRef, maxResults); // 执行搜索,使用自定义收集器 searcher->search(luceneQuery, collector); // 获取收集到的文档 scoreDocs = collector->getScoreDocs(); qInfo() << "Filename search execution time:" << searchTimer.elapsed() << "ms" << "Total hits:" << collector->getTotalHits() << "Collected:" << scoreDocs.size(); } catch (const SearchCancelledException &e) { qInfo() << "Filename search cancelled during execution"; return; } // Measure the time taken to process search results QElapsedTimer resultTimer; resultTimer.start(); auto docsSize = scoreDocs.size(); m_results.reserve(docsSize); // 实时处理搜索结果 for (int i = 0; i < docsSize; i++) { if (m_cancelledRef && m_cancelledRef->load()) { qInfo() << "Filename search cancelled"; break; } try { ScoreDocPtr scoreDoc = scoreDocs[i]; DocumentPtr doc = searcher->doc(scoreDoc->doc); QString path = QString::fromStdWString(doc->get(LuceneFieldNames::FileName::kFullPath)); // Path filtering, excluded paths, hidden file — handled at query layer // 处理搜索结果 if (Q_UNLIKELY(m_options.detailedResultsEnabled())) { m_results.append(processDetailedSearchResult(path, doc)); } else { SearchResult result(path); m_results.append(std::move(result)); } // 实时发送结果 if (Q_UNLIKELY(m_options.resultFoundEnabled())) emit resultFound(m_results.last()); } catch (const LuceneException &e) { qWarning() << "Error processing result:" << QString::fromStdWString(e.getError()); continue; } } qInfo() << "Filename result processing time:" << resultTimer.elapsed() << "ms"; } SearchResult FileNameIndexedStrategy::processDetailedSearchResult( const QString &path, const Lucene::DocumentPtr &doc) { // 创建搜索结果 SearchResult result(path); FileNameResultAPI api(result); // 文件类型 QString type = QString::fromStdWString(doc->get(LuceneFieldNames::FileName::kFileType)); api.setFileType(type); api.setIsDirectory(type == "dir"); // 文件名和扩展名 QString fileName = QString::fromStdWString(doc->get(LuceneFieldNames::FileName::kFileName)); api.setFilename(fileName); QString fileExt = QString::fromStdWString(doc->get(LuceneFieldNames::FileName::kFileExt)); api.setFileExtension(fileExt); // 文件大小 QString size = QString::fromStdWString(doc->get(LuceneFieldNames::FileName::kFileSizeStr)); api.setSize(size); // 文件大小(数值,字节) QString fileSizeStr = QString::fromStdWString(doc->get(LuceneFieldNames::FileName::kFileSize)); if (!fileSizeStr.isEmpty()) { bool ok = false; qint64 fileSizeBytes = fileSizeStr.toLongLong(&ok); if (ok && fileSizeBytes >= 0) { api.setFileSizeBytes(fileSizeBytes); } } // 隐藏状态 QString isHiddenStr = QString::fromStdWString(doc->get(LuceneFieldNames::FileName::kIsHidden)).toLower(); api.setIsHidden(isHiddenStr == "y"); // 修改时间 QString modifyTimeStr = QString::fromStdWString(doc->get(LuceneFieldNames::FileName::kModifyTime)); if (!modifyTimeStr.isEmpty()) { bool ok = false; qint64 modifyTimestamp = modifyTimeStr.toLongLong(&ok); if (ok && modifyTimestamp > 0) { api.setModifyTimestamp(modifyTimestamp); } } // 创建时间 QString birthTimeStr = QString::fromStdWString(doc->get(LuceneFieldNames::FileName::kBirthTime)); if (!birthTimeStr.isEmpty()) { bool ok = false; qint64 birthTimestamp = birthTimeStr.toLongLong(&ok); if (ok && birthTimestamp > 0) { api.setBirthTimestamp(birthTimestamp); } } return result; } Lucene::QueryPtr FileNameIndexedStrategy::buildLuceneQuery(const IndexQuery &query) const { BooleanQueryPtr finalQuery = newLucene(); bool hasValidQuery = false; switch (query.type) { case SearchType::Simple: if (!query.terms.isEmpty()) { QueryPtr simpleQuery = m_queryBuilder->buildSimpleQuery(query.terms.first(), query.caseSensitive); if (simpleQuery) { finalQuery->add(simpleQuery, BooleanClause::MUST); hasValidQuery = true; } } break; case SearchType::Wildcard: if (!query.terms.isEmpty()) { QueryPtr wildcardQuery = m_queryBuilder->buildWildcardQuery(query.terms.first(), query.caseSensitive); if (wildcardQuery) { finalQuery->add(wildcardQuery, BooleanClause::MUST); hasValidQuery = true; } } break; case SearchType::Boolean: if (!query.terms.isEmpty()) { BooleanQueryPtr booleanQuery = buildBooleanTermsQuery(query); if (booleanQuery) { finalQuery->add(booleanQuery, BooleanClause::MUST); hasValidQuery = true; } } break; case SearchType::Pinyin: if (!query.terms.isEmpty()) { BooleanQueryPtr combinedQuery = newLucene(); // 添加拼音查询 if (Global::isPinyinSequence(query.terms.first())) { QueryPtr pinyinQuery = m_queryBuilder->buildPinyinQuery(query.terms); if (pinyinQuery) { combinedQuery->add(pinyinQuery, BooleanClause::SHOULD); hasValidQuery = true; } } // 添加普通关键词查询 QueryPtr simpleQuery = m_queryBuilder->buildSimpleQuery(query.terms.first(), query.caseSensitive); if (simpleQuery) { combinedQuery->add(simpleQuery, BooleanClause::SHOULD); hasValidQuery = true; } if (hasValidQuery) { finalQuery->add(combinedQuery, BooleanClause::MUST); } } break; case SearchType::PinyinAcronym: if (!query.terms.isEmpty()) { BooleanQueryPtr combinedQuery = newLucene(); // 添加拼音首字母查询 if (Global::isPinyinAcronymSequence(query.terms.first())) { QueryPtr pinyinAcronymQuery = m_queryBuilder->buildPinyinAcronymQuery(query.terms); if (pinyinAcronymQuery) { combinedQuery->add(pinyinAcronymQuery, BooleanClause::SHOULD); hasValidQuery = true; } } // 添加普通关键词查询 QueryPtr simpleQuery = m_queryBuilder->buildSimpleQuery(query.terms.first(), query.caseSensitive); if (simpleQuery) { combinedQuery->add(simpleQuery, BooleanClause::SHOULD); hasValidQuery = true; } if (hasValidQuery) { finalQuery->add(combinedQuery, BooleanClause::MUST); } } break; case SearchType::Combined: if (!query.terms.isEmpty()) { BooleanQueryPtr combinedQuery = buildBooleanTermsQuery(query); if (combinedQuery) { finalQuery->add(combinedQuery, BooleanClause::MUST); hasValidQuery = true; } } // 构建文件类型查询 if (query.combineWithFileType && !query.fileTypes.isEmpty()) { QueryPtr typeQuery = m_queryBuilder->buildTypeQuery(query.fileTypes); if (typeQuery) { finalQuery->add(typeQuery, BooleanClause::MUST); hasValidQuery = true; } } // 构建文件后缀查询 if (query.combineWithFileExt && !query.fileExtensions.isEmpty()) { QueryPtr extQuery = m_queryBuilder->buildExtQuery(query.fileExtensions); if (extQuery) { finalQuery->add(extQuery, BooleanClause::MUST); hasValidQuery = true; } } break; } // hiddenOnly 本身就是有效主查询条件,不依赖其他 keyword/type/ext。 // 让 "下载的隐藏文件" 这类无关键字的语义搜索能直接走 kIsHidden 倒排索引, // 同时让后续 pathPrefixQuery / excludedPaths / hidden filter 能正确叠加。 // 必须在 pathPrefixQuery 等依赖 hasValidQuery 的块之前注入。 if (m_options.hiddenOnly()) { QueryPtr hiddenOnlyQuery = Lucene::newLucene( Lucene::newLucene( LuceneFieldNames::FileName::kIsHidden, L"Y")); finalQuery->add(hiddenOnlyQuery, Lucene::BooleanClause::MUST); hasValidQuery = true; } // Add time range filter query if (m_options.hasTimeRangeFilter()) { TimeRangeFilter filter = m_options.timeRangeFilter(); QueryPtr timeQuery = TimeRangeUtils::buildTimeRangeFilterQuery( filter, LuceneFieldNames::FileName::kBirthTime, LuceneFieldNames::FileName::kModifyTime); if (timeQuery) { finalQuery->add(timeQuery, BooleanClause::MUST); hasValidQuery = true; } } // Add file size range filter query if (m_options.hasSizeRangeFilter()) { SizeRangeFilter sizeFilter = m_options.sizeRangeFilter(); QueryPtr sizeQuery = TimeRangeUtils::buildNumericRangeQuery( LuceneFieldNames::FileName::kFileSize, sizeFilter.minSize(), sizeFilter.maxSize(), sizeFilter.includeLower(), sizeFilter.includeUpper()); if (sizeQuery) { finalQuery->add(sizeQuery, BooleanClause::MUST); hasValidQuery = true; } } // Add path prefix query optimization QStringList searchPathsList = m_options.searchPaths(); if (hasValidQuery) { QueryPtr pathPrefixQuery = LuceneQueryUtils::buildMultiPathPrefixQuery( searchPathsList, QString::fromWCharArray(LuceneFieldNames::FileName::kAncestorPaths)); if (pathPrefixQuery) { finalQuery->add(pathPrefixQuery, BooleanClause::MUST); qInfo() << "Using multi-path prefix query for optimization:" << searchPathsList; } } // Add excluded paths filter (pushed down to query layer) if (hasValidQuery) { const QStringList &excludedPaths = m_options.searchExcludedPaths(); if (!excludedPaths.isEmpty()) { QueryPtr excludedQuery = LuceneQueryUtils::buildMultiPathPrefixQuery( excludedPaths, QString::fromWCharArray(LuceneFieldNames::FileName::kAncestorPaths)); if (excludedQuery) { finalQuery->add(excludedQuery, BooleanClause::MUST_NOT); } } } // Filter hidden files at query level to avoid losing results due to maxResults limit if (hasValidQuery && Q_LIKELY(!m_options.includeHidden())) { QueryPtr hiddenQuery = Lucene::newLucene( Lucene::newLucene( LuceneFieldNames::FileName::kIsHidden, L"Y")); finalQuery->add(hiddenQuery, Lucene::BooleanClause::MUST_NOT); } return hasValidQuery ? finalQuery : nullptr; } BooleanQueryPtr FileNameIndexedStrategy::buildBooleanTermsQuery(const IndexQuery &query) const { // 创建布尔查询 BooleanQueryPtr booleanQuery = newLucene(); bool hasValidQuery = false; // 对每个搜索词创建子查询 for (const QString &term : query.terms) { BooleanQueryPtr termQuery = newLucene(); bool termHasQuery = false; // 添加普通关键词查询 QueryPtr keywordQuery = m_queryBuilder->buildSimpleQuery(term, query.caseSensitive); if (keywordQuery) { termQuery->add(keywordQuery, BooleanClause::SHOULD); termHasQuery = true; } // 添加拼音查询 if (query.usePinyin && Global::isPinyinSequence(term)) { QueryPtr pinyinQuery = m_queryBuilder->buildPinyinQuery(QStringList { term }); if (pinyinQuery) { termQuery->add(pinyinQuery, BooleanClause::SHOULD); termHasQuery = true; } } // 添加拼音首字母查询 if (query.usePinyinAcronym && Global::isPinyinAcronymSequence(term)) { QueryPtr pinyinAcronymQuery = m_queryBuilder->buildPinyinAcronymQuery(QStringList { term }); if (pinyinAcronymQuery) { termQuery->add(pinyinAcronymQuery, BooleanClause::SHOULD); termHasQuery = true; } } // 将当前词的查询添加到最终查询中,维持原始bool逻辑 if (termHasQuery) { booleanQuery->add(termQuery, query.booleanOp == SearchQuery::BooleanOperator::AND ? BooleanClause::MUST : BooleanClause::SHOULD); hasValidQuery = true; } } return hasValidQuery ? booleanQuery : nullptr; } void FileNameIndexedStrategy::cancel() { if (m_cancelledRef) m_cancelledRef->store(true); } DFM_SEARCH_END_NS indexedstrategy.h000066400000000000000000000113021523477242100356030ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef FILENAME_INDEXED_STRATEGY_H #define FILENAME_INDEXED_STRATEGY_H #include "basestrategy.h" #include #include #include using namespace Lucene; DFM_SEARCH_BEGIN_NS class QueryBuilder; class SearchCache; class IndexManager; /** * @brief 文件名索引搜索策略 * 使用 Lucene 实现高性能文件搜索 */ class FileNameIndexedStrategy : public FileNameBaseStrategy { Q_OBJECT public: // 内部使用的搜索类型枚举 enum class SearchType { Simple, // 简单关键词搜索 Wildcard, // 通配符搜索 Boolean, // 布尔多关键词搜索 Pinyin, // 拼音搜索 PinyinAcronym, // 拼音首字母搜索 Combined // 组合搜索(关键词+文件类型/拼音/文件后缀/文件类型和后缀) }; // 索引查询结构 struct IndexQuery { SearchType type = SearchType::Simple; QStringList terms; QStringList fileTypes; QStringList fileExtensions; bool caseSensitive = false; bool usePinyin = false; bool usePinyinAcronym = false; SearchQuery::BooleanOperator booleanOp = SearchQuery::BooleanOperator::AND; bool combineWithFileType = false; // 是否与文件类型组合 bool combineWithFileExt = false; // 是否与文件后缀组合 }; explicit FileNameIndexedStrategy(const SearchOptions &options, QObject *parent = nullptr); ~FileNameIndexedStrategy() override; void search(const SearchQuery &query) override; void cancel() override; private: // 初始化索引相关设置 void initializeIndexing(); // 执行索引搜索 void performIndexSearch(const SearchQuery &query, const FileNameOptionsAPI &api); // 确定搜索类型 SearchType determineSearchType(const SearchQuery &query, bool pinyinEnabled, bool pinyinAcronymEnabled, const QStringList &fileTypes, const QStringList &fileExtensions) const; // 构建索引查询 IndexQuery buildIndexQuery(const SearchQuery &query, SearchType searchType, bool caseSensitive, bool pinyinEnabled, bool pinyinAcronymEnabled, const QStringList &fileTypes, const QStringList &fileExtensions); // 执行索引查询并处理结果 void executeIndexQuery(const IndexQuery &query); // 构建 Lucene 查询 QueryPtr buildLuceneQuery(const IndexQuery &query) const; // 构建布尔查询的辅助方法 BooleanQueryPtr buildBooleanTermsQuery(const IndexQuery &query) const; // 处理详细搜索结果(读取所有索引字段) SearchResult processDetailedSearchResult(const QString &path, const Lucene::DocumentPtr &doc); // 成员变量 QString m_indexDir; // 索引目录路径 // Lucene 相关组件 std::unique_ptr m_queryBuilder; // 查询构建器 std::unique_ptr m_indexManager; // 索引管理器 }; /** * @brief 查询构建器类 * 负责构建各种类型的 Lucene 查询 */ class QueryBuilder { public: QueryBuilder(); // 构建各种类型的查询 QueryPtr buildTypeQuery(const QStringList &types) const; QueryPtr buildExtQuery(const QStringList &extensions) const; QueryPtr buildPinyinQuery(const QStringList &pinyins, SearchQuery::BooleanOperator op = SearchQuery::BooleanOperator::AND) const; QueryPtr buildPinyinAcronymQuery(const QStringList &acronyms, SearchQuery::BooleanOperator op = SearchQuery::BooleanOperator::AND) const; QueryPtr buildWildcardQuery(const QString &keyword, bool caseSensitive) const; QueryPtr buildSimpleQuery(const QString &keyword, bool caseSensitive) const; }; /** * @brief 索引管理器类 * 管理 Lucene 索引目录和读取器 */ class IndexManager { public: IndexManager(); // 获取索引目录 FSDirectoryPtr getIndexDirectory(const QString &indexPath) const; // 获取索引读取器 IndexReaderPtr getIndexReader(FSDirectoryPtr directory) const; // 获取搜索器 SearcherPtr getSearcher(IndexReaderPtr reader) const; private: mutable FSDirectoryPtr m_cachedDirectory; mutable IndexReaderPtr m_cachedReader; mutable SearcherPtr m_cachedSearcher; mutable QString m_cachedIndexPath; }; DFM_SEARCH_END_NS #endif // FILENAME_INDEXED_STRATEGY_H realtimestrategy.cpp000066400000000000000000000264351523477242100363350ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "realtimestrategy.h" #include #include #include #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS FileNameRealTimeStrategy::FileNameRealTimeStrategy(const SearchOptions &options, QObject *parent) : FileNameBaseStrategy(options, parent) { } FileNameRealTimeStrategy::~FileNameRealTimeStrategy() = default; void FileNameRealTimeStrategy::search(const SearchQuery &query) { m_results.clear(); // 从搜索选项获取参数 const QString searchPath = m_options.searchPath(); const QStringList excludedPaths = m_options.searchExcludedPaths(); const bool caseSensitive = m_options.caseSensitive(); const bool includeHidden = m_options.includeHidden(); const int maxResults = m_options.maxResults() > 0 ? m_options.maxResults() : INT_MAX; // 获取文件类型过滤 FileNameOptionsAPI optionsApi(const_cast(m_options)); const QStringList fileExts = optionsApi.fileExtensions(); const bool detailedResults = m_options.detailedResultsEnabled(); const bool resultFoundEnabled = m_options.resultFoundEnabled(); // 检查搜索路径 QFileInfo pathInfo(searchPath); if (!pathInfo.exists() || !pathInfo.isDir()) { emit errorOccurred(SearchError(SearchErrorCode::PathNotFound)); emit searchFinished(m_results); return; } // 性能计时 QElapsedTimer searchTimer; searchTimer.start(); // 设置基础过滤器 QDir::Filters dirFilters = QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot; if (includeHidden) { dirFilters |= QDir::Hidden; } // 使用非递归的方式进行遍历 QStack directoryStack; directoryStack.push(searchPath); int count = 0; QSet visitedDirs; // 防止符号链接循环 while (!directoryStack.isEmpty() && count < maxResults && !(m_cancelledRef && m_cancelledRef->load())) { // 取出一个目录进行处理 QString currentDir = directoryStack.pop(); // 避免符号链接循环 QString canonicalPath = QFileInfo(currentDir).canonicalFilePath(); if (visitedDirs.contains(canonicalPath)) { continue; } visitedDirs.insert(canonicalPath); // 检查是否在排除路径中 if (std::any_of(excludedPaths.cbegin(), excludedPaths.cend(), [¤tDir](const QString &excludedPath) { return currentDir.startsWith(excludedPath); })) { continue; } // 获取当前目录的内容 QDir dir(currentDir); QFileInfoList entries; try { entries = dir.entryInfoList(dirFilters, QDir::Name); } catch (const std::exception &e) { qWarning() << "Permission Denied: " << dir.absolutePath(); continue; } // 如果无法读取目录,可能是因为权限问题 if (entries.isEmpty() && !dir.exists()) { qWarning() << "Permission Denied: " << dir.absolutePath(); continue; } // 处理当前目录中的每个条目 for (const QFileInfo &info : std::as_const(entries)) { if ((m_cancelledRef && m_cancelledRef->load()) || count >= maxResults) { break; } // 将子目录添加到栈中以便后续处理 // 符号链接目录不递归进入(防止循环),但其名称仍参与匹配 if (info.isDir() && !info.isSymLink()) { directoryStack.push(info.filePath()); } // 检查文件名是否匹配查询 QString fileName = info.fileName(); bool matches = false; // 如果只有过滤条件(时间/大小)没有关键词,直接匹配 bool hasKeyword = !query.keyword().isEmpty() || query.type() == SearchQuery::Type::Boolean; if (!hasKeyword && (m_options.hasTimeRangeFilter() || m_options.hasSizeRangeFilter())) { matches = true; } // 简单查询模式 else if (query.type() == SearchQuery::Type::Simple) { matches = fileName.contains(query.keyword(), caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); } // 通配符查询模式 else if (query.type() == SearchQuery::Type::Wildcard) { matches = matchWildcard(fileName, query.keyword(), caseSensitive); } // 布尔查询模式 else if (query.type() == SearchQuery::Type::Boolean) { matches = matchBoolean(fileName, query, caseSensitive, false); } // 如果文件 suffix 过滤器不为空,检查文件 suffix 是否匹配 if (matches && !fileExts.isEmpty()) { const QString &suffix = info.suffix().toLower(); if (!fileExts.contains(suffix)) { matches = false; } } // 时间范围过滤 if (matches && m_options.hasTimeRangeFilter()) { TimeRangeFilter filter = m_options.timeRangeFilter(); auto [start, end] = filter.resolveTimeRange(); QDateTime fileTime = (filter.timeField() == TimeField::BirthTime) ? info.birthTime() : info.lastModified(); // 时间范围检查 bool timeMatch = true; if (start.isValid()) { if (filter.includeLower()) { timeMatch = timeMatch && (fileTime >= start); } else { timeMatch = timeMatch && (fileTime > start); } } if (end.isValid()) { if (filter.includeUpper()) { timeMatch = timeMatch && (fileTime <= end); } else { timeMatch = timeMatch && (fileTime < end); } } matches = timeMatch; } // 文件大小范围过滤 if (matches && m_options.hasSizeRangeFilter()) { SizeRangeFilter sizeFilter = m_options.sizeRangeFilter(); qint64 fileSize = info.size(); bool sizeMatch = true; if (sizeFilter.minSize() > 0) { if (sizeFilter.includeLower()) { sizeMatch = sizeMatch && (fileSize >= sizeFilter.minSize()); } else { sizeMatch = sizeMatch && (fileSize > sizeFilter.minSize()); } } if (sizeFilter.maxSize() > 0) { if (sizeFilter.includeUpper()) { sizeMatch = sizeMatch && (fileSize <= sizeFilter.maxSize()); } else { sizeMatch = sizeMatch && (fileSize < sizeFilter.maxSize()); } } matches = sizeMatch; } if (matches && m_options.hiddenOnly() && !info.isHidden()) { matches = false; } if (matches) { // 创建搜索结果 SearchResult result(info.filePath()); if (detailedResults) { FileNameResultAPI api(result); api.setIsDirectory(info.isDir()); if (!info.isDir()) { api.setFileType(info.suffix().isEmpty() ? "unknown" : info.suffix().toLower()); api.setFileExtension(info.suffix().toLower()); api.setSize(QString::number(info.size())); api.setFileSizeBytes(info.size()); } else { api.setFileType("dir"); } api.setFilename(info.fileName()); api.setIsHidden(info.isHidden()); // 设置修改时间戳 api.setModifyTimestamp(info.lastModified().toSecsSinceEpoch()); // 设置创建时间戳 QDateTime birthTime = info.birthTime(); if (birthTime.isValid()) { api.setBirthTimestamp(birthTime.toSecsSinceEpoch()); } } // 实时发送结果 if (resultFoundEnabled) { emit resultFound(result); } // 添加到结果集合 m_results.append(result); count++; } } } qInfo() << "Real-time filename search completed in" << searchTimer.elapsed() << "ms with" << count << "results"; emit searchFinished(m_results); } bool FileNameRealTimeStrategy::matchPinyin(const QString &fileName, const QString &keyword) { // 实时搜索不支持拼音匹配 Q_UNUSED(fileName); Q_UNUSED(keyword); return false; } bool FileNameRealTimeStrategy::matchBoolean(const QString &fileName, const SearchQuery &query, bool caseSensitive, bool pinyinEnabled) { Q_UNUSED(pinyinEnabled); // 实时搜索不支持拼音匹配 // 获取布尔操作符类型 auto op = query.booleanOperator(); // 获取子查询列表 const auto &subQueries = query.subQueries(); // 如果没有子查询,使用简单匹配 if (subQueries.isEmpty()) { return fileName.contains(query.keyword(), caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); } // 根据布尔操作符处理 switch (op) { case SearchQuery::BooleanOperator::AND: { // 所有子查询都必须匹配 for (const auto &subQuery : subQueries) { bool subMatch = matchBoolean(fileName, subQuery, caseSensitive, false); if (!subMatch) { return false; } } return true; } case SearchQuery::BooleanOperator::OR: { // 任一子查询匹配即可 for (const auto &subQuery : subQueries) { bool subMatch = matchBoolean(fileName, subQuery, caseSensitive, false); if (subMatch) { return true; } } return false; } } return false; } bool FileNameRealTimeStrategy::matchWildcard(const QString &fileName, const QString &pattern, bool caseSensitive) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // 利用Qt内置的通配符匹配功能 QRegularExpression regex = QRegularExpression::fromWildcard( pattern, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); return regex.match(fileName).hasMatch(); #else QRegExp regex = QRegExp(pattern, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard); return regex.exactMatch(fileName); #endif } void FileNameRealTimeStrategy::cancel() { if (m_cancelledRef) m_cancelledRef->store(true); } DFM_SEARCH_END_NS realtimestrategy.h000066400000000000000000000020251523477242100357670ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef FILENAME_REALTIME_STRATEGY_H #define FILENAME_REALTIME_STRATEGY_H #include "basestrategy.h" DFM_SEARCH_BEGIN_NS /** * @brief 文件名实时搜索策略 */ class FileNameRealTimeStrategy : public FileNameBaseStrategy { Q_OBJECT public: explicit FileNameRealTimeStrategy(const SearchOptions &options, QObject *parent = nullptr); ~FileNameRealTimeStrategy() override; void search(const SearchQuery &query) override; void cancel() override; private: // 拼音匹配 bool matchPinyin(const QString &fileName, const QString &keyword); // 布尔查询匹配 bool matchBoolean(const QString &fileName, const SearchQuery &query, bool caseSensitive, bool pinyinEnabled); // 通配符匹配 bool matchWildcard(const QString &fileName, const QString &pattern, bool caseSensitive); }; DFM_SEARCH_END_NS #endif // FILENAME_REALTIME_STRATEGY_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/ocrtextsearch/000077500000000000000000000000001523477242100263465ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextsearchapi.cpp000066400000000000000000000031651523477242100324270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include DFM_SEARCH_BEGIN_NS OcrTextOptionsAPI::OcrTextOptionsAPI(SearchOptions &options) : TextSearchOptionsAPI(options) { // init default if (!m_options.hasCustomOption("maxPreviewLength")) setMaxPreviewLength(200); if (!m_options.hasCustomOption("searchResultHighlight")) setSearchResultHighlightEnabled(false); if (!m_options.hasCustomOption("fullTextRetrieval")) setFullTextRetrievalEnabled(true); if (!m_options.hasCustomOption("filenameOcrContentMixedAndSearchEnabled")) setFilenameOcrContentMixedAndSearchEnabled(false); } void OcrTextOptionsAPI::setFilenameOcrContentMixedAndSearchEnabled(bool enabled) { m_options.setCustomOption("filenameOcrContentMixedAndSearchEnabled", enabled); } bool OcrTextOptionsAPI::isFilenameOcrContentMixedAndSearchEnabled() const { return m_options.customOption("filenameOcrContentMixedAndSearchEnabled").toBool(); } OcrTextResultAPI::OcrTextResultAPI(SearchResult &result) : TextSearchResultAPI(result) { } QString OcrTextResultAPI::ocrContent() const { return m_result.customAttribute("ocrContent").toString(); } void OcrTextResultAPI::setOcrContent(const QString &content) { m_result.setCustomAttribute("ocrContent", content); } QString OcrTextResultAPI::checksum() const { return m_result.customAttribute("checksum").toString(); } void OcrTextResultAPI::setChecksum(const QString &checksum) { m_result.setCustomAttribute("checksum", checksum); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextsearchengine.cpp000066400000000000000000000040321523477242100331150ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "ocrtextsearchengine.h" #include #include "ocrtextstrategies/indexedstrategy.h" DFM_SEARCH_BEGIN_NS DCORE_USE_NAMESPACE OcrTextSearchEngine::OcrTextSearchEngine(QObject *parent) : GenericSearchEngine(parent) { } OcrTextSearchEngine::~OcrTextSearchEngine() = default; void OcrTextSearchEngine::setupStrategyFactory() { // Set up OCR text search strategy factory auto factory = std::make_unique(); m_worker->setStrategyFactory(std::move(factory)); } SearchError OcrTextSearchEngine::validateSearchConditions() { // First execute base class validation auto result = GenericSearchEngine::validateSearchConditions(); if (result.isError()) { return result; } // OCR text search specific validation if (m_options.method() != SearchMethod::Indexed) { return SearchError(SearchErrorCode::InvalidSerchMethod); } // Check for unsupported Wildcard query type if (m_currentQuery.type() == SearchQuery::Type::Wildcard) { return SearchError(OcrTextSearchErrorCode::WildcardNotSupported); } OcrTextOptionsAPI optAPI(m_options); if (m_currentQuery.type() == SearchQuery::Type::Simple && m_currentQuery.keyword().toUtf8().size() < Global::kMinContentSearchKeywordLength && optAPI.filenameKeyword().isEmpty()) { return SearchError(OcrTextSearchErrorCode::KeywordTooShort); } return result; } std::unique_ptr OcrTextSearchStrategyFactory::createStrategy( SearchType searchType, const SearchOptions &options) { // Ensure correct search type if (searchType != SearchType::Ocr) { return nullptr; } // Create corresponding strategy based on search method if (options.method() == SearchMethod::Indexed) { return std::make_unique(options); } return nullptr; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextsearchengine.h000066400000000000000000000023721523477242100325670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef OCRTEXT_SEARCH_ENGINE_H #define OCRTEXT_SEARCH_ENGINE_H #include "core/genericsearchengine.h" DFM_SEARCH_BEGIN_NS /** * @brief OCR text search engine * * Implements search functionality for text extracted from images using OCR. * This engine uses Lucene indexes similar to content search, but with * simplified features (no highlighting support). */ class OcrTextSearchEngine : public GenericSearchEngine { Q_OBJECT public: explicit OcrTextSearchEngine(QObject *parent = nullptr); ~OcrTextSearchEngine() override; // Implement search type SearchType searchType() const override { return SearchType::Ocr; } protected: // Setup strategy factory void setupStrategyFactory() override; // Override validation to add specific checks SearchError validateSearchConditions() override; }; /** * @brief OCR text search strategy factory */ class OcrTextSearchStrategyFactory : public SearchStrategyFactory { public: std::unique_ptr createStrategy( SearchType searchType, const SearchOptions &options) override; }; DFM_SEARCH_END_NS #endif // OCRTEXT_SEARCH_ENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies/000077500000000000000000000000001523477242100321315ustar00rootroot00000000000000basestrategy.h000066400000000000000000000012771523477242100347270ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef OCRTEXT_BASE_STRATEGY_H #define OCRTEXT_BASE_STRATEGY_H #include "core/searchstrategy/basesearchstrategy.h" #include DFM_SEARCH_BEGIN_NS /** * @brief OCR text search strategy base class */ class OcrTextBaseStrategy : public BaseSearchStrategy { Q_OBJECT public: explicit OcrTextBaseStrategy(const SearchOptions &options, QObject *parent = nullptr) : BaseSearchStrategy(options, parent) { } SearchType searchType() const override { return SearchType::Ocr; } }; DFM_SEARCH_END_NS #endif // OCRTEXT_BASE_STRATEGY_H indexedstrategy.cpp000066400000000000000000000650131523477242100357660ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "indexedstrategy.h" #include #include #include #include #include #include #include #include #include #include #include #include "utils/cancellablecollector.h" #include "utils/contenthighlighter.h" #include "utils/lucenequeryutils.h" #include "utils/lucene_cancellation_compat.h" #include "utils/timerangeutils.h" using namespace Lucene; DFM_SEARCH_BEGIN_NS OcrTextIndexedStrategy::OcrTextIndexedStrategy(const SearchOptions &options, QObject *parent) : OcrTextBaseStrategy(options, parent) { initializeIndexing(); } OcrTextIndexedStrategy::~OcrTextIndexedStrategy() = default; void OcrTextIndexedStrategy::initializeIndexing() { // Get OCR text index directory m_indexDir = Global::ocrTextIndexDirectory(); // Check if index directory exists if (!QDir(m_indexDir).exists()) { qWarning() << "OCR text index directory does not exist:" << m_indexDir; } } void OcrTextIndexedStrategy::search(const SearchQuery &query) { m_results.clear(); try { // Perform OCR text index search performOcrTextSearch(query); } catch (const std::exception &e) { qWarning() << "OCR Text Index Search Exception:" << e.what(); emit errorOccurred(SearchError(OcrTextSearchErrorCode::OcrTextIndexException)); } } Lucene::QueryPtr OcrTextIndexedStrategy::buildLuceneQuery(const SearchQuery &query) { try { m_keywords.clear(); OcrTextOptionsAPI optAPI(m_options); bool mixedAndEnabled = optAPI.isFilenameOcrContentMixedAndSearchEnabled(); Lucene::QueryPtr mainQuery; if (query.type() == SearchQuery::Type::Simple) { mainQuery = buildSimpleOcrContentsQuery(query); } else if (query.type() == SearchQuery::Type::Boolean) { if (query.subQueries().isEmpty()) { // For an empty boolean query, match nothing. mainQuery = newLucene(); } else { // Determine which logic path to take for boolean queries if (mixedAndEnabled && query.booleanOperator() == SearchQuery::BooleanOperator::AND) { // New "advanced" AND logic for ocr_contents/filename mainQuery = buildAdvancedAndQuery(query); } else { // "Standard" ocr_contents-only logic mainQuery = buildStandardBooleanOcrContentsQuery(query); } } } else { qWarning() << "Unknown SearchQuery type encountered."; mainQuery = newLucene(); // Should not happen } // Add filename keyword query (before filters, so it replaces empty content query correctly) QString filenameKw = optAPI.filenameKeyword(); if (!filenameKw.isEmpty()) { Lucene::QueryPtr filenameQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::OcrText::kFilename), filenameKw); if (filenameQuery) { // Check if content keywords are effectively empty bool noContentKeywords = (query.type() == SearchQuery::Type::Simple) ? query.keyword().isEmpty() : (query.subQueries().isEmpty() || std::all_of(query.subQueries().cbegin(), query.subQueries().cend(), [](const auto &sq) { return sq.keyword().isEmpty(); })); if (noContentKeywords) { // Filename-only search: replace empty content query with filename query mainQuery = filenameQuery; } else if (mainQuery) { // Both content and filename: AND combination BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(filenameQuery, BooleanClause::MUST); mainQuery = finalQuery; } m_keywords.append(filenameKw); } } // Add path prefix query optimization QStringList searchPathsList = m_options.searchPaths(); if (mainQuery) { QueryPtr pathPrefixQuery = LuceneQueryUtils::buildMultiPathPrefixQuery( searchPathsList, QString::fromWCharArray(LuceneFieldNames::OcrText::kAncestorPaths)); if (pathPrefixQuery) { BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(pathPrefixQuery, BooleanClause::MUST); qInfo() << "Using multi-path prefix query for OCR text search optimization:" << searchPathsList; mainQuery = finalQuery; } } // Add excluded paths filter (pushed down to query layer) if (mainQuery) { const QStringList &excludedPaths = m_options.searchExcludedPaths(); if (!excludedPaths.isEmpty()) { QueryPtr excludedQuery = LuceneQueryUtils::buildMultiPathPrefixQuery( excludedPaths, QString::fromWCharArray(LuceneFieldNames::OcrText::kAncestorPaths)); if (excludedQuery) { BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(excludedQuery, BooleanClause::MUST_NOT); mainQuery = finalQuery; } } } // Add hidden file filter (pushed down to query layer) if (mainQuery && !m_options.includeHidden()) { QueryPtr hiddenQuery = Lucene::newLucene( Lucene::newLucene( LuceneFieldNames::OcrText::kIsHidden, L"Y")); BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(hiddenQuery, BooleanClause::MUST_NOT); mainQuery = finalQuery; } // When hiddenOnly is true, only return hidden files (MUST kIsHidden:Y) if (mainQuery && m_options.hiddenOnly()) { QueryPtr hiddenOnlyQuery = Lucene::newLucene( Lucene::newLucene( LuceneFieldNames::OcrText::kIsHidden, L"Y")); BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(hiddenOnlyQuery, BooleanClause::MUST); mainQuery = finalQuery; } // Add time range filter query if (m_options.hasTimeRangeFilter()) { TimeRangeFilter filter = m_options.timeRangeFilter(); QueryPtr timeQuery = TimeRangeUtils::buildTimeRangeFilterQuery( filter, LuceneFieldNames::OcrText::kBirthTime, LuceneFieldNames::OcrText::kModifyTime); if (timeQuery) { if (mainQuery) { BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(timeQuery, BooleanClause::MUST); mainQuery = finalQuery; } else { // Time filter alone is a valid query mainQuery = timeQuery; } } } // Add file size range filter query if (m_options.hasSizeRangeFilter()) { SizeRangeFilter sizeFilter = m_options.sizeRangeFilter(); QueryPtr sizeQuery = TimeRangeUtils::buildNumericRangeQuery( LuceneFieldNames::OcrText::kFileSize, sizeFilter.minSize(), sizeFilter.maxSize(), sizeFilter.includeLower(), sizeFilter.includeUpper()); if (sizeQuery) { if (mainQuery) { BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(sizeQuery, BooleanClause::MUST); mainQuery = finalQuery; } else { // Size filter alone is a valid query mainQuery = sizeQuery; } } } // Add file extension filter query (pushed down to index query layer) { const QStringList fileExtensions = optAPI.fileExtensions(); if (!fileExtensions.isEmpty() && mainQuery) { Lucene::BooleanQueryPtr extQuery = newLucene(); for (const QString &ext : fileExtensions) { extQuery->add( Lucene::newLucene( Lucene::newLucene( LuceneFieldNames::OcrText::kFileExt, ext.toStdWString())), Lucene::BooleanClause::SHOULD); } extQuery->setMinimumNumberShouldMatch(1); BooleanQueryPtr finalQuery = newLucene(); finalQuery->add(mainQuery, BooleanClause::MUST); finalQuery->add(extQuery, BooleanClause::MUST); mainQuery = finalQuery; } } return mainQuery; } catch (const Lucene::LuceneException &e) { qWarning() << "Error building Lucene query:" << QString::fromStdWString(e.getError()); return nullptr; } catch (const std::exception &e) { qWarning() << "Standard exception building Lucene query:" << e.what(); return nullptr; } } QueryPtr OcrTextIndexedStrategy::buildAdvancedAndQuery(const SearchQuery &query) { // This method implements the "mixed" AND logic similar to content search. Lucene::BooleanQueryPtr overallQuery = newLucene(); Lucene::BooleanQueryPtr mainAndClausesQuery = newLucene(); Lucene::BooleanQueryPtr allOcrContentsQuery = newLucene(); Lucene::BooleanQueryPtr allFilenamesQuery = newLucene(); bool hasValidKeywords = false; for (const auto &subQuery : query.subQueries()) { m_keywords.append(subQuery.keyword()); if (subQuery.keyword().isEmpty()) { continue; // Skip empty keywords } hasValidKeywords = true; Lucene::QueryPtr ocrContentsTermQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::OcrText::kOcrContents), subQuery.keyword()); Lucene::QueryPtr filenameTermQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::OcrText::kFilename), subQuery.keyword()); // Build (ocr_contents:keyword OR filename:keyword) Lucene::BooleanQueryPtr combinedTermQuery = newLucene(); combinedTermQuery->add(ocrContentsTermQuery, Lucene::BooleanClause::SHOULD); combinedTermQuery->add(filenameTermQuery, Lucene::BooleanClause::SHOULD); mainAndClausesQuery->add(combinedTermQuery, Lucene::BooleanClause::MUST); allOcrContentsQuery->add(ocrContentsTermQuery, Lucene::BooleanClause::MUST); allFilenamesQuery->add(filenameTermQuery, Lucene::BooleanClause::MUST); } if (!hasValidKeywords) { // All subQuery keywords were empty qWarning() << "No valid keywords found in advanced AND query"; return newLucene(); // Matches nothing } // Exclude pure filename-only matches // Final query: ( (ocr:k1 OR f:k1) AND ... ) AND NOT (f:k1 AND f:k2 ... AND NOT (ocr:k1 AND ocr:k2 ...)) Lucene::BooleanQueryPtr pureFilenameQuery = newLucene(); pureFilenameQuery->add(allFilenamesQuery, Lucene::BooleanClause::MUST); pureFilenameQuery->add(allOcrContentsQuery, Lucene::BooleanClause::MUST_NOT); overallQuery->add(mainAndClausesQuery, Lucene::BooleanClause::MUST); overallQuery->add(pureFilenameQuery, Lucene::BooleanClause::MUST_NOT); return overallQuery; } QueryPtr OcrTextIndexedStrategy::buildStandardBooleanOcrContentsQuery(const SearchQuery &query) { // This method implements the "original" boolean logic, searching only "ocr_contents". Lucene::BooleanQueryPtr booleanQuery = newLucene(); for (const auto &subQuery : query.subQueries()) { m_keywords.append(subQuery.keyword()); if (subQuery.keyword().isEmpty()) { continue; // Skip empty keywords } Lucene::QueryPtr termQuery = LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::OcrText::kOcrContents), subQuery.keyword()); booleanQuery->add(termQuery, query.booleanOperator() == SearchQuery::BooleanOperator::AND ? Lucene::BooleanClause::MUST : Lucene::BooleanClause::SHOULD); } return booleanQuery; } QueryPtr OcrTextIndexedStrategy::buildSimpleOcrContentsQuery(const SearchQuery &query) { m_keywords.append(query.keyword()); if (query.keyword().isEmpty()) { return newLucene(); // Match nothing for empty keyword } return LuceneQueryUtils::buildNGramSearchQuery( QString::fromWCharArray(LuceneFieldNames::OcrText::kOcrContents), query.keyword()); } void OcrTextIndexedStrategy::processSearchResults(const Lucene::IndexSearcherPtr &searcher, const Lucene::Collection &scoreDocs) { // Measure the time taken to process search results QElapsedTimer resultTimer; resultTimer.start(); auto docsSize = scoreDocs.size(); OcrTextOptionsAPI optAPI(m_options); bool enableHTML = optAPI.isSearchResultHighlightEnabled(); int previewLen = optAPI.maxPreviewLength() > 0 ? optAPI.maxPreviewLength() : 50; bool enableRetrieval = optAPI.isFullTextRetrievalEnabled(); bool detailedResults = m_options.detailedResultsEnabled(); // Build field selector to avoid loading the large 'ocr_contents' field when not needed. // The ocr_contents field stores OCR-recognized text and loading it for every result // (even when only path is needed) causes significant disk I/O overhead. Lucene::Collection fieldsToLoad = Lucene::Collection::newInstance(); if (enableRetrieval) { fieldsToLoad.add(LuceneFieldNames::OcrText::kOcrContents); } fieldsToLoad.add(LuceneFieldNames::OcrText::kPath); if (Q_UNLIKELY(detailedResults)) { fieldsToLoad.add(LuceneFieldNames::OcrText::kFilename); fieldsToLoad.add(LuceneFieldNames::OcrText::kIsHidden); fieldsToLoad.add(LuceneFieldNames::OcrText::kModifyTime); fieldsToLoad.add(LuceneFieldNames::OcrText::kBirthTime); fieldsToLoad.add(LuceneFieldNames::OcrText::kCheckSum); fieldsToLoad.add(LuceneFieldNames::OcrText::kFileSize); } Lucene::FieldSelectorPtr fieldSelector = newLucene(fieldsToLoad); // Pre-allocate to avoid reallocation during append m_results.reserve(m_results.size() + static_cast(docsSize)); for (int32_t i = 0; i < docsSize; ++i) { if (m_cancelledRef && m_cancelledRef->load()) { qInfo() << "OCR text search cancelled"; break; } try { Lucene::ScoreDocPtr scoreDoc = scoreDocs[i]; if (!scoreDoc || scoreDoc->doc < 0) { qWarning() << "Invalid ScoreDoc at index" << i; continue; } Lucene::DocumentPtr doc; try { doc = searcher->doc(scoreDoc->doc, fieldSelector); if (!doc) { qWarning() << "Failed to retrieve document at index:" << scoreDoc->doc; continue; } } catch (const Lucene::LuceneException &e) { qWarning() << "Exception while retrieving document:" << QString::fromStdWString(e.getError()); continue; } catch (const std::exception &e) { qWarning() << "Standard exception while retrieving document:" << e.what(); continue; } // Path filtering, excluded paths, hidden file — handled at query layer Lucene::String pathField = doc->get(LuceneFieldNames::OcrText::kPath); if (pathField.empty()) { qWarning() << "Document missing path field at index:" << scoreDoc->doc; continue; } SearchResult result(QString::fromStdWString(pathField)); // 设置 OCR 内容结果 OcrTextResultAPI resultApi(result); result.setCustomAttribute("plainContentMatch", QString()); result.setCustomAttribute("snippetOffset", -1); // 使用ContentHighlighter命名空间进行高亮 if (enableRetrieval) { try { Lucene::String ocrContentField = doc->get(LuceneFieldNames::OcrText::kOcrContents); if (!ocrContentField.empty()) { const QString content = QString::fromStdWString(ocrContentField); // Fix: keep a dedicated plain-text snippet for CLI verbose output // instead of reusing HTML-oriented highlightedContent. const ContentHighlighter::PlainSnippetResult plainSnippet = ContentHighlighter::plainSnippet( m_keywords, content, previewLen, previewLen); // 设置原始 OCR 内容 resultApi.setOcrContent(content); resultApi.setCharCount(content.size()); // 设置高亮内容 const QString highlightedContent = ContentHighlighter::customHighlight( m_keywords, content, previewLen, enableHTML); resultApi.setHighlightedContent(highlightedContent); result.setCustomAttribute("plainContentMatch", plainSnippet.content); result.setCustomAttribute("snippetOffset", plainSnippet.snippetOffset); } } catch (const Lucene::LuceneException &e) { qWarning() << "Exception retrieving OCR content field:" << QString::fromStdWString(e.getError()); } catch (const std::exception &e) { qWarning() << "Standard exception retrieving OCR content field:" << e.what(); } } // 设置详细结果(如果启用) if (Q_UNLIKELY(detailedResults)) { // 文件名 Lucene::String filenameField = doc->get(LuceneFieldNames::OcrText::kFilename); if (!filenameField.empty()) { resultApi.setFilename(QString::fromStdWString(filenameField)); } // 隐藏状态 Lucene::String hiddenField = doc->get(LuceneFieldNames::OcrText::kIsHidden); if (!hiddenField.empty()) { resultApi.setIsHidden(QString::fromStdWString(hiddenField).toLower() == "y"); } // 修改时间 Lucene::String modifyTimeField = doc->get(LuceneFieldNames::OcrText::kModifyTime); if (!modifyTimeField.empty()) { bool ok = false; qint64 timestamp = QString::fromStdWString(modifyTimeField).toLongLong(&ok); if (ok && timestamp > 0) { resultApi.setModifyTimestamp(timestamp); } } // 创建时间 Lucene::String birthTimeField = doc->get(LuceneFieldNames::OcrText::kBirthTime); if (!birthTimeField.empty()) { bool ok = false; qint64 timestamp = QString::fromStdWString(birthTimeField).toLongLong(&ok); if (ok && timestamp > 0) { resultApi.setBirthTimestamp(timestamp); } } // 文件校验和 Lucene::String checksumField = doc->get(LuceneFieldNames::OcrText::kCheckSum); if (!checksumField.empty()) { resultApi.setChecksum(QString::fromStdWString(checksumField)); } // 文件大小 Lucene::String fileSizeField = doc->get(LuceneFieldNames::OcrText::kFileSize); if (!fileSizeField.empty()) { bool ok = false; qint64 fileSize = QString::fromStdWString(fileSizeField).toLongLong(&ok); if (ok && fileSize > 0) { resultApi.setFileSizeBytes(fileSize); } } } // Add to result collection m_results.append(std::move(result)); // Real-time result emission if (Q_UNLIKELY(m_options.resultFoundEnabled())) emit resultFound(m_results.last()); } catch (const Lucene::LuceneException &e) { qWarning() << "Error processing result:" << QString::fromStdWString(e.getError()); continue; } catch (const std::exception &e) { qWarning() << "Standard exception:" << e.what(); continue; } catch (...) { qWarning() << "Unknown exception during result processing"; continue; } } qInfo() << "OCR text result processing time:" << resultTimer.elapsed() << "ms"; emit searchFinished(m_results); } void OcrTextIndexedStrategy::performOcrTextSearch(const SearchQuery &query) { // 防御性空指针拦截:正常流程 m_cancelledRef 由引擎注入,非空; // 若注入失败(setCancelledFlag 拒绝空指针),此处拦截避免底层 // SearchCancellationGuard / CancellableCollector 解引用 nullptr 导致 SIGSEGV。 if (!m_cancelledRef) { qWarning("OcrTextIndexedStrategy::performOcrTextSearch: cancellation flag is null, aborting search"); emit errorOccurred(SearchError(OcrTextSearchErrorCode::OcrTextIndexException)); emit searchFinished(m_results); return; } // RAII guard: automatically manage cancellation flag lifecycle SearchCancellationGuard guard(m_cancelledRef); try { // Get index directory FSDirectoryPtr directory = FSDirectory::open(m_indexDir.toStdWString()); if (!directory) { qWarning() << "Failed to open OCR text index directory:" << m_indexDir; emit errorOccurred(SearchError(OcrTextSearchErrorCode::OcrTextIndexNotFound)); return; } // Get index reader IndexReaderPtr reader = IndexReader::open(directory, true); if (!reader || reader->numDocs() == 0) { qWarning() << "OCR text index is empty or cannot be opened"; emit errorOccurred(SearchError(OcrTextSearchErrorCode::OcrTextIndexNotFound)); return; } // Create searcher IndexSearcherPtr searcher = newLucene(reader); // Build query m_currentQuery = buildLuceneQuery(query); if (!m_currentQuery) { qWarning() << "Failed to build Lucene query for OCR text search"; emit errorOccurred(SearchError(OcrTextSearchErrorCode::OcrTextIndexException)); return; } // Execute search QElapsedTimer searchTimer; searchTimer.start(); int32_t maxResults = m_options.maxResults() > 0 ? m_options.maxResults() : reader->numDocs(); // Use custom CancellableCollector for interruptible search Collection scoreDocs; try { // Create cancellable collector boost::shared_ptr collector = newLucene(m_cancelledRef, maxResults); // Execute search with custom collector qInfo() << "OCR text search execution start:" << query.keyword(); searcher->search(m_currentQuery, collector); // Get collected documents scoreDocs = collector->getScoreDocs(); qInfo() << "OCR text search execution time:" << searchTimer.elapsed() << "ms" << "Total hits:" << collector->getTotalHits() << "Collected:" << scoreDocs.size() << "Keyword:" << query.keyword() << "Cancelled" << (m_cancelledRef ? m_cancelledRef->load() : false); } catch (const SearchCancelledException &e) { qInfo() << "OCR text search cancelled during execution"; emit searchFinished(m_results); return; } catch (const RuntimeException &e) { #if LUCENE_HAS_SEARCH_CANCELLATION // Check if this is a cancellation exception thrown in ExactPhraseScorer::phraseFreq() QString errorMsg = QString::fromStdWString(e.getError()); if (errorMsg.contains("cancelled", Qt::CaseInsensitive)) { qInfo() << "OCR text search cancelled in phraseFreq():" << errorMsg; emit searchFinished(m_results); return; } #endif // Other runtime exceptions, rethrow throw; } // Process search results processSearchResults(searcher, scoreDocs); } catch (const LuceneException &e) { qWarning() << "Lucene search exception:" << QString::fromStdWString(e.getError()); emit errorOccurred(SearchError(OcrTextSearchErrorCode::OcrTextIndexException)); } catch (const std::exception &e) { qWarning() << "Standard exception:" << e.what(); emit errorOccurred(SearchError(OcrTextSearchErrorCode::OcrTextIndexException)); } } void OcrTextIndexedStrategy::cancel() { if (m_cancelledRef) m_cancelledRef->store(true); } DFM_SEARCH_END_NS indexedstrategy.h000066400000000000000000000036011523477242100354260ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef OCRTEXT_INDEXED_STRATEGY_H #define OCRTEXT_INDEXED_STRATEGY_H #include "basestrategy.h" #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief OCR text index search strategy * * This strategy searches for text extracted from images using OCR technology. * It uses a Lucene index similar to content search, but with simplified logic * (no highlighting support). */ class OcrTextIndexedStrategy : public OcrTextBaseStrategy { Q_OBJECT public: explicit OcrTextIndexedStrategy(const SearchOptions &options, QObject *parent = nullptr); ~OcrTextIndexedStrategy() override; void search(const SearchQuery &query) override; void cancel() override; private: // Initialize index directory void initializeIndexing(); // Perform OCR text search void performOcrTextSearch(const SearchQuery &query); // Build Lucene query Lucene::QueryPtr buildLuceneQuery(const SearchQuery &query); // Helper for simple queries Lucene::QueryPtr buildSimpleOcrContentsQuery(const SearchQuery &query); // Helper for "standard" boolean logic Lucene::QueryPtr buildStandardBooleanOcrContentsQuery(const SearchQuery &query); // Helper for "advanced" mixed AND logic (searches "ocr_contents" and "filename") Lucene::QueryPtr buildAdvancedAndQuery(const SearchQuery &query); // Process search results void processSearchResults(const Lucene::IndexSearcherPtr &searcher, const Lucene::Collection &scoreDocs); QString m_indexDir; Lucene::QueryPtr m_currentQuery; QStringList m_keywords; }; DFM_SEARCH_END_NS #endif // OCRTEXT_INDEXED_STRATEGY_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/recentsearch/000077500000000000000000000000001523477242100261365ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/recentsearch/recentsearchengine.cpp000066400000000000000000000022311523477242100324740ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "recentsearchengine.h" #include "recentstrategies/recentsearchstrategy.h" #include DFM_SEARCH_BEGIN_NS RecentSearchEngine::RecentSearchEngine(QObject *parent) : GenericSearchEngine(parent) { } RecentSearchEngine::~RecentSearchEngine() = default; void RecentSearchEngine::setupStrategyFactory() { auto factory = std::make_unique(); m_worker->setStrategyFactory(std::move(factory)); } SearchError RecentSearchEngine::validateSearchConditions() { // 最近使用搜索不依赖搜索路径(数据来自 DBus),跳过基类的路径校验。 // 也不要求关键词非空——"看过的" 单独使用时应返回全部最近文件。 return SearchError(); } std::unique_ptr RecentSearchStrategyFactory::createStrategy( SearchType searchType, const SearchOptions &options) { if (searchType != SearchType::Recent) { return nullptr; } return std::make_unique(options); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/recentsearch/recentsearchengine.h000066400000000000000000000024731523477242100321510ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef RECENTSEARCHENGINE_H #define RECENTSEARCHENGINE_H #include "core/genericsearchengine.h" DFM_SEARCH_BEGIN_NS /** * @brief 搜索引擎:最近使用文件(DBus RecentManager 数据源) * * 数据源为 org.deepin.Filemanager.Daemon.RecentManager.GetItemsInfo, * 返回 JSON 数组,每条含 Href/Path/modified。 * * 该引擎不复用 lucene++ 索引——最近使用记录不在索引中, * 因此所有 filename/content 搜索路径都无法触及此数据。 */ class RecentSearchEngine : public GenericSearchEngine { Q_OBJECT public: explicit RecentSearchEngine(QObject *parent = nullptr); ~RecentSearchEngine() override; SearchType searchType() const override { return SearchType::Recent; } protected: void setupStrategyFactory() override; SearchError validateSearchConditions() override; }; /** * @brief 策略工厂:始终创建 RecentSearchStrategy(无 indexed/realtime 分支) */ class RecentSearchStrategyFactory : public SearchStrategyFactory { public: std::unique_ptr createStrategy( SearchType searchType, const SearchOptions &options) override; }; DFM_SEARCH_END_NS #endif // RECENTSEARCHENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/recentsearch/recentstrategies/000077500000000000000000000000001523477242100315115ustar00rootroot00000000000000recentsearchstrategy.cpp000066400000000000000000000221711523477242100363720ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/recentsearch/recentstrategies// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "recentsearchstrategy.h" #include #include #include #include #include #include #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS namespace { constexpr auto kDBusService = "org.deepin.Filemanager.Daemon"; constexpr auto kDBusPath = "/org/deepin/Filemanager/Daemon/RecentManager"; constexpr auto kDBusInterface = "org.deepin.Filemanager.Daemon.RecentManager"; constexpr auto kDBusMethod = "GetItemsInfo"; } // namespace RecentSearchStrategy::RecentSearchStrategy(const SearchOptions &options, QObject *parent) : BaseSearchStrategy(options, parent) { } // ── DBus 获取 + JSON 解析 ────────────────────────────────────────── QList RecentSearchStrategy::fetchRecentItems() { QList items; QDBusInterface iface(QString::fromLatin1(kDBusService), QString::fromLatin1(kDBusPath), QString::fromLatin1(kDBusInterface), QDBusConnection::sessionBus()); if (!iface.isValid()) { qWarning() << "RecentManager DBus interface is invalid:" << iface.lastError().message(); return items; } QDBusPendingReply reply = iface.call(QString::fromLatin1(kDBusMethod)); if (!reply.isValid()) { qWarning() << "RecentManager GetItemsInfo failed:" << reply.error().message(); return items; } const QVariantList &topLevelList = reply.value(); for (const auto &v : topLevelList) { QDBusArgument dbusArg = v.value(); QVariantMap map; dbusArg >> map; // 直接将QDBusArgument解包为QVariantMap if (!map.isEmpty()) { RecentItem item; item.href = map.value("Href").toString(); item.path = map.value("Path").toString(); item.modified = static_cast(map.value("modified").toLongLong()); if (!item.path.isEmpty() && item.modified > 0) { items.append(std::move(item)); } } } return items; } // ── 过滤管道 ─────────────────────────────────────────────────────── // // 三个 filterBy* 纯函数:输入 items + 约束 → 输出子集。 // 行为与 FileNameRealTimeStrategy 中的对应过滤逻辑保持一致, // 确保语义搜索在不同数据源下产生可预期的匹配结果。 QList RecentSearchStrategy::filterByKeyword(const QList &items, const QString &keyword) const { if (keyword.isEmpty()) { return items; } const bool caseSensitive = m_options.caseSensitive(); const Qt::CaseSensitivity cs = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; QList out; for (const RecentItem &item : items) { const QString fileName = QFileInfo(item.path).fileName(); if (fileName.contains(keyword, cs)) { out.append(item); } } return out; } QList RecentSearchStrategy::filterByExtensions(const QList &items, const QStringList &extensions) const { if (extensions.isEmpty()) { return items; } QList out; for (const RecentItem &item : items) { const QString suffix = QFileInfo(item.path).suffix().toLower(); if (extensions.contains(suffix)) { out.append(item); } } return out; } QList RecentSearchStrategy::filterByTimeRange(const QList &items) const { const TimeRangeFilter filter = m_options.timeRangeFilter(); auto [start, end] = filter.resolveTimeRange(); QList out; for (const RecentItem &item : items) { const QDateTime fileTime = QDateTime::fromSecsSinceEpoch(item.modified); bool match = true; if (start.isValid()) { match = filter.includeLower() ? (fileTime >= start) : (fileTime > start); } if (match && end.isValid()) { match = filter.includeUpper() ? (fileTime <= end) : (fileTime < end); } if (match) { out.append(item); } } return out; } QList RecentSearchStrategy::filterByExcludedPaths(const QList &items) const { QStringList excludedPaths = m_options.searchExcludedPaths(); if (excludedPaths.isEmpty()) return items; // 标准化路径:去除末尾斜杠、解析 "."/"..",确保路径边界匹配可靠。 for (auto &p : excludedPaths) p = QDir::cleanPath(p); QList out; for (const RecentItem &item : std::as_const(items)) { const bool excluded = std::any_of(excludedPaths.cbegin(), excludedPaths.cend(), [&item](const QString &excludedPath) { // 路径边界匹配:仅排除 excludedPath 自身或其子路径, // 避免 /home/uos/Downloads 误匹配 /home/uos/Downloads_backup。 return item.path == excludedPath || item.path.startsWith(excludedPath + QLatin1Char('/')); }); if (!excluded) { out.append(item); } } return out; } // ── 结果构建 ─────────────────────────────────────────────────────── SearchResult RecentSearchStrategy::toSearchResult(const RecentItem &item) const { SearchResult result(item.path); QFileInfo fi(item.path); if (m_options.detailedResultsEnabled()) { FileNameResultAPI api(result); api.setIsDirectory(fi.isDir()); if (!fi.isDir()) { api.setFileType(fi.suffix().isEmpty() ? QStringLiteral("unknown") : fi.suffix().toLower()); api.setFileExtension(fi.suffix().toLower()); api.setSize(QString::number(fi.size())); api.setFileSizeBytes(fi.size()); } else { api.setFileType(QStringLiteral("dir")); } api.setFilename(fi.fileName()); api.setIsHidden(fi.isHidden()); api.setModifyTimestamp(item.modified); // 最近使用时间,非文件系统 mtime } return result; } // ── 主搜索流程 ───────────────────────────────────────────────────── void RecentSearchStrategy::search(const SearchQuery &query) { m_results.clear(); QElapsedTimer timer; timer.start(); // Step 1: 从 DBus 拉取全部最近使用记录 QList items = fetchRecentItems(); if (m_cancelledRef && m_cancelledRef->load()) { emit searchFinished(m_results); return; } // Step 2: 黑名单路径过滤(尽早排除黑名单目录下的条目,减少后续过滤工作量) items = filterByExcludedPaths(items); // Step 3: 关键词过滤(仅 Simple 类型有 keyword;Boolean/Wildcard 暂不支持) QString keyword; if (query.type() == SearchQuery::Type::Simple) { keyword = query.keyword(); } else if (query.type() == SearchQuery::Type::Boolean && !query.subQueries().isEmpty()) { // 布尔查询取第一个子查询作为关键词(最近记录量小,不做全文布尔) keyword = query.subQueries().first().keyword(); } items = filterByKeyword(items, keyword); // Step 4: 扩展名过滤 FileNameOptionsAPI optApi(const_cast(m_options)); const QStringList exts = optApi.fileExtensions(); items = filterByExtensions(items, exts); // Step 5: 时间范围过滤(基于 modified 时间戳) if (m_options.hasTimeRangeFilter()) { items = filterByTimeRange(items); } // Step 6: 构建 SearchResult 并发射 const bool resultFoundEnabled = m_options.resultFoundEnabled(); const int maxResults = m_options.maxResults() > 0 ? m_options.maxResults() : INT_MAX; int count = 0; for (const RecentItem &item : std::as_const(items)) { if ((m_cancelledRef && m_cancelledRef->load()) || count >= maxResults) { break; } SearchResult result = toSearchResult(item); m_results.append(result); if (resultFoundEnabled) { emit resultFound(result); } ++count; } qInfo() << "Recent search completed in" << timer.elapsed() << "ms with" << count << "results"; emit searchFinished(m_results); } void RecentSearchStrategy::cancel() { if (m_cancelledRef) m_cancelledRef->store(true); } DFM_SEARCH_END_NS recentsearchstrategy.h000066400000000000000000000067171523477242100360470ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/recentsearch/recentstrategies// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef RECENTSEARCHSTRATEGY_H #define RECENTSEARCHSTRATEGY_H #include "core/searchstrategy/basesearchstrategy.h" #include #include DFM_SEARCH_BEGIN_NS struct RecentItem { QString path; // 本地路径 (DBus "Path" 字段) QString href; // file:// URI (DBus "Href" 字段) qint64 modified = 0; // 最近访问时间戳,秒级 Unix epoch }; /** * @brief 搜索策略:从 DBus RecentManager 获取最近使用文件并过滤。 * * 数据流: * 1. 调用 org.deepin.Filemanager.Daemon.RecentManager.GetItemsInfo * 2. 解析返回的 JSON 数组 → RecentItem 列表 * 3. 按 SearchQuery 关键词、SearchOptions 中的 fileExtensions + TimeRangeFilter 过滤 * 4. 发射 resultFound / searchFinished * * 时间过滤使用 RecentItem.modified 字段(最近访问时间), * 而非文件系统的 lastModified——因为"最近使用"的语义是"上次打开时间"。 */ class RecentSearchStrategy : public BaseSearchStrategy { Q_OBJECT public: explicit RecentSearchStrategy(const SearchOptions &options, QObject *parent = nullptr); ~RecentSearchStrategy() override = default; SearchType searchType() const override { return SearchType::Recent; } void search(const SearchQuery &query) override; void cancel() override; protected: // 调用 DBus 获取最近使用记录;失败时返回空列表并记录 warning。 // 访问权限为 protected 以便测试子类通过 VADDR 宏取到成员函数指针进行打桩。 QList fetchRecentItems(); private: // ── 过滤管道(每个阶段纯函数,便于测试与组合) ── /** * @brief 按关键词过滤文件名。 * * 关键词匹配 fileName() 而非完整 path——用户说"打开过的报告"时, * "报告"应匹配文件名,不应匹配路径中的目录名。 * 空关键词时不过滤。 */ QList filterByKeyword(const QList &items, const QString &keyword) const; /** * @brief 按扩展名过滤。 * SearchOptions.fileExtensions 为小写后缀列表(如 ["pdf", "docx"])。 * 空列表时不过滤。 */ QList filterByExtensions(const QList &items, const QStringList &extensions) const; /** * @brief 按 TimeRangeFilter 过滤 modified 时间戳。 * * 注意:TimeRangeFilter.resolveTimeRange() 返回 QDateTime 对比, * 但 RecentItem.modified 是秒级 epoch。需转 QDateTime 比较。 * start/end 无效时不约束该侧。 */ QList filterByTimeRange(const QList &items) const; /** * @brief 按黑名单路径过滤。 * * 排除 path 等于或位于黑名单目录下的最近使用项, * 采用路径边界匹配(excludedPath 或 excludedPath + "/"), * 避免 /home/uos/Downloads 误匹配 /home/uos/Downloads_backup。 * 与 FileNameRealTimeStrategy 的排除语义保持一致。 * 空列表时不过滤。 */ QList filterByExcludedPaths(const QList &items) const; /** * @brief 将 RecentItem 转换为 SearchResult,填充详细属性。 */ SearchResult toSearchResult(const RecentItem &item) const; }; DFM_SEARCH_END_NS #endif // RECENTSEARCHSTRATEGY_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/000077500000000000000000000000001523477242100252735ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/000077500000000000000000000000001523477242100274715ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.cpp000066400000000000000000000142561523477242100334160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "actionextractor.h" #include "semantic/semanticruleengine.h" #include "locationextractor.h" #include #include #include #include DFM_SEARCH_BEGIN_NS ActionExtractor::ActionExtractor(SemanticRuleEngine *engine) : m_engine(engine) { } ActionExtractor::~ActionExtractor() = default; void ActionExtractor::extract(const QString &input, ParsedIntent &intent) { if (!m_engine->hasGroup("action")) { return; } // Use matchAll to support co-existing action dimensions // (e.g., "别人传的修改过的文档" = IM received + modify time). QStringList ruleIds; const QList matches = m_engine->matchAll("action", input, &ruleIds); for (int i = 0; i < matches.size(); ++i) { const QRegularExpressionMatch &m = matches[i]; const QVariantMap metadata = m_engine->ruleMetadata("action", ruleIds[i]); const QString timeFieldStr = metadata.value("time_field").toString(); const QString imCategory = metadata.value("im_category").toString(); const bool includeHidden = metadata.value("include_hidden", false).toBool(); const bool hiddenOnly = metadata.value("hidden_only", false).toBool(); const bool recent = metadata.value("recent", false).toBool(); // ── Recently-used files (DBus RecentManager data source) ── if (recent) { intent.setRecentOnly(true); // Force ModifyTime — recent records only carry a "modified" timestamp intent.timeConstraint().setTimeField(TimeField::ModifyTime); MatchSpan span; span.setStart(m.capturedStart()); span.setEnd(m.capturedEnd()); span.setRuleId(ruleIds[i]); intent.consumedSpans().append(span); continue; } // ── Hidden-only files (filename search only, include hidden directories) ── if (hiddenOnly) { intent.setHiddenOnly(true); intent.setIncludeHidden(true); intent.setSearchTarget(SearchTarget::FileNameOnly); MatchSpan span; span.setStart(m.capturedStart()); span.setEnd(m.capturedEnd()); span.setRuleId(ruleIds[i]); intent.consumedSpans().append(span); continue; } // ── Include hidden files ── if (includeHidden) { intent.setIncludeHidden(true); MatchSpan span; span.setStart(m.capturedStart()); span.setEnd(m.capturedEnd()); span.setRuleId(ruleIds[i]); intent.consumedSpans().append(span); continue; } // ── IM category actions (e.g., received → IM directories) ── if (imCategory == QLatin1String("received")) { const QStringList resolvedPaths = resolveImReceivedPaths(); if (!resolvedPaths.isEmpty()) { for (const QString &path : resolvedPaths) { if (!intent.searchDirectories().contains(path)) { intent.searchDirectories().append(path); } } MatchSpan span; span.setStart(m.capturedStart()); span.setEnd(m.capturedEnd()); span.setRuleId(ruleIds[i]); intent.consumedSpans().append(span); } // else: no IM directories resolved → do NOT consume span // (trigger words demote to keyword fallback) continue; } // ── Existing time_field actions ── if (timeFieldStr == QLatin1String("birth")) { intent.timeConstraint().setTimeField(TimeField::BirthTime); } else if (timeFieldStr == QLatin1String("modify")) { intent.timeConstraint().setTimeField(TimeField::ModifyTime); } else { qWarning() << "Unknown action metadata in rule" << ruleIds[i] << ":" << metadata; continue; } MatchSpan span; span.setStart(m.capturedStart()); span.setEnd(m.capturedEnd()); span.setRuleId(ruleIds[i]); intent.consumedSpans().append(span); } } QStringList ActionExtractor::resolveImReceivedPaths() const { QStringList paths; if (!m_engine->hasGroup("location")) { return paths; } const QStringList locRuleIds = m_engine->ruleIds("location"); for (const QString &rid : locRuleIds) { const QVariantMap meta = m_engine->ruleMetadata("location", rid); if (!meta.value("im_received").toBool()) { continue; } const QString xdgType = meta.value("xdg_type").toString(); const QVariant customPathVar = meta.value("custom_path"); QStringList resolved; if (customPathVar.userType() == QMetaType::QVariantList) { for (const QVariant &pathVar : customPathVar.toList()) { const QString relPath = pathVar.toString(); if (relPath.isEmpty()) continue; const QStringList expanded = LocationExtractor::expandCustomPath( QDir::homePath(), relPath); for (const QString &p : expanded) { if (!resolved.contains(p)) resolved.append(p); } } } else { const QString customSubdir = customPathVar.toString(); if (!customSubdir.isEmpty()) { resolved = LocationExtractor::expandCustomPath( QDir::homePath(), customSubdir); } else if (!xdgType.isEmpty()) { const QString path = LocationExtractor::resolveXdgPath(xdgType); if (!path.isEmpty()) { resolved = { path }; } } } for (const QString &p : resolved) { if (QFileInfo::exists(p) && !paths.contains(p)) { paths.append(p); } } } return paths; } QString ActionExtractor::name() const { return QStringLiteral("action"); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.h000066400000000000000000000012341523477242100330530ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef ACTIONEXTRACTOR_H #define ACTIONEXTRACTOR_H #include DFM_SEARCH_BEGIN_NS class SemanticRuleEngine; class ActionExtractor : public DimensionExtractor { public: explicit ActionExtractor(SemanticRuleEngine *engine); ~ActionExtractor() override; void extract(const QString &input, ParsedIntent &intent) override; QString name() const override; private: SemanticRuleEngine *m_engine; QStringList resolveImReceivedPaths() const; }; DFM_SEARCH_END_NS #endif // ACTIONEXTRACTOR_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/filetypeextractor.cpp000066400000000000000000000050221523477242100337510ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "filetypeextractor.h" #include "semantic/semanticruleengine.h" DFM_SEARCH_BEGIN_NS FileTypeExtractor::FileTypeExtractor(SemanticRuleEngine *engine) : m_engine(engine) { } FileTypeExtractor::~FileTypeExtractor() = default; void FileTypeExtractor::extract(const QString &input, ParsedIntent &intent) { if (!m_engine->hasGroup("filetype")) { return; } QStringList ruleIds; const QList matches = m_engine->matchAll("filetype", input, &ruleIds); QSet seenExtensions; for (int i = 0; i < matches.size(); ++i) { const QRegularExpressionMatch &m = matches[i]; const QVariantMap metadata = m_engine->ruleMetadata("filetype", ruleIds[i]); // Skip a filetype match whose span is fully contained within an // already-consumed span (e.g. "视频" consumed by the location rule // "视频目录"). This resolves the location-vs-filetype ambiguity where // the same word ("视频", "音乐", "文档", "图片") triggers both a // location directory and a file type: the location interpretation wins, // and a distinct filetype word elsewhere in the query survives. bool contained = false; for (const MatchSpan &existing : intent.consumedSpans()) { if (m.capturedStart() >= existing.start() && m.capturedEnd() <= existing.end()) { contained = true; break; } } if (contained) { continue; } MatchSpan span; span.setStart(m.capturedStart()); span.setEnd(m.capturedEnd()); span.setRuleId(ruleIds[i]); intent.consumedSpans().append(span); const QStringList extensions = metadata.value("extensions").toStringList(); const bool isGeneral = metadata.value("general", false).toBool(); // If this is a general/fallback type but we already have specific extensions, // skip to avoid over-specificity dilution if (isGeneral && !seenExtensions.isEmpty()) { continue; } for (const QString &ext : extensions) { if (!seenExtensions.contains(ext)) { seenExtensions.insert(ext); } } } intent.setFileExtensions(seenExtensions.values()); } QString FileTypeExtractor::name() const { return QStringLiteral("filetype"); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/filetypeextractor.h000066400000000000000000000011711523477242100334170ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef FILETYPEEXTRACTOR_H #define FILETYPEEXTRACTOR_H #include DFM_SEARCH_BEGIN_NS class SemanticRuleEngine; class FileTypeExtractor : public DimensionExtractor { public: explicit FileTypeExtractor(SemanticRuleEngine *engine); ~FileTypeExtractor() override; void extract(const QString &input, ParsedIntent &intent) override; QString name() const override; private: SemanticRuleEngine *m_engine; }; DFM_SEARCH_END_NS #endif // FILETYPEEXTRACTOR_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/keywordextractor.cpp000066400000000000000000000140061523477242100336160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "keywordextractor.h" #include "semantic/semanticruleengine.h" #include DFM_SEARCH_BEGIN_NS KeywordExtractor::KeywordExtractor(SemanticRuleEngine *engine) : m_engine(engine) { } KeywordExtractor::~KeywordExtractor() = default; void KeywordExtractor::extract(const QString &input, ParsedIntent &intent) { // Strategy 1: structured keyword patterns (e.g., "contains X and Y") if (extractStructuredKeywords(input, intent)) { return; } // Strategy 2: extract unconsumed text regions extractUnconsumedText(input, intent); } bool KeywordExtractor::extractStructuredKeywords(const QString &input, ParsedIntent &intent) { if (!m_engine->hasGroup("keyword")) { return false; } QString ruleId; QRegularExpressionMatch match; if (!m_engine->match("keyword", input, match, &ruleId)) { return false; } const QVariantMap metadata = m_engine->ruleMetadata("keyword", ruleId); const int captureGroup = metadata.value("capture_group", 1).toInt(); if (captureGroup <= 0 || captureGroup > match.lastCapturedIndex()) { return false; } QString captured = match.captured(captureGroup).trimmed(); if (captured.isEmpty()) { return false; } const bool multiKeyword = metadata.value("multi_keyword", false).toBool(); if (multiKeyword) { intent.setKeywords(splitMultiKeywords(captured, metadata)); } else { intent.setKeywords({ captured }); } // Determine search target from rule metadata const QString targetStr = metadata.value("search_target").toString(); if (targetStr == "filename") { intent.setSearchTarget(SearchTarget::FileNameOnly); } else if (targetStr == "content") { intent.setSearchTarget(SearchTarget::ContentOnly); } // "all" or empty → keep default SearchTarget::All // Mark the entire matched region as consumed MatchSpan span; span.setStart(match.capturedStart()); span.setEnd(match.capturedEnd()); span.setRuleId(ruleId); intent.consumedSpans().append(span); return true; } void KeywordExtractor::extractUnconsumedText(const QString &input, ParsedIntent &intent) { QList allSpans = intent.consumedSpans(); // Also consume noise words if (m_engine->hasGroup("noise")) { QStringList noiseRuleIds; const QList noiseMatches = m_engine->matchAll("noise", input, &noiseRuleIds); for (int i = 0; i < noiseMatches.size(); ++i) { MatchSpan span; span.setStart(noiseMatches[i].capturedStart()); span.setEnd(noiseMatches[i].capturedEnd()); span.setRuleId(noiseRuleIds[i]); allSpans.append(span); } } // Extract text not covered by any consumed span const QString unconsumed = extractUnconsumedRegions(input, allSpans); if (unconsumed.isEmpty()) { return; } // Clean up punctuation and particles using pattern from rule metadata // Default: strip whitespace const QString cleanupPattern = QStringLiteral("[\\s]+"); QRegularExpression cleanupRe(cleanupPattern); // Try to get a more specific cleanup pattern from keyword rules // Load from ALL rules in the group (not just matching ones), // since cleanup_pattern is a configuration property, not a per-match property. if (m_engine->hasGroup("keyword")) { const QStringList allRuleIds = m_engine->ruleIds("keyword"); for (const QString &rid : allRuleIds) { const QVariantMap meta = m_engine->ruleMetadata("keyword", rid); const QString pattern = meta.value("cleanup_pattern").toString(); if (!pattern.isEmpty()) { cleanupRe.setPattern(pattern); break; } } } const QString cleaned = unconsumed.trimmed() .replace(cleanupRe, " ") .simplified(); if (cleaned.isEmpty()) { return; } intent.setKeywords({ cleaned }); } QString KeywordExtractor::extractUnconsumedRegions(const QString &input, const QList &allSpans) const { if (input.isEmpty()) { return {}; } // Build a set of consumed character positions QVector consumed(input.size(), false); for (const MatchSpan &span : allSpans) { if (span.isValid() && span.end() <= input.size()) { for (int i = span.start(); i < span.end(); ++i) { consumed[i] = true; } } } // Extract unconsumed regions QString result; int regionStart = -1; for (int i = 0; i < input.size(); ++i) { if (!consumed[i]) { if (regionStart < 0) { regionStart = i; } } else { if (regionStart >= 0) { result += input.mid(regionStart, i - regionStart) + " "; regionStart = -1; } } } // Trailing region if (regionStart >= 0) { result += input.mid(regionStart); } return result.trimmed(); } QStringList KeywordExtractor::splitMultiKeywords(const QString &text, const QVariantMap &metadata) { // Default split on comma QString splitPattern = QStringLiteral("[,]+"); // Try to get language-specific split pattern from metadata const QString metaSplit = metadata.value("split_pattern").toString(); if (!metaSplit.isEmpty()) { splitPattern = metaSplit; } QRegularExpression splitRe(splitPattern); const QStringList parts = text.split(splitRe, Qt::SkipEmptyParts); QStringList result; for (const QString &part : parts) { const QString trimmed = part.trimmed(); if (!trimmed.isEmpty()) { result.append(trimmed); } } return result; } QString KeywordExtractor::name() const { return QStringLiteral("keyword"); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/keywordextractor.h000066400000000000000000000017211523477242100332630ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef KEYWORDEXTRACTOR_H #define KEYWORDEXTRACTOR_H #include DFM_SEARCH_BEGIN_NS class SemanticRuleEngine; class KeywordExtractor : public DimensionExtractor { public: explicit KeywordExtractor(SemanticRuleEngine *engine); ~KeywordExtractor() override; void extract(const QString &input, ParsedIntent &intent) override; QString name() const override; private: bool extractStructuredKeywords(const QString &input, ParsedIntent &intent); void extractUnconsumedText(const QString &input, ParsedIntent &intent); QString extractUnconsumedRegions(const QString &input, const QList &allSpans) const; static QStringList splitMultiKeywords(const QString &text, const QVariantMap &metadata); SemanticRuleEngine *m_engine; }; DFM_SEARCH_END_NS #endif // KEYWORDEXTRACTOR_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.cpp000066400000000000000000000137531523477242100337520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "locationextractor.h" #include "semantic/semanticruleengine.h" #include #include #include #include DFM_SEARCH_BEGIN_NS LocationExtractor::LocationExtractor(SemanticRuleEngine *engine) : m_engine(engine) { } LocationExtractor::~LocationExtractor() = default; void LocationExtractor::extract(const QString &input, ParsedIntent &intent) { if (!m_engine->hasGroup("location")) { return; } // Use matchAll to support multiple directory mentions (e.g., "桌面和下载的图片") QStringList ruleIds; const QList matches = m_engine->matchAll("location", input, &ruleIds); for (int i = 0; i < matches.size(); ++i) { const QRegularExpressionMatch &m = matches[i]; const QVariantMap metadata = m_engine->ruleMetadata("location", ruleIds[i]); const QString xdgType = metadata.value("xdg_type").toString(); const QVariant customPathVar = metadata.value("custom_path"); const bool includeHidden = metadata.value("include_hidden", false).toBool(); // Always record the consumed span when a rule matches, // regardless of whether the resolved directory exists on this machine. // This ensures KeywordExtractor correctly excludes trigger words. MatchSpan span; span.setStart(m.capturedStart()); span.setEnd(m.capturedEnd()); span.setRuleId(ruleIds[i]); intent.consumedSpans().append(span); QStringList resolvedPaths; if (customPathVar.userType() == QMetaType::QVariantList) { for (const QVariant &pathVar : customPathVar.toList()) { const QString relPath = pathVar.toString(); if (relPath.isEmpty()) continue; const QStringList expanded = expandCustomPath(QDir::homePath(), relPath); for (const QString &p : expanded) { if (!resolvedPaths.contains(p)) resolvedPaths.append(p); } } } else { const QString customSubdir = customPathVar.toString(); if (!customSubdir.isEmpty()) { resolvedPaths = expandCustomPath(QDir::homePath(), customSubdir); } else { const QString path = resolveXdgPath(xdgType); if (!path.isEmpty()) { resolvedPaths = { path }; } } } if (resolvedPaths.isEmpty()) { continue; } for (const QString &path : resolvedPaths) { if (!QFileInfo::exists(path)) { continue; } if (!intent.searchDirectories().contains(path)) { intent.searchDirectories().append(path); } } if (includeHidden) { intent.setIncludeHidden(true); } } } QStringList LocationExtractor::expandCustomPath(const QString &basePath, const QString &relPath) { // Reject path traversal segments to prevent escaping basePath const QStringList segments = relPath.split(QLatin1String("/"), Qt::SkipEmptyParts); if (segments.contains(QLatin1String("..")) || segments.contains(QLatin1String("."))) { qWarning() << "Path traversal rejected in custom_path:" << relPath; return {}; } const QString normalizedBase = QDir::cleanPath(basePath); QStringList candidates = { normalizedBase }; for (const QString &seg : segments) { const bool isGlob = seg.contains(QLatin1Char('*')) || seg.contains(QLatin1Char('?')); if (isGlob) { QStringList next; for (const QString &base : candidates) { QDir dir(base); const QStringList matches = dir.entryList( QStringList { seg }, QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); for (const QString &match : matches) { next.append(base + QLatin1String("/") + match); } } candidates = next; } else { for (QString &c : candidates) { c += QLatin1String("/") + seg; } } } // Final guard: verify all results stay within basePath const QStringList valid = [&]() { QStringList result; for (const QString &p : candidates) { const QString cleaned = QDir::cleanPath(p); if (cleaned.startsWith(normalizedBase + QLatin1String("/")) || cleaned == normalizedBase) { result.append(cleaned); } else { qWarning() << "Path escaped basePath:" << cleaned << "base:" << normalizedBase; } } return result; }(); return valid; } QString LocationExtractor::resolveXdgPath(const QString &xdgType) { if (xdgType == QLatin1String("desktop")) { return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); } if (xdgType == QLatin1String("download")) { return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); } if (xdgType == QLatin1String("documents")) { return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); } if (xdgType == QLatin1String("pictures")) { return QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); } if (xdgType == QLatin1String("music")) { return QStandardPaths::writableLocation(QStandardPaths::MusicLocation); } if (xdgType == QLatin1String("movies")) { return QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); } if (xdgType == QLatin1String("trash")) { return QDir::homePath() + QLatin1String("/.local/share/Trash/files"); } return {}; } QString LocationExtractor::name() const { return QStringLiteral("location"); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.h000066400000000000000000000032141523477242100334060ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef LOCATIONEXTRACTOR_H #define LOCATIONEXTRACTOR_H #include DFM_SEARCH_BEGIN_NS class SemanticRuleEngine; class LocationExtractor : public DimensionExtractor { public: explicit LocationExtractor(SemanticRuleEngine *engine); ~LocationExtractor() override; void extract(const QString &input, ParsedIntent &intent) override; QString name() const override; /** * @brief Resolve an XDG type string to an absolute filesystem path. * @param xdgType One of: "desktop", "download", "documents", "pictures", * "music", "movies", "trash" * @return The resolved absolute path, or empty string if unknown * * @note For non-XDG paths (e.g., WeChat), use metadata "custom_path" * instead, which resolves to ~/. */ static QString resolveXdgPath(const QString &xdgType); /** * @brief Expand a custom_path (may contain glob wildcards) to concrete absolute paths. * @param basePath The base directory (e.g., home path) * @param relPath The relative path, may contain * or ? wildcards * @return List of absolute paths. Empty if glob matches nothing. * * Walk relPath segment-by-segment: non-glob segments are appended directly, * glob segments are expanded by enumerating the parent directory. */ static QStringList expandCustomPath(const QString &basePath, const QString &relPath); private: SemanticRuleEngine *m_engine; }; DFM_SEARCH_END_NS #endif // LOCATIONEXTRACTOR_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/sizeextractor.cpp000066400000000000000000000111141523477242100331010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "sizeextractor.h" #include "semantic/semanticruleengine.h" #include DFM_SEARCH_BEGIN_NS SizeExtractor::SizeExtractor(SemanticRuleEngine *engine) : m_engine(engine) { } SizeExtractor::~SizeExtractor() = default; void SizeExtractor::extract(const QString &input, ParsedIntent &intent) { if (!m_engine->hasGroup("size")) { return; } QString ruleId; QRegularExpressionMatch match; if (!m_engine->match("size", input, match, &ruleId)) { return; } // Size extraction runs after time/filetype. If this size span is already // fully consumed by an earlier, higher-confidence dimension such as a // year-month time expression ("2025-5"), ignore it to avoid overwriting // the intent with a bogus size constraint. for (const MatchSpan &existing : intent.consumedSpans()) { if (match.capturedStart() >= existing.start() && match.capturedEnd() <= existing.end()) { return; } } const QVariantMap metadata = m_engine->ruleMetadata("size", ruleId); const QString typeStr = metadata.value("type").toString(); SizeConstraint sc; if (typeStr == "preset") { sc.setMinSize(metadata.value("min_bytes", 0).toLongLong()); sc.setMaxSize(metadata.value("max_bytes", 0).toLongLong()); if (metadata.contains("include_upper")) { sc.setIncludeUpper(metadata.value("include_upper").toBool()); } if (metadata.contains("include_lower")) { sc.setIncludeLower(metadata.value("include_lower").toBool()); } } else if (typeStr == "dynamic") { const QString direction = metadata.value("direction").toString(); if (direction == "min") { const QString value = match.captured("value"); const QString unit = normalizeUnit(match.captured("unit"), metadata); qint64 bytes = parseSizeToBytes(value, unit); if (bytes <= 0) { return; } sc.setMinSize(bytes); sc.setIncludeLower(true); } else if (direction == "max") { const QString value = match.captured("value"); const QString unit = normalizeUnit(match.captured("unit"), metadata); qint64 bytes = parseSizeToBytes(value, unit); if (bytes <= 0) { return; } sc.setMaxSize(bytes); sc.setIncludeUpper(true); } else if (direction == "range") { const QString minVal = match.captured("min_val"); const QString minUnit = normalizeUnit(match.captured("min_unit"), metadata); const QString maxVal = match.captured("max_val"); const QString maxUnit = normalizeUnit(match.captured("max_unit"), metadata); qint64 minBytes = parseSizeToBytes(minVal, minUnit); qint64 maxBytes = parseSizeToBytes(maxVal, maxUnit); if (minBytes <= 0 || maxBytes <= 0) { return; } sc.setMinSize(minBytes); sc.setMaxSize(maxBytes); } } if (sc.isValid()) { intent.setSizeConstraint(sc); MatchSpan span; span.setStart(match.capturedStart()); span.setEnd(match.capturedEnd()); span.setRuleId(ruleId); intent.consumedSpans().append(span); } } QString SizeExtractor::normalizeUnit(const QString &rawUnit, const QVariantMap &metadata) { if (rawUnit.isEmpty()) { return {}; } const QVariantMap unitMap = metadata.value("unit_map").toMap(); if (!unitMap.isEmpty()) { const QString mapped = unitMap.value(rawUnit).toString(); if (!mapped.isEmpty()) { return mapped; } } return rawUnit; } qint64 SizeExtractor::parseSizeToBytes(const QString &value, const QString &unit) { bool ok = false; double num = value.toDouble(&ok); if (!ok || num <= 0) { return -1; } const QString u = unit.toUpper(); if (u.isEmpty() || u == "B" || u == "BB") { return static_cast(num); } if (u == "K" || u == "KB") { return static_cast(num * 1024); } if (u == "M" || u == "MB") { return static_cast(num * 1024 * 1024); } if (u == "G" || u == "GB") { return static_cast(num * 1024 * 1024 * 1024); } qWarning() << "Unknown size unit:" << unit; return -1; } QString SizeExtractor::name() const { return QStringLiteral("size"); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/sizeextractor.h000066400000000000000000000014051523477242100325500ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SIZEEXTRACTOR_H #define SIZEEXTRACTOR_H #include DFM_SEARCH_BEGIN_NS class SemanticRuleEngine; class SizeExtractor : public DimensionExtractor { public: explicit SizeExtractor(SemanticRuleEngine *engine); ~SizeExtractor() override; void extract(const QString &input, ParsedIntent &intent) override; QString name() const override; private: static qint64 parseSizeToBytes(const QString &value, const QString &unit); static QString normalizeUnit(const QString &rawUnit, const QVariantMap &metadata); SemanticRuleEngine *m_engine; }; DFM_SEARCH_END_NS #endif // SIZEEXTRACTOR_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/targetextractor.cpp000066400000000000000000000017141523477242100334220ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "targetextractor.h" #include "semantic/semanticruleengine.h" DFM_SEARCH_BEGIN_NS TargetExtractor::TargetExtractor(SemanticRuleEngine *engine) : m_engine(engine) { } TargetExtractor::~TargetExtractor() = default; void TargetExtractor::extract(const QString &input, ParsedIntent &intent) { if (!m_engine->hasGroup("target")) { return; } QStringList ruleIds; const QList matches = m_engine->matchAll("target", input, &ruleIds); for (int i = 0; i < matches.size(); ++i) { MatchSpan span; span.setStart(matches[i].capturedStart()); span.setEnd(matches[i].capturedEnd()); span.setRuleId(ruleIds[i]); intent.consumedSpans().append(span); } } QString TargetExtractor::name() const { return QStringLiteral("target"); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/targetextractor.h000066400000000000000000000011551523477242100330660ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TARGETEXTRACTOR_H #define TARGETEXTRACTOR_H #include DFM_SEARCH_BEGIN_NS class SemanticRuleEngine; class TargetExtractor : public DimensionExtractor { public: explicit TargetExtractor(SemanticRuleEngine *engine); ~TargetExtractor() override; void extract(const QString &input, ParsedIntent &intent) override; QString name() const override; private: SemanticRuleEngine *m_engine; }; DFM_SEARCH_END_NS #endif // TARGETEXTRACTOR_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/timeextractor.cpp000066400000000000000000000350211523477242100330700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "timeextractor.h" #include "semantic/semanticruleengine.h" #include #include #include DFM_SEARCH_BEGIN_NS TimeExtractor::TimeExtractor(SemanticRuleEngine *engine) : m_engine(engine) { } TimeExtractor::~TimeExtractor() = default; void TimeExtractor::extract(const QString &input, ParsedIntent &intent) { if (!m_engine->hasGroup("time")) { return; } QString ruleId; QRegularExpressionMatch match; if (!m_engine->match("time", input, match, &ruleId)) { return; } const QVariantMap metadata = m_engine->ruleMetadata("time", ruleId); const QString typeStr = metadata.value("type").toString(); TimeConstraint tc; if (typeStr == "preset") { const QString presetStr = metadata.value("preset").toString(); static const QMap kPresetMap = { { "today", TimePreset::Today }, { "yesterday", TimePreset::Yesterday }, { "day_before_yesterday", TimePreset::DayBeforeYesterday }, { "this_week", TimePreset::ThisWeek }, { "last_week", TimePreset::LastWeek }, { "this_month", TimePreset::ThisMonth }, { "last_month", TimePreset::LastMonth }, { "this_year", TimePreset::ThisYear }, { "last_year", TimePreset::LastYear }, }; if (kPresetMap.contains(presetStr)) { tc.setKind(TimeConstraintKind::Preset); tc.setPreset(kPresetMap.value(presetStr)); } } else if (typeStr == "custom") { parseCustomTime(match, metadata, tc); } else if (typeStr == "preset_year_month") { parsePresetYearMonth(match, metadata, tc); } else if (typeStr == "preset_week_day") { parsePresetWeekDay(match, metadata, tc); } else if (typeStr == "preset_month_day") { parsePresetMonthDay(match, metadata, tc); } else if (typeStr == "relative") { parseRelativeTime(metadata, tc); } else if (typeStr == "relative_dynamic") { parseDynamicRelativeTime(match, metadata, tc); } if (tc.isValid()) { intent.setTimeConstraint(tc); MatchSpan span; span.setStart(match.capturedStart()); span.setEnd(match.capturedEnd()); span.setRuleId(ruleId); intent.consumedSpans().append(span); } } void TimeExtractor::parseCustomTime(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc) { Q_UNUSED(metadata); auto tryCapture = [&match](const QString &name) -> QString { const QString val = match.captured(name); return val.isNull() ? QString() : val; }; // Load locale-aware number conversion from rule metadata const QMap digitMap = mapFromVariant(metadata.value("digit_map")); const QString tensUnit = metadata.value("tens_unit").toString(); const QDate today = QDate::currentDate(); int year = today.year(); int month = 0; int day = 0; { const QString yearStr = tryCapture("year"); if (!yearStr.isEmpty()) { year = localeAwareToInt(yearStr, digitMap, tensUnit); if (year <= 0) { qWarning() << "Invalid year:" << yearStr; return; } if (year < 100) { year += 2000; } } } { const QString monthStr = tryCapture("month"); if (!monthStr.isEmpty()) { month = localeAwareToInt(monthStr, digitMap, tensUnit); if (month < 1 || month > 12) { qWarning() << "Invalid month:" << monthStr; return; } } } { const QString dayStr = tryCapture("day"); if (!dayStr.isEmpty()) { day = localeAwareToInt(dayStr, digitMap, tensUnit); if (day < 1 || day > 31) { qWarning() << "Invalid day:" << dayStr; return; } } } // Validate date if (month > 0 && day > 0) { QDate date(year, month, day); if (!date.isValid()) { qWarning() << "Invalid date:" << year << month << day; return; } tc.setKind(TimeConstraintKind::Custom); tc.setCustomStart(QDateTime(date, QTime(0, 0, 0))); tc.setCustomEnd(QDateTime(date, QTime(23, 59, 59))); } else if (month > 0 && day == 0) { // Year-month only: entire month QDate monthStart(year, month, 1); QDate monthEnd(year, month, monthStart.daysInMonth()); tc.setKind(TimeConstraintKind::Custom); tc.setCustomStart(QDateTime(monthStart, QTime(0, 0, 0))); tc.setCustomEnd(QDateTime(monthEnd, QTime(23, 59, 59))); } else if (month == 0) { // Year only: entire year tc.setKind(TimeConstraintKind::Custom); tc.setCustomStart(QDateTime(QDate(year, 1, 1), QTime(0, 0, 0))); tc.setCustomEnd(QDateTime(QDate(year, 12, 31), QTime(23, 59, 59))); } } void TimeExtractor::parsePresetYearMonth(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc) { const QMap digitMap = mapFromVariant(metadata.value("digit_map")); const QString tensUnit = metadata.value("tens_unit").toString(); const QString monthStr = match.captured("month"); if (monthStr.isEmpty()) { return; } const int month = localeAwareToInt(monthStr, digitMap, tensUnit); if (month < 1 || month > 12) { return; } const QDate today = QDate::currentDate(); int year = today.year(); if (metadata.value("preset").toString() == QLatin1String("last_year")) { year -= 1; } QDate monthStart(year, month, 1); QDate monthEnd(year, month, monthStart.daysInMonth()); tc.setKind(TimeConstraintKind::Custom); tc.setCustomStart(QDateTime(monthStart, QTime(0, 0, 0))); tc.setCustomEnd(QDateTime(monthEnd, QTime(23, 59, 59))); } void TimeExtractor::parsePresetWeekDay(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc) { const QMap weekdayMap = mapFromVariant(metadata.value("weekday_map")); const QString weekdayStr = match.captured("weekday"); if (weekdayStr.isEmpty() || !weekdayMap.contains(weekdayStr)) { return; } const int weekday = weekdayMap.value(weekdayStr); // 1=Mon … 7=Sun const QDate today = QDate::currentDate(); // Qt: Monday=1 … Sunday=7 — directly matches our weekday encoding. const QDate mondayOfThisWeek = today.addDays(1 - today.dayOfWeek()); QDate targetDate; if (metadata.value("preset").toString() == QLatin1String("last_week")) { targetDate = mondayOfThisWeek.addDays(-7 + (weekday - 1)); } else { targetDate = mondayOfThisWeek.addDays(weekday - 1); } if (!targetDate.isValid()) { return; } tc.setKind(TimeConstraintKind::Custom); tc.setCustomStart(QDateTime(targetDate, QTime(0, 0, 0))); tc.setCustomEnd(QDateTime(targetDate, QTime(23, 59, 59))); } void TimeExtractor::parsePresetMonthDay(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc) { const QMap digitMap = mapFromVariant(metadata.value("digit_map")); const QString tensUnit = metadata.value("tens_unit").toString(); const QString dayStr = match.captured("day"); if (dayStr.isEmpty()) { return; } const int day = localeAwareToInt(dayStr, digitMap, tensUnit); if (day < 1 || day > 31) { return; } const QDate today = QDate::currentDate(); QDate targetDate; if (metadata.value("preset").toString() == QLatin1String("last_month")) { targetDate = today.addMonths(-1); } else { targetDate = today; } targetDate.setDate(targetDate.year(), targetDate.month(), day); if (!targetDate.isValid()) { return; } tc.setKind(TimeConstraintKind::Custom); tc.setCustomStart(QDateTime(targetDate, QTime(0, 0, 0))); tc.setCustomEnd(QDateTime(targetDate, QTime(23, 59, 59))); } void TimeExtractor::parseRelativeTime(const QVariantMap &metadata, TimeConstraint &tc) { const QDateTime now = QDateTime::currentDateTime(); const int agoEndSecs = metadata.value("ago_end_seconds", 0).toInt(); const int agoStartSecs = metadata.value("ago_start_seconds", 0).toInt(); tc.setKind(TimeConstraintKind::Relative); tc.setCustomEnd(now.addSecs(-agoEndSecs)); if (agoStartSecs < 0) { // Sentinel: "from epoch" tc.setCustomStart(QDateTime::fromMSecsSinceEpoch(0)); } else { tc.setCustomStart(now.addSecs(-agoStartSecs)); } } void TimeExtractor::parseDynamicRelativeTime(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc) { // Load locale-aware number conversion from rule metadata const QMap digitMap = mapFromVariant(metadata.value("digit_map")); const QString tensUnit = metadata.value("tens_unit").toString(); // Extract numeric value from any of the named capture groups (value, value2, value3, value4) int value = 0; const QStringList captureNames = { QStringLiteral("value"), QStringLiteral("value2"), QStringLiteral("value3"), QStringLiteral("value4") }; for (const QString &name : captureNames) { const QString captured = match.captured(name); if (!captured.isNull()) { value = localeAwareToInt(captured, digitMap, tensUnit); if (value > 0) { break; } value = 0; } } if (value <= 0 || value > 3650) { return; } // Determine unit from metadata or capture const QString unitStr = metadata.value("default_unit").toString(); TimeUnit unit = TimeUnit::Days; if (unitStr == "hours") { unit = TimeUnit::Hours; } else if (unitStr == "weeks") { unit = TimeUnit::Weeks; } else if (unitStr == "months") { unit = TimeUnit::Months; } const QDateTime now = QDateTime::currentDateTime(); qint64 totalSeconds = 0; switch (unit) { case TimeUnit::Minutes: totalSeconds = static_cast(value) * 60; break; case TimeUnit::Hours: totalSeconds = static_cast(value) * 3600; break; case TimeUnit::Days: totalSeconds = static_cast(value) * 86400; break; case TimeUnit::Weeks: totalSeconds = static_cast(value) * 7 * 86400; break; case TimeUnit::Months: { // Approximate: use average days per month const QDate startDate = now.addMonths(-value).date(); tc.setKind(TimeConstraintKind::Relative); tc.setCustomStart(QDateTime(startDate, QTime(0, 0, 0))); tc.setCustomEnd(now); tc.setRelativeValue(value); tc.setRelativeUnit(unit); return; } case TimeUnit::Years: totalSeconds = static_cast(value) * 365 * 86400; break; } tc.setKind(TimeConstraintKind::Relative); tc.setCustomStart(now.addSecs(-totalSeconds)); tc.setCustomEnd(now); tc.setRelativeValue(value); tc.setRelativeUnit(unit); } int TimeExtractor::localeAwareToInt(const QString &input, const QMap &digitMap, const QString &tensUnit) { if (input.isEmpty()) { return -1; } // Try direct integer conversion first (handles Arabic numerals) bool ok = false; int directValue = input.toInt(&ok); if (ok) { return directValue; } // Single digit character from digit_map if (input.size() == 1 && digitMap.contains(input)) { return digitMap.value(input); } // No digit_map or tens_unit configured — cannot parse locale-specific numbers if (digitMap.isEmpty() || tensUnit.isEmpty()) { return -1; } // Leading tens-unit pattern: "十X" (e.g., "十一" = 11, "十五" = 15) if (input.size() == 2 && input.left(1) == tensUnit) { int suffix = digitMap.value(input.right(1), -1); if (suffix >= 0) { return 10 + suffix; } } // Two-character pattern: "XY" where Y is the tens unit (e.g., "二十" = 20) if (input.size() == 2 && input.mid(1) == tensUnit) { int prefix = digitMap.value(input.left(1), -1); if (prefix > 0) { return prefix * 10; } // "十" alone = 10 if (prefix == -1 && input.left(1) == tensUnit) { return 10; } } // Three-character pattern: "X Y Z" where Y is tens unit (e.g., "二十五" = 25) if (input.size() == 3 && input.mid(1) == tensUnit) { int prefix = digitMap.value(input.left(1), -1); int suffix = digitMap.value(input.right(1), 0); if (prefix > 0) { return prefix * 10 + suffix; } } // Digit-by-digit Chinese numeral string (e.g., "二五" = 25, "二零二五" = 2025, // "二〇二五" = 2025). Used for year written in Chinese numerals position-by-position. // Each character must map to a single digit 0-9 — this excludes "十" (10) and "两", // which are not used in digit-by-digit year notation. This branch runs after the // "十"-based branches above, so "十一"/"二十"/"十二" are already handled. bool allSingleDigits = true; for (const QChar &ch : input) { auto it = digitMap.constFind(ch); if (it == digitMap.constEnd() || it.value() > 9) { allSingleDigits = false; break; } } if (allSingleDigits && input.size() >= 2) { int value = 0; for (const QChar &ch : input) { value = value * 10 + digitMap.value(ch); } return value; } return -1; } QMap TimeExtractor::mapFromVariant(const QVariant &variant) { QMap result; const QVariantMap map = variant.toMap(); for (auto it = map.constBegin(); it != map.constEnd(); ++it) { result.insert(it.key(), it.value().toInt()); } return result; } QString TimeExtractor::name() const { return QStringLiteral("time"); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/extractors/timeextractor.h000066400000000000000000000041651523477242100325420ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TIMEEXTRACTOR_H #define TIMEEXTRACTOR_H #include #include DFM_SEARCH_BEGIN_NS class SemanticRuleEngine; class TimeExtractor : public DimensionExtractor { public: explicit TimeExtractor(SemanticRuleEngine *engine); ~TimeExtractor() override; void extract(const QString &input, ParsedIntent &intent) override; QString name() const override; private: void parsePresetYearMonth(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc); void parsePresetWeekDay(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc); void parsePresetMonthDay(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc); void parseCustomTime(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc); void parseRelativeTime(const QVariantMap &metadata, TimeConstraint &tc); void parseDynamicRelativeTime(const QRegularExpressionMatch &match, const QVariantMap &metadata, TimeConstraint &tc); /** * @brief Convert a string to int using locale-aware digit mapping. * * First tries direct integer conversion (Arabic numerals). * Falls back to digit_map lookup and positional tens-unit parsing. * @param input The string to convert * @param digitMap Mapping of locale-specific digit characters to integers (from rule metadata) * @param tensUnit The character representing the tens place (from rule metadata, e.g. "十") * @return The integer value, or -1 if conversion fails */ static int localeAwareToInt(const QString &input, const QMap &digitMap, const QString &tensUnit); /** * @brief Convert a QVariantMap (from JSON) to a QMap. */ static QMap mapFromVariant(const QVariant &variant); SemanticRuleEngine *m_engine; }; DFM_SEARCH_END_NS #endif // TIMEEXTRACTOR_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/intentparser.cpp000066400000000000000000000037061523477242100305230ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "intentparser.h" #include "semanticruleengine.h" #include "extractors/actionextractor.h" #include "extractors/filetypeextractor.h" #include "extractors/keywordextractor.h" #include "extractors/locationextractor.h" #include "extractors/sizeextractor.h" #include "extractors/targetextractor.h" #include "extractors/timeextractor.h" DFM_SEARCH_BEGIN_NS IntentParser::IntentParser(SemanticRuleEngine *engine) : m_engine(engine) { initDefaultExtractors(); } IntentParser::~IntentParser() = default; void IntentParser::parse(const QString &input, ParsedIntent &intent) { for (DimensionExtractor *extractor : m_extractors) { extractor->extract(input, intent); } } void IntentParser::addExtractor(std::unique_ptr extractor) { m_extractors.push_back(extractor.get()); m_extractorOwners.push_back(std::move(extractor)); } QStringList IntentParser::extractorNames() const { QStringList names; for (DimensionExtractor *e : m_extractors) { names.append(e->name()); } return names; } void IntentParser::initDefaultExtractors() { // Order matters: Location must run before FileType so that span-based // overlap suppression in FileTypeExtractor can drop a filetype match that // is fully contained within a location span (e.g. "视频" inside "视频目录"). // Keyword MUST be last (depends on consumedSpans). addExtractor(std::make_unique(m_engine)); addExtractor(std::make_unique(m_engine)); addExtractor(std::make_unique(m_engine)); addExtractor(std::make_unique(m_engine)); addExtractor(std::make_unique(m_engine)); addExtractor(std::make_unique(m_engine)); addExtractor(std::make_unique(m_engine)); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/intentparser.h000066400000000000000000000027161523477242100301700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef INTENTPARSER_H #define INTENTPARSER_H #include #include #include #include #include DFM_SEARCH_BEGIN_NS class SemanticRuleEngine; /** * @brief Orchestrates dimension extractors to parse natural language into intent. * * Extractors run in order. KeywordExtractor MUST be last * because it relies on consumedSpans from earlier extractors. */ class IntentParser { public: explicit IntentParser(SemanticRuleEngine *engine); ~IntentParser(); /** * @brief Parse natural language input into a structured intent. * @param input The raw natural language string * @param intent Output: parsed intent */ void parse(const QString &input, ParsedIntent &intent); /** * @brief Add a custom dimension extractor. * Extractors are called in the order they are added. */ void addExtractor(std::unique_ptr extractor); /** * @brief Get the list of extractor names. */ QStringList extractorNames() const; private: void initDefaultExtractors(); SemanticRuleEngine *m_engine; std::vector m_extractors; std::vector> m_extractorOwners; }; DFM_SEARCH_END_NS #endif // INTENTPARSER_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/ruleconfigloader.cpp000066400000000000000000000121331523477242100313230ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "ruleconfigloader.h" #include "semanticruleengine.h" #include #include #include #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS namespace { #ifdef CMAKE_INSTALL_PREFIX constexpr auto kInstallPrefix = CMAKE_INSTALL_PREFIX; #else constexpr auto kInstallPrefix = "/usr"; #endif constexpr auto kLibName = "dfm-search"; } // namespace QString RuleConfigLoader::libName() { return QLatin1String(kLibName); } QString RuleConfigLoader::systemRulesDir() { return QDir(QDir(QLatin1String(kInstallPrefix)) .absoluteFilePath(QLatin1String("share/deepin/") + libName() + "/semantic/rules")) .absolutePath(); } QString RuleConfigLoader::userRulesDir() { return QDir(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/deepin/" + libName() + "/semantic/rules") .absolutePath(); } QString RuleConfigLoader::currentLocaleName() { return QLocale::system().name().simplified(); } QStringList RuleConfigLoader::ruleFilePaths() { QStringList paths; QSet seen; // deduplicate by filename const QStringList dirs { resolveLocaleDir(userRulesDir()), resolveLocaleDir(systemRulesDir()) }; for (const QString &dir : dirs) { const QStringList files = QDir(dir).entryList( QStringList { QStringLiteral("*.json") }, QDir::Files | QDir::Readable); for (const QString &filename : files) { const QString absPath = QDir(dir).absoluteFilePath(filename); if (!seen.contains(filename) && validateRuleFile(absPath)) { paths.append(absPath); seen.insert(filename); } } } return paths; } QString RuleConfigLoader::resolveLocaleDir(const QString &baseDir) { const QString locale = currentLocaleName(); // Try full locale name (e.g., zh_CN) const QString fullLocalePath = QDir(baseDir).absoluteFilePath(locale); if (QDir(fullLocalePath).exists()) { return fullLocalePath; } // Try language only (e.g., zh from zh_CN) const QString langOnly = locale.split(QLatin1Char('_')).value(0); if (!langOnly.isEmpty() && langOnly != locale) { const QString langOnlyPath = QDir(baseDir).absoluteFilePath(langOnly); if (QDir(langOnlyPath).exists()) { return langOnlyPath; } } // Fallback to default locale return QDir(baseDir).absoluteFilePath(QLatin1String(kDefaultLocale)); } QString RuleConfigLoader::resolveRulePath(const QString &filename) { // User-local override with locale const QString userLocaleDir = resolveLocaleDir(userRulesDir()); const QString userPath = QDir(userLocaleDir).absoluteFilePath(filename); if (QFile::exists(userPath) && validateRuleFile(userPath)) { return userPath; } // System rules with locale const QString sysLocaleDir = resolveLocaleDir(systemRulesDir()); const QString sysPath = QDir(sysLocaleDir).absoluteFilePath(filename); if (QFile::exists(sysPath) && validateRuleFile(sysPath)) { return sysPath; } return {}; } bool RuleConfigLoader::loadRuleFile(const QString &path, QList &groups) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Failed to open rule file:" << path; return false; } QJsonParseError parseError; const QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parseError); if (parseError.error != QJsonParseError::NoError) { qWarning() << "JSON parse error in" << path << ":" << parseError.errorString(); return false; } const QJsonObject root = doc.object(); const QJsonArray groupsArray = root.value("groups").toArray(); for (const QJsonValue &gv : groupsArray) { RuleGroup group; if (!SemanticRuleEngine::parseRuleGroupStatic(gv.toObject(), group)) { qWarning() << "Invalid rule group in" << path; continue; } groups.append(group); } return !groups.isEmpty(); } bool RuleConfigLoader::validateRuleFile(const QString &path) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { return false; } QJsonParseError parseError; const QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parseError); if (parseError.error != QJsonParseError::NoError) { return false; } const QJsonObject root = doc.object(); return root.contains("groups") && root.value("groups").isArray(); } bool RuleConfigLoader::ensureUserRulesDir() { const QString dir = userRulesDir(); if (!QDir().mkpath(dir)) { qWarning() << "Failed to create user rules directory:" << dir; return false; } return true; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/ruleconfigloader.h000066400000000000000000000061701523477242100307740ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef RULECONFIGLOADER_H #define RULECONFIGLOADER_H #include #include DFM_SEARCH_BEGIN_NS struct RuleGroup; /** * @brief Loads semantic rule configuration files from system/user paths. * * Locale resolution: rules are organized under locale subdirectories * (e.g., rules/zh_CN/, rules/en_US/). The loader resolves the locale * at runtime using QLocale and falls back through a chain: * zh_CN -> zh -> zh_CN (default) * * Priority: user-local config > system-installed config. * System path: /usr/share/deepin/dfm-search/semantic/rules/ * User path: ~/.config/deepin/dfm-search/semantic/rules/ */ class RuleConfigLoader { public: /** * @brief Get the library name based on Qt version. */ static QString libName(); /** * @brief Get the system-installed rules directory. */ static QString systemRulesDir(); /** * @brief Get the user-local rules directory for overrides. */ static QString userRulesDir(); /** * @brief Get the current locale name (e.g., "zh_CN"). * Uses QLocale::system().name().simplified(). */ static QString currentLocaleName(); /** * @brief Scan locale directories and return resolved paths for all rule files. * Scans user dir first, then system dir; user files take priority. * Falls back through locale chain: zh_CN -> zh -> zh_CN (default). * @return Deduplicated list of resolved absolute paths */ static QStringList ruleFilePaths(); /** * @brief Resolve the effective path for a single rule file. * Checks user dir first, then system dir, with locale subdirectory lookup. * Falls back to zh_CN if the current locale directory is not found. * @param filename The rule file name (e.g., "time_rules.json") * @return The resolved absolute path, or empty if not found */ static QString resolveRulePath(const QString &filename); /** * @brief Load and parse a rule file into groups. * @param path Absolute path to the JSON file * @param groups Output: parsed rule groups * @return true if file was loaded and valid */ static bool loadRuleFile(const QString &path, QList &groups); /** * @brief Validate JSON structure of a rule file. * @param path Absolute path to the JSON file * @return true if valid */ static bool validateRuleFile(const QString &path); /** * @brief Ensure user rules directory exists. * @return true on success */ static bool ensureUserRulesDir(); private: /** * @brief Get the locale subdirectory name with fallback chain. * Tries: full locale (zh_CN) -> language only (zh) -> default (zh_CN) * @param baseDir The base rules directory * @return The locale subdirectory path that exists, or baseDir/defaultLocale */ static QString resolveLocaleDir(const QString &baseDir); static constexpr const char *kDefaultLocale = "zh_CN"; }; DFM_SEARCH_END_NS #endif // RULECONFIGLOADER_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/000077500000000000000000000000001523477242100264255ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/000077500000000000000000000000001523477242100274265ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/action_rules.json000066400000000000000000000045571523477242100330230ustar00rootroot00000000000000{ "version": "1.0.0", "groups": [ { "name": "action", "version": "1.0.0", "locale": "zh-CN", "rules": [ { "id": "action_create", "pattern": "新建的|创建的|存下来的|保存的|新加的", "description": "Action: created files (BirthTime)", "enabled": true, "priority": 200, "metadata": { "time_field": "birth" } }, { "id": "action_modify", "pattern": "修改过的|编辑过的|修改的|改过的|写过的|更新的", "description": "Action: modified files (ModifyTime)", "enabled": true, "priority": 200, "metadata": { "time_field": "modify" } }, { "id": "action_hidden_file", "pattern": "隐藏文件|隐藏的|藏起来的|看不到的", "description": "Action: search hidden files only", "enabled": true, "priority": 190, "metadata": { "hidden_only": true } }, { "id": "action_im_receive", "pattern": "发给我的|接收的|别人传的|传给我的|别人发的|别人给的", "description": "Action: IM received files — maps to all IM received-file directories", "enabled": true, "priority": 170, "metadata": { "im_category": "received" } }, { "id": "action_recent", "pattern": "打开的|打开过的|看过的|浏览过的|读过的|点开过的", "description": "Action: recently used files — sourced from RecentManager DBus (XDG recent records)", "enabled": true, "priority": 180, "metadata": { "recent": true } } ] } ] } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/filetype_rules.json000066400000000000000000000166741523477242100333720ustar00rootroot00000000000000{ "version": "1.0.0", "groups": [ { "name": "filetype", "version": "1.0.0", "locale": "zh-CN", "rules": [ { "id": "filetype_word", "pattern": "word|docx|doc", "description": "Word documents", "enabled": true, "priority": 200, "metadata": { "extensions": ["doc", "docx"], "fileTypes": ["doc"] } }, { "id": "filetype_pdf", "pattern": "pdf", "description": "PDF documents", "enabled": true, "priority": 200, "metadata": { "extensions": ["pdf"] } }, { "id": "filetype_excel", "pattern": "excel|xlsx|xls", "description": "Excel spreadsheets", "enabled": true, "priority": 200, "metadata": { "extensions": ["xls", "xlsx"], "fileTypes": ["doc"] } }, { "id": "filetype_ppt", "pattern": "pptx|ppt", "description": "PowerPoint presentations", "enabled": true, "priority": 200, "metadata": { "extensions": ["ppt", "pptx"] } }, { "id": "filetype_document_general", "pattern": "文档|报告|文章|方案|文本|资料|笔记|稿件", "description": "Generic documents (fallback)", "enabled": true, "priority": 100, "metadata": { "extensions": ["rtf","odt","ods","odp","odg","docx","xlsx","et","pptx","ppsx","md","xls","xlsb","doc","dot","wps","ppt","pps","txt","pdf","dps","sh","json","yaml","ini","bat","sql","uof","ofd","pot","docm","dotx","dotm","wpt","uot","uott","uot3","uott3","ott","eig","otl"], "fileTypes": ["doc"], "general": true } }, { "id": "filetype_spreadsheet_general", "pattern": "表格|统计表|报表|名单|数据表|数据|明细", "description": "Generic spreadsheets (fallback)", "enabled": true, "priority": 100, "metadata": { "extensions": ["xls", "xlsx", "csv", "ods", "et", "xlsm", "xlt", "xltx", "xltm", "ett", "uos", "uost", "uos3", "uost3", "ots", "tsv", "dbt"], "fileTypes": ["doc"], "general": true } }, { "id": "filetype_presentation_general", "pattern": "幻灯片|演示文稿|汇报|课件|宣讲", "description": "Generic presentations (fallback)", "enabled": true, "priority": 100, "metadata": { "extensions": ["ppt", "pptx", "dps", "odp", "pptm", "ppsm", "potx", "potm", "dpt", "uop", "uopt", "uop3", "uopt3", "otp"], "general": true } }, { "id": "filetype_image", "pattern": "图片|照片|截图|壁纸|海报|相片|表情包|图", "description": "Images", "enabled": true, "priority": 150, "metadata": { "extensions": ["ani","avif","avifs","bmp","bw","dci","eps","epsf","epsi","exr","gif","heic","heif","heis","heix","icns","ico","jpe","jpeg","jpg","kra","mng","ora","pcx","pic","png","psd","raf","ras","raw","rgb","rgba","sgi","svg","svgz","tga","tif","tiff","wbmp","webp","wmf","xcf","cr2","crw","nef","dng","arw","orf","rw2","pef","srw","mrw","x3f","dcr","kdc","3fr","nrw","mef","iiq","sr2","erf","mos","rwl"], "fileTypes": ["pic"] } }, { "id": "filetype_video", "pattern": "视频|录像|电影|动画|短片|片子", "description": "Videos", "enabled": true, "priority": 150, "metadata": { "extensions": ["3g2","3gp","3gp2","3gpp","amr","amv","asf","asx","avi","bdmv","bik","d2v","divx","drc","dsa","dsm","dss","dsv","evo","f4v","flc","fli","flic","flv","hdmov","ifo","ivf","m1v","m2p","m2t","m2ts","m2v","m4b","m4p","m4v","mkv","mp2v","mp4","mp4v","mpe","mpeg","mpg","mpls","mpv2","mpv4","mov","mts","ogm","ogv","pss","pva","qt","ram","ratdvd","rm","rmm","rmvb","roq","rpm","smil","smk","swf","tp","tpr","vob","vp6","webm","wm","wmp","wmv"], "fileTypes": ["video"] } }, { "id": "filetype_audio", "pattern": "音频|音乐|录音|歌|语音", "description": "Audio files", "enabled": true, "priority": 150, "metadata": { "extensions": ["aac","ac3","aif","aifc","aiff","au","cda","dts","fla","flac","it","m1a","m2a","m3u","m4a","mid","midi","mka","mod","mp2","mp3","mpa","ogg","opus","ra","rmi","spc","snd","umx","voc","wav","wma","xm","ape"], "fileTypes": ["audio"] } }, { "id": "filetype_archive", "pattern": "压缩包|归档|源码包|打包文件", "description": "Archive files", "enabled": true, "priority": 150, "metadata": { "extensions": ["7z","ace","arj","bz2","cab","gz","gzip","jar","r00","r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r11","r12","r13","r14","r15","r16","r17","r18","r19","r20","r21","r22","r23","r24","r25","r26","r27","r28","r29","rar","tar","tgz","z","zip"], "fileTypes": ["archive"] } }, { "id": "filetype_application", "pattern": "安装包|软件|应用|脚本|程序|包(?!含)", "description": "Application packages", "enabled": true, "priority": 150, "metadata": { "extensions": ["deb", "AppImage", "sh", "py", "bin", "run", "desktop"], "fileTypes": ["app"] } }, { "id": "filetype_design_source", "pattern": "源文件|设计稿|矢量图|工程文件", "description": "Design source files", "enabled": true, "priority": 160, "metadata": { "extensions": ["psd", "ai", "fig", "sketch"] } } ] } ] } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/keyword_rules.json000066400000000000000000000056111523477242100332220ustar00rootroot00000000000000{ "version": "1.0.0", "groups": [ { "name": "keyword", "version": "1.0.0", "locale": "zh-CN", "rules": [ { "id": "keyword_contains", "pattern": "包含(.+?)(?:的|$)", "description": "Contains keyword pattern", "enabled": true, "priority": 200, "metadata": { "capture_group": 1, "multi_keyword": true, "search_target": "all", "cleanup_pattern": "[\\s。、\u201c\u201d\uff0c\u3001\u7684\u4e86\u8981\u554a]+", "split_pattern": "[\u548c\u4e0e\u4ee5\u53ca\u6216\u3001]+" } }, { "id": "keyword_named", "pattern": "名为(.+?)(?:的|$)|叫做(.+?)(?:的|$)", "description": "Named keyword pattern", "enabled": true, "priority": 200, "metadata": { "capture_group": 1, "multi_keyword": false, "search_target": "filename", "cleanup_pattern": "[\\s。、\u201c\u201d\uff0c\u3001\u7684\u4e86\u8981\u554a]+", "split_pattern": "[\u548c\u4e0e\u4ee5\u53ca\u6216\u3001]+" } }, { "id": "keyword_content_has", "pattern": "内容(?:包含|含有|带有)(.+?)(?:的|$)", "description": "Content contains keyword pattern", "enabled": true, "priority": 210, "metadata": { "capture_group": 1, "multi_keyword": true, "search_target": "content", "cleanup_pattern": "[\\s。、\u201c\u201d\uff0c\u3001\u7684\u4e86\u8981\u554a]+", "split_pattern": "[\u548c\u4e0e\u4ee5\u53ca\u6216\u3001]+" } }, { "id": "keyword_filename_has", "pattern": "文件名(?:包含|含有|带有|是|为)(.+?)(?:的|$)", "description": "Filename contains keyword pattern", "enabled": true, "priority": 210, "metadata": { "capture_group": 1, "multi_keyword": true, "search_target": "filename", "cleanup_pattern": "[\\s。、\u201c\u201d\uff0c\u3001\u7684\u4e86\u8981\u554a]+", "split_pattern": "[\u548c\u4e0e\u4ee5\u53ca\u6216\u3001]+" } } ] } ] } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/location_rules.json000066400000000000000000000076641523477242100333600ustar00rootroot00000000000000{ "version": "1.0.0", "groups": [ { "name": "location", "version": "1.0.0", "locale": "zh-CN", "rules": [ { "id": "loc_desktop", "pattern": "桌面|屏幕上|电脑桌面", "description": "Desktop directory", "enabled": true, "priority": 200, "metadata": { "xdg_type": "desktop", "include_hidden": false } }, { "id": "loc_download", "pattern": "下载|下载目录|下下来的|浏览器下载", "description": "Downloads directory", "enabled": true, "priority": 200, "metadata": { "xdg_type": "download", "include_hidden": false } }, { "id": "loc_documents_dir", "pattern": "文档里|文档下|文档中|我的文档|文档目录|文档文件夹|文档库", "description": "Documents directory (must contain directory/folder word)", "enabled": true, "priority": 200, "metadata": { "xdg_type": "documents", "include_hidden": false } }, { "id": "loc_pictures_dir", "pattern": "图片里|图片下|图片中|图片目录|图片文件夹|照片目录|照片文件夹|相册里|图片库", "description": "Pictures directory (must contain directory/folder word)", "enabled": true, "priority": 200, "metadata": { "xdg_type": "pictures", "include_hidden": false } }, { "id": "loc_music_dir", "pattern": "音乐里|音乐下|音乐中|音乐目录|音乐文件夹", "description": "Music directory (must contain directory/folder word)", "enabled": true, "priority": 200, "metadata": { "xdg_type": "music", "include_hidden": false } }, { "id": "loc_videos_dir", "pattern": "视频里|视频下|视频中|视频目录|视频文件夹|电影目录|电影文件夹", "description": "Videos directory (must contain directory/folder word)", "enabled": true, "priority": 200, "metadata": { "xdg_type": "movies", "include_hidden": false } }, { "id": "loc_wechat_data", "pattern": "微信发给我的|微信发我的|微信接收的|微信群里的|微信群里|微信群|微信里|微信|vx|wechat", "description": "WeChat received files directory — longer patterns first for maximal span consumption", "enabled": true, "priority": 180, "metadata": { "custom_path": [ "Documents/xwechat_files/*/msg/file", "Documents/xwechat_files/*/msg/video" ], "exclude_patterns": ["*_thumb.jpg", "[0-9][0-9][0-9][0-9]-[0-9][0-9]"], "include_hidden": false, "im_received": true } } ] } ] } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/noise_rules.json000066400000000000000000000046611523477242100326570ustar00rootroot00000000000000{ "version": "1.0.0", "groups": [ { "name": "noise", "version": "1.0.0", "locale": "zh-CN", "rules": [ { "id": "noise_action", "pattern": "帮我找到|帮我搜|帮我找|搜索|查找|找一下|搜一下|查一下|找下|搜下|查下|找找|搜搜", "description": "Search action words to consume", "enabled": true, "priority": 100, "metadata": {} }, { "id": "noise_suffix", "pattern": "的文件|的图片|的文档|的视频|的音频|的照片|的音乐|的压缩包|的安装包", "description": "Trailing suffix words to consume", "enabled": true, "priority": 90, "metadata": {} }, { "id": "noise_size_lead", "pattern": "大小在|大小为|大小是|体积在|体积为|体积是|容量在|容量为|容量是|大小不超过|大小超过|大小不到|大小最多|大小最少|大小至少", "description": "Size constraint lead-in words to consume", "enabled": true, "priority": 140, "metadata": {} }, { "id": "noise_polite", "pattern": "请|麻烦|谢谢|帮我", "description": "Polite words to consume", "enabled": true, "priority": 80, "metadata": {} }, { "id": "noise_generic", "pattern": "里面|关于|含有|带有|和|与|以及|或者|或", "description": "Generic filler words to consume", "enabled": true, "priority": 70, "metadata": {} }, { "id": "noise_location_connector", "pattern": "上的|里的|下的|中的|内的|上|里|下|中|内", "description": "Location connector words to consume", "enabled": true, "priority": 190, "metadata": {} } ] } ] } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/size_rules.json000066400000000000000000000105251523477242100325100ustar00rootroot00000000000000{ "version": "1.0.0", "groups": [ { "name": "size", "version": "1.0.0", "locale": "zh-CN", "rules": [ { "id": "size_fuzzy_large", "pattern": "大文件|很大的|占空间的|几个G的|几个g的", "description": "Fuzzy large files (>500MB)", "enabled": true, "priority": 200, "metadata": { "type": "preset", "min_bytes": 524288000, "max_bytes": 0 } }, { "id": "size_small", "pattern": "小文件|很小的| tiny的", "description": "Small files (<1MB)", "enabled": true, "priority": 200, "metadata": { "type": "preset", "min_bytes": 0, "max_bytes": 1048576, "include_upper": false } }, { "id": "size_dynamic_min_suffix", "pattern": "(?\\d+(?:\\.\\d+)?)\\s*(?[KkMmGg][Bb]?|兆|字节|千|万)\\s*以上", "description": "Dynamic size min with suffix only (e.g. 10M以上, 1G以上, 500K以上)", "enabled": true, "priority": 160, "metadata": { "type": "dynamic", "direction": "min", "unit_map": {"兆": "M", "字节": "B", "千": "K", "万": "K"} } }, { "id": "size_dynamic_max_suffix", "pattern": "(?\\d+(?:\\.\\d+)?)\\s*(?[KkMmGg][Bb]?|兆|字节|千|万)\\s*(?:以内|以下)", "description": "Dynamic size max with suffix only (e.g. 10M以内, 1G以下, 500K以内)", "enabled": true, "priority": 160, "metadata": { "type": "dynamic", "direction": "max", "unit_map": {"兆": "M", "字节": "B", "千": "K", "万": "K"} } }, { "id": "size_dynamic", "pattern": "(大于|超过|最少|至少|>)\\s*(?\\d+(?:\\.\\d+)?)\\s*(?[KkMmGg][Bb]?|兆|字节|千|万)?以?[上内]?", "description": "Dynamic precise size min (e.g. 大于500M, 1G以上, 大于100兆)", "enabled": true, "priority": 150, "metadata": { "type": "dynamic", "direction": "min", "unit_map": {"兆": "M", "字节": "B", "千": "K", "万": "K"} } }, { "id": "size_dynamic_less", "pattern": "(小于|不超过|不到|最多|<)\\s*(?\\d+(?:\\.\\d+)?)\\s*(?[KkMmGg][Bb]?|兆|字节|千|万)?以?[下内]?", "description": "Dynamic precise size max (e.g. 小于100K, 不到1G, 小于100兆)", "enabled": true, "priority": 150, "metadata": { "type": "dynamic", "direction": "max", "unit_map": {"兆": "M", "字节": "B", "千": "K", "万": "K"} } }, { "id": "size_dynamic_between", "pattern": "(?\\d+(?:\\.\\d+)?)\\s*(?[KkMmGg][Bb]?|兆|字节|千|万)?[\\s~\\-到至]+(?\\d+(?:\\.\\d+)?)\\s*(?[KkMmGg][Bb]?|兆|字节|千|万)?", "description": "Size range (e.g. 1M-10M, 100K到500K, 1兆到10兆)", "enabled": true, "priority": 150, "metadata": { "type": "dynamic", "direction": "range", "unit_map": {"兆": "M", "字节": "B", "千": "K", "万": "K"} } } ] } ] } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/target_rules.json000066400000000000000000000021011523477242100330130ustar00rootroot00000000000000{ "version": "1.0.0", "groups": [ { "name": "target", "version": "1.0.0", "locale": "zh-CN", "rules": [ { "id": "target_file_generic", "pattern": "文件", "description": "Generic file target noun", "enabled": true, "priority": 100, "metadata": {} }, { "id": "target_filename", "pattern": "文件名", "description": "Filename target noun", "enabled": true, "priority": 120, "metadata": {} }, { "id": "target_folder", "pattern": "文件夹|目录", "description": "Folder target noun", "enabled": true, "priority": 100, "metadata": {} } ] } ] } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/time_rules.json000066400000000000000000000551261523477242100325020ustar00rootroot00000000000000{ "version": "1.0.0", "groups": [ { "name": "time", "version": "1.0.0", "locale": "zh-CN", "rules": [ { "id": "time_today", "pattern": "今天|今日份|今日", "description": "Today", "enabled": true, "priority": 200, "metadata": { "type": "preset", "preset": "today" } }, { "id": "time_yesterday", "pattern": "昨天上午|昨天下午|昨天晚上|昨天|昨日|昨晚", "description": "Yesterday", "enabled": true, "priority": 200, "metadata": { "type": "preset", "preset": "yesterday" } }, { "id": "time_day_before_yesterday", "pattern": "前天", "description": "Day before yesterday", "enabled": true, "priority": 200, "metadata": { "type": "preset", "preset": "day_before_yesterday" } }, { "id": "time_this_week", "pattern": "本周|这周|这个星期|这一个星期", "description": "This week", "enabled": true, "priority": 190, "metadata": { "type": "preset", "preset": "this_week" } }, { "id": "time_last_week", "pattern": "上周|上个星期|上星期|上一个星期", "description": "Last week", "enabled": true, "priority": 190, "metadata": { "type": "preset", "preset": "last_week" } }, { "id": "time_this_month", "pattern": "本月|这个月|当月", "description": "This month", "enabled": true, "priority": 180, "metadata": { "type": "preset", "preset": "this_month" } }, { "id": "time_last_month", "pattern": "上个月|上月", "description": "Last month", "enabled": true, "priority": 180, "metadata": { "type": "preset", "preset": "last_month" } }, { "id": "time_this_year", "pattern": "今年|本年|这年", "description": "This year", "enabled": true, "priority": 170, "metadata": { "type": "preset", "preset": "this_year" } }, { "id": "time_last_year", "pattern": "去年一整年|去年|上一年", "description": "Last year", "enabled": true, "priority": 170, "metadata": { "type": "preset", "preset": "last_year" } }, { "id": "time_last_year_month", "pattern": "(?:去年|上一年)(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))月份?", "description": "Last year + month (e.g. 去年12月, 上一年三月)", "enabled": true, "priority": 175, "metadata": { "type": "preset_year_month", "preset": "last_year", "digit_map": { "零": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_this_year_month", "pattern": "(?:今年|本年|这年)(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))月份?", "description": "This year + month (e.g. 今年3月, 本年十二月)", "enabled": true, "priority": 175, "metadata": { "type": "preset_year_month", "preset": "this_year", "digit_map": { "零": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_this_week_day", "pattern": "(?:本周|这周|这个星期|这一个星期)(?[一二三四五六日天])", "description": "This week + weekday (e.g. 本周三, 这周星期五)", "enabled": true, "priority": 195, "metadata": { "type": "preset_week_day", "preset": "this_week", "weekday_map": { "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "日": 7, "天": 7 } } }, { "id": "time_last_week_day", "pattern": "(?:上周|上个星期|上星期|上一个星期)(?[一二三四五六日天])", "description": "Last week + weekday (e.g. 上周三, 上个星期五)", "enabled": true, "priority": 195, "metadata": { "type": "preset_week_day", "preset": "last_week", "weekday_map": { "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "日": 7, "天": 7 } } }, { "id": "time_this_month_day", "pattern": "(?:本月|这个月|当月)(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[日号]", "description": "This month + day (e.g. 本月5号, 当月十五日)", "enabled": true, "priority": 185, "metadata": { "type": "preset_month_day", "preset": "this_month", "digit_map": { "零": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_last_month_day", "pattern": "(?:上个月|上月)(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[日号]", "description": "Last month + day (e.g. 上个月5号, 上月十五日)", "enabled": true, "priority": 185, "metadata": { "type": "preset_month_day", "preset": "last_month", "digit_map": { "零": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_exact_year", "pattern": "(?\\d{2,4}|[零〇一二三四五六七八九]{2,4})年", "description": "Exact year (e.g. 2025年, 25年, 二五年, 二零二五年)", "enabled": true, "priority": 120, "metadata": { "type": "custom", "format": "year", "digit_map": { "零": 0, "〇": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_exact_month_current_year", "pattern": "(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))月份?", "description": "Month this year (e.g. 12月, 5月份)", "enabled": true, "priority": 120, "metadata": { "type": "custom", "format": "month", "digit_map": { "零": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_exact_year_month", "pattern": "(?\\d{2,4}|[零〇一二三四五六七八九]{2,4})[年\\./\\-](?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))(?:月份?)?", "description": "Exact year-month (e.g. 2025年12月, 25年12月, 二五年十二月, 二零二五年12月)", "enabled": true, "priority": 140, "metadata": { "type": "custom", "format": "year_month", "digit_map": { "零": 0, "〇": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_exact_date_current_year", "pattern": "(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))月(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[日号]", "description": "Exact date this year (e.g. 12月5日, 3月8号)", "enabled": true, "priority": 140, "metadata": { "type": "custom", "format": "date", "digit_map": { "零": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_exact_full_date", "pattern": "(?\\d{2,4}|[零〇一二三四五六七八九]{2,4})[年\\./\\-](?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[月\\./\\-](?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[日号]?", "description": "Exact full date (e.g. 2025年12月5日, 2025-12-05, 二零二五年十二月五日)", "enabled": true, "priority": 160, "metadata": { "type": "custom", "format": "full_date", "digit_map": { "零": 0, "〇": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_just_now", "pattern": "这会儿|刚才|刚刚|刚", "description": "Just now (last 2 hours)", "enabled": true, "priority": 80, "metadata": { "type": "relative", "relative_id": "just_now", "ago_start_seconds": 7200, "ago_end_seconds": 0 } }, { "id": "time_recent_days", "pattern": "这阵子|近期|最近|这几天", "description": "Recent days (last 3 days)", "enabled": true, "priority": 80, "metadata": { "type": "relative", "relative_id": "recent_days", "ago_start_seconds": 259200, "ago_end_seconds": 0 } }, { "id": "time_recent_dynamic_days", "pattern": "最?近(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?天|过去(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?天|前(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?天|近(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?天", "description": "Recent N days (e.g. 最近3天, 最近三天, 近1周, 过去七天)", "enabled": true, "priority": 150, "metadata": { "type": "relative_dynamic", "default_unit": "days", "digit_map": { "零": 0, "一": 1, "二": 2, "两": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_recent_dynamic_hours", "pattern": "最?近(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?小时|过去(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?小时|近(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?小时|前(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?小时", "description": "Recent N hours (e.g. 最近2小时, 最近两小时, 近一小时)", "enabled": true, "priority": 150, "metadata": { "type": "relative_dynamic", "default_unit": "hours", "digit_map": { "零": 0, "一": 1, "二": 2, "两": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_recent_dynamic_weeks", "pattern": "最?近(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?周|过去(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?周|近(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?周|前(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?周", "description": "Recent N weeks (e.g. 最近2周, 最近两周, 近一周)", "enabled": true, "priority": 150, "metadata": { "type": "relative_dynamic", "default_unit": "weeks", "digit_map": { "零": 0, "一": 1, "二": 2, "两": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_recent_dynamic_months", "pattern": "最?近(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?月|过去(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?月|近(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?月|前(?[一二两三四五六七八九十百千万\\d]+)\\s*(?:个)?月", "description": "Recent N months (e.g. 最近3个月, 最近三个月, 近一月)", "enabled": true, "priority": 150, "metadata": { "type": "relative_dynamic", "default_unit": "months", "digit_map": { "零": 0, "一": 1, "二": 2, "两": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10 }, "tens_unit": "十" } }, { "id": "time_past_few_days", "pattern": "那些天|之前几天|前几天", "description": "Past few days (3-7 days ago)", "enabled": true, "priority": 80, "metadata": { "type": "relative", "relative_id": "past_few_days", "ago_start_seconds": 604800, "ago_end_seconds": 259200 } }, { "id": "time_a_while_ago", "pattern": "早些时候|以前|之前", "description": "A while ago (beyond 30 days)", "enabled": true, "priority": 80, "metadata": { "type": "relative", "relative_id": "a_while_ago", "ago_start_seconds": -1, "ago_end_seconds": 2592000 } } ] } ] } linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/semanticquerybuilder.cpp000066400000000000000000000250711523477242100322440ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "semanticquerybuilder.h" #include #include #include #include #include DFM_SEARCH_BEGIN_NS SemanticQueryBuilder::SemanticQueryBuilder() = default; SemanticQueryBuilder::~SemanticQueryBuilder() = default; SemanticSearchPlan SemanticQueryBuilder::build(const ParsedIntent &intent) { SemanticSearchPlan plan; // Pass location info through to plan (searcher handles per-directory options) plan.searchDirectories = intent.searchDirectories(); plan.includeHidden = intent.includeHidden() || intent.hiddenOnly(); plan.hiddenOnly = intent.hiddenOnly(); plan.recentOnly = intent.recentOnly(); // When hiddenOnly is true, force filename-only search (no content/OCR) const SearchTarget effectiveTarget = intent.hiddenOnly() ? SearchTarget::FileNameOnly : intent.searchTarget(); // ── Recently-used files: route to RecentSearchEngine (DBus data source) ── // 最近使用记录不在 lucene++ 索引中,因此当 recentOnly 为 true 时, // 抑制所有 index-based 引擎,仅启动 Recent 引擎。 if (plan.recentOnly) { plan.recentOptions = buildBaseOptions(intent.timeConstraint(), intent.sizeConstraint()); // Recent 引擎不依赖 SearchMethod::Indexed,但保留 options 一致性 plan.recentOptions->setSearchMethod(SearchMethod::Realtime); FileNameOptionsAPI recentApi(*plan.recentOptions); if (!intent.fileExtensions().isEmpty()) { recentApi.setFileExtensions(intent.fileExtensions()); } if (intent.keywords().size() == 1) { plan.recentQuery = SearchFactory::createQuery(intent.keywords().first()); } else if (intent.keywords().size() > 1) { plan.recentQuery = SearchFactory::createQuery(intent.keywords(), SearchQuery::Type::Boolean); } else { plan.recentQuery = SearchFactory::createQuery(QStringLiteral("")); } return plan; // recentOnly 独占搜索路径,不启动其他引擎 } // Determine time field strategy if (intent.timeConstraint().isValid() && intent.timeConstraint().timeField() == TimeField::Unspecified) { // Time constraint exists but no action specified → search both birth and modify time plan.timeField = TimeField::Both; } else if (intent.timeConstraint().timeField() == TimeField::BirthTime) { plan.timeField = TimeField::BirthTime; } else if (intent.timeConstraint().timeField() == TimeField::ModifyTime) { plan.timeField = TimeField::ModifyTime; } else { plan.timeField = TimeField::ModifyTime; } // Base options shared across all search paths SearchOptions baseOpts = buildBaseOptions(intent.timeConstraint(), intent.sizeConstraint()); baseOpts.setSearchMethod(SearchMethod::Indexed); // Determine which search paths to enable based on user intent const bool enableFileName = (effectiveTarget == SearchTarget::All || effectiveTarget == SearchTarget::FileNameOnly); const bool enableContent = (effectiveTarget == SearchTarget::All || effectiveTarget == SearchTarget::ContentOnly); const bool enableOcr = enableContent; // OCR is a content search path // --- File name search --- if (enableFileName) { SearchOptions opts = baseOpts; FileNameOptionsAPI fnameApi(opts); if (!intent.fileExtensions().isEmpty()) { fnameApi.setFileExtensions(intent.fileExtensions()); } if (intent.keywords().size() == 1) { plan.fileNameQuery = SearchFactory::createQuery(intent.keywords().first()); } else if (intent.keywords().size() > 1) { plan.fileNameQuery = SearchFactory::createQuery(intent.keywords(), SearchQuery::Type::Boolean); } else { // 无关键字时的兜底 —— 必须区分是否有 fileExtensions,避免走错 indexedstrategy 分支: // - 有 fileExtensions("今天的图片"、"下载的pdf"):保持空 Simple query,让 // determineSearchType 走 Combined 分支,由 extQuery 作为主查询条件 // - 无 fileExtensions("桌面的文件"、"下载的隐藏文件"、"下载的文件"):用 // wildcard "*" 匹配所有文件名,让 pathPrefixQuery / hiddenOnlyQuery 作为 // 真正的过滤器(Wildcard 分支不会被 Combined 误导走 NGram) if (!intent.fileExtensions().isEmpty()) { plan.fileNameQuery = SearchFactory::createQuery(""); } else { plan.fileNameQuery = SearchFactory::createQuery(QStringLiteral("*"), SearchQuery::Type::Wildcard); } } plan.fileNameOptions = opts; } // --- Content search (when keywords available and content target enabled) --- if (enableContent) { const bool hasKeywords = !intent.keywords().isEmpty(); bool contentEnabled = hasKeywords; // Check if content index is available if (contentEnabled && !Global::isContentIndexAvailable()) { contentEnabled = false; } if (contentEnabled) { // Check minimum keyword length const int minLen = Global::kMinContentSearchKeywordLength; bool hasValidKeyword = false; for (const QString &kw : intent.keywords()) { if (kw.length() >= minLen) { hasValidKeyword = true; break; } } if (!hasValidKeyword) { contentEnabled = false; } } if (contentEnabled) { SearchOptions opts = baseOpts; ContentOptionsAPI contentApi(opts); // Enable filename-content mixed AND search contentApi.setFilenameContentMixedAndSearchEnabled(true); // Pass file extension filter to content search if (!intent.fileExtensions().isEmpty()) { contentApi.setFileExtensions(intent.fileExtensions()); } if (intent.keywords().size() == 1) { plan.contentQuery = SearchFactory::createQuery(intent.keywords().first()); } else if (intent.keywords().size() > 1) { plan.contentQuery = SearchFactory::createQuery(intent.keywords(), SearchQuery::Type::Boolean); } plan.contentOptions = opts; } } // --- OCR search (when keywords available and content target enabled) --- if (enableOcr) { const bool hasKeywords = !intent.keywords().isEmpty(); bool ocrEnabled = hasKeywords; if (ocrEnabled && !Global::isOcrTextIndexAvailable()) { ocrEnabled = false; } if (ocrEnabled) { SearchOptions opts = baseOpts; OcrTextOptionsAPI ocrApi(opts); // Enable filename-OCR content mixed AND search ocrApi.setFilenameOcrContentMixedAndSearchEnabled(true); // Pass file extension filter to OCR search if (!intent.fileExtensions().isEmpty()) { ocrApi.setFileExtensions(intent.fileExtensions()); } if (intent.keywords().size() == 1) { plan.ocrQuery = SearchFactory::createQuery(intent.keywords().first()); } else if (intent.keywords().size() > 1) { plan.ocrQuery = SearchFactory::createQuery(intent.keywords(), SearchQuery::Type::Boolean); } plan.ocrOptions = opts; } } return plan; } TimeRangeFilter SemanticQueryBuilder::buildTimeRangeFilter(const TimeConstraint &tc) const { TimeRangeFilter filter; if (!tc.isValid()) { return filter; } switch (tc.kind()) { case TimeConstraintKind::Preset: switch (tc.preset()) { case TimePreset::Today: filter.setToday(); break; case TimePreset::Yesterday: filter.setYesterday(); break; case TimePreset::DayBeforeYesterday: { const QDate today = QDate::currentDate(); const QDate dayBefore = today.addDays(-2); filter.setRange(QDateTime(dayBefore, QTime(0, 0, 0)), QDateTime(dayBefore, QTime(23, 59, 59))); break; } case TimePreset::ThisWeek: filter.setThisWeek(); break; case TimePreset::LastWeek: filter.setLastWeek(); break; case TimePreset::ThisMonth: filter.setThisMonth(); break; case TimePreset::LastMonth: filter.setLastMonth(); break; case TimePreset::ThisYear: filter.setThisYear(); break; case TimePreset::LastYear: filter.setLastYear(); break; } break; case TimeConstraintKind::Relative: filter.setRange(tc.customStart(), tc.customEnd()); break; case TimeConstraintKind::Custom: filter.setRange(tc.customStart(), tc.customEnd()); break; case TimeConstraintKind::None: break; } // Set time field on the filter if (tc.timeField() == TimeField::BirthTime || tc.timeField() == TimeField::ModifyTime) { filter.setTimeField(tc.timeField()); } else if (tc.timeField() == TimeField::Unspecified || tc.timeField() == TimeField::Both) { // No specific time field or both requested → search both birth and modify time filter.setTimeField(TimeField::Both); } return filter; } SizeRangeFilter SemanticQueryBuilder::buildSizeRangeFilter(const SizeConstraint &sc) const { SizeRangeFilter filter; if (!sc.isValid()) { return filter; } filter.setMin(sc.minSize()); filter.setMax(sc.maxSize()); filter.setIncludeLower(sc.includeLower()); filter.setIncludeUpper(sc.includeUpper()); return filter; } SearchOptions SemanticQueryBuilder::buildBaseOptions(const TimeConstraint &tc, const SizeConstraint &sc) const { SearchOptions opts; const TimeRangeFilter timeFilter = buildTimeRangeFilter(tc); if (timeFilter.isValid()) { opts.setTimeRangeFilter(timeFilter); } const SizeRangeFilter sizeFilter = buildSizeRangeFilter(sc); if (sizeFilter.isValid()) { opts.setSizeRangeFilter(sizeFilter); } return opts; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/semanticquerybuilder.h000066400000000000000000000037261523477242100317140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEMANTICQUERYBUILDER_H #define SEMANTICQUERYBUILDER_H #include #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief Search plan for all three search paths. */ struct SemanticSearchPlan { SearchQuery fileNameQuery; SearchOptions fileNameOptions; std::optional contentQuery; std::optional contentOptions; std::optional ocrQuery; std::optional ocrOptions; std::optional recentQuery; // 最近使用文件搜索 (DBus 数据源) std::optional recentOptions; TimeField timeField = TimeField::ModifyTime; // BirthTime, ModifyTime, or Both QStringList searchDirectories; // Empty = use default homePath bool includeHidden = false; // For trash directory bool hiddenOnly = false; // true = only return hidden files (filename only) bool recentOnly = false; // true = search recently-used files only (suppress index engines) }; /** * @brief Converts a ParsedIntent into concrete SearchQuery + SearchOptions for each path. */ class SemanticQueryBuilder { public: SemanticQueryBuilder(); ~SemanticQueryBuilder(); /** * @brief Build a search plan from parsed intent. * @param intent The parsed intent * @return A search plan with queries and options for each search path */ SemanticSearchPlan build(const ParsedIntent &intent); private: TimeRangeFilter buildTimeRangeFilter(const TimeConstraint &tc) const; SizeRangeFilter buildSizeRangeFilter(const SizeConstraint &sc) const; SearchOptions buildBaseOptions(const TimeConstraint &tc, const SizeConstraint &sc) const; }; DFM_SEARCH_END_NS #endif // SEMANTICQUERYBUILDER_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/semanticruleengine.cpp000066400000000000000000000165211523477242100316650ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "semanticruleengine.h" #include "ruleconfigloader.h" #include #include #include DFM_SEARCH_BEGIN_NS SemanticRuleEngine::SemanticRuleEngine(QObject *parent) : QObject(parent) { } SemanticRuleEngine::~SemanticRuleEngine() = default; bool SemanticRuleEngine::loadRules() { QMap newGroups; for (const QString &path : RuleConfigLoader::ruleFilePaths()) { QList loaded; if (!RuleConfigLoader::loadRuleFile(path, loaded)) { qWarning() << "Failed to load rule file:" << path; continue; } for (RuleGroup &group : loaded) { if (newGroups.contains(group.name)) { // Merge: later rules override by ID for (const Rule &rule : group.rules) { auto &existingRules = newGroups[group.name].rules; bool replaced = false; for (int i = 0; i < existingRules.size(); ++i) { if (existingRules[i].id == rule.id) { existingRules[i] = rule; replaced = true; break; } } if (!replaced) { existingRules.append(rule); } } } else { newGroups.insert(group.name, std::move(group)); } m_ruleFilePaths.insert(group.name, path); } } if (newGroups.isEmpty()) { qWarning() << "No rule files loaded, keeping existing rules"; return !m_groups.isEmpty(); } m_groups = newGroups; return true; } bool SemanticRuleEngine::loadRuleFile(const QString &path) { QList loaded; if (!RuleConfigLoader::loadRuleFile(path, loaded)) { qWarning() << "Failed to load rule file:" << path; return false; } for (RuleGroup &group : loaded) { if (m_groups.contains(group.name)) { // Merge: later rules override by ID for (const Rule &rule : group.rules) { auto &existingRules = m_groups[group.name].rules; bool replaced = false; for (int i = 0; i < existingRules.size(); ++i) { if (existingRules[i].id == rule.id) { existingRules[i] = rule; replaced = true; break; } } if (!replaced) { existingRules.append(rule); } } } else { m_groups.insert(group.name, std::move(group)); } m_ruleFilePaths.insert(group.name, path); } return true; } bool SemanticRuleEngine::match(const QString &group, const QString &input, QRegularExpressionMatch &outMatch, QString *outRuleId) { if (!m_groups.contains(group)) { return false; } const QList &rules = m_groups.value(group).rules; QList sorted = rules; std::stable_sort(sorted.begin(), sorted.end(), [](const Rule &a, const Rule &b) { return a.priority > b.priority; }); for (const Rule &rule : sorted) { if (!rule.enabled || !rule.regex.isValid()) { continue; } QRegularExpressionMatch m = rule.regex.match(input); if (m.hasMatch()) { outMatch = m; if (outRuleId) { *outRuleId = rule.id; } return true; } } return false; } QList SemanticRuleEngine::matchAll(const QString &group, const QString &input, QStringList *outRuleIds) { QList results; if (!m_groups.contains(group)) { return results; } const QList &rules = m_groups.value(group).rules; QList sorted = rules; std::stable_sort(sorted.begin(), sorted.end(), [](const Rule &a, const Rule &b) { return a.priority > b.priority; }); for (const Rule &rule : sorted) { if (!rule.enabled || !rule.regex.isValid()) { continue; } // Use globalMatch to find ALL occurrences of this rule's pattern. // This is important for noise rules (e.g., "和" appearing multiple times). auto it = rule.regex.globalMatch(input); while (it.hasNext()) { QRegularExpressionMatch m = it.next(); if (m.hasMatch()) { results.append(m); if (outRuleIds) { outRuleIds->append(rule.id); } } } } return results; } QVariantMap SemanticRuleEngine::ruleMetadata(const QString &group, const QString &ruleId) const { if (!m_groups.contains(group)) { return {}; } for (const Rule &rule : m_groups.value(group).rules) { if (rule.id == ruleId) { return rule.metadata; } } return {}; } QVariantMap SemanticRuleEngine::ruleMetadataById(const QString &ruleId) const { for (const RuleGroup &group : m_groups) { for (const Rule &rule : group.rules) { if (rule.id == ruleId) { return rule.metadata; } } } return {}; } bool SemanticRuleEngine::hasGroup(const QString &group) const { return m_groups.contains(group); } QStringList SemanticRuleEngine::ruleIds(const QString &group) const { const auto it = m_groups.constFind(group); if (it == m_groups.constEnd()) { return {}; } QStringList ids; for (const Rule &rule : it->rules) { ids.append(rule.id); } return ids; } QStringList SemanticRuleEngine::groupNames() const { return m_groups.keys(); } bool SemanticRuleEngine::parseRuleGroupStatic(const QJsonObject &groupObj, RuleGroup &outGroup) { if (!groupObj.contains("name") || !groupObj.contains("rules")) { return false; } outGroup.name = groupObj.value("name").toString(); outGroup.version = groupObj.value("version").toString("1.0.0"); outGroup.locale = groupObj.value("locale").toString(); const QJsonArray rulesArray = groupObj.value("rules").toArray(); for (const QJsonValue &rv : rulesArray) { const QJsonObject ruleObj = rv.toObject(); Rule rule; rule.id = ruleObj.value("id").toString(); rule.pattern = ruleObj.value("pattern").toString(); rule.description = ruleObj.value("description").toString(); rule.enabled = ruleObj.value("enabled").toBool(true); rule.priority = ruleObj.value("priority").toInt(0); rule.metadata = ruleObj.value("metadata").toVariant().toMap(); if (rule.pattern.isEmpty() || rule.id.isEmpty()) { continue; } rule.regex.setPattern(rule.pattern); rule.regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption); if (!rule.regex.isValid()) { qWarning() << "Invalid regex for rule" << rule.id << ":" << rule.regex.errorString(); continue; } outGroup.rules.append(rule); } return !outGroup.rules.isEmpty(); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/semanticruleengine.h000066400000000000000000000073121523477242100313300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEMANTICRULEENGINE_H #define SEMANTICRULEENGINE_H #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS struct Rule { QString id; QString pattern; QString description; bool enabled = true; int priority = 0; QVariantMap metadata; QRegularExpression regex; }; struct RuleGroup { QString name; QString version; QString locale; QList rules; }; /** * @brief Rule engine that loads regex rules from JSON config files. * * Provides match/matchAll operations with priority-based ordering. */ class SemanticRuleEngine : public QObject { Q_OBJECT public: explicit SemanticRuleEngine(QObject *parent = nullptr); ~SemanticRuleEngine() override; /** * @brief Load rules from all rule files in the config directory. * @return true if at least one valid rule file was loaded. */ bool loadRules(); /** * @brief Load rules from a specific rule file. * Useful for testing or loading custom rule files. * Merges with any previously loaded rules by group name. * @param path Absolute path to a JSON rule file * @return true if the file was loaded successfully */ bool loadRuleFile(const QString &path); /** * @brief Find the highest-priority matching rule in a group. * @param group The rule group name * @param input The input text to match against * @param outMatch Output: the regex match result * @param outRuleId Output: the matched rule's ID (optional) * @return true if a match was found */ bool match(const QString &group, const QString &input, QRegularExpressionMatch &outMatch, QString *outRuleId = nullptr); /** * @brief Find all matching rules in a group (priority order). * @param group The rule group name * @param input The input text to match against * @param outRuleIds Output: matched rule IDs (optional, parallel to result list) * @return List of all matches */ QList matchAll(const QString &group, const QString &input, QStringList *outRuleIds = nullptr); /** * @brief Get a rule's metadata by group and rule ID. */ QVariantMap ruleMetadata(const QString &group, const QString &ruleId) const; /** * @brief Get a rule's metadata by rule ID, searching across all groups. * @param ruleId The rule ID to look up * @return The metadata map, or empty if not found * * Unlike ruleMetadata(group, ruleId), this searches all groups. * Useful when the caller has a ruleId from consumedSpans but doesn't * know which group it belongs to. */ QVariantMap ruleMetadataById(const QString &ruleId) const; /** * @brief Get all rule IDs in a group. */ QStringList ruleIds(const QString &group) const; /** * @brief Check if a rule group exists and has enabled rules. */ bool hasGroup(const QString &group) const; /** * @brief Get the list of loaded rule group names. */ QStringList groupNames() const; /** * @brief Static helper to parse a rule group from JSON. */ static bool parseRuleGroupStatic(const QJsonObject &groupObj, RuleGroup &outGroup); private: bool parseRuleGroup(const QJsonObject &groupObj, RuleGroup &outGroup); QMap m_groups; QMap m_ruleFilePaths; // group name -> resolved file path }; DFM_SEARCH_END_NS #endif // SEMANTICRULEENGINE_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/semanticsearcher.cpp000066400000000000000000000413121523477242100313200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include "semanticsearcher_p.h" #include "semanticquerybuilder.h" #include "intentparser.h" #include "semanticruleengine.h" #include #include #include #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS namespace { bool hasKeywordRuleSpan(const ParsedIntent &intent) { for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId().startsWith(QStringLiteral("keyword_"))) { return true; } } return false; } bool hasTargetRuleSpan(const ParsedIntent &intent) { for (const MatchSpan &span : intent.consumedSpans()) { if (span.ruleId().startsWith(QStringLiteral("target_"))) { return true; } } return false; } // 计算语义维度个数。任何单维度单独都不算语义查询("下载"、"隐藏的"、"打开过的"、 // "图片"、"去年"、"大于10M" 都只是单维度),必须至少 2 个维度组合才表达完整搜索意图。 // 维度:location / hiddenOnly / recentOnly / time / size / fileType / keyword / target int countSemanticDimensions(const ParsedIntent &intent) { int count = 0; if (!intent.searchDirectories().isEmpty()) ++count; // location if (intent.hiddenOnly()) ++count; // hiddenOnly 属性 if (intent.recentOnly()) ++count; // recentOnly 属性 if (intent.timeConstraint().isValid()) ++count; // time 约束 if (intent.sizeConstraint().isValid()) ++count; // size 约束 if (!intent.fileExtensions().isEmpty()) ++count; // fileType if (!intent.keywords().isEmpty()) ++count; // keyword (unconsumed text) if (hasTargetRuleSpan(intent)) ++count; // target 名词 (文件/文件名/文件夹) // 当 specific filetype 与 general filetype 同时命中(如 "PPT文档"、"pdf文档"), // general 类型词(文档/表格/幻灯片)充当目标名词,贡献额外维度。 // 单独的 general filetype("文档")或单独的 specific filetype("PPT")不触发, // 仍为单维度,由 guardNonSemantic 拦下。 bool hasSpecificFt = false; bool hasGeneralFt = false; for (const MatchSpan &span : intent.consumedSpans()) { const QString rid = span.ruleId(); if (rid.startsWith(QLatin1String("filetype_"))) { if (rid.endsWith(QLatin1String("_general"))) hasGeneralFt = true; else hasSpecificFt = true; } } if (hasSpecificFt && hasGeneralFt) ++count; return count; } bool isSemanticIntent(const ParsedIntent &intent) { // 结构化关键字规则 (keyword_* span) 本身已表达明确搜索意图,单独成立 // (如 "包含测试的文件"、"文件名包含报告的文档") if (hasKeywordRuleSpan(intent)) { return true; } // 至少 2 个语义维度组合才算完整语义查询。 // 单维度(纯位置/纯属性/纯时间/纯大小/纯文件类型)不构成可执行的搜索意图, // 由 guardNonSemantic 拦下报 InvalidQuery,避免进入 doSearch 后空关键字报错。 return countSemanticDimensions(intent) >= 2; } } // namespace SemanticSearcherData::SemanticSearcherData(SemanticSearcher *q_ptr) : q(q_ptr), ruleEngine(new SemanticRuleEngine(q)), intentParser(new IntentParser(ruleEngine)), queryBuilder(new SemanticQueryBuilder()), timeoutTimer(new QTimer(q)) { timeoutTimer->setSingleShot(true); timeoutTimer->setInterval(timeoutSeconds * 1000); QObject::connect(timeoutTimer, &QTimer::timeout, q, [this]() { qWarning() << "Semantic search timed out after" << timeoutSeconds << "seconds"; doCancel(); }); // Load rules if (!ruleEngine->loadRules()) { qWarning() << "Failed to load semantic rules"; } } SemanticSearcherData::~SemanticSearcherData() { doCancel(); } SearchError SemanticSearcherData::guardNonSemantic(const QString &input) { if (q->isSemanticQuery(input)) return SearchError(); // valid semantic query qWarning() << "Non-semantic query:" << input << "- Use FileName/Content search instead"; return SearchError(SearchErrorCode::InvalidQuery); } void SemanticSearcherData::doSearch(const QString &naturalLanguage, const QStringList &searchDirectories) { if (naturalLanguage.trimmed().isEmpty()) { Q_EMIT q->errorOccurred(SearchError(SearchErrorCode::InvalidQuery)); return; } // Step 1: Validate + reset state cancelled.store(false); allResults.clear(); seenPaths.clear(); status.store(SearchStatus::Searching); Q_EMIT q->statusChanged(SearchStatus::Searching); // Step 2: Parse natural language into intent (before searchStarted // so that intentParsed listeners have the data when searchStarted fires) ParsedIntent intent; intentParser->parse(naturalLanguage, intent); Q_EMIT q->intentParsed(intent); Q_EMIT q->searchStarted(); // Step 3: Build search plan const SemanticSearchPlan plan = queryBuilder->build(intent); // Step 4: Determine search directories // Priority: NLP-parsed directories > caller-specified directories > home directory // NLP 解析出的路径是用户意图的最直接表达(如"在文档里找xxx"), // 应优先于调用者传入的默认路径。 QStringList dirs; if (!plan.searchDirectories.isEmpty()) { dirs = plan.searchDirectories; } else if (!searchDirectories.isEmpty()) { dirs = searchDirectories; } else { dirs = QStringList { QDir::homePath() }; } excludeNamePatterns.clear(); for (const MatchSpan &span : intent.consumedSpans()) { const QVariantMap meta = ruleEngine->ruleMetadataById(span.ruleId()); const QVariant ep = meta.value("exclude_patterns"); if (ep.userType() == QMetaType::QVariantList) { for (const auto &v : ep.toList()) { const QString p = v.toString(); if (!p.isEmpty() && !excludeNamePatterns.contains(p)) excludeNamePatterns.append(p); } } } // Step 5: Set up signal/slot handlers auto onFinished = [this](const SearchResultList &r) { onEngineFinished(r); }; auto onError = [this](const SearchError &e) { onEngineError(e); }; // Step 6: Clean up any previous search engines pendingFinishCount.store(0); for (SearchEngine *e : engines) { e->deleteLater(); } engines.clear(); // Step 7: Helper to prepare options with multi-path and hidden settings auto prepareOptions = [&dirs, &plan](const SearchOptions &baseOpts) -> SearchOptions { SearchOptions opts = baseOpts; opts.setSearchPaths(dirs); if (plan.includeHidden) { opts.setIncludeHidden(true); } if (plan.hiddenOnly) { opts.setHiddenOnly(true); } return opts; }; // Apply caller-level options auto applyCallerOptions = [this](SearchOptions &opts) { if (detailedResultsEnabled) { opts.setDetailedResultsEnabled(true); } if (maxResults > 0) { opts.setMaxResults(maxResults); } if (!excludedPaths.isEmpty()) { opts.setSearchExcludedPaths(excludedPaths); } }; // Step 8: Launch engines based on plan // TimeField::Both is no longer expanded here; it is handled by the Lucene strategy layer. // Multiple directories are passed via setSearchPaths(). // ── Recently-used files: DBus data source, exclusive path ── // recentOnly 抑制所有 index-based 引擎(数据不在索引中)。 if (plan.recentOnly && plan.recentQuery.has_value() && plan.recentOptions.has_value()) { SearchOptions recentOpts = *plan.recentOptions; applyCallerOptions(recentOpts); createAndLaunchEngine(SearchType::Recent, *plan.recentQuery, recentOpts, onFinished, onError); } else { // ── Index-based engines: FileName, Content, OCR ── // File name search (always, if index is ready) if (Global::isFileNameIndexReadyForSearch()) { SearchOptions fnameOpts = prepareOptions(plan.fileNameOptions); applyCallerOptions(fnameOpts); createAndLaunchEngine(SearchType::FileName, plan.fileNameQuery, fnameOpts, onFinished, onError); } // Content search if (plan.contentQuery.has_value() && plan.contentOptions.has_value()) { SearchOptions contentOpts = prepareOptions(*plan.contentOptions); applyCallerOptions(contentOpts); createAndLaunchEngine(SearchType::Content, *plan.contentQuery, contentOpts, onFinished, onError); } // OCR search if (plan.ocrQuery.has_value() && plan.ocrOptions.has_value()) { SearchOptions ocrOpts = prepareOptions(*plan.ocrOptions); applyCallerOptions(ocrOpts); createAndLaunchEngine(SearchType::Ocr, *plan.ocrQuery, ocrOpts, onFinished, onError); } } // end of else (index-based engines) // Step 9: Handle no-engine case if (pendingFinishCount.load() == 0) { timeoutTimer->stop(); status.store(SearchStatus::Finished); Q_EMIT q->statusChanged(SearchStatus::Finished); Q_EMIT q->searchFinished({}); } else { if (timeoutSeconds > 0) { timeoutTimer->start(); } } } void SemanticSearcherData::createAndLaunchEngine( SearchType type, const SearchQuery &query, const SearchOptions &options, std::function onFinished, std::function onError) { SearchEngine *engine = SearchEngine::create(type, q); engine->setSearchOptions(options); QObject::connect(engine, &SearchEngine::searchFinished, q, onFinished); QObject::connect(engine, &SearchEngine::errorOccurred, q, onError); engines.append(engine); pendingFinishCount.fetch_add(1); engine->search(query); } void SemanticSearcherData::onEngineFinished(const SearchResultList &results) { for (const SearchResult &r : results) { if (!excludeNamePatterns.isEmpty()) { const QString fileName = QFileInfo(r.path()).fileName(); bool excluded = std::any_of(excludeNamePatterns.cbegin(), excludeNamePatterns.cend(), [&fileName](const QString &pattern) { return QDir::match(pattern, fileName); }); if (excluded) continue; } if (!seenPaths.contains(r.path())) { seenPaths.insert(r.path()); allResults.append(r); } } if (pendingFinishCount.fetch_sub(1) == 1) { timeoutTimer->stop(); if (maxResults > 0 && allResults.size() > maxResults) { allResults = allResults.mid(0, maxResults); } if (cancelled.load()) { status.store(SearchStatus::Cancelled); Q_EMIT q->statusChanged(SearchStatus::Cancelled); Q_EMIT q->searchCancelled(); } else { status.store(SearchStatus::Finished); Q_EMIT q->statusChanged(SearchStatus::Finished); Q_EMIT q->searchFinished(allResults); } } } void SemanticSearcherData::onEngineError(const SearchError &error) { qWarning() << "Search error:" << error.message(); onEngineFinished({}); } void SemanticSearcherData::doCancel() { cancelled.store(true); timeoutTimer->stop(); for (SearchEngine *e : engines) { e->cancel(); } } // --- SemanticSearcher public API --- SemanticSearcher::SemanticSearcher(QObject *parent) : QObject(parent), d_ptr(new SemanticSearcherData(this)) { } SemanticSearcher::~SemanticSearcher() = default; SearchStatus SemanticSearcher::status() const { return d_ptr->status.load(); } void SemanticSearcher::setSearchTimeout(int seconds) { d_ptr->timeoutSeconds = seconds; d_ptr->timeoutTimer->setInterval(seconds * 1000); } int SemanticSearcher::searchTimeout() const { return d_ptr->timeoutSeconds; } void SemanticSearcher::search(const QString &naturalLanguage) { if (d_ptr->status.load() == SearchStatus::Searching) { qWarning() << "Search already in progress"; return; } if (const auto err = d_ptr->guardNonSemantic(naturalLanguage); err.isError()) { Q_EMIT errorOccurred(err); return; } d_ptr->doSearch(naturalLanguage, {}); } void SemanticSearcher::search(const QString &naturalLanguage, const QStringList &searchDirectories) { if (d_ptr->status.load() == SearchStatus::Searching) { qWarning() << "Search already in progress"; return; } if (const auto err = d_ptr->guardNonSemantic(naturalLanguage); err.isError()) { Q_EMIT errorOccurred(err); return; } d_ptr->doSearch(naturalLanguage, searchDirectories); } ParsedIntent SemanticSearcher::parseIntent(const QString &input) const { ParsedIntent intent; if (input.trimmed().isEmpty()) { return intent; // default-constructed: all constraints invalid/empty } d_ptr->intentParser->parse(input, intent); return intent; } bool SemanticSearcher::isSemanticQuery(const QString &input) const { return isSemanticIntent(parseIntent(input)); } bool SemanticSearcher::isSemanticQuery(const ParsedIntent &intent) const { return isSemanticIntent(intent); } void SemanticSearcher::cancel() { d_ptr->doCancel(); } void SemanticSearcher::setDetailedResultsEnabled(bool enable) { d_ptr->detailedResultsEnabled = enable; } bool SemanticSearcher::isDetailedResultsEnabled() const { return d_ptr->detailedResultsEnabled; } void SemanticSearcher::setMaxResults(int count) { d_ptr->maxResults = count; } int SemanticSearcher::maxResults() const { return d_ptr->maxResults; } void SemanticSearcher::setSearchExcludedPaths(const QStringList &paths) { d_ptr->excludedPaths = paths; } SearchResultExpected SemanticSearcher::searchSync(const QString &naturalLanguage) { return searchSync(naturalLanguage, {}); } SearchResultExpected SemanticSearcher::searchSync(const QString &naturalLanguage, const QStringList &searchDirectories) { if (d_ptr->status.load() == SearchStatus::Searching) { qWarning() << "Search already in progress"; return Dtk::Core::DUnexpected(SearchError(SearchErrorCode::InvalidQuery)); } if (naturalLanguage.trimmed().isEmpty()) { return Dtk::Core::DUnexpected(SearchError(SearchErrorCode::InvalidQuery)); } if (const auto err = d_ptr->guardNonSemantic(naturalLanguage); err.isError()) { return Dtk::Core::DUnexpected(err); } SearchResultList results; bool hasError = false; SearchError lastError; bool cancelled = false; bool done = false; QEventLoop eventLoop; // Use a shared guard flag so late-arriving signals after eventLoop exits are harmless. // The internal doSearch timeout mechanism is relied upon for actual cancellation. QObject::connect(this, &SemanticSearcher::searchFinished, this, [&](const SearchResultList &r) { if (!done) { results = r; done = true; eventLoop.quit(); } }); QObject::connect(this, &SemanticSearcher::searchCancelled, this, [&]() { if (!done) { cancelled = true; done = true; eventLoop.quit(); } }); QObject::connect(this, &SemanticSearcher::errorOccurred, this, [&](const SearchError &error) { if (!done) { hasError = true; lastError = error; done = true; eventLoop.quit(); } }); // Start the async search (uses internal timeout mechanism) d_ptr->doSearch(naturalLanguage, searchDirectories); // Block until completion, cancellation, or error eventLoop.exec(); if (cancelled) { return results; } if (hasError) { return Dtk::Core::DUnexpected(lastError); } return results; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/semantic/semanticsearcher_p.h000066400000000000000000000063261523477242100313120ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEMANTICSEARCHER_P_H #define SEMANTICSEARCHER_P_H #include #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS class SemanticRuleEngine; class IntentParser; class SemanticQueryBuilder; class SemanticSearchPlan; class SemanticSearcher; class SemanticSearcherData { public: explicit SemanticSearcherData(SemanticSearcher *q); ~SemanticSearcherData(); void doSearch(const QString &naturalLanguage, const QStringList &searchDirectories); void doCancel(); /** * @brief Guard non-semantic queries at public entry points. * * Calls q->isSemanticQuery() to avoid duplicating intent parsing logic. * * @param input The query string to validate. * @return An empty SearchError if the query is valid (caller proceeds). * A non-empty SearchError if rejected (caller must propagate the error and return). */ SearchError guardNonSemantic(const QString &input); /** * @brief Create, configure, and launch a search engine * * Creates a SearchEngine of the given type, sets its options, connects * signal/slot handlers, appends it to the engines list, increments the * pending finish counter, and starts the search. * * @param type The search engine type (FileName, Content, or Ocr) * @param query The search query to execute * @param options The search options (including multi-path and time filter) * @param onFinished Callback for engine completion and result collection * @param onError Callback for error handling */ void createAndLaunchEngine(SearchType type, const SearchQuery &query, const SearchOptions &options, std::function onFinished, std::function onError); void onEngineFinished(const SearchResultList &results); void onEngineError(const SearchError &error); SemanticSearcher *q = nullptr; // State std::atomic status { SearchStatus::Ready }; std::atomic cancelled { false }; int timeoutSeconds = 60; // Core components (owned) SemanticRuleEngine *ruleEngine = nullptr; IntentParser *intentParser = nullptr; SemanticQueryBuilder *queryBuilder = nullptr; // Sub-engines (owned per search, parented to q for auto-cleanup) QList engines; std::atomic pendingFinishCount { 0 }; // Result collection SearchResultList allResults; QSet seenPaths; // Timeout QTimer *timeoutTimer = nullptr; // Options forwarded from caller bool detailedResultsEnabled = false; int maxResults = 0; // 0 = unlimited QStringList excludedPaths; // directories to exclude from search QStringList excludeNamePatterns; // glob patterns to filter from results (e.g. *_thumb.jpg) }; DFM_SEARCH_END_NS #endif // SEMANTICSEARCHER_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/textsearch/000077500000000000000000000000001523477242100256425ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/textsearch/textsearchapi.cpp000066400000000000000000000074711523477242100312230ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include DFM_SEARCH_BEGIN_NS // ==================== TextSearchOptionsAPI ==================== TextSearchOptionsAPI::TextSearchOptionsAPI(SearchOptions &options) : m_options(options) { } void TextSearchOptionsAPI::setMaxPreviewLength(int length) { m_options.setCustomOption("maxPreviewLength", length); } int TextSearchOptionsAPI::maxPreviewLength() const { return m_options.customOption("maxPreviewLength").toInt(); } void TextSearchOptionsAPI::setSearchResultHighlightEnabled(bool enable) { m_options.setCustomOption("searchResultHighlight", enable); } bool TextSearchOptionsAPI::isSearchResultHighlightEnabled() const { return m_options.customOption("searchResultHighlight").toBool(); } void TextSearchOptionsAPI::setFullTextRetrievalEnabled(bool enable) { m_options.setCustomOption("fullTextRetrieval", enable); } bool TextSearchOptionsAPI::isFullTextRetrievalEnabled() const { return m_options.customOption("fullTextRetrieval").toBool(); } void TextSearchOptionsAPI::setFileExtensions(const QStringList &extensions) { m_options.setCustomOption("fileExtensions", extensions); } QStringList TextSearchOptionsAPI::fileExtensions() const { return m_options.customOption("fileExtensions").toStringList(); } void TextSearchOptionsAPI::setFilenameKeyword(const QString &keyword) { m_options.setCustomOption("filenameKeyword", keyword); } QString TextSearchOptionsAPI::filenameKeyword() const { return m_options.customOption("filenameKeyword").toString(); } // ==================== TextSearchResultAPI ==================== void TextSearchResultAPI::setFileSizeBytes(qint64 bytes) { m_result.setCustomAttribute("fileSizeBytes", bytes); } qint64 TextSearchResultAPI::fileSizeBytes() const { return m_result.customAttribute("fileSizeBytes").toLongLong(); } void TextSearchResultAPI::setCharCount(int count) { m_result.setCustomAttribute("charCount", count); } int TextSearchResultAPI::charCount() const { return m_result.customAttribute("charCount").toInt(); } TextSearchResultAPI::TextSearchResultAPI(SearchResult &result) : m_result(result) { } QString TextSearchResultAPI::highlightedContent() const { return m_result.customAttribute("highlightedContent").toString(); } void TextSearchResultAPI::setHighlightedContent(const QString &content) { m_result.setCustomAttribute("highlightedContent", content); } QString TextSearchResultAPI::filename() const { return m_result.customAttribute("filename").toString(); } void TextSearchResultAPI::setFilename(const QString &name) { m_result.setCustomAttribute("filename", name); } bool TextSearchResultAPI::isHidden() const { return m_result.customAttribute("isHidden").toBool(); } void TextSearchResultAPI::setIsHidden(bool hidden) { m_result.setCustomAttribute("isHidden", hidden); } void TextSearchResultAPI::setModifyTimestamp(qint64 timestamp) { m_result.setCustomAttribute("modifyTimestamp", timestamp); } qint64 TextSearchResultAPI::modifyTimestamp() const { return m_result.customAttribute("modifyTimestamp").toLongLong(); } QString TextSearchResultAPI::modifyTimeString() const { qint64 ts = modifyTimestamp(); return ts > 0 ? TimeResultAPI::formatTimestamp(ts) : QString(); } void TextSearchResultAPI::setBirthTimestamp(qint64 timestamp) { m_result.setCustomAttribute("birthTimestamp", timestamp); } qint64 TextSearchResultAPI::birthTimestamp() const { return m_result.customAttribute("birthTimestamp").toLongLong(); } QString TextSearchResultAPI::birthTimeString() const { qint64 ts = birthTimestamp(); return ts > 0 ? TimeResultAPI::formatTimestamp(ts) : QString(); } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/000077500000000000000000000000001523477242100246305ustar00rootroot00000000000000linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/cancellablecollector.cpp000066400000000000000000000045061523477242100314750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "cancellablecollector.h" #include using namespace Lucene; DFM_SEARCH_BEGIN_NS CancellableCollector::CancellableCollector(std::atomic *cancelled, int32_t maxDocs) : m_cancelled(cancelled), m_maxDocs(maxDocs), m_docBase(0), m_totalHits(0) { // 预分配空间以提高性能 m_scoreDocs = Collection::newInstance(); } void CancellableCollector::setScorer(const ScorerPtr &scorer) { if (m_cancelled && m_cancelled->load()) { // 抛出异常中断搜索过程 throw SearchCancelledException(); } m_scorer = scorer; } void CancellableCollector::collect(int32_t doc) { // 关键:每次收集文档时检查取消标志 // 这是实现搜索可中断的核心机制 if (m_cancelled && m_cancelled->load()) { // 抛出异常中断搜索过程 throw SearchCancelledException(); } m_totalHits++; // 只收集不超过最大数量的文档 if (m_scoreDocs.size() < m_maxDocs) { try { // 获取当前文档的评分 double score = m_scorer ? m_scorer->score() : 0.0; // 创建评分文档对象 ScoreDocPtr scoreDoc = newLucene(m_docBase + doc, score); // 添加到结果集合 m_scoreDocs.add(scoreDoc); } catch (const LuceneException &e) { qWarning() << "Error collecting document:" << QString::fromStdWString(e.getError()); } catch (const std::exception &e) { qWarning() << "Standard exception in collect:" << e.what(); } } } void CancellableCollector::setNextReader(const IndexReaderPtr &reader, int32_t docBase) { Q_UNUSED(reader); if (m_cancelled && m_cancelled->load()) { // 抛出异常中断搜索过程 throw SearchCancelledException(); } // 设置当前段的文档基址,用于计算全局文档 ID m_docBase = docBase; } bool CancellableCollector::acceptsDocsOutOfOrder() { // 返回 true 表示可以接受乱序的文档,这可以提高性能 // 因为我们不依赖文档的顺序 return true; } Collection CancellableCollector::getScoreDocs() const { return m_scoreDocs; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/cancellablecollector.h000066400000000000000000000041751523477242100311440ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CANCELLABLE_COLLECTOR_H #define CANCELLABLE_COLLECTOR_H #include #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief 搜索取消异常 * 当搜索被用户取消时抛出此异常 */ class SearchCancelledException : public std::exception { public: SearchCancelledException() = default; const char *what() const noexcept override { return "Search cancelled by user"; } }; /** * @brief 可取消的文档收集器 * * 继承自 Lucene::Collector,在收集文档过程中检查取消标志 * 如果检测到取消请求,立即抛出 SearchCancelledException 中断搜索 */ class CancellableCollector : public Lucene::Collector { public: /** * @brief 构造函数 * @param cancelled 取消标志的指针(atomic bool) * @param maxDocs 最大文档数量限制 */ CancellableCollector(std::atomic *cancelled, int32_t maxDocs); ~CancellableCollector() override = default; // Lucene::Collector 接口实现 void setScorer(const Lucene::ScorerPtr &scorer) override; void collect(int32_t doc) override; void setNextReader(const Lucene::IndexReaderPtr &reader, int32_t docBase) override; bool acceptsDocsOutOfOrder() override; /** * @brief 获取收集到的文档列表 * @return 按评分排序的文档列表 */ Lucene::Collection getScoreDocs() const; /** * @brief 获取总命中数量 * @return 总命中文档数 */ int32_t getTotalHits() const { return m_totalHits; } private: std::atomic *m_cancelled; // 取消标志指针 Lucene::Collection m_scoreDocs; // 收集的文档 int32_t m_maxDocs; // 最大文档数 int32_t m_docBase; // 当前段的文档基址 int32_t m_totalHits; // 总命中数 Lucene::ScorerPtr m_scorer; // 评分器 }; DFM_SEARCH_END_NS #endif // CANCELLABLE_COLLECTOR_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/contenthighlighter.cpp000066400000000000000000000257131523477242100312350ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "contenthighlighter.h" #include #include #include #include #include #include #include #include #include using namespace Lucene; DFM_SEARCH_BEGIN_NS namespace ContentHighlighter { namespace { struct KeywordMatch { int position; int length; QString keyword; }; int positioningBeforeLength(int maxLength, int positioningMaxLength) { const int effectivePositioningLength = qMax(30, positioningMaxLength); if (maxLength > 0) { return qMin(effectivePositioningLength / 2, maxLength / 2); } return effectivePositioningLength / 2; } /** * @brief 检查给定位置是否是段落的开头 * @param content 原始文本内容 * @param position 需要检查的位置 * @return 如果是段落开头返回true,否则返回false */ bool isParagraphStart(const QString &content, int position) { // 如果位置为0,则一定是文档开头 if (position == 0) { return true; } // 如果位置超出内容范围,返回false if (position >= content.length()) { return false; } // 向前搜索,跳过所有空白字符 int lookBackPos = position - 1; while (lookBackPos >= 0 && content.at(lookBackPos).isSpace() && content.at(lookBackPos) != '\n' && content.at(lookBackPos) != '\r') { lookBackPos--; } // 如果找到段落结束符或已到文档开头,则认为是段落开头 if (lookBackPos < 0) { return true; // 文档开头 } QChar prevChar = content.at(lookBackPos); return (prevChar == '\n' || prevChar == '\r'); } int findOptimalStartPosition(const QString &content, int keywordPos, int maxLength) { if (keywordPos == 0) return 0; // 计算关键词前后的理想长度 int idealBeforeLength = maxLength / 2; int idealAfterLength = maxLength - idealBeforeLength; // 如果关键词在文档开头附近,直接返回0 if (keywordPos <= idealBeforeLength) { return 0; } // 如果关键词在文档末尾附近,确保有足够的空间显示关键词前的文本 if (keywordPos + idealAfterLength >= content.length()) { return qMax(0, content.length() - maxLength); } // 尝试找到最近的换行符作为起始点 int start = keywordPos - idealBeforeLength; int lastNewline = content.lastIndexOf('\n', keywordPos); // 如果找到换行符,并且它在理想起始点之后,则使用换行符作为起始点 if (lastNewline != -1 && lastNewline > start) { start = lastNewline + 1; } // 确保起始位置不会导致文本超出maxLength if (keywordPos - start + idealAfterLength > maxLength) { start = keywordPos - (maxLength - idealAfterLength); } return qMax(0, start); } int findOptimalEndPosition(const QString &content, int keywordPos, int keywordLength, int maxLength, int startPos) { // 计算关键词前后的理想长度 int idealBeforeLength = keywordPos - startPos; int idealAfterLength = maxLength - idealBeforeLength; // 如果关键词在文档末尾,直接返回文档末尾 if (keywordPos + keywordLength >= content.length()) { return content.length(); } // 尝试找到下一个换行符作为结束点 int end = keywordPos + keywordLength + idealAfterLength; int nextNewline = content.indexOf('\n', keywordPos + keywordLength); // 如果找到换行符,并且它在理想结束点之前,则使用换行符作为结束点 if (nextNewline != -1 && nextNewline < end) { end = nextNewline; } // 确保结束位置不会导致文本超出maxLength if (end - startPos > maxLength) { end = startPos + maxLength; } return qMin(content.length(), end); } QString highlightKeyword(const QString &text, const QString &keyword) { if (text.isEmpty() || keyword.isEmpty()) return text; QString result = text; int pos = 0; const int keywordLength = keyword.length(); while ((pos = result.indexOf(keyword, pos, Qt::CaseInsensitive)) != -1) { result.insert(pos + keywordLength, ""); result.insert(pos, ""); pos += keywordLength + 7; } return result; } KeywordMatch findFirstKeywordMatch(const QString &content, const QStringList &keywords) { KeywordMatch match = { -1, 0, QString() }; for (const QString &keyword : keywords) { int pos = content.indexOf(keyword, 0, Qt::CaseInsensitive); if (pos != -1 && (match.position == -1 || pos < match.position)) { match.position = pos; match.length = keyword.length(); match.keyword = keyword; break; } } return match; } KeywordMatch findEarliestKeywordMatch(const QString &content, const QStringList &keywords) { KeywordMatch match = { -1, 0, QString() }; for (const QString &keyword : keywords) { if (keyword.isEmpty()) { continue; } const int pos = content.indexOf(keyword, 0, Qt::CaseInsensitive); if (pos != -1 && (match.position == -1 || pos < match.position)) { match.position = pos; match.length = keyword.length(); match.keyword = keyword; } } return match; } } // namespace QString customHighlight(const QStringList &keywords, const QString &content, int maxLength, bool enableHtml, int positioningMaxLength) { if (content.isEmpty() || keywords.isEmpty()) { return QString(); } // Ensure there's at least one non-empty keyword. // findFirstKeywordMatch handles empty strings in the list, but if all are empty, it's like no keywords. bool hasValidKeyword = false; for (const QString &kw : keywords) { if (!kw.isEmpty()) { hasValidKeyword = true; break; } } if (!hasValidKeyword) { return QString(); } KeywordMatch match = findFirstKeywordMatch(content, keywords); if (match.position == -1) { // No keyword found in content. // As per problem, if no keyword, no range to show. // Alternative: return content.left(maxLength).simplified(); if some default text is needed. return QString(); } // If the keyword itself is longer than or equal to the final desired maxLength if (match.length >= maxLength) { if (enableHtml) { // Highlight the keyword itself if HTML is enabled return highlightKeyword(match.keyword, match.keyword); } return match.keyword; // Return the keyword as is (original behavior) } // Enforce minimum of 30 for the positioning window const int effectivePositioningLength = qMax(30, positioningMaxLength); // 1. Calculate the optimal start position. // This start position is determined based on making the keyword visible // and well-positioned within the positioning window. int optimalStart = findOptimalStartPosition(content, match.position, effectivePositioningLength); // 2. Calculate the optimal end position. // This uses the `optimalStart` calculated above and extends the snippet // up to the overall `maxLength` (e.g., 200 chars for the final result), // or the end of the content, whichever is first. int optimalEnd = findOptimalEndPosition(content, match.position, match.length, maxLength, optimalStart); // Safeguard: If somehow optimalStart >= optimalEnd, try to at least show the keyword. // This can happen if content is very short or keyword is at the very end and newline logic truncates aggressively. if (optimalStart >= optimalEnd && match.length > 0) { optimalStart = match.position; optimalEnd = qMin(content.length(), match.position + match.length); // If still bad, it means something is wrong, but mid(X,0) or mid(X, negative) is empty string } QString resultSnippet = content.mid(optimalStart, optimalEnd - optimalStart); // .simplified() removes leading/trailing whitespace and replaces multiple internal whitespaces with one. // This is applied *after* extraction. resultSnippet = resultSnippet.simplified(); if (enableHtml) { // Highlight all keywords from the list that appear in the *extracted and simplified* snippet. // Create a temporary string for highlighting because highlightKeyword modifies the string it's given, // and we are iterating. QString tempHighlightedSnippet = resultSnippet; for (const QString &kwToHighlight : keywords) { if (!kwToHighlight.isEmpty()) { tempHighlightedSnippet = highlightKeyword(tempHighlightedSnippet, kwToHighlight); } } resultSnippet = tempHighlightedSnippet; } // 只有在段落开头被截断时才添加省略号 if (optimalStart > 0 && !isParagraphStart(content, optimalStart)) { // Unicode 水平省略号字符 (U+2026) static constexpr QChar kEllipsis(8230); resultSnippet = QString(kEllipsis) + resultSnippet; } return resultSnippet; } PlainSnippetResult plainSnippet(const QStringList &keywords, const QString &content, int maxLength, int positioningMaxLength) { PlainSnippetResult result; if (content.isEmpty() || keywords.isEmpty()) { return result; } const KeywordMatch match = findEarliestKeywordMatch(content, keywords); if (match.position == -1) { return result; } int snippetStart = qMax(0, match.position - positioningBeforeLength(maxLength, positioningMaxLength)); if (maxLength > 0 && content.length() - snippetStart < maxLength) { snippetStart = qMax(0, content.length() - maxLength); } const int snippetLength = maxLength > 0 ? qMin(maxLength, content.length() - snippetStart) : content.length() - snippetStart; result.content = content.mid(snippetStart, snippetLength); result.snippetOffset = snippetStart; return result; } QString previewSnippet(const QString &content, int offset, int maxLength, const QString &keyword, int *keywordOffset) { if (keywordOffset) { *keywordOffset = -1; } if (content.isEmpty() || offset < 0) { return {}; } if (keyword.isEmpty()) { // 无 keyword:直接从 offset 截取 return maxLength > 0 ? content.mid(offset, maxLength) : content.mid(offset); } // 有 keyword:从 offset 开始搜索,找到后从匹配位置截取 int pos = content.indexOf(keyword, offset, Qt::CaseInsensitive); if (pos == -1) { return {}; } if (keywordOffset) { *keywordOffset = pos; } return maxLength > 0 ? content.mid(pos, maxLength) : content.mid(pos); } } // namespace ContentHighlighter DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/contenthighlighter.h000066400000000000000000000077771523477242100307140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef CONTENTHIGHLIGHTER_H #define CONTENTHIGHLIGHTER_H #include #include #include #include DFM_SEARCH_BEGIN_NS namespace ContentHighlighter { struct PlainSnippetResult { QString content; int snippetOffset = -1; }; /** * @brief Extracts and highlights the most relevant text snippet from content based on the provided keywords. * * This function searches for the first occurrence(s) of the specified keywords within the input content and returns * a concise, contextually meaningful snippet containing the match(es). The snippet is intelligently trimmed based * on paragraph structure (if present) or character length, and keywords are optionally wrapped with tags for * highlighting. * * Behavior Details: * - Case-insensitive matching is used. * - Wildcards '*' and '?' are supported in keywords. * - If content contains line breaks, the entire matching line is returned. * - Otherwise, a substring centered around the first match is returned, constrained by maxLength. * - If multiple keywords are given, only the first matching fragment is returned, with as many highlights as possible within the limit. * - No match results in an empty string. * * @param keywords A list of search keywords (supports wildcards *, ?). * @param content The original document content to be searched. * @param maxLength The maximum length of the returned snippet (used when no line breaks are present). * @param enableHtml Whether to wrap matched keywords with tags for highlighting. * @param positioningMaxLength Keyword positioning window size for finding the optimal snippet start/end (min 30). * @return A snippet of content with matched keywords highlighted, or an empty string if no match is found. */ QString customHighlight(const QStringList &keywords, const QString &content, int maxLength, bool enableHtml, int positioningMaxLength = 30); /** * @brief Extracts a plain-text snippet for CLI verbose output. * * Unlike customHighlight(), this helper preserves the original content bytes * in the returned snippet: it does not simplify whitespace, insert HTML tags, * or prepend ellipsis. The snippet start is returned so callers can expose the * offset in the original full text. * * @param keywords Search keywords used to anchor the snippet. * @param content Original document content. * @param maxLength Maximum length of the returned snippet. <= 0 means until EOF. * @param positioningMaxLength Window used to position the first content match. * @return PlainSnippetResult with snippet text and snippetOffset, or empty/-1 if no content match exists. */ PlainSnippetResult plainSnippet(const QStringList &keywords, const QString &content, int maxLength, int positioningMaxLength = 30); /** * @brief 精确预览:从 offset 位置截取内容,或从 offset 搜索 keyword 后从匹配位置截取 * * 无 keyword: content.mid(offset, maxLength) * 有 keyword: content.indexOf(keyword, offset) → 从匹配位置截取 maxLength * 不做 simplified,不加省略号,不高亮 * * maxLength <= 0 表示无限制,返回从 offset(或 keyword 匹配位置)开始的全部剩余内容。 * * @param content 原始文档内容 * @param offset 内容偏移值(默认0) * @param maxLength 最大截取长度,<= 0 表示无限制 * @param keyword 搜索关键词(可选) * @param keywordOffset 输出 keyword 在全文中的匹配位置(-1 if 无 keyword 或未匹配) * @return 截取的内容片段,找不到 keyword 或 offset 越界时返回空字符串 */ QString previewSnippet(const QString &content, int offset, int maxLength, const QString &keyword = {}, int *keywordOffset = nullptr); } // namespace ContentHighlighter DFM_SEARCH_END_NS #endif // CONTENTHIGHLIGHTER_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/contentretriever.cpp000066400000000000000000000447541523477242100307540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "utils/contenthighlighter.h" #include "utils/highlightoptions_p.h" #include "utils/previewoptions_p.h" #include "utils/previewresult_p.h" #include "utils/searchutility.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Lucene; DFM_SEARCH_BEGIN_NS namespace { const wchar_t *contentFieldName(SearchType type) { return (type == SearchType::Ocr) ? LuceneFieldNames::OcrText::kOcrContents : LuceneFieldNames::Content::kContents; } const wchar_t *pathFieldName(SearchType type) { return (type == SearchType::Ocr) ? LuceneFieldNames::OcrText::kPath : LuceneFieldNames::Content::kPath; } QString defaultIndexDirectoryForType(SearchType type) { return (type == SearchType::Ocr) ? Global::ocrTextIndexDirectory() : Global::contentIndexDirectory(); } QString storedContentFromDocument(const DocumentPtr &doc, SearchType type) { if (!doc) { return {}; } const String contentField = doc->get(contentFieldName(type)); if (contentField.empty()) { return {}; } return QString::fromStdWString(contentField); } QStringList splitKeywords(const QString &keyword) { if (keyword.isEmpty()) { return {}; } #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) return keyword.split(',', Qt::SkipEmptyParts); #else return keyword.split(',', QString::SkipEmptyParts); #endif } DocumentPtr findDocumentByPath(const SearcherPtr &searcher, const QString &path, SearchType type) { TermPtr term = newLucene(pathFieldName(type), path.toStdWString()); QueryPtr query = newLucene(term); TopDocsPtr topDocs = searcher->search(query, 1); if (!topDocs || topDocs->totalHits == 0) { return nullptr; } return searcher->doc(topDocs->scoreDocs[0]->doc); } struct CachedIndexContext { QString indexDirectory; FSDirectoryPtr directory; IndexReaderPtr reader; SearcherPtr searcher; }; /** * @brief Route SearchType::Semantic to Content or Ocr based on file extension. * @return The resolved SearchType, or std::nullopt if the extension matches neither category. */ static std::optional resolveSemanticType(const QString &path) { const QString suffix = QFileInfo(path).suffix().toLower(); if (suffix.isEmpty()) return std::nullopt; if (SearchUtility::semanticDocExtensions().contains(suffix)) return SearchType::Content; if (SearchUtility::semanticPicExtensions().contains(suffix)) return SearchType::Ocr; return std::nullopt; } } // namespace // ── HighlightOptions (Pimpl) ────────────────────────────────────────── HighlightOptions::HighlightOptions() : d(new HighlightOptionsPrivate) { } HighlightOptions::~HighlightOptions() = default; HighlightOptions::HighlightOptions(const HighlightOptions &other) = default; HighlightOptions &HighlightOptions::operator=(const HighlightOptions &other) = default; int HighlightOptions::maxPreviewLength() const { return d->maxPreviewLength; } void HighlightOptions::setMaxPreviewLength(int length) { d->maxPreviewLength = length; } int HighlightOptions::positioningMaxLength() const { return d->positioningMaxLength; } void HighlightOptions::setPositioningMaxLength(int length) { d->positioningMaxLength = length; } bool HighlightOptions::enableHtml() const { return d->enableHtml; } void HighlightOptions::setEnableHtml(bool enable) { d->enableHtml = enable; } // ── PreviewOptions (Pimpl) ───────────────────────────────────────────── PreviewOptions::PreviewOptions() : d(new PreviewOptionsPrivate) { } PreviewOptions::~PreviewOptions() = default; PreviewOptions::PreviewOptions(const PreviewOptions &other) = default; PreviewOptions &PreviewOptions::operator=(const PreviewOptions &other) = default; int PreviewOptions::offset() const { return d->offset; } void PreviewOptions::setOffset(int offset) { d->offset = offset; } int PreviewOptions::maxLength() const { return d->maxLength; } void PreviewOptions::setMaxLength(int length) { d->maxLength = length; } QString PreviewOptions::keyword() const { return d->keyword; } void PreviewOptions::setKeyword(const QString &keyword) { d->keyword = keyword; } // ── PreviewResult (Pimpl) ────────────────────────────────────────────── PreviewResult::PreviewResult() : d(new PreviewResultPrivate) { } PreviewResult::~PreviewResult() = default; PreviewResult::PreviewResult(const PreviewResult &other) = default; PreviewResult &PreviewResult::operator=(const PreviewResult &other) = default; PreviewStatus PreviewResult::status() const { return d->status; } QString PreviewResult::content() const { return d->content; } int PreviewResult::charCount() const { return d->charCount; } int PreviewResult::keywordOffset() const { return d->keywordOffset; } // ── ContentRetriever ─────────────────────────────────────────────────── struct ContentRetriever::Private { QString contentIndexDirectory; QString ocrIndexDirectory; mutable QMutex mutex; mutable QHash cacheByType; CachedIndexContext *ensureIndexContext(SearchType type, const QString &indexDir) const { CachedIndexContext &ctx = cacheByType[static_cast(type)]; if (ctx.searcher && ctx.reader && ctx.directory && ctx.indexDirectory == indexDir) { try { if (!ctx.reader->isCurrent()) { IndexReaderPtr reopened = ctx.reader->reopen(true); if (reopened != ctx.reader) { ctx.reader = reopened; ctx.searcher = newLucene(ctx.reader); } } return &ctx; } catch (const LuceneException &e) { qWarning() << "ContentRetriever: failed to refresh index reader" << QString::fromStdWString(e.getError()); ctx = {}; } catch (const std::exception &e) { qWarning() << "ContentRetriever: failed to refresh index reader" << e.what(); ctx = {}; } } try { ctx.indexDirectory = indexDir; ctx.directory = FSDirectory::open(indexDir.toStdWString()); if (!IndexReader::indexExists(ctx.directory)) { ctx = {}; return nullptr; } ctx.reader = IndexReader::open(ctx.directory, true); ctx.searcher = newLucene(ctx.reader); return &ctx; } catch (const LuceneException &e) { qWarning() << "ContentRetriever: failed to open index" << QString::fromStdWString(e.getError()); } catch (const std::exception &e) { qWarning() << "ContentRetriever: failed to open index" << e.what(); } ctx = {}; return nullptr; } }; ContentRetriever::ContentRetriever(QObject *parent) : QObject(parent), d(std::make_unique()) { } ContentRetriever::~ContentRetriever() = default; void ContentRetriever::setIndexDirectory(SearchType type, const QString &indexDirectory) { if (type != SearchType::Content && type != SearchType::Ocr) { return; } QMutexLocker locker(&d->mutex); if (type == SearchType::Ocr) { d->ocrIndexDirectory = indexDirectory; } else { d->contentIndexDirectory = indexDirectory; } d->cacheByType.remove(static_cast(type)); } QString ContentRetriever::indexDirectory(SearchType type) const { if (type == SearchType::Ocr) { return d->ocrIndexDirectory.isEmpty() ? defaultIndexDirectoryForType(type) : d->ocrIndexDirectory; } if (type == SearchType::Content) { return d->contentIndexDirectory.isEmpty() ? defaultIndexDirectoryForType(type) : d->contentIndexDirectory; } return {}; } QString ContentRetriever::fetchHighlight(const QString &path, const QString &keyword, SearchType type, const HighlightOptions &options) const { if (path.isEmpty() || keyword.isEmpty()) return {}; // Route SearchType::Semantic to Content or Ocr based on file extension if (type == SearchType::Semantic) { std::optional resolved = resolveSemanticType(path); if (!resolved) { qWarning() << "ContentRetriever: cannot route Semantic type for" << path << "- extension does not match doc or pic suffixes"; return {}; } type = *resolved; } else if (type != SearchType::Content && type != SearchType::Ocr) { return {}; } const QStringList keywords = splitKeywords(keyword); if (keywords.isEmpty()) return {}; const QString indexDir = indexDirectory(type); QMutexLocker locker(&d->mutex); CachedIndexContext *ctx = d->ensureIndexContext(type, indexDir); if (!ctx || !ctx->searcher) { return {}; } try { const DocumentPtr doc = findDocumentByPath(ctx->searcher, path, type); const QString content = storedContentFromDocument(doc, type); if (content.isEmpty()) { return {}; } return ContentHighlighter::customHighlight( keywords, content, options.maxPreviewLength(), options.enableHtml(), options.positioningMaxLength()); } catch (const LuceneException &e) { qWarning() << "ContentRetriever: error fetching highlight for" << path << QString::fromStdWString(e.getError()); return {}; } catch (const std::exception &e) { qWarning() << "ContentRetriever: std error for" << path << e.what(); return {}; } } QMap ContentRetriever::fetchHighlights(const QStringList &paths, const QString &keyword, SearchType type, const HighlightOptions &options) const { QMap results; if (paths.isEmpty() || keyword.isEmpty()) return results; if (type != SearchType::Content && type != SearchType::Ocr && type != SearchType::Semantic) return results; const QStringList keywords = splitKeywords(keyword); if (keywords.isEmpty()) return results; QMutexLocker locker(&d->mutex); for (const QString &path : paths) { // For Semantic, route each path individually based on its extension SearchType effectiveType = type; if (type == SearchType::Semantic) { std::optional resolved = resolveSemanticType(path); if (!resolved) { qWarning() << "ContentRetriever: cannot route Semantic type for" << path << "- extension does not match doc or pic suffixes"; results.insert(path, {}); continue; } effectiveType = *resolved; } const QString indexDir = indexDirectory(effectiveType); CachedIndexContext *ctx = d->ensureIndexContext(effectiveType, indexDir); if (!ctx || !ctx->searcher) { results.insert(path, {}); continue; } try { const DocumentPtr doc = findDocumentByPath(ctx->searcher, path, effectiveType); const QString content = storedContentFromDocument(doc, effectiveType); if (content.isEmpty()) { results.insert(path, {}); continue; } results.insert(path, ContentHighlighter::customHighlight( keywords, content, options.maxPreviewLength(), options.enableHtml(), options.positioningMaxLength())); } catch (const LuceneException &e) { qWarning() << "ContentRetriever: error for" << path << QString::fromStdWString(e.getError()); results.insert(path, {}); } catch (const std::exception &e) { qWarning() << "ContentRetriever: std error for" << path << e.what(); results.insert(path, {}); } } return results; } QString ContentRetriever::fetchContent(const QString &path, SearchType type) const { if (path.isEmpty()) return {}; if (type == SearchType::Semantic) { std::optional resolved = resolveSemanticType(path); if (!resolved) { qWarning() << "ContentRetriever: cannot route Semantic type for" << path << "- extension does not match doc or pic suffixes"; return {}; } type = *resolved; } else if (type != SearchType::Content && type != SearchType::Ocr) { return {}; } const QString indexDir = indexDirectory(type); QMutexLocker locker(&d->mutex); CachedIndexContext *ctx = d->ensureIndexContext(type, indexDir); if (!ctx || !ctx->searcher) { return {}; } try { return storedContentFromDocument(findDocumentByPath(ctx->searcher, path, type), type); } catch (const LuceneException &e) { qWarning() << "ContentRetriever: error fetching content for" << path << QString::fromStdWString(e.getError()); return {}; } catch (const std::exception &e) { qWarning() << "ContentRetriever: std error for" << path << e.what(); return {}; } } QMap ContentRetriever::fetchContents(const QStringList &paths, SearchType type) const { QMap results; if (paths.isEmpty()) return results; if (type != SearchType::Content && type != SearchType::Ocr && type != SearchType::Semantic) return results; QMutexLocker locker(&d->mutex); for (const QString &path : paths) { SearchType effectiveType = type; if (type == SearchType::Semantic) { std::optional resolved = resolveSemanticType(path); if (!resolved) { qWarning() << "ContentRetriever: cannot route Semantic type for" << path << "- extension does not match doc or pic suffixes"; results.insert(path, {}); continue; } effectiveType = *resolved; } const QString indexDir = indexDirectory(effectiveType); CachedIndexContext *ctx = d->ensureIndexContext(effectiveType, indexDir); if (!ctx || !ctx->searcher) { results.insert(path, {}); continue; } try { results.insert(path, storedContentFromDocument(findDocumentByPath(ctx->searcher, path, effectiveType), effectiveType)); } catch (const LuceneException &e) { qWarning() << "ContentRetriever: error for" << path << QString::fromStdWString(e.getError()); results.insert(path, {}); } catch (const std::exception &e) { qWarning() << "ContentRetriever: std error for" << path << e.what(); results.insert(path, {}); } } return results; } PreviewResult ContentRetriever::fetchPreview(const QString &path, SearchType type, const PreviewOptions &options) const { PreviewResult result; if (path.isEmpty()) { result.d->status = PreviewStatus::NotIndexed; return result; } // Route SearchType::Semantic to Content or Ocr based on file extension if (type == SearchType::Semantic) { std::optional resolved = resolveSemanticType(path); if (!resolved) { qWarning() << "ContentRetriever: cannot route Semantic type for" << path << "- extension does not match doc or pic suffixes"; result.d->status = PreviewStatus::NotIndexed; return result; } type = *resolved; } else if (type != SearchType::Content && type != SearchType::Ocr) { result.d->status = PreviewStatus::NotIndexed; return result; } const QString indexDir = indexDirectory(type); QMutexLocker locker(&d->mutex); CachedIndexContext *ctx = d->ensureIndexContext(type, indexDir); if (!ctx || !ctx->searcher) { result.d->status = PreviewStatus::NotIndexed; return result; } try { const DocumentPtr doc = findDocumentByPath(ctx->searcher, path, type); if (!doc) { result.d->status = PreviewStatus::NotIndexed; return result; } const QString content = storedContentFromDocument(doc, type); // Keep preview metadata in the same QString character unit as offset/keywordOffset. result.d->charCount = content.size(); int keywordOffset = -1; const QString snippet = ContentHighlighter::previewSnippet( content, options.offset(), options.maxLength(), options.keyword(), &keywordOffset); result.d->content = snippet; result.d->keywordOffset = keywordOffset; } catch (const LuceneException &e) { qWarning() << "ContentRetriever: error fetching preview for" << path << QString::fromStdWString(e.getError()); result.d->status = PreviewStatus::NotIndexed; } catch (const std::exception &e) { qWarning() << "ContentRetriever: std error for" << path << e.what(); result.d->status = PreviewStatus::NotIndexed; } return result; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/filenameblacklistmatcher.cpp000066400000000000000000000033671523477242100323620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "filenameblacklistmatcher.h" #include DFM_SEARCH_BEGIN_NS namespace Global { namespace BlacklistMatcher { QString normalizePathForBlacklistMatch(const QString &path) { return QDir::cleanPath(path.trimmed()); } static bool isAbsolutePathMatch(const QString &normalizedPath, const QString &blacklistAbsolutePath) { const QString normalizedRulePath = normalizePathForBlacklistMatch(blacklistAbsolutePath); if (normalizedRulePath.isEmpty()) { return false; } if (normalizedRulePath == "/") { return normalizedPath.startsWith('/'); } return normalizedPath == normalizedRulePath || normalizedPath.startsWith(normalizedRulePath + '/'); } bool isPathBlacklisted(const QString &inputPath, const QStringList &blacklistEntries) { const QString normalizedPath = normalizePathForBlacklistMatch(inputPath); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QStringList pathSegments = normalizedPath.split('/', Qt::SkipEmptyParts); #else const QStringList pathSegments = normalizedPath.split('/', QString::SkipEmptyParts); #endif for (const QString &entry : blacklistEntries) { const QString trimmedEntry = entry.trimmed(); if (trimmedEntry.isEmpty()) { continue; } if (QDir::isAbsolutePath(trimmedEntry)) { if (isAbsolutePathMatch(normalizedPath, trimmedEntry)) { return true; } continue; } if (pathSegments.contains(trimmedEntry)) { return true; } } return false; } } // namespace BlacklistMatcher } // namespace Global DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/filenameblacklistmatcher.h000066400000000000000000000011471523477242100320210ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef FILENAMEBLACKLISTMATCHER_H #define FILENAMEBLACKLISTMATCHER_H #include #include #include DFM_SEARCH_BEGIN_NS namespace Global { namespace BlacklistMatcher { QString normalizePathForBlacklistMatch(const QString &path); bool isPathBlacklisted(const QString &inputPath, const QStringList &blacklistEntries); } // namespace BlacklistMatcher } // namespace Global DFM_SEARCH_END_NS #endif // FILENAMEBLACKLISTMATCHER_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/highlightoptions_p.h000066400000000000000000000013521523477242100307040ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef HIGHLIGHTOPTIONS_P_H #define HIGHLIGHTOPTIONS_P_H #include #include DFM_SEARCH_BEGIN_NS class HighlightOptionsPrivate : public QSharedData { public: HighlightOptionsPrivate() = default; HighlightOptionsPrivate(const HighlightOptionsPrivate &other) = default; int maxPreviewLength = 200; ///< Maximum snippet length in characters int positioningMaxLength = 30; ///< Keyword positioning window size (min 30) bool enableHtml = false; ///< Wrap matched keywords with tags }; DFM_SEARCH_END_NS #endif // HIGHLIGHTOPTIONS_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/lucene_cancellation_compat.h000066400000000000000000000105211523477242100323320ustar00rootroot00000000000000///////////////////////////////////////////////////////////////////////////// // Copyright (C) 2024 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: GPL-3.0-or-later // // Lucene++ SearchCancellation Compatibility Layer // // This header provides compatibility for Lucene++ search cancellation feature. // It automatically detects whether the installed Lucene++ version supports // SearchCancellation and provides appropriate implementation. ///////////////////////////////////////////////////////////////////////////// #ifndef LUCENE_CANCELLATION_COMPAT_H #define LUCENE_CANCELLATION_COMPAT_H #include // 自动检测 Lucene++ 是否支持 SearchCancellation // 使用 C++17 的 __has_include 特性进行编译时检测 #if defined(__has_include) # if __has_include() # define LUCENE_HAS_SEARCH_CANCELLATION 1 # else # define LUCENE_HAS_SEARCH_CANCELLATION 0 # endif #else // 如果编译器不支持 __has_include,默认假设没有 # define LUCENE_HAS_SEARCH_CANCELLATION 0 #endif // ============================================================================ // Deepin 环境:使用 Lucene++ 官方实现 // ============================================================================ #if LUCENE_HAS_SEARCH_CANCELLATION # include // 使用 Lucene++ 的完整实现 // - SearchCancellation::setFlag() // - SearchCancellation::shouldCancel() // - SearchCancellationGuard (RAII) // ============================================================================ // 其他发行版:提供兼容层空实现 // ============================================================================ #else # include namespace Lucene { /** * @brief 兼容层:搜索取消上下文(空实现) * * 在不支持 SearchCancellation 的 Lucene++ 版本中, * 此类提供空操作,确保代码可以编译通过。 * * 注意:取消功能将不可用,但不会影响基本搜索功能。 * 用户在这种环境下仍可通过 CancellableCollector 实现部分取消功能。 */ class SearchCancellation { public: /** * @brief 设置取消标志(空实现) * @param flag 取消标志指针 */ static void setFlag(std::atomic *flag) { // 空实现:不执行任何操作 (void)flag; // 避免未使用参数警告 } /** * @brief 获取取消标志(空实现) * @return nullptr */ static std::atomic *getFlag() { return nullptr; } /** * @brief 清除取消标志(空实现) */ static void clear() { // 空实现:不执行任何操作 } /** * @brief 检查是否应该取消(空实现) * @return false(永远不取消) */ static bool shouldCancel() { return false; } }; /** * @brief 兼容层:RAII 取消守护类(空实现) * * 此类提供与 Lucene++ 官方实现相同的接口, * 但所有操作都是空操作,确保代码可以编译。 * * 使用示例: * @code * void performSearch() { * Lucene::SearchCancellationGuard guard(&m_cancelled); * // ... 执行搜索 ... * } // 自动清理(空操作) * @endcode */ class SearchCancellationGuard { public: /** * @brief 构造函数(空实现) * @param flag 取消标志指针 */ explicit SearchCancellationGuard(std::atomic *flag) { // 空实现:不执行任何操作 (void)flag; // 避免未使用参数警告 } /** * @brief 析构函数(空实现) */ ~SearchCancellationGuard() { // 空实现:不执行任何操作 } // 禁用拷贝和移动,保持与官方实现一致的接口 SearchCancellationGuard(const SearchCancellationGuard &) = delete; SearchCancellationGuard &operator=(const SearchCancellationGuard &) = delete; SearchCancellationGuard(SearchCancellationGuard &&) = delete; SearchCancellationGuard &operator=(SearchCancellationGuard &&) = delete; }; } // namespace Lucene // 编译时提示信息 # pragma message("Lucene++ SearchCancellation not available. Search cancellation in phraseFreq() disabled, but CancellableCollector still works.") #endif // LUCENE_HAS_SEARCH_CANCELLATION #endif // LUCENE_CANCELLATION_COMPAT_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/lucenequeryutils.cpp000066400000000000000000000115431523477242100307620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "lucenequeryutils.h" #include #include #include #include DFM_SEARCH_BEGIN_NS namespace LuceneQueryUtils { namespace { Lucene::String toLuceneString(const QString &str, bool caseSensitive) { QString normalized = caseSensitive ? str : str.toLower(); QByteArray utf8Bytes = normalized.toUtf8(); Lucene::String luceneStr = Lucene::StringUtils::toUnicode(std::string(utf8Bytes.constData(), utf8Bytes.length())); if (luceneStr.empty()) { luceneStr = Lucene::StringUtils::toUnicode(normalized.toStdString()); } return luceneStr; } Lucene::TermPtr buildTerm(const QString &fieldName, const QString &text, bool caseSensitive) { return Lucene::newLucene( toLuceneString(fieldName, true), toLuceneString(text, caseSensitive)); } int phrasePositionForStandardNGram2(int startOffset) { // Standard lucene++ NGramTokenizer(1,2) emits 1-gram then 2-gram at each // character offset, and every emitted token advances the phrase position by 1. // Therefore the 2-gram starting at offset i lands at position 2 * i + 1. return startOffset * 2 + 1; } } // namespace std::wstring getLuceneSpecialChars() { // Lucene 特殊字符(按 Lucene 语法需要转义) return L"+-&&||!(){}[]^\"~*?:\\/ "; } Lucene::String processQueryString(const QString &str, bool caseSensitive) { // Step 1: 转成 std::wstring std::wstring input = str.toStdWString(); // Step 2: 转义 Lucene 特殊字符 const std::wstring specialChars = getLuceneSpecialChars(); std::wstring escaped; escaped.reserve(input.size() * 2); // 预留空间,提高性能 for (wchar_t ch : input) { // 如果是 Lucene 特殊字符,就在前面加 '\' if (specialChars.find(ch) != std::wstring::npos) { escaped += L'\\'; } escaped += ch; } // Step 3: 转成 Lucene::String(Lucene++ 使用的是 UTF-8) QString tempQString = QString::fromStdWString(escaped); QByteArray utf8Bytes = tempQString.toUtf8(); Lucene::String luceneStr = Lucene::StringUtils::toUnicode(std::string(utf8Bytes.constData(), utf8Bytes.length())); if (luceneStr.empty()) { luceneStr = Lucene::StringUtils::toUnicode(str.toStdString()); } // Step 4: 如果不区分大小写就转小写(Lucene::String 是 Unicode) if (!caseSensitive) { Lucene::StringUtils::toLower(luceneStr); } return luceneStr; } Lucene::QueryPtr buildNGramSearchQuery(const QString &fieldName, const QString &keyword, bool caseSensitive) { if (fieldName.isEmpty() || keyword.isEmpty()) { return nullptr; } if (keyword.size() <= 2) { return Lucene::newLucene( buildTerm(fieldName, keyword, caseSensitive)); } Lucene::PhraseQueryPtr phraseQuery = Lucene::newLucene(); for (int pos = 0; pos + 2 <= keyword.size(); pos += 2) { phraseQuery->add(buildTerm(fieldName, keyword.mid(pos, 2), caseSensitive), phrasePositionForStandardNGram2(pos)); } if (keyword.size() % 2 != 0) { const int tailPos = keyword.size() - 2; phraseQuery->add(buildTerm(fieldName, keyword.mid(tailPos, 2), caseSensitive), phrasePositionForStandardNGram2(tailPos)); } return phraseQuery; } Lucene::QueryPtr buildPathPrefixQuery(const QString &pathPrefix, const QString &fieldName) { if (pathPrefix.isEmpty() || fieldName.isEmpty()) { return nullptr; } // 标准化路径:去掉多余的斜杠,去掉末尾的斜杠 (例如 "/home/user/" -> "/home/user") const QString normalizedPath = QDir::cleanPath(pathPrefix); return Lucene::newLucene( Lucene::newLucene( Lucene::StringUtils::toUnicode(fieldName.toStdString()), Lucene::StringUtils::toUnicode(normalizedPath.toStdString()))); } Lucene::QueryPtr buildMultiPathPrefixQuery(const QStringList &paths, const QString &fieldName) { if (paths.isEmpty() || fieldName.isEmpty()) { return nullptr; } if (paths.size() == 1) { return buildPathPrefixQuery(paths.first(), fieldName); } Lucene::BooleanQueryPtr boolQuery = Lucene::newLucene(); bool hasValid = false; for (const QString &path : paths) { Lucene::QueryPtr pathQuery = buildPathPrefixQuery(path, fieldName); if (pathQuery) { boolQuery->add(pathQuery, Lucene::BooleanClause::SHOULD); hasValid = true; } } return hasValid ? boolQuery : nullptr; } } // namespace LuceneQueryUtils DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/lucenequeryutils.h000066400000000000000000000047401523477242100304300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef LUCENEQUERYUTILS_H #define LUCENEQUERYUTILS_H #include #include #include #include DFM_SEARCH_BEGIN_NS /** * @brief The LuceneQueryUtils namespace provides utility functions for Lucene query operations */ namespace LuceneQueryUtils { /** * @brief Process a string for Lucene query, escaping special characters * @param str The string to process * @param caseSensitive Whether the search is case sensitive * @return The processed Lucene::String */ Lucene::String processQueryString(const QString &str, bool caseSensitive = false); /** * @brief Build a query that matches text indexed by NGramAnalyzer(1, 2) * * The query is built directly instead of passing user input through an n-gram * analyzer at search time. One- and two-character keywords use TermQuery. * Longer keywords use a sparse PhraseQuery over 2-grams to avoid generating * every overlapping query term. * * @param fieldName The indexed field name * @param keyword The raw user keyword * @param caseSensitive Whether the search is case sensitive * @return Lucene query object, or nullptr if fieldName or keyword is empty */ Lucene::QueryPtr buildNGramSearchQuery(const QString &fieldName, const QString &keyword, bool caseSensitive = false); /** * @brief Get a list of Lucene special characters that need escaping * @return List of special characters */ std::wstring getLuceneSpecialChars(); /** * @brief Build a path prefix query for Lucene * @param pathPrefix The path prefix to search for * @param fieldName The index field name (e.g., "full_path" or "path") * @return Lucene query object, or nullptr if pathPrefix is empty */ Lucene::QueryPtr buildPathPrefixQuery(const QString &pathPrefix, const QString &fieldName); /** * @brief Build a multi-path prefix query for Lucene * * When multiple paths are provided, builds a BooleanQuery with SHOULD clauses * for each path. When only one path is provided, returns a simple pathPrefixQuery. * * @param paths List of path prefixes to search for * @param fieldName The index field name (e.g., "ancestor_paths") * @return Lucene query object, or nullptr if paths is empty */ Lucene::QueryPtr buildMultiPathPrefixQuery(const QStringList &paths, const QString &fieldName); } // namespace LuceneQueryUtils DFM_SEARCH_END_NS #endif // LUCENEQUERYUTILS_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/parsedintent.cpp000066400000000000000000000064311523477242100300400ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "parsedintent_p.h" #include DFM_SEARCH_BEGIN_NS // ── ParsedIntent constructors ────────────────────────────────────── ParsedIntent::ParsedIntent() : d(new ParsedIntentPrivate) { } ParsedIntent::~ParsedIntent() = default; ParsedIntent::ParsedIntent(const ParsedIntent &other) = default; ParsedIntent &ParsedIntent::operator=(const ParsedIntent &other) = default; ParsedIntent::ParsedIntent(ParsedIntent &&other) noexcept = default; ParsedIntent &ParsedIntent::operator=(ParsedIntent &&other) noexcept = default; // ── ParsedIntent accessors ────────────────────────────────────────── // timeConstraint const TimeConstraint &ParsedIntent::timeConstraint() const { return d->timeConstraint; } TimeConstraint &ParsedIntent::timeConstraint() { return d->timeConstraint; } void ParsedIntent::setTimeConstraint(const TimeConstraint &tc) { d->timeConstraint = tc; } // sizeConstraint const SizeConstraint &ParsedIntent::sizeConstraint() const { return d->sizeConstraint; } SizeConstraint &ParsedIntent::sizeConstraint() { return d->sizeConstraint; } void ParsedIntent::setSizeConstraint(const SizeConstraint &sc) { d->sizeConstraint = sc; } // fileExtensions const QStringList &ParsedIntent::fileExtensions() const { return d->fileExtensions; } QStringList &ParsedIntent::fileExtensions() { return d->fileExtensions; } void ParsedIntent::setFileExtensions(const QStringList &extensions) { d->fileExtensions = extensions; } // searchDirectories const QStringList &ParsedIntent::searchDirectories() const { return d->searchDirectories; } QStringList &ParsedIntent::searchDirectories() { return d->searchDirectories; } void ParsedIntent::setSearchDirectories(const QStringList &dirs) { d->searchDirectories = dirs; } // includeHidden bool ParsedIntent::includeHidden() const { return d->includeHidden; } void ParsedIntent::setIncludeHidden(bool include) { d->includeHidden = include; } // hiddenOnly bool ParsedIntent::hiddenOnly() const { return d->hiddenOnly; } void ParsedIntent::setHiddenOnly(bool hidden) { d->hiddenOnly = hidden; } // recentOnly bool ParsedIntent::recentOnly() const { return d->recentOnly; } void ParsedIntent::setRecentOnly(bool recent) { d->recentOnly = recent; } // keywords const QStringList &ParsedIntent::keywords() const { return d->keywords; } QStringList &ParsedIntent::keywords() { return d->keywords; } void ParsedIntent::setKeywords(const QStringList &kw) { d->keywords = kw; } // searchTarget SearchTarget ParsedIntent::searchTarget() const { return d->searchTarget; } void ParsedIntent::setSearchTarget(SearchTarget target) { d->searchTarget = target; } // consumedSpans const QList &ParsedIntent::consumedSpans() const { return d->consumedSpans; } QList &ParsedIntent::consumedSpans() { return d->consumedSpans; } void ParsedIntent::setConsumedSpans(const QList &spans) { d->consumedSpans = spans; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/parsedintent_p.h000066400000000000000000000016201523477242100300170ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef PARSEDINTENT_P_H #define PARSEDINTENT_P_H #include #include #include DFM_SEARCH_BEGIN_NS class ParsedIntentPrivate : public QSharedData { public: ParsedIntentPrivate() = default; ParsedIntentPrivate(const ParsedIntentPrivate &other) = default; TimeConstraint timeConstraint; SizeConstraint sizeConstraint; QStringList fileExtensions; QStringList searchDirectories; bool includeHidden = false; bool hiddenOnly = false; bool recentOnly = false; // true = search recently-used files (DBus RecentManager) QStringList keywords; SearchTarget searchTarget = SearchTarget::All; QList consumedSpans; }; DFM_SEARCH_END_NS #endif // PARSEDINTENT_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/previewoptions_p.h000066400000000000000000000013521523477242100304160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef PREVIEWOPTIONS_P_H #define PREVIEWOPTIONS_P_H #include #include #include DFM_SEARCH_BEGIN_NS class PreviewOptionsPrivate : public QSharedData { public: PreviewOptionsPrivate() = default; PreviewOptionsPrivate(const PreviewOptionsPrivate &other) = default; int offset = 0; ///< Content offset: start reading from the n-th character int maxLength = 200; ///< Maximum snippet length in characters QString keyword; ///< Search keyword (empty = raw offset read) }; DFM_SEARCH_END_NS #endif // PREVIEWOPTIONS_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/previewresult_p.h000066400000000000000000000014311523477242100302370ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef PREVIEWRESULT_P_H #define PREVIEWRESULT_P_H #include #include #include DFM_SEARCH_BEGIN_NS class PreviewResultPrivate : public QSharedData { public: PreviewResultPrivate() = default; PreviewResultPrivate(const PreviewResultPrivate &other) = default; PreviewStatus status = PreviewStatus::Success; QString content; ///< Extracted content snippet int charCount = 0; ///< Full stored content length in QString character units int keywordOffset = -1; ///< Position of keyword in full content (-1 if none/not matched) }; DFM_SEARCH_END_NS #endif // PREVIEWRESULT_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/searchutility.cpp000066400000000000000000001003011523477242100302200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "searchutility.h" #include "filenameblacklistmatcher.h" #include #include #include #include #include #include #include #include #include #include #include DFM_SEARCH_BEGIN_NS using namespace Lucene; namespace Global { /** * @brief Read index version from a JSON status file * @param indexDir The index directory path * @param statusFile The name of the status JSON file * @return The version number, or -1 if reading fails */ static int readIndexVersion(const QString &indexDir, const QString &statusFile) { const QString versionFilePath = QDir(indexDir).filePath(statusFile); QFile file(versionFilePath); if (!file.exists()) { qWarning() << "Index version file not found:" << versionFilePath; return -1; } if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "Failed to open index version file:" << file.errorString(); return -1; } QByteArray jsonData = file.readAll(); file.close(); QJsonParseError parseError; QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Failed to parse index version JSON:" << parseError.errorString(); return -1; } QJsonObject jsonObj = jsonDoc.object(); if (!jsonObj.contains("version")) { qWarning() << "Index version JSON missing 'version' field"; return -1; } // Handle both numeric and string version values const QJsonValue versionValue = jsonObj.value("version"); return versionValue.isDouble() ? versionValue.toInt() : versionValue.toString().toInt(); } // 辅助递归函数,尝试所有可能的分割方法 static bool isPinyinSequenceHelper(const QString &str, int startPos, const QSet &validSyllables) { int len = str.length(); // 如果已经处理到字符串末尾,则匹配成功 if (startPos >= len) return true; // 尝试从长到短匹配音节 for (int syllableLen = qMin(6, len - startPos); syllableLen >= 1; syllableLen--) { QString syllable = str.mid(startPos, syllableLen); if (validSyllables.contains(syllable)) { // 当前音节匹配成功,递归处理剩余部分 if (isPinyinSequenceHelper(str, startPos + syllableLen, validSyllables)) return true; } } // 所有可能的分割方式都尝试失败 return false; } // This function is internal to this unit (static) and handles the core DConfig loading. static std::optional tryLoadStringListFromDConfigInternal( const QString &appId, const QString &schemaId, const QString &keyName) { // Dtk::Core::DConfig::create returns a pointer and ideally needs a parent // for memory management. For a short-lived object within a function, // a local QObject can serve as a temporary parent. QObject dconfigParent; // Temporary parent for the DConfig instance Dtk::Core::DConfig *dconfigPtr = Dtk::Core::DConfig::create(appId, schemaId, "", &dconfigParent); // Check if DConfig object was created successfully if (!dconfigPtr) { qWarning() << "DConfig: Failed to create DConfig instance for appId:" << appId << "schemaId:" << schemaId; return std::nullopt; } // Check if the created DConfig instance is valid if (!dconfigPtr->isValid()) { qWarning() << "DConfig: Instance is invalid for appId:" << appId << "schemaId:" << schemaId; // No need to delete dconfigPtr, dconfigParent will manage it. return std::nullopt; } QVariant value = dconfigPtr->value(keyName); // No need to delete dconfigPtr, dconfigParent will manage it when it goes out of scope. return SearchUtility::parseVariantToStringList(value); } // --- Specific Loader for "supportedFileExtensions" --- static std::optional> tryLoadSupportedFileExtensionsFromDConfig() { const QString appId = "org.deepin.dde.file-manager"; // Assuming this is the correct appId const QString schemaId = "org.deepin.dde.file-manager.textindex"; const QString keyName = "supportedFileExtensions"; std::optional stringListOpt = tryLoadStringListFromDConfigInternal(appId, schemaId, keyName); if (!stringListOpt) { return std::nullopt; // Loading failed at the generic level } const QStringList &extensionsFromDConfigList = *stringListOpt; QSet extensionsSet; for (const QString &ext : extensionsFromDConfigList) { QString cleanedExt = ext.startsWith('.') ? ext.mid(1) : ext; if (!cleanedExt.isEmpty()) { extensionsSet.insert(cleanedExt); } } if (extensionsFromDConfigList.isEmpty()) { qDebug() << "DConfig: Key '" << keyName << "' in schema '" << schemaId << "' provided an empty list."; } else if (extensionsSet.isEmpty() && !extensionsFromDConfigList.isEmpty()) { qDebug() << "DConfig: Key '" << keyName << "' in schema '" << schemaId << "' contained only invalid/empty entries after cleaning."; } else if (!extensionsSet.isEmpty()) { qDebug() << "DConfig: Successfully processed" << extensionsSet.size() << "supported extensions from DConfig."; } return extensionsSet; // Return the processed set (might be empty if DConfig list was empty or all invalid) } // --- Specific Loader for "indexing_paths" --- static std::optional tryLoadIndexingNamesFromDConfig() { const QString appId = "org.deepin.anything"; const QString schemaId = "org.deepin.anything"; // As per your requirement const QString keyName = "indexing_paths"; std::optional stringListOpt = tryLoadStringListFromDConfigInternal(appId, schemaId, keyName); if (!stringListOpt) { return std::nullopt; // Loading failed } const QStringList &namesFromDConfigList = *stringListOpt; if (namesFromDConfigList.isEmpty()) { qDebug() << "DConfig: Key '" << keyName << "' in schema '" << schemaId << "' provided an empty list."; } return namesFromDConfigList; // Return the processed set } // --- Specific Loader for "blacklist_paths" --- static std::optional tryLoadBlacklistPathsFromDConfig() { const QString appId = "org.deepin.anything"; const QString schemaId = "org.deepin.anything"; const QString keyName = "blacklist_paths"; std::optional stringListOpt = tryLoadStringListFromDConfigInternal(appId, schemaId, keyName); if (!stringListOpt) { return std::nullopt; // Loading failed } const QStringList &pathsFromDConfigList = *stringListOpt; if (pathsFromDConfigList.isEmpty()) { qDebug() << "DConfig: Key '" << keyName << "' in schema '" << schemaId << "' provided an empty list."; } return pathsFromDConfigList; // Return the processed list } // 辅助函数:提供硬编码的默认扩展名集合 static const QSet &getDefaultSupportedExtensions() { static const QSet kExtensions = { "rtf", "odt", "ods", "odp", "odg", "docx", "xlsx", "pptx", "ppsx", "md", "xls", "xlsb", "doc", "dot", "wps", "ppt", "pps", "txt", "pdf", "dps", "sh", "html", "htm", "xml", "xhtml", "dhtml", "shtm", "shtml", "json", "css", "yaml", "ini", "bat", "js", "sql", "uof", "ofd" }; return kExtensions; } // 主接口:获取支持的文件扩展名 // 优先从 DConfig 获取,如果失败或DConfig中的列表为空,则使用硬编码的默认值。 static const QSet &supportedExtensions() { // 静态局部变量的初始化只发生一次,并且是线程安全的。 // 使用立即调用的lambda表达式来执行初始化逻辑。 static const QSet kResolvedExtensions = []() -> QSet { std::optional> dconfigExtensionsOpt = tryLoadSupportedFileExtensionsFromDConfig(); // 如果DConfig成功返回了一个【非空】的集合,则使用它 if (dconfigExtensionsOpt && !dconfigExtensionsOpt->isEmpty()) { qDebug() << "Using extensions from DConfig."; return *dconfigExtensionsOpt; } // 否则 (DConfig读取失败,或DConfig返回了一个空集合),使用默认值 if (dconfigExtensionsOpt && dconfigExtensionsOpt->isEmpty()) { qDebug() << "DConfig provided an empty list of extensions. Using fallback default extensions."; } else if (!dconfigExtensionsOpt) { // implies dconfigExtensionsOpt is std::nullopt qDebug() << "Failed to load extensions from DConfig. Using fallback default extensions."; } return getDefaultSupportedExtensions(); }(); // 注意这里的 (),立即调用lambda return kResolvedExtensions; } static QStringList getResolvedIndexedDirectories() // Renamed for clarity { const QString homePath = QDir::homePath(); // Cache for frequent use const QStringList fallbackResult { homePath }; std::optional dconfigNamesOpt = tryLoadIndexingNamesFromDConfig(); if (!dconfigNamesOpt) { qDebug() << "Failed to load indexing names from DConfig or DConfig instance invalid, using fallback."; return fallbackResult; } const QStringList &rawPathsFromDConfig = *dconfigNamesOpt; if (rawPathsFromDConfig.isEmpty()) { qDebug() << "DConfig provided an empty list for indexing names, using fallback."; return fallbackResult; } QList processedPaths; // Use QList to maintain order before final sort QSet uniquePathsChecker; // To ensure uniqueness during processing // Special handling for homePath if it's intended to be first bool homePathEncounteredAndAdded = false; for (const QString &rawPath : rawPathsFromDConfig) { QString currentPath = rawPath.trimmed(); // Remove leading/trailing whitespace // 1. Expand environment variables (specifically $HOME and ~) if (currentPath == QLatin1String("$HOME") || currentPath == QLatin1String("~")) { currentPath = homePath; } else if (currentPath.startsWith(QLatin1String("$HOME/"))) { currentPath = QDir(homePath).filePath(currentPath.mid(6)); // Length of "$HOME/" } else if (currentPath.startsWith(QLatin1String("~/"))) { currentPath = QDir(homePath).filePath(currentPath.mid(2)); // Length of "~/" } // Add more generic environment variable expansion here if needed: // e.g., using QProcessEnvironment::systemEnvironment().value("VAR_NAME") // or a regex for ${VAR_NAME} patterns. // 2. Normalize and validate path (basic validation: non-empty) if (currentPath.isEmpty()) { qDebug() << "Skipping empty path string after processing raw entry:" << rawPath; continue; } currentPath = QDir::cleanPath(currentPath); // Normalize path separators (e.g. \\ to /) // Further validation could be done here, e.g., check if it's an absolute path // QFileInfo fileInfo(currentPath); // if (!fileInfo.isAbsolute()) { // qWarning() << "Skipping relative path:" << currentPath; // continue; // } // if (!fileInfo.isDir() || !fileInfo.exists()) { // Stricter: must be an existing directory // qWarning() << "Skipping non-existent or non-directory path:" << currentPath; // continue; // } // 3. Add to list if unique and handle homePath ordering if (currentPath == homePath) { if (!homePathEncounteredAndAdded) { processedPaths.prepend(currentPath); // Ensure homePath is first if encountered uniquePathsChecker.insert(currentPath); homePathEncounteredAndAdded = true; } // If homePath was already added (e.g. from a previous "$HOME"), skip subsequent identical entries. // The uniquePathsChecker will also prevent adding it again if it wasn't the first home path encountered. } else { if (!uniquePathsChecker.contains(currentPath)) { processedPaths.append(currentPath); uniquePathsChecker.insert(currentPath); } } } // Ensure home path is first if it was added but not via prepend (e.g. if it wasn't the first item processed) // This can happen if DConfig has ["/foo", "$HOME"] and homePath wasn't prepended. // The above loop tries to prepend, but this is a safeguard if the logic changes or has subtle bugs. if (homePathEncounteredAndAdded && !processedPaths.isEmpty() && processedPaths.first() != homePath) { processedPaths.removeAll(homePath); // Remove from other positions processedPaths.prepend(homePath); // Add to front } if (processedPaths.isEmpty()) { qDebug() << "All paths from DConfig were invalid or empty after processing, using fallback."; return fallbackResult; } qDebug() << "Resolved indexed directories:" << processedPaths; return processedPaths; } // --- Loader for semantic doc/pic extensions from org.deepin.anything --- static QSet loadSemanticExtensionsFromDConfig(const QString &keyName) { const QString appId = "org.deepin.anything"; const QString schemaId = "org.deepin.anything"; const QString valueKey = keyName; std::optional stringListOpt = tryLoadStringListFromDConfigInternal(appId, schemaId, valueKey); if (!stringListOpt) return {}; QSet set; for (const QString &ext : *stringListOpt) { QString cleaned = ext.startsWith('.') ? ext.mid(1) : ext; if (!cleaned.isEmpty()) set.insert(cleaned.toLower()); } return set; } bool isPinyinSequence(const QString &input) { if (input.isEmpty()) return false; // 清洗输入:移除所有非字母字符,只保留字母用于拼音检验 QString cleanedInput = input; cleanedInput.remove(QRegularExpression("[^a-zA-Z]")); if (cleanedInput.isEmpty()) return false; // 合法的拼音音节表(预先定义所有可能的拼音音节组合) static const QSet validSyllables = { // 单韵母音节 - 只有 a, o, e 可以单独成音节 "a", "o", "e", "ai", "ei", "ao", "ou", "an", "en", "ang", "eng", "er", // b开头音节 "ba", "bo", "bi", "bu", "bai", "bei", "bao", "ban", "ben", "bin", "bie", "biao", "bian", "bing", "bang", "beng", // p开头音节 "pa", "po", "pi", "pu", "pai", "pei", "pao", "pan", "pen", "pin", "pie", "piao", "pian", "ping", "pang", "peng", // m开头音节 "ma", "mo", "me", "mi", "mu", "mai", "mei", "mao", "mou", "man", "men", "min", "mie", "miao", "miu", "mian", "min", "ming", "mang", "meng", // f开头音节 "fa", "fo", "fu", "fei", "fan", "fen", "fang", "feng", // d开头音节 "da", "de", "di", "du", "dai", "dao", "dou", "dan", "den", "dang", "deng", "ding", "dong", "die", "diao", "diu", "dian", "duan", "dun", "duo", // t开头音节 "ta", "te", "ti", "tu", "tai", "tao", "tou", "tan", "tang", "teng", "ting", "tong", "tie", "tiao", "tian", "tuan", "tun", "tuo", // n开头音节 "na", "ne", "ni", "nu", "nv", "nai", "nei", "nao", "nou", "nan", "nen", "nang", "neng", "ning", "nong", "nie", "niao", "niu", "nian", "niang", "nuan", "nve", "nuo", "nun", // l开头音节 "la", "le", "li", "lu", "lv", "lai", "lei", "lao", "lou", "lan", "lang", "leng", "ling", "long", "lie", "liao", "liu", "lian", "liang", "luan", "lun", "luo", "lve", // g开头音节 "ga", "ge", "gu", "gai", "gei", "gao", "gou", "gan", "gen", "gang", "geng", "gong", "gua", "guo", "guai", "gui", "guan", "gun", "guang", // k开头音节 "ka", "ke", "ku", "kai", "kao", "kou", "kan", "ken", "kang", "keng", "kong", "kua", "kuo", "kuai", "kui", "kuan", "kun", "kuang", // h开头音节 "ha", "he", "hu", "hai", "hei", "hao", "hou", "han", "hen", "hang", "heng", "hong", "hua", "huo", "huai", "hui", "huan", "hun", "huang", // j开头音节 "ji", "ju", "jue", "jiu", "jie", "jia", "jin", "jing", "jiang", "jiong", "juan", "jun", "jian", "jiao", // q开头音节 "qi", "qu", "que", "qiu", "qie", "qia", "qin", "qing", "qiang", "qiong", "quan", "qun", "qian", "qiao", // x开头音节 "xi", "xu", "xue", "xiu", "xie", "xia", "xin", "xing", "xiang", "xiong", "xuan", "xun", "xian", "xiao", // zh开头音节 "zha", "zhe", "zhi", "zhu", "zhai", "zhao", "zhou", "zhan", "zhen", "zhang", "zheng", "zhong", "zhua", "zhuo", "zhuai", "zhui", "zhuan", "zhun", "zhuang", // ch开头音节 "cha", "che", "chi", "chu", "chai", "chao", "chou", "chan", "chen", "chang", "cheng", "chong", "chua", "chuo", "chuai", "chui", "chuan", "chun", "chuang", // sh开头音节 "sha", "she", "shi", "shu", "shai", "shao", "shou", "shan", "shen", "shang", "sheng", "shua", "shuo", "shuai", "shui", "shuan", "shun", "shuang", // r开头音节 "ra", "re", "ri", "ru", "rao", "rou", "ran", "ren", "rang", "reng", "rong", "rua", "ruo", "rui", "ruan", "run", // z开头音节 "za", "ze", "zi", "zu", "zai", "zei", "zao", "zou", "zan", "zen", "zang", "zeng", "zong", "zuo", "zui", "zuan", "zun", // c开头音节 "ca", "ce", "ci", "cu", "cai", "cao", "cou", "can", "cen", "cang", "ceng", "cong", "cuo", "cui", "cuan", "cun", // s开头音节 "sa", "se", "si", "su", "sai", "sao", "sou", "san", "sen", "sang", "seng", "song", "suo", "sui", "suan", "sun", // y开头音节 - 注意yi/you/yao等整体认读音节 "ya", "ye", "yi", "yo", "yu", "yue", "yao", "you", "yan", "yin", "yang", "ying", "yong", "yuan", "yun", // w开头音节 - 注意wu/wei等整体认读音节 "wa", "wo", "wu", "wai", "wei", "wan", "wen", "wang", "weng" }; // 特殊处理规则:单个字母'i', 'u', 'v', 'ü'不能单独成音节 if (cleanedInput.length() == 1) { QChar ch = cleanedInput.toLower()[0]; if (ch == 'i' || ch == 'u' || ch == 'v' || ch == QChar(0x00FC)) // 0x00FC是ü的Unicode编码 return false; } // 特殊处理规则:检查重复字母如'vvv' if (cleanedInput.length() >= 3) { bool allSame = true; QChar firstChar = cleanedInput.toLower()[0]; for (int i = 1; i < cleanedInput.length(); i++) { if (cleanedInput.toLower()[i] != firstChar) { allSame = false; break; } } if (allSame) return false; } QString str = cleanedInput.toLower(); str.replace("ü", "v"); // 统一处理 ü // 尝试所有可能的分割方式 return isPinyinSequenceHelper(str, 0, validSyllables); } bool isPinyinAcronymSequence(const QString &input) { if (input.isEmpty()) return false; QString str = input.trimmed(); // 长度检查 if (str.length() == 0 || str.length() > 255) return false; // 核心验证: // 1. 必须包含至少一个英文字母 // 2. 不能包含中文字符 // 3. 允许数字、符号等其他任意字符 bool hasLetter = false; for (const QChar &ch : str) { // 检查是否为中文(拼音缩写不应包含中文) if (ch.script() == QChar::Script_Han) return false; // 检查是否为英文字母(拉丁字母) if (ch.isLetter() && ch.script() == QChar::Script_Latin) hasLetter = true; } return hasLetter; } bool isHiddenPathOrInHiddenDir(const QString &absolutePath) { int start = 0; int len = absolutePath.length(); // 跳过根目录标记(如果是绝对路径) if (len > 0 && absolutePath[0] == '/') { start = 1; } for (int i = start; i < len; ++i) { // 找到路径分隔符或到达字符串末尾 if (absolutePath[i] == '/' || i == len - 1) { // 计算当前目录/文件名的起始位置 int nameStart = (start == i) ? start : start + 1; // 如果目录/文件名以.开头且不是"."或".." if (nameStart < i && absolutePath[nameStart] == '.') { // 排除 "." 和 ".." 这两个特殊目录 if (!(i - nameStart == 1 || (i - nameStart == 2 && absolutePath[nameStart + 1] == '.'))) { return true; } } // 更新下一段的起始位置 start = i; } } return false; } bool isSupportedContentSearchExtension(const QString &suffix) { return supportedExtensions().contains(suffix.toLower()); } QStringList defaultContentSearchExtensions() { return supportedExtensions().values(); } QStringList defaultIndexedDirectory() { static const QStringList dirs = getResolvedIndexedDirectories(); if (dirs.isEmpty()) { return QStringList(); } // 创建一个新列表来存储结果 QStringList result; // 先将所有路径规范化并排序,以便父目录出现在子目录之前 QStringList normalizedDirs; for (const QString &dir : dirs) { QString normalizedPath = QDir(dir).absolutePath(); // 确保路径以 '/' 结尾,便于后续比较 if (!normalizedPath.endsWith('/')) { normalizedPath += '/'; } normalizedDirs.append(normalizedPath); } // 排序,确保短路径(潜在的父路径)优先 std::sort(normalizedDirs.begin(), normalizedDirs.end(), [](const QString &a, const QString &b) { return a.length() < b.length(); }); // 检查路径之间的父子关系 for (const QString ¤tPath : normalizedDirs) { bool isSubdirectory = false; // 检查当前路径是否是已添加路径的子目录 for (const QString &addedPath : result) { if (currentPath.startsWith(addedPath)) { isSubdirectory = true; break; } } // 如果不是子目录,则添加到结果中 if (!isSubdirectory) { // 移除末尾的 '/',除非是根目录 "/" QString pathToAdd = currentPath; if (pathToAdd.length() > 1 && pathToAdd.endsWith('/')) { pathToAdd.chop(1); } result.append(pathToAdd); } } return result; } QStringList defaultBlacklistPaths() { std::optional dconfigPathsOpt = tryLoadBlacklistPathsFromDConfig(); if (!dconfigPathsOpt) { qDebug() << "Failed to load blacklist paths from DConfig or DConfig instance invalid, returning empty list."; return QStringList(); } const QStringList &pathsFromDConfig = *dconfigPathsOpt; qDebug() << "Resolved blacklist paths:" << pathsFromDConfig; return pathsFromDConfig; } /** * @brief Check if a path is within any of the specified directories. * @param path The path to check * @param dirs The directories to check against * @return true if the path is within any of the directories, false otherwise */ static bool isPathInAnyDirectory(const QString &path, const QStringList &dirs) { // Normalize the path once (it doesn't change during iteration) QString normalizedPath = path; if (normalizedPath.endsWith('/') && normalizedPath.length() > 1) { normalizedPath.chop(1); } return std::any_of(dirs.cbegin(), dirs.cend(), [&normalizedPath](const QString &dir) { QString normalizedDir = dir; if (normalizedDir.endsWith('/') && normalizedDir.length() > 1) { normalizedDir.chop(1); } // Exact match - the path is the indexed directory itself if (normalizedPath == normalizedDir) { return true; } // Check if path is within the directory by ensuring proper path separation const QString dirWithSeparator = normalizedDir + '/'; return normalizedPath.startsWith(dirWithSeparator); }); } bool isPathInContentIndexDirectory(const QString &path) { if (!isContentIndexAvailable()) return false; static const QStringList kDirs = DFMSEARCH::Global::defaultIndexedDirectory(); return isPathInAnyDirectory(path, kDirs); } bool isContentIndexAvailable() { const QString &dir = contentIndexDirectory(); if (!IndexReader::indexExists(FSDirectory::open(dir.toStdWString()))) return false; const QString &statusFile = dir + "/index_status.json"; // 1. 尝试打开文件 QFile file(statusFile); if (!file.open(QIODevice::ReadOnly)) { return false; // 文件无法打开 } // 2. 读取并解析 JSON QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); file.close(); if (doc.isNull() || !doc.isObject()) { return false; // JSON 格式无效 } // 3. 检查 lastUpdateTime 字段 QJsonObject obj = doc.object(); if (!obj.contains("lastUpdateTime")) { return false; // 字段不存在 } const QString lastUpdateTime = obj["lastUpdateTime"].toString(); return !lastUpdateTime.isEmpty(); // 字段值非空则为有效 } QString contentIndexDirectory() { QString dataDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); QDir deepinDir(dataDir); QString indexPath = deepinDir.filePath("deepin/dde-file-manager/fulltext-index"); return indexPath; } QString ocrTextIndexDirectory() { QString dataDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); QDir deepinDir(dataDir); QString indexPath = deepinDir.filePath("deepin/dde-file-manager/ocrtext-index"); return indexPath; } bool isPathInOcrTextIndexDirectory(const QString &path) { if (!isOcrTextIndexAvailable()) return false; static const QStringList kDirs = DFMSEARCH::Global::defaultIndexedDirectory(); return isPathInAnyDirectory(path, kDirs); } bool isOcrTextIndexAvailable() { const QString &dir = ocrTextIndexDirectory(); if (!IndexReader::indexExists(FSDirectory::open(dir.toStdWString()))) return false; const QString &statusFile = dir + "/index_status.json"; // 1. 尝试打开文件 QFile file(statusFile); if (!file.open(QIODevice::ReadOnly)) { return false; // 文件无法打开 } // 2. 读取并解析 JSON QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); file.close(); if (doc.isNull() || !doc.isObject()) { return false; // JSON 格式无效 } // 3. 检查 lastUpdateTime 字段 QJsonObject obj = doc.object(); if (!obj.contains("lastUpdateTime")) { return false; // 字段不存在 } const QString lastUpdateTime = obj["lastUpdateTime"].toString(); return !lastUpdateTime.isEmpty(); // 字段值非空则为有效 } bool isPathInFileNameIndexDirectory(const QString &path) { if (!isFileNameIndexDirectoryAvailable()) return false; static const QStringList kBlackPaths = defaultBlacklistPaths(); if (BlacklistMatcher::isPathBlacklisted(path, kBlackPaths)) return false; static const QStringList kIndexedDirs = DFMSEARCH::Global::defaultIndexedDirectory(); return isPathInAnyDirectory(path, kIndexedDirs); } bool isFileNameIndexDirectoryAvailable() { try { const QString &indexDir = fileNameIndexDirectory(); bool exists = IndexReader::indexExists(FSDirectory::open(indexDir.toStdWString())); return exists; } catch (const LuceneException &e) { qWarning() << "Failed to check index existence:" << QString::fromStdWString(e.getError()); return false; } } bool isFileNameIndexReadyForSearch() { // First check if the index physically exists if (!isFileNameIndexDirectoryAvailable()) { qDebug() << "Index directory does not exist physically."; return false; } // Then check the status to ensure it's in monitoring state std::optional currentStatus = fileNameIndexStatus(); if (!currentStatus) { qWarning() << "Failed to get file name index status."; return false; } const QStringList &validStatus = { "monitoring" }; const QString &status = currentStatus.value(); if (!validStatus.contains(status)) { qDebug() << "Index status is '" << status << "', expected or 'monitoring'. Index not ready for search."; return false; } return true; } std::optional fileNameIndexStatus() { if (!isFileNameIndexDirectoryAvailable()) { qWarning() << "Index directory not available"; return std::nullopt; } const QString &statusFilePath = QDir(fileNameIndexDirectory()).filePath("status.json"); QFile statusFile(statusFilePath); // 检查文件是否存在和可读 if (!statusFile.exists()) { qWarning() << "Status file does not exist:" << statusFilePath; return std::nullopt; } if (!statusFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "Failed to open status file:" << statusFile.errorString(); return std::nullopt; } // 读取和解析JSON QJsonParseError jsonError; const QJsonDocument doc = QJsonDocument::fromJson(statusFile.readAll(), &jsonError); statusFile.close(); if (jsonError.error != QJsonParseError::NoError) { qWarning() << "JSON parse error:" << jsonError.errorString() << "at offset:" << jsonError.offset; return std::nullopt; } // 检查JSON结构 if (!doc.isObject()) { qWarning() << "Invalid JSON format: root is not an object"; return std::nullopt; } const QJsonObject root = doc.object(); if (!root.contains("status") || !root["status"].isString()) { qWarning() << "Missing or invalid 'status' field"; return std::nullopt; } // 返回小写状态 return root["status"].toString().toLower(); } QString fileNameIndexDirectory() { return QString("/run/user/%1/deepin-anything-server").arg(getuid()); } int fileNameIndexVersion() { return readIndexVersion(fileNameIndexDirectory(), "status.json"); } int contentIndexVersion() { return readIndexVersion(contentIndexDirectory(), "index_status.json"); } int ocrTextIndexVersion() { return readIndexVersion(ocrTextIndexDirectory(), "index_status.json"); } } // namespace Global namespace SearchUtility { QStringList extractBooleanKeywords(const SearchQuery &query) { QStringList keywords; if (query.type() == SearchQuery::Type::Boolean) { // 布尔查询,提取所有子查询的关键词 for (const auto &subQuery : query.subQueries()) { keywords.append(subQuery.keyword()); } // 如果没有子查询,添加主关键词 if (keywords.isEmpty()) { keywords.append(query.keyword()); } } else { // 简单查询,直接添加关键词 keywords.append(query.keyword()); } // 移除空关键词 keywords.removeAll(""); return keywords; } QStringList deepinAnythingFileTypes() { static const QStringList kTypes { "app", "archive", "audio", "doc", "pic", "video", "dir", "other" }; return kTypes; } QSet semanticDocExtensions() { static const QSet kExts = DFMSEARCH::Global::loadSemanticExtensionsFromDConfig("doc_file_suffix"); return kExts; } QSet semanticPicExtensions() { static const QSet kExts = DFMSEARCH::Global::loadSemanticExtensionsFromDConfig("pic_file_suffix"); return kExts; } std::optional parseVariantToStringList(const QVariant &value) { if (!value.isValid()) { return std::nullopt; } if (!value.canConvert()) { return std::nullopt; } // DConfig value 可能是 JSON 数组(list 类型)或分号分隔字符串; // 当为 JSON 数组时 toString() 返回空,必须用 toStringList()。 if (value.type() == QVariant::List || value.type() == QVariant::StringList) { return value.toStringList(); } return value.toString().split(';'); } } // namespace SearchUtility DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/searchutility.h000066400000000000000000000041351523477242100276750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEARCHUTILITY_H #define SEARCHUTILITY_H #include #include #include #include #include // SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include #include DFM_SEARCH_BEGIN_NS /** * @brief The SearchUtility namespace provides utility functions for search operations */ namespace SearchUtility { /** * @brief Extract keywords from a boolean search query * @param query The search query to process * @return List of extracted keywords */ QStringList extractBooleanKeywords(const SearchQuery &query); /** * @brief Parse a DConfig QVariant value into a QStringList * * DConfig values may arrive as a semicolon-separated string or as a JSON array * (QVariant::List / QVariant::StringList). When the value is a list type, * toString() returns an empty string, so toStringList() must be used instead. * * @param value The QVariant from DConfig::value() * @return The parsed string list, or std::nullopt if the value is invalid or not convertible */ std::optional parseVariantToStringList(const QVariant &value); /** * @brief Get the list of supported file types for Deepin Anything * @return List of supported file types */ QStringList deepinAnythingFileTypes(); /** * @brief Get document file extensions for semantic search routing * * Loads from dconfig `org.deepin.anything` key `doc_file_suffix`. * @return Set of lowercase extensions (without leading dot), empty if dconfig unavailable */ QSet semanticDocExtensions(); /** * @brief Get image file extensions for semantic search routing * * Loads from dconfig `org.deepin.anything` key `pic_file_suffix`. * @return Set of lowercase extensions (without leading dot), empty if dconfig unavailable */ QSet semanticPicExtensions(); } // namespace SearchUtility DFM_SEARCH_END_NS #endif // SEARCHUTILITY_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/semanticvalue.cpp000066400000000000000000000101211523477242100301670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "semanticvalue_p.h" #include DFM_SEARCH_BEGIN_NS // ── MatchSpan ────────────────────────────────────────────────────── MatchSpan::MatchSpan() : d(new MatchSpanPrivate) { } MatchSpan::~MatchSpan() = default; MatchSpan::MatchSpan(const MatchSpan &other) = default; MatchSpan &MatchSpan::operator=(const MatchSpan &other) = default; MatchSpan::MatchSpan(MatchSpan &&other) noexcept = default; MatchSpan &MatchSpan::operator=(MatchSpan &&other) noexcept = default; int MatchSpan::start() const { return d->start; } void MatchSpan::setStart(int start) { d->start = start; } int MatchSpan::end() const { return d->end; } void MatchSpan::setEnd(int end) { d->end = end; } QString MatchSpan::ruleId() const { return d->ruleId; } void MatchSpan::setRuleId(const QString &ruleId) { d->ruleId = ruleId; } bool MatchSpan::isValid() const { return d->start >= 0 && d->end > d->start; } // ── TimeConstraint ───────────────────────────────────────────────── TimeConstraint::TimeConstraint() : d(new TimeConstraintPrivate) { } TimeConstraint::~TimeConstraint() = default; TimeConstraint::TimeConstraint(const TimeConstraint &other) = default; TimeConstraint &TimeConstraint::operator=(const TimeConstraint &other) = default; TimeConstraint::TimeConstraint(TimeConstraint &&other) noexcept = default; TimeConstraint &TimeConstraint::operator=(TimeConstraint &&other) noexcept = default; TimeConstraintKind TimeConstraint::kind() const { return d->kind; } void TimeConstraint::setKind(TimeConstraintKind kind) { d->kind = kind; } TimePreset TimeConstraint::preset() const { return d->preset; } void TimeConstraint::setPreset(TimePreset preset) { d->preset = preset; } int TimeConstraint::relativeValue() const { return d->relativeValue; } void TimeConstraint::setRelativeValue(int value) { d->relativeValue = value; } TimeUnit TimeConstraint::relativeUnit() const { return d->relativeUnit; } void TimeConstraint::setRelativeUnit(TimeUnit unit) { d->relativeUnit = unit; } QDateTime TimeConstraint::customStart() const { return d->customStart; } void TimeConstraint::setCustomStart(const QDateTime &start) { d->customStart = start; } QDateTime TimeConstraint::customEnd() const { return d->customEnd; } void TimeConstraint::setCustomEnd(const QDateTime &end) { d->customEnd = end; } TimeField TimeConstraint::timeField() const { return d->timeField; } void TimeConstraint::setTimeField(TimeField field) { d->timeField = field; } bool TimeConstraint::isValid() const { return d->kind != TimeConstraintKind::None; } // ── SizeConstraint ───────────────────────────────────────────────── SizeConstraint::SizeConstraint() : d(new SizeConstraintPrivate) { } SizeConstraint::~SizeConstraint() = default; SizeConstraint::SizeConstraint(const SizeConstraint &other) = default; SizeConstraint &SizeConstraint::operator=(const SizeConstraint &other) = default; SizeConstraint::SizeConstraint(SizeConstraint &&other) noexcept = default; SizeConstraint &SizeConstraint::operator=(SizeConstraint &&other) noexcept = default; qint64 SizeConstraint::minSize() const { return d->minSize; } void SizeConstraint::setMinSize(qint64 size) { d->minSize = size; } qint64 SizeConstraint::maxSize() const { return d->maxSize; } void SizeConstraint::setMaxSize(qint64 size) { d->maxSize = size; } bool SizeConstraint::includeLower() const { return d->includeLower; } void SizeConstraint::setIncludeLower(bool include) { d->includeLower = include; } bool SizeConstraint::includeUpper() const { return d->includeUpper; } void SizeConstraint::setIncludeUpper(bool include) { d->includeUpper = include; } bool SizeConstraint::isValid() const { return d->minSize > 0 || d->maxSize > 0; } DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/semanticvalue_p.h000066400000000000000000000021411523477242100301560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SEMANTICVALUE_P_H #define SEMANTICVALUE_P_H #include #include #include #include #include DFM_SEARCH_BEGIN_NS class MatchSpanPrivate : public QSharedData { public: MatchSpanPrivate() = default; int start = -1; int end = -1; QString ruleId; }; class TimeConstraintPrivate : public QSharedData { public: TimeConstraintPrivate() = default; TimeConstraintKind kind = TimeConstraintKind::None; TimePreset preset = TimePreset::Today; int relativeValue = 0; TimeUnit relativeUnit = TimeUnit::Days; QDateTime customStart; QDateTime customEnd; TimeField timeField = TimeField::Unspecified; }; class SizeConstraintPrivate : public QSharedData { public: SizeConstraintPrivate() = default; qint64 minSize = 0; qint64 maxSize = 0; bool includeLower = true; bool includeUpper = true; }; DFM_SEARCH_END_NS #endif // SEMANTICVALUE_P_H linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/timerangeutils.cpp000066400000000000000000000051701523477242100303730ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "timerangeutils.h" #include #include #include DFM_SEARCH_BEGIN_NS namespace TimeRangeUtils { qint64 toEpochSecs(const QDateTime &dt) { if (!dt.isValid()) { return 0; } return dt.toSecsSinceEpoch(); } Lucene::QueryPtr buildNumericRangeQuery( const wchar_t *fieldName, qint64 startEpoch, qint64 endEpoch, bool includeLower, bool includeUpper) { // Use INT64_MIN and INT64_MAX for unbounded ranges int64_t minVal = (startEpoch == 0) ? INT64_MIN : startEpoch; int64_t maxVal = (endEpoch == 0) ? INT64_MAX : endEpoch; // Use the default precisionStep (4) return Lucene::NumericRangeQuery::newLongRange( Lucene::String(fieldName), minVal, maxVal, includeLower, includeUpper); } Lucene::QueryPtr buildTimeRangeFilterQuery( const TimeRangeFilter &filter, const wchar_t *birthTimeField, const wchar_t *modifyTimeField) { if (!filter.isValid()) { return nullptr; } auto [start, end] = filter.resolveTimeRange(); qint64 startEpoch = toEpochSecs(start); qint64 endEpoch = toEpochSecs(end); if (filter.timeField() == TimeField::Both) { // Build BooleanQuery with SHOULD for both time fields Lucene::BooleanQueryPtr timeBoolQuery = Lucene::newLucene(); Lucene::QueryPtr birthQuery = buildNumericRangeQuery( birthTimeField, startEpoch, endEpoch, filter.includeLower(), filter.includeUpper()); if (birthQuery) { timeBoolQuery->add(birthQuery, Lucene::BooleanClause::SHOULD); } Lucene::QueryPtr modifyQuery = buildNumericRangeQuery( modifyTimeField, startEpoch, endEpoch, filter.includeLower(), filter.includeUpper()); if (modifyQuery) { timeBoolQuery->add(modifyQuery, Lucene::BooleanClause::SHOULD); } // Need at least one clause for a valid BooleanQuery return (birthQuery || modifyQuery) ? timeBoolQuery : nullptr; } // Single field query const wchar_t *fieldName = (filter.timeField() == TimeField::BirthTime) ? birthTimeField : modifyTimeField; return buildNumericRangeQuery( fieldName, startEpoch, endEpoch, filter.includeLower(), filter.includeUpper()); } } // namespace TimeRangeUtils DFM_SEARCH_END_NS linuxdeepin-util-dfm-a4307d3/src/dfm-search/dfm-search-lib/utils/timerangeutils.h000066400000000000000000000043471523477242100300450ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TIMERANGEUTILS_H #define TIMERANGEUTILS_H #include #include #include DFM_SEARCH_BEGIN_NS class TimeRangeFilter; /** * @brief TimeRangeUtils provides utility functions for time range operations */ namespace TimeRangeUtils { /** * @brief Convert QDateTime to Unix timestamp in seconds * @param dt The datetime to convert * @return Unix timestamp in seconds (0 if invalid) */ qint64 toEpochSecs(const QDateTime &dt); /** * @brief Build a Lucene NumericRangeQuery for time range filtering * @param fieldName The field name to query (e.g., L"birth_time", L"modify_time") * @param startEpoch The start timestamp (in seconds), use 0 for no lower bound * @param endEpoch The end timestamp (in seconds), use INT64_MAX for no upper bound * @param includeLower Whether to include the lower bound * @param includeUpper Whether to include the upper bound * @return A NumericRangeQuery pointer, or nullptr if range is invalid */ Lucene::QueryPtr buildNumericRangeQuery( const wchar_t *fieldName, qint64 startEpoch, qint64 endEpoch, bool includeLower, bool includeUpper); /** * @brief Build a Lucene query for time range filtering, supporting TimeField::Both * * When filter.timeField() is TimeField::Both, this builds a BooleanQuery with SHOULD * clauses for both birth_time and modify_time fields. Otherwise, it builds a single * NumericRangeQuery for the specified field. * * The returned query is designed to be added to an outer query with BooleanClause::MUST. * * @param filter The time range filter containing field selection and range * @param birthTimeField The Lucene field name for birth time * @param modifyTimeField The Lucene field name for modification time * @return A Lucene query (single NumericRangeQuery or BooleanQuery for Both), or nullptr if invalid */ Lucene::QueryPtr buildTimeRangeFilterQuery( const TimeRangeFilter &filter, const wchar_t *birthTimeField, const wchar_t *modifyTimeField); } // namespace TimeRangeUtils DFM_SEARCH_END_NS #endif // TIMERANGEUTILS_H