listfiles 40.5 KB
Newer Older
Neil Turton's avatar
Neil Turton committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/* Copyright 1996 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
     List files as specified in a selection.

Revision History:

0.00  02-Jun-89  JSR  Created from extracts of c.actionwind.

0.01  27-Jun-89  JSR  Update to cope with arbitrary length file names.

0.02  29-Sep-89  JSR  Use overflowing_ memory allocation routines.
                      Add selection_summary.
                      Add name_of_next_node.

0.03  17-Oct-89  JSR  Upgrade next_nodename and next_filename to not
                      prepend the directory if the directory is a nul
                      string.
*/

#if 0
34 35 36
#define debuglist(k) dprintf k
#else
#define debuglist(k) /* Disabled */
Neil Turton's avatar
Neil Turton committed
37 38 39 40 41
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
42
#include <stdint.h>
Neil Turton's avatar
Neil Turton committed
43

44 45
#include "Interface/HighFSI.h"

Neil Turton's avatar
Neil Turton committed
46 47
#include "os.h"

48
#include "Options.h"
Neil Turton's avatar
Neil Turton committed
49 50 51 52
#include "malloc+.h"
#include "listfiles.h"
#include "allerrs.h"
#include "debug.h"
53 54
#include "memmanage.h"

Neil Turton's avatar
Neil Turton committed
55
#define No 0
56
#define Yes (!No)
Neil Turton's avatar
Neil Turton committed
57 58

#define Directory_Buffer_Size 20
59
#define Temp_DirBuffer_Size   640
Neil Turton's avatar
Neil Turton committed
60 61 62

typedef struct
{
Robert Sprowson's avatar
Robert Sprowson committed
63 64 65 66 67 68
     int      load_address;
     int      execution_address;
     uint32_t length;
     int      attributes;
     int      object_type;
     char     *object_name;
69

70
     #ifdef USE_PROGRESS_BAR
Robert Sprowson's avatar
Robert Sprowson committed
71
     void     *chain_ref;
72 73
     #endif

Neil Turton's avatar
Neil Turton committed
74 75 76 77 78
}    directory_buffer_entry;

typedef struct Search_Nest_Level
{
     struct Search_Nest_Level *next_search_nest_level;
Robert Sprowson's avatar
Robert Sprowson committed
79 80 81
     int                      offset_to_next_item;
     int                      entries_in_buffer;
     int                      next_entry_to_return;
Neil Turton's avatar
Neil Turton committed
82
     directory_buffer_entry   directory_buffer[ Directory_Buffer_Size ];
83

84
     #ifdef USE_PROGRESS_BAR
Robert Sprowson's avatar
Robert Sprowson committed
85 86 87 88
     BOOL                     counted;
     uint32_t                 total_entries;
     int                      total_progress;
     int                      progress_per_object;
89 90
     #endif

Neil Turton's avatar
Neil Turton committed
91 92 93 94 95
}    search_nest_level;

typedef struct File_Selection
{
     struct File_Selection *next_file;
96
     char                  *selection_name;
Neil Turton's avatar
Neil Turton committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
}    file_selection;

/*
     This enumerates the states for finding the next leaf to process.
*/
typedef enum
{
     Next_Leaf,               /* Find the next leaf at this level */
     Process_Node,            /* Process that found by Next_Leaf */
     Read_Next_Cache_Full,    /* Read another directory cache-full */
     Nest_Into_Directory      /* Start up into a nested directory */
}    next_leaf_state;

typedef struct search_context
{
Robert Sprowson's avatar
Robert Sprowson committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125
    search_nest_level *nested_filenames;
    char              *directory;
    file_selection    *selection;
    file_selection    **last_selections_link;
    next_leaf_state   action;
    uint32_t          selections_size;
    int               selections_loadaddr;
    int               selections_execaddr;
    int               selections_attributes;
    int               selections_objecttype;
    int               recursive:1;
    int               directories_first:1;
    int               directories_last:1;
    int               partitions_as_directories:1;
126

127
    #ifdef USE_PROGRESS_BAR
Robert Sprowson's avatar
Robert Sprowson committed
128 129 130 131
    uint32_t          total_entries;
    int               total_progress;
    int               progress_per_object;
    void              *chain_ref;
132 133 134 135
    #endif

} search_context;

136
#ifdef USE_PROGRESS_BAR
Robert Sprowson's avatar
Robert Sprowson committed
137
static uint32_t count_objects_in_dir( char *dir )
138
{
Robert Sprowson's avatar
Robert Sprowson committed
139 140 141 142 143
    os_error   *err;
    os_gbpbstr request;
    char       buff[Temp_DirBuffer_Size];
    int        context = 0, c, num = 0;
    uint32_t   total = 0;
144

145 146
    while (context != -1)
    {
Robert Sprowson's avatar
Robert Sprowson committed
147 148 149 150 151 152 153 154 155 156 157 158
        request.action      = OSGBPB_ReadDirEntries;
        request.file_handle = (int)dir;
        request.data_addr   = buff;
        request.number      = 256;
        request.seq_point   = context;
        request.buf_len     = Temp_DirBuffer_Size;
        request.wild_fld    = 0;

        err = os_gbpb( &request );
        num = request.number;
        c = request.seq_point;
        if (err == NULL)
159 160 161
        {
            if (c == context)
            {
162 163 164 165 166 167
                /*
                   Broken archives cause context to never be updated ... not sure why
                   the SWI does not return an error.
                */
                debuglist(( "count_objects_in_dir: aborting\n" ));
                return 0;
168
            }
169

170 171 172 173 174
            context = c;
            total += num;
        }
        else
        {
175
            debuglist(( "count_objects_in_dir: %s\n", e->errmess ));
176 177
            return 0;
        }
178 179
    }

180
    return total;
181
}
182
#endif
183

