MOBI_RET mobi_posfid_to_link(char *link, const MOBIRawml *rawml, const char *value, MOBIAttrType *pref_attr) {
    /* "kindle:pos:fid:0000:off:0000000000" */
    /* extract fid and off */
    if (strlen(value) < (sizeof("kindle:pos:fid:0000:off:0000000000") - 1)) {
        debug_print("Skipping too short link: %s\n", value);
        *link = '\0';
        return MOBI_SUCCESS;
    }
    value += (sizeof("kindle:pos:fid:") - 1);
    if (value[4] != ':') {
        debug_print("Skipping malformed link: kindle:pos:fid:%s\n", value);
        *link = '\0';
        return MOBI_SUCCESS;
    }
    char str_fid[4 + 1];
    strncpy(str_fid, value, 4);
    str_fid[4] = '\0';
    char str_off[10 + 1];
    value += (sizeof("0001:off:") - 1);
    strncpy(str_off, value, 10);
    str_off[10] = '\0';
    
    /* get file number and id value */
    uint32_t pos_off;
    uint32_t pos_fid;
    MOBI_RET ret = mobi_base32_decode(&pos_off, str_off);
    if (ret != MOBI_SUCCESS) {
        return ret;
    }
    ret = mobi_base32_decode(&pos_fid, str_fid);
    if (ret != MOBI_SUCCESS) {
        return ret;
    }
    uint32_t part_id;
    char id[MOBI_ATTRVALUE_MAXSIZE + 1];
    ret = mobi_get_id_by_posoff(&part_id, id, rawml, pos_fid, pos_off, pref_attr);
    if (ret != MOBI_SUCCESS) {
        return ret;
    }
    /* FIXME: pos_off == 0 means top of file? */
    if (pos_off) {
        int n = snprintf(link, MOBI_ATTRVALUE_MAXSIZE + 1, "\"part%05u.html#%s\"", part_id, id);
        if (n > MOBI_ATTRVALUE_MAXSIZE + 1) {
            debug_print("Skipping truncated link: %s\n", link);
            *link = '\0';
            return MOBI_SUCCESS;
       }
    } else {
        snprintf(link, MOBI_ATTRVALUE_MAXSIZE + 1, "\"part%05u.html\"", part_id);
    }
    return MOBI_SUCCESS;
}
MOBI_RET mobi_posfid_to_link(char *link, const MOBIRawml *rawml, const char *value, MOBIAttrType *pref_attr) {
     
     
    if (strlen(value) < (sizeof("kindle:pos:fid:0000:off:0000000000") - 1)) {
        debug_print("Skipping too short link: %s\n", value);
        *link = '\0';
        return MOBI_SUCCESS;
    }
    value += (sizeof("kindle:pos:fid:") - 1);
    if (value[4] != ':') {
        debug_print("Skipping malformed link: kindle:pos:fid:%s\n", value);
        *link = '\0';
        return MOBI_SUCCESS;
    }
    char str_fid[4 + 1];
    strncpy(str_fid, value, 4);
    str_fid[4] = '\0';
    char str_off[10 + 1];
    value += (sizeof("0001:off:") - 1);
    strncpy(str_off, value, 10);
    str_off[10] = '\0';
    
     
    uint32_t pos_off;
    uint32_t pos_fid;
    MOBI_RET ret = mobi_base32_decode(&pos_off, str_off);
    if (ret != MOBI_SUCCESS) {
        return ret;
    }
    ret = mobi_base32_decode(&pos_fid, str_fid);
    if (ret != MOBI_SUCCESS) {
        return ret;
    }
    uint32_t part_id;
    char id[MOBI_ATTRVALUE_MAXSIZE + 1];
    ret = mobi_get_id_by_posoff(&part_id, id, rawml, pos_fid, pos_off, pref_attr);
    if (ret != MOBI_SUCCESS) {
        return ret;
    }
     
    if (pos_off) {
        int n = snprintf(link, MOBI_ATTRVALUE_MAXSIZE + 1, "\"part%05u.html#%s\"", part_id, id);
        if (n > MOBI_ATTRVALUE_MAXSIZE + 1) {
            debug_print("Skipping truncated link: %s\n", link);
            *link = '\0';
            return MOBI_SUCCESS;
       }
    } else {
        snprintf(link, MOBI_ATTRVALUE_MAXSIZE + 1, "\"part%05u.html\"", part_id);
    }
    return MOBI_SUCCESS;
}