जैसे ही आप बॉरो-चेकर के दर्द की सीमा को पार करते हैं और महसूस करते हैं कि रस्ट आपको ऐसी चीजें करने की अनुमति देता है जो अन्य भाषाओं में अकल्पनीय (और कभी-कभी खतरनाक) होती हैं, तो आपके पास रिस्ट एवरीथिंग टू रस्ट को रिवाइस्ट करने की एक ही अथक इच्छा हो सकती है। हालांकि सबसे अच्छे मामले में यह अनुत्पादक है (कई परियोजनाओं के लिए व्यर्थ व्यर्थ प्रयास), सबसे खराब में यह कोड की गुणवत्ता में कमी की ओर जाता है (आखिरकार, आप अपने मूल लेखक के लिए पुस्तकालय का उपयोग करने के क्षेत्र में खुद को अधिक अनुभवी क्यों मानते हैं?)
अपने कोड का पुन: उपयोग करके मूल लाइब्रेरी के लिए एक सुरक्षित इंटरफ़ेस प्रदान करना अधिक उपयोगी होगा।
• पहला कदम
• हम chmlib-sys इकट्ठा करते हैं
• रस्ट में एक सुरक्षित आवरण लिखना
• नाम से आइटम के लिए खोजें
• फ़िल्टर द्वारा बायपास तत्वों
• फ़ाइल सामग्री पढ़ना
• उदाहरण जोड़ें
• सामग्री की सीएचएम फ़ाइल तालिका
• सीएचएम फ़ाइल को डिस्क पर अनपैक करना
• आगे क्या?
यह लेख एक वास्तविक परियोजना पर चर्चा करता है। मुझे मौजूदा सीएचएम फाइलों से जानकारी निकालनी थी, लेकिन प्रारूप को समझने का समय नहीं था। आलस्य प्रगति का इंजन है।
Chmlib टोकरा crates.io पर प्रकाशित किया गया है , और इसका स्रोत कोड GitHub पर उपलब्ध है । यदि आप इसे उपयोगी पाते हैं या इसमें समस्याएं पाते हैं, तो मुझे बगट्रैकर के माध्यम से बताएं।
पहला कदम
शुरू करने के लिए, यह समझने के लायक है कि पुस्तकालय के साथ काम की मूल रूप से कल्पना कैसे की गई थी।
यह आपको न केवल इसका उपयोग करना सिखाएगा, बल्कि यह भी सुनिश्चित करेगा कि सब कुछ हो रहा है। यदि आप भाग्यशाली हैं, तो आपको तैयार परीक्षण और उदाहरण भी मिलेंगे।
इस कदम को छोड़ो मत!
हम CHMLib के साथ काम करेंगे, Microsoft संकलित HTML मदद ( .chm
) फ़ाइलों को पढ़ने के लिए एक सी लाइब्रेरी।
चलो एक नई परियोजना बनाकर और CHMLib को गिट सबमॉडल के रूप में जोड़कर शुरू करते हैं:
$ git init chmlib && cd chmlib Initialized empty Git repository in /home/michael/Documents/chmlib/.git/ $ touch README.md Cargo.toml $ cargo new --lib chmlib Created library `chmlib` package $ cargo new --lib chmlib-sys Created library `chmlib-sys` package $ cat Cargo.toml [workspace] members = ["chmlib", "chmlib-sys"] $ git submodule add git@github.com:jedwing/CHMLib.git vendor/CHMLib Cloning into '/home/michael/Documents/chmlib/vendor/CHMLib'... remote: Enumerating objects: 99, done. remote: Total 99 (delta 0), reused 0 (delta 0), pack-reused 99 Receiving objects: 100% (99/99), 375.51 KiB | 430.00 KiB/s, done. Resolving deltas: 100% (45/45), done.
उसके बाद, tree
के अंदर क्या है, इस पर एक नज़र डालें:
$ tree vendor/CHMLib vendor/CHMLib ├── acinclude.m4 ├── AUTHORS ├── ChangeLog ├── ChmLib-ce.zip ├── ChmLib-ds6.zip ├── configure.in ├── contrib │ └── mozilla_helper.sh ├── COPYING ├── Makefile.am ├── NEWS ├── NOTES ├── README └── src ├── chm_http.c ├── chm_lib.c ├── chm_lib.h ├── enum_chmLib.c ├── enumdir_chmLib.c ├── extract_chmLib.c ├── lzx.c ├── lzx.h ├── Makefile.am ├── Makefile.simple └── test_chmLib.c 2 directories, 23 files
ऐसा लगता है कि लाइब्रेरी का निर्माण करने के लिए GNU ऑटोटूल का उपयोग करता है। यह अच्छा नहीं है, क्योंकि chmlib crate (और उनके उपयोगकर्ताओं) के सभी उपयोगकर्ताओं को ऑटोटूल स्थापित करने की आवश्यकता होगी।
हम सी कोड को मैन्युअल रूप से इकट्ठा करके इस "संक्रामक" निर्भरता से छुटकारा पाने की कोशिश करेंगे, लेकिन बाद में और अधिक।
Lzx.h और lzx.c फाइलों में LZX कम्प्रेशन एल्गोरिथ्म का कार्यान्वयन होता है। सामान्य तौर पर, मुफ्त में और सभी के लिए अपडेट प्राप्त करने के लिए कुछ प्रकार के लिब्ज़ज़ लाइब्रेरी का उपयोग करना बेहतर होगा, लेकिन शायद इन फ़ाइलों को बेवकूफ़ बनाना आसान होगा।
enum_chmLib.c, enumdir_chmLib.c, extract_chmLib.c, chm_enumerate (), chm_retumerate_dir (), chm_retrieve_object () का उपयोग करके कार्यों के उदाहरण प्रतीत होते हैं। यह काम आएगा ...
फ़ाइल test_chmLib.c में एक और उदाहरण है, इस बार CHM फ़ाइल से डिस्क में एक पृष्ठ को निकालने पर।
chm_http.c किसी ब्राउज़र में एक .chm फ़ाइल दिखाने वाला एक सरल HTTP सर्वर लागू करता है। यह, शायद, अब उपयोगी नहीं होगा।
इसलिए हमने वेंडर / CHMLib / src में मौजूद हर चीज़ को हल किया। क्या हम पुस्तकालय इकट्ठा करेंगे?
ईमानदारी से, यह वैज्ञानिक प्रहार विधि को लागू करने के लिए काफी छोटा है।
$ clang chm_lib.c enum_chmLib.c -o enum_chmLib /usr/bin/ld: /tmp/chm_lib-537dfe.o: in function `chm_close': chm_lib.c:(.text+0x8fa): undefined reference to `LZXteardown' /usr/bin/ld: /tmp/chm_lib-537dfe.o: in function `_chm_decompress_region': chm_lib.c:(.text+0x18ca): undefined reference to `LZXinit' /usr/bin/ld: /tmp/chm_lib-537dfe.o: in function `_chm_decompress_block': chm_lib.c:(.text+0x2900): undefined reference to `LZXreset' /usr/bin/ld: chm_lib.c:(.text+0x2a4b): undefined reference to `LZXdecompress' /usr/bin/ld: chm_lib.c:(.text+0x2abe): undefined reference to `LZXreset' /usr/bin/ld: chm_lib.c:(.text+0x2bf4): undefined reference to `LZXdecompress' clang: error: linker command failed with exit code 1 (use -v to see invocation)
ठीक है, शायद यह LZX अभी भी जरूरत है ...
$ clang chm_lib.c enum_chmLib.c lzx.c -o enum_chmLib
उह ... सब?
यह सुनिश्चित करने के लिए कि कोड काम कर रहा है, मैंने इंटरनेट से एक उदाहरण डाउनलोड किया:
$ curl http://www.innovasys.com/static/hs/samples/topics.classic.chm.zip \ -o topics.classic.chm.zip $ unzip topics.classic.chm.zip Archive: topics.classic.chm.zip inflating: output/compiled/topics.classic.chm $ file output/compiled/topics.classic.chm output/compiled/topics.classic.chm: MS Windows HtmlHelp Data
आइए देखें कि enum_chmLib इसे कैसे संभालता है:
$ ./enum_chmLib output/compiled/topics.classic.chm output/compiled/topics.classic.chm: spc start length type name === ===== ====== ==== ==== 0 0 0 normal dir / 1 5125797 4096 special file /#IDXHDR ... 1 4944434 11234 normal file /BrowserView.html ... 0 0 0 normal dir /flash/ 1 532689 727 normal file /flash/expressinstall.swf 0 0 0 normal dir /Images/Commands/RealWorld/ 1 24363 1254 normal file /Images/Commands/RealWorld/BrowserBack.bmp ... 1 35672 1021 normal file /Images/Employees24.gif ... 1 3630715 200143 normal file /template/packages/jquery-mobile/script/ jquery.mobile-1.4.5.min.js ... 0 134 1296 meta file ::DataSpace/Storage/MSCompressed/Transform/ {7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/ InstanceData/ResetTable
भगवान, यहां तक कि jQuery के (\ _ (_) _ / Query
Chmlib-sys बनाएँ
अब हम chmlib -sys टोकरे में CHMLib का उपयोग करने के लिए पर्याप्त जानते हैं, जो कि देशी पुस्तकालय के निर्माण के लिए जिम्मेदार है, इसे Rast कंपाइलर और C फ़ंक्शंस के लिए एक इंटरफ़ेस से जोड़ता है।
लाइब्रेरी बनाने के लिए आपको build.rs
फाइल लिखना होगा। Cc crate का उपयोग करते हुए , वह C कंपाइलर को कॉल करेगा और अन्य दोस्ती करेगा ताकि सब कुछ एक साथ काम करे जैसा कि उसे करना चाहिए।
हम भाग्यशाली हैं कि हम अधिकांश काम को सीसी में स्थानांतरित कर सकते हैं, लेकिन कभी-कभी यह अधिक कठिन होता है। असेंबली स्क्रिप्ट के लिए प्रलेखन में अधिक पढ़ें।
पहले chmlib-sys के लिए एक निर्भरता के रूप में cc जोड़ें:
$ cd chmlib-sys $ cargo add --build cc Updating 'https://github.com/rust-lang/crates.io-index' index Adding cc v1.0.46 to build-dependencies
फिर हम build.rs
:
आपको कार्गो को यह भी बताना होगा कि chmlib-sys chmlib लाइब्रेरी से लिंक करता है। तब कार्गो गारंटी दे सकता है कि पूरे निर्भरता ग्राफ में विशिष्ट देशी पुस्तकालय के आधार पर केवल एक रैक है। यह बार-बार पात्रों या असंगत पुस्तकालयों के आकस्मिक उपयोग के बारे में अस्पष्ट संदेशों से बचा जाता है।
इसके बाद, हमें chmlib पुस्तकालय द्वारा निर्यात किए गए सभी कार्यों को घोषित करने की आवश्यकता है ताकि उनका उपयोग Rast से किया जा सके।
यह इस के लिए है कि एक अद्भुत बाइंडजेन परियोजना है। C हैडर फ़ाइल इनपुट को दी गई है, और Rast के लिए FFI बाइंडिंग वाली फाइल आउटपुट है।
$ cargo install bindgen $ bindgen ../vendor/CHMLib/src/chm_lib.h \ -o src/lib.rs \ --raw-line '#![allow(non_snake_case, non_camel_case_types)]' $ head src/lib.rs /* automatically generated by rust-bindgen */ #![allow(non_snake_case, non_camel_case_types)] pub const CHM_UNCOMPRESSED: u32 = 0; pub const CHM_COMPRESSED: u32 = 1; pub const CHM_MAX_PATHLEN: u32 = 512; pub const CHM_PARAM_MAX_BLOCKS_CACHED: u32 = 0; pub const CHM_RESOLVE_SUCCESS: u32 = 0; pub const CHM_RESOLVE_FAILURE: u32 = 1; $ tail src/lib.rs extern "C" { pub fn chm_enumerate_dir( h: *mut chmFile, prefix: *const ::std::os::raw::c_char, what: ::std::os::raw::c_int, e: CHM_ENUMERATOR, context: *mut ::std::os::raw::c_void, ) -> ::std::os::raw::c_int; }
यदि आप इसके निकास में कुछ ठीक करने की आवश्यकता है, तो मैं Bindgen उपयोगकर्ता पुस्तिका पढ़ने की अत्यधिक सलाह देता हूं।
इस स्तर पर, यह एक धूम्रपान परीक्षण लिखने के लिए उपयोगी होगा जो यह सत्यापित करेगा कि सब कुछ उम्मीद के मुताबिक काम करता है और हम वास्तव में मूल सी लाइब्रेरी के कार्यों को कॉल कर सकते हैं।
cargo test
कहता है कि सब कुछ क्रम में लगता है:
$ cargo test Finished test [unoptimized + debuginfo] target(s) in 0.03s Running ~/chmlib/target/debug/deps/chmlib_sys-2ffd7b11a9fd8437 running 1 test test bindgen_test_layout_chmUnitInfo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Running ~/chmlib/target/debug/deps/smoke_test-f7be9810412559dc running 1 test test open_example_file ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Doc-tests chmlib-sys running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
रस्ट में एक सुरक्षित आवरण लिखना
तकनीकी रूप से और तकनीकी रूप से, अब हम Rasta से CHMLib कह सकते हैं, लेकिन इसके लिए एक असुरक्षित ढेर की आवश्यकता है। यह हैक किए गए शिल्प के लिए काम कर सकता है, लेकिन crates.io पर प्रकाशन के लिए यह सभी असुरक्षित कोड के लिए एक सुरक्षित आवरण लिखने के लायक है।
यदि आप cargo doc --open
का उपयोग करते हुए chmlib-sys API को देखते हैं, तो आप कई कार्यों को देख सकते हैं जो पहले तर्क के रूप में *mut ChmFile
लेते हैं। यह वस्तुओं और विधियों के समान है।
CHMLib हैडर फ़ाइल #ifndef INCLUDED_CHMLIB_H #define INCLUDED_CHMLIB_H #ifdef __cplusplus extern "C" { #endif #ifdef PPC_BSTR #include <wtypes.h> #endif #ifdef WIN32 #ifdef __MINGW32__ #define __int64 long long #endif typedef unsigned __int64 LONGUINT64; typedef __int64 LONGINT64; #else typedef unsigned long long LONGUINT64; typedef long long LONGINT64; #endif /* the two available spaces in a CHM file */ /* NB: The format supports arbitrarily many spaces, but only */ /* two appear to be used at present. */ #define CHM_UNCOMPRESSED (0) #define CHM_COMPRESSED (1) /* structure representing an ITS (CHM) file stream */ struct chmFile; /* structure representing an element from an ITS file stream */ #define CHM_MAX_PATHLEN (512) struct chmUnitInfo { LONGUINT64 start; LONGUINT64 length; int space; int flags; char path[CHM_MAX_PATHLEN+1]; }; /* open an ITS archive */ #ifdef PPC_BSTR /* RWE 6/12/2003 */ struct chmFile* chm_open(BSTR filename); #else struct chmFile* chm_open(const char *filename); #endif /* close an ITS archive */ void chm_close(struct chmFile *h); /* methods for ssetting tuning parameters for particular file */ #define CHM_PARAM_MAX_BLOCKS_CACHED 0 void chm_set_param(struct chmFile *h, int paramType, int paramVal); /* resolve a particular object from the archive */ #define CHM_RESOLVE_SUCCESS (0) #define CHM_RESOLVE_FAILURE (1) int chm_resolve_object(struct chmFile *h, const char *objPath, struct chmUnitInfo *ui); /* retrieve part of an object from the archive */ LONGINT64 chm_retrieve_object(struct chmFile *h, struct chmUnitInfo *ui, unsigned char *buf, LONGUINT64 addr, LONGINT64 len); /* enumerate the objects in the .chm archive */ typedef int (*CHM_ENUMERATOR)(struct chmFile *h, struct chmUnitInfo *ui, void *context); #define CHM_ENUMERATE_NORMAL (1) #define CHM_ENUMERATE_META (2) #define CHM_ENUMERATE_SPECIAL (4) #define CHM_ENUMERATE_FILES (8) #define CHM_ENUMERATE_DIRS (16) #define CHM_ENUMERATE_ALL (31) #define CHM_ENUMERATOR_FAILURE (0) #define CHM_ENUMERATOR_CONTINUE (1) #define CHM_ENUMERATOR_SUCCESS (2) int chm_enumerate(struct chmFile *h, int what, CHM_ENUMERATOR e, void *context); int chm_enumerate_dir(struct chmFile *h, const char *prefix, int what, CHM_ENUMERATOR e, void *context); #ifdef __cplusplus } #endif #endif /* INCLUDED_CHMLIB_H */
आइए डेटा प्रकार के साथ शुरू करें, जो विधायक में chm_open () और विध्वंसक में chm_close () कहता है।
pub unsafe extern "C" fn chm_open(filename: *const c_char) -> *mut chmFile; pub unsafe extern "C" fn chm_close(h: *mut chmFile);
त्रुटि से निपटने को सरल बनाने के लिए, हम थेरेटर टोकरा का उपयोग करते हैं, जो std::error::Error
को स्वचालित रूप से लागू करता है।
$ cd chmlib $ cargo add thiserror
अब आपको यह पता लगाने की आवश्यकता है कि std::path::Path
*const c_char
। दुर्भाग्य से, संगतता के साथ विभिन्न चुटकुलों के कारण ऐसा करना आसान नहीं है।
अब ChmFile की संरचना को परिभाषित करें। यह chmlib_sys :: chmFile में एक नॉन-नाल पॉइंटर स्टोर करता है। यदि chm_open () एक अशक्त पॉइंटर लौटाता है, तो इसका मतलब है कि वह किसी प्रकार की त्रुटि के कारण फ़ाइल को नहीं खोल सकता है।
यह सुनिश्चित करने के लिए कि कोई मेमोरी लीक नहीं है, Valgrind के तहत एक साधारण परीक्षण चलाएं। वह एक ChmFile बनाएगा और उसे तुरंत रिलीज़ करेगा।
वैलग्राइंड का कहना है कि अब कोई अनकही स्मृति नहीं बची है:
$ valgrind ../target/debug/deps/chmlib-8d8c740d578324 open_valid_chm_file ==8953== Memcheck, a memory error detector ==8953== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==8953== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info ==8953== Command: ~/chmlib/target/debug/deps/chmlib-8d8c740d578324 open_valid_chm_file ==8953== running 1 test test tests::open_valid_chm_file ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ==8953== ==8953== HEAP SUMMARY: ==8953== in use at exit: 0 bytes in 0 blocks ==8953== total heap usage: 249 allocs, 249 frees, 43,273 bytes allocated ==8953== ==8953== All heap blocks were freed -- no leaks are possible ==8953== ==8953== For counts of detected and suppressed errors, rerun with: -v ==8953== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
नाम से आइटम खोजें
पंक्ति में अगला chm_resolve_object () फ़ंक्शन है:
pub const CHM_RESOLVE_SUCCESS: u32 = 0; pub const CHM_RESOLVE_FAILURE: u32 = 1; pub unsafe extern "C" fn chm_resolve_object( h: *mut chmFile, objPath: *const c_char, ui: *mut chmUnitInfo ) -> c_int;
खोज विफल हो सकती है, इसलिए chm_resolve_object () सफलता या विफलता की रिपोर्ट करने में त्रुटि कोड देता है, और मिली हुई वस्तु के बारे में जानकारी chmUnitInfo को दिए गए सूचक द्वारा दर्ज की जाएगी ।
प्रकार std::mem::MaybeUninit
केवल हमारे मामले के लिए आउट पैरामीटर यूआई के साथ बनाया std::mem::MaybeUninit
।
अभी के लिए, चलो UnitInfo संरचना को खाली छोड़ दें - यह chmUnitInfo C संरचना के बराबर है। जब हम ChmFile से पढ़ना शुरू करेंगे तो हम फ़ील्ड जोड़ देंगे।
ध्यान दें कि ChmFile :: ढूँढें () स्वीकार करता है &mut self
पारस्परिक रूप से, हालांकि रास्ट पर कोड में एक स्पष्ट राज्य परिवर्तन नहीं होता है। तथ्य यह है कि सी कार्यान्वयन फ़ाइल के चारों ओर ले जाने के लिए सभी प्रकार के fseek () का उपयोग करता है, इसलिए खोज के दौरान आंतरिक स्थिति अभी भी बदलती है।
चलिए ChmFile :: प्रायोगिक फ़ाइल जो हमने पहले डाउनलोड की थी, पर देखें:
बायपास आइटम को फ़िल्टर करें
CHMLB एक बिटमस्क फ़िल्टर के माध्यम से CHM फ़ाइल की सामग्री को देखने के लिए एक एपीआई प्रदान करता है।
मास्क और झंडे के साथ काम करने के लिए सुविधाजनक बिटफ़्लैग क्रेट लें:
$ cargo add bitflags Updating 'https://github.com/rust-lang/crates.io-index' index Adding bitflags v1.2.1 to dependencies
और chm_lib.h से स्थिरांक के आधार पर फ़िल्टर चेकबॉक्स को परिभाषित करें:
हमें रस्तोवीह क्लोजर के लिए एक बाहरी extern "C"
एडेप्टर की भी आवश्यकता है, जिसे एक फ़ंक्शन के लिए पॉइंटर के रूप में सी पास किया जा सकता है:
function_wrapper
में एक मुश्किल असुरक्षित कोड है, जिसका आपको उपयोग करने में सक्षम होना चाहिए:
state
सूचक को बंद करने के उदाहरण को इंगित करना चाहिए एफ।- एक बंद द्वारा निष्पादित रस्ता कोड आतंक पैदा कर सकता है। यह रास्ट और सी के बीच की सीमा को पार नहीं करना चाहिए, क्योंकि विभिन्न भाषाओं में स्टैक प्रमोशन अपरिभाषित व्यवहार है। एक संभावित आतंक को
std::panic::catch_unwind()
का उपयोग करके इंटरसेप्ट किया जाना चाहिए std::panic::catch_unwind()
। - Chmlib_sys का एक पॉइंटर :: chmFile function_wrapper को दिया जाता है, जिसे कॉलिंग ChFFile में भी संग्रहीत किया जाता है। कॉल की अवधि के लिए, आपको यह सुनिश्चित करना होगा कि केवल क्लोजर chmlib_sys :: chmFile में हेरफेर कर सकता है, अन्यथा रेस की स्थिति हो सकती है।
- क्लोजर को पास
&mut ChmFile
पास करने की आवश्यकता है, और इसके लिए आपको मौजूदा पॉइंटर का उपयोग करके स्टैक पर एक अस्थायी ऑब्जेक्ट बनाने की आवश्यकता होगी। हालाँकि, अगर इस मामले में ChmFile विध्वंसक चलता है, तो chmlib_sys :: chmFile को भी जल्द ही मुक्त कर दिया जाएगा। इस समस्या को हल करने के लिए, std::mem::ManuallyDrop
।
यह है कि ChmFile::for_each()
को लागू करने के लिए कैसे किया जाता है:
ध्यान दें कि कैसे जेनेरिक function_wrapper फ़ंक्शन के साथ F पैरामीटर इंटरैक्ट करता है। इस तकनीक का उपयोग अक्सर तब किया जाता है जब आपको किसी अन्य भाषा में कोड करने के लिए FFI के माध्यम से जंग बंद करने की आवश्यकता होती है।
फ़ाइल सामग्री पढ़ना
अंतिम फ़ंक्शन जो हमें चाहिए, वह वास्तव में chm_retrieve_object () का उपयोग करके फ़ाइल को पढ़ने के लिए जिम्मेदार है।
इसका कार्यान्वयन बहुत तुच्छ है। यह एक विशिष्ट एसटीडी के समान है :: io :: स्पष्ट फ़ाइल ऑफसेट के अपवाद के साथ, लक्षण पढ़ें।
बेशक, "पढ़ने में विफल" की तुलना में अधिक विस्तृत त्रुटि संदेश होना अच्छा होगा, लेकिन स्रोत कोड, chm_retrieve_object () विशेष रूप से त्रुटियों के बीच अंतर नहीं करता है:
- रिटर्न 0 जब फ़ाइल को अंत में पढ़ा जाता है;
- अमान्य तर्कों के लिए 0 रिटर्न: अशक्त संकेत या सीमा से बाहर जाना;
- सिस्टम द्वारा फ़ाइलों को पढ़ने में त्रुटियों पर returns1 लौटाता है (और इरनो को भरता है);
- डेटा भ्रष्टाचार को भेद किए बिना, विघटन त्रुटियों के लिए returns1 रिटर्न और, कहते हैं, मॉलोक () के माध्यम से एक अस्थायी बफर के लिए मेमोरी आवंटित करने में असमर्थता।
आप ज्ञात सामग्री के साथ फ़ाइलों का उपयोग करके ChmFile :: read () का परीक्षण कर सकते हैं:
उदाहरण जोड़ें
हमने CHMLib लाइब्रेरी APIs में से अधिकांश को कवर किया है और पोर्टिंग को सफलतापूर्वक पूरा करने पर विचार करते हुए कई ने इसे समाप्त कर दिया है। हालांकि, हमारे रैक को और अधिक उपयोगकर्ता के अनुकूल बनाना अच्छा होगा। — , Rust Go ( , rustdoc godoc ).
, CHMLib , .
, , .
CHM-
CHM- .
#include "chm_lib.h" #include <stdio.h> #include <stdlib.h> #include <string.h> /* * callback function for enumerate API */ int _print_ui(struct chmFile *h, struct chmUnitInfo *ui, void *context) { static char szBuf[128]; memset(szBuf, 0, 128); if(ui->flags & CHM_ENUMERATE_NORMAL) strcpy(szBuf, "normal "); else if(ui->flags & CHM_ENUMERATE_SPECIAL) strcpy(szBuf, "special "); else if(ui->flags & CHM_ENUMERATE_META) strcpy(szBuf, "meta "); if(ui->flags & CHM_ENUMERATE_DIRS) strcat(szBuf, "dir"); else if(ui->flags & CHM_ENUMERATE_FILES) strcat(szBuf, "file"); printf(" %1d %8d %8d %s\t\t%s\n", (int)ui->space, (int)ui->start, (int)ui->length, szBuf, ui->path); return CHM_ENUMERATOR_CONTINUE; } int main(int c, char **v) { struct chmFile *h; int i; for (i=1; i<c; i++) { h = chm_open(v[i]); if (h == NULL) { fprintf(stderr, "failed to open %s\n", v[i]); exit(1); } printf("%s:\n", v[i]); printf(" spc start length type\t\t\tname\n"); printf(" === ===== ====== ====\t\t\t====\n"); if (! chm_enumerate(h, CHM_ENUMERATE_ALL, _print_ui, NULL)) printf(" *** ERROR ***\n"); chm_close(h); } return 0; }
_print_ui() Rust. UnitInfo , , .
main() , , describe_item() ChmFile::for_each().
:
$ cargo run --example enumerate-items topics.classic.chm > rust-example.txt $ cd vendor/CHMLib/src $ clang chm_lib.c enum_chmLib.c lzx.c -o enum_chmLib $ cd ../../.. $ ./vendor/CHMLib/src/enum_chmLib topics.classic.chm > c-example.txt $ diff -u rust-example.txt c-example.txt $ echo $? 0
diff , , , , . - , diff.
diff --git a/chmlib/examples/enumerate-items.rs b/chmlib/examples/enumerate-items.rs index e68fa58..ef855ac 100644 --- a/chmlib/examples/enumerate-items.rs +++ b/chmlib/examples/enumerate-items.rs @@ -36,6 +36,10 @@ fn describe_item(item: UnitInfo) { description.push_str("file"); } + if item.length() % 7 == 0 { + description.push_str(" :)"); + } + println!( " {} {:8} {:8} {}\t\t{}", item.space(),
:
$ cargo run --example enumerate-items topics.classic.chm > rust-example.txt $ diff -u rust-example.txt c-example.txt --- rust-example.txt 2019-10-20 16:51:53.933560892 +0800 +++ c-example.txt 2019-10-20 16:40:42.007053966 +0800 @@ -1,9 +1,9 @@ topics.classic.chm: spc start length type name
!
CHM-
, CHMLib, «» .
#include "chm_lib.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef WIN32 #include <windows.h> #include <direct.h> #define mkdir(X, Y) _mkdir(X) #define snprintf _snprintf #else #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #endif struct extract_context { const char *base_path; }; static int dir_exists(const char *path) { #ifdef WIN32 /* why doesn't this work?!? */ HANDLE hFile; hFile = CreateFileA(path, FILE_LIST_DIRECTORY, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); return 1; } else return 0; #else struct stat statbuf; if (stat(path, &statbuf) != -1) return 1; else return 0; #endif } static int rmkdir(char *path) { /* * strip off trailing components unless we can stat the directory, or we * have run out of components */ char *i = strrchr(path, '/'); if(path[0] == '\0' || dir_exists(path)) return 0; if (i != NULL) { *i = '\0'; rmkdir(path); *i = '/'; mkdir(path, 0777); } #ifdef WIN32 return 0; #else if (dir_exists(path)) return 0; else return -1; #endif } /* * callback function for enumerate API */ int _extract_callback(struct chmFile *h, struct chmUnitInfo *ui, void *context) { LONGUINT64 ui_path_len; char buffer[32768]; struct extract_context *ctx = (struct extract_context *)context; char *i; if (ui->path[0] != '/') return CHM_ENUMERATOR_CONTINUE; /* quick hack for security hole mentioned by Sven Tantau */ if (strstr(ui->path, "/../") != NULL) { /* fprintf(stderr, "Not extracting %s (dangerous path)\n", ui->path); */ return CHM_ENUMERATOR_CONTINUE; } if (snprintf(buffer, sizeof(buffer), "%s%s", ctx->base_path, ui->path) > 1024) return CHM_ENUMERATOR_FAILURE; /* Get the length of the path */ ui_path_len = strlen(ui->path)-1; /* Distinguish between files and dirs */ if (ui->path[ui_path_len] != '/' ) { FILE *fout; LONGINT64 len, remain=ui->length; LONGUINT64 offset = 0; printf("--> %s\n", ui->path); if ((fout = fopen(buffer, "wb")) == NULL) { /* make sure that it isn't just a missing directory before we abort */ char newbuf[32768]; strcpy(newbuf, buffer); i = strrchr(newbuf, '/'); *i = '\0'; rmkdir(newbuf); if ((fout = fopen(buffer, "wb")) == NULL) return CHM_ENUMERATOR_FAILURE; } while (remain != 0) { len = chm_retrieve_object(h, ui, (unsigned char *)buffer, offset, 32768); if (len > 0) { fwrite(buffer, 1, (size_t)len, fout); offset += len; remain -= len; } else { fprintf(stderr, "incomplete file: %s\n", ui->path); break; } } fclose(fout); } else { if (rmkdir(buffer) == -1) return CHM_ENUMERATOR_FAILURE; } return CHM_ENUMERATOR_CONTINUE; } int main(int c, char **v) { struct chmFile *h; struct extract_context ec; if (c < 3) { fprintf(stderr, "usage: %s <chmfile> <outdir>\n", v[0]); exit(1); } h = chm_open(v[1]); if (h == NULL) { fprintf(stderr, "failed to open %s\n", v[1]); exit(1); } printf("%s:\n", v[1]); ec.base_path = v[2]; if (! chm_enumerate(h, CHM_ENUMERATE_ALL, _extract_callback, (void *)&ec)) printf(" *** ERROR ***\n"); chm_close(h); return 0; }
. , .
extract(). , .
main() , extract(), .
CHM- HTML-, -.
$ cargo run --example extract -- ./topics.classic.chm ./extracted $ tree ./extracted ./extracted ├── default.html ├── BrowserForward.html ... ├── Images │ ├── Commands │ │ └── RealWorld │ │ ├── BrowserBack.bmp ... ├── script │ ├── _community │ │ └── disqus.js │ ├── hs-common.js ... └── userinterface.html $ firefox topics.classic/default.html ( default.html Firefox)
JavaScript ( - Microsoft Help), , .
आगे क्या है?
chmlib , , crates.io.
:
- ChmFile::for_each() ChmFile::for_each_item_in_dir() , , .
- , ChmFile
Continuation::Continue
. , F: FnMut(&mut ChmFile, UnitInfo) -> C
C: Into<Continuation>
, impl From<()> for Continuation
. - (, extract()) ChmFile::for_each() .
impl<E> From<Result<(), E>> for Continuation where E: Error + 'static
. - -
std::fs::File
. , ChmFile::read() - std::io::Writer
.