Neil Turton's avatar
Neil Turton committed
184 185 186 187 188 189 190 191
/*
     This initialises an empty search context.
     The search will return a list of files or directories, which must
     be added to the context after it has been created. The context is
     created recursive.
*/
os_error *create_search_context( search_handle *handle )
{
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
    search_context *new_context;

    if ( ( new_context = overflowing_malloc( sizeof( search_context ))) != NULL )
    {
        *handle = (search_handle)new_context;
        new_context->directory = NULL;
        new_context->nested_filenames = NULL;
        new_context->selection = NULL;
        new_context->last_selections_link = &new_context->selection;
        new_context->recursive = Yes;
        new_context->directories_first = No;
        new_context->directories_last = No;
        new_context->partitions_as_directories = Yes;
        new_context->action = Process_Node;

207
        #ifdef USE_PROGRESS_BAR
208
        new_context->total_entries = 0;
209
        new_context->total_progress = INT32_MAX;
210 211 212
        new_context->progress_per_object = 0;
        new_context->chain_ref = NULL;
        #endif
Neil Turton's avatar
Neil Turton committed
213

214 215 216 217 218 219
        return NULL;
    }
    else
    {
        return error( mb_malloc_failed );
    }
Neil Turton's avatar
Neil Turton committed
220 221 222 223 224
}


static int is_a_directory( search_handle handle, int objecttype )
{
225 226
    if ( objecttype == object_directory ||
            (objecttype == (object_directory | object_file) &&
227 228 229 230 231 232 233 234
             handle->partitions_as_directories) )
    {
            return Yes;
    }
    else
    {
            return No;
    }
Neil Turton's avatar
Neil Turton committed
235 236 237 238 239
}

/*
     Sets whether the search is recursive or not
*/
240
void recurse_search_context( search_handle handle, BOOL recursive )
Neil Turton's avatar
Neil Turton committed
241
{
242 243 244 245 246 247 248 249 250 251 252 253 254
    /*
            Make sure when changing to recursive that the children of this
            directory get returned
    */
    if ( !handle->recursive &&
         recursive &&
         is_a_directory( handle, objecttype_of_next_node( handle )) &&
         handle->action == Next_Leaf )
    {
            handle->action = Nest_Into_Directory;
    }

    handle->recursive = recursive != No;
Neil Turton's avatar
Neil Turton committed
255 256 257
}

/*
258 259
    Sets whether directories should be returned before their contents.
    This is relevant only when recursing.
Neil Turton's avatar
Neil Turton committed
260
*/
261
void return_directories_first( search_handle handle, BOOL directories_first )
Neil Turton's avatar
Neil Turton committed
262
{
263
    ((search_context *)handle)->directories_first = directories_first != No;
Neil Turton's avatar
Neil Turton committed
264 265 266
}

/*
267 268
    Sets whether directories should be returned after their contents.
    This is relevant only when recursing.
Neil Turton's avatar
Neil Turton committed
269
*/
270
void return_directories_last( search_handle handle, BOOL directories_last )
Neil Turton's avatar
Neil Turton committed
271
{
272
    handle->directories_last = directories_last != No;
Neil Turton's avatar
Neil Turton committed
273 274 275 276 277 278
}

/*
     Sets whether partitions should be treated the same as directories or not.
     This is relevant only when recursing.
*/
279
void treat_partitions_as_directories( search_handle handle, BOOL partitions_are_directories )
Neil Turton's avatar
Neil Turton committed
280
{
281
    handle->partitions_as_directories = partitions_are_directories;
Neil Turton's avatar
Neil Turton committed
282 283 284 285 286 287 288 289 290 291 292
}

/*
     Changes the directory in which the search will take place
*/
os_error *set_directory( search_handle handle, char *directory )
{
     search_context *context = (search_context *)handle;

     if ( context->directory )
     {
293
         overflowing_free( context->directory );
Neil Turton's avatar
Neil Turton committed
294 295 296 297
     }

     if ( ( context->directory = overflowing_malloc( strlen( directory ) + 1 )) != NULL )
     {
298
         strcpy( context->directory, directory );
Neil Turton's avatar
Neil Turton committed
299

300
         return NULL;
Neil Turton's avatar
Neil Turton committed
301 302 303
     }
     else
     {
304
         return error( mb_malloc_failed );
Neil Turton's avatar
Neil Turton committed
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
     }
}

/*
     frees the passed selection
*/
static void free_selection( file_selection *selection )
{
     overflowing_free( selection->selection_name );
     overflowing_free( selection );
}

/*
     Clears down a whole selection
*/
void clear_selection( search_handle handle )
{
     search_context *context = (search_context *)handle;
     file_selection *next_selection;
     file_selection *this_selection;

     /*
327
      Free the chain of selections
Neil Turton's avatar
Neil Turton committed
328 329
     */
     for ( this_selection = context->selection;
330 331
       this_selection != NULL;
       this_selection = next_selection )
Neil Turton's avatar
Neil Turton committed
332
     {
333 334
         next_selection = this_selection->next_file;
         free_selection( this_selection );
Neil Turton's avatar
Neil Turton committed
335 336 337
     }

     /*
338
      close off the head of the list
Neil Turton's avatar
Neil Turton committed
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
     */
     context->selection = NULL;
     context->last_selections_link = &context->selection;
}

