/*
   
   qarch.c: Utility to find out which compiler and -qarch options 
   were used for a binary.

   Copyright:
   Fred Hucht (fred@thp.Uni-Dusiburg.DE) and
   Michael Staats (michael@thp.Uni-Dusiburg.DE)
   
   Free software according GNU Public license
   
   Thanks to Thomas Braunbeck (braunbec@mainz.ibm.de),
   AS Software Service AIX, Germany 

   Compilation:
   either "make qarch"
   or     "cc -O -o qarch qarch.c"

   
   */


#define VERSION "1.1"
static char *What[] = { "@(#)qarch V " VERSION " (C) F. Hucht & M. Staats",
                         "@(#)Email: michael@thp.Uni-Duisburg.DE",
                         "@(#)       fred@thp.Uni-Duisburg.DE"};

#include <a.out.h>
#include <ar.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/debug.h>

/* Under AIX4, these are in <aouthdr.h> */
#ifndef TCPU_INVALID
# define TCPU_INVALID   0
# define TCPU_PPC       1
# define TCPU_PPC64     2
# define TCPU_COM       3
# define TCPU_PWR       4
# define TCPU_ANY       5
# define TCPU_601       6
# define TCPU_PWR1      10
# define TCPU_PWRX      224
#endif

struct TypeNames {
    short n;
    const char *s;
} CPUTypes[] = {
    TCPU_INVALID,       "Invalid",
    TCPU_PPC,           "PowerPC32",
    TCPU_PPC64,         "PowerPC64",
    TCPU_COM,           "Common",
    TCPU_PWR,           "POWER",
    TCPU_ANY,           "Any",
    TCPU_601,           "PPC601",
    TCPU_PWR1,          "POWER1",
    TCPU_PWRX,          "POWER2",
    -1,                 "Unknown"
},
CompilerTypes[] = {
    TB_C,         "C",
    TB_FORTRAN,   "Fortran",
    TB_PASCAL,    "Pascal",     
    TB_ADA,       "Ada",
#ifdef TB_PLI
    TB_PLI,       "PL/I",
#endif
#ifdef TB_PL1
    TB_PL1,       "PL/I",
#endif
    TB_BASIC,     "Basic",
    TB_LISP,      "Lisp",
    TB_COBOL,     "Cobol",
    TB_MODULA2,   "Modula2",
    TB_CPLUSPLUS, "C++",
    TB_RPG,       "RPG",
    TB_PL8,       "PL8",
    TB_ASM,       "Assembler",
    -1,           "Unknown"
};

const char *Type2String(unsigned short n, struct TypeNames TN[]) {
    int i;
    for (i = 0; TN[i].n != n && TN[i].n >= 0; i++);
    return TN[i].s;
}

int parse_coff(char *FName, int fd, long offset) {
    struct filehdr f_hdr;
    struct syment sym;
    union  auxent aux;
    int file_found = 0, nsyms, naux;

    lseek(fd, offset, 0);
    
    if(read(fd, &f_hdr, sizeof (f_hdr)) != sizeof(f_hdr) ||
       f_hdr.f_magic != 0x01df) {
        fprintf(stderr, "%s: not in XCOFF format\n", FName);
        return;
    }
    lseek(fd, f_hdr.f_symptr + offset, 0);
    
    for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++) {
        read(fd, &sym, SYMESZ);
        for (naux = 0; naux < sym.n_numaux; naux++, nsyms++) {
            read(fd, &aux, AUXESZ);
            if (sym.n_sclass == C_FILE && aux.x_file._x.x_ftype == XFT_FN) {
                file_found = 1;
                printf("%s(%s): %s:%s\n", FName, aux.x_file.x_fname,
                       Type2String((sym.n_type >> 8) & 0xff, CompilerTypes),
                       Type2String(sym.n_type        & 0xff, CPUTypes));
            }
        }
    }
    if (!file_found) {
        fprintf(stderr, "%s: no source file information found\n", FName);
    }
}

int decode_lib_subf(char *Fname, int fd, long subfile_offset, 
                    long *nextoffset,  char **member_name_ptr) {
    int namelen;
    int member_length;
    char *name;
    struct ar_hdr hdr1;
    int bytes_read;

    lseek(fd, subfile_offset, 0);
    bytes_read = read(fd, &hdr1, sizeof(hdr1));

    if (bytes_read == 0) return 0; /* end of archive, should not happen */

    if (sizeof(hdr1) != bytes_read) {
        fprintf(stderr, "%s: malformed library archive\n", Fname);
        return 0;
    }
    if (sscanf(hdr1.ar_size, "%d", &member_length) != 1) {
        fprintf(stderr, 
                "malformatted header of archive member (ar_size) in %s\n", 
                Fname);
        return 0;
    }
    if (sscanf(hdr1.ar_namlen, "%d", &namelen) != 1) {
        fprintf(stderr, 
                "malformatted header of archive member (ar_namlen) in %s\n", 
                Fname);
        return 0;
    }
    if (sscanf(hdr1.ar_nxtmem, "%d", nextoffset) != 1) {
        fprintf(stderr, 
                "malformatted header of archive member (ar_nxtmem) in %s\n", 
                Fname);
        return 0;
    }

    name = (char *)malloc(namelen+1);
    strncpy (name, hdr1._ar_name.ar_name, namelen);
    if (read(fd, name + 2, namelen - 2) != namelen - 2) {
        fprintf(stderr, 
                "malformatted header of archive member (name) in %s\n", 
                Fname);
        return 0;
    }
    name[namelen] = 0;
    *member_name_ptr = name;
    return member_length;
}




int main(int argc, char *argv[]) {
    int fd, i, member_len;
    char *member, label[512];
    long subfile_offset, lstmoff, nextoffset, coff_offset;
    FL_HDR filehdr;
    
    for(i=1; i<argc; i++) {
        if(0 > (fd = open(argv[i], O_RDONLY))) {
            perror(argv[i]);
        } else {
            if (read(fd, &filehdr, FL_HSZ) == FL_HSZ && 
                strncmp(filehdr.fl_magic, AIAMAG, SAIAMAG) == 0) {
                /* library */
                nextoffset = atoi(filehdr.fl_fstmoff);
                lstmoff    = atoi(filehdr.fl_lstmoff);
                if (nextoffset != 0) while (subfile_offset != lstmoff) {
                    subfile_offset = nextoffset;
                    if ((member_len = decode_lib_subf(argv[i], fd, 
                                                      subfile_offset,
                                                      &nextoffset, 
                                                      &member) <= 0)) break;
                    
                    coff_offset = subfile_offset + AR_HSZ + strlen(member);
                    if (coff_offset & 1) coff_offset++;
                    strncpy(label, argv[i], 250);
                    strcat(label, "->");
                    strncat(label, member, 255);
                    parse_coff(label, fd, coff_offset);
                }
            } else {
                parse_coff(argv[i], fd, 0L);
            }
            close(fd);
        }
    }
    return 0;
}
