Codebase list edid-decode / 723f11c
Import upstream version 0.1~git20220315.cb74358c2896+git20220406.1.8a8d673 Debian Janitor 2 years ago
13 changed file(s) with 240 addition(s) and 134 deletion(s). Raw diff Collapse all Expand all
00 edid-decode
1 edid-decode.wasm
2 edid-decode.js
13 *.dSYM
2828 $(CXX) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(WARN_FLAGS) -g $(sha) $(date) -o $@ $(SOURCES) -lm
2929
3030 edid-decode.js: $(SOURCES) edid-decode.h oui.h Makefile
31 $(EMXX) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(WARN_FLAGS) $(sha) $(date) -s EXPORTED_FUNCTIONS='["_parse_edid"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -o $@ $(SOURCES) -lm
31 $(EMXX) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(WARN_FLAGS) $(sha) $(date) -s EXPORTED_FUNCTIONS='["_parse_edid"]' -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -o $@ $(SOURCES) -lm
3232
3333 clean:
34 rm -f edid-decode
34 rm -f edid-decode edid-decode.js edid-decode.wasm
3535
3636 install:
3737 mkdir -p $(DESTDIR)$(bindir)
7676
7777 static char options[OptLast];
7878
79 #ifndef __EMSCRIPTEN__
7980 static struct option long_options[] = {
8081 { "help", no_argument, 0, OptHelp },
8182 { "output-format", required_argument, 0, OptOutputFormat },
191192 " --list-rid-timings <rid> List all timings for RID <rid> or all known RIDs if <rid> is 0.\n"
192193 " -h, --help Display this help message.\n");
193194 }
195 #endif
194196
195197 static std::string s_msgs[EDID_MAX_BLOCKS + 1][2];
196198
233235 }
234236
235237
236 void do_checksum(const char *prefix, const unsigned char *x, size_t len)
238 void do_checksum(const char *prefix, const unsigned char *x, size_t len, unsigned unused_bytes)
237239 {
238240 unsigned char check = x[len - 1];
239241 unsigned char sum = 0;
245247 sum += x[i];
246248
247249 if ((unsigned char)(check + sum) != 0) {
248 printf(" (should be 0x%02x)\n", -sum & 0xff);
250 printf(" (should be 0x%02x)", -sum & 0xff);
249251 fail("Invalid checksum 0x%02x (should be 0x%02x).\n",
250252 check, -sum & 0xff);
251 return;
252 }
253 }
254 if (unused_bytes)
255 printf(" Unused space in Extension Block: %u byte%s",
256 unused_bytes, unused_bytes > 1 ? "s" : "");
253257 printf("\n");
254258 }
255259
10231027 return true;
10241028 }
10251029
1026 static unsigned char crc_calc(const unsigned char *b)
1027 {
1028 unsigned char sum = 0;
1029 unsigned i;
1030
1031 for (i = 0; i < 127; i++)
1032 sum += b[i];
1033 return 256 - sum;
1034 }
1035
1036 static int crc_ok(const unsigned char *b)
1037 {
1038 return crc_calc(b) == b[127];
1039 }
1040
1041 static void hexdumpedid(FILE *f, const unsigned char *edid, unsigned size)
1042 {
1043 unsigned b, i, j;
1044
1045 for (b = 0; b < size / 128; b++) {
1046 const unsigned char *buf = edid + 128 * b;
1047
1048 if (b)
1049 fprintf(f, "\n");
1050 for (i = 0; i < 128; i += 0x10) {
1051 fprintf(f, "%02x", buf[i]);
1052 for (j = 1; j < 0x10; j++) {
1053 fprintf(f, " %02x", buf[i + j]);
1054 }
1055 fprintf(f, "\n");
1056 }
1057 if (!crc_ok(buf))
1058 fprintf(f, "Block %u has a checksum error (should be 0x%02x).\n",
1059 b, crc_calc(buf));
1060 }
1061 }
1062
1063 static void carraydumpedid(FILE *f, const unsigned char *edid, unsigned size)
1064 {
1065 unsigned b, i, j;
1066
1067 fprintf(f, "const unsigned char edid[] = {\n");
1068 for (b = 0; b < size / 128; b++) {
1069 const unsigned char *buf = edid + 128 * b;
1070
1071 if (b)
1072 fprintf(f, "\n");
1073 for (i = 0; i < 128; i += 8) {
1074 fprintf(f, "\t0x%02x,", buf[i]);
1075 for (j = 1; j < 8; j++) {
1076 fprintf(f, " 0x%02x,", buf[i + j]);
1077 }
1078 fprintf(f, "\n");
1079 }
1080 if (!crc_ok(buf))
1081 fprintf(f, "\t/* Block %u has a checksum error (should be 0x%02x). */\n",
1082 b, crc_calc(buf));
1083 }
1084 fprintf(f, "};\n");
1085 }
1086
1087 // This format can be read by the QuantumData EDID editor
1088 static void xmldumpedid(FILE *f, const unsigned char *edid, unsigned size)
1089 {
1090 fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
1091 fprintf(f, "<DATAOBJ>\n");
1092 fprintf(f, " <HEADER TYPE=\"DID\" VERSION=\"1.0\"/>\n");
1093 fprintf(f, " <DATA>\n");
1094 for (unsigned b = 0; b < size / 128; b++) {
1095 const unsigned char *buf = edid + 128 * b;
1096
1097 fprintf(f, " <BLOCK%u>", b);
1098 for (unsigned i = 0; i < 128; i++)
1099 fprintf(f, "%02X", buf[i]);
1100 fprintf(f, "</BLOCK%u>\n", b);
1101 }
1102 fprintf(f, " </DATA>\n");
1103 fprintf(f, "</DATAOBJ>\n");
1104 }
1105
1106
1107 static int edid_to_file(const char *to_file, enum output_format out_fmt)
1108 {
1109 FILE *out;
1110
1111 if (!strcmp(to_file, "-")) {
1112 to_file = "stdout";
1113 out = stdout;
1114 } else if ((out = fopen(to_file, "w")) == NULL) {
1115 perror(to_file);
1116 return -1;
1117 }
1118 if (out_fmt == OUT_FMT_DEFAULT)
1119 out_fmt = out == stdout ? OUT_FMT_HEX : OUT_FMT_RAW;
1120
1121 switch (out_fmt) {
1122 default:
1123 case OUT_FMT_HEX:
1124 hexdumpedid(out, edid, state.edid_size);
1125 break;
1126 case OUT_FMT_RAW:
1127 fwrite(edid, state.edid_size, 1, out);
1128 break;
1129 case OUT_FMT_CARRAY:
1130 carraydumpedid(out, edid, state.edid_size);
1131 break;
1132 case OUT_FMT_XML:
1133 xmldumpedid(out, edid, state.edid_size);
1134 break;
1135 }
1136
1137 if (out != stdout)
1138 fclose(out);
1139 return 0;
1140 }
1141
11421030 static int edid_from_file(const char *from_file, FILE *error)
11431031 {
11441032 #ifdef O_BINARY
12691157 {
12701158 block = block_name(x[0]);
12711159 data_block.clear();
1160 unused_bytes = 0;
12721161
12731162 printf("\n");
12741163 if (block_nr && x[0] == 0)
13061195 }
13071196
13081197 data_block.clear();
1309 do_checksum("", x, EDID_PAGE_SIZE);
1198 do_checksum("", x, EDID_PAGE_SIZE, unused_bytes);
13101199 }
13111200
13121201 void edid_state::print_preferred_timings()
15491438 return failures ? -2 : 0;
15501439 }
15511440
1441 #ifndef __EMSCRIPTEN__
1442
1443 static unsigned char crc_calc(const unsigned char *b)
1444 {
1445 unsigned char sum = 0;
1446 unsigned i;
1447
1448 for (i = 0; i < 127; i++)
1449 sum += b[i];
1450 return 256 - sum;
1451 }
1452
1453 static int crc_ok(const unsigned char *b)
1454 {
1455 return crc_calc(b) == b[127];
1456 }
1457
1458 static void hexdumpedid(FILE *f, const unsigned char *edid, unsigned size)
1459 {
1460 unsigned b, i, j;
1461
1462 for (b = 0; b < size / 128; b++) {
1463 const unsigned char *buf = edid + 128 * b;
1464
1465 if (b)
1466 fprintf(f, "\n");
1467 for (i = 0; i < 128; i += 0x10) {
1468 fprintf(f, "%02x", buf[i]);
1469 for (j = 1; j < 0x10; j++) {
1470 fprintf(f, " %02x", buf[i + j]);
1471 }
1472 fprintf(f, "\n");
1473 }
1474 if (!crc_ok(buf))
1475 fprintf(f, "Block %u has a checksum error (should be 0x%02x).\n",
1476 b, crc_calc(buf));
1477 }
1478 }
1479
1480 static void carraydumpedid(FILE *f, const unsigned char *edid, unsigned size)
1481 {
1482 unsigned b, i, j;
1483
1484 fprintf(f, "const unsigned char edid[] = {\n");
1485 for (b = 0; b < size / 128; b++) {
1486 const unsigned char *buf = edid + 128 * b;
1487
1488 if (b)
1489 fprintf(f, "\n");
1490 for (i = 0; i < 128; i += 8) {
1491 fprintf(f, "\t0x%02x,", buf[i]);
1492 for (j = 1; j < 8; j++) {
1493 fprintf(f, " 0x%02x,", buf[i + j]);
1494 }
1495 fprintf(f, "\n");
1496 }
1497 if (!crc_ok(buf))
1498 fprintf(f, "\t/* Block %u has a checksum error (should be 0x%02x). */\n",
1499 b, crc_calc(buf));
1500 }
1501 fprintf(f, "};\n");
1502 }
1503
1504 // This format can be read by the QuantumData EDID editor
1505 static void xmldumpedid(FILE *f, const unsigned char *edid, unsigned size)
1506 {
1507 fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
1508 fprintf(f, "<DATAOBJ>\n");
1509 fprintf(f, " <HEADER TYPE=\"DID\" VERSION=\"1.0\"/>\n");
1510 fprintf(f, " <DATA>\n");
1511 for (unsigned b = 0; b < size / 128; b++) {
1512 const unsigned char *buf = edid + 128 * b;
1513
1514 fprintf(f, " <BLOCK%u>", b);
1515 for (unsigned i = 0; i < 128; i++)
1516 fprintf(f, "%02X", buf[i]);
1517 fprintf(f, "</BLOCK%u>\n", b);
1518 }
1519 fprintf(f, " </DATA>\n");
1520 fprintf(f, "</DATAOBJ>\n");
1521 }
1522
1523 static int edid_to_file(const char *to_file, enum output_format out_fmt)
1524 {
1525 FILE *out;
1526
1527 if (!strcmp(to_file, "-")) {
1528 to_file = "stdout";
1529 out = stdout;
1530 } else if ((out = fopen(to_file, "w")) == NULL) {
1531 perror(to_file);
1532 return -1;
1533 }
1534 if (out_fmt == OUT_FMT_DEFAULT)
1535 out_fmt = out == stdout ? OUT_FMT_HEX : OUT_FMT_RAW;
1536
1537 switch (out_fmt) {
1538 default:
1539 case OUT_FMT_HEX:
1540 hexdumpedid(out, edid, state.edid_size);
1541 break;
1542 case OUT_FMT_RAW:
1543 fwrite(edid, state.edid_size, 1, out);
1544 break;
1545 case OUT_FMT_CARRAY:
1546 carraydumpedid(out, edid, state.edid_size);
1547 break;
1548 case OUT_FMT_XML:
1549 xmldumpedid(out, edid, state.edid_size);
1550 break;
1551 }
1552
1553 if (out != stdout)
1554 fclose(out);
1555 return 0;
1556 }
1557
15521558 enum cvt_opts {
15531559 CVT_WIDTH = 0,
15541560 CVT_HEIGHT,
21182124 return ret ? ret : state.parse_edid();
21192125 }
21202126
2121 #ifdef __EMSCRIPTEN__
2127 #else
2128
21222129 /*
21232130 * The surrounding JavaScript implementation will call this function
21242131 * each time it wants to decode an EDID. So this should reset all the
21372144 int ret = edid_from_file(input, stderr);
21382145 return ret ? ret : state.parse_edid();
21392146 }
2147
21402148 #endif
158158 base.sec_gtf_start_freq = 0;
159159 base.C = base.M = base.K = base.J = 0;
160160 base.max_pos_neg_hor_freq_khz = 0;
161 base.uses_srgb = false;
161162 base.detailed_block_cnt = base.dtd_cnt = 0;
162163
163164 base.min_display_hor_freq_hz = base.max_display_hor_freq_hz =
167168
168169 // CTA-861 block state
169170 cta.has_vic_1 = cta.first_svd_might_be_preferred = cta.has_sldb =
170 cta.has_hdmi = cta.has_vcdb = cta.has_vfpdb = false;
171 cta.has_hdmi = cta.has_vcdb = cta.has_vfpdb = cta.has_cdb = false;
171172 cta.previous_cta_tag = 0xfff;
172173 cta.have_hf_vsdb = cta.have_hf_scdb = false;
173174 cta.image_width = cta.image_height = 0;
208209 unsigned block_nr;
209210 std::string block;
210211 std::string data_block;
212 unsigned unused_bytes;
211213 bool has_cta;
212214 bool has_dispid;
213215 bool hide_serial_numbers;
242244 double C, M, K, J;
243245 bool supports_cvt;
244246 bool has_spwg;
247 bool uses_srgb;
245248 unsigned detailed_block_cnt;
246249 unsigned dtd_cnt;
247250 bool seen_non_detailed_descriptor;
281284 bool has_hdmi;
282285 bool has_vcdb;
283286 bool has_vfpdb;
287 bool has_cdb;
284288 unsigned preparsed_speaker_count;
285289 bool preparsed_sld_has_coord;
286290 bool preparsed_sld;
383387 void cta_rcdb(const unsigned char *x, unsigned length);
384388 void cta_sldb(const unsigned char *x, unsigned length);
385389 void cta_preparse_sldb(const unsigned char *x, unsigned length);
390 void cta_colorimetry_block(const unsigned char *x, unsigned length);
386391 void cta_hdmi_block(const unsigned char *x, unsigned length);
387392 void cta_displayid_type_7(const unsigned char *x, unsigned length);
388393 void cta_displayid_type_8(const unsigned char *x, unsigned length);
495500
496501 #endif
497502
498 void do_checksum(const char *prefix, const unsigned char *x, size_t len);
503 void do_checksum(const char *prefix, const unsigned char *x, size_t len, unsigned unused_bytes = 0);
499504 std::string utohex(unsigned char x);
500505 std::string ouitohex(unsigned oui);
501506 std::string containerid2s(const unsigned char *x);
978978 switch ((flags & 0x18) >> 3) {
979979 case 0x00:
980980 s_flags = "analog composite";
981 [[clang::fallthrough]];
981982 /* fall-through */
982983 case 0x01:
983984 if (s_flags.empty())
14951496 fail("sRGB is signaled, but the chromaticities do not match.\n");
14961497 if (x[0x17] != 120)
14971498 warn("sRGB is signaled, but the gamma != 2.2.\n");
1499 base.uses_srgb = true;
14981500 } else if (!memcmp(x + 0x19, srgb_chromaticity, sizeof(srgb_chromaticity))) {
14991501 fail("The chromaticities match sRGB, but sRGB is not signaled.\n");
1502 base.uses_srgb = true;
15001503 }
15011504
15021505 if (base.edid_minor >= 4) {
20732073 "ST2113RGB",
20742074 };
20752075
2076 static void cta_colorimetry_block(const unsigned char *x, unsigned length)
2076 void edid_state::cta_colorimetry_block(const unsigned char *x, unsigned length)
20772077 {
20782078 unsigned i;
20792079
20892089 for (i = 0; i < ARRAY_SIZE(colorimetry2_map); i++)
20902090 if (x[1] & (1 << i))
20912091 printf(" %s\n", colorimetry2_map[i]);
2092 // The sRGB bit (added in CTA-861.6) allows sources to explicitly
2093 // signal sRGB colorimetry. Without this the default colorimetry
2094 // of an RGB video is either sRGB or defaultRGB. It depends on the
2095 // Source which is used, and the Sink has no idea what it is getting.
2096 //
2097 // For proper compatibility with PCs enabling sRGB support is
2098 // desirable.
2099 if (!base.uses_srgb && !(x[1] & 0x20))
2100 warn("Set the sRGB colorimetry bit to avoid interop issues.\n");
20922101 }
20932102
20942103 static const char *eotf_map[] = {
25602569 case 0x07:
25612570 if (x[i + 1] == 0x0d)
25622571 cta.has_vfpdb = true;
2572 if (x[i + 1] == 0x05)
2573 cta.has_cdb = true;
25632574 if (x[i + 1] == 0x13 && (x[i + 2] & 0x40)) {
25642575 cta.preparsed_speaker_count = 1 + (x[i + 2] & 0x1f);
25652576 cta.preparsed_sld = x[i + 2] & 0x20;
25762587 if (x[i + 1] != 0x0e)
25772588 continue;
25782589 for_ycbcr420 = true;
2590 [[clang::fallthrough]];
25792591 /* fall-through */
25802592 case 0x02:
25812593 for (unsigned j = 1 + for_ycbcr420; j <= (x[i] & 0x1f); j++) {
26892701 }
26902702 detailed_block(detailed);
26912703 }
2692 if (!memchk(detailed, x + 127 - detailed)) {
2704 unused_bytes = x + 127 - detailed;
2705 if (!memchk(detailed, unused_bytes)) {
26932706 data_block = "Padding";
2694 fail("CTA-861 padding contains non-zero bytes.\n");
2707 fail("Contains non-zero bytes.\n");
26952708 }
26962709 } while (0);
26972710
27062719 fail("HDMI VIC Codes must have their CTA-861 VIC equivalents in the VSB.\n");
27072720 if (!cta.has_vcdb)
27082721 fail("Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues.\n");
2722 if (!base.uses_srgb && !cta.has_cdb)
2723 warn("Add a Colorimetry Data Block with the sRGB colorimetry bit set to avoid interop issues.\n");
27092724 }
27102725
27112726 void edid_state::cta_resolve_svr(vec_timings_ext::iterator iter)
20212021 data_block.clear();
20222022 do_checksum(" ", x + 1, x[2] + 5);
20232023
2024 if (!memchk(x + 1 + x[2] + 5, 0x7f - (1 + x[2] + 5))) {
2024 unused_bytes = 0x7f - (1 + x[2] + 5);
2025 if (!memchk(x + 1 + x[2] + 5, unused_bytes)) {
20252026 data_block = "Padding";
2026 fail("DisplayID padding contains non-zero bytes.\n");
2027 fail("Contains non-zero bytes.\n");
20272028 }
20282029 dispid.is_base_block = false;
20292030 }
6363 parse_string_table(x + 1);
6464 x += x[0];
6565 }
66 if (!memchk(x, orig + 127 - x)) {
66 unused_bytes = orig + 127 - x;
67 if (!memchk(x, unused_bytes)) {
6768 data_block.clear();
6869 fail("Non-zero values in unused space.\n");
6970 }
5454 print_standard_timing(" ", x[0], x[1], true);
5555 }
5656 }
57 unused_bytes = y - x;
58 if (!memchk(x, unused_bytes)) {
59 data_block = "Padding";
60 fail("Contains non-zero bytes.\n");
61 }
5762 }
2020 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
2121 <ConfigurationType>Application</ConfigurationType>
2222 <UseDebugLibraries>true</UseDebugLibraries>
23 <PlatformToolset>v142</PlatformToolset>
23 <PlatformToolset>v143</PlatformToolset>
2424 <CharacterSet>Unicode</CharacterSet>
2525 </PropertyGroup>
2626 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
2727 <ConfigurationType>Application</ConfigurationType>
2828 <UseDebugLibraries>false</UseDebugLibraries>
29 <PlatformToolset>v142</PlatformToolset>
29 <PlatformToolset>v143</PlatformToolset>
3030 <WholeProgramOptimization>true</WholeProgramOptimization>
3131 <CharacterSet>Unicode</CharacterSet>
3232 </PropertyGroup>
8484 <ConformanceMode>true</ConformanceMode>
8585 <AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
8686 <DisableSpecificWarnings>4244; 4018; 4267; 4996; 26451; 6385; 6001</DisableSpecificWarnings>
87 <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
8788 </ClCompile>
8889 <Link>
8990 <SubSystem>Console</SubSystem>
9394 </Link>
9495 </ItemDefinitionGroup>
9596 <ItemGroup>
97 <ClCompile Include="..\calc-gtf-cvt.cpp" />
98 <ClCompile Include="..\calc-ovt.cpp" />
9699 <ClCompile Include="getopt.c" />
97100 <ClCompile Include="..\edid-decode.cpp" />
98101 <ClCompile Include="..\parse-base-block.cpp" />
101104 <ClCompile Include="..\parse-displayid-block.cpp" />
102105 <ClCompile Include="..\parse-ls-ext-block.cpp" />
103106 <ClCompile Include="..\parse-vtb-ext-block.cpp" />
107 <ClCompile Include="getsubopt.c" />
104108 </ItemGroup>
105109 <ItemGroup>
110 <ClInclude Include="..\oui.h" />
106111 <ClInclude Include="getopt.h" />
107112 <ClInclude Include="unistd.h" />
108113 <ClInclude Include="..\edid-decode.h" />
3333 <ClCompile Include="..\edid-decode.cpp">
3434 <Filter>edid-decode</Filter>
3535 </ClCompile>
36 <ClCompile Include="getsubopt.c">
37 <Filter>windows-unix</Filter>
38 </ClCompile>
39 <ClCompile Include="..\calc-gtf-cvt.cpp">
40 <Filter>edid-decode</Filter>
41 </ClCompile>
42 <ClCompile Include="..\calc-ovt.cpp">
43 <Filter>edid-decode</Filter>
44 </ClCompile>
3645 </ItemGroup>
3746 <ItemGroup>
3847 <ClInclude Include="..\edid-decode.h">
4453 <ClInclude Include="unistd.h">
4554 <Filter>windows-unix</Filter>
4655 </ClInclude>
56 <ClInclude Include="..\oui.h">
57 <Filter>edid-decode</Filter>
58 </ClInclude>
4759 </ItemGroup>
4860 </Project>
8989
9090 extern int getopt_long( int, char * const [], const char *, const struct option *, int * );
9191 extern int getopt_long_only( int, char * const [], const char *, const struct option *, int * );
92
93 extern int getsubopt(char** opt, char* const* keys, char** val);
94
9295 /*
9396 * Previous MinGW implementation had...
9497 */
0 /**
1 * Copyright © 2005-2020 Rich Felker, et al.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining
4 * a copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be
12 * included in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 #include <stdlib.h>
24 #include <string.h>
25
26 int getsubopt(char** opt, char* const* keys, char** val)
27 {
28 char* s = *opt;
29 int i;
30
31 *val = NULL;
32 *opt = strchr(s, ',');
33 if (*opt) *(*opt)++ = 0;
34 else *opt = s + strlen(s);
35
36 for (i = 0; keys[i]; i++) {
37 size_t l = strlen(keys[i]);
38 if (strncmp(keys[i], s, l)) continue;
39 if (s[l] == '=')
40 *val = s + l + 1;
41 else if (s[l]) continue;
42 return i;
43 }
44 return -1;
45 }