/*
     Add a selection to an initialised search context. The search context
     will return found files in the order in which they were added.
*/
os_error *add_selection( search_handle handle, char *file_name, int wordlen )
{
     file_selection *new_selection;
     search_context *context = (search_context *)handle;

     /*
354 355 356 357
      These two if statements allocate the new selection structure and
      then some space for the file name. The mallocs are checked to
      work, and if they don't everything is tidied up and an error
      is returned.
Neil Turton's avatar
Neil Turton committed
358 359 360 361
     */
     new_selection = overflowing_malloc( sizeof( file_selection ));
     if ( new_selection != NULL )
     {
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
         new_selection->selection_name = overflowing_malloc( wordlen + 1 );
         if ( new_selection->selection_name != NULL )
         {
              /*
               Everything allocated OK, so copy the string and
               link the new selection onto the end of the list.
              */
              *context->last_selections_link = new_selection;
              strncpy( new_selection->selection_name, file_name, wordlen );
              new_selection->selection_name[ wordlen ] = '\0';
              context->last_selections_link = &new_selection->next_file;
              new_selection->next_file = NULL;

              debuglist(( "context: %x new selection: %s\n", context, new_selection->selection_name ));
              #ifdef USE_PROGRESS_BAR
              context->total_entries++;
              context->progress_per_object = context->total_progress / context->total_entries;
              #endif

              return NULL;
         }
         else
         {
              /*
               No room for selection name.
               Free up the selection structure and tidy up the ends
              */
              overflowing_free( new_selection );

              return error( mb_malloc_failed );
         }
Neil Turton's avatar
Neil Turton committed
393 394 395
     }
     else
     {
396
         return error( mb_malloc_failed );
Neil Turton's avatar
Neil Turton committed
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
     }
}

/*
     Free the abstract data type search_context. This is an equivalent
     to free(), but for this data type. It free all things hanging off it
     as well as the base structure.
*/
void dispose_search_context( search_handle handle )
{
     search_context *context = (search_context *)handle;
     search_nest_level *rover;
     search_nest_level *temp_rover;
     int i;

     /*
413
      Free the search nest level chain
Neil Turton's avatar
Neil Turton committed
414 415 416 417 418
     */
     rover = context->nested_filenames;

     while( rover != NULL )
     {
419 420 421 422 423 424 425
         temp_rover = rover;
         rover = rover->next_search_nest_level;
         for ( i = 0; i < Directory_Buffer_Size; i++ )
         {
              overflowing_free( temp_rover->directory_buffer[ i ].object_name );
         }
         overflowing_free( temp_rover );
Neil Turton's avatar
Neil Turton committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
     }

     clear_selection( handle );

     overflowing_free( context->directory );

     overflowing_free( context );
}

static int size_of_next_filename( search_context *context, search_nest_level *nesting )
{
     search_nest_level *nest_level = nesting;
     int returned_length = 0;

     if ( context->directory &&
441 442
      context->selection &&
      context->selection->selection_name )
Neil Turton's avatar
Neil Turton committed
443
     {
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
         returned_length = strlen( context->directory );

         /*
              Only Add one in if there's going to be a . in between dir and selection
         */
         if ( returned_length )
              returned_length++;

         returned_length += strlen( context->selection->selection_name );

         if ( context->recursive )
         {
              while ( nest_level != NULL )
              {
                  returned_length += 1 + strlen( nest_level->
                                                directory_buffer[ nest_level->next_entry_to_return ].
                                                object_name );
                  nest_level = nest_level->next_search_nest_level;
              }
         }
Neil Turton's avatar
Neil Turton committed
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
     }

     return returned_length;
}

/*
     Returns a pointer to the next filename in search_context

     This is a 'malloc'ed piece of memory, which should be 'free'ed when
     it is no longer needed.
*/
static os_error *next_filename( search_context *context, search_nest_level *nesting, char **hook_location )
{
     search_nest_level *nest_level = nesting;
     char *buffer = NULL;
     char *rover;
     int next_filename_size = size_of_next_filename( context, nesting );
     int part_length;

     if ( next_filename_size > 0 )
     {
485
         buffer = overflowing_malloc( next_filename_size + 1 );
Neil Turton's avatar
Neil Turton committed
486

487 488 489 490
         if ( buffer == NULL )
         {
              return error( mb_malloc_failed );
         }
Neil Turton's avatar
Neil Turton committed
491 492 493 494
     }

     if ( buffer != NULL )
     {
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
         if ( context->directory[0] )
         {
              sprintf( buffer, "%s.%s", context->directory, context->selection->selection_name );
         }
         else
         {
              sprintf( buffer, "%s", context->selection->selection_name );
         }

         rover = buffer + next_filename_size;

         *rover = '\0';

         while ( nest_level != NULL )
         {
              part_length = strlen( nest_level->
                directory_buffer[ nest_level->next_entry_to_return ].
                object_name );
              rover -= part_length + 1;

              rover[0] = '.';

              memcpy( &rover[1], nest_level->
                directory_buffer[ nest_level->next_entry_to_return ].
                object_name, part_length );

              nest_level = nest_level->next_search_nest_level;
         }
Neil Turton's avatar
Neil Turton committed
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
     }

     *hook_location = buffer;

     return NULL;
}

os_error *next_nodename( search_handle handle, char **hook_location )
{
     search_context *context = (search_context *)handle;
     return next_filename( context, context->nested_filenames, hook_location );
}

os_error *selection_summary( search_handle handle, char **summary )
{
538 539 540 541 542
    search_context *context = (search_context *)handle;
    char *cont;

    if ( context->selection == NULL )
    {
543
        cont = msgs_lookup("93"); /* 'nothing' */
544 545 546
    }
    else if ( context->selection->next_file == NULL )
    {
547
        cont = context->selection->selection_name;
548 549 550
    }
    else
    {
551
        cont = msgs_lookup("92"); /* 'many' */
552 553 554 555 556
    }

    *summary = overflowing_malloc( strlen( context->directory ) + 1 + strlen( cont ) + 1 );

    if ( *summary == NULL )
557
        return error( mb_malloc_failed );
558 559 560 561

    sprintf( *summary, "%s.%s", context->directory, cont );

    return NULL;
Neil Turton's avatar
Neil Turton committed
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
}

