int parse(char *elf) {
    int fd;
    struct stat st;
    uint8_t *elf_map;
    int count;
    char *tmp;
    char *name;
    char flag[4];

    MODE = get_elf_class(elf);

    fd = open(elf, O_RDONLY);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    if (fstat(fd, &st) < 0) {
        perror("fstat");
        return -1;
    }

    elf_map = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    if (elf_map == MAP_FAILED) {
        perror("mmap");
        return -1;
    }

    /* 32bit */
    if (MODE == ELFCLASS32) {
        /* ELF Header Information */
        Elf32_Ehdr *ehdr;
        ehdr = (Elf32_Ehdr *)elf_map;

        INFO("ELF Header\n");
        switch (ehdr->e_type) {
            case ET_NONE:
                tmp = "An unknown type";
                break;

            case ET_REL:
                tmp = "A relocatable file";
                break;

            case ET_EXEC:
                tmp = "An executable file";
                break;

            case ET_DYN:
                tmp = "A shared object";
                break;

            case ET_CORE:
                tmp = "A core file";
                break;

            default:
                tmp = "An unknown type";
                break;
        }
        PRINT_HEADER_EXP("e_type:", ehdr->e_type, tmp);

        switch (ehdr->e_type) {
            case EM_NONE:
                tmp = "An unknown machine";
                break;

            case EM_M32:
                tmp = "AT&T WE 32100";
                break;

            case EM_SPARC:
                tmp = "Sun Microsystems SPARC";
                break;

            case EM_386:
                tmp = "Intel 80386";
                break;

            case EM_68K:
                tmp = "Motorola 68000";
                break;

            case EM_88K:
                tmp = "Motorola 88000";
                break;

            case EM_860:
                tmp = "Intel 80860";
                break;

            case EM_MIPS:
                tmp = "MIPS RS3000 (big-endian only)";
                break;

            case EM_PARISC:
                tmp = "HP/PA";
                break;

            case EM_SPARC32PLUS:
                tmp = "SPARC with enhanced instruction set";
                break;

            case EM_PPC:
                tmp = "PowerPC";
                break;

            case EM_PPC64:
                tmp = "PowerPC 64-bit";
                break;

            case EM_S390:
                tmp = "IBM S/390";
                break;

            case EM_ARM:
                tmp = "Advanced RISC Machines";
                break;

            case EM_SH:
                tmp = "Renesas SuperH";
                break;

            case EM_SPARCV9:
                tmp = "SPARC v9 64-bit";
                break;

            case EM_IA_64:
                tmp = "Intel Itanium";
                break;

            case EM_X86_64:
                tmp = "AMD x86-64";
                break;

            case EM_VAX:
                tmp = "DEC Vax";
                break;

            default:
                tmp = "An unknown machine";
                break;
        }
        PRINT_HEADER_EXP("e_machine:", ehdr->e_machine, tmp);

        switch (ehdr->e_version) {
            case EV_NONE:
                tmp = "Invalid version";
                break;

            case EV_CURRENT:
                tmp = "Current version";
                break;

            default:
                tmp = "Known version";
                break;
        }
        PRINT_HEADER_EXP("e_version:", ehdr->e_version, tmp);
        PRINT_HEADER("e_entry:", ehdr->e_entry);
        PRINT_HEADER("e_phoff:", ehdr->e_phoff);
        PRINT_HEADER("e_shoff:", ehdr->e_shoff);
        PRINT_HEADER("e_flags:", ehdr->e_flags);
        PRINT_HEADER("e_ehsize:", ehdr->e_ehsize);
        PRINT_HEADER("e_phentsize:", ehdr->e_phentsize);
        PRINT_HEADER("e_phnum:", ehdr->e_phnum);
        PRINT_HEADER("e_shentsize:", ehdr->e_shentsize);
        PRINT_HEADER("e_shentsize:", ehdr->e_shentsize);
        PRINT_HEADER("e_shstrndx:", ehdr->e_shstrndx);

        /* Section Information */
        Elf32_Shdr *shdr;
        Elf32_Phdr *phdr;
        Elf32_Shdr shstrtab;

        shdr = (Elf32_Shdr *)&elf_map[ehdr->e_shoff];
        phdr = (Elf32_Phdr *)&elf_map[ehdr->e_phoff];
        shstrtab = shdr[ehdr->e_shstrndx];

        INFO("Section Header Table\n");
        PRINT_SECTION_TITLE("Nr", "Name", "Type", "Addr", "Off", "Size", "Es", "Flg", "Lk", "Inf", "Al");
        for (int i = 0; i < ehdr->e_shnum; i++) {
            name = elf_map + shstrtab.sh_offset + shdr[i].sh_name;

            switch (shdr[i].sh_type) {
                case SHT_NULL:
                    tmp = "SHT_NULL";
                    break;

                case SHT_PROGBITS:
                    tmp = "SHT_PROGBITS";
                    break;

                case SHT_SYMTAB:
                    tmp = "SHT_SYMTAB";
                    break;

                case SHT_STRTAB:
                    tmp = "SHT_STRTAB";
                    break;

                case SHT_RELA:
                    tmp = "SHT_RELA";
                    break;

                case SHT_HASH:
                    tmp = "SHT_HASH";
                    break;

                case SHT_DYNAMIC:
                    tmp = "SHT_DYNAMIC";
                    break;

                case SHT_NOTE:
                    tmp = "SHT_NOTE";
                    break;

                case SHT_NOBITS:
                    tmp = "SHT_NOBITS";
                    break;

                case SHT_REL:
                    tmp = "SHT_REL";
                    break;

                case SHT_SHLIB:
                    tmp = "SHT_SHLIB";
                    break;

                case SHT_DYNSYM:
                    tmp = "SHT_DYNSYM";
                    break;

                case SHT_LOPROC:
                    tmp = "SHT_LOPROC";
                    break;

                case SHT_HIPROC:
                    tmp = "SHT_HIPROC";
                    break;

                case SHT_LOUSER:
                    tmp = "SHT_LOUSER";
                    break;

                case SHT_HIUSER:
                    tmp = "SHT_HIUSER";
                    break;

                default:
                    break;
            }

            if (strlen(name) > 15) {
                strcpy(&name[15 - 6], "[...]");
            }
            strcpy(flag, "   ");
            flag2str_sh(shdr[i].sh_flags, flag);
            PRINT_SECTION(i, name, tmp, shdr[i].sh_addr, shdr[i].sh_offset, shdr[i].sh_size, shdr[i].sh_entsize, \
                            flag, shdr[i].sh_link, shdr[i].sh_info, shdr[i].sh_addralign);
        }

        INFO("Program Header Table\n");
        PRINT_PROGRAM_TITLE("Nr", "Type", "Offset", "Virtaddr", "Physaddr", "Filesiz", "Memsiz", "Flg", "Align");
        for (int i = 0; i < ehdr->e_phnum; i++) {
            switch (phdr[i].p_type) {
                case PT_NULL:
                    tmp = "PT_NULL";
                    break;

                case PT_LOAD:
                    tmp = "PT_LOAD";
                    break;

                case PT_DYNAMIC:
                    tmp = "PT_DYNAMIC";
                    break;

                case PT_INTERP:
                    tmp = "PT_INTERP";
                    break;

                case PT_NOTE:
                    tmp = "PT_NOTE";
                    break;

                case PT_SHLIB:
                    tmp = "PT_SHLIB";
                    break;

                case PT_PHDR:
                    tmp = "PT_PHDR";
                    break;

                case PT_LOPROC:
                    tmp = "PT_LOPROC";
                    break;

                case PT_HIPROC:
                    tmp = "PT_HIPROC";
                    break;

                case PT_GNU_STACK:
                    tmp = "PT_GNU_STACK";
                    break;

                default:
                    break;
            }
            strcpy(flag, "   ");
            flag2str(phdr[i].p_flags, flag);
            PRINT_PROGRAM(i, tmp, phdr[i].p_offset, phdr[i].p_vaddr, phdr[i].p_paddr, phdr[i].p_filesz, phdr[i].p_memsz, flag, phdr[i].p_align);
        }

        INFO("Section to segment mapping\n");
        for (int i = 0; i < ehdr->e_phnum; i++) {
            printf("     [%2d]", i);
            for (int j = 0; j < ehdr->e_shnum; j++) {
                name = elf_map + shstrtab.sh_offset + shdr[j].sh_name;
                if (shdr[j].sh_addr >= phdr[i].p_vaddr && shdr[j].sh_addr + shdr[j].sh_size <= phdr[i].p_vaddr + phdr[i].p_memsz && shdr[j].sh_type != SHT_NULL) {
                    if (shdr[j].sh_flags >> 1 & 0x1) {
                        printf(" %s", name);
                    }
                }
            }
            printf("\n");
        }

        INFO("Dynamic link information\n");
        int dynstr;
        int dynamic;
        Elf32_Dyn *dyn;
        for (int i = 0; i < ehdr->e_shnum; i++) {
            name = elf_map + shstrtab.sh_offset + shdr[i].sh_name;
            if (!strcmp(name, ".dynstr")) {
                dynstr = i;
            }
            if (!strcmp(name, ".dynamic")) {
                dynamic = i;
            }
        }

        char value[50];
        name = "";
        dyn = (Elf32_Dyn *)&elf_map[shdr[dynamic].sh_offset];
        count = shdr[dynamic].sh_size / sizeof(Elf32_Dyn);
        INFO("Dynamic section at offset 0x%x contains %d entries\n", shdr[dynamic].sh_offset, count);
        PRINT_DYN_TITLE("Tag", "Type", "Name/Value");

        for(int i = 0; i < count; i++) {
            tmp = "";
            memset(value, 0, 50);
            snprintf(value, 50, "0x%x", dyn[i].d_un.d_val);
            switch (dyn[i].d_tag) {
                /* Legal values for d_tag (dynamic entry type).  */
                case DT_NULL:
                    tmp = "DT_NULL";
                    break;

                case DT_NEEDED:
                    tmp = "DT_NEEDED";
                    name = elf_map + shdr[dynstr].sh_offset + dyn[i].d_un.d_val;
                    snprintf(value, 50, "Shared library: [%s]", name);
                    break;

                case DT_PLTRELSZ:
                    tmp = "DT_PLTRELSZ";
                    break;

                case DT_PLTGOT:
                    tmp = "DT_PLTGOT";
                    break;

                case DT_HASH:
                    tmp = "DT_HASH";
                    break;

                case DT_STRTAB:
                    tmp = "DT_STRTAB";
                    break;

                case DT_SYMTAB:
                    tmp = "DT_SYMTAB";
                    break;

                case DT_RELA:
                    tmp = "DT_RELA";
                    break;

                case DT_RELASZ:
                    tmp = "DT_RELASZ";
                    break;

                case DT_RELAENT:
                    tmp = "DT_RELAENT";
                    break;

                case DT_STRSZ:
                    tmp = "DT_STRSZ";
                    break;

                case DT_SYMENT:
                    tmp = "DT_SYMENT";
                    break;

                case DT_INIT:
                    tmp = "DT_INIT";
                    break;

                case DT_FINI:
                    tmp = "DT_FINI";
                    break;

                case DT_SONAME:
                    tmp = "DT_SONAME";
                    break;

                case DT_RPATH:
                    tmp = "DT_RPATH";
                    break;

                case DT_SYMBOLIC:
                    tmp = "DT_SYMBOLIC";
                    break;

                case DT_REL:
                    tmp = "DT_REL";
                    break;

                case DT_RELSZ:
                    tmp = "DT_RELSZ";
                    break;

                case DT_RELENT:
                    tmp = "DT_RELENT";
                    break;

                case DT_PLTREL:
                    tmp = "DT_PLTREL";
                    break;

                case DT_DEBUG:
                    tmp = "DT_DEBUG";
                    break;

                case DT_TEXTREL:
                    tmp = "DT_TEXTREL";
                    break;

                case DT_JMPREL:
                    tmp = "DT_JMPREL";
                    break;

                case DT_BIND_NOW:
                    tmp = "DT_BIND_NOW";
                    break;

                case DT_INIT_ARRAY:
                    tmp = "DT_INIT_ARRAY";
                    break;

                case DT_FINI_ARRAY:
                    tmp = "DT_FINI_ARRAY";
                    break;

                case DT_INIT_ARRAYSZ:
                    tmp = "DT_INIT_ARRAYSZ";
                    break;

                case DT_FINI_ARRAYSZ:
                    tmp = "DT_FINI_ARRAYSZ";
                    break;

                case DT_RUNPATH:
                    tmp = "DT_RUNPATH";
                    break;

                case DT_FLAGS:
                    tmp = "DT_FLAGS";
                    snprintf(value, 50, "Flags: %d", dyn[i].d_un.d_val);
                    break;

                case DT_ENCODING:
                    tmp = "DT_ENCODING";
                    break;

                case DT_PREINIT_ARRAYSZ:
                    tmp = "DT_PREINIT_ARRAYSZ";
                    break;

                case DT_SYMTAB_SHNDX:
                    tmp = "DT_SYMTAB_SHNDX";
                    break;

                case DT_NUM:
                    tmp = "DT_NUM";
                    break;

                case DT_LOOS:
                    tmp = "DT_LOOS";
                    break;

                case DT_HIOS:
                    tmp = "DT_HIOS";
                    break;

                case DT_LOPROC:
                    tmp = "DT_LOPROC";
                    break;

                case DT_HIPROC:
                    tmp = "DT_HIPROC";
                    break;

                case DT_PROCNUM:
                    tmp = "DT_LOPROC";
                    break;

                /* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
                 * Dyn.d_un.d_val field of the Elf*_Dyn structure.  This follows Sun's
                 * approach. */

                case DT_VALRNGLO:
                    tmp = "DT_VALRNGLO";
                    break;

                case DT_GNU_PRELINKED:
                    tmp = "DT_GNU_PRELINKED";
                    break;

                case DT_GNU_CONFLICTSZ:
                    tmp = "DT_GNU_CONFLICTSZ";
                    break;

                case DT_GNU_LIBLISTSZ:
                    tmp = "DT_GNU_LIBLISTSZ";
                    break;

                case DT_CHECKSUM:
                    tmp = "DT_CHECKSUM";
                    break;

                case DT_PLTPADSZ:
                    tmp = "DT_PLTPADSZ";
                    break;

                case DT_MOVEENT:
                    tmp = "DT_MOVEENT";
                    break;

                case DT_MOVESZ:
                    tmp = "DT_MOVESZ";
                    break;

                case DT_FEATURE_1:
                    tmp = "DT_FEATURE_1";
                    break;

                case DT_POSFLAG_1:
                    tmp = "DT_POSFLAG_1";
                    break;

                case DT_SYMINSZ:
                    tmp = "DT_SYMINSZ";
                    break;

                case DT_SYMINENT:
                    tmp = "DT_SYMINENT";
                    break;

                /* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
                 * Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
                 * If any adjustment is made to the ELF object after it has been
                 * built these entries will need to be adjusted.  */
                case DT_ADDRRNGLO:
                    tmp = "DT_ADDRRNGLO";
                    break;

                case DT_GNU_HASH:
                    tmp = "DT_GNU_HASH";
                    break;

                case DT_TLSDESC_PLT:
                    tmp = "DT_TLSDESC_PLT";
                    break;

                case DT_TLSDESC_GOT:
                    tmp = "DT_TLSDESC_GOT";
                    break;

                case DT_GNU_CONFLICT:
                    tmp = "DT_GNU_CONFLICT";
                    break;

                case DT_GNU_LIBLIST:
                    tmp = "DT_GNU_LIBLIST";
                    break;

                case DT_CONFIG:
                    tmp = "DT_CONFIG";
                    break;

                case DT_DEPAUDIT:
                    tmp = "DT_DEPAUDIT";
                    break;

                case DT_AUDIT:
                    tmp = "DT_AUDIT";
                    break;

                case DT_PLTPAD:
                    tmp = "DT_PLTPAD";
                    break;

                case DT_MOVETAB:
                    tmp = "DT_MOVETAB";
                    break;

                case DT_SYMINFO:
                    tmp = "DT_SYMINFO";
                    break;

                /* The versioning entry types.  The next are defined as part of the
                 * GNU extension.  */
                case DT_VERSYM:
                    tmp = "DT_VERSYM";
                    break;

                case DT_RELACOUNT:
                    tmp = "DT_RELACOUNT";
                    break;

                case DT_RELCOUNT:
                    tmp = "DT_RELCOUNT";
                    break;

                /* These were chosen by Sun.  */
                case DT_FLAGS_1:
                    tmp = "DT_FLAGS_1";
                    switch (dyn[i].d_un.d_val) {
                        case DF_1_PIE:
                            snprintf(value, 50, "Flags: %s", "PIE");
                            break;

                        default:
                            snprintf(value, 50, "Flags: %d", dyn[i].d_un.d_val);
                            break;
                    }

                    break;

                case DT_VERDEF:
                    tmp = "DT_VERDEF";
                    break;

                case DT_VERDEFNUM:
                    tmp = "DT_VERDEFNUM";
                    break;

                case DT_VERNEED:
                    tmp = "DT_VERNEED";
                    break;

                case DT_VERNEEDNUM:
                    tmp = "DT_VERNEEDNUM";
                    break;

                default:
                    break;
            }
            PRINT_DYN(dyn[i].d_tag, tmp, value);
        }
    }

    /* 64bit */
    if (MODE == ELFCLASS64) {
        /* ELF Header Information */
        Elf64_Ehdr *ehdr;
        ehdr = (Elf64_Ehdr *)elf_map;

        INFO("ELF Header\n");
        switch (ehdr->e_type) {
            case ET_NONE:
                tmp = "An unknown type";
                break;

            case ET_REL:
                tmp = "A relocatable file";
                break;

            case ET_EXEC:
                tmp = "An executable file";
                break;

            case ET_DYN:
                tmp = "A shared object";
                break;

            case ET_CORE:
                tmp = "A core file";
                break;

            default:
                tmp = "An unknown type";
                break;
        }
        PRINT_HEADER_EXP("e_type:", ehdr->e_type, tmp);

        switch (ehdr->e_type) {
            case EM_NONE:
                tmp = "An unknown machine";
                break;

            case EM_M32:
                tmp = "AT&T WE 32100";
                break;

            case EM_SPARC:
                tmp = "Sun Microsystems SPARC";
                break;

            case EM_386:
                tmp = "Intel 80386";
                break;

            case EM_68K:
                tmp = "Motorola 68000";
                break;

            case EM_88K:
                tmp = "Motorola 88000";
                break;

            case EM_860:
                tmp = "Intel 80860";
                break;

            case EM_MIPS:
                tmp = "MIPS RS3000 (big-endian only)";
                break;

            case EM_PARISC:
                tmp = "HP/PA";
                break;

            case EM_SPARC32PLUS:
                tmp = "SPARC with enhanced instruction set";
                break;

            case EM_PPC:
                tmp = "PowerPC";
                break;

            case EM_PPC64:
                tmp = "PowerPC 64-bit";
                break;

            case EM_S390:
                tmp = "IBM S/390";
                break;

            case EM_ARM:
                tmp = "Advanced RISC Machines";
                break;

            case EM_SH:
                tmp = "Renesas SuperH";
                break;

            case EM_SPARCV9:
                tmp = "SPARC v9 64-bit";
                break;

            case EM_IA_64:
                tmp = "Intel Itanium";
                break;

            case EM_X86_64:
                tmp = "AMD x86-64";
                break;

            case EM_VAX:
                tmp = "DEC Vax";
                break;

            default:
                tmp = "An unknown machine";
                break;
        }
        PRINT_HEADER_EXP("e_machine:", ehdr->e_machine, tmp);

        switch (ehdr->e_version) {
            case EV_NONE:
                tmp = "Invalid version";
                break;

            case EV_CURRENT:
                tmp = "Current version";
                break;

            default:
                tmp = "Known version";
                break;
        }
        PRINT_HEADER_EXP("e_version:", ehdr->e_version, tmp);
        PRINT_HEADER("e_entry:", ehdr->e_entry);
        PRINT_HEADER("e_phoff:", ehdr->e_phoff);
        PRINT_HEADER("e_shoff:", ehdr->e_shoff);
        PRINT_HEADER("e_flags:", ehdr->e_flags);
        PRINT_HEADER("e_ehsize:", ehdr->e_ehsize);
        PRINT_HEADER("e_phentsize:", ehdr->e_phentsize);
        PRINT_HEADER("e_phnum:", ehdr->e_phnum);
        PRINT_HEADER("e_shentsize:", ehdr->e_shentsize);
        PRINT_HEADER("e_shentsize:", ehdr->e_shentsize);
        PRINT_HEADER("e_shstrndx:", ehdr->e_shstrndx);

        /* Section Information */
        Elf64_Shdr *shdr;
        Elf64_Phdr *phdr;
        Elf64_Shdr shstrtab;

        shdr = (Elf64_Shdr *)&elf_map[ehdr->e_shoff];
        phdr = (Elf64_Phdr *)&elf_map[ehdr->e_phoff];
        shstrtab = shdr[ehdr->e_shstrndx];

        INFO("Section Header Table\n");
        PRINT_SECTION_TITLE("Nr", "Name", "Type", "Addr", "Off", "Size", "Es", "Flg", "Lk", "Inf", "Al");
        for (int i = 0; i < ehdr->e_shnum; i++) {
            name = elf_map + shstrtab.sh_offset + shdr[i].sh_name;

            switch (shdr[i].sh_type) {
                case SHT_NULL:
                    tmp = "SHT_NULL";
                    break;

                case SHT_PROGBITS:
                    tmp = "SHT_PROGBITS";
                    break;

                case SHT_SYMTAB:
                    tmp = "SHT_SYMTAB";
                    break;

                case SHT_STRTAB:
                    tmp = "SHT_STRTAB";
                    break;

                case SHT_RELA:
                    tmp = "SHT_RELA";
                    break;

                case SHT_HASH:
                    tmp = "SHT_HASH";
                    break;

                case SHT_DYNAMIC:
                    tmp = "SHT_DYNAMIC";
                    break;

                case SHT_NOTE:
                    tmp = "SHT_NOTE";
                    break;

                case SHT_NOBITS:
                    tmp = "SHT_NOBITS";
                    break;

                case SHT_REL:
                    tmp = "SHT_REL";
                    break;

                case SHT_SHLIB:
                    tmp = "SHT_SHLIB";
                    break;

                case SHT_DYNSYM:
                    tmp = "SHT_DYNSYM";
                    break;

                case SHT_LOPROC:
                    tmp = "SHT_LOPROC";
                    break;

                case SHT_HIPROC:
                    tmp = "SHT_HIPROC";
                    break;

                case SHT_LOUSER:
                    tmp = "SHT_LOUSER";
                    break;

                case SHT_HIUSER:
                    tmp = "SHT_HIUSER";
                    break;

                default:
                    break;
            }

            if (strlen(name) > 15) {
                strcpy(&name[15 - 6], "[...]");
            }
            strcpy(flag, "   ");
            flag2str_sh(shdr[i].sh_flags, flag);
            PRINT_SECTION(i, name, tmp, shdr[i].sh_addr, shdr[i].sh_offset, shdr[i].sh_size, shdr[i].sh_entsize, \
                            flag, shdr[i].sh_link, shdr[i].sh_info, shdr[i].sh_addralign);
        }

        INFO("Program Header Table\n");
        PRINT_PROGRAM_TITLE("Nr", "Type", "Offset", "Virtaddr", "Physaddr", "Filesiz", "Memsiz", "Flg", "Align");
        for (int i = 0; i < ehdr->e_phnum; i++) {
            switch (phdr[i].p_type) {
                case PT_NULL:
                    tmp = "PT_NULL";
                    break;

                case PT_LOAD:
                    tmp = "PT_LOAD";
                    break;

                case PT_DYNAMIC:
                    tmp = "PT_DYNAMIC";
                    break;

                case PT_INTERP:
                    tmp = "PT_INTERP";
                    break;

                case PT_NOTE:
                    tmp = "PT_NOTE";
                    break;

                case PT_SHLIB:
                    tmp = "PT_SHLIB";
                    break;

                case PT_PHDR:
                    tmp = "PT_PHDR";
                    break;

                case PT_LOPROC:
                    tmp = "PT_LOPROC";
                    break;

                case PT_HIPROC:
                    tmp = "PT_HIPROC";
                    break;

                case PT_GNU_STACK:
                    tmp = "PT_GNU_STACK";
                    break;

                default:
                    break;
            }
            strcpy(flag, "   ");
            flag2str(phdr[i].p_flags, flag);
            PRINT_PROGRAM(i, tmp, phdr[i].p_offset, phdr[i].p_vaddr, phdr[i].p_paddr, phdr[i].p_filesz, phdr[i].p_memsz, flag, phdr[i].p_align);
        }

        INFO("Section to segment mapping\n");
        for (int i = 0; i < ehdr->e_phnum; i++) {
            printf("     [%2d]", i);
            for (int j = 0; j < ehdr->e_shnum; j++) {
                name = elf_map + shstrtab.sh_offset + shdr[j].sh_name;
                if (shdr[j].sh_addr >= phdr[i].p_vaddr && shdr[j].sh_addr + shdr[j].sh_size <= phdr[i].p_vaddr + phdr[i].p_memsz && shdr[j].sh_type != SHT_NULL) {
                    if (shdr[j].sh_flags >> 1 & 0x1) {
                        printf(" %s", name);
                    }
                }
            }
            printf("\n");
        }

        INFO("Dynamic link information\n");
        int dynstr;
        int dynamic;
        Elf64_Dyn *dyn;
        for (int i = 0; i < ehdr->e_shnum; i++) {
            name = elf_map + shstrtab.sh_offset + shdr[i].sh_name;
            if (!strcmp(name, ".dynstr")) {
                dynstr = i;
            }
            if (!strcmp(name, ".dynamic")) {
                dynamic = i;
            }
        }

        char value[50];
        name = "";
        dyn = (Elf64_Dyn *)&elf_map[shdr[dynamic].sh_offset];
        count = shdr[dynamic].sh_size / sizeof(Elf64_Dyn);
        INFO("Dynamic section at offset 0x%x contains %d entries\n", shdr[dynamic].sh_offset, count);
        PRINT_DYN_TITLE("Tag", "Type", "Name/Value");

        for(int i = 0; i < count; i++) {
            tmp = "";
            memset(value, 0, 50);
            snprintf(value, 50, "0x%x", dyn[i].d_un.d_val);
            switch (dyn[i].d_tag) {
                /* Legal values for d_tag (dynamic entry type).  */
                case DT_NULL:
                    tmp = "DT_NULL";
                    break;

                case DT_NEEDED:
                    tmp = "DT_NEEDED";
                    name = elf_map + shdr[dynstr].sh_offset + dyn[i].d_un.d_val;
                    snprintf(value, 50, "Shared library: [%s]", name);
                    break;

                case DT_PLTRELSZ:
                    tmp = "DT_PLTRELSZ";
                    break;

                case DT_PLTGOT:
                    tmp = "DT_PLTGOT";
                    break;

                case DT_HASH:
                    tmp = "DT_HASH";
                    break;

                case DT_STRTAB:
                    tmp = "DT_STRTAB";
                    break;

                case DT_SYMTAB:
                    tmp = "DT_SYMTAB";
                    break;

                case DT_RELA:
                    tmp = "DT_RELA";
                    break;

                case DT_RELASZ:
                    tmp = "DT_RELASZ";
                    break;

                case DT_RELAENT:
                    tmp = "DT_RELAENT";
                    break;

                case DT_STRSZ:
                    tmp = "DT_STRSZ";
                    break;

                case DT_SYMENT:
                    tmp = "DT_SYMENT";
                    break;

                case DT_INIT:
                    tmp = "DT_INIT";
                    break;

                case DT_FINI:
                    tmp = "DT_FINI";
                    break;

                case DT_SONAME:
                    tmp = "DT_SONAME";
                    break;

                case DT_RPATH:
                    tmp = "DT_RPATH";
                    break;

                case DT_SYMBOLIC:
                    tmp = "DT_SYMBOLIC";
                    break;

                case DT_REL:
                    tmp = "DT_REL";
                    break;

                case DT_RELSZ:
                    tmp = "DT_RELSZ";
                    break;

                case DT_RELENT:
                    tmp = "DT_RELENT";
                    break;

                case DT_PLTREL:
                    tmp = "DT_PLTREL";
                    break;

                case DT_DEBUG:
                    tmp = "DT_DEBUG";
                    break;

                case DT_TEXTREL:
                    tmp = "DT_TEXTREL";
                    break;

                case DT_JMPREL:
                    tmp = "DT_JMPREL";
                    break;

                case DT_BIND_NOW:
                    tmp = "DT_BIND_NOW";
                    break;

                case DT_INIT_ARRAY:
                    tmp = "DT_INIT_ARRAY";
                    break;

                case DT_FINI_ARRAY:
                    tmp = "DT_FINI_ARRAY";
                    break;

                case DT_INIT_ARRAYSZ:
                    tmp = "DT_INIT_ARRAYSZ";
                    break;

                case DT_FINI_ARRAYSZ:
                    tmp = "DT_FINI_ARRAYSZ";
                    break;

                case DT_RUNPATH:
                    tmp = "DT_RUNPATH";
                    break;

                case DT_FLAGS:
                    tmp = "DT_FLAGS";
                    snprintf(value, 50, "Flags: %d", dyn[i].d_un.d_val);
                    break;

                case DT_ENCODING:
                    tmp = "DT_ENCODING";
                    break;

                case DT_PREINIT_ARRAYSZ:
                    tmp = "DT_PREINIT_ARRAYSZ";
                    break;

                case DT_SYMTAB_SHNDX:
                    tmp = "DT_SYMTAB_SHNDX";
                    break;

                case DT_NUM:
                    tmp = "DT_NUM";
                    break;

                case DT_LOOS:
                    tmp = "DT_LOOS";
                    break;

                case DT_HIOS:
                    tmp = "DT_HIOS";
                    break;

                case DT_LOPROC:
                    tmp = "DT_LOPROC";
                    break;

                case DT_HIPROC:
                    tmp = "DT_HIPROC";
                    break;

                case DT_PROCNUM:
                    tmp = "DT_LOPROC";
                    break;

                /* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
                 * Dyn.d_un.d_val field of the Elf*_Dyn structure.  This follows Sun's
                 * approach. */

                case DT_VALRNGLO:
                    tmp = "DT_VALRNGLO";
                    break;

                case DT_GNU_PRELINKED:
                    tmp = "DT_GNU_PRELINKED";
                    break;

                case DT_GNU_CONFLICTSZ:
                    tmp = "DT_GNU_CONFLICTSZ";
                    break;

                case DT_GNU_LIBLISTSZ:
                    tmp = "DT_GNU_LIBLISTSZ";
                    break;

                case DT_CHECKSUM:
                    tmp = "DT_CHECKSUM";
                    break;

                case DT_PLTPADSZ:
                    tmp = "DT_PLTPADSZ";
                    break;

                case DT_MOVEENT:
                    tmp = "DT_MOVEENT";
                    break;

                case DT_MOVESZ:
                    tmp = "DT_MOVESZ";
                    break;

                case DT_FEATURE_1:
                    tmp = "DT_FEATURE_1";
                    break;

                case DT_POSFLAG_1:
                    tmp = "DT_POSFLAG_1";
                    break;

                case DT_SYMINSZ:
                    tmp = "DT_SYMINSZ";
                    break;

                case DT_SYMINENT:
                    tmp = "DT_SYMINENT";
                    break;

                /* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
                 * Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
                 * If any adjustment is made to the ELF object after it has been
                 * built these entries will need to be adjusted.  */
                case DT_ADDRRNGLO:
                    tmp = "DT_ADDRRNGLO";
                    break;

                case DT_GNU_HASH:
                    tmp = "DT_GNU_HASH";
                    break;

                case DT_TLSDESC_PLT:
                    tmp = "DT_TLSDESC_PLT";
                    break;

                case DT_TLSDESC_GOT:
                    tmp = "DT_TLSDESC_GOT";
                    break;

                case DT_GNU_CONFLICT:
                    tmp = "DT_GNU_CONFLICT";
                    break;

                case DT_GNU_LIBLIST:
                    tmp = "DT_GNU_LIBLIST";
                    break;

                case DT_CONFIG:
                    tmp = "DT_CONFIG";
                    break;

                case DT_DEPAUDIT:
                    tmp = "DT_DEPAUDIT";
                    break;

                case DT_AUDIT:
                    tmp = "DT_AUDIT";
                    break;

                case DT_PLTPAD:
                    tmp = "DT_PLTPAD";
                    break;

                case DT_MOVETAB:
                    tmp = "DT_MOVETAB";
                    break;

                case DT_SYMINFO:
                    tmp = "DT_SYMINFO";
                    break;

                /* The versioning entry types.  The next are defined as part of the
                 * GNU extension.  */
                case DT_VERSYM:
                    tmp = "DT_VERSYM";
                    break;

                case DT_RELACOUNT:
                    tmp = "DT_RELACOUNT";
                    break;

                case DT_RELCOUNT:
                    tmp = "DT_RELCOUNT";
                    break;

                /* These were chosen by Sun.  */
                case DT_FLAGS_1:
                    tmp = "DT_FLAGS_1";
                    switch (dyn[i].d_un.d_val) {
                        case DF_1_PIE:
                            snprintf(value, 50, "Flags: %s", "PIE");
                            break;

                        default:
                            snprintf(value, 50, "Flags: %d", dyn[i].d_un.d_val);
                            break;
                    }

                    break;

                case DT_VERDEF:
                    tmp = "DT_VERDEF";
                    break;

                case DT_VERDEFNUM:
                    tmp = "DT_VERDEFNUM";
                    break;

                case DT_VERNEED:
                    tmp = "DT_VERNEED";
                    break;

                case DT_VERNEEDNUM:
                    tmp = "DT_VERNEEDNUM";
                    break;

                default:
                    break;
            }
            PRINT_DYN(dyn[i].d_tag, tmp, value);
        }
    }

    return 0;
}


