コード例 #1
0
ファイル: sg_turs.c プロジェクト: hreinecke/sg3_utils
int
main(int argc, char * argv[])
{
    bool start_tm_valid = false;
    int k, res, progress, pr, rem, num_done;
    int err = 0;
    int ret = 0;
    int sg_fd = -1;
    int64_t elapsed_usecs = 0;
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
    struct timespec start_tm, end_tm;
#elif defined(HAVE_GETTIMEOFDAY)
    struct timeval start_tm, end_tm;
#endif
    struct loop_res_t loop_res;
    struct loop_res_t * resp = &loop_res;
    struct sg_pt_base * ptvp = NULL;
    struct opts_t opts;
    struct opts_t * op = &opts;


    memset(op, 0, sizeof(opts));
    memset(resp, 0, sizeof(loop_res));
    op->do_number = 1;
    res = parse_cmd_line(op, argc, argv);
    if (res)
        return res;
    if (op->do_help) {
        usage_for(op);
        return 0;
    }
#ifdef DEBUG
    pr2serr("In DEBUG mode, ");
    if (op->verbose_given && op->version_given) {
        pr2serr("but override: '-vV' given, zero verbose and continue\n");
        op->verbose_given = false;
        op->version_given = false;
        op->verbose = 0;
    } else if (! op->verbose_given) {
        pr2serr("set '-vv'\n");
        op->verbose = 2;
    } else
        pr2serr("keep verbose=%d\n", op->verbose);
#else
    if (op->verbose_given && op->version_given)
        pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
#endif
    if (op->version_given) {
        pr2serr("Version string: %s\n", version_str);
        return 0;
    }

    if (NULL == op->device_name) {
        pr2serr("No DEVICE argument given\n");
        usage_for(op);
        return SG_LIB_SYNTAX_ERROR;
    }

    if ((sg_fd = sg_cmds_open_device(op->device_name, true /* ro */,
                                     op->verbose)) < 0) {
        pr2serr("%s: error opening file: %s: %s\n", __func__,
                op->device_name, safe_strerror(-sg_fd));
        ret = sg_convert_errno(-sg_fd);
        goto fini;
    }
    ptvp = construct_scsi_pt_obj_with_fd(sg_fd, op->verbose);
    if ((NULL == ptvp) || ((err = get_scsi_pt_os_err(ptvp)))) {
        pr2serr("%s: unable to construct pt object\n", __func__);
        ret = sg_convert_errno(err ? err : ENOMEM);
        goto fini;
    }
    if (op->do_progress) {
        for (k = 0; k < op->do_number; ++k) {
            if (k > 0)
                sleep_for(30);
            progress = -1;
            res = sg_ll_test_unit_ready_progress_pt(ptvp, k, &progress,
                             (1 == op->do_number), op->verbose);
            if (progress < 0) {
                ret = res;
                break;
            } else {
                pr = (progress * 100) / 65536;
                rem = ((progress * 100) % 65536) / 656;
                printf("Progress indication: %d.%02d%% done\n", pr, rem);
            }
        }
        if (op->do_number > 1)
            printf("Completed %d Test Unit Ready commands\n",
                   ((k < op->do_number) ? k + 1 : k));
    } else {            /* --progress not given */
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
        if (op->do_time) {
            start_tm.tv_sec = 0;
            start_tm.tv_nsec = 0;
            if (0 == clock_gettime(CLOCK_MONOTONIC, &start_tm))
                start_tm_valid = true;
            else
                perror("clock_gettime(CLOCK_MONOTONIC)\n");
        }
#elif defined(HAVE_GETTIMEOFDAY)
        if (op->do_time) {
            start_tm.tv_sec = 0;
            start_tm.tv_usec = 0;
            gettimeofday(&start_tm, NULL);
            start_tm_valid = true;
        }
#else
        start_tm_valid = false;
#endif

        num_done = loop_turs(ptvp, resp, op);

        if (op->do_time && start_tm_valid) {
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
            if (start_tm.tv_sec || start_tm.tv_nsec) {

                res = clock_gettime(CLOCK_MONOTONIC, &end_tm);
                if (res < 0) {
                    err = errno;
                    perror("clock_gettime");
                    if (EINVAL == err)
                        pr2serr("clock_gettime(CLOCK_MONOTONIC) not "
                                "supported\n");
                }
                elapsed_usecs = (end_tm.tv_sec - start_tm.tv_sec) * 1000000;
                /* Note: (end_tm.tv_nsec - start_tm.tv_nsec) may be negative */
                elapsed_usecs += (end_tm.tv_nsec - start_tm.tv_nsec) / 1000;
            }
#elif defined(HAVE_GETTIMEOFDAY)
            if (start_tm.tv_sec || start_tm.tv_usec) {
                gettimeofday(&end_tm, NULL);
                elapsed_usecs = (end_tm.tv_sec - start_tm.tv_sec) * 1000000;
                elapsed_usecs += (end_tm.tv_usec - start_tm.tv_usec);
            }
#endif
            if (elapsed_usecs > 0) {
                int64_t nom = num_done;

                printf("time to perform commands was %u.%06u secs",
                       (unsigned)(elapsed_usecs / 1000000),
                       (unsigned)(elapsed_usecs % 1000000));
                nom *= 1000000; /* scale for integer division */
                printf("; %d operations/sec\n", (int)(nom / elapsed_usecs));
            } else
                printf("Recorded 0 or less elapsed microseconds ??\n");
        }
        if (((op->do_number > 1) || (resp->num_errs > 0)) &&
            (! resp->reported))
            printf("Completed %d Test Unit Ready commands with %d errors\n",
                   op->do_number, resp->num_errs);
        if (1 == op->do_number)
            ret = resp->ret;
    }
fini:
    if (ptvp)
        destruct_scsi_pt_obj(ptvp);
    if (sg_fd >= 0)
        sg_cmds_close_device(sg_fd);
    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
コード例 #2
0
ファイル: sg_modes.c プロジェクト: ArikaChen/sg3_utils
int
main(int argc, char * argv[])
{
    int sg_fd, k, num, len, res, md_len, bd_len, longlba, page_num, spf;
    char ebuff[EBUFF_SZ];
    const char * descp;
    unsigned char * rsp_buff = NULL;
    unsigned char def_rsp_buff[DEF_ALLOC_LEN];
    unsigned char * malloc_rsp_buff = NULL;
    int rsp_buff_size = DEF_ALLOC_LEN;
    int ret = 0;
    int density_code_off, t_proto, inq_pdt, inq_byte6, resp_mode6;
    int num_ua_pages;
    unsigned char * ucp;
    unsigned char uc;
    struct sg_simple_inquiry_resp inq_out;
    char pdt_name[64];
    struct opts_t opts;

    memset(&opts, 0, sizeof(opts));
    opts.pg_code = -1;
    res = process_cl(&opts, argc, argv);
    if (res)
        return SG_LIB_SYNTAX_ERROR;
    if (opts.do_help) {
        usage_for(&opts);
        return 0;
    }
    if (opts.do_version) {
        fprintf(stderr, "Version string: %s\n", version_str);
        return 0;
    }

    if (NULL == opts.device_name) {
        if (opts.do_list) {
            if ((opts.pg_code < 0) || (opts.pg_code > PG_CODE_MAX)) {
                printf("    Assume peripheral device type: disk\n");
                list_page_codes(0, 0, -1);
            } else {
                printf("    peripheral device type: %s\n",
                       sg_get_pdt_str(opts.pg_code, sizeof(pdt_name),
                                      pdt_name));
                if (opts.subpg_code_set)
                    list_page_codes(opts.pg_code, 0, opts.subpg_code);
                else
                    list_page_codes(opts.pg_code, 0, -1);
            }
            return 0;
        }
        fprintf(stderr, "No DEVICE argument given\n");
        usage_for(&opts);
        return SG_LIB_SYNTAX_ERROR;
    }

    if (opts.do_examine && (opts.pg_code >= 0)) {
        fprintf(stderr, "can't give '-e' and a page number\n");
        return SG_LIB_SYNTAX_ERROR;
    }

    if ((opts.do_six) && (opts.do_llbaa)) {
        fprintf(stderr, "LLBAA not defined for MODE SENSE 6, try "
                "without '-L'\n");
        return SG_LIB_SYNTAX_ERROR;
    }
    if (opts.maxlen > 0) {
        if (opts.do_six && (opts.maxlen > 255)) {
            fprintf(stderr, "For Mode Sense (6) maxlen cannot exceed "
                    "255\n");
            return SG_LIB_SYNTAX_ERROR;
        }
        if (opts.maxlen > DEF_ALLOC_LEN) {
            malloc_rsp_buff = (unsigned char *)malloc(opts.maxlen);
            if (NULL == malloc_rsp_buff) {
                fprintf(stderr, "Unable to malloc maxlen=%d bytes\n",
                        opts.maxlen);
                return SG_LIB_SYNTAX_ERROR;
            }
            rsp_buff = malloc_rsp_buff;
        } else
            rsp_buff = def_rsp_buff;
        rsp_buff_size = opts.maxlen;
    } else {    /* maxlen == 0 */
        rsp_buff_size = opts.do_six ? DEF_6_ALLOC_LEN : DEF_ALLOC_LEN;
        rsp_buff = def_rsp_buff;
    }
    /* If no pages or list selected than treat as 'a' */
    if (! ((opts.pg_code >= 0) || opts.do_all || opts.do_list ||
            opts.do_examine))
        opts.do_all = 1;

    if (opts.do_raw) {
        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
            perror("sg_set_binary_mode");
            return SG_LIB_FILE_ERROR;
        }
    }

    if ((sg_fd = sg_cmds_open_device(opts.device_name, 1 /* ro */,
                                     opts.do_verbose)) < 0) {
        fprintf(stderr, "error opening file: %s: %s\n",
                opts.device_name, safe_strerror(-sg_fd));
        if (malloc_rsp_buff)
            free(malloc_rsp_buff);
        return SG_LIB_FILE_ERROR;
    }

    if (sg_simple_inquiry(sg_fd, &inq_out, 1, opts.do_verbose)) {
        fprintf(stderr, "%s doesn't respond to a SCSI INQUIRY\n",
                opts.device_name);
        ret = SG_LIB_CAT_OTHER;
        goto finish;
    }
    inq_pdt = inq_out.peripheral_type;
    inq_byte6 = inq_out.byte_6;
    if (0 == opts.do_raw)
        printf("    %.8s  %.16s  %.4s   peripheral_type: %s [0x%x]\n",
               inq_out.vendor, inq_out.product, inq_out.revision,
               sg_get_pdt_str(inq_pdt, sizeof(pdt_name), pdt_name), inq_pdt);
    if (opts.do_list) {
        if (opts.subpg_code_set)
            list_page_codes(inq_pdt, inq_byte6, opts.subpg_code);
        else
            list_page_codes(inq_pdt, inq_byte6, -1);
        goto finish;
    }
    if (opts.do_examine) {
        ret = examine_pages(sg_fd, inq_pdt, inq_byte6, &opts);
        goto finish;
    }
    if (PG_CODE_ALL == opts.pg_code) {
        if (0 == opts.do_all)
            ++opts.do_all;
    } else if (opts.do_all)
        opts.pg_code = PG_CODE_ALL;
    if (opts.do_all > 1)
        opts.subpg_code = SPG_CODE_ALL;

    if (opts.do_raw > 1) {
        if (opts.do_all) {
            if (opts.opt_new)
                fprintf(stderr, "'-R' requires a specific (sub)page, not "
                        "all\n");
            else
                fprintf(stderr, "'-r' requires a specific (sub)page, not "
                        "all\n");
            usage_for(&opts);
            ret = SG_LIB_SYNTAX_ERROR;
            goto finish;
        }
    }

    memset(rsp_buff, 0, sizeof(rsp_buff));
    if (opts.do_six) {
        res = sg_ll_mode_sense6(sg_fd, opts.do_dbd, opts.page_control,
                                opts.pg_code, opts.subpg_code, rsp_buff,
                                rsp_buff_size, 1, opts.do_verbose);
        if (SG_LIB_CAT_INVALID_OP == res)
            fprintf(stderr, ">>>>>> try again without the '-6' "
                    "switch for a 10 byte MODE SENSE command\n");
    } else {
        res = sg_ll_mode_sense10(sg_fd, opts.do_llbaa, opts.do_dbd,
                                 opts.page_control, opts.pg_code,
                                 opts.subpg_code, rsp_buff, rsp_buff_size,
                                 1, opts.do_verbose);
        if (SG_LIB_CAT_INVALID_OP == res)
            fprintf(stderr, ">>>>>> try again with a '-6' "
                    "switch for a 6 byte MODE SENSE command\n");
    }
    if (SG_LIB_CAT_ILLEGAL_REQ == res) {
        if (opts.subpg_code > 0)
            fprintf(stderr, "invalid field in cdb (perhaps subpages "
                    "not supported)\n");
        else if (opts.page_control > 0)
            fprintf(stderr, "invalid field in cdb (perhaps "
                    "page control (PC) not supported)\n");
        else
            fprintf(stderr, "invalid field in cdb (perhaps "
                "page 0x%x not supported)\n", opts.pg_code);
    } else if (SG_LIB_CAT_NOT_READY == res)
        fprintf(stderr, "device not ready\n");
    else if (SG_LIB_CAT_UNIT_ATTENTION == res)
        fprintf(stderr, "unit attention\n");
    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
        fprintf(stderr, "aborted command\n");
    ret = res;
    if (0 == res) {
        int medium_type, specific, headerlen;

        ret = 0;
        resp_mode6 = opts.do_six;
        if (opts.do_flexible) {
            num = rsp_buff[0];
            if (opts.do_six && (num < 3))
                resp_mode6 = 0;
            if ((0 == opts.do_six) && (num > 5)) {
                if ((num > 11) && (0 == (num % 2)) && (0 == rsp_buff[4]) &&
                    (0 == rsp_buff[5]) && (0 == rsp_buff[6])) {
                    rsp_buff[1] = num;
                    rsp_buff[0] = 0;
                    fprintf(stderr, ">>> msense(10) but resp[0]=%d and "
                            "not msense(6) response so fix length\n", num);
                } else
                    resp_mode6 = 1;
            }
        }
        if ((! opts.do_raw) && (1 != opts.do_hex)) {
            if (resp_mode6 == opts.do_six)
                printf("Mode parameter header from MODE SENSE(%s):\n",
                       (opts.do_six ? "6" : "10"));
            else
                printf(" >>> Mode parameter header from MODE SENSE(%s),\n"
                       "     decoded as %s byte response:\n",
                       (opts.do_six ? "6" : "10"), (resp_mode6 ? "6" : "10"));
        }
        if (resp_mode6) {
            headerlen = 4;
            md_len = rsp_buff[0] + 1;
            bd_len = rsp_buff[3];
            medium_type = rsp_buff[1];
            specific = rsp_buff[2];
            longlba = 0;
        } else {
            headerlen = 8;
            md_len = (rsp_buff[0] << 8) + rsp_buff[1] + 2;
            bd_len = (rsp_buff[6] << 8) + rsp_buff[7];
            medium_type = rsp_buff[2];
            specific = rsp_buff[3];
            longlba = rsp_buff[4] & 1;
        }
        if ((bd_len + headerlen) > md_len) {
            fprintf(stderr, "Invalid block descriptor length=%d, ignore\n",
                    bd_len);
            bd_len = 0;
        }
        if (opts.do_raw) {
            if (1 == opts.do_raw)
                dStrRaw((const char *)rsp_buff, md_len);
            else {
                ucp = rsp_buff + bd_len + headerlen;
                md_len -= bd_len + headerlen;
                spf = ((ucp[0] & 0x40) ? 1 : 0);
                len = (spf ? ((ucp[2] << 8) + ucp[3] + 4) : (ucp[1] + 2));
                len = (len < md_len) ? len : md_len;
                for (k = 0; k < len; ++k)
                    printf("%02x\n", ucp[k]);
            }
            goto finish;
        }
        if (1 == opts.do_hex) {
            dStrHex((const char *)rsp_buff, md_len, 1);
            goto finish;
        } else if (opts.do_hex > 1)
            dStrHex((const char *)rsp_buff, headerlen, 1);
        if (0 == inq_pdt)
            printf("  Mode data length=%d, medium type=0x%.2x, WP=%d,"
                   " DpoFua=%d, longlba=%d\n", md_len, medium_type,
                   !!(specific & 0x80), !!(specific & 0x10), longlba);
        else
            printf("  Mode data length=%d, medium type=0x%.2x, specific"
                   " param=0x%.2x, longlba=%d\n", md_len, medium_type,
                   specific, longlba);
        if (md_len > rsp_buff_size) {
            printf("Only fetched %d bytes of response, truncate output\n",
                   rsp_buff_size);
            md_len = rsp_buff_size;
            if (bd_len + headerlen > rsp_buff_size)
                bd_len = rsp_buff_size - headerlen;
        }
        if (! opts.do_dbout) {
            printf("  Block descriptor length=%d\n", bd_len);
            if (bd_len > 0) {
                len = 8;
                density_code_off = 0;
                num = bd_len;
                if (longlba) {
                    printf("> longlba direct access device block "
                           "descriptors:\n");
                    len = 16;
                    density_code_off = 8;
                }
                else if (0 == inq_pdt) {
                    printf("> Direct access device block descriptors:\n");
                    density_code_off = 4;
                }
                else
                    printf("> General mode parameter block descriptors:\n");

                ucp = rsp_buff + headerlen;
                while (num > 0) {
                    printf("   Density code=0x%x\n",
                           *(ucp + density_code_off));
                    dStrHex((const char *)ucp, len, 1);
                    ucp += len;
                    num -= len;
                }
                printf("\n");
            }
        }
        ucp = rsp_buff + bd_len + headerlen;    /* start of mode page(s) */
        md_len -= bd_len + headerlen;           /* length of mode page(s) */
        num_ua_pages = 0;
        for (k = 0; md_len > 0; ++k) { /* got mode page(s) */
            if ((k > 0) && (! opts.do_all) &&
                (SPG_CODE_ALL != opts.subpg_code)) {
                fprintf(stderr, "Unexpectedly received extra mode page "
                                "responses, ignore\n");
                break;
            }
            uc = *ucp;
            spf = ((uc & 0x40) ? 1 : 0);
            len = (spf ? ((ucp[2] << 8) + ucp[3] + 4) : (ucp[1] + 2));
            page_num = ucp[0] & PG_CODE_MASK;
            if (0x0 == page_num) {
                ++num_ua_pages;
                if((num_ua_pages > 3) && (md_len > 0xa00)) {
                    fprintf(stderr, ">>> Seen 3 unit attention pages "
                            "(only one should be at end)\n     and mpage "
                            "length=%d, looks malformed, try '-f' option\n",
                            md_len);
                    break;
                }
            }
            if (opts.do_hex) {
                if (spf)
                    printf(">> page_code=0x%x, subpage_code=0x%x, page_cont"
                           "rol=%d\n", page_num, ucp[1], opts.page_control);
                else
                    printf(">> page_code=0x%x, page_control=%d\n", page_num,
                           opts.page_control);
            } else {
                descp = NULL;
                if ((0x18 == page_num) || (0x19 == page_num)) {
                    t_proto = (spf ? ucp[5] : ucp[2]) & 0xf;
                    descp = find_page_code_desc(page_num, (spf ? ucp[1] : 0),
                                                inq_pdt, inq_byte6, t_proto);
                } else
                    descp = find_page_code_desc(page_num, (spf ? ucp[1] : 0),
                                                inq_pdt, inq_byte6, -1);
                if (NULL == descp) {
                    if (spf)
                        snprintf(ebuff, EBUFF_SZ, "0x%x, subpage_code: 0x%x",
                                 page_num, ucp[1]);
                    else
                        snprintf(ebuff, EBUFF_SZ, "0x%x", page_num);
                }
                if (descp)
                    printf(">> %s, page_control: %s\n", descp,
                           pg_control_str_arr[opts.page_control]);
                else
                    printf(">> page_code: %s, page_control: %s\n", ebuff,
                           pg_control_str_arr[opts.page_control]);
            }
            num = (len > md_len) ? md_len : len;
            if ((k > 0) && (num > 256)) {
                num = 256;
                fprintf(stderr, ">>> page length (%d) > 256 bytes, unlikely "
                                "trim\n    Try '-f' option\n", len);
            }
            dStrHex((const char *)ucp, num , 1);
            ucp += len;
            md_len -= len;
        }
    }

finish:
    sg_cmds_close_device(sg_fd);
    if (malloc_rsp_buff)
        free(malloc_rsp_buff);
    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
コード例 #3
0
ファイル: sg_turs.c プロジェクト: ArikaChen/sg3_utils
int main(int argc, char * argv[])
{
    int sg_fd, k, res, progress, pr, rem;
    int num_errs = 0;
    int reported = 0;
    int ret = 0;
#ifndef SG_LIB_MINGW
    struct timeval start_tm, end_tm;
#endif
    struct opts_t opts;

    memset(&opts, 0, sizeof(opts));
    opts.do_number = 1;
    res = process_cl(&opts, argc, argv);
    if (res)
        return SG_LIB_SYNTAX_ERROR;
    if (opts.do_help) {
        usage_for(&opts);
        return 0;
    }
    if (opts.do_version) {
        fprintf(stderr, "Version string: %s\n", version_str);
        return 0;
    }

    if (NULL == opts.device_name) {
        fprintf(stderr, "No DEVICE argument given\n");
        usage_for(&opts);
        return SG_LIB_SYNTAX_ERROR;
    }

    if ((sg_fd = sg_cmds_open_device(opts.device_name, 1 /* ro */,
                                     opts.do_verbose)) < 0) {
        fprintf(stderr, "sg_turs: error opening file: %s: %s\n",
                opts.device_name, safe_strerror(-sg_fd));
        return SG_LIB_FILE_ERROR;
    }
    if (opts.do_progress) {
        for (k = 0; k < opts.do_number; ++k) {
            if (k > 0)
                sleep_for(30);
            progress = -1;
            res = sg_ll_test_unit_ready_progress(sg_fd, k, &progress,
                     ((1 == opts.do_number) ? 1 : 0), opts.do_verbose);
            if (progress < 0) {
                ret = res;
                break;
            } else {
                pr = (progress * 100) / 65536;
                rem = ((progress * 100) % 65536) / 656;
                printf("Progress indication: %d.%02d%% done\n", pr, rem);
            }
        }
        if (opts.do_number > 1)
            printf("Completed %d Test Unit Ready commands\n",
                   ((k < opts.do_number) ? k + 1 : k));
    } else {
#ifndef SG_LIB_MINGW
        if (opts.do_time) {
            start_tm.tv_sec = 0;
            start_tm.tv_usec = 0;
            gettimeofday(&start_tm, NULL);
        }
#endif
        for (k = 0; k < opts.do_number; ++k) {
            /* Might get Unit Attention on first invocation */
            res = sg_ll_test_unit_ready(sg_fd, k, (0 == k), opts.do_verbose);
            if (res) {
                ++num_errs;
                ret = res;
                if ((1 == opts.do_number) && (SG_LIB_CAT_NOT_READY == res)) {
                    printf("device not ready\n");
                    reported = 1;
                    break;
                }
            }
        }
#ifndef SG_LIB_MINGW
        if ((opts.do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
            struct timeval res_tm;
            double a, b;

            gettimeofday(&end_tm, NULL);
            res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
            res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
            if (res_tm.tv_usec < 0) {
                --res_tm.tv_sec;
                res_tm.tv_usec += 1000000;
            }
            a = res_tm.tv_sec;
            a += (0.000001 * res_tm.tv_usec);
            b = (double)opts.do_number;
            printf("time to perform commands was %d.%06d secs",
                   (int)res_tm.tv_sec, (int)res_tm.tv_usec);
            if (a > 0.00001)
                printf("; %.2f operations/sec\n", b / a);
            else
                printf("\n");
        }
#endif

        if (((opts.do_number > 1) || (num_errs > 0)) && (! reported))
            printf("Completed %d Test Unit Ready commands with %d errors\n",
                   opts.do_number, num_errs);
    }
    sg_cmds_close_device(sg_fd);
    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}