/*
     Finds the degree by which mstring matches the current next node.
     It returns the next mismatching position, and other information
     concerning where the mismatch occured
*/
static char *first_position_not_matched( search_handle handle, search_nest_level *nl, char *mstring,
     search_nest_level **deepest_match, search_nest_level **shallowest_non_match )
{
     char *matched_position;
     char *ms;
     int dl;
     int sl;

     if ( nl == NULL )
     {
579
         matched_position = mstring;
Neil Turton's avatar
Neil Turton committed
580

581
         dl = strlen( handle->directory );
Neil Turton's avatar
Neil Turton committed
582

583 584
         if ( strncmp( handle->directory, matched_position, dl ) != 0 )
              return NULL;
Neil Turton's avatar
Neil Turton committed
585

586
         matched_position += dl;
Neil Turton's avatar
Neil Turton committed
587

588 589
         if ( *matched_position != '.' )
              return NULL;
Neil Turton's avatar
Neil Turton committed
590

591
         matched_position += 1;
Neil Turton's avatar
Neil Turton committed
592

593
         sl = strlen( handle->selection->selection_name );
Neil Turton's avatar
Neil Turton committed
594

595 596
         if ( strncmp( handle->selection->selection_name, matched_position, sl ) != 0 )
              return NULL;
Neil Turton's avatar
Neil Turton committed
597

598
         matched_position += sl;
Neil Turton's avatar
Neil Turton committed
599

600
         return matched_position;
Neil Turton's avatar
Neil Turton committed
601 602 603
     }
     else
     {
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
         matched_position = first_position_not_matched( handle, nl->next_search_nest_level, mstring,
              deepest_match, shallowest_non_match );

         if ( matched_position == NULL )
              return NULL;

         ms = nl->directory_buffer[ nl->next_entry_to_return ].object_name;
         sl = strlen( ms );

         if ( *matched_position == '.' &&
              strncmp( matched_position + 1, ms, sl ) == 0 )
         {
              matched_position += 1 + sl;

              *deepest_match = nl;
         }
         else
         {
              if ( nl->next_search_nest_level == *deepest_match )
              {
                  *shallowest_non_match = nl;
              }
         }

         return matched_position;
Neil Turton's avatar
Neil Turton committed
629 630 631 632
     }
}

/*
633
    Informs that next node has been deleted
Neil Turton's avatar
Neil Turton committed
634 635 636
*/
void deleted_next_node( search_handle handle, char *deleted_node )
{
637 638
    if ( handle->nested_filenames && handle->selection )
    {
639 640 641
        char *matched_position;
        search_nest_level *deepest_match = NULL;
        search_nest_level *shallowest_non_match = NULL;
642

643
        matched_position = first_position_not_matched( handle, handle->nested_filenames, deleted_node, &deepest_match, &shallowest_non_match );
644

645 646
        if ( matched_position != NULL )
        {
647
            /*
648
             Deleted node is before something in shallowest non-match
649 650 651 652 653 654 655 656
            */
            if ( matched_position == strrchr( deleted_node, '.' ) &&
                 shallowest_non_match != NULL )
            {
                    shallowest_non_match->offset_to_next_item--;
            }

            /*
657
             Deleted is current node or a parent of it (gulp!)
658 659 660 661 662 663
            */
            if ( *matched_position == '\0' &&
                 deepest_match != NULL )
            {
                    deepest_match->offset_to_next_item--;
            }
664
        }
665
    }
Neil Turton's avatar
Neil Turton committed
666 667 668 669 670 671 672 673
}

/*
     Read the parameters for the next node, don't update
     if an error occurs
*/
void read_next_node_parameters( search_handle handle )
{
674 675 676
    os_filestr fileplace;
    char *filename;
    os_error *err;
Neil Turton's avatar
Neil Turton committed
677

678
    err = next_nodename( handle, &filename );
Neil Turton's avatar
Neil Turton committed
679

680 681
    if ( err )
            return;
Neil Turton's avatar
Neil Turton committed
682

683
    fileplace.action = OSFile_ReadInfo;
684
    fileplace.name = filename;
Neil Turton's avatar
Neil Turton committed
685

686
    err = os_file( &fileplace );
Neil Turton's avatar
Neil Turton committed
687

688 689
    if ( err )
            return;
Neil Turton's avatar
Neil Turton committed
690

691
    handle->selections_size       = fileplace.start;
692 693
    handle->selections_loadaddr   = fileplace.loadaddr;
    handle->selections_execaddr   = fileplace.execaddr;
694
    handle->selections_attributes = fileplace.end;
Neil Turton's avatar
Neil Turton committed
695 696
}

697 698
typedef enum {
  the_size, the_load_address, the_execute_address, the_attributes, the_type, the_name
699
  #ifdef USE_PROGRESS_BAR
700 701 702
  , the_progress, the_ref_ptr
  #endif
} which_thing;
Neil Turton's avatar
Neil Turton committed
703 704