MagickExport void DestroyImageProperties(Image *image)
{
  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  if (image->properties != (void *) NULL)
    image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
      image->properties);
}


MagickExport MagickBooleanType SetImageProperty(Image *image,
  const char *property,const char *value)
{
  ExceptionInfo
    *exception;

  MagickBooleanType
    status;

  MagickStatusType
    flags;

  size_t
    property_length;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image->filename);
  if (image->properties == (void *) NULL)
    image->properties=NewSplayTree(CompareSplayTreeString,
      RelinquishMagickMemory,RelinquishMagickMemory);  /* create splay-tree */
  if (value == (const char *) NULL)
    return(DeleteImageProperty(image,property));  /* delete if NULL */
  exception=(&image->exception);
  property_length=strlen(property);
  if ((property_length > 2) && (*(property+(property_length-2)) == ':') &&
      (*(property+(property_length-1)) == '*'))
    {
      (void) ThrowMagickException(exception,GetMagickModule(),
        OptionWarning,"SetReadOnlyProperty","`%s'",property);
      return(MagickFalse);
    }
  /*
    FUTURE: These should produce 'illegal settings'
     * binary chars in p[roperty key
     * first letter must be a alphabetic
     * single letter property keys (read only)
     * known special prefix (read only, they don't get saved!)
  */
  status=MagickTrue;
  switch (*property)
  {
    case 'B':
    case 'b':
    {
      if (LocaleCompare("background",property) == 0)
        {
          (void) QueryColorDatabase(value,&image->background_color,exception);
          break;
        }
      if (LocaleCompare("bias",property) == 0)
        {
          image->bias=StringToDoubleInterval(value,(double) QuantumRange+1.0);
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    case 'C':
    case 'c':
    {
      if (LocaleCompare("colorspace",property) == 0)
        {
          ssize_t
            colorspace;

          colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
            value);
          if (colorspace < 0)
            break;
          status=SetImageColorspace(image,(ColorspaceType) colorspace);
          break;
        }
      if (LocaleCompare("compose",property) == 0)
        {
          ssize_t
            compose;

          compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
          if (compose < 0)
            break;
          image->compose=(CompositeOperator) compose;
          break;
        }
      if (LocaleCompare("compress",property) == 0)
        {
          ssize_t
            compression;

          compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
            value);
          if (compression < 0)
            break;
          image->compression=(CompressionType) compression;
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    case 'D':
    case 'd':
    {
      if (LocaleCompare("delay",property) == 0)
        {
          GeometryInfo
            geometry_info;

          flags=ParseGeometry(value,&geometry_info);
          if ((flags & GreaterValue) != 0)
            {
              if (image->delay > (size_t) floor(geometry_info.rho+0.5))
                image->delay=(size_t) floor(geometry_info.rho+0.5);
            }
          else
            if ((flags & LessValue) != 0)
              {
                if ((double) image->delay < floor(geometry_info.rho+0.5))
                  image->ticks_per_second=CastDoubleToLong(
                    floor(geometry_info.sigma+0.5));
              }
            else
              image->delay=(size_t) floor(geometry_info.rho+0.5);
          if ((flags & SigmaValue) != 0)
            image->ticks_per_second=CastDoubleToLong(floor(
              geometry_info.sigma+0.5));
          break;
        }
      if (LocaleCompare("density",property) == 0)
        {
          GeometryInfo
            geometry_info;

          flags=ParseGeometry(value,&geometry_info);
          if ((flags & RhoValue) != 0)
            image->x_resolution=geometry_info.rho;
          image->y_resolution=image->x_resolution;
          if ((flags & SigmaValue) != 0)
            image->y_resolution=geometry_info.sigma;
        }
      if (LocaleCompare("depth",property) == 0)
        {
          image->depth=StringToUnsignedLong(value);
          break;
        }
      if (LocaleCompare("dispose",property) == 0)
        {
          ssize_t
            dispose;

          dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
          if (dispose < 0)
            break;
          image->dispose=(DisposeType) dispose;
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    case 'G':
    case 'g':
    {
      if (LocaleCompare("gamma",property) == 0)
        {
          image->gamma=StringToDouble(value,(char **) NULL);
          break;
        }
      if (LocaleCompare("gravity",property) == 0)
        {
          ssize_t
            gravity;

          gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
          if (gravity < 0)
            break;
          image->gravity=(GravityType) gravity;
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    case 'I':
    case 'i':
    {
      if (LocaleCompare("intensity",property) == 0)
        {
          ssize_t
            intensity;

          intensity=ParseCommandOption(MagickPixelIntensityOptions,MagickFalse,
            value);
          if (intensity < 0)
            break;
          image->intensity=(PixelIntensityMethod) intensity;
          break;
        }
      if (LocaleCompare("interpolate",property) == 0)
        {
          ssize_t
            interpolate;

          interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
            value);
          if (interpolate < 0)
            break;
          image->interpolate=(InterpolatePixelMethod) interpolate;
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    case 'L':
    case 'l':
    {
      if (LocaleCompare("loop",property) == 0)
        {
          image->iterations=StringToUnsignedLong(value);
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    case 'P':
    case 'p':
    {
      if (LocaleCompare("page",property) == 0)
        {
          char
            *geometry;

          geometry=GetPageGeometry(value);
          flags=ParseAbsoluteGeometry(geometry,&image->page);
          geometry=DestroyString(geometry);
          break;
        }
      if (LocaleCompare("profile",property) == 0)
        {
          ImageInfo
            *image_info;

          StringInfo
            *profile;

          image_info=AcquireImageInfo();
          (void) CopyMagickString(image_info->filename,value,MaxTextExtent);
          (void) SetImageInfo(image_info,1,exception);
          profile=FileToStringInfo(image_info->filename,~0UL,exception);
          if (profile != (StringInfo *) NULL)
            {
              status=SetImageProfile(image,image_info->magick,profile);
              profile=DestroyStringInfo(profile);
            }
          image_info=DestroyImageInfo(image_info);
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    case 'R':
    case 'r':
    {
      if (LocaleCompare("rendering-intent",property) == 0)
        {
          ssize_t
            rendering_intent;

          rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
            value);
          if (rendering_intent < 0)
            break;
          image->rendering_intent=(RenderingIntent) rendering_intent;
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    case 'T':
    case 't':
    {
      if (LocaleCompare("tile-offset",property) == 0)
        {
          char
            *geometry;

          geometry=GetPageGeometry(value);
          flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
          geometry=DestroyString(geometry);
          break;
        }
     if (LocaleCompare("type",property) == 0)
        {
          ssize_t
            type;

          type=ParseCommandOption(MagickTypeOptions,MagickFalse,value);
          if (type < 0)
            return(MagickFalse);
          image->type=(ImageType) type;
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    case 'U':
    case 'u':
    {
      if (LocaleCompare("units",property) == 0)
        {
          ssize_t
            units;

          units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
          if (units < 0)
            break;
          image->units=(ResolutionType) units;
          break;
        }
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
    default:
    {
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
        ConstantString(property),ConstantString(value));
      break;
    }
  }
  return(status);
}


static inline signed int ReadPropertySignedLong(const EndianType endian,
  const unsigned char *buffer)
{
  union
  {
    unsigned int
      unsigned_value;

    signed int
      signed_value;
  } quantum;

  unsigned int
    value;

  if (endian == LSBEndian)
    {
      value=(unsigned int) buffer[3] << 24;
      value|=(unsigned int) buffer[2] << 16;
      value|=(unsigned int) buffer[1] << 8;
      value|=(unsigned int) buffer[0];
      quantum.unsigned_value=value & 0xffffffff;
      return(quantum.signed_value);
    }
  value=(unsigned int) buffer[0] << 24;
  value|=(unsigned int) buffer[1] << 16;
  value|=(unsigned int) buffer[2] << 8;
  value|=(unsigned int) buffer[3];
  quantum.unsigned_value=value & 0xffffffff;
  return(quantum.signed_value);
}


MagickExport MagickBooleanType CloneImageProperties(Image *image,
  const Image *clone_image)
{
  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  assert(clone_image != (const Image *) NULL);
  assert(clone_image->signature == MagickCoreSignature);
  if (clone_image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      clone_image->filename);
  (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
  (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
    MaxTextExtent);
  image->compression=clone_image->compression;
  image->quality=clone_image->quality;
  image->depth=clone_image->depth;
  image->background_color=clone_image->background_color;
  image->border_color=clone_image->border_color;
  image->matte_color=clone_image->matte_color;
  image->transparent_color=clone_image->transparent_color;
  image->gamma=clone_image->gamma;
  image->chromaticity=clone_image->chromaticity;
  image->rendering_intent=clone_image->rendering_intent;
  image->black_point_compensation=clone_image->black_point_compensation;
  image->units=clone_image->units;
  image->montage=(char *) NULL;
  image->directory=(char *) NULL;
  (void) CloneString(&image->geometry,clone_image->geometry);
  image->offset=clone_image->offset;
  image->x_resolution=clone_image->x_resolution;
  image->y_resolution=clone_image->y_resolution;
  image->page=clone_image->page;
  image->tile_offset=clone_image->tile_offset;
  image->extract_info=clone_image->extract_info;
  image->bias=clone_image->bias;
  image->filter=clone_image->filter;
  image->blur=clone_image->blur;
  image->fuzz=clone_image->fuzz;
  image->intensity=clone_image->intensity;
  image->interlace=clone_image->interlace;
  image->interpolate=clone_image->interpolate;
  image->endian=clone_image->endian;
  image->gravity=clone_image->gravity;
  image->compose=clone_image->compose;
  image->orientation=clone_image->orientation;
  image->scene=clone_image->scene;
  image->dispose=clone_image->dispose;
  image->delay=clone_image->delay;
  image->ticks_per_second=clone_image->ticks_per_second;
  image->iterations=clone_image->iterations;
  image->total_colors=clone_image->total_colors;
  image->taint=clone_image->taint;
  image->progress_monitor=clone_image->progress_monitor;
  image->client_data=clone_image->client_data;
  image->start_loop=clone_image->start_loop;
  image->error=clone_image->error;
  image->signature=clone_image->signature;
  if (clone_image->properties != (void *) NULL)
    {
      if (image->properties != (void *) NULL)
        DestroyImageProperties(image);
      image->properties=CloneSplayTree((SplayTreeInfo *)
        clone_image->properties,(void *(*)(void *)) ConstantString,
        (void *(*)(void *)) ConstantString);
    }
  return(MagickTrue);
}


static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
{
  char
    *attribute,
    format[MaxTextExtent],
    name[MaxTextExtent],
    *resource;

  const StringInfo
    *profile;

  const unsigned char
    *info;

  long
    start,
    stop;

  MagickBooleanType
    status;

  ssize_t
    i;

  size_t
    length;

  ssize_t
    count,
    id,
    sub_number;

  /*
    There are no newlines in path names, so it's safe as terminator.
  */
  profile=GetImageProfile(image,"8bim");
  if (profile == (StringInfo *) NULL)
    return(MagickFalse);
  count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%1024[^\n]\n%1024[^\n]",&start,&stop,
    name,format);
  if ((count != 2) && (count != 3) && (count != 4))
    return(MagickFalse);
  if (count < 4)
    (void) CopyMagickString(format,"SVG",MaxTextExtent);
  if (count < 3)
    *name='\0';
  sub_number=1;
  if (*name == '#')
    sub_number=(ssize_t) StringToLong(&name[1]);
  sub_number=MagickMax(sub_number,1L);
  resource=(char *) NULL;
  status=MagickFalse;
  length=GetStringInfoLength(profile);
  info=GetStringInfoDatum(profile);
  while ((length > 0) && (status == MagickFalse))
  {
    if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
      continue;
    if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
      continue;
    if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
      continue;
    if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
      continue;
    id=(ssize_t) ReadPropertyMSBShort(&info,&length);
    if (id < (ssize_t) start)
      continue;
    if (id > (ssize_t) stop)
      continue;
    if (resource != (char *) NULL)
      resource=DestroyString(resource);
    count=(ssize_t) ReadPropertyByte(&info,&length);
    if ((count != 0) && ((size_t) count <= length))
      {
        resource=(char *) NULL;
        if (~((size_t) count) >= (MaxTextExtent-1))
          resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
            sizeof(*resource));
        if (resource != (char *) NULL)
          {
            for (i=0; i < (ssize_t) count; i++)
              resource[i]=(char) ReadPropertyByte(&info,&length);
            resource[count]='\0';
          }
      }
    if ((count & 0x01) == 0)
      (void) ReadPropertyByte(&info,&length);
    count=(ssize_t) ReadPropertyMSBLong(&info,&length);
    if ((count < 0) || ((size_t) count > length))
      {
        length=0;
        continue;
      }
    if ((*name != '\0') && (*name != '#'))
      if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
        {
          /*
            No name match, scroll forward and try next.
          */
          info+=count;
          length-=MagickMin(count,(ssize_t) length);
          continue;
        }
    if ((*name == '#') && (sub_number != 1))
      {
        /*
          No numbered match, scroll forward and try next.
        */
        sub_number--;
        info+=count;
        length-=MagickMin(count,(ssize_t) length);
        continue;
      }
    /*
      We have the resource of interest.
    */
    attribute=(char *) NULL;
    if (~((size_t) count) >= (MaxTextExtent-1))
      attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
        sizeof(*attribute));
    if (attribute != (char *) NULL)
      {
        (void) memcpy(attribute,(char *) info,(size_t) count);
        attribute[count]='\0';
        info+=count;
        length-=MagickMin(count,(ssize_t) length);
        if ((id <= 1999) || (id >= 2999))
          (void) SetImageProperty((Image *) image,key,(const char *) attribute);
        else
          {
            char
              *path;

            if (LocaleCompare(format,"svg") == 0)
              path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
                image->columns,image->rows);
            else
              path=TracePSClippath((unsigned char *) attribute,(size_t) count,
                image->columns,image->rows);
            (void) SetImageProperty((Image *) image,key,(const char *) path);
            path=DestroyString(path);
          }
        attribute=DestroyString(attribute);
        status=MagickTrue;
      }
  }
  if (resource != (char *) NULL)
    resource=DestroyString(resource);
  return(status);
}


static inline unsigned short ReadPropertyUnsignedShort(const EndianType endian,
  const unsigned char *buffer)
{
  unsigned short
    value;

  if (endian == LSBEndian)
    {
      value=(unsigned short) buffer[1] << 8;
      value|=(unsigned short) buffer[0];
      return(value & 0xffff);
    }
  value=(unsigned short) buffer[0] << 8;
  value|=(unsigned short) buffer[1];
  return(value & 0xffff);
}


MagickExport MagickBooleanType FormatImageProperty(Image *image,
  const char *property,const char *format,...)
{
  char
    value[MaxTextExtent];

  ssize_t
    n;

  va_list
    operands;

  va_start(operands,format);
  n=FormatLocaleStringList(value,MaxTextExtent,format,operands);
  (void) n;
  va_end(operands);
  return(SetImageProperty(image,property,value));
}


MagickExport MagickBooleanType DeleteImageProperty(Image *image,
  const char *property)
{
  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  if (image->properties == (void *) NULL)
    return(MagickFalse);
  return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
}


static MagickBooleanType GetIPTCProperty(const Image *image,const char *key)
{
  char
    *attribute,
    *message;

  const StringInfo
    *profile;

  long
    count,
    dataset,
    record;

  ssize_t
    i;

  size_t
    length;

  profile=GetImageProfile(image,"iptc");
  if (profile == (StringInfo *) NULL)
    profile=GetImageProfile(image,"8bim");
  if (profile == (StringInfo *) NULL)
    return(MagickFalse);
  count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
  if (count != 2)
    return(MagickFalse);
  attribute=(char *) NULL;
  for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length)
  {
    length=1;
    if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c)
      continue;
    length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
    length|=GetStringInfoDatum(profile)[i+4];
    if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
        ((long) GetStringInfoDatum(profile)[i+2] == record))
      {
        message=(char *) NULL;
        if (~length >= 1)
          message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
        if (message != (char *) NULL)
          {
            (void) CopyMagickString(message,(char *) GetStringInfoDatum(
              profile)+i+5,length+1);
            (void) ConcatenateString(&attribute,message);
            (void) ConcatenateString(&attribute,";");
            message=DestroyString(message);
          }
      }
    i+=5;
  }
  if ((attribute == (char *) NULL) || (*attribute == ';'))
    {
      if (attribute != (char *) NULL)
        attribute=DestroyString(attribute);
      return(MagickFalse);
    }
  attribute[strlen(attribute)-1]='\0';
  (void) SetImageProperty((Image *) image,key,(const char *) attribute);
  attribute=DestroyString(attribute);
  return(MagickTrue);
}


static inline void TraceBezierCurve(char *message,PointInfo *last,
  PointInfo *point)
{
  /*
    Handle special cases when Bezier curves are used to describe
    corners and straight lines.
  */
  if (((last+1)->x == (last+2)->x) && ((last+1)->y == (last+2)->y) &&
      (point->x == (point+1)->x) && (point->y == (point+1)->y))
    (void) FormatLocaleString(message,MagickPathExtent,
      "L %g %g\n",point[1].x,point[1].y);
  else
    (void) FormatLocaleString(message,MagickPathExtent,"C %g %g %g %g %g %g\n",
      (last+2)->x,(last+2)->y,point->x,point->y,(point+1)->x,(point+1)->y);
}


MagickExport const char *GetImageProperty(const Image *image,
  const char *property)
{
  double
    alpha;

  ExceptionInfo
    *exception;

  FxInfo
    *fx_info;

  MagickStatusType
    status;

  const char
    *p;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  p=(const char *) NULL;
  if (image->properties != (void *) NULL)
    {
      if (property == (const char *) NULL)
        {
          ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
          p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
            image->properties);
          return(p);
        }
      if (LocaleNCompare("fx:",property,3) != 0) /* NOT fx: !!!! */
        {
          p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
            image->properties,property);
          if (p != (const char *) NULL)
            return(p);
        }
    }
  if ((property == (const char *) NULL) ||
      (strchr(property,':') == (char *) NULL))
    return(p);
  exception=(&((Image *) image)->exception);
  switch (*property)
  {
    case '8':
    {
      if (LocaleNCompare("8bim:",property,5) == 0)
        {
          (void) Get8BIMProperty(image,property);
          break;
        }
      break;
    }
    case 'E':
    case 'e':
    {
      if (LocaleNCompare("exif:",property,5) == 0)
        {
          (void) GetEXIFProperty(image,property);
          break;
        }
      break;
    }
    case 'F':
    case 'f':
    {
      if (LocaleNCompare("fx:",property,3) == 0)
        {
          if ((image->columns == 0) || (image->rows == 0))
            break;
          fx_info=AcquireFxInfo(image,property+3);
          status=FxEvaluateChannelExpression(fx_info,DefaultChannels,0,0,&alpha,
            exception);
          fx_info=DestroyFxInfo(fx_info);
          if (status != MagickFalse)
            {
              char
                value[MaxTextExtent];

              (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
                GetMagickPrecision(),(double) alpha);
              (void) SetImageProperty((Image *) image,property,value);
            }
          break;
        }
      break;
    }
    case 'H':
    case 'h':
    {
      if (LocaleNCompare("hex:",property,4) == 0)
        {
          MagickPixelPacket
            pixel;

          if ((image->columns == 0) || (image->rows == 0))
            break;
          GetMagickPixelPacket(image,&pixel);
          fx_info=AcquireFxInfo(image,property+4);
          status=FxEvaluateChannelExpression(fx_info,RedChannel,0,0,&alpha,
            exception);
          pixel.red=(MagickRealType) QuantumRange*alpha;
          status&=FxEvaluateChannelExpression(fx_info,GreenChannel,0,0,&alpha,
            exception);
          pixel.green=(MagickRealType) QuantumRange*alpha;
          status&=FxEvaluateChannelExpression(fx_info,BlueChannel,0,0,&alpha,
            exception);
          pixel.blue=(MagickRealType) QuantumRange*alpha;
          status&=FxEvaluateChannelExpression(fx_info,OpacityChannel,0,0,&alpha,
            exception);
          pixel.opacity=(MagickRealType) QuantumRange*(1.0-alpha);
          if (image->colorspace == CMYKColorspace)
            {
              status&=FxEvaluateChannelExpression(fx_info,BlackChannel,0,0,
                &alpha,exception);
              pixel.index=(MagickRealType) QuantumRange*alpha;
            }
          fx_info=DestroyFxInfo(fx_info);
          if (status != MagickFalse)
            {
              char
                hex[MaxTextExtent];

              GetColorTuple(&pixel,MagickTrue,hex);
              (void) SetImageProperty((Image *) image,property,hex+1);
            }
          break;
        }
      break;
    }
    case 'I':
    case 'i':
    {
      if ((LocaleNCompare("icc:",property,4) == 0) ||
          (LocaleNCompare("icm:",property,4) == 0))
        {
          (void) GetICCProperty(image,property);
          break;
        }
      if (LocaleNCompare("iptc:",property,5) == 0)
        {
          (void) GetIPTCProperty(image,property);
          break;
        }
      break;
    }
    case 'P':
    case 'p':
    {
      if (LocaleNCompare("pixel:",property,6) == 0)
        {
          MagickPixelPacket
            pixel;

          GetMagickPixelPacket(image,&pixel);
          fx_info=AcquireFxInfo(image,property+6);
          status=FxEvaluateChannelExpression(fx_info,RedChannel,0,0,&alpha,
            exception);
          pixel.red=(MagickRealType) QuantumRange*alpha;
          status&=FxEvaluateChannelExpression(fx_info,GreenChannel,0,0,&alpha,
            exception);
          pixel.green=(MagickRealType) QuantumRange*alpha;
          status&=FxEvaluateChannelExpression(fx_info,BlueChannel,0,0,&alpha,
            exception);
          pixel.blue=(MagickRealType) QuantumRange*alpha;
          status&=FxEvaluateChannelExpression(fx_info,OpacityChannel,0,0,&alpha,
            exception);
          pixel.opacity=(MagickRealType) QuantumRange*(1.0-alpha);
          if (image->colorspace == CMYKColorspace)
            {
              status&=FxEvaluateChannelExpression(fx_info,BlackChannel,0,0,
                &alpha,exception);
              pixel.index=(MagickRealType) QuantumRange*alpha;
            }
          fx_info=DestroyFxInfo(fx_info);
          if (status != MagickFalse)
            {
              char
                name[MaxTextExtent];

              const char
                *value;

              GetColorTuple(&pixel,MagickFalse,name);
              value=GetImageArtifact(image,"pixel:compliance");
              if (value != (char *) NULL)
                {
                  ComplianceType compliance=(ComplianceType) ParseCommandOption(
                    MagickComplianceOptions,MagickFalse,value);
                  (void) QueryMagickColorname(image,&pixel,compliance,name,
                    exception);
                }
              (void) SetImageProperty((Image *) image,property,name);
            }
          break;
        }
      break;
    }
    case 'X':
    case 'x':
    {
      if (LocaleNCompare("xmp:",property,4) == 0)
        {
          (void) GetXMPProperty(image,property);
          break;
        }
      break;
    }
    default:
      break;
  }
  if (image->properties != (void *) NULL)
    {
      p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
        image->properties,property);
      return(p);
    }
  return((const char *) NULL);
}


static inline signed int ReadPropertyMSBLong(const unsigned char **p,
  size_t *length)
{
  union
  {
    unsigned int
      unsigned_value;

    signed int
      signed_value;
  } quantum;

  int
    c;

  ssize_t
    i;

  unsigned char
    buffer[4];

  unsigned int
    value;

  if (*length < 4)
    return(-1);
  for (i=0; i < 4; i++)
  {
    c=(int) (*(*p)++);
    (*length)--;
    buffer[i]=(unsigned char) c;
  }
  value=(unsigned int) buffer[0] << 24;
  value|=(unsigned int) buffer[1] << 16;
  value|=(unsigned int) buffer[2] << 8;
  value|=(unsigned int) buffer[3];
  quantum.unsigned_value=value & 0xffffffff;
  return(quantum.signed_value);
}


static MagickBooleanType GetICCProperty(const Image *image,const char *property)
{
  const StringInfo
    *profile;

  /*
    Return ICC profile property.
  */
  magick_unreferenced(property);
  profile=GetImageProfile(image,"icc");
  if (profile == (StringInfo *) NULL)
    profile=GetImageProfile(image,"icm");
  if (profile == (StringInfo *) NULL)
    return(MagickFalse);
  if (GetStringInfoLength(profile) < 128)
    return(MagickFalse);  /* minimum ICC profile length */
#if defined(MAGICKCORE_LCMS_DELEGATE)
  {
    cmsHPROFILE
      icc_profile;

    icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
      (cmsUInt32Number) GetStringInfoLength(profile));
    if (icc_profile != (cmsHPROFILE *) NULL)
      {
#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
        const char
          *name;

        name=cmsTakeProductName(icc_profile);
        if (name != (const char *) NULL)
          (void) SetImageProperty((Image *) image,"icc:name",name);
#else
        StringInfo
          *info;

        unsigned int
          extent;

        info=AcquireStringInfo(0);
        extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,"en","US",
          NULL,0);
        if (extent != 0)
          {
            SetStringInfoLength(info,extent+1);
            extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,"en",
              "US",(char *) GetStringInfoDatum(info),extent);
            if (extent != 0)
              (void) SetImageProperty((Image *) image,"icc:description",
                (char *) GetStringInfoDatum(info));
         }
        extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,"en","US",
          NULL,0);
       if (extent != 0)
          {
            SetStringInfoLength(info,extent+1);
            extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,"en",
              "US",(char *) GetStringInfoDatum(info),extent);
            if (extent != 0)
              (void) SetImageProperty((Image *) image,"icc:manufacturer",
                (char *) GetStringInfoDatum(info));
          }
        extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en","US",
          NULL,0);
        if (extent != 0)
          {
            SetStringInfoLength(info,extent+1);
            extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en","US",
              (char *) GetStringInfoDatum(info),extent);
            if (extent != 0)
              (void) SetImageProperty((Image *) image,"icc:model",
                (char *) GetStringInfoDatum(info));
          }
        extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,"en","US",
          NULL,0);
        if (extent != 0)
          {
            SetStringInfoLength(info,extent+1);
            extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,"en",
              "US",(char *) GetStringInfoDatum(info),extent);
            if (extent != 0)
              (void) SetImageProperty((Image *) image,"icc:copyright",
                (char *) GetStringInfoDatum(info));
          }
        info=DestroyStringInfo(info);
