e4dec9a9587e7f31f4853ee94a9a5935c4c3acf6
[sdk] / ear / cmd / ear.ec
1 #ifdef __APPLE__
2 #define __unix__
3 #endif
4
5 #if defined(__unix__)
6 #define uint _uint
7 #include <sys/stat.h>
8 #undef uint
9 #endif
10
11 import "ecere"
12
13 static void ShowSyntax()
14 {
15    Log("ECERE Archiver v0.1\n");
16    Log("Copyright (c) 2003 Jerome Jacovella-St-Louis\n\n");
17    Log("General Syntax:\n");
18    Log("   eAR <command> <archive> <parameters>\n");
19    Log("Extraction Commands:\n");
20    Log("   v  (View)         <archive> [files...]\n");
21    Log("   x  (Extract All)  <archive> [where]\n");
22    Log("   e  (Extract)      <archive> <files...>\n");
23    Log("Modification Commands:\n");
24    Log("   a  (Add)          <archive> <files...>\n");
25    Log("   r  (Refresh)      <archive> <files...>\n");
26    Log("   u  (Update)       <archive> <files...>\n");
27    Log("   m  (Move)         <archive> <files...> <to>\n");
28    Log("   n  (Rename)       <archive> <file> <new name>\n");
29    Log("   d  (Delete)       <archive> <files...>\n");
30    Log("   c  (Clear)        <archive>\n");
31    Log("   s  (Self Extract) <archive> <self-extractable> (With a: overwrite)\n");
32    Log("Options:\n");
33    Log("(aru)    f  Treat <files> as folders to pack at the root of the archive\n");
34    Log("(aru)    0  No Compression\n");
35    Log("(aru)    1 ... 9  (Fastest Compression ... Best Compression (default = 9))\n");
36    Log("(earu)   w  Specify an output directory after <files>\n");
37    Log("(xearu)  q  Quiet mode\n");
38 }
39
40 #define ARCHIVE_ACTION_VIEW      1
41 #define ARCHIVE_ACTION_XTRACTALL 2
42 #define ARCHIVE_ACTION_EXTRACT   4
43 #define ARCHIVE_ACTION_ADD       5
44 #define ARCHIVE_ACTION_MOVE      6
45 #define ARCHIVE_ACTION_RENAME    7
46 #define ARCHIVE_ACTION_DELETE    8
47 #define ARCHIVE_ACTION_CLEAR     9
48 #define ARCHIVE_ACTION_SELFEXT   10
49
50 static void ViewArchive(char * path)
51 {
52    FileListing listing { path };
53    char string[MAX_LOCATION], * directory;
54
55    SplitArchivePath(path, string, &directory);
56
57    if(directory[0])
58    {
59       strcpy(string, directory);
60       if(!strcmp(directory, "/") || !strcmp(directory, "\\"))
61          strcpy(string, DIR_SEPS);
62       else
63          strcat(string, DIR_SEPS);
64       strcat(string, "\n");
65       Log(string);
66    }
67    
68    while(listing.Find())
69    {
70       char timeString[28];
71       strcpy(string, directory);
72       if(string[0])
73       {
74          if(!strcmp(directory, "/") || !strcmp(directory, "\\"))
75             strcpy(string, DIR_SEPS);
76          else
77             strcat(string, DIR_SEPS);
78       }
79       PathCat(string, listing.name);
80
81       ((DateTime)listing.stats.modified).local.OnGetString(timeString, null, null);
82
83       strcat(string, "\n   Modified: ");
84       strcat(string, timeString);
85       strcat(string, "\n");
86       
87       if(listing.stats.attribs.isDirectory)
88          ViewArchive(listing.path);
89       else
90          Log(string);
91    }
92 }
93 #define BUFFERSIZE 0x10000
94
95 static void ExtractFileFromArchive(char * path, char * outputFile)
96 {
97    char fileName[MAX_LOCATION];
98    FileAttribs exists = FileExists(path);
99    bool setTime = false;
100    FileStats stats;
101
102    if(exists.isDirectory)
103    {
104       FileListing listing { path };
105
106       if(outputFile[0])
107       {
108          if(MakeDir(outputFile))
109          {
110             setTime = true;
111             FileGetStats(path, &stats);
112          }
113       }
114
115       while(listing.Find())
116       {
117          strcpy(fileName, outputFile);
118
119          // Tweak file name if out
120          if(outputFile[0])
121          {
122             if(!strcmp(fileName, ".")) fileName[0] = '\0';
123             if(listing.name[0] == '/' || listing.name[0] == '\\')
124             {
125                char * afterSlash, rest[MAX_LOCATION];
126                for(afterSlash = fileName; *afterSlash == '/' || *afterSlash == '\\'; afterSlash++);
127                strcpy(rest, afterSlash);
128                PathCat(fileName, "_root");
129                PathCat(fileName, rest);
130             }
131             else if(listing.name[1] == ':')
132             {
133                char letter[10];
134                sprintf(letter, "_%cdrive", toupper(listing.name[0]));
135                PathCat(fileName, letter);
136                PathCat(fileName, listing.name[2] ? (listing.name + 3) : (listing.name + 2));
137             }
138             else
139                PathCat(fileName, listing.name);
140          }
141          else
142             PathCat(fileName, listing.name);
143          if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
144             strcpy(fileName, DIR_SEPS);
145          ExtractFileFromArchive(listing.path, fileName);
146       }
147    }
148    else if(exists)
149    {
150       File input = FileOpen(path, read);
151       if(input)
152       {
153          File output = FileOpen(outputFile, write);
154          if(output)
155          {
156             FileSize dataSize, c;
157             static byte buffer[BUFFERSIZE];
158             FileGetSize(path, &dataSize);
159             if(!quiet)
160                Logf("Extracting %s...\n", outputFile);
161             for(c = 0; c<dataSize; c += BUFFERSIZE)
162             {
163                uint size = (dataSize > c + BUFFERSIZE) ? BUFFERSIZE : (dataSize - c);
164                input.Read(buffer, 1, size);
165                output.Write(buffer, 1, size);
166             }
167             delete output;
168             setTime = true;
169             FileGetStats(path, &stats);
170          }
171          delete input;
172       }
173    }
174    if(setTime)
175       FileSetTime(outputFile, stats.created, 0, stats.modified);
176 }
177
178 static bool AddToArchive(Archive archive, ArchiveDir parentDir, char * name, char * path, ArchiveAddMode addMode, int compression)
179 {
180    bool result = true;
181    FileAttribs exists = FileExists(path);
182    if(exists.isDirectory)
183    {
184       ArchiveDir directory;
185       if(name[0] || !parentDir)
186       {
187          FileStats stats;
188          FileGetStats(path, &stats);
189          if(parentDir)
190             directory = parentDir.OpenDirectory(name, &stats, addMode);
191          else
192             directory = archive.OpenDirectory(name, &stats, addMode);
193       }
194       else
195          directory = parentDir;
196       if(directory)
197       {
198          FileListing listing { path };
199          while(listing.Find())
200          {
201             if(!AddToArchive(archive, directory, listing.name, listing.path, addMode, compression))
202             {
203                result = false;
204                break;
205             }
206          }
207          if(directory != parentDir)
208             delete directory;
209       }
210    }
211    else if(exists)
212    {
213       int ratio;
214       uint newPosition;
215       if(!quiet)
216          Logf("Adding %s...", name);
217       if(parentDir.Add(name, path, addMode, compression, &ratio, &newPosition))
218       {
219          if(newPosition)
220          {
221             if(ratio && !quiet)
222                Logf("(%2d.%1d%%)", ratio / 10, ratio % 10);
223             if(!quiet)
224                Log("\n");
225          }
226          else
227             Logf("Skipped%s%s.\n", quiet ? " " : "", quiet ? name : "");
228       }
229       else
230       {
231          Logf("Out of disk space.\nError: Ran out of disk space while archiving%s%s.\n", quiet ? " " : "", quiet ? name : "");
232          ((GuiApplication)__thisModule).exitCode = 1;
233          result = false;
234       }
235    }
236    return result;
237 }
238
239 static void MoveFileInArchive(Archive* archive, char * sourcePath, char * outputDirectory)
240 {
241    // Verify if source file/directory exists and figure its kind
242    FileAttribs exists = FileExists(sourcePath);
243    if(exists)
244    {
245       char sourceFileName[MAX_FILENAME], sourceDirectory[MAX_LOCATION];
246       char archiveName[MAX_LOCATION], * source;
247       char existingFilePath[MAX_LOCATION], * existingFile;
248       bool rootMoving = false;
249       FileAttribs outputExists;
250       bool doMove = false;
251
252       SplitArchivePath(sourcePath, archiveName, &source);
253       
254       GetLastDirectory(source, sourceFileName);
255       StripLastDirectory(source, sourceDirectory);
256
257       sprintf(existingFilePath, "<%s>", archiveName);
258       existingFile = existingFilePath + strlen(existingFilePath);
259       PathCat(existingFile, outputDirectory);
260
261       if(!sourceDirectory[0] &&
262          ((sourceFileName[0] && sourceFileName[1] == ':') ||
263          sourceFileName[0] == '\\' || sourceFileName[0] == '/'))
264          rootMoving = true;
265       else
266          PathCat(existingFile, sourceFileName);
267
268       if(rootMoving || fstrcmp(outputDirectory, sourceDirectory))
269       {
270          // If directory exists in destination directory, move files and then delete
271          outputExists = FileExists(existingFilePath);
272       
273          // If source is a directory
274          if(exists.isDirectory)
275          {
276             // Check if destination directory is within the source directory
277             bool within = true;
278             char outputPart[MAX_FILENAME], outputRest[MAX_LOCATION];
279             char sourcePart[MAX_FILENAME], sourceRest[MAX_LOCATION];
280          
281             strcpy(outputRest, outputDirectory);
282             strcpy(sourceRest, source);
283
284             for(;sourceRest[0];)
285             {
286                SplitDirectory(outputRest, outputPart, outputRest);
287                SplitDirectory(sourceRest, sourcePart, sourceRest);
288                if(fstrcmp(sourcePart, outputPart) || !outputPart[0])
289                {
290                   within = false;
291                   break;
292                }
293             }
294
295             // Proceed with the move
296             if(!within || !source[0])
297             {
298                // If directory exists in destination directory, move files and then delete
299                if(outputExists.isDirectory || rootMoving || !source[0])
300                {
301                   ArchiveDir input;
302                   FileListing listing { sourcePath };
303                   
304                   if(!source[0])
305                   {
306                      ArchiveDir dir;
307                      Logf("Moving files in root to %s.\n", outputDirectory[0] ? outputDirectory : "root");
308                      dir = archive->OpenDirectory(outputDirectory, null, 0);
309                      if(dir)
310                      {
311                         char archiveName[MAX_LOCATION], * archiveFile;
312                         delete dir;
313                         delete *archive;
314                         SplitArchivePath(sourcePath, archiveName, &archiveFile);
315                         *archive = ArchiveOpen(archiveName, { true });
316                      }
317                   }
318                   else if(!rootMoving)
319                      Logf("Merging directory %s in %s with %s in %s.\n", 
320                         sourceFileName, 
321                         sourceDirectory[0] ? sourceDirectory : "root",
322                         sourceFileName,
323                         outputDirectory[0] ? outputDirectory : "root");
324                   
325                   while(listing.Find())
326                   {
327                      if(strcmp(listing.path, existingFilePath))
328                         MoveFileInArchive(archive, listing.path, existingFile);
329                   }
330
331                   if(source[0])
332                   {
333                      input = archive->OpenDirectory(sourceDirectory, null, 0);
334                      if(input)
335                      {
336                         input.Delete(sourceFileName);
337                         delete input;
338                      }
339                   }
340                }
341                else if(outputExists)
342                   Logf("A file with the same name already exists (%s).\n", existingFile);
343                else
344                   // Perform operation
345                   doMove = true;
346             }
347             else
348                Logf("Can't move directory %s inside itself.\n", source);
349          }
350          // If source is a file
351          else if(outputExists.isDirectory)
352             Logf("A folder with the same name already exists (%s).\n", existingFile);
353          else
354             doMove = true;
355
356          if(doMove)
357          {
358             // It is important for the output directory to be opened first, as it might
359             // interfere with the input directory while its path is created.
360             ArchiveDir output = archive->OpenDirectory(outputDirectory, null, 0);
361             if(output)
362             {
363                ArchiveDir input = archive->OpenDirectory(sourceDirectory, null, 0);
364                if(input)
365                {
366                   // If a file by the same name exists already, replace it
367                   if(outputExists.isFile)
368                      output.Delete(sourceFileName);
369
370                   Logf("Moving file %s in directory %s to %s.\n", sourceFileName, 
371                      sourceDirectory[0] ? sourceDirectory : "root", 
372                      outputDirectory[0] ? outputDirectory : "root");
373
374                   // Perform operation
375                   input.Move(sourceFileName, output);
376
377                   delete input;
378                }
379                delete output;
380             }
381          }
382       }
383       else
384          Logf("File is already in directory \"%s\".\n",
385             sourceDirectory[0] ? sourceDirectory : "root");
386    }
387 }
388
389 static bool quiet;
390
391 class EARApp : GuiApplication
392 {
393    void Main()
394    {
395       bool valid = false;
396       int numFiles = 0;
397       int firstFileArg = 0;
398       int action = 0;
399       bool extractWhere = false;
400       bool addFolders = false;
401       bool selfExtract = false;
402       int compression = 9;
403       ArchiveAddMode addMode = 0;
404       int c;
405
406       quiet = false;
407          
408       DumpErrors(false);
409       SetLoggingMode(stdOut, null);
410
411       if(argc > 2)
412       {
413          // First validate command / options
414          char ch;
415         
416          valid = true;
417          for(c = 0; (ch = (char)tolower(argv[1][c])) && valid; c++)
418          {
419             int command = 0;
420             switch(ch)
421             {
422                case 'v': command = ARCHIVE_ACTION_VIEW; break;
423                case 'x': command = ARCHIVE_ACTION_XTRACTALL; break;
424                case 'e': command = ARCHIVE_ACTION_EXTRACT; break;
425                case 'a': command = ARCHIVE_ACTION_ADD; addMode = replace; break;
426                case 'r': command = ARCHIVE_ACTION_ADD; addMode = refresh; break;
427                case 'u': command = ARCHIVE_ACTION_ADD; addMode = update; break;
428                case 'm': command = ARCHIVE_ACTION_MOVE; break;
429                case 'n': command = ARCHIVE_ACTION_RENAME; break;
430                case 'd': command = ARCHIVE_ACTION_DELETE; break;
431                case 'c': command = ARCHIVE_ACTION_CLEAR; break;
432                case 's':
433                   if(action == ARCHIVE_ACTION_ADD && addMode == replace)
434                      selfExtract = true;
435                   else if(!action)
436                      command = ARCHIVE_ACTION_SELFEXT;
437                   else
438                      valid = true;
439                   break;
440                case 'f':
441                   if(action == ARCHIVE_ACTION_ADD)
442                      addFolders = true;
443                   else
444                      valid = false;
445                   break;
446                case 'w':
447                   if(action == ARCHIVE_ACTION_EXTRACT || action == ARCHIVE_ACTION_ADD)
448                      extractWhere = true;
449                   else
450                      valid = false;
451                   break;
452                case 'q':
453                   quiet = true;
454                   break;
455                default:
456                   if(action == ARCHIVE_ACTION_ADD &&
457                      ch >= '0' && ch <= '9' && compression == 9)
458                      compression = ch - '0';
459                   else
460                      valid = false;
461             }
462             if(command)
463             {
464                if(action)
465                   valid = false;
466                else
467                   action = command;
468             }
469          }
470
471          if(valid)
472          {
473             // Validate commands needing parameters
474             switch(action)
475             {
476                case ARCHIVE_ACTION_VIEW:
477                   numFiles = argc - 3;
478                   firstFileArg = 3;
479                   break;
480                case ARCHIVE_ACTION_EXTRACT: 
481                   firstFileArg = 3;
482                   if(extractWhere)
483                      numFiles = argc - 4;
484                   else
485                      numFiles = argc - 3;
486                   valid = numFiles > 0;
487                   break;
488                case ARCHIVE_ACTION_ADD:
489                   firstFileArg = 3;
490                   if(extractWhere)
491                      numFiles = argc - 4;
492                   else
493                      numFiles = argc - 3;
494                   valid = numFiles > 0;
495                   break;
496                case ARCHIVE_ACTION_MOVE:
497                   firstFileArg = 3;
498                   numFiles = argc - 4;
499                   valid = numFiles > 0;
500                   break;
501                case ARCHIVE_ACTION_RENAME:
502                   valid = argc > 4;
503                   break;
504                case ARCHIVE_ACTION_DELETE:
505                   firstFileArg = 3;
506                   numFiles = argc - 3;
507                   valid = numFiles > 0;
508                   break;
509                case ARCHIVE_ACTION_SELFEXT:
510                   valid = argc > 3;
511                   break;
512             }
513          }
514       }
515
516       if(valid)
517       {
518          char archivePath[MAX_LOCATION];
519          int archivePathLen;
520
521          sprintf(archivePath, "<%s>", argv[2]);
522          archivePathLen = strlen(archivePath);
523
524          if(action != ARCHIVE_ACTION_ADD)
525          {
526             FileSize size;
527             if(!FileExists(argv[2]))
528             {
529                Logf("Archive file not found: %s\n", argv[2]);
530                action = 0;
531             }
532             else if(FileGetSize(argv[2], &size) && !size)
533             {
534                Logf("Archive file is empty: %s\n", argv[2]);
535                action = 0;
536             }
537             else 
538             {
539                Archive archive = ArchiveOpen(argv[2], { false });
540                if(archive)
541                   delete archive;
542                else
543                {
544                   Logf("File is not a valid ECERE archive: %s\n", argv[2]);
545                   action = 0;
546                }
547             }
548          }
549          else if(selfExtract)
550          {
551    #ifdef __WIN32__
552             ExtractFileFromArchive(":extract.exe", argv[2]);
553    #else
554             ExtractFileFromArchive(":extract", argv[2]);
555             chmod(argv[2], 0755);
556    #endif
557          }
558
559          switch(action)
560          {
561             case ARCHIVE_ACTION_VIEW:
562             {     
563                if(!numFiles)
564                   ViewArchive(archivePath);
565                else
566                {
567                   for(c = firstFileArg; c<numFiles + firstFileArg; c++)
568                   {
569                      char *name, archive[MAX_LOCATION], fileName[MAX_LOCATION];
570                      FileAttribs exists;
571
572                      strcpy(fileName, archivePath);
573                      PathCat(fileName, argv[c]);
574                      FileFixCase(fileName);
575                      SplitArchivePath(fileName, archive, &name);
576
577                      if((exists = FileExists(fileName)))
578                      {
579                         if(exists.isDirectory)
580                            ViewArchive(fileName);
581                         else
582                            Logf("%s\n", fileName + archivePathLen);
583                      }
584                      else
585                         Logf("File Not Found: %s\n", name);
586                   }
587                }
588                break;
589             }
590             case ARCHIVE_ACTION_XTRACTALL:
591                ExtractFileFromArchive(archivePath, (argc > 3) ? argv[3] : "");
592                break;
593             case ARCHIVE_ACTION_EXTRACT:
594             {
595                for(c = firstFileArg; c<firstFileArg + numFiles; c++)
596                {
597                   char directory[MAX_LOCATION];
598                   char fileName[MAX_LOCATION];
599                   char outputFile[MAX_LOCATION] = "";
600
601                   strcpy(fileName, archivePath);
602                   strcat(fileName, argv[c]);
603                   if(FileExists(fileName))
604                   {
605                      if(extractWhere)
606                      {
607                         strcpy(outputFile, argv[argc-1]);
608                         if(!strcmp(outputFile, ".")) outputFile[0] = '\0';
609                         if(argv[c][0] == '/' || argv[c][0] == '\\')
610                         {
611                            char * afterSlash, rest[MAX_LOCATION];
612                            for(afterSlash = fileName; *afterSlash == '/' || *afterSlash == '\\'; afterSlash++);
613                            strcpy(rest, afterSlash);
614                            PathCat(fileName, "_root");
615                            PathCat(fileName, rest);
616                         }
617                         else if(argv[c][1] == ':')
618                         {
619                            char letter[10];
620                            sprintf(letter, "_%cdrive", toupper(argv[c][0]));
621                            PathCat(outputFile, letter);
622                            PathCat(outputFile, argv[c][2] ? (argv[c] + 3) : (argv[c] + 2));
623                         }
624                      }
625                      else
626                         PathCat(outputFile, argv[c]);
627
628                      if(!strcmp(outputFile, "/") || !strcmp(outputFile, "\\"))
629                         strcpy(outputFile, DIR_SEPS);
630
631                      StripLastDirectory(outputFile, directory);
632                   
633                      MakeDir(directory);
634
635                      ExtractFileFromArchive(fileName, outputFile);
636                   }
637                }
638                break;
639             }
640             case ARCHIVE_ACTION_ADD:
641             {
642                Archive archive = ArchiveOpen(argv[2], { true });
643                if(archive)
644                {
645                   if(selfExtract)
646                      archive.totalSize = 0;
647                   if(addFolders)
648                   {
649                      for(c = firstFileArg; c<firstFileArg + numFiles; c++)
650                      {
651                         if(!AddToArchive(archive, null, extractWhere ? argv[argc-1] : "", argv[c], addMode, compression))
652                            break;
653                      }
654                   }
655                   else
656                   {
657                      for(c = firstFileArg; c<firstFileArg + numFiles; c++)
658                      {
659                         FileAttribs exists = FileExists(argv[c]);
660                         if(exists)
661                         {
662                            ArchiveDir dir = null;
663                            char file[MAX_LOCATION], directory[MAX_LOCATION] = "";
664                            FileStats stats;
665                            GetLastDirectory(argv[c], file);
666                            if(extractWhere)
667                            {
668                               strcpy(directory, argv[argc-1]);
669                               FileGetStats(directory, &stats);
670                            }
671                            else
672                               StripLastDirectory(argv[c], directory);
673                            
674                            dir = archive.OpenDirectory(directory, extractWhere ? &stats : null, addMode);
675                            if(dir)
676                            {
677                               if(!AddToArchive(archive, dir, file, argv[c], addMode, compression))
678                                  break;
679                               delete dir;
680                            }
681                         }
682                      }
683                   }
684                   delete archive;
685                }
686                break;
687             }
688             case ARCHIVE_ACTION_CLEAR:
689             {
690                // Strip the archive from a file (useful for data embedded with executable code)
691                Archive archive = ArchiveOpen(argv[2], { true });
692                if(archive)
693                {
694                   archive.Clear();
695                   delete archive;
696                   Logf("Archive cleared: %s.\n", argv[2]);
697                }
698                break;
699             }
700             case ARCHIVE_ACTION_DELETE:
701             {
702                Archive archive = ArchiveOpen(argv[2], { true });
703                if(archive)
704                {
705                   for(c = firstFileArg; c<firstFileArg + numFiles; c++)
706                   {
707                      char fileName[MAX_LOCATION];
708                      FileAttribs exists;
709
710                      strcpy(fileName, archivePath);
711                      PathCat(fileName, argv[c]);
712                      FileFixCase(fileName);
713                      exists = FileExists(fileName);
714                      if(exists)
715                      {
716                         ArchiveDir dir;
717                         char file[MAX_LOCATION], directory[MAX_LOCATION] = "";
718                         GetLastDirectory(argv[c], file);
719                         StripLastDirectory(argv[c], directory);
720                         dir = archive.OpenDirectory(directory, null, 0);
721                         if(dir)
722                         {
723                            Logf("Deleting file %s in directory %s.\n", file,
724                               directory[0] ? directory : "root");
725                            dir.Delete(file);
726                            delete dir;
727                         }
728                      }
729                   }
730                   delete archive;
731                }
732                break;
733             }
734             case ARCHIVE_ACTION_MOVE:
735             {
736                Archive archive = ArchiveOpen(argv[2], { true });
737                if(archive)
738                {
739                   char fileName[MAX_LOCATION];
740     
741                   for(c = firstFileArg; c<firstFileArg + numFiles; c++)
742                   {
743                      strcpy(fileName, archivePath);
744                      PathCat(fileName, argv[c]);
745                      MoveFileInArchive(&archive, fileName, argv[argc-1]);
746                   }
747                   delete archive;
748                }
749                break;
750             }
751             case ARCHIVE_ACTION_RENAME:
752             {
753                Archive archive = ArchiveOpen(argv[2], { true });
754                if(archive)
755                {
756                   char fileName[MAX_LOCATION];
757                   FileAttribs exists;
758                   strcpy(fileName, archivePath);
759                   PathCat(fileName, argv[3]);
760                   exists = FileExists(fileName);
761                   if(exists)
762                   {
763                      char newName[MAX_FILENAME] = "", rest[MAX_FILENAME];
764                      SplitDirectory(argv[4], newName, rest);
765                      if(!rest[0])
766                      {
767                         ArchiveDir dir;
768                         char name[MAX_LOCATION], directory[MAX_LOCATION] = "";
769
770                         GetLastDirectory(argv[3], name);
771                         StripLastDirectory(argv[3], directory);
772                         if(!directory[0] ||
773                            ((!newName[0] || newName[1] != ':') &&
774                             newName[0] != '\\' && newName[0] != '/'))
775                         {
776                            char existingFilePath[MAX_LOCATION], * existingFile;
777                            FileAttribs outputExists;
778
779                            Logf("Renaming %s in directory %s to %s.\n", name,
780                               directory[0] ? directory : "root", newName);
781                            strcpy(existingFilePath, archivePath);
782                            existingFile = existingFilePath + strlen(existingFilePath);
783                            PathCat(existingFile, directory);
784                            PathCat(existingFile, newName);
785
786                            outputExists = FileExists(existingFilePath);
787                            if(outputExists.isDirectory)
788                            {
789                               FileListing listing;
790                               while(listing.Find())
791                                  MoveFileInArchive(&archive, listing.path, existingFile);
792                               dir = archive.OpenDirectory(directory, null, 0);
793                               if(dir)
794                               {
795                                  dir.Delete(name);
796                                  delete dir;
797                               }
798                            }
799                            else
800                            {
801                               dir = archive.OpenDirectory(directory, null, 0);
802                               if(dir)
803                               {
804                                  if(outputExists)
805                                     dir.Delete(newName);
806                                  dir.Rename(name, newName);
807                                  delete dir;
808                               }
809                            }
810                         }
811                         else
812                            Logf("Drive letters and %s only valid at root.\n", DIR_SEPS);
813                      }
814                      else
815                         Log("New name contains directory structure.\n");
816                   }
817                   delete archive;
818                }
819                break;
820             }
821             case ARCHIVE_ACTION_SELFEXT:
822             {
823                Archive archive;
824    #ifdef __WIN32__
825                ExtractFileFromArchive(":extract.exe", argv[3]);
826    #else
827                ExtractFileFromArchive(":extract", argv[3]);
828                chmod(argv[3], 0755);
829    #endif
830                archive = ArchiveOpen(argv[3], { true });
831                if(archive)
832                {
833                   archive.totalSize = 0;
834                   AddToArchive(archive, null, "", archivePath, replace, 9);
835                   delete archive;
836                }
837                break;
838             }
839          }
840       }
841       else
842          ShowSyntax();
843    }
844 }