/*
705 706
    Returns one of the values associated with the next node.
    If the node doesn't exist the value not_found is returned.
Neil Turton's avatar
Neil Turton committed
707
*/
Robert Sprowson's avatar
Robert Sprowson committed
708
static int thing_of_next_node( search_handle handle, which_thing thing, int not_found )
Neil Turton's avatar
Neil Turton committed
709
{
710 711 712 713 714 715
    search_context *context = (search_context *)handle;
    search_nest_level *nf;

    if ( context->selection )
    {
        if ( (nf = context->nested_filenames) == NULL )
Robert Sprowson's avatar
Robert Sprowson committed
716
        {
717 718 719
            switch( thing )
            {
            case the_size:
Robert Sprowson's avatar
Robert Sprowson committed
720
                return (int) context->selections_size;
721
            case the_load_address:
Robert Sprowson's avatar
Robert Sprowson committed
722
                return context->selections_loadaddr;
723
            case the_execute_address:
Robert Sprowson's avatar
Robert Sprowson committed
724
                return context->selections_execaddr;
725
            case the_attributes:
Robert Sprowson's avatar
Robert Sprowson committed
726
                return context->selections_attributes;
727
            case the_type:
Robert Sprowson's avatar
Robert Sprowson committed
728
                return context->selections_objecttype;
729
            case the_name:
Robert Sprowson's avatar
Robert Sprowson committed
730
                return (int) context->selection->selection_name;
731

732
            #ifdef USE_PROGRESS_BAR
733
            case the_progress:
Robert Sprowson's avatar
Robert Sprowson committed
734 735 736 737 738 739 740 741 742 743
                if (context->selections_objecttype == object_directory)
                {
                    debuglist(( "ponn: selection: %08x\n", 0 ));
                    return 0;
                }
                else
                {
                    debuglist(( "ponn: selection: %08x\n", context->progress_per_object ));
                    return context->progress_per_object;
                }
744 745

            case the_ref_ptr:
Robert Sprowson's avatar
Robert Sprowson committed
746
                return (int) &context->chain_ref;
747 748
            #endif

749
            } /* end switch */
750 751
        }
        else
Neil Turton's avatar
Neil Turton committed
752
        {
753 754 755 756 757 758 759
            directory_buffer_entry *d;

            d = nf->directory_buffer + nf->next_entry_to_return;

            switch( thing )
            {
            case the_size:
Robert Sprowson's avatar
Robert Sprowson committed
760
                return (int) d->length;
761
            case the_load_address:
Robert Sprowson's avatar
Robert Sprowson committed
762
                return d->load_address;
763
            case the_execute_address:
Robert Sprowson's avatar
Robert Sprowson committed
764
                return d->execution_address;
765
            case the_attributes:
Robert Sprowson's avatar
Robert Sprowson committed
766
                return d->attributes;
767
            case the_type:
Robert Sprowson's avatar
Robert Sprowson committed
768
                return d->object_type;
769
            case the_name:
Robert Sprowson's avatar
Robert Sprowson committed
770
                return (int) d->object_name;
771

772
            #ifdef USE_PROGRESS_BAR
773
            case the_progress:
Robert Sprowson's avatar
Robert Sprowson committed
774 775 776 777 778 779 780 781 782 783
                if (d->object_type == object_directory)
                {
                    debuglist(( "ponn: nested: %08x\n", 0 ));
                    return 0;
                }
                else
                {
                    debuglist(( "ponn: nested: %08x\n", nf->progress_per_object ));
                    return nf->progress_per_object;
                }
784 785 786 787 788 789

            case the_ref_ptr:
                return (int) &d->chain_ref;
            #endif

            }
Neil Turton's avatar
Neil Turton committed
790
        }
791
    }
Neil Turton's avatar
Neil Turton committed
792

793
    debuglist(( "tonn: not found\n" ));
794

795
    return not_found;
Neil Turton's avatar
Neil Turton committed
796 797
}

Robert Sprowson's avatar
Robert Sprowson committed
798
uint32_t size_of_next_node( search_handle handle )
Neil Turton's avatar
Neil Turton committed
799
{
Robert Sprowson's avatar
Robert Sprowson committed
800
    return (uint32_t)thing_of_next_node( handle, the_size, 0 );
Neil Turton's avatar
Neil Turton committed
801 802
}

Robert Sprowson's avatar
Robert Sprowson committed
803
int reload_of_next_node( search_handle handle )
Neil Turton's avatar
Neil Turton committed
804
{
805
    return thing_of_next_node( handle, the_load_address, -1 );
Neil Turton's avatar
Neil Turton committed
806 807
}

Robert Sprowson's avatar
Robert Sprowson committed
808
int execute_of_next_node( search_handle handle )
Neil Turton's avatar
Neil Turton committed
809
{
810
    return thing_of_next_node( handle, the_execute_address, -1 );
Neil Turton's avatar
Neil Turton committed
811 812
}

Robert Sprowson's avatar
Robert Sprowson committed
813
int attributes_of_next_node( search_handle handle )
Neil Turton's avatar
Neil Turton committed
814
{
815
    return thing_of_next_node( handle, the_attributes, -1 );
Neil Turton's avatar
Neil Turton committed
816 817
}

Robert Sprowson's avatar
Robert Sprowson committed
818
int objecttype_of_next_node( search_handle handle )
Neil Turton's avatar
Neil Turton committed
819
{
820
    return thing_of_next_node( handle, the_type, object_nothing );
Neil Turton's avatar
Neil Turton committed
821 822 823 824
}

char *name_of_next_node( search_handle handle )
{
825 826 827
    return (char *)thing_of_next_node( handle, the_name, NULL );
}

828
#ifdef USE_PROGRESS_BAR
Robert Sprowson's avatar
Robert Sprowson committed
829
uint32_t progress_of_next_node( search_handle handle )
830
{
Robert Sprowson's avatar
Robert Sprowson committed
831
    return (uint32_t)thing_of_next_node( handle, the_progress, 0 );
Neil Turton's avatar
Neil Turton committed
832 833
}

834

Robert Sprowson's avatar
Robert Sprowson committed
835
void **chain_ref_ptr_of_next_node( search_handle handle )
836
{
Robert Sprowson's avatar
Robert Sprowson committed
837
    return (void **)thing_of_next_node( handle, the_ref_ptr, NULL );
838 839 840
}
#endif

Neil Turton's avatar
Neil Turton committed
841
/*
842 843
    Assuming a directory has just been found, return whether this return
    of this directory was before or after its contents.
Neil Turton's avatar
Neil Turton committed
844
*/
Robert Sprowson's avatar
Robert Sprowson committed
845
BOOL directory_is_after_contents( search_handle handle )
Neil Turton's avatar
Neil Turton committed
846
{
847
    search_context *context = (search_context *)handle;
Neil Turton's avatar
Neil Turton committed
848

849
    return context->action == Next_Leaf;
Neil Turton's avatar
Neil Turton committed
850 851 852 853 854 855
}

