1 /* deletecore.cc
2  * This file belongs to Worker, a file manager for UN*X/X11.
3  * Copyright (C) 2013-2021 Ralf Hoffmann.
4  * You can contact me at: ralf@boomerangsworld.de
5  *   or http://www.boomerangsworld.de/worker
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "deletecore.hh"
23 #include "worker_locale.h"
24 #include "deleteop.h"
25 #include "nmcopyopdir.hh"
26 #include "deleteorder.hh"
27 #include "simplelist.hh"
28 #include "verzeichnis.hh"
29 #include "aguix/lowlevelfunc.h"
30 #include "aguix/util.h"
31 #include "worker.h"
32 
DeleteCore(struct deleteorder * delorder)33 DeleteCore::DeleteCore( struct deleteorder *delorder ) :
34     m_delorder( delorder ),
35     m_cancel( false )
36 {
37 }
38 
~DeleteCore()39 DeleteCore::~DeleteCore()
40 {
41 }
42 
buildDeleteDatabase()43 int DeleteCore::buildDeleteDatabase()
44 {
45     unsigned long files = 0,
46         dirs = 0,
47         gf = 0,
48         gd = 0;
49 
50     DeleteOpWin *dowin = m_delorder->dowin;
51 
52     if ( dowin->redraw() != 0 ) m_cancel = true;
53 
54     // create the NM_CopyOp_Dir for each dir in dellist
55 
56     for ( auto &cbe : m_del_list ) {
57         if ( m_cancel == true ) break;
58 
59         bool enter = false;
60 
61         if ( cbe.entry().isDir() == true ) {
62             if ( cbe.entry().isLink == false ) enter = true;
63         }
64 
65         if ( enter == true ) {
66             // fe is a dir so creating corresponding entry
67             NM_CopyOp_Dir *cod1 = new NM_CopyOp_Dir( &cbe.entry() );
68 
69             if ( cod1->user_abort == false ) {
70                 // recursive call
71                 if ( cod1->createSubDirs( m_delorder, &gf, &gd ) != 0 ) {
72                     m_cancel = true;
73                 }
74             } else {
75                 m_cancel = true;
76             }
77 
78             // add the values from this subdir to this dir
79             files += cod1->files;
80             dirs += cod1->dirs;
81 
82             cbe.m_cod = cod1;
83             // this is a dir so inc the counter
84             dirs++;
85             gd++;
86 
87             dowin->set_files_to_delete( gf );
88             dowin->set_dirs_to_delete( gd );
89             if ( dowin->redraw() != 0 ) m_cancel = true;
90         } else {
91             // is not dir (mostly a file but can also be links ...)
92             files++;
93             gf++;
94             cbe.m_cod = NULL;
95         }
96     }
97 
98     dowin->set_files_to_delete( files );
99     dowin->set_dirs_to_delete( dirs );
100 
101     return 0;
102 }
103 
executeDelete()104 int DeleteCore::executeDelete()
105 {
106     DeleteOpWin *dowin = m_delorder->dowin;
107 
108     for ( auto &cbe : m_del_list ) {
109         if ( m_cancel ) break;
110 
111         NM_CopyOp_Dir *tcod;
112 
113         tcod = cbe.m_cod;
114 
115         if ( m_pre_cb ) {
116             m_pre_cb( cbe.entry(), cbe.m_row, cbe.m_cod );
117         }
118 
119         nm_delete_t ce1 = NM_DELETE_CANCEL;
120 
121         if ( tcod != NULL ) {
122             // determine if the dir is empty or not
123             bool empty;
124             bool skip;
125 
126             if ( ( tcod->files + tcod->dirs ) > 0 ) empty = false;
127             else empty = true;
128 
129             skip = false;
130 
131             // skip if the user doesn't want to delete non-empty dirs
132             if ( ( m_delorder->dirdel == m_delorder->NM_DIRDELETE_NONE ) && ( empty == false ) ) skip = true;
133 
134             // if the user doesn't select ALL or NONE and the the dir isn't empty show a request
135             if ( ( m_delorder->dirdel == m_delorder->NM_DIRDELETE_NORMAL ) && ( empty == false ) ) {
136                 char *buttonstr, *textstr;
137                 int erg;
138 
139                 textstr = (char*)_allocsafe( strlen( catalog.getLocale( 146 ) ) +
140                                              strlen( cbe.entry().fullname ) + 1 );
141                 sprintf( textstr, catalog.getLocale( 146 ), cbe.entry().fullname );
142                 buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
143                                                strlen( catalog.getLocale( 287 ) ) + 1 +
144                                                strlen( catalog.getLocale( 288 ) ) + 1 +
145                                                strlen( catalog.getLocale( 225 ) ) + 1 +
146                                                strlen( catalog.getLocale( 8 ) ) + 1 );
147                 sprintf( buttonstr, "%s|%s|%s|%s|%s", catalog.getLocale( 11 ),
148                          catalog.getLocale( 287 ),
149                          catalog.getLocale( 288 ),
150                          catalog.getLocale( 225 ),
151                          catalog.getLocale( 8 ) );
152                 erg = dowin->request( catalog.getLocale( 123 ), textstr, buttonstr );
153                 _freesafe( buttonstr );
154                 _freesafe( textstr );
155                 switch ( erg ) {
156                     case 1:
157                         m_delorder->dirdel = m_delorder->NM_DIRDELETE_ALL;
158                         break;
159                     case 2:
160                         m_delorder->dirdel = m_delorder->NM_DIRDELETE_NONE;
161                         skip = true;
162                         break;
163                     case 3:
164                         skip = true;
165                         break;
166                     case 4:
167                         m_cancel = true;
168                         skip = true;
169                         break;
170                 }
171             }
172             if ( skip == false ) {
173                 ce1 = deldir( &cbe.entry(), tcod, m_delorder );
174                 if ( ce1 == DeleteCore::NM_DELETE_CANCEL ) {
175                     m_cancel = true;
176                 } else if ( ce1 == DeleteCore::NM_DELETE_OK ) {
177                     Worker::pushCommandLog( catalog.getLocale( 1422 ),
178                                             cbe.entry().fullname,
179                                             cbe.entry().fullname );
180                 }
181             } else {
182 
183                 // dir skiped so dec DeleteOpWin counter by the files/dirs skiped
184                 dowin->dec_file_counter( tcod->files );
185                 dowin->dec_dir_counter( tcod->dirs );
186 
187                 ce1 = NM_DELETE_SKIP;
188             }
189 
190             if ( m_post_cb ) {
191                 m_post_cb( cbe.entry(), cbe.m_row,
192                            ce1, cbe.m_cod );
193             }
194         } else {
195             if ( dowin != NULL ) dowin->setfilename( cbe.entry().fullname );
196             ce1 = delfile( &cbe.entry(), m_delorder, false );
197             if ( ce1 == DeleteCore::NM_DELETE_CANCEL ) {
198                 m_cancel = true;
199             } else if ( ce1 == DeleteCore::NM_DELETE_OK ) {
200                 Worker::pushCommandLog( catalog.getLocale( 1422 ),
201                                         cbe.entry().fullname,
202                                         cbe.entry().fullname );
203             }
204 
205             if ( m_post_cb ) {
206                 m_post_cb( cbe.entry(), cbe.m_row,
207                            ce1, NULL );
208             }
209         }
210 
211         if ( dowin->redraw() != 0 ) m_cancel = true;
212     }
213 
214     return 0;
215 }
216 
deldir(const FileEntry * fe,NM_CopyOp_Dir * cod,struct deleteorder * delorder)217 DeleteCore::nm_delete_t DeleteCore::deldir( const FileEntry *fe, NM_CopyOp_Dir *cod, struct deleteorder *delorder )
218 {
219     NM_CopyOp_Dir *cod1 = NULL;
220     int id1;
221     bool skip;
222     nm_delete_t ce1;
223     DeleteOpWin *dowin = delorder->dowin;
224     bool delerror;
225 
226     skip = false;
227 
228     if ( dowin != NULL ) dowin->setfilename( fe->fullname );
229 
230     // first del all subdirs
231     // then del all files
232     id1 = cod->subdirs->initEnum();
233     cod1 = (NM_CopyOp_Dir*)cod->subdirs->getFirstElement( id1 );
234     while ( cod1 != NULL ) {
235         ce1 = deldir( cod1->fileentry, cod1, delorder );
236         if ( ce1 == NM_DELETE_OK ) {
237             // success
238             cod->error_counter += cod1->error_counter;
239         } else {
240             cod->error_counter += cod1->error_counter + 1;
241             if ( ce1 == NM_DELETE_CANCEL ) {
242                 setCancel( true );
243                 break;
244             }
245         }
246         cod1 = (NM_CopyOp_Dir*)cod->subdirs->getNextElement( id1 );
247     }
248     cod->subdirs->closeEnum( id1 );
249 
250     // next only if read this dir correctly
251     if ( ( cod->ok == true ) && ( getCancel() == false ) ) {
252         for ( Verzeichnis::verz_it subfe_it1 = cod->verz->begin();
253               subfe_it1 != cod->verz->end();
254               subfe_it1++ ) {
255             FileEntry *subfe = *subfe_it1;
256             if ( strcmp( subfe->name, ".." ) != 0 ) {
257                 bool isdir = false;
258                 if ( subfe->isDir() == true ) {
259                     if ( subfe->isLink == false ) isdir = true;
260                 }
261                 if ( isdir == false ) {
262                     ce1 = delfile( subfe, delorder, false );
263                     if ( ce1 == NM_DELETE_OK ) {
264                         // success
265                     } else {
266                         cod->error_counter++;
267                         if ( ce1 == NM_DELETE_CANCEL ) {
268                             setCancel( true );
269                             break;
270                         }
271                     }
272                 }
273             }
274         }
275     }
276 
277     delerror = true;
278     if ( getCancel() == false ) {
279         if ( cod->error_counter == 0 ) {
280             ce1 = delfile( fe, delorder, true );
281             if ( ce1 == NM_DELETE_OK ) {
282                 // success
283                 delerror = false;
284             } else {
285                 cod->error_counter++;
286                 if ( ce1 == NM_DELETE_CANCEL ) {
287                     setCancel( true );
288                 }
289             }
290         }
291     }
292     if ( delerror == true ) {
293         char *textstr = (char*)_allocsafe( strlen( catalog.getLocale( 289 ) ) +
294                                            strlen( fe->fullname ) + 1 );
295         sprintf( textstr, catalog.getLocale( 289 ), fe->fullname );
296         char *buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
297                                              strlen( catalog.getLocale( 8 ) ) + 1 );
298         sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
299                  catalog.getLocale( 8 ) );
300         int erg = dowin->request( catalog.getLocale( 125 ), textstr, buttonstr );
301         _freesafe( buttonstr );
302         _freesafe( textstr );
303         if ( erg == 1 ) setCancel( true );
304         else skip = true;
305     }
306     if ( getCancel() == true ) return NM_DELETE_CANCEL;
307     else if ( skip == true ) {
308         cod->error_counter++;
309         return NM_DELETE_SKIP;
310     }
311     return NM_DELETE_OK;
312 }
313 
delfile(const FileEntry * fe,struct deleteorder * delorder,bool dir)314 DeleteCore::nm_delete_t DeleteCore::delfile( const FileEntry *fe, struct deleteorder *delorder, bool dir )
315 {
316     bool skip;
317     int erg;
318     DeleteOpWin *dowin = delorder->dowin;
319 
320     skip = false;
321 
322     if ( dir == true ) erg = worker_rmdir( fe->fullname );
323     else erg = worker_unlink( fe->fullname );
324 
325     if ( erg != 0 ) {
326         //    textstr="Failed to remove this file!";
327         //    buttonstr="Ok|Cancel";
328 
329         char *textstr, *buttonstr;
330 
331         if ( dir == true ) {
332             textstr = (char*)_allocsafe( strlen( catalog.getLocale( 147 ) ) +
333                                          strlen( fe->fullname ) + 1 );
334             sprintf( textstr, catalog.getLocale( 147 ), fe->fullname );
335         } else {
336             textstr = (char*)_allocsafe( strlen( catalog.getLocale( 286 ) ) +
337                                          strlen( fe->fullname ) + 1 );
338             sprintf( textstr, catalog.getLocale( 286 ), fe->fullname );
339         }
340         buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
341                                        strlen( catalog.getLocale( 8 ) ) + 1 );
342         sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
343                  catalog.getLocale( 8 ) );
344         erg = dowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
345         _freesafe( buttonstr );
346         _freesafe( textstr );
347 
348         if ( erg == 1 ) setCancel( true );
349         else skip = true;
350     }
351     if ( dowin != NULL ) {
352         if ( dir == true ) {
353             dowin->dir_finished();
354             if ( dowin->redraw() != 0 ) setCancel( true );
355         } else dowin->file_finished();
356     }
357     if ( getCancel() == true ) return NM_DELETE_CANCEL;
358     else if ( skip == true ) return NM_DELETE_SKIP;
359     return NM_DELETE_OK;
360 }
361 
setPreDeleteCallback(std::function<void (const FileEntry & fe,int row,const NM_CopyOp_Dir * cod)> cb)362 void DeleteCore::setPreDeleteCallback( std::function< void( const FileEntry &fe, int row, const NM_CopyOp_Dir *cod ) > cb )
363 {
364     m_pre_cb = cb;
365 }
366 
setPostDeleteCallback(std::function<void (const FileEntry & fe,int row,nm_delete_t res,const NM_CopyOp_Dir * cod)> cb)367 void DeleteCore::setPostDeleteCallback( std::function< void( const FileEntry &fe, int row,
368                                                              nm_delete_t res, const NM_CopyOp_Dir *cod ) > cb )
369 {
370     m_post_cb = cb;
371 }
372 
registerFEToDelete(const FileEntry & fe,int row)373 void DeleteCore::registerFEToDelete( const FileEntry &fe,
374                                      int row )
375 {
376     m_del_list.push_back( delete_base_entry( fe, row ) );
377 }
378 
getCancel() const379 bool DeleteCore::getCancel() const
380 {
381     return m_cancel;
382 }
383 
setCancel(bool nv)384 void DeleteCore::setCancel( bool nv )
385 {
386     // disallow clearing the flag
387     if ( nv == true ) {
388         m_cancel = nv;
389     }
390 }
391 
delete_base_entry(const FileEntry & fe,int row)392 DeleteCore::delete_base_entry::delete_base_entry( const FileEntry &fe,
393                                                   int row ) :
394     m_row( row ),
395     m_cod( NULL ),
396     m_fe( fe )
397 {
398 }
399 
~delete_base_entry()400 DeleteCore::delete_base_entry::~delete_base_entry()
401 {
402     delete m_cod;
403 }
404