30 May 2015, 06:23 | #1 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,434
|
UnArchiving LHA Files Programmatically: Confused about XPK, XAD, XFD
Hi all!
I am trying to simply extract one (or all) files from a LHA archive programmatically and I am confused: should I use XPK, XAD, or XFD? Reading the docs and Web sites, it sems that XAD is the way to go but then I look at the code examples and see that, for example, xadUnFile.c is a thousand lines of code! I was naively thinking that calling the xadmaster.library in few lines of code would be sufficient Am I missing something or is there really no free lunch? Thanks! |
30 May 2015, 14:01 | #2 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
xadunfile does more than just unpacking one file. it parses lots of arguments and options, has pattern matching, error handling, and code is prepared for use multiple files as input.
what should xadmaster do if your uncompressed file already exists? it is not part of the unpacking libs to handle these scenarios. that's the code you have to provide. no, there is no free lunch code block will be much smaller without that many options, see start() section |
04 June 2015, 17:51 | #3 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,434
|
Hi BigFan!
Thanks a lot! You are right: the source code of the xadUnFile and its start() function is very helpful... I am stating from there and already got something compiling and working so-so... Cheers! Last edited by tygre; 04 June 2015 at 19:15. Reason: Wrong tool name! |
08 June 2015, 04:51 | #4 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,434
|
@BigFan and all!
For the record, here is a minimal use of the xadmasterl.library to unarchive some archive, XAD is very neat! Code:
int extract_archive( const char *archive_path, const char *destination_path) { struct xadMasterBase *xadmasterbase; struct xadArchiveInfo *ai; struct xadFileInfo *fi; LONG e; UBYTE destination_name[NAMEBUFSIZE]; if((xadmasterbase = (struct xadMasterBase *)OpenLibrary("xadmaster.library", 11))) { xadMasterBase = xadmasterbase; if((ai = (struct xadArchiveInfo *)xadAllocObjectA(XADOBJ_ARCHIVEINFO, 0))) { e = xadGetInfo( ai, XAD_INFILENAME, "RAM:Slurp10.lha", TAG_DONE); if(DEBUG) { printf("xc_ArchiverName = %s\n", ai->xai_Client->xc_ArchiverName); } if(!e) { fi = ai->xai_FileInfo; while(fi) { if(!(fi->xfi_Flags & XADFIF_LINK || fi->xfi_Flags & XADFIF_DIRECTORY)) { CopyMem("RAM:", destination_name, strlen("RAM:") + 1); AddPart(destination_name, FilePart(fi->xfi_FileName), NAMEBUFSIZE); e = xadFileUnArc( ai, XAD_OUTFILENAME, destination_name, XAD_ENTRYNUMBER, fi->xfi_EntryNumber, TAG_DONE); if(DEBUG) { printf("e = %lu\n", e); } } else if(DEBUG) { printf("\tSkipped link or directory\n"); } fi = fi->xfi_Next; } goto _RETURN_OK; } else { perror("Error in extract_archive(...), call to xadGetInfo(...) failed"); goto _RETURN_ERROR; } } else { perror("Error in extract_archive(...), cannot allocate struct xadArchiveInfo"); goto _RETURN_ERROR; } } else { perror("Error in extract_archive(...), cannot open xadmaster.library"); goto _RETURN_ERROR; } goto _RETURN_OK; _RETURN_OK: xadFreeInfo(ai); xadFreeObjectA(ai, 0); CloseLibrary((struct Library *)xadmasterbase); return OK; _RETURN_ERROR: if(ai) { xadFreeInfo(ai); xadFreeObjectA(ai, 0); } if(xadmasterbase) { CloseLibrary((struct Library *)xadmasterbase); } return ERROR; } PS. Being a newbie in Amiga programming, any advice is welcome to improve my code! |
08 June 2015, 06:09 | #5 | |
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 48
Posts: 3,831
|
Quote:
Code:
int bla(void) { struct foo *a; struct bar *b; int c; a = function1("abcdef"); if (a == NULL) { printError("Some error."); goto error1; } b = function2(12345); if (b == NULL) { printError("Some other error."); goto error2; } c = funtion3(a, b); cleanup2(b); cleanup1(a); return c; error2: cleanup1(a); error1: return -1; } |
|
08 June 2015, 12:22 | #6 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
I disagree.
Goto is useless in this case. But first: Check your code for redundancy. Doing so you'll notice, that goto is repeated and the code under both _RETURN_xx labels is identical. My suggestion: set an integer variable named rc (as returncode) replace goto with rc=OK; or rc=ERROR; appropriatly. delete labels set cleanup code as if there is an error: if(ai) { xadFreeInfo(ai); xadFreeObjectA(ai, 0); } if(xadmasterbase) { CloseLibrary((struct Library *)xadmasterbase); } return rc; this way your are always fine (on success and failure). else {} conditions will be executed only when if() returns false, that is : if inner loop succeeded , all if() succeeded before and none of the else{} will be executed. if not then code will jump to the end of (un)conditioned {} , say it skips all else{} statements prior to the one that fails or can't be checked or do not need to be checked. your goto doesn't do anything usefull. i think you used this to distuingish between success and failure. be careful: any statement that is not part of else{} will be executed always as regular. the exzessive use of goto is a sign of bad code. use goto wisely, e.g. your code is recursive and the only way to exit on failure or infinite loop is a goto. me too, i hate doctrines and religious talk about the use of goto, but in your case it leads to weird code quickly. |
08 June 2015, 16:00 | #7 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,434
|
@Thorham and @BigFan
Thanks for reading and commenting my code! My use of gotos was my (poor) attempt to mimic throws/catches in C . I'll revise my code based on your suggestions... Would you have any comment on my (mis)use/lack-of-use of AmigaOS functions? Thanks! |
08 June 2015, 16:48 | #8 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Path "RAM:" is hardcoded, CopyMem() is not necessary if you initialize with
Code:
ubyte destination_name[NAMEBUFFER]={'R','A','M',':',0}; What abuse of AOS functions? There aren't many. Last edited by BigFan; 08 June 2015 at 17:25. Reason: stumbled over *xadmasterbase , text deleted |
08 June 2015, 18:02 | #9 | |
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 48
Posts: 3,831
|
No, it's not, because it can get rid of the deep indentation and the else blocks, making the code shorter and easier to follow. You can see this in my example.
Quote:
Another point is that you may want to do a little more than just cleanup in the case of an error. Or just something different. My example shows the latter (only one cleanup function is called, because the first failed). If you have to duplicate a large amount of code, then it's probably a good idea to use a return variable. Here's an example of how I actually use goto: Code:
static int monitor_readFile(lua_State *L) { struct FileInfoBlock *finfo; BPTR fhandle; APTR fmem; char *fname = (char *)luaL_checkstring(L, 1); long ret; finfo = AllocDosObject(DOS_FIB, fibTags); if (finfo == NULL) goto error1; fhandle = Open(fname, MODE_OLDFILE); if (fhandle == NULL) goto error2; if (ExamineFH(fhandle, finfo) == 0) goto error3; fmem = malloc(finfo->fib_Size); if (fmem == NULL) goto error3; ret = Read(fhandle, fmem, finfo->fib_Size); Close(fhandle); FreeDosObject(DOS_FIB, finfo); lua_pushunsigned(L, (unsigned long)fmem); lua_pushinteger(L, finfo->fib_Size); // stupid bug here lua_pushinteger(L, ret); return 3; error3: Close(fhandle); error2: FreeDosObject(DOS_FIB, finfo); error1: lua_pushnil(L); return 1; } Last edited by Thorham; 09 June 2015 at 09:02. |
|
08 June 2015, 19:02 | #10 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,434
|
Thanks BigFan and Thorham, I'm going to update my code to make it more lean and mean!
Last edited by tygre; 08 June 2015 at 19:10. |
09 June 2015, 07:31 | #11 |
Join Date: Jul 2008
Location: Sweden
Posts: 2,269
|
That's spaghetti-code Four levels of indentation isn't really bad, but if you want to get rid of them, then initializing each pointer and structure on the condition that the previous one succeeded is a lot cleaner and easier to follow than a bunch of gotos.
You also have a bug in there: accessing the data in finfo after having called FreeDosObject on it. |
09 June 2015, 08:57 | #12 | |
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 48
Posts: 3,831
|
I don't agree.
Quote:
Missed that one, thanks |
|
09 June 2015, 09:34 | #13 | |
Join Date: Jul 2008
Location: Sweden
Posts: 2,269
|
Quote:
Code:
static int monitor_readFile(lua_State *L) { struct FileInfoBlock* finfo; char* fname; BPTR fhandle = 0; APTR fmem = NULL; BOOL exres = FALSE; int retval; fname = (char*)luaL_checkstring(L, 1); finfo = AllocDosObject(DOS_FIB, fibTags); if (finfo) fhandle = Open(fname, MODE_OLDFILE); if (fhandle) exres = ExamineFH(fhandle, finfo); if (exres) fmem = malloc(finfo->fib_Size); if (fmem) { long ret = Read(fhandle, fmem, finfo->fib_Size); lua_pushunsigned(L, (unsigned long)fmem); lua_pushinteger(L, finfo->fib_Size); lua_pushinteger(L, ret); retval = 3; } else { lua_pushnil(L); retval = 1; } if (fhandle) Close(fhandle); if (finfo) FreeDosObject(DOS_FIB, finfo); return retval; } Last edited by Leffmann; 09 June 2015 at 16:18. |
|
09 June 2015, 13:16 | #14 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
@Thorham
My posting about 'goto' addresses Tygre, not you. Mentioned '_Return_' code section is definitly not yours, so stop taking things personally. And i'm not willing to use @ periodically and rewrite every posting to avoid users getting pissed when misinterpreting my posts. And i agree with Leffmann, redundant code and usage of 'goto' is the path to the dark side of the force. Once you get used to it, you'll hardly find a way back into the 'light'. It looks like someone has ported BASIC to C. Whenever a user is posting spaghetti code, he'll be confronted with harsh criticism in any advanded users forum. No need to encourage the usage of 'goto', except you can fundamentally argue for it because of its advantage in a very special case. |
09 June 2015, 15:32 | #15 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,434
|
@thread
Thank you all for your comments, I really appreciate your suggestions and will revise my code accordingly . You all make good points pros and cons using gotos . My using gotos was my (poor) attempt to simulate throws/catches but also to have "single exit points", actually two: one exit point for "normal returns" and one for "error returns". Please feel free to give more advices on my code! Let's not argue whether this is spaghetti code or not... we are still far from Toyota's code and their 10,000 global variables . Cheers! Last edited by tygre; 09 June 2015 at 15:33. Reason: Minor plead |
09 June 2015, 17:39 | #16 | |||
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 48
Posts: 3,831
|
Quote:
That's what I was actually referring to. Goto isn't useless in that code if used properly. Quote:
Quote:
|
|||
15 June 2015, 20:05 | #17 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,434
|
Hi all!
For the record, here is the (minimum?) code needed to use the xadmaster.library to unarchive an archive file (no subfolders or links, no checking whether the destination exists... really the bare minimum), thanks a lot for your help and suggestions! The rest of the code is available here and I welcome all your comments/suggestions to improve it! Cheers! Tygre Code:
/* AmiModRadio All of Aminet modules at your fingertips Copyright 2015 Tygre <tygre@chingu.asia> This file is part of AmiModRadio. AmiModRadio is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. AmiModRadio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with AmiModRadio. If not, see <http://www.gnu.org/licenses/>. Entirely developed on an Amiga! (Except for source code versioning...) tygre@chingu.asia */ #include "xad.h" #include "fortify.h" #include "utils.h" #include <stdio.h> #include <errno.h> #include <string.h> #include <proto/xadmaster.h> #include <proto/exec.h> #include <proto/dos.h> #define DEBUG 0 #define DEBUG_VERBOSE 0 #define MAX_SIZE_LIST 512 #define MAX_SIZE_NAME 512 struct xadMasterBase *xadMasterBase = NULL; int extract_archive( IN char *archive_path, IN char *destination_path, OUT char **list) { struct xadMasterBase *xadmasterbase = NULL; struct xadArchiveInfo *ai = NULL; struct xadFileInfo *fi = NULL; LONG e = 0; char *list_temp = NULL; char *list_temp_for_realloc = NULL; int number_of_chars = 0; int maximum_number_of_chars = 0; UBYTE destination_name[MAX_SIZE_NAME]; maximum_number_of_chars = MAX_SIZE_LIST; list_temp = calloc(maximum_number_of_chars, sizeof(char)); if(list_temp == NULL) { perror("Error in extract_archive(...), cannot allocate memory"); goto _RETURN_ERROR; } if(!(xadmasterbase = (struct xadMasterBase *)OpenLibrary("xadmaster.library", 11))) { perror("Error in extract_archive(...), cannot open xadmaster.library"); goto _RETURN_ERROR; } xadMasterBase = xadmasterbase; if(!(ai = (struct xadArchiveInfo *)xadAllocObjectA(XADOBJ_ARCHIVEINFO, 0))) { perror("Error in extract_archive(...), cannot allocate struct xadArchiveInfo"); goto _RETURN_ERROR; } e = xadGetInfo( ai, XAD_INFILENAME, archive_path, TAG_DONE); if(e) { perror("Error in extract_archive(...), call to xadGetInfo(...) failed"); goto _RETURN_ERROR; } if(DEBUG) { printf("xc_ArchiverName = %s\n", ai->xai_Client->xc_ArchiverName); } // Everything is set, let's extract the content of the archive :-) // This loop ignores links and directories. It only extracts files in the archive root. fi = ai->xai_FileInfo; while(fi) { if(!(fi->xfi_Flags & XADFIF_LINK || fi->xfi_Flags & XADFIF_DIRECTORY)) { CopyMem(destination_path, destination_name, strlen(destination_path) + 1); AddPart(destination_name, FilePart(fi->xfi_FileName), MAX_SIZE_NAME); e = xadFileUnArc( ai, XAD_OUTFILENAME, destination_name, XAD_ENTRYNUMBER, fi->xfi_EntryNumber, TAG_DONE); if(e) { printf("Error in extract_archive(...), call to xadFileUnArc(...) failed\n"); printf("\te = %ld for file = %s\n", e, destination_name); } else { number_of_chars += strlen(destination_name) + 2; if(number_of_chars >= maximum_number_of_chars) { maximum_number_of_chars += MAX_SIZE_LIST; list_temp_for_realloc = realloc(list_temp, maximum_number_of_chars); if(list_temp_for_realloc == NULL) { perror("Error in extract_archive(...), cannot reallocate memory"); goto _RETURN_ERROR; } list_temp = list_temp_for_realloc; } strcat(list_temp, destination_name); strcat(list_temp, "\n"); } } else if(DEBUG) { printf("\tSkipped link or directory\n"); } fi = fi->xfi_Next; } goto _RETURN_OK; _RETURN_OK: xadFreeInfo(ai); xadFreeObjectA(ai, 0); CloseLibrary((struct Library *)xadmasterbase); if(string_fit(&*list, list_temp) == ERROR) { perror("Error in extract_archive(...), cannot trim list"); goto _RETURN_ERROR; } free(list_temp); return OK; goto _RETURN_ERROR; _RETURN_ERROR: if(ai) { xadFreeInfo(ai); xadFreeObjectA(ai, 0); } if(xadmasterbase) { CloseLibrary((struct Library *)xadmasterbase); } if(list_temp != NULL) { free(list_temp); } return ERROR; } |
16 June 2015, 22:57 | #18 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
My suggestions:
Use either c or aos functions. this code is not portable and i suggest you to replace c functions, especially when it comes to memory allocation. Sometimes perror and printf is used. Why? Do not use goto like you did in the past. Your code is f*cking weird spaghetti code. Your code gets unreadible, instead use modular and structured code. Split functions. Keep them small and simple. If **list is for viewing/printing only, then write dest_names to file open in T: This saves you from reallocation and the possible failure of it. Thats another reason to split up your functions. AddtoList can be file open/write instead of realloc() without interefering with existing code blocks. I hope you get the point I have not compiled this. Take it with a grain of salt. Code:
#include "xad.h" #include "fortify.h" #include "utils.h" #include <stdio.h> #include <errno.h> #include <string.h> #include <proto/xadmaster.h> #include <proto/exec.h> #include <proto/dos.h> #define DEBUG 0 #define DEBUG_VERBOSE 0 #define MAX_SIZE_LIST 512 #define MAX_SIZE_NAME 512 // Error messages used in extract_archive() // message text takes less memory now #define ERRMSG_100 "Error in extract_archive(), " #define ERRMSG_101 "cannot allocate memory!" #define ERRMSG_102 "cannot open xadmaster.library!" #define ERRMSG_103 "call to xadFileUnArc(...) failed!" #define ERRMSG_104 "cannot reallocate memory!" #define ERRMSG_104 "cannot trim list!" //char * errmsg_105 = ; //char * errmsg_106 = ; struct xadMasterBase * xadMasterBase = NULL; // list entry management is new function // keep function bodies lean and clean, do not overload with different purposes // do not overload with features int AddtoList(char *, char *); int extract_archive( IN char *archive_path, IN char *destination_path, OUT char **list) { struct xadMasterBase *xadmasterbase = NULL; struct xadArchiveInfo *ai = NULL; struct xadFileInfo *fi = NULL; char *list_temp = NULL; char *list_temp_for_realloc = NULL; long e ; int rc = OK; UBYTE destination_name[MAX_SIZE_NAME]; if ((list_temp = calloc(MAX_SIZE_LIST, sizeof(char))) == NULL); { // compiler concatenates subsequent strings automatically perror(ERRMSG_100 ERRMSG_101); // there is not much to clean up at this point, so just quit this funtion block return ERROR; } if((xadmasterbase = (struct xadMasterBase *)OpenLibrary("xadmaster.library", 11)) == NULL) { perror(ERRMSG_100 ERRMSG_102); return ERROR; } xadMasterBase = xadmasterbase; if(ai = (struct xadArchiveInfo *)xadAllocObjectA(XADOBJ_ARCHIVEINFO, 0)) { if(e = xadGetInfo(ai, XAD_INFILENAME, archive_path, TAG_DONE)) { // Everything is set, let's extract the content of the archive :-) // This loop ignores links and directories. It only extracts files in the archive root. fi = ai->xai_FileInfo; while(fi) { if(!(fi->xfi_Flags & XADFIF_LINK || fi->xfi_Flags & XADFIF_DIRECTORY)) { CopyMem(destination_path, destination_name, strlen(destination_path) + 1); AddPart(destination_name, FilePart(fi->xfi_FileName), MAX_SIZE_NAME); if (e = xadFileUnArc(ai, XAD_OUTFILENAME, destination_name, XAD_ENTRYNUMBER, fi->xfi_EntryNumber, TAG_DONE)) { printf(ERRMSG_100 ERRMSG_103); printf("\te = %ld for file = %s\n", e, destination_name); } else { //quit while-loop on error (realloc failed) if(AddtoList(list_temp, destination_name)) { rc = ERROR; break; } } } fi = fi->xfi_Next; } } else { rc = ERROR; } } else { rc = ERROR; } // clean up section if(ai) { xadFreeInfo(ai); xadFreeObjectA(ai, 0); } if(xadmasterbase) { CloseLibrary((struct Library *)xadmasterbase); } if(string_fit(&*list, list_temp) == ERROR) { perror("Error in extract_archive(...), cannot trim list"); } if(list_temp) { free(list_temp); } return rc; } int AddtoList(char * list_temp, char * destination_name) { static int num_chars = 0; static int listsize = MAX_SIZE_LIST; num_chars += strlen(destination_name) + 2; if(number_of_chars >= MAX_SIZE_LIST) { int * list_temp_for_realloc = realloc(list_temp, (listsize += MAX_SIZE_LIST)); if(list_temp_for_realloc != NULL) { list_temp = list_temp_for_realloc; } else { perror(ERRMSG_100 ERRMSG_104); return 20; // or set return code to ERROR } } strcat(list_temp, destination_name); strcat(list_temp, "\n"); return 0; } Last edited by BigFan; 16 June 2015 at 23:02. |
17 June 2015, 00:26 | #19 | ||||
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,434
|
Hi BigFan!
Quote:
No good reason, my bad Actually, could you suggest me the related AmigaOS function? I saw PrintFault but it seems to require always at least one parameter? Quote:
Quote:
Quote:
Cheers! Last edited by tygre; 17 June 2015 at 03:21. Reason: Forgot to say that I am using Cubic IDE and vbcc. |
||||
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Unarchiving LHA & LZX files on Windows XP | amigauser | Amiga scene | 58 | 28 September 2017 23:36 |
Fixed XAD for lha (OS3.9) | ancalimon | request.Apps | 1 | 27 July 2014 17:39 |
Amateur requires help with LHA files..... | THEGMAN | support.WinUAE | 3 | 27 September 2010 11:27 |
lha files read by UAE? | dionisis | New to Emulation or Amiga scene | 3 | 09 April 2007 11:55 |
How to use LHA files (WinUAE) | Jacko | New to Emulation or Amiga scene | 4 | 14 January 2007 11:46 |
|
|