/*
     Returns 0 if no more nodes
     Returns non-0 if more nodes
*/
Robert Sprowson's avatar
Robert Sprowson committed
856
BOOL another_node( search_handle handle )
Neil Turton's avatar
Neil Turton committed
857
{
858
    return ((search_context *)handle)->selection != NULL;
Neil Turton's avatar
Neil Turton committed
859 860 861
}

/*
862
    When finding a selection fails call this to skip it.
Neil Turton's avatar
Neil Turton committed
863 864 865
*/
void skip_failed_selection( search_handle handle )
{
866 867 868 869 870
    search_context *context = (search_context *)handle;

    switch ( context->action )
    {
    case Next_Leaf:
871 872 873 874
        /*
        Can't generate an error in this state
        */
        break;
875 876

    case Process_Node:
877 878
        context->action = Next_Leaf;
        break;
879 880

    case Read_Next_Cache_Full:
881 882 883 884 885
        /*
        This forces un-nesting then continuing at the upper level
        */
        context->nested_filenames->offset_to_next_item = -1;
        break;
886 887

    case Nest_Into_Directory:
888 889
        context->action = Next_Leaf;
        break;
890 891 892 893 894 895 896 897 898 899
    }
}

void skip_list_file( search_handle handle )
{
    search_context *context = (search_context *)handle;

    context->action = Next_Leaf;
}

