1 // NoahAM.cpp
2 //-- control many archiver routines --
3
4 #include "stdafx.h"
5 #include "resource.h"
6 #include "NoahApp.h"
7 #include "NoahAM.h"
8 #include "ArcDLL.h"
9 #include "ArcAce.h"
10 #include "ArcMsc.h"
11 #include "ArcB2e.h"
12 #include "ArcCpt.h"
13
14 //----------------------------------------------//
15 //------ 実働部隊のデータで初期化しておく ------//
16 //----------------------------------------------//
17
18 void CNoahArchiverManager::init()
19 {
20 const char* kl = mycnf().kill();
21 static int dead[128];
22 while( *kl ) dead[ 0x7f & (*(kl++)) ] = 1;
23
24 // 初期対応形式
25 if( !dead['L'] ) m_AList.add( new CArcLzh );
26 if( !dead['7'] ) m_AList.add( new CArc7z ),
27 m_AList.add( new CArc7zZip );
28 if( !dead['Z'] ) m_AList.add( new CArcUnZip );
29 if( !dead['z'] ) m_AList.add( new CArcZip );
30 if( !dead['T'] ) m_AList.add( new CArcTar );
31 if( !dead['C'] ) m_AList.add( new CArcCab );
32 if( !dead['R'] ) m_AList.add( new CArcRar );
33 if( !dead['A'] ) m_AList.add( new CArcArj );
34 if( !dead['B'] ) m_AList.add( new CArcBga );
35 if( !dead['Y'] ) m_AList.add( new CArcYz1 );
36 if( !dead['G'] ) m_AList.add( new CArcGca );
37 if( !dead['a'] ) m_AList.add( new CArcAce );
38 if( !dead['M'] ) m_AList.add( new CArcMsc );
39 if( !dead['c'] ) m_AList.add( new CArcCpt );
40
41 // 拡張スクリプトロード
42 char prev_cur[MAX_PATH];
43 ::GetCurrentDirectory(MAX_PATH, prev_cur);
44 ::SetCurrentDirectory( CArcB2e::init_b2e_path() );
45 kiFindFile find;
46 find.begin( "*.b2e" );
47 WIN32_FIND_DATA fd;
48 for( int t=0; find.next(&fd); t++ )
49 m_AList.add( new CArcB2e(fd.cFileName) );
50 m_b2e = (t>1);
51 ::SetCurrentDirectory(prev_cur);
52 }
53
54 //----------------------------------------------//
55 //------------ ファイルリストを記憶 ------------//
56 //----------------------------------------------//
57
58 unsigned long CNoahArchiverManager::set_files( const cCharArray& files )
59 {
60 //-- クリア
61 m_FName.empty();
62 m_BasePathList.empty();
63
64 //-- 基底パスを取得( 出来るだけ利用範囲を広げるため、8.3形式で )
65 if( files.len() != 0 )
66 {
67 char spath[MAX_PATH];
68 m_BasePath =
69 ( 0!=::GetShortPathName( files[0], spath, MAX_PATH ) )
70 ? spath : "";
71 if( !m_BasePath.beDirOnly() )
72 {
73 m_BasePath.beSpecialPath( kiPath::Cur );
74 m_BasePath.beBackSlash( true );
75 }
76 }
77
78 //-- 短いファイル名と長いのを両方取得しておく
79 m_FName.alloc( files.len() );
80 m_BasePathList.alloc( files.len() );
81 for( unsigned int i=0,c=0; i!=files.len(); i++ )
82 if( kiFindFile::findfirst( files[i], &m_FName[c] ) )
83 {
84 if( m_FName[c].cAlternateFileName[0] == '\0' )
85 ::lstrcpy(m_FName[c].cAlternateFileName,m_FName[c].cFileName);
86 m_BasePathList[c] = files[i];
87 if( !m_BasePathList[c].beDirOnly() )
88 {
89 m_BasePathList[c].beSpecialPath( kiPath::Cur );
90 m_BasePathList[c].beBackSlash( true );
91 }
92 ++c;
93 }
94 m_FName.forcelen( c );
95 m_BasePathList.forcelen( c );
96 return c;
97 }
98
99 //----------------------------------------------//
100 //--- ファイルリストに解凍ルーチンを割り当て ---//
101 //----------------------------------------------//
102
103 // 指定された拡張子に対応しているルーチンを線形探索
104 CArchiver* CNoahArchiverManager::fromExt( const char* ext )
105 {
106 kiStr tmp = ext;
107 tmp.lower();
108
109 for( unsigned int i=0; i!=m_AList.len(); i++ )
110 if( m_AList[i]->extCheck( tmp )
111 && (m_AList[i]->ability() & aMelt) )
112 return m_AList[i];
113 return NULL;
114 }
115
116 bool CNoahArchiverManager::map_melters( int mode ) // 1:cmp 2:mlt 3:must_mlt
117 {
118 // クリア
119 m_Melters.empty();
120
121 #define attrb (m_FName[ct].dwFileAttributes)
122 #define lname (m_FName[ct].cFileName)
123 #define sname (m_FName[ct].cAlternateFileName[0]==0 ? m_FName[ct].cFileName : m_FName[ct].cAlternateFileName)
124
125 kiPath fnm;
126 const char* ext;
127 for( unsigned int ct=0, bad=0; ct!=file_num(); ct++ )
128 {
129 // fnm = m_BasePath, fnm += sname;
130 fnm = m_BasePathList[ct], fnm += sname;
131
132 //-- 0byteファイル / ディレクトリは弾く
133 if( !(attrb & FILE_ATTRIBUTE_DIRECTORY) && 0!=kiFile::getSize( fnm, 0 ) )
134 {
135 //-- まず対応拡張子かどうかで候補Aを一つ選出
136 CArchiver* x = fromExt( ext=kiPath::ext(lname) );
137
138 //-- 候補Aで、ファイル内容によるチェック
139 if( x && x->check( fnm ) )
140 {
141 m_Melters.add( x );
142 continue;
143 }
144
145 //-- 候補Aが内容チェック不可なものだったらそれを使う
146 if( x && !(x->ability() & aCheck) )
147 {
148 m_Melters.add( x );
149 continue;
150 }
151
152 //-- 候補Aがダメなら、その他の内容チェック可能なルーチン全てで試す
153 if( mode!=1 || 0==ki_strcmpi( "exe", ext ) )
154 {
155 for( unsigned long j=0; j!=m_AList.len(); j++ )
156 if( m_AList[j]!=x && m_AList[j]->check( fnm ) )
157 {
158 m_Melters.add( m_AList[j] );
159 break;
160 }
161 if( m_Melters.len() == ct+1 )
162 continue;
163 }
164 }
165
166 //-- チェックの結果、解凍不能でしたとさ
167 if( mode!=3 )
168 return false; //-- 解凍専用モードでなければ終了
169 m_Melters.add( NULL ), bad++;
170 }
171 #undef sname
172 #undef lname
173 #undef attrb
174
175 return (ct!=bad);
176 }
177
178 //----------------------------------------------//
179 //--- ファイルリストに圧縮ルーチンを割り当て ---//
180 //----------------------------------------------//
181
182 bool CNoahArchiverManager::map_compressor( const char* ext, const char* method, bool sfx )
183 {
184 int m;
185 m_Method = -1;
186 m_Sfx = sfx;
187
188 for( unsigned int i=0; i!=m_AList.len(); i++ )
189 if( -1 != (m=m_AList[i]->cancompressby(ext,method,sfx)) )
190 if( m!=-2 ) // 完全一致
191 {
192 m_Compressor = m_AList[i];
193 m_Method = m;
194 break;
195 }
196 else if( m_Method == -1 ) // 形式名のみ一致した最初のモノ
197 {
198 m_Compressor = m_AList[i];
199 m_Method = m_AList[i]->cmp_mhd_default();
200 }
201 return (m_Method != -1);
202 }
203
204 //----------------------------------------------//
205 //------------ バージョン情報文字列 ------------//
206 //----------------------------------------------//
207
208 void CNoahArchiverManager::get_version( kiStr& str )
209 {
210 kiStr tmp;
211 for( unsigned int i=0; i!=m_AList.len(); i++ )
212 if( m_AList[i]->ver( tmp ) )
213 str+=tmp, str+="\r\n";
214 }
215
216 //----------------------------------------------//
217 //--------------- 圧縮形式リスト ---------------//
218 //----------------------------------------------//
219
220 static unsigned int find( const cCharArray& x, const char* o )
221 {
222 for( unsigned int i=0; i!=x.len(); i++ )
223 if( 0==ki_strcmp( x[i], o ) )
224 return i;
225 return 0xffffffff;
226 }
227
228 static unsigned int find( const StrArray& x, const char* o )
229 {
230 for( unsigned int i=0; i!=x.len(); i++ )
231 if( x[i]==o )
232 return i;
233 return 0xffffffff;
234 }
235
236 void CNoahArchiverManager::get_cmpmethod(
237 const char* set,
238 int& def_mhd,
239 StrArray& mhd_list,
240 bool need_ext,
241 cCharArray* ext_list )
242 {
243 def_mhd = -1;
244
245 const char* x;
246 for( unsigned int i=0; i!=m_AList.len(); i++ )
247 {
248 if( *(x = m_AList[i]->cmp_ext())=='\0' )
249 continue;
250 if( need_ext )
251 {
252 if( -1 == find( *ext_list, x ) )
253 ext_list->add( x );
254 }
255 if( 0 == ki_strcmp( set, x ) )
256 {
257 if( mhd_list.len()==0 )
258 {
259 def_mhd = m_AList[i]->cmp_mhd_default();
260 for( unsigned int j=0; j!=m_AList[i]->cmp_mhd_list().len(); j++ )
261 mhd_list.add( (m_AList[i]->cmp_mhd_list())[j] );
262 }
263 else
264 {
265 for( unsigned int j=0; j!=m_AList[i]->cmp_mhd_list().len(); j++ )
266 if( -1 == find( mhd_list, (m_AList[i]->cmp_mhd_list())[j] ) )
267 mhd_list.add( (m_AList[i]->cmp_mhd_list())[j] );
268 }
269 }
270 }
271
272 if( def_mhd == -1 )
273 def_mhd = 0;
274 }
275
276 //----------------------------------------------//
277 //--------------- 書庫一覧モード ---------------//
278 //----------------------------------------------//
279
280 #include "SubDlg.h"
281
282 void CNoahArchiverManager::do_listing( kiPath& destdir )
283 {
284 kiWindow* mptr = app()->mainwnd();
285 kiPath ddir;
286 int mdf = mycnf().mkdir();
287 bool rmn = mycnf().mnonum();
288 destdir.beBackSlash( true );
289
290 //-- ダイアログの個数カウンタをクリア
291 kiArray<CArcViewDlg*> views;
292 CArcViewDlg::clear();
293
294 //-- ダイアログ起動
295 for( unsigned int i=0; i!=m_FName.len(); i++ )
296 {
297 if( !m_Melters[i] )
298 continue;
299
300 arcname an(
301 m_BasePathList[i],
302 // m_BasePath,
303 m_FName[i].cAlternateFileName[0]==0 ? m_FName[i].cFileName : m_FName[i].cAlternateFileName,
304 m_FName[i].cFileName );
305 ddir = destdir;
306
307 if( mdf )
308 generate_dirname( m_FName[i].cFileName, ddir, rmn );
309
310 CArcViewDlg* x = new CArcViewDlg( m_Melters[i],an,ddir );
311 views.add( x );
312 x->createModeless( NULL );
313 }
314
315 //-- 全部終了するまで待機
316 kiWindow::msgLoop( kiWindow::GET );
317
318 //-- お終い
319 app()->setMainWnd( mptr );
320 for( i=0; i!=views.len(); i++ )
321 delete views[i];
322 }
323
324 //----------------------------------------------//
325 //----------------- 解凍作業 -------------------//
326 //----------------------------------------------//
327
328 void CNoahArchiverManager::do_melting( kiPath& destdir )
329 {
330 //-- 設定ロード
331 const int mdf = mycnf().mkdir(); // Make Directory Flag( 0:no 1:no1file 2: noddir 3:yes )
332 const bool rmn = mycnf().mnonum(); // Remove NuMber ?
333
334 //-- 出力先
335 destdir.beBackSlash( true );
336 destdir.mkdir(), destdir.beShortPath();
337
338 for( unsigned int i=0; i!=m_FName.len(); i++ )
339 if( m_Melters[i] )
340 {
341 //-- 出力先
342
343 int mk=2; // 0:no 1:yes 2:???
344 kiPath ddir( destdir ), dnm;
345 if( mdf==0 )
346 mk=0;
347 else if( mdf==3 )
348 mk=1;
349 else
350 {
351 kiPath anm(m_BasePathList[i]);
352 // kiPath anm(m_BasePath);
353 anm+=m_FName[i].cFileName;
354 int c = m_Melters[i]->contents( anm, dnm );
355 if( c==aSingleDir || (c==aSingleFile && mdf==1) )
356 mk=0; // 2重フォルダ防止処理(強)
357 else if( c==aMulti )
358 mk=1;
359 }
360 if( mk )
361 {
362 generate_dirname( m_FName[i].cFileName, ddir, rmn );
363 if( mk==2 && kiSUtil::exist(ddir) )
364 mk=1;
365 ddir+='\\';
366 ddir.mkdir();
367 ddir.beShortPath();
368 }
369
370 //-- 解凍!
371
372 arcname an( m_BasePathList[i],
373 // arcname an( m_BasePath,
374 m_FName[i].cAlternateFileName[0]==0 ? m_FName[i].cFileName : m_FName[i].cAlternateFileName,
375 m_FName[i].cFileName );
376 int result = m_Melters[i]->melt( an, ddir );
377 if( result<0x8000 )
378 {
379 if( mk==2 ) // 2重フォルダ防止処理(弱)
380 break_ddir( ddir, mdf==2 );
381 else if( mk==0 && dnm.len() ) // 2重フォルダ防止処理(強)
382 if( dnm.len()<=1 || dnm[1]!=':' ) // 絶対パスは開かない
383 ddir+=dnm, ddir+='\\';
384 // 出力先を開くかも
385 myapp().open_folder( ddir, 1 );
386 }
387 else if( result!=0x8020 )
388 {
389 //エラー!
390 char str[255];
391 wsprintf( str, "%s\nError No: [%x]",
392 (const char*)kiStr().loadRsrc( IDS_M_ERROR ), result );
393 app()->msgBox( str );
394 }
395 }
396 }
397
398 void CNoahArchiverManager::generate_dirname( const char* src, kiPath& dst, bool rmn )
399 {
400 // srcで示された書庫名からディレクトリ名を生成し、
401 // dstへ足す。rmn==trueなら末尾の数字も削除
402
403 // 一番左の . と左から二番目の . を探す
404 const char *fdot=NULL, *sdot=NULL, *tail;
405 for( tail=src; *tail; tail=kiStr::next(tail) )
406 if( *tail=='.' )
407 sdot=fdot, fdot=tail;
408
409 // .tar.xxx か、.xxx.gz/.xxx.z/.xxx.bz2 なら二つ削除
410 if( fdot )
411 {
412 tail = fdot;
413 if( sdot )
414 if( 0==::lstrcmpi(fdot,".gz")
415 || 0==::lstrcmpi(fdot,".z")
416 || 0==::lstrcmpi(fdot,".bz2")
417 || (sdot+4==fdot
418 && (sdot[1]=='t'||sdot[1]=='T')
419 && (sdot[2]=='a'||sdot[2]=='A')
420 && (sdot[3]=='r'||sdot[3]=='R')
421 ))
422 tail = sdot;
423 }
424
425 // 末尾の数字と'-'と'_'と'.'削除。半角スペースも。
426 bool del[256];
427 ki_memzero( del, sizeof(del) );
428 if( rmn )
429 {
430 del['-'] = del['_'] = del['.'] = true;
431 for( char c='0'; c<='9'; ++c )
432 del[c] = true;
433 }
434 del[' '] = true;
435
436 const char* mjs=NULL;
437 for( const char *x=src; x<tail; x=kiStr::next(x) )
438 if( !del[(unsigned char)(*x)] )
439 mjs = NULL;
440 else if( !mjs )
441 mjs = x;
442 if( mjs && mjs!=src )
443 tail = mjs;
444
445 // 空になってしまったら "noahmelt" という名前にしてしまう。
446 if( src==tail )
447 dst += "noahmelt";
448 else
449 while( src!=tail )
450 dst += *src++;
451 }
452
453 bool CNoahArchiverManager::break_ddir( kiPath& dir, bool onlydir )
454 {
455 // 2重フォルダ or 単一ファイル 状態を解消
456 //
457 // 素直に格納ファイル名を一度走査するのが本当なんですが、
458 // 特に巨大書庫の時速度低下が激しいのと、FindFirst系を
459 // サポートしたDLLや内蔵エンジン以外に対応できないという
460 // 欠点があるため、相変わらず Noah 2.xx と同じ手法です。
461
462 //-- 中に1個しか入ってないことを確認 -----------------
463 char wild[MAX_PATH];
464 ki_strcpy( wild, dir );
465 ki_strcat( wild, "*.*" );
466 kiFindFile find;
467 if( !find.begin( wild ) )
468 return false;
469 WIN32_FIND_DATA fd,fd2,fd3;
470 find.next( &fd );
471 if( find.next( &fd2 ) )
472 return false;
473 find.close();
474 //----------------------------------------------------
475
476 //-- to:最終移動先ファイル名。ついでに、カレントDirは消せない問題の回避策 -----
477 kiPath to(dir); to.beBackSlash( false ), to.beDirOnly();
478 ::SetCurrentDirectory( to );
479 to += fd.cFileName;
480 //-------------------------------------------------------------------------
481
482 //-- ファイルだった場合 --------------------------------------
483 if( !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
484 {
485 if( !onlydir )
486 {
487 // now 現在のファイル名
488 kiStr now=dir; now+=fd.cFileName;
489
490 // now -> to 移動
491 if( ::MoveFile( now, to ) )
492 {
493 dir.remove();
494 dir.beBackSlash( false ), dir.beDirOnly();
495 return true;
496 }
497 }
498 }
499 //-- フォルダだった場合 ----------------------------------------
500 else
501 {
502 // 'base/aaa/aaa/' だと中のaaaを外にmoveできない。
503 // よって、回避策。-> 'base/aaa_noah_tmp_178116/aaa/'
504
505 dir.beBackSlash( false );
506 kiFindFile::findfirst( dir, &fd3 );
507 kiPath dirx( dir ); dirx+="_noah_tmp_178116";
508
509 if( ::MoveFile( dir, dirx ) )
510 {
511 // now 現在のファイル名
512 kiStr now( dirx ); now+='\\', now+=fd.cFileName;
513
514 // ディレクトリを移動
515 if( ::MoveFile( now, to ) )
516 {
517 dirx.remove();
518 dir=to, dir.beBackSlash( true );
519 return true;
520 }
521 else
522 {
523 // 'base/aaa_noah_tmp_178116/aaa/' -> 'base/aaa/aaa/'
524 dir.beDirOnly(), dir+=fd3.cFileName;
525 ::MoveFile( dirx, dir );
526 }
527 }
528
529 dir.beBackSlash( true );
530 }
531 //------------------------------------------------------------
532 return false;
533 }
534
535 //----------------------------------------------//
536 //----------------- 圧縮作業 -------------------//
537 //----------------------------------------------//
538
539 void CNoahArchiverManager::do_compressing( kiPath& destdir, bool each )
540 {
541 int result = 0xffff, tr;
542
543 // 出力先を確実に作っておく
544 destdir.beBackSlash( true );
545 destdir.mkdir();
546 destdir.beShortPath();
547
548 // 個別圧縮モードか、Archiving不可の形式なら一個ずつ
549 if( each || !(m_Compressor->ability() & aArchive) )
550 {
551 wfdArray templist;
552
553 for( unsigned int i=0; i!=m_FName.len(); i++ )
554 {
555 templist.empty();
556 templist.add( m_FName[i] );
557 tr = m_Compressor->compress( m_BasePath,templist,destdir,m_Method,m_Sfx );
558 if( tr<0x8000 || tr==0x8020 )
559 result = tr;
560 }
561 }
562 else
563 result = m_Compressor->compress( m_BasePath,m_FName,destdir,m_Method,m_Sfx );
564
565 // 開くかも
566 if( result<0x8000 )
567 myapp().open_folder( destdir, 2 );
568 else if( result!=0x8020 )
569 {
570 //エラー!
571 char str[255];
572 wsprintf( str, "%s\nError No: [%x]", "Compression Error", result );
573 app()->msgBox( str );
574 }
575 }