#endif
        (void) cmsCloseProfile(icc_profile);
      }
  }
#endif
  return(MagickTrue);
}


MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
  Image *image,const char *property)
{
  char
    value[MaxTextExtent];

  const char
    *string;

  assert(property[0] != '\0');
  if (property[1] == '\0')  /* single letter property request */
    return(GetMagickPropertyLetter(image_info,image,*property));
  *value='\0';  /* formatted string */
  string=(char *) NULL;  /* constant string reference */
  switch (*property)
  {
    case 'b':
    {
      if ((LocaleCompare("base",property) == 0) ||
          (LocaleCompare("basename",property) == 0) )
        {
          GetPathComponent(image->magick_filename,BasePath,value);
          break;
        }
      if (LocaleCompare("bit-depth",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
            GetImageDepth(image,&image->exception));
          break;
        }
      if (LocaleCompare("bounding-box",property) == 0)
        {
          RectangleInfo
            geometry;

          geometry=GetImageBoundingBox(image,&image->exception);
          (void) FormatLocaleString(value,MagickPathExtent,"%g,%g %g,%g\n",
            (double) geometry.x,(double) geometry.y,
            (double) geometry.x+geometry.width,
            (double) geometry.y+geometry.height);
          break;
        }
      break;
    }
    case 'c':
    {
      if (LocaleCompare("channels",property) == 0)
        {
          /*
            Image channels.
          */
          (void) FormatLocaleString(value,MaxTextExtent,"%s",
            CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
            image->colorspace));
          LocaleLower(value);
          if (image->matte != MagickFalse)
            (void) ConcatenateMagickString(value,"a",MaxTextExtent);
          break;
        }
      if (LocaleCompare("colors",property) == 0)
        {
          image->colors=GetNumberColors(image,(FILE *) NULL,&image->exception);
          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
            image->colors);
          break;
        }
      if (LocaleCompare("colorspace",property) == 0)
        {
          string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
            image->colorspace);
          break;
        }
      if (LocaleCompare("compose",property) == 0)
        {
          string=CommandOptionToMnemonic(MagickComposeOptions,(ssize_t)
            image->compose);
          break;
        }
      if (LocaleCompare("compression",property) == 0)
        {
          string=CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
            image->compression);
          break;
        }
      if (LocaleCompare("copyright",property) == 0)
        {
          (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
          break;
        }
      break;
    }
    case 'd':
    {
      if (LocaleCompare("depth",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
            image->depth);
          break;
        }
      if (LocaleCompare("directory",property) == 0)
        {
          GetPathComponent(image->magick_filename,HeadPath,value);
          break;
        }
      break;
    }
    case 'e':
    {
      if (LocaleCompare("entropy",property) == 0)
        {
          double
            entropy;

          (void) GetImageChannelEntropy(image,image_info->channel,&entropy,
            &image->exception);
          (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
            GetMagickPrecision(),entropy);
          break;
        }
      if (LocaleCompare("extension",property) == 0)
        {
          GetPathComponent(image->magick_filename,ExtensionPath,value);
          break;
        }
      break;
    }
    case 'g':
    {
      if (LocaleCompare("gamma",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
            GetMagickPrecision(),image->gamma);
          break;
        }
      if ((image_info != (ImageInfo *) NULL) &&
          (LocaleCompare("group",property) == 0))
        {
          (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",(unsigned long)
            image_info->group);
          break;
        }
      break;
    }
    case 'h':
    {
      if (LocaleCompare("height",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
            image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
          break;
        }
      break;
    }
    case 'i':
    {
      if (LocaleCompare("input",property) == 0)
        {
          string=image->filename;
          break;
        }
      if (LocaleCompare("interlace",property) == 0)
        {
          string=CommandOptionToMnemonic(MagickInterlaceOptions,(ssize_t)
            image->interlace);
          break;
        }
      break;
    }
    case 'k':
    {
      if (LocaleCompare("kurtosis",property) == 0)
        {
          double
            kurtosis,
            skewness;

          (void) GetImageChannelKurtosis(image,image_info->channel,&kurtosis,
            &skewness,&image->exception);
          (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
            GetMagickPrecision(),kurtosis);
          break;
        }
      break;
    }
    case 'm':
    {
      if (LocaleCompare("magick",property) == 0)
        {
          string=image->magick;
          break;
        }
      if ((LocaleCompare("max",property) == 0) ||
          (LocaleCompare("maxima",property) == 0))
        {
          double
            maximum,
            minimum;

          (void) GetImageChannelRange(image,image_info->channel,&minimum,
            &maximum,&image->exception);
          (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
            GetMagickPrecision(),maximum);
          break;
        }
      if (LocaleCompare("mean",property) == 0)
        {
          double
            mean,
            standard_deviation;

          (void) GetImageChannelMean(image,image_info->channel,&mean,
            &standard_deviation,&image->exception);
          (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
            GetMagickPrecision(),mean);
          break;
        }
      if ((LocaleCompare("min",property) == 0) ||
          (LocaleCompare("minima",property) == 0))
        {
          double
            maximum,
            minimum;

          (void) GetImageChannelRange(image,image_info->channel,&minimum,
            &maximum,&image->exception);
          (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
            GetMagickPrecision(),minimum);
          break;
        }
      break;
    }
    case 'o':
    {
      if (LocaleCompare("opaque",property) == 0)
        {
          MagickBooleanType
            opaque;

          opaque=IsOpaqueImage(image,&image->exception);
          (void) CopyMagickString(value,opaque != MagickFalse ? "true" :
            "false",MaxTextExtent);
          break;
        }
      if (LocaleCompare("orientation",property) == 0)
        {
          string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
            image->orientation);
          break;
        }
      if ((image_info != (ImageInfo *) NULL) &&
          (LocaleCompare("output",property) == 0))
        {
          (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
          break;
        }
     break;
    }
    case 'p':
    {
      if (LocaleCompare("page",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",(double)
            image->page.width,(double) image->page.height);
          break;
        }
      if (LocaleNCompare("papersize:",property,10) == 0)
        {
          char
            *papersize;

          *value='\0';
          papersize=GetPageGeometry(property+10);
          if (papersize != (const char *) NULL)
            {
              RectangleInfo
                page = { 0, 0, 0, 0 };

              (void) ParseAbsoluteGeometry(papersize,&page);
              (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
                (double) page.width,(double) page.height);
              papersize=DestroyString(papersize);
            }
          break;
        }
#if defined(MAGICKCORE_LCMS_DELEGATE)
      if (LocaleCompare("profile:icc",property) == 0 ||
          LocaleCompare("profile:icm",property) == 0)
        {
#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
#define cmsUInt32Number  DWORD
#endif

          const StringInfo
            *profile;

          cmsHPROFILE
            icc_profile;

          profile=GetImageProfile(image,property+8);
          if (profile == (StringInfo *) NULL)
            break;

          icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
            (cmsUInt32Number) GetStringInfoLength(profile));
          if (icc_profile != (cmsHPROFILE *) NULL)
            {
#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
              string=cmsTakeProductName(icc_profile);
#else
              (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
                "en","US",value,MaxTextExtent);
#endif
              (void) cmsCloseProfile(icc_profile);
            }
      }
#endif
      if (LocaleCompare("printsize.x",property) == 0)
        {
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
            GetMagickPrecision(),PerceptibleReciprocal(image->x_resolution)*
              image->columns);
          break;
        }
      if (LocaleCompare("printsize.y",property) == 0)
        {
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
            GetMagickPrecision(),PerceptibleReciprocal(image->y_resolution)*
              image->rows);
          break;
        }
      if (LocaleCompare("profiles",property) == 0)
        {
          const char
            *name;

          ResetImageProfileIterator(image);
          name=GetNextImageProfile(image);
          if (name != (char *) NULL)
            {
              (void) CopyMagickString(value,name,MaxTextExtent);
              name=GetNextImageProfile(image);
              while (name != (char *) NULL)
              {
                ConcatenateMagickString(value,",",MaxTextExtent);
                ConcatenateMagickString(value,name,MaxTextExtent);
                name=GetNextImageProfile(image);
              }
            }
          break;
        }
      break;
    }
    case 'q':
    {
      if (LocaleCompare("quality",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
            image->quality);
          break;
        }
      break;
    }
    case 'r':
    {
      if (LocaleCompare("rendering-intent",property) == 0)
        {
          string=CommandOptionToMnemonic(MagickIntentOptions,(ssize_t)
            image->rendering_intent);
          break;
        }
      if (LocaleCompare("resolution.x",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%g",
            image->x_resolution);
          break;
        }
      if (LocaleCompare("resolution.y",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%g",
            image->y_resolution);
          break;
        }
      break;
    }
    case 's':
    {
      if (LocaleCompare("scene",property) == 0)
        {
          if ((image_info != (ImageInfo *) NULL) &&
              (image_info->number_scenes != 0))
            (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
              image_info->scene);
          else
            (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
              image->scene);
          break;
        }
      if (LocaleCompare("scenes",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
            GetImageListLength(image));
          break;
        }
      if (LocaleCompare("size",property) == 0)
        {
          (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
          break;
        }
      if (LocaleCompare("skewness",property) == 0)
        {
          double
            kurtosis,
            skewness;

          (void) GetImageChannelKurtosis(image,image_info->channel,&kurtosis,
            &skewness,&image->exception);
          (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
            GetMagickPrecision(),skewness);
          break;
        }
      if ((LocaleCompare("standard-deviation",property) == 0) ||
          (LocaleCompare("standard_deviation",property) == 0))
        {
          double
            mean,
            standard_deviation;

          (void) GetImageChannelMean(image,image_info->channel,&mean,
            &standard_deviation,&image->exception);
          (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
            GetMagickPrecision(),standard_deviation);
          break;
        }
       break;
    }
    case 't':
    {
      if (LocaleCompare("type",property) == 0)
        {
          string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
            IdentifyImageType(image,&image->exception));
          break;
        }
       break;
    }
    case 'u':
    {
      if ((image_info != (ImageInfo *) NULL) &&
          (LocaleCompare("unique",property) == 0))
        {
          string=image_info->unique;
          break;
        }
      if (LocaleCompare("units",property) == 0)
        {
          /*
            Image resolution units.
          */
          string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
            image->units);
          break;
        }
      break;
    }
    case 'v':
    {
      if (LocaleCompare("version",property) == 0)
        {
          string=GetMagickVersion((size_t *) NULL);
          break;
        }
      break;
    }
    case 'w':
    {
      if (LocaleCompare("width",property) == 0)
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
            (image->magick_columns != 0 ? image->magick_columns : 256));
          break;
        }
      break;
    }
    case 'x': /* FUTURE: Obsolete X resolution */
    {
      if ((LocaleCompare("xresolution",property) == 0) ||
          (LocaleCompare("x-resolution",property) == 0) )
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
            image->x_resolution);
          break;
        }
      break;
    }
    case 'y': /* FUTURE: Obsolete Y resolution */
    {
      if ((LocaleCompare("yresolution",property) == 0) ||
          (LocaleCompare("y-resolution",property) == 0) )
        {
          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
            image->y_resolution);
          break;
        }
      break;
    }
    case 'z':
    {
      if ((image_info != (ImageInfo *) NULL) &&
          (LocaleCompare("zero",property) == 0))
        {
          string=image_info->zero;
          break;
        }
      break;
    }
  }
  if (*value != '\0')
    string=value;
  if (string != (char *) NULL)
    {
      (void) SetImageArtifact(image,"get-property", string);
      return(GetImageArtifact(image,"get-property"));
    }
  return((char *) NULL);
}