Robert Sprowson's avatar
Robert Sprowson committed
900
os_error *step_to_next_node( search_handle handle, uint32_t *progress )
901 902 903 904 905 906 907 908
{
    search_context *context = (search_context *)handle;
    search_nest_level *temp_context;
    os_gbpbstr gbpbplace;
    os_filestr fileplace;
    char temp_directory_buffer[ Temp_DirBuffer_Size ];
    int i;
    int pos;
909
    BOOL resolved = No;
910 911 912 913
    os_error *err = NULL;
    file_selection *next_selection;
    int objecttype;

914
    debuglist(( "\nstep_to_next_node\n" ));
915 916 917 918 919 920 921 922 923

    /*
        While the answer hasn't resolved itself and there's no error
        then try the next step of resolving the answer
    */
    while( !resolved && !err )
    {
        switch( context->action )
        {
924
        case Next_Leaf:
925
            /*
926
                Step to the next leaf at this nesting level
927
            */
928 929
            debuglist(( "Next_Leaf " ));
            if ( context->nested_filenames == NULL )
930
            {
Neil Turton's avatar
Neil Turton committed
931
                /*
932
                    Get next selection
Neil Turton's avatar
Neil Turton committed
933
                */
934 935 936 937 938 939 940
                next_selection = context->selection->next_file;
                free_selection( context->selection );
                context->selection = next_selection;

                debuglist(( "get next selection" ));

                context->action = Process_Node;
941 942 943 944
            }
            else
            {
                /*
945
                    Get next cached entry
946
                */
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
                context->nested_filenames->next_entry_to_return++;
                if ( context->nested_filenames->next_entry_to_return >=
                 context->nested_filenames->entries_in_buffer )
                {
                    /*
                        We've run out of cached entries
                    */
                    debuglist(( "cache empty " ));
                    context->action = Read_Next_Cache_Full;
                }
                else
                {
                    /*
                        Found an entry in the cache, so let's
                        process it
                    */
                    debuglist(( "cache ok " ));
                    context->action = Process_Node;
                }
966
            }
967
            break;
Neil Turton's avatar
Neil Turton committed
968

969
        case Process_Node:
970
            /*
971
                Process the node as supplied by Next_Leaf
972
            */
973 974
            debuglist(( "Process_Node " ));
            if ( context->selection == NULL )
975
            {
976 977 978 979 980 981 982 983 984 985 986 987 988 989
                /*
                    No more entries in the selection, so we've
                    resolved what happens next
                */
                debuglist(( "resolved all " ));
                resolved = Yes;
            }
            else
            {
                /*
                    Get type of node if needed
                */
                debuglist(( "%s ", name_of_next_node( context ) ));
                if ( context->nested_filenames == NULL )
990
                {
991 992 993 994 995 996 997 998 999
                    fileplace.action = OSFile_ReadNoPath;
                    err = next_filename( context, context->nested_filenames, &fileplace.name );
                    debuglist(( "%s ", fileplace.name ));

                    if ( err )
                    {
                        debuglist(( "err\n" ));
                        continue;
                    }
1000

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
                    err = os_file( &fileplace );

                    if ( err )
                    {
                        overflowing_free( fileplace.name );
                        debuglist(( " error\n" ));
                        continue;
                    }

                    /*
                        Didn't find a selection - generate an error.
                    */
                    if ( fileplace.action == object_nothing )
                    {
                        fileplace.loadaddr = fileplace.action;
                        fileplace.action = OSFile_MakeError;
                        err = os_file( &fileplace );
                        overflowing_free( fileplace.name );
                        debuglist(( " not found\n" ));
                        continue;
                    }
1022 1023

                    overflowing_free( fileplace.name );
Neil Turton's avatar
Neil Turton committed
1024

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
                    context->selections_objecttype = fileplace.action;
                    context->selections_size = fileplace.start;
                    context->selections_loadaddr = fileplace.loadaddr;
                    context->selections_execaddr = fileplace.execaddr;
                    context->selections_attributes = fileplace.end;
                } /* end if (nested_filenames == NULL) */

                objecttype = objecttype_of_next_node( (search_handle)context );

                if ( objecttype == object_nothing )
1035
                {
1036 1037 1038 1039 1040
                    /*
                        Didn't find that, so go around
                        for another time - nothing to do here
                    */
                    debuglist(( "not found " ));
Neil Turton's avatar
Neil Turton committed
1041

1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
                    context->action = Next_Leaf;
                }
                else if ( context->recursive &&
                      is_a_directory( context, objecttype ) )
                {
                    /*
                        If we are returning directories first, then
                        we have resolved it at this level
                    */
                    debuglist(( "directory " ));
                    resolved = context->directories_first;
Neil Turton's avatar
Neil Turton committed
1053

1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066
                    context->action = Nest_Into_Directory;
                }
                else
                {
                    /*
                        Found a file or we found a directory
                        when not recursing, in which case
                        we've found something worth while and
                        so we've resolved things.
                    */
                    resolved = Yes;
                    context->action = Next_Leaf;
                    debuglist(( "resolved " ));
Neil Turton's avatar
Neil Turton committed
1067

1068 1069 1070
                }
            }
            break;
Neil Turton's avatar
Neil Turton committed
1071

1072 1073 1074 1075 1076 1077
        case Read_Next_Cache_Full:
            /*
                If run out of entries in this directory
            */
            debuglist(( "Read_Next_Cache_Full " ));
            if ( context->nested_filenames->offset_to_next_item == -1 )
1078
            {
1079 1080 1081 1082
                #ifdef USE_PROGRESS_BAR
                search_nest_level *nf = context->nested_filenames;
                int p = 0;

1083
                /*
1084 1085 1086
                   Compensate for rounding errors by adding the 'spare' progress
                   Isn't totally accurate if copying, as the actual progress values
                   used will have been halved.
1087
                */
1088
                p = nf->total_progress - (nf->total_entries * nf->progress_per_object);
Robert Sprowson's avatar
Robert Sprowson committed
1089
                if (p > 0) *progress += (uint32_t)p;
1090 1091 1092 1093
                debuglist(( "finished %d entries (total %08x) extra progress +%08x", nf->total_entries, nf->total_progress, p ));
                #else
                IGNORE(progress);
                #endif
1094 1095

                /*
1096
                    Down down one level
1097
                */
1098 1099 1100 1101 1102 1103 1104
                temp_context = context->nested_filenames->next_search_nest_level;
                for ( i = 0; i < Directory_Buffer_Size; i++ )
                {
                    overflowing_free( context->nested_filenames->directory_buffer[ i ].object_name );
                }
                overflowing_free( context->nested_filenames );
                context->nested_filenames = temp_context;
1105 1106

                /*
1107
                    Return the directory after all the files in it
1108
                */
1109
                resolved = context->directories_last;
Neil Turton's avatar
Neil Turton committed
1110

1111
                context->action = Next_Leaf;
1112
            }
1113
            else
1114
            {
1115
                char **filename_store;
Robert Sprowson's avatar
Robert Sprowson committed
1116
                search_nest_level *nf = context->nested_filenames;
1117

1118 1119 1120 1121
                /*
                    Read more of this directory
                */
                gbpbplace.action      = OSGBPB_ReadDirEntriesInfo;
1122

Robert Sprowson's avatar
Robert Sprowson committed
1123
                err = next_filename( context, nf->next_search_nest_level, (char **)&gbpbplace.file_handle );
1124

1125 1126
                if ( err )
                    continue;
1127

1128
                debuglist(( "%s ", (char *)gbpbplace.file_handle ));
1129 1130


1131
                #ifdef USE_PROGRESS_BAR
Robert Sprowson's avatar
Robert Sprowson committed
1132
                if (!nf->counted)
1133
                {
Robert Sprowson's avatar
Robert Sprowson committed
1134 1135 1136
                    nf->total_entries = count_objects_in_dir( (char *)gbpbplace.file_handle );
                    nf->counted = Yes;
                    if (nf->total_entries != 0)
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
                    {
                        nf->progress_per_object = nf->total_progress / nf->total_entries;
                    }
                    else
                    {
                        void *ref;

                        /*
                           Modify progress values so that half the progress for the dir is
                           added when the dir is finished (in the rounding process above)
                           and the other half when written. We have to do this now as when
                           the dir was added to the chain, we didn't know it was empty.
                           Of course if the dir is not in the chain, we are not copying, and
                           so we add all the progress for this dir at once.
                        */

                        if (nf->next_search_nest_level == NULL)
                        {
Robert Sprowson's avatar
Robert Sprowson committed
1155
                            /* top level */
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174
                            ref = context->chain_ref;
                        }
                        else
                        {
                            int i = nf->next_search_nest_level->next_entry_to_return;
                            ref = nf->next_search_nest_level->directory_buffer[i].chain_ref;
                        }

                        if (ref != NULL)
                        {
                            nf->total_progress /= 2;
                            nf->progress_per_object = 0;
                            modify_chain_file_progress(ref, nf->total_progress);
                        }

                    } /* end if (total_entries > 0) */

                    debuglist(( "%d entries %08x total progress %08x per object ", nf->total_entries, nf->total_progress, nf->progress_per_object ));

Robert Sprowson's avatar
Robert Sprowson committed
1175
                } /* end if (!counted) */
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186

                #endif

                gbpbplace.data_addr   = &temp_directory_buffer;
                gbpbplace.number      = Directory_Buffer_Size;
                gbpbplace.seq_point   = context->nested_filenames->offset_to_next_item;
                gbpbplace.buf_len     = Temp_DirBuffer_Size;
                gbpbplace.wild_fld    = "*";

                err = os_gbpb( &gbpbplace );
                overflowing_free( (void *)gbpbplace.file_handle );
1187

1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
                if ( err )
                {
                    if ( (err->errnum & FileError_Mask) == ErrorNumber_NotFound )
                    {
                        /*
                                Cancel the error
                        */
                        err = NULL;

                        /*
                             Down down one level
                        */
                        temp_context = context->nested_filenames->next_search_nest_level;
                        for ( i = 0; i < Directory_Buffer_Size; i++ )
                        {
                                overflowing_free( context->nested_filenames->directory_buffer[ i ].object_name );
                        }
                        overflowing_free( context->nested_filenames );
                        context->nested_filenames = temp_context;

                        /*
                                Don't return the directory, as it
                                doesn't exist!
                        */

                        context->action = Next_Leaf;
                    }
1215

1216 1217
                    continue;
                }
1218

1219
                for ( i = 0, pos = 0; i < gbpbplace.number; i++ )
Neil Turton's avatar
Neil Turton committed
1220
                {
1221 1222 1223 1224
                    context->nested_filenames->directory_buffer[ i ].load_address      = *(int *)&temp_directory_buffer[ pos ];
                    pos += sizeof(int);
                    context->nested_filenames->directory_buffer[ i ].execution_address = *(int *)&temp_directory_buffer[ pos ];
                    pos += sizeof(int);
Robert Sprowson's avatar
Robert Sprowson committed
1225 1226
                    context->nested_filenames->directory_buffer[ i ].length            = *(uint32_t *)&temp_directory_buffer[ pos ];
                    pos += sizeof(uint32_t);
1227 1228 1229 1230
                    context->nested_filenames->directory_buffer[ i ].attributes        = *(int *)&temp_directory_buffer[ pos ];
                    pos += sizeof(int);
                    context->nested_filenames->directory_buffer[ i ].object_type       = *(int *)&temp_directory_buffer[ pos ];
                    pos += sizeof(int);
1231 1232

                    /*
1233
                        Free the filename if there's one there
1234
                    */
1235 1236
                    filename_store = &context->nested_filenames->directory_buffer[ i ].object_name;
                    if ( *filename_store )
1237
                    {
1238 1239
                        overflowing_free ( *filename_store );
                        *filename_store = NULL;
1240 1241 1242
                    }

                    /*
1243
                        Allocate some space for the file name
1244
                    */
1245 1246 1247 1248 1249 1250
                    if ( ( *filename_store = overflowing_malloc( strlen( &temp_directory_buffer[ pos ] ) + 1 ) ) == NULL )
                    {
                        /*
                                If the allocation failed, free everything up
                        */
                        int j;
1251

1252 1253 1254 1255 1256
                        for ( j = 0; j < i; j++ )
                        {
                                overflowing_free( context->nested_filenames->directory_buffer[ j ].object_name );
                                context->nested_filenames->directory_buffer[ j ].object_name = NULL;
                        }
1257

1258
                        err = error( mb_malloc_failed );
1259

1260
                        break;
1261 1262
                    }

1263
                    strcpy( *filename_store, &temp_directory_buffer[ pos ] );
1264

1265 1266 1267 1268
                    pos += strlen( &temp_directory_buffer[ pos ] ) + 1;

                    /* round pos up to a word boundary */
                    pos = (( pos + 3 ) / 4 ) * 4;
1269 1270
                }

1271 1272
                if ( err )
                    break;
1273

1274 1275 1276
                context->nested_filenames->offset_to_next_item = gbpbplace.seq_point;
                context->nested_filenames->entries_in_buffer = gbpbplace.number;
                context->nested_filenames->next_entry_to_return = -1;
1277

1278
                context->action = Next_Leaf;
1279 1280 1281
            }
        break;

1282 1283 1284 1285 1286
        case Nest_Into_Directory:
            /*
                Go down into the next nesting level
            */
            debuglist(( "Nest_Into_Directory " ));
1287

1288
            temp_context = context->nested_filenames;
1289

1290 1291 1292 1293 1294 1295 1296
            context->nested_filenames = overflowing_malloc( sizeof( search_nest_level ));
            if ( context->nested_filenames == NULL )
            {
                context->nested_filenames = temp_context;
                err = error( mb_malloc_failed );
                continue;
            }
1297

1298 1299 1300 1301
            context->nested_filenames->next_search_nest_level = temp_context;
            context->nested_filenames->offset_to_next_item = 0;
            context->nested_filenames->entries_in_buffer = 0;
            context->nested_filenames->next_entry_to_return = -1;
1302

1303
            #ifdef USE_PROGRESS_BAR
1304 1305 1306 1307 1308 1309 1310 1311 1312 1313
            if (temp_context != NULL)
            {
              context->nested_filenames->total_progress = temp_context->progress_per_object;
            }
            else
            {
              context->nested_filenames->total_progress = context->progress_per_object;
            }
            /* Default values, will be overwitten if this object is a dir */
            context->nested_filenames->progress_per_object = 0;
Robert Sprowson's avatar
Robert Sprowson committed
1314 1315
            context->nested_filenames->total_entries = 0;
            context->nested_filenames->counted = No;
1316
            #endif
Neil Turton's avatar
Neil Turton committed
1317

1318 1319 1320 1321 1322 1323 1324
            for ( i = 0; i < Directory_Buffer_Size; i++ )
            {
                context->nested_filenames->directory_buffer[ i ].object_name = NULL;
                #ifdef USE_PROGRESS_BAR
                context->nested_filenames->directory_buffer[ i ].chain_ref = NULL;
                #endif
            }
1325

1326 1327 1328
            context->action = Next_Leaf;

            break;
1329 1330

        default:  /* disaster!!!!! */
1331 1332
            err = error( mb_unexpected_state );
            break;
1333
        } /* end switch */
1334

1335
        debuglist(( "\n" ));
1336

1337
    } /* end while */
1338 1339

    return err;
Neil Turton's avatar
Neil Turton committed
1340 1341
}

1342

1343
#ifdef USE_PROGRESS_BAR
1344
void listfiles_convert_to_copymove( search_handle handle )
1345
{
1346 1347 1348 1349 1350 1351 1352
  search_context *context = (search_context *)handle;

  if (context == NULL) return;

  context->total_progress /= 2;
}
#endif