static char *TracePSClippath(const unsigned char *blob,size_t length,
  const size_t magick_unused(columns),const size_t magick_unused(rows))
{
  char
    *path,
    *message;

  MagickBooleanType
    in_subpath;

  PointInfo
    first[3],
    last[3],
    point[3];

  ssize_t
    i,
    x;

  ssize_t
    knot_count,
    selector,
    y;

  magick_unreferenced(columns);
  magick_unreferenced(rows);

  path=AcquireString((char *) NULL);
  if (path == (char *) NULL)
    return((char *) NULL);
  message=AcquireString((char *) NULL);
  (void) FormatLocaleString(message,MaxTextExtent,"/ClipImage\n");
  (void) ConcatenateString(&path,message);
  (void) FormatLocaleString(message,MaxTextExtent,"{\n");
  (void) ConcatenateString(&path,message);
  (void) FormatLocaleString(message,MaxTextExtent,"  /c {curveto} bind def\n");
  (void) ConcatenateString(&path,message);
  (void) FormatLocaleString(message,MaxTextExtent,"  /l {lineto} bind def\n");
  (void) ConcatenateString(&path,message);
  (void) FormatLocaleString(message,MaxTextExtent,"  /m {moveto} bind def\n");
  (void) ConcatenateString(&path,message);
  (void) FormatLocaleString(message,MaxTextExtent,
    "  /v {currentpoint 6 2 roll curveto} bind def\n");
  (void) ConcatenateString(&path,message);
  (void) FormatLocaleString(message,MaxTextExtent,
    "  /y {2 copy curveto} bind def\n");
  (void) ConcatenateString(&path,message);
  (void) FormatLocaleString(message,MaxTextExtent,
    "  /z {closepath} bind def\n");
  (void) ConcatenateString(&path,message);
  (void) FormatLocaleString(message,MaxTextExtent,"  newpath\n");
  (void) ConcatenateString(&path,message);
  /*
    The clipping path format is defined in "Adobe Photoshop File
    Formats Specification" version 6.0 downloadable from adobe.com.
  */
  (void) memset(point,0,sizeof(point));
  (void) memset(first,0,sizeof(first));
  (void) memset(last,0,sizeof(last));
  knot_count=0;
  in_subpath=MagickFalse;
  while (length > 0)
  {
    selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
    switch (selector)
    {
      case 0:
      case 3:
      {
        if (knot_count != 0)
          {
            blob+=24;
            length-=MagickMin(24,(ssize_t) length);
            break;
          }
        /*
          Expected subpath length record.
        */
        knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
        blob+=22;
        length-=MagickMin(22,(ssize_t) length);
        break;
      }
      case 1:
      case 2:
      case 4:
      case 5:
      {
        if (knot_count == 0)
          {
            /*
              Unexpected subpath knot
            */
            blob+=24;
            length-=MagickMin(24,(ssize_t) length);
            break;
          }
        /*
          Add sub-path knot
        */
        for (i=0; i < 3; i++)
        {
          y=(size_t) ReadPropertyMSBLong(&blob,&length);
          x=(size_t) ReadPropertyMSBLong(&blob,&length);
          point[i].x=(double) x/4096.0/4096.0;
          point[i].y=1.0-(double) y/4096.0/4096.0;
        }
        if (in_subpath == MagickFalse)
          {
            (void) FormatLocaleString(message,MaxTextExtent,"  %g %g m\n",
              point[1].x,point[1].y);
            for (i=0; i < 3; i++)
            {
              first[i]=point[i];
              last[i]=point[i];
            }
          }
        else
          {
            /*
              Handle special cases when Bezier curves are used to describe
              corners and straight lines.
            */
            if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
                (point[0].x == point[1].x) && (point[0].y == point[1].y))
              (void) FormatLocaleString(message,MaxTextExtent,
                "  %g %g l\n",point[1].x,point[1].y);
            else
              if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
                (void) FormatLocaleString(message,MaxTextExtent,
                  "  %g %g %g %g v\n",point[0].x,point[0].y,
                  point[1].x,point[1].y);
              else
                if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
                  (void) FormatLocaleString(message,MaxTextExtent,
                    "  %g %g %g %g y\n",last[2].x,last[2].y,
                    point[1].x,point[1].y);
                else
                  (void) FormatLocaleString(message,MaxTextExtent,
                    "  %g %g %g %g %g %g c\n",last[2].x,
                    last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
            for (i=0; i < 3; i++)
              last[i]=point[i];
          }
        (void) ConcatenateString(&path,message);
        in_subpath=MagickTrue;
        knot_count--;
        /*
          Close the subpath if there are no more knots.
        */
        if (knot_count == 0)
          {
            /*
              Same special handling as above except we compare to the
              first point in the path and close the path.
            */
            if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
                (first[0].x == first[1].x) && (first[0].y == first[1].y))
              (void) FormatLocaleString(message,MaxTextExtent,
                "  %g %g l z\n",first[1].x,first[1].y);
            else
              if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
                (void) FormatLocaleString(message,MaxTextExtent,
                  "  %g %g %g %g v z\n",first[0].x,first[0].y,
                  first[1].x,first[1].y);
              else
                if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
                  (void) FormatLocaleString(message,MaxTextExtent,
                    "  %g %g %g %g y z\n",last[2].x,last[2].y,
                    first[1].x,first[1].y);
                else
                  (void) FormatLocaleString(message,MaxTextExtent,
                    "  %g %g %g %g %g %g c z\n",last[2].x,
                    last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
            (void) ConcatenateString(&path,message);
            in_subpath=MagickFalse;
          }
        break;
      }
      case 6:
      case 7:
      case 8:
      default:
      {
        blob+=24;
        length-=MagickMin(24,(ssize_t) length);
        break;
      }
    }
  }
  /*
    Returns an empty PS path if the path has no knots.
  */
  (void) FormatLocaleString(message,MaxTextExtent,"  eoclip\n");
  (void) ConcatenateString(&path,message);
  (void) FormatLocaleString(message,MaxTextExtent,"} bind def");
  (void) ConcatenateString(&path,message);
  message=DestroyString(message);
  return(path);
}


static inline signed short ReadPropertySignedShort(const EndianType endian,
  const unsigned char *buffer)
{
  union
  {
    unsigned short
      unsigned_value;

    signed short
      signed_value;
  } quantum;

  unsigned short
    value;

  if (endian == LSBEndian)
    {
      value=(unsigned short) buffer[1] << 8;
      value|=(unsigned short) buffer[0];
      quantum.unsigned_value=value & 0xffff;
      return(quantum.signed_value);
    }
  value=(unsigned short) buffer[0] << 8;
  value|=(unsigned short) buffer[1];
  quantum.unsigned_value=value & 0xffff;
  return(quantum.